]> icculus.org git repositories - taylor/freespace2.git/blob - src/ship/aicode.cpp
added copyright header
[taylor/freespace2.git] / src / ship / aicode.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Ship/AiCode.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  * 
15  * AI code that does interesting stuff
16  *
17  * $Log$
18  * Revision 1.4  2002/06/09 04:41:26  relnev
19  * added copyright header
20  *
21  * Revision 1.3  2002/06/01 07:12:34  relnev
22  * a few NDEBUG updates.
23  *
24  * removed a few warnings.
25  *
26  * Revision 1.2  2002/05/03 13:34:33  theoddone33
27  * More stuff compiles
28  *
29  * Revision 1.1.1.1  2002/05/03 03:28:10  root
30  * Initial import.
31  *
32  * 
33  * 107   9/15/99 4:42a Mikek
34  * Make any big ship attacking Colossus, or Colossus attacking any large
35  * ship not use big cruiser movement code.
36  * 
37  * 106   9/15/99 3:28a Jimb
38  * Make all big ships in sm3-08 not do cruiser chase code when attacking
39  * Colossus.  Added so Beast doesn't swerve away from Colossus.
40  * 
41  * 105   9/14/99 4:18p Andsager
42  * hack for mission sm3-08 to abort cruiser_chase as sathanas is about to
43  * begin circling colossus.
44  * 
45  * 104   9/08/99 10:44p Andsager
46  * Make HUGE ships not die when warping out, after warp effect started.
47  * 
48  * 103   9/03/99 11:40p Mikek
49  * Comment out an annoying nprintf().
50  * 
51  * 102   9/01/99 11:26p Dave
52  * Fixed release build warnings.
53  * 
54  * 101   9/01/99 9:12p Mikek
55  * Make it a boatload harder to become a traitor from hitting a large
56  * ship.
57  * 
58  * 100   9/01/99 4:01p Andsager
59  * Make sure BIG|HUGE ships do not respond to shockwaves
60  * 
61  * 99    9/01/99 10:09a Dave
62  * Pirate bob.
63  * 
64  * 98    8/31/99 4:24p Andsager
65  * Reduce collisions when attacking big ships.
66  * 
67  * 97    8/31/99 7:33a Mikek
68  * Improvements in formation flying, less silly behavior, especially when
69  * leader is moving very slowly.
70  * 
71  * 96    8/31/99 5:48a Mikek
72  * Making ships not overshoot so much in formation flying.  Intermediate
73  * checkin.
74  * 
75  * 95    8/30/99 12:03a Mikek
76  * Make guard behavior much less annoying.  Guarders don't get quite so
77  * close and they try to avoid striking the target they are guarding.
78  * 
79  * 94    8/29/99 4:18p Andsager
80  * New "burst" limit for friendly damage.  Also credit more damage done
81  * against large friendly ships.
82  * 
83  * 93    8/28/99 7:29p Dave
84  * Fixed wingmen persona messaging. Make sure locked turrets don't count
85  * towards the # attacking a player.
86  * 
87  * 92    8/26/99 10:46p Andsager
88  * Apply shockwave damage to lethality.
89  * 
90  * 91    8/26/99 8:52p Dave
91  * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
92  * 
93  * 90    8/26/99 5:14p Andsager
94  * 
95  * 89    8/24/99 8:55p Dave
96  * Make sure nondimming pixels work properly in tech menu.
97  * 
98  * 88    8/23/99 6:21p Jefff
99  * added "no traitor" option to missions (and fred)
100  * 
101  * 87    8/20/99 3:36p Andsager
102  * Make sure we don;t miss stealth sweep points.
103  * 
104  * 86    8/16/99 8:21a Andsager
105  * fix link error
106  * 
107  * 85    8/16/99 8:19a Andsager
108  * Add project_point_onto_bbox() to fvi and include in aicode
109  * 
110  * 84    8/15/99 1:30p Dave
111  * Removed some bounding box code because of link errors. Assuming needed
112  * function just needs to get checked in by DaveA.
113  * 
114  * 83    8/15/99 11:59a Andsager
115  * For targing big/huge ships, find nearest distance to bbox, not center.
116  * 
117  * 82    8/13/99 2:20p Andsager
118  * Add speed modification to chances turret will find stealth ship
119  * 
120  * 81    8/13/99 10:49a Andsager
121  * Knossos and HUGE ship warp out.  HUGE ship warp in.  Stealth search
122  * modes dont collide big ships.
123  * 
124  * 80    8/10/99 5:02p Andsager
125  * Fix bug where AI gets stuck in SM_EVADE_WEAPON with no target.
126  * 
127  * 79    8/10/99 11:58a Andsager
128  * Allow turrets to sometimes see stealth.
129  * 
130  * 78    7/31/99 2:57p Dave
131  * Scaled flak aim and jitter by weapon subsystem strength.
132  * 
133  * 77    7/27/99 10:33p Andsager
134  * improve ai for attacking stealth.  reduced jitter in aim.  reduced
135  * error in position when avoiding.  skill level support for attacking
136  * stealth.  Made target error same for team vs. team.
137  * 
138  * 76    7/27/99 10:49a Andsager
139  * Make turret fire rate independent of team for HUGE turrets, and also
140  * for mult team vs. team.
141  * 
142  * 75    7/26/99 12:14p Andsager
143  * Apply cap to how much slower a transport flies with cargo.  Remove
144  * limit on waypoint speed for training.  Enemy ai get stealth exact pos
145  * when stealth fires
146  * 
147  * 74    7/20/99 1:49p Dave
148  * Peter Drake build. Fixed some release build warnings.
149  * 
150  * 73    7/19/99 2:13p Dave
151  * Added some new strings for Heiko.
152  * 
153  * 72    7/19/99 12:02p Andsager
154  * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
155  * only blow up subsystem if its strength is > 0
156  * 
157  * 71    7/15/99 9:20a Andsager
158  * FS2_DEMO initial checkin
159  * 
160  * 70    7/14/99 1:44p Andsager
161  * modify ai_guard for BIG ships to circle around the long axis
162  * 
163  * 69    7/09/99 5:54p Dave
164  * Seperated cruiser types into individual types. Added tons of new
165  * briefing icons. Campaign screen.
166  * 
167  * 68    7/08/99 4:32p Andsager
168  * fix bug with turret-tagged-only
169  * 
170  * 67    7/08/99 12:06p Andsager
171  * Add turret-tagged-only and turret-tagged-clear sexp.
172  * 
173  * 66    7/02/99 3:49p Andsager
174  * Remove debug code.  Allow targeting of stealth from any weapon it
175  * fires.
176  * 
177  * 65    7/02/99 2:01p Andsager
178  * Fix bug where big ship tries to evade dumpfire weapon.
179  * 
180  * 64    7/02/99 10:58a Andsager
181  * Put in big ship - big ship attack mode.  Modify stealth sweep ai.
182  * 
183  * 63    6/30/99 5:53p Dave
184  * Put in new anti-camper code.
185  * 
186  * 62    6/28/99 3:22p Anoop
187  * Fix turret optimization, where ship may not have any valid subsystems
188  * (all blown off).
189  * 
190  * 61    6/25/99 5:56p Andsager
191  * First real pass on stealth ai.
192  * 
193  * 60    6/25/99 3:08p Dave
194  * Multiple flyby sounds.
195  * 
196  * 59    6/25/99 1:12p Danw
197  * DKA:  Make sure big ship has subsystems before trying to target them.
198  * 
199  * 58    6/25/99 10:56a Johnson
200  * Fixed dumb ai code.
201  * 
202  * 57    6/24/99 5:15p Dave
203  * Make sure stride is always at least one for checking turret subsystem
204  * targets.
205  * 
206  * 56    6/24/99 4:59p Dave
207  * Significant speedups to turret firing.
208  * 
209  * 55    6/23/99 5:51p Andsager
210  * Add waypoint-cap-speed.  Checkin stealth ai - inactive.
211  * 
212  * 54    6/16/99 10:21a Dave
213  * Added send-message-list sexpression.
214  * 
215  * 53    6/15/99 9:25a Andsager
216  * Make guard and dynamic chase (who hit you) work with stealth
217  * 
218  * 52    6/14/99 3:21p Andsager
219  * Allow collisions between ship and its debris.  Fix up collision pairs
220  * when large ship is warping out.
221  * 
222  * 51    6/14/99 10:45a Dave
223  * Made beam weapons specify accuracy by skill level in the weapons.tbl
224  * 
225  * 50    6/03/99 8:11a Andsager
226  * 
227  * 49    6/02/99 5:41p Andsager
228  * Reduce range of secondary weapons not fired from turrets in nebula.
229  * Reduce range of beams fired from turrrets in nebula
230  * 
231  * 48    6/02/99 3:23p Andsager
232  * Make AI aware of team visibility.  Allow player targeting with team
233  * visibility info.  Make stealth ships not targetable by AI in nebula
234  * unless tagged.
235  * 
236  * 47    6/02/99 12:52p Andsager
237  * Added team-wide ship visibility.  Implemented for player.
238  * 
239  * 46    6/01/99 8:35p Dave
240  * Finished lockarm weapons. Added proper supercap weapons/damage. Added
241  * awacs-set-radius sexpression.
242  * 
243  * 45    5/28/99 5:35p Andsager
244  * Make ai nebula aware
245  * 
246  * 44    5/24/99 9:55a Dave
247  * Fixed stream weapon ai firing problem. ick.
248  * 
249  * 43    5/20/99 7:00p Dave
250  * Added alternate type names for ships. Changed swarm missile table
251  * entries.
252  * 
253  * 42    5/18/99 1:30p Dave
254  * Added muzzle flash table stuff.
255  * 
256  * 41    5/12/99 2:55p Andsager
257  * Implemented level 2 tag as priority in turret object selection
258  * 
259  * 40    5/12/99 10:42a Andsager
260  * Fix turret bug allowing HUGE turrets to fire at fighters
261  * 
262  * 39    5/06/99 11:46a Andsager
263  * Bug fixes.  Don't get into illegal strafe submode.  Don't choose turret
264  * enemy objnum for beam protected.
265  * 
266  * 38    5/03/99 10:50p Andsager
267  * Make Asteroid_obj_list.  Change get_nearest_turret_objnum() to use
268  * Asteroid_obj_list, Ship_obj_list and Missile_obj_list vs.
269  * obj_used_list.
270  * 
271  * 37    4/29/99 2:29p Dave
272  * Made flak work much better in multiplayer.
273  * 
274  * 36    4/28/99 11:36p Dave
275  * Tweaked up subspace missile strike a bit,
276  * 
277  * 35    4/28/99 3:11p Andsager
278  * Stagger turret weapon fire times.  Make turrets smarter when target is
279  * protected or beam protected.  Add weaopn range to weapon info struct.
280  * 
281  * 34    4/26/99 10:58a Andsager
282  * Add OF_BEAM_PROTECTED flag to keep object from being targeted for zing.
283  * 
284  * 33    4/23/99 12:12p Andsager
285  * Modify wing positions when player is wing leader to prevent some
286  * collisions.
287  * 
288  * 32    4/23/99 12:01p Johnson
289  * Added SIF_HUGE_SHIP
290  * 
291  * 31    4/22/99 11:06p Dave
292  * Final pass at beam weapons. Solidified a lot of stuff. All that remains
293  * now is to tweak and fix bugs as they come up. No new beam weapon
294  * features.
295  * 
296  * 30    4/20/99 6:39p Dave
297  * Almost done with artillery targeting. Added support for downloading
298  * images on the PXO screen.
299  * 
300  * 29    4/20/99 3:40p Andsager
301  * Changes to big ship ai.  Uses bounding box as limit where to fly to
302  * when flying away.
303  * 
304  * 28    4/16/99 5:54p Dave
305  * Support for on/off style "stream" weapons. Real early support for
306  * target-painting lasers.
307  * 
308  * 27    4/02/99 9:55a Dave
309  * Added a few more options in the weapons.tbl for beam weapons. Attempt
310  * at putting "pain" packets into multiplayer.
311  * 
312  * 26    3/28/99 5:58p Dave
313  * Added early demo code. Make objects move. Nice and framerate
314  * independant, but not much else. Don't use yet unless you're me :)
315  * 
316  * 25    3/19/99 9:51a Dave
317  * Checkin to repair massive source safe crash. Also added support for
318  * pof-style nebulae, and some new weapons code.
319  * 
320  * 24    3/08/99 7:03p Dave
321  * First run of new object update system. Looks very promising.
322  * 
323  * 23    3/05/99 3:55p Anoop
324  * Handle some asserts properly.
325  * 
326  * 22    3/04/99 6:09p Dave
327  * Added in sexpressions for firing beams and checking for if a ship is
328  * tagged.
329  * 
330  * 21    3/02/99 9:25p Dave
331  * Added a bunch of model rendering debug code. Started work on fixing
332  * beam weapon wacky firing.
333  * 
334  * 20    2/25/99 2:32p Anoop
335  * (Alan). Fixed ai path following code for AI_BAY_EMERGE. Put in sanity
336  * check so that when the last point on the path is reached, it finishes.
337  * 
338  * 19    2/19/99 2:11p Anoop
339  * Put in some nice handling code for wacky support ship problems (like no
340  * docking paths)
341  * 
342  * 18    2/17/99 2:11p Dave
343  * First full run of squad war. All freespace and tracker side stuff
344  * works.
345  * 
346  * 17    2/11/99 5:22p Andsager
347  * Fixed bugs, generalized block Sexp_variables
348  * 
349  * 16    1/29/99 5:07p Dave
350  * Fixed multiplayer stuff. Put in multiplayer support for rapid fire
351  * missiles.
352  * 
353  * 15    1/29/99 2:25p Andsager
354  * Added turret_swarm_missiles
355  * 
356  * 14    1/27/99 9:56a Dave
357  * Temporary checkin of beam weapons for Dan to make cool sounds.
358  * 
359  * 13    1/24/99 11:37p Dave
360  * First full rev of beam weapons. Very customizable. Removed some bogus
361  * Int3()'s in low level net code.
362  * 
363  * 12    1/21/99 10:44a Dave
364  * More beam weapon stuff. Put in warmdown time.
365  * 
366  * 11    1/12/99 5:45p Dave
367  * Moved weapon pipeline in multiplayer to almost exclusively client side.
368  * Very good results. Bandwidth goes down, playability goes up for crappy
369  * connections. Fixed object update problem for ship subsystems.
370  * 
371  * 10    1/08/99 2:08p Dave
372  * Fixed software rendering for pofview. Super early support for AWACS and
373  * beam weapons.
374  * 
375  * 9     12/23/98 2:53p Andsager
376  * Added ship activation and gas collection subsystems, removed bridge
377  * 
378  * 8     11/12/98 12:13a Dave
379  * Tidied code up for multiplayer test. Put in network support for flak
380  * guns.
381  * 
382  * 7     11/05/98 5:55p Dave
383  * Big pass at reducing #includes
384  * 
385  * 6     10/26/98 9:42a Dave
386  * Early flak gun support.
387  * 
388  * 5     10/23/98 3:51p Dave
389  * Full support for tstrings.tbl and foreign languages. All that remains
390  * is to make it active in Fred.
391  * 
392  * 4     10/20/98 1:39p Andsager
393  * Make so sparks follow animated ship submodels.  Modify
394  * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
395  * submodel_num.  Add submodel_num to multiplayer hit packet.
396  * 
397  * 3     10/13/98 9:29a Dave
398  * Started neatening up freespace.h. Many variables renamed and
399  * reorganized. Added AlphaColors.[h,cpp]
400  * 
401  * 2     10/07/98 10:53a Dave
402  * Initial checkin.
403  * 
404  * 1     10/07/98 10:51a Dave
405  * 
406  * 
407  * $NoKeywords: $
408  */
409
410 // This module contains the actual AI code that does interesting stuff
411 // to objects.   The code in Ai.cpp is just for bookeeping, allocating
412 // ai slots and linking them to ships.
413
414 #include "pstypes.h"
415 #include "fix.h"
416 #include "linklist.h"
417 #include "object.h"
418 #include "physics.h"
419 #include "vecmat.h"
420 #include "ship.h"
421 #include "model.h"
422 #include "2d.h"
423 #include "3d.h"
424 #include "ai.h"
425 #include "floating.h"
426 #include "player.h"
427 #include "freespace.h"
428 #include "weapon.h"
429 #include "missiongoals.h"
430 #include "missionlog.h"
431 #include "timer.h"
432 #include "sound.h"
433 #include "aigoals.h"
434 #include "gamesnd.h"
435 #include "hudmessage.h"
436 #include "missionmessage.h"
437 #include "cmeasure.h"
438 #include "staticrand.h"
439 #include "multimsgs.h"
440 #include "afterburner.h"
441 #include "hudets.h"
442 #include "shipfx.h"
443 #include "shiphit.h"
444 #include "aibig.h"
445 #include "multiutil.h"
446 #include "hud.h"
447 #include "objcollide.h"
448 #include "asteroid.h"
449 #include "hudlock.h"
450 #include "missiontraining.h"
451 #include "gamesequence.h"
452 #include "joy_ff.h"
453 #include "localize.h"
454 #include "flak.h"
455 #include "beam.h"
456 #include "multi.h"
457 #include "swarm.h"
458 #include "multi_team.h"
459 #include "awacs.h"
460 #include "fvi.h"
461
462 #ifndef PLAT_UNIX
463 #pragma optimize("", off)
464 #pragma auto_inline(off)
465 #endif
466
467 #define UNINITIALIZED_VALUE     -99999.9f
468
469 #define INSTRUCTOR_SHIP_NAME NOX("instructor")
470
471 #define AICODE_SMALL_MAGNITUDE  0.001f          // cosider a vector NULL if mag is less than this
472
473 #define NEXT_REARM_TIMESTAMP (60*1000)                  //      Ships will re-request rearm, typically, after this long.
474
475 #define BEAM_NEBULA_RANGE_REDUCE_FACTOR         0.8
476
477 // AIM_CHASE submode defines
478 // SM_STEALTH_FIND
479 #define SM_SF_AHEAD             0
480 #define SM_SF_BEHIND    1
481 #define SM_SF_BAIL              2
482
483 // SM_STEALTH_SWEEP
484 #define SM_SS_SET_GOAL  -1
485 #define SM_SS_BOX0              0
486 #define SM_SS_LR                        1
487 #define SM_SS_UL                        2
488 #define SM_SS_BOX1              3
489 #define SM_SS_UR                        4
490 #define SM_SS_LL                        5
491 #define SM_SS_BOX2              6
492 #define SM_SS_DONE              7
493
494 //XSTR:OFF
495
496 char *Mode_text[MAX_AI_BEHAVIORS] = {
497         "CHASE",
498         "EVADE",
499         "GET_BEHIND",
500         "CHASE_LONG",
501         "SQUIGGLE",
502         "GUARD",
503         "AVOID",
504         "WAYPOINTS",
505         "DOCK",
506         "NONE",
507         "BIGSHIP",
508         "PATH",
509         "BE_REARMED",
510         "SAFETY",
511         "EV_WEAPON",
512         "STRAFE",
513         "PLAY_DEAD",
514         "BAY_EMERGE",
515         "BAY_DEPART",
516         "SENTRYGUN",
517         "WARP_OUT",
518 };
519
520 //      Submode text is only valid for CHASE mode.
521 char *Submode_text[] = {
522 "undefined",
523 "CONT_TURN",
524 "ATTACK   ",
525 "E_SQUIG  ",
526 "E_BRAKE  ",
527 "EVADE    ",
528 "SUP_ATTAK",
529 "AVOID    ",
530 "BEHIND   ",
531 "GET_AWAY ",
532 "E_WEAPON ",
533 "FLY_AWAY ",
534 "ATK_4EVER",
535 "STLTH_FND",
536 "STLTH_SWP",
537 "BIG_APPR",
538 "BIG_CIRC",
539 "BIG_PARL"
540 };
541
542 char *Strafe_submode_text[5] = {
543 "ATTACK",
544 "AVOID",
545 "RETREAT1",
546 "RETREAT2",
547 "POSITION"
548 };
549 //XSTR:ON
550
551 /*
552 //      Used for global ignore of objects.  If an object appears in the Ignore_objects array,
553 //      no one will attack it.
554 #define MAX_IGNORE_OBJECTS      16
555 typedef struct {
556         int     objnum;
557         int     signature;
558 } ignore_object;
559
560 ignore_object   Ignore_objects[MAX_IGNORE_OBJECTS];
561 */
562
563 typedef struct eval_enemy_obj_struct {
564         int                     turret_parent_objnum;                   // parent of turret
565         float                   weapon_travel_dist;                             // max targeting range of turret weapon
566         int                     enemy_team_mask;
567         int                     weapon_system_ok;                                       // is the weapon subsystem of turret ship ok
568         int                     big_only_flag;                                          // turret fires only at big and huge ships
569         vector          *tpos;
570         vector          *tvec;
571         ship_subsys *turret_subsys;
572         int                     current_enemy;
573
574
575         float                   nearest_attacker_dist;                  // nearest ship 
576         int                     nearest_attacker_objnum;
577
578         float                   nearest_homing_bomb_dist;               // nearest homing bomb
579         int                     nearest_homing_bomb_objnum;
580
581         float                   nearest_bomb_dist;                              // nearest non-homing bomb
582         int                     nearest_bomb_objnum;
583
584         float                   nearest_dist;                                           // nearest ship attacking this turret
585         int                     nearest_objnum;
586 }       eval_enemy_obj_struct;
587
588
589 control_info    AI_ci;
590
591 object *Pl_objp;
592 object *En_objp;
593
594 waypoint_list Waypoint_lists[MAX_WAYPOINT_LISTS];
595
596 // How close a turret has to be point at its target before it
597 // can fire.  If the dot of the gun normal and the vector from gun
598 // to target is greater than this, the turret fires.  The smaller
599 // the sloppier the shooting.
600 #define AICODE_TURRET_DUMBFIRE_ANGLE            (0.8f)  
601 #define AICODE_TURRET_HEATSEEK_ANGLE            (0.7f)  
602 #define AICODE_TURRET_MAX_TIME_IN_RANGE (5.0f)
603
604 #define REARM_SOUND_DELAY               (3*F1_0)                //      Amount of time to delay rearm/repair after mode start
605 #define REARM_BREAKOFF_DELAY    (3*F1_0)                //      Amount of time to wait after fully rearmed to breakoff.
606
607 #define MIN_DIST_TO_WAYPOINT_GOAL       5.0f
608 #define MAX_GUARD_DIST                                  250.0f
609 #define BIG_GUARD_RADIUS                                500.0f
610
611 #define MAX_EVADE_TIME                  (15 * 1000)     //      Max time to evade a weapon.
612
613 // defines for repair ship stuff.
614 #define MAX_REPAIR_SPEED                        25.0f
615 #define MAX_UNDOCK_ABORT_SPEED  2.0f
616
617 // defines for EMP effect stuff
618 #define MAX_EMP_INACCURACY              50.0f
619
620 // defines for stealth
621 #define MAX_STEALTH_INACCURACY  50.0f           // at max view dist
622 #define STEALTH_MAX_VIEW_DIST   400             // dist at which 1) stealth no longer visible 2) firing inaccuracy is greatest
623 #define STEALTH_VIEW_CONE_DOT   0.707           // (half angle of 45 degrees)
624
625
626 ai_class        Ai_classes[MAX_AI_CLASSES];
627 int     Ai_firing_enabled = 1;
628 int     Num_ai_classes;
629
630 int     AI_FrameCount = 0;
631 int     Ship_info_inited = 0;
632 int     AI_watch_object = 0; // Debugging, object to spew debug info for.
633 int     Num_waypoint_lists = 0;
634 int     Mission_all_attack = 0;                                 //      !0 means all teams attack all teams.
635
636 char *Skill_level_names(int level, int translate)
637 {
638         char *str = NULL;
639
640         #if NUM_SKILL_LEVELS != 5
641         #error Number of skill levels is wrong!
642         #endif
643
644         if(translate){
645                 switch( level ) {
646                 case 0:
647                         str = XSTR("Very Easy", 469);
648                         break;
649                 case 1:
650                         str = XSTR("Easy", 470);
651                         break;
652                 case 2:
653                         str = XSTR("Medium", 471);
654                         break;
655                 case 3:
656                         str = XSTR("Hard", 472);
657                         break;
658                 case 4:
659                         str = XSTR("Insane", 473);
660                         break;
661                 default:        
662                         Int3();
663                 }
664         } else {
665                 switch( level ) {
666                 case 0:
667                         str = NOX("Very Easy");
668                         break;
669                 case 1:
670                         str = NOX("Easy");
671                         break;
672                 case 2:
673                         str = NOX("Medium");
674                         break;
675                 case 3:
676                         str = NOX("Hard");
677                         break;
678                 case 4:
679                         str = NOX("Insane");
680                         break;
681                 default:        
682                         Int3();
683                 }
684         }
685
686         return str;
687 }
688
689 #define DELAY_TARGET_TIME       (12*1000)               //      time in milliseconds until a ship can target a new enemy after an order.
690
691 //      Make enemy ships turn more slowly at lower skill levels.
692 float   Turn_time_skill_level_scale[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.6f, 1.3f, 1.0f};
693
694 //      Maximum number of simultaneous homing weapons on player based on skill level.
695 int     Max_allowed_player_homers[NUM_SKILL_LEVELS] = {2, 3, 4, 7, 99};
696
697 //      Number of ships that can attack another ship at a given skill level.
698 int     Skill_level_max_attackers[NUM_SKILL_LEVELS] = {2, 3, 4, 5, 99};
699
700 //      How long until next predict position.
701 fix Skill_level_delay[NUM_SKILL_LEVELS] = {2*F1_0, 3*F1_0/2, 4*F1_0/3, F1_0/2, 0};
702
703 //      AI ships link primary weapons if energy levels greater than the following amounts:
704 float   Link_energy_levels_always[NUM_SKILL_LEVELS] = {100.0f, 80.0f, 60.0f, 40.0f, 20.0f};     //      always link
705 float   Link_energy_levels_maybe[NUM_SKILL_LEVELS] = {90.0f, 60.0f, 40.0f, 20.0f, 10.0f};       //      link if hull strength low
706
707 //      Seconds to add to time it takes to get enemy in range.  Only for player's enemies.
708 float   In_range_time[NUM_SKILL_LEVELS] = {2.0f, 1.4f, 0.75f, 0.0f, -1.0f};
709
710 //      No matter what, a random unit vector gets scaled by this amount in firing at an enemy.
711 //      Note that for shorter in-range times, these values get scaled, so a value of 0.5f is meaningful.
712 float   Aiming_error[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.3f, 0.7f, 0.2f};
713
714 //      Chance a countermeasure will be fired based on skill level.
715 float Cmeasure_fire_chance[NUM_SKILL_LEVELS] = {0.2f, 0.3f, 0.5f, 0.9f, 1.1f};  //      Note, this gets scaled by ai_class
716
717 float Shield_manage_delays[NUM_SKILL_LEVELS] = {5.0f, 4.0f, 2.5f, 1.2f, 0.1f};
718
719 // accuracy we feed into the beam weapons based upon skill system
720 // float Beam_accuracy[NUM_SKILL_LEVELS] = {2.0f, 1.5f, 1.0f, 0.7f, 0.4f};
721
722 extern float Ship_fire_delay_scale_hostile[NUM_SKILL_LEVELS];
723 extern float Ship_fire_delay_scale_friendly[NUM_SKILL_LEVELS];
724
725 pnode           Path_points[MAX_PATH_POINTS];
726 pnode           *Ppfp;                  //      Free pointer in path points.
727
728 float   AI_frametime;
729
730 char *Ai_class_names[MAX_AI_CLASSES];
731
732 // global for rearm status for teams
733 int Ai_friendly_rearm_timestamp, Ai_hostile_rearm_timestamp, Ai_neutral_rearm_timestamp, Ai_traitor_rearm_timestamp, Ai_unknown_rearm_timestamp;
734
735 // globals for dealing with when to fire huge secondary weapons
736 #define MAX_HUGE_SECONDARY_INFO 10
737
738 typedef struct {
739         int team;
740         int weapon_index;
741         int max_fire_count;
742         char    *shipname;
743 } huge_fire_info;
744
745 huge_fire_info Ai_huge_fire_info[MAX_HUGE_SECONDARY_INFO];
746
747 int Ai_last_arrive_path;        // index of ship_bay path used by last arrival from a fighter bay
748
749 // forward declarations
750 int     ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index);
751 void    create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count=1);
752 void    copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt=-1);
753
754 // ai_set_rearm_status takes a team (friendly, hostile, neutral) and a time.  This function
755 // sets the timestamp used to tell is it is a good time for this team to rearm.  Once the timestamp
756 // is no longer valid, then rearming is not a "good time"
757 // not safe.  Called from sexpression code.
758 void ai_set_rearm_status( int team, int time )
759 {
760         Assert( time >= 0 );
761
762         switch (team) {
763         case TEAM_FRIENDLY:
764                 Ai_friendly_rearm_timestamp = timestamp( time * 1000 );
765                 break;
766         case TEAM_HOSTILE:
767                 Ai_hostile_rearm_timestamp = timestamp( time * 1000 );
768                 break;
769         case TEAM_NEUTRAL:
770                 Ai_neutral_rearm_timestamp = timestamp( time * 1000 );
771                 break;
772         case TEAM_TRAITOR:
773                 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
774                 break;
775         case TEAM_UNKNOWN:
776                 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
777                 break;
778         default:
779                 Int3();
780                 break;
781         }
782 }
783
784 // int ai_good_time_to_rearm returns true(1) or false(0) if it is "safe" for the given
785 // object to rearm.  "safe" is currently defined by the mission designer using the good/bad
786 // time to rearm sexpressions.  This status is currently team based.  This function could
787 // be easily expended to further the definition of "safe"
788 int ai_good_time_to_rearm( object *objp )
789 {
790         int team, status;
791
792         Assert(objp->type == OBJ_SHIP);
793         team = Ships[objp->instance].team;
794         status = 0;
795
796         switch(team) {
797         case TEAM_FRIENDLY:
798                 status = timestamp_valid(Ai_friendly_rearm_timestamp);
799                 break;
800         case TEAM_HOSTILE:
801                 status = timestamp_valid(Ai_hostile_rearm_timestamp);
802                 break;
803         case TEAM_NEUTRAL:
804                 status = timestamp_valid(Ai_neutral_rearm_timestamp);
805                 break;
806         case TEAM_TRAITOR:
807                 status = timestamp_valid(Ai_traitor_rearm_timestamp);
808                 break;
809         case TEAM_UNKNOWN:
810                 status = timestamp_valid(Ai_unknown_rearm_timestamp);
811                 break;
812         default:
813                 Int3();
814                 break;
815         }
816
817         return status;
818 }
819
820 // functions to deal with letting the ai know about good times to fire powerful secondary
821 // weapons.
822
823 // this function is entry point from sexpression code to set internal data for use by ai code.
824 void ai_good_secondary_time( int team, int weapon_index, int max_fire_count, char *shipname )
825 {
826         int i, index;
827
828         // find an open slot to put this data
829         for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
830                 if ( Ai_huge_fire_info[i].weapon_index == -1 )
831                         break;
832         }
833
834         Assert( i < MAX_HUGE_SECONDARY_INFO );                  // we've run out of room
835
836         Ai_huge_fire_info[i].weapon_index = weapon_index;
837         Ai_huge_fire_info[i].team = team;
838         Ai_huge_fire_info[i].max_fire_count = max_fire_count;
839
840         Ai_huge_fire_info[i].shipname = ai_get_goal_ship_name( shipname, &index );
841 }
842
843 // function called internally to the ai code to tell whether or not weapon_num can be fired
844 // from firer_objp at target_objp.  This function will resolve the team for the firer.
845 // returns:
846 //              -1  -- when conditions don't allow firer to fire weapon_num on target_objp
847 //              >=0 -- when conditions allow firer to fire.  Return value is max number of weapon_nums
848 //           which can be fired on target_objp
849 int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
850 {
851         int i, firer_team, target_signature;
852         ship *firer_ship;
853         huge_fire_info *hfi = NULL;
854
855         Assert( firer_objp->type == OBJ_SHIP );
856         firer_ship = &Ships[firer_objp->instance];
857         firer_team = firer_ship->team;
858
859         // get target object's signature and try to find it in the list.
860         target_signature = target_objp->signature;
861         for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
862                 int ship_index, signature;
863
864                 hfi = &Ai_huge_fire_info[i];
865                 if ( hfi->weapon_index == -1 )
866                         continue;
867
868                 ship_index = ship_name_lookup( hfi->shipname );
869                 if ( ship_index == -1 )
870                         continue;
871
872                 signature = Objects[Ships[ship_index].objnum].signature;
873
874                 // sigatures, weapon_index, and team must match
875                 if ( (signature == target_signature) && (hfi->weapon_index == weapon_num) && (hfi->team == firer_team) )
876                         break;
877         }
878
879         // return -1 if not found
880         if ( i == MAX_HUGE_SECONDARY_INFO )
881                 return -1;
882
883         // otherwise, we can return the max number of weapons we can fire against target_objps
884
885         return hfi->max_fire_count;
886 }
887
888 // function to clear out secondary firing infomration between levels
889 void ai_init_secondary_info()
890 {
891         int i;
892
893         // clear out the data for dealing with when ai ships can fire huge secondary weapons
894         for (i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
895                 Ai_huge_fire_info[i].weapon_index = -1;
896                 Ai_huge_fire_info[i].team = -1;
897                 Ai_huge_fire_info[i].max_fire_count = -1;
898                 Ai_huge_fire_info[i].shipname = NULL;
899         }
900 }
901
902
903 //      Garbage collect the Path_points buffer.
904 //      Scans all objects, looking for used Path_points records.
905 //      Compresses Path_points buffer, updating aip->path_start and aip->path_cur indices.
906 //      Updates Ppfp to point to first free record.
907 //      This function is fairly fast.  Its worst-case running time is proportional to
908 //      3*MAX_PATH_POINTS + MAX_OBJECTS
909 //      Things to do to optimize this function:
910 //              1. if (t != 0) xlt++; can be replaced by xlt += t; assuming t can only be 0 or 1.
911 //              2. When pp_xlate is getting stuffed the first time, note highest index and use that 
912 //                      instead of MAX_PATH_POINTS in following two for loops.
913 void garbage_collect_path_points()
914 {
915         int     i;
916         int     pp_xlate[MAX_PATH_POINTS];
917         object  *A;
918         ship_obj        *so;
919
920         //      Scan all objects and create Path_points xlate table.
921         for (i=0; i<MAX_PATH_POINTS; i++)
922                 pp_xlate[i] = 0;
923
924         //      in pp_xlate, mark all used Path_point records
925         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
926                 A = &Objects[so->objnum];
927                 ship    *shipp = &Ships[A->instance];
928                 if (shipp->ai_index != -1) {
929                         ai_info *aip = &Ai_info[shipp->ai_index];
930
931                         if ((aip->path_length > 0) && (aip->path_start > -1)) {
932
933                                 for (int i=aip->path_start; i<aip->path_start + aip->path_length; i++) {
934                                         Assert(pp_xlate[i] == 0);       //      If this is not 0, then two paths use this point!
935                                         pp_xlate[i] = 1;
936                                 }
937                         }
938                 }
939         }
940
941         //      Now, stuff xlate index in pp_xlate.  This is the number to translate any path_start
942         //      or path_cur index to.
943         int     xlt = 0;
944         for (i=0; i<MAX_PATH_POINTS; i++) {
945                 int     t = pp_xlate[i];
946
947                 pp_xlate[i] = xlt;
948                 if (t != 0)
949                         xlt++;
950         }
951         
952         //      Update global Path_points free pointer.
953         Ppfp = &Path_points[xlt];
954
955         //      Now, using pp_xlate, fixup all aip->path_cur and aip->path_start indices
956         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
957                 A = &Objects[so->objnum];
958                 ship    *shipp = &Ships[A->instance];
959                 if (shipp->ai_index != -1) {
960                         ai_info *aip = &Ai_info[shipp->ai_index];
961
962                         if ((aip->path_length > 0) && (aip->path_start > -1)) {
963                                 Assert(aip->path_start < MAX_PATH_POINTS);
964                                 aip->path_start = pp_xlate[aip->path_start];
965
966                                 Assert((aip->path_cur >= 0) && (aip->path_cur < MAX_PATH_POINTS));
967                                 aip->path_cur = pp_xlate[aip->path_cur];
968                         }
969                 }
970         }
971
972         //      Now, compress the buffer.
973         for (i=0; i<MAX_PATH_POINTS; i++)
974                 if (i != pp_xlate[i])
975                         Path_points[pp_xlate[i]] = Path_points[i];
976
977 }
978
979 //      Hash two values together, return result.
980 //      Hash function: curval shifted right circular by one, newval xored in.
981 int hash(unsigned int curval, int newval)
982 {
983         int     addval = curval & 1;
984
985         curval >>= 1;
986         if (addval)
987                 curval |= 0x80000000;
988         curval ^= newval;
989
990         return curval;
991 }
992
993 //      Hash some information in an object together.
994 //      On 2/20/97, the information is position and orientation.
995 int create_object_hash(object *objp)
996 {
997         int     *ip;
998         unsigned int    hashval = 0;
999         int     i;
1000
1001         ip = (int *) &objp->orient;
1002
1003         for (i=0; i<9; i++) {
1004                 hashval = hash(hashval, *ip);
1005                 ip++;
1006         }
1007
1008         ip = (int *) &objp->pos;
1009
1010         for (i=0; i<3; i++) {
1011                 hashval = hash(hashval, *ip);
1012                 ip++;
1013         }
1014
1015         return hashval;
1016 }
1017
1018 //      Stuff a list of NUM_SKILL_LEVELS floats at *plist.
1019 void parse_float_list(float *plist)
1020 {
1021         int     i;
1022
1023         for (i=0; i<NUM_SKILL_LEVELS; i++) {
1024                 stuff_float(&plist[i]);
1025         }
1026 }
1027
1028 void parse_ai_class()
1029 {
1030         ai_class        *aicp = &Ai_classes[Num_ai_classes];
1031
1032         required_string("$Name:");
1033         stuff_string(aicp->name, F_NAME, NULL);
1034
1035         Ai_class_names[Num_ai_classes] = aicp->name;
1036
1037         required_string("$accuracy:");
1038         parse_float_list(aicp->ai_accuracy);
1039
1040         required_string("$evasion:");
1041         parse_float_list(aicp->ai_evasion);
1042
1043         required_string("$courage:");
1044         parse_float_list(aicp->ai_courage);
1045
1046         required_string("$patience:");
1047         parse_float_list(aicp->ai_patience);
1048 }
1049
1050 void parse_aitbl()
1051 {
1052         // open localization
1053         lcl_ext_open();
1054
1055         read_file_text("ai.tbl");
1056
1057         reset_parse();
1058
1059         Num_ai_classes = 0;
1060
1061         required_string("#AI Classes");
1062
1063         while (required_string_either("#End", "$Name:")) {
1064                 Assert( Num_ai_classes < MAX_AI_CLASSES);
1065
1066                 parse_ai_class();
1067
1068                 Num_ai_classes++;
1069         }
1070
1071         // close localization
1072         lcl_ext_close();
1073 }
1074
1075 LOCAL int ai_inited = 0;
1076
1077 //========================= BOOK-KEEPING FUNCTIONS =======================
1078
1079 // Called once at game start-up
1080 void ai_init()
1081 {
1082         if ( !ai_inited )       {
1083                 // Do the first time initialization stuff here
1084                 int     rval;
1085
1086                 if ((rval = setjmp(parse_abort)) != 0) {
1087                         Error(LOCATION, "Error parsing 'ai.tbl'\r\nError code = %i.\r\n", rval);
1088                 } else {                        
1089                         parse_aitbl();                  
1090                 }
1091
1092                 ai_inited = 1;
1093         }
1094
1095         init_semirand();
1096         
1097         ai_level_init();
1098 }
1099
1100 // this inits the ai.  You should be able to call this between
1101 // levels to reset everything.
1102 void ai_level_init()
1103 {
1104         int i;
1105  
1106         // Do the stuff to reset all ai stuff here
1107         for (i=0; i<MAX_AI_INFO ; i++) {
1108                 Ai_info[i].shipnum = -1;
1109         }
1110         Ai_goal_signature = 0;
1111         Ai_friendly_rearm_timestamp = timestamp(-1);
1112         Ai_hostile_rearm_timestamp = timestamp(-1);
1113         Ai_neutral_rearm_timestamp = timestamp(-1);
1114         Ai_traitor_rearm_timestamp = timestamp(-1);
1115
1116         // clear out the stuff needed for AI firing powerful secondary weapons
1117         ai_init_secondary_info();
1118
1119         Ai_last_arrive_path=0;
1120 }
1121
1122 // BEGIN STEALTH
1123 // -----------------------------------------------------------------------------
1124 // Check if object is a stealth ship
1125 int is_object_stealth_ship(object* objp)
1126 {
1127         if (objp->type == OBJ_SHIP) {
1128                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_STEALTH) {
1129                         return 1;
1130                 }
1131         }
1132
1133         // not stealth ship
1134         return 0;
1135 }
1136
1137 // -----------------------------------------------------------------------------
1138 // Init necessary ai info for new stealth target
1139 void init_ai_stealth_info(ai_info *aip, object *stealth_objp)
1140 {
1141         Assert(is_object_stealth_ship(stealth_objp));
1142
1143         // set necessary ai info for new stealth target
1144         aip->stealth_last_pos = stealth_objp->pos;
1145         aip->stealth_velocity = stealth_objp->phys_info.vel;
1146         aip->stealth_last_visible_stamp = timestamp();
1147 }
1148
1149 // -----------------------------------------------------------------------------
1150 // Check whether Pl_objp can see a stealth ship object
1151 #define STEALTH_INVISIBLE                       0
1152 #define STEALTH_VISIBLE                         1
1153 #define STEALTH_FULLY_TARGETABLE        2
1154
1155 float get_skill_stealth_dist_scaler()
1156 {
1157         // return dist scaler based on skill level
1158         switch (Game_skill_level) {
1159         case 0: // very easy
1160                 return 0.65f;
1161
1162         case 1: // easy
1163                 return 0.9f;
1164
1165         case 2: // medium
1166                 return 1.0f;
1167
1168         case 3: // hard
1169                 return 1.1f;
1170
1171         case 4: // insane
1172                 return 1.3f;
1173
1174         default:
1175                 Int3();
1176         }
1177
1178         return 1.0f;
1179 }
1180
1181 float get_skill_stealth_dot_scaler()
1182 {
1183         // return multiplier on dot based on skill level
1184         switch (Game_skill_level) {
1185         case 0: // very easy
1186                 return 1.3f;
1187
1188         case 1: // easy
1189                 return 1.1f;
1190
1191         case 2: // medium
1192                 return 1.0f;
1193
1194         case 3: // hard
1195                 return 0.9f;
1196
1197         case 4: // insane
1198                 return 0.7f;
1199
1200         default:
1201                 Int3();
1202         }
1203
1204         return 1.0f;
1205 }
1206
1207 int ai_is_stealth_visible(object *viewer_objp, object *stealth_objp)
1208 {
1209         ship *shipp;
1210         vector vec_to_stealth;
1211         float dot_to_stealth, dist_to_stealth, max_stealth_dist;
1212
1213         Assert(stealth_objp->type == OBJ_SHIP);
1214         shipp = &Ships[stealth_objp->instance];
1215         Assert(viewer_objp->type == OBJ_SHIP);
1216
1217         // check if stealth ship
1218         Assert(Ship_info[shipp->ship_info_index].flags & SIF_STEALTH);
1219
1220         // check if in neb and below awac level for visible
1221         if ( !ship_is_visible_by_team(stealth_objp->instance, Ships[viewer_objp->instance].team) ) {
1222                 vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &viewer_objp->pos);
1223                 dist_to_stealth = vm_vec_mag_quick(&vec_to_stealth);
1224                 dot_to_stealth = vm_vec_dotprod(&viewer_objp->orient.fvec, &vec_to_stealth) / dist_to_stealth;
1225
1226                 // get max dist at which stealth is visible
1227                 max_stealth_dist = get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST;
1228
1229                 // now check if within view frustrum
1230                 float needed_dot_to_stealth;
1231                 if (dist_to_stealth < 100) {
1232                         needed_dot_to_stealth = 0.0f;
1233                 } else {
1234                         needed_dot_to_stealth = get_skill_stealth_dot_scaler() * float(STEALTH_VIEW_CONE_DOT) * (dist_to_stealth / max_stealth_dist);
1235                 }
1236                 if (dot_to_stealth > needed_dot_to_stealth) {
1237                         if (dist_to_stealth < max_stealth_dist) {
1238                                 return STEALTH_VISIBLE;
1239                         }
1240                 }
1241
1242                 // not within frustrum
1243                 return STEALTH_INVISIBLE;
1244         }
1245
1246         // visible by awacs level
1247         return STEALTH_FULLY_TARGETABLE;
1248 }
1249
1250 // END STEALTH
1251
1252 //      Compute dot product of direction vector and forward vector.
1253 //      Direction vector is vector from one object to other object.
1254 //      Forward vector is the forward vector of the ship.
1255 //      If from_dot == NULL, don't fill it in.
1256 float compute_dots(object *objp, object *other_objp, float *to_dot, float *from_dot)
1257 {
1258         vector  v2o;
1259         float           dist;
1260
1261         dist = vm_vec_normalized_dir(&v2o, &other_objp->pos, &objp->pos);
1262
1263         *to_dot = vm_vec_dot(&objp->orient.fvec, &v2o);
1264
1265         if (from_dot != NULL)
1266                 *from_dot = - vm_vec_dot(&other_objp->orient.fvec, &v2o);
1267
1268         return dist;
1269 }
1270
1271 // -----------------------------------------------------------------------------
1272 // update estimated stealth info
1273 // this is a "cheat" update
1274 // error increases with time not seen, true distance away, dot to enemey
1275 // this is done only if we can not see the stealth target
1276 // need to infer its position either by weapon fire pos or last know pos
1277 void update_ai_stealth_info_with_error(ai_info *aip/*, int no_error*/)
1278 {
1279         object *ship;
1280         object *stealth_objp;
1281         /*
1282         float error_time_mult, error_dist_mult, error_dot_mult, error_mult;
1283         float pos_error, vel_error;
1284         vector error_vec, vec_to_stealth;
1285         float dist_to_stealth, dot_to_stealth;
1286         float delta_time, delta_capped;
1287         */
1288
1289         // make sure I am targeting a stealth ship
1290         Assert( is_object_stealth_ship(&Objects[aip->target_objnum]) );
1291         stealth_objp = &Objects[aip->target_objnum];
1292
1293         // my_ship
1294         ship = &Objects[Ships[aip->shipnum].objnum];
1295
1296         // if update is due to weapon fire, get exact stealth position
1297 //      if (no_error) {
1298         aip->stealth_last_pos = stealth_objp->pos;
1299         aip->stealth_velocity = stealth_objp->phys_info.vel;
1300         aip->stealth_last_visible_stamp = timestamp();
1301 //              return;
1302 //      }
1303 /*
1304         // get time since last seen
1305         delta_time = 0.001f * (timestamp() - aip->stealth_last_visible_stamp);
1306
1307         // we don't want our "cheat" guess to more off than what we would get from extrapolating from last visible
1308         // only update if stealth info is "old"
1309         if ( (delta_time) < 0.5 ) {
1310                 return;
1311         }
1312
1313         // find vec_to_stealth and dist
1314         vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &ship->pos);
1315         dist_to_stealth = vm_vec_normalize_quick(&vec_to_stealth);
1316         dot_to_stealth = vm_vec_dotprod(&vec_to_stealth, &ship->orient.fvec);
1317
1318         // put cap on time
1319         delta_capped = delta_time;
1320         if (delta_time > 5.0) {
1321                 delta_capped = 5.0f;
1322         }
1323
1324         // erorr_time_mult (for 0-5) -> (1-6)
1325         error_time_mult = (1.0f + delta_capped);
1326
1327         // error_dot_mult (-1 to 1) -> (1-3)
1328         error_dot_mult = (2 - dot_to_stealth);
1329
1330         // error_dist_mult (0-1000+) -> (1-4)
1331         error_dist_mult = dist_to_stealth * 4.0f * 0.001f;
1332         if (error_dist_mult < 1) {
1333                 error_dist_mult = 1.0f;
1334         } else if (error_dist_mult > 4) {
1335                 error_dist_mult = 4.0f;
1336         }
1337
1338         // multiply error out
1339         error_mult = error_time_mult * error_dot_mult * error_dist_mult;
1340
1341         float base_pos_error = 10;
1342         float base_vel_error = 2;
1343
1344         // find the position and velocity error magnitude;
1345         pos_error = base_pos_error * error_mult;
1346         vel_error = base_vel_error * error_mult;
1347
1348         // get an error that changes slowly over time
1349         static_randvec( ((int)aip ^ (Missiontime >> 18)) & 7, &error_vec);
1350         vm_vec_zero(&error_vec);
1351
1352         // update pos and vel with error
1353         vm_vec_scale_add(&aip->stealth_velocity, &stealth_objp->phys_info.vel, &error_vec, vel_error);
1354
1355         // revise last "known" position to arrive at last pos with given error
1356         vm_vec_scale_add(&aip->stealth_last_pos, &stealth_objp->pos, &error_vec, pos_error);
1357         vm_vec_scale_add2(&aip->stealth_last_pos, &aip->stealth_velocity, -(0.001f * delta_time));
1358         */
1359 }
1360
1361 //      Update danger_weapon_objnum and signature in ai_info to say this weapon is to be avoided.
1362 void ai_update_danger_weapon(int attacked_objnum, int weapon_objnum)
1363 {
1364         object  *objp, *weapon_objp;
1365         ai_info *aip;
1366         float           old_dist, new_dist;
1367         float           old_dot, new_dot;
1368         object  *old_weapon_objp = NULL;
1369
1370         if ((attacked_objnum == -1) || (weapon_objnum == -1)) {
1371                 return;
1372         }
1373
1374         objp = &Objects[attacked_objnum];
1375
1376         // AL 2-24-98: If this isn't a ship, we don't need to worry about updating weapon_objnum (ie it would be
1377         //                                      an asteroid or bomb).
1378         if ( objp->type != OBJ_SHIP ) {
1379                 return;
1380         }
1381
1382         weapon_objp = &Objects[weapon_objnum];
1383
1384         aip = &Ai_info[Ships[objp->instance].ai_index];
1385
1386         // if my taraget is a stealth ship and is not visible
1387         if (aip->target_objnum >= 0) {
1388                 if ( is_object_stealth_ship(&Objects[aip->target_objnum]) ) {
1389                         if ( ai_is_stealth_visible(objp, &Objects[aip->target_objnum]) == STEALTH_INVISIBLE ) {
1390                                 // and the weapon is coming from that stealth ship
1391                                 if (weapon_objp->parent == aip->target_objnum) {
1392                                         // update my position estimate for stealth ship
1393                                         update_ai_stealth_info_with_error(aip/*, 1*/);
1394                                 }
1395                         }
1396                 }
1397         }
1398
1399         if (aip->danger_weapon_objnum != -1) {
1400                 old_weapon_objp = &Objects[aip->danger_weapon_objnum];
1401                 if ((old_weapon_objp->type == OBJ_WEAPON) && (old_weapon_objp->signature == aip->danger_weapon_signature)) {
1402                         ;
1403                 } else {
1404                         aip->danger_weapon_objnum = -1;
1405                 }
1406         }
1407
1408         new_dist = compute_dots(weapon_objp, objp, &new_dot, NULL);
1409
1410         if (aip->danger_weapon_objnum == -1) {
1411                 if (new_dist < 1500.0f) {
1412                         if (new_dot > 0.5f) {
1413                                 aip->danger_weapon_objnum = weapon_objnum;
1414                                 aip->danger_weapon_signature = weapon_objp->signature;
1415                         }
1416                 }
1417         } else {
1418                 Assert(old_weapon_objp != NULL);
1419                 old_dist = compute_dots(old_weapon_objp, objp, &old_dot, NULL);
1420         
1421                 if (old_dot < 0.5f) {
1422                         aip->danger_weapon_objnum = -1;
1423                         old_dist = 9999.9f;
1424                 }
1425
1426                 if ((new_dot > 0.5f) && (new_dot > old_dot-0.01f)) {
1427                         if (new_dist < old_dist) {
1428                                 aip->danger_weapon_objnum = weapon_objnum;
1429                                 aip->danger_weapon_signature = weapon_objp->signature;
1430                         }
1431                 }
1432         }
1433 }
1434
1435 //      If rvec != NULL, use it to match bank by calling vm_matrix_interpolate.
1436 //      (rvec defaults to NULL)
1437 void ai_turn_towards_vector(vector *dest, object *objp, 
1438                                                                          float frametime, float turn_time, vector *slide_vec, vector *rel_pos, float bank_override, int flags, vector *rvec)
1439 {
1440         //matrix        goal_orient;
1441         matrix  curr_orient;
1442         vector  vel_in, vel_out, desired_fvec, src;
1443         float           delta_time;
1444         physics_info    *pip;
1445         vector  vel_limit, acc_limit;
1446         float           delta_bank;
1447
1448         //      Don't allow a ship to turn if it has no engine strength.
1449         // AL 3-12-98: objp may not always be a ship!
1450         if ( objp->type == OBJ_SHIP ) {
1451                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f)
1452                         return;
1453         }
1454                         
1455         //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));
1456         pip = &objp->phys_info;
1457
1458         vel_in = pip->rotvel;
1459         curr_orient = objp->orient;
1460         delta_time = flFrametime;
1461
1462         Assert(turn_time > 0.0f);
1463         
1464         //      Scale turn_time based on skill level and team.
1465         if (!(flags & AITTV_FAST)){
1466                 if (objp->type == OBJ_SHIP){
1467                         if (Ships[objp->instance].team != Ships[Player_obj->instance].team){
1468                                 turn_time *= Turn_time_skill_level_scale[Game_skill_level];
1469                         }
1470                 }
1471         }
1472
1473         //      Set max turn rate.
1474         vel_limit.x = 2*PI/turn_time;
1475         vel_limit.y = 2*PI/turn_time;
1476         vel_limit.z = 2*PI/turn_time;
1477
1478         //      Set rate at which ship can accelerate to its rotational velocity.
1479         //      For now, weapons just go much faster.
1480         acc_limit = vel_limit;
1481         if (objp->type == OBJ_WEAPON)
1482                 vm_vec_scale(&acc_limit, 8.0f);
1483
1484         src = objp->pos;
1485
1486         if (rel_pos != NULL) {
1487                 vector  gun_point;
1488                 vm_vec_unrotate(&gun_point, rel_pos, &objp->orient);
1489                 vm_vec_add2(&src, &gun_point);
1490         }
1491
1492         vm_vec_normalized_dir(&desired_fvec, dest, &src);
1493
1494         //      Since ship isn't necessarily moving in the direction it's pointing, sometimes it's better
1495         //      to be moving towards goal rather than just pointing.  So, if slide_vec is !NULL, try to
1496         //      make ship move towards goal, not point at goal.
1497         if (slide_vec != NULL) {
1498                 vm_vec_add2(&desired_fvec, slide_vec);
1499                 vm_vec_normalize(&desired_fvec);
1500         }
1501
1502         //      Should be more general case here.  Currently, anything that is not a weapon will bank when it turns.
1503         if (objp->type == OBJ_WEAPON)
1504                 delta_bank = 0.0f;
1505         else if ((bank_override) && (Ships[objp->instance].team & opposing_team_mask(Player_ship->team))) {     //      Theoretically, this will only happen for Shivans.
1506                 delta_bank = bank_override;
1507                 //nprintf(("AI", "%i: %7.3f\n", Framecount, bank_override));
1508         } else {
1509                 delta_bank = vm_vec_dot(&curr_orient.rvec, &objp->last_orient.rvec);
1510                 delta_bank = 100.0f * (1.0f - delta_bank);
1511                 if (vm_vec_dot(&objp->last_orient.fvec, &objp->orient.rvec) < 0.0f)
1512                         delta_bank = -delta_bank;
1513
1514                 //nprintf(("AI", "%s: Frame %i: delta bank = %7.3f\n", Ships[objp->instance].ship_name, Framecount, delta_bank));
1515         }
1516
1517         //      Dave Andsager: The non-indented lines here are debug code to help you track down the problem in the physics
1518         //      that is causing ships to inexplicably rotate very far.  If you hit the Int3(), set the next statement to be
1519         //      the one marked "HERE".  (Do this clicking the cursor there, then right clicking.  Choose the right option.)
1520         //      This will allow you to rerun vm_forward_interpolate() with the values that caused the error.
1521         //      Note, you'll need to enable the Int3() about ten lines below.
1522 #ifndef NDEBUG
1523 vector tvec = objp->orient.fvec;
1524 vector  vel_in_copy;
1525 matrix  objp_orient_copy;
1526
1527 vel_in_copy = vel_in;
1528 objp_orient_copy = objp->orient;
1529
1530 vel_in = vel_in_copy;   //      HERE
1531 objp->orient = objp_orient_copy;
1532 #endif
1533         if (rvec != NULL) {
1534                 matrix  out_orient, goal_orient;
1535
1536                 vm_vector_2_matrix(&goal_orient, &desired_fvec, NULL, rvec);
1537                 vm_matrix_interpolate(&goal_orient, &curr_orient, &vel_in, delta_time, &out_orient, &vel_out, &vel_limit, &acc_limit);
1538                 objp->orient = out_orient;
1539         } else {
1540                 vm_forward_interpolate(&desired_fvec, &curr_orient, &vel_in, delta_time, delta_bank, &objp->orient, &vel_out, &vel_limit, &acc_limit);
1541         }
1542 #ifndef NDEBUG
1543 if (!((objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE))) {
1544         if (delta_time < 0.25f && vm_vec_dot(&objp->orient.fvec, &tvec) < 0.1f)
1545                 Int3(); //      Get Andsager.  A ship has turned too far in one frame.
1546 }
1547 #endif
1548
1549         pip->rotvel = vel_out;
1550 }
1551
1552 void init_ship_info()
1553 {
1554         int     i;
1555
1556         if (Ship_info_inited)
1557                 return;
1558
1559         for (i=0; i<MAX_SHIP_TYPES; i++) {
1560                 Ship_info[i].min_speed = - Ship_info[i].max_rear_vel;
1561                 Ship_info[i].max_accel = Ship_info[i].max_vel.z;
1562         }
1563
1564         Ship_info_inited = 1;
1565
1566 }
1567
1568 //      Set aip->target_objnum to objnum
1569 //      Update aip->previous_target_objnum.
1570 //      If new target (objnum) is different than old target, reset target_time.
1571 int set_target_objnum(ai_info *aip, int objnum)
1572 {
1573 /*
1574         char    old_name[32], new_name[32];
1575
1576         if (!timestamp_elapsed(aip->ok_to_target_timestamp))
1577                 return aip->target_objnum;
1578
1579         if (Player_ship && (Ships[aip->shipnum].team == Player_ship->team)) {
1580                 if (aip->target_objnum == -1)
1581                         strcpy(old_name, "none");
1582                 else
1583                         strcpy(old_name, Ships[Objects[aip->target_objnum].instance].ship_name);
1584
1585                 if (objnum == -1)
1586                         strcpy(new_name, "none");
1587                 else
1588                         strcpy(new_name, Ships[Objects[objnum].instance].ship_name);
1589
1590                 nprintf(("AI", "Ship %s changing target from %s to %s\n", Ships[aip->shipnum].ship_name, old_name, new_name));
1591         }
1592 */
1593
1594         // AL 2-25-97: Ensure that a protected ship isn't being set as a target (for non-players only)
1595         /*
1596         if ( objnum >= 0 ) {
1597                 if ( !(Objects[Ships[aip->shipnum].objnum].flags & OF_PLAYER_SHIP) ) {
1598                         if ( Objects[objnum].flags & OF_PROTECTED ) {
1599                                 // AL 2-26-97: removing Int3() until issue with setting OF_PROTECTED in ai_set_attack_subsystem()
1600                                 //Int3();                                                               // this should not happen
1601                                 return aip->target_objnum;              // don't change targets
1602                         }
1603                 }
1604         }
1605         */
1606
1607         if ((aip != Player_ai) && (!timestamp_elapsed(aip->ok_to_target_timestamp))) {
1608                 return aip->target_objnum;
1609         }
1610
1611         if (aip->target_objnum == objnum) {
1612                 aip->previous_target_objnum = aip->target_objnum;
1613         } else {
1614                 aip->previous_target_objnum = aip->target_objnum;
1615
1616                 // ignore this assert if a multiplayer observer
1617                 if((Game_mode & GM_MULTIPLAYER) && (aip == Player_ai) && (Player_obj->type == OBJ_OBSERVER)){
1618                 } else {
1619                         Assert(objnum != Ships[aip->shipnum].objnum);   //      make sure not targeting self
1620                 }
1621
1622                 // if stealth target, init ai_info for stealth
1623                 if ( (objnum > 0) && is_object_stealth_ship(&Objects[objnum]) ) {
1624                         init_ai_stealth_info(aip, &Objects[objnum]);
1625                 }
1626
1627                 aip->target_objnum = objnum;
1628                 aip->target_time = 0.0f;
1629                 aip->target_signature = Objects[objnum].signature;
1630                 // clear targeted subsystem
1631                 set_targeted_subsys(aip, NULL, -1);
1632         }
1633         
1634         return aip->target_objnum;
1635 }
1636
1637 int ai_select_primary_weapon(object *objp, object *other_objp, int flags);
1638
1639 //      Make new_subsys the targeted subsystem of ship *aip.
1640 ship_subsys *set_targeted_subsys(ai_info *aip, ship_subsys *new_subsys, int parent_objnum)
1641 {
1642         Assert(aip != NULL);
1643
1644         aip->last_subsys_target = aip->targeted_subsys;
1645         aip->targeted_subsys = new_subsys;
1646         aip->targeted_subsys_parent = parent_objnum;
1647
1648         if ( new_subsys ) {
1649                 // Make new_subsys target
1650                 if (new_subsys->system_info->type == SUBSYSTEM_ENGINE) {
1651                         if ( aip != Player_ai ) {
1652                                 ai_select_primary_weapon(&Objects[Ships[aip->shipnum].objnum], &Objects[parent_objnum], WIF_PUNCTURE);
1653                                 ship_primary_changed(&Ships[aip->shipnum]);     // AL: maybe send multiplayer information when AI ship changes primaries
1654                         }
1655                 }
1656
1657                 if ( aip == Player_ai ) {
1658                         hud_lock_reset(0.5f);
1659                 }
1660
1661         } else {
1662                 // Cleanup any subsys path information if it exists
1663                 ai_big_subsys_path_cleanup(aip);
1664         }
1665         
1666         return aip->targeted_subsys;
1667 }                                                                                         
1668
1669 // called to init the data for single ai object.  At this point,
1670 // the ship and the object and the ai_info are are correctly
1671 // linked together. Ai_info[ai_index].shipnum is the only valid field 
1672 // in ai_info.
1673 //      This is called right when the object is parsed, so you can't assume much
1674 //      has been initialized.  For example, wings, waypoints, goals are probably
1675 //      not yet loaded. --MK, 10/8/96
1676 void ai_object_init(object * obj, int ai_index)
1677 {
1678         ai_info *aip;
1679         Assert(ai_index >= 0 && ai_index < MAX_AI_INFO);
1680
1681         aip = &Ai_info[ai_index];
1682
1683         aip->type = 0;          //      0 means not in use.
1684         aip->wing = -1;         //      Member of what wing? -1 means none.
1685         aip->ai_class = Ship_info[Ships[obj->instance].ship_info_index].ai_class;
1686         aip->behavior = AIM_NONE;
1687 }
1688
1689 //      If *aip is docked, set max acceleration to A->mass/(A->mass + B->mass) where A is *aip and B is dock object
1690 void adjust_accel_for_docking(ai_info *aip)
1691 {
1692         if (aip->dock_objnum != -1) {
1693                 object  *obj2p = &Objects[aip->dock_objnum];
1694                 object  *obj1p;
1695
1696                 obj1p = &Objects[Ships[aip->shipnum].objnum];
1697
1698                 if (obj2p->signature == aip->dock_signature) {
1699                         float   ratio;
1700
1701                         ratio = obj1p->phys_info.mass / (obj1p->phys_info.mass + obj2p->phys_info.mass);
1702
1703                         // put cap on how much ship can slow down
1704                         if (ratio < 0.8) {
1705                                 ratio = 0.8f;
1706                         }
1707
1708                         if (AI_ci.forward > ratio) {
1709                                 AI_ci.forward = ratio;
1710                         }
1711                 }
1712         }
1713 }
1714
1715 // -------------------------------------------------------------------
1716 void accelerate_ship(ai_info *aip, float accel)
1717 {
1718         aip->prev_accel = accel;
1719         AI_ci.forward = accel;
1720         adjust_accel_for_docking(aip);
1721 }
1722
1723 //      --------------------------------------------------------------------------
1724 void change_acceleration(ai_info *aip, float delta_accel)
1725 {
1726         float   new_accel;
1727
1728         if (delta_accel < 0.0f) {
1729                 if (aip->prev_accel > 0.0f)
1730                         aip->prev_accel = 0.0f;
1731         } else if (aip->prev_accel < 0.0f)
1732                 aip->prev_accel = 0.0f;
1733
1734         new_accel = aip->prev_accel + delta_accel * flFrametime;
1735
1736         if (new_accel > 1.0f)
1737                 new_accel = 1.0f;
1738         else if (new_accel < -1.0f)
1739                 new_accel = -1.0f;
1740         
1741         aip->prev_accel = new_accel;
1742
1743         AI_ci.forward = new_accel;
1744         adjust_accel_for_docking(aip);
1745 }
1746
1747 void set_accel_for_target_speed(object *objp, float tspeed)
1748 {
1749         float   max_speed;
1750         ai_info *aip;
1751
1752         aip = &Ai_info[Ships[objp->instance].ai_index];
1753
1754         max_speed = Ships[objp->instance].current_max_speed;
1755
1756         AI_ci.forward = tspeed/max_speed;
1757         aip->prev_accel = AI_ci.forward;
1758
1759         adjust_accel_for_docking(aip);
1760 }
1761
1762 //      Stuff perim_point with a point on the perimeter of the sphere defined by object *objp
1763 //      on the vector from the center of *objp through the point *vp.
1764 void project_point_to_perimeter(vector *perim_point, vector *pos, float radius, vector *vp)
1765 {
1766         vector  v1;
1767         float           mag;
1768
1769         vm_vec_sub(&v1, vp, pos);
1770         mag = vm_vec_mag(&v1);
1771
1772         if (mag == 0.0f) {
1773                 Warning(LOCATION, "projectable point is at center of sphere.");
1774                 vm_vec_make(&v1, 0.0f, radius, 0.0f);
1775         } else {
1776                 vm_vec_normalize(&v1);
1777                 vm_vec_scale(&v1, 1.1f * radius + 10.0f);
1778         }
1779
1780         vm_vec_add2(&v1, pos);
1781         *perim_point = v1;
1782 }
1783
1784 //      Stuff tan1 with tangent point on sphere.  tan1 is point nearer to *p1
1785 //      *p0 is point through which tangents pass.
1786 //      *centerp is center of sphere.
1787 //      *p1 is another point in space to define the plane in which tan1, tan2 reside.
1788 //      radius is the radius of the sphere.
1789 //      Note, this is a very approximate function just for AI.
1790 //      Note also: On 12/26/96, p1 is used to define the plane perpendicular to that which
1791 //      contains the tangent point.
1792 void get_tangent_point(vector *tan1, vector *p0, vector *centerp, vector *p1, float radius)
1793 {
1794         vector  dest_vec, v2c, perp_vec, temp_vec, v2;
1795         float           dist, ratio;
1796
1797         //      Detect condition of point inside sphere.
1798         if (vm_vec_dist(p0, centerp) < radius)
1799                 project_point_to_perimeter(tan1, centerp, radius, p0);
1800         else {
1801                 vm_vec_normalized_dir(&v2c, centerp, p0);
1802
1803                 //      Compute perpendicular vector using p0, centerp, p1
1804                 vm_vec_normal(&temp_vec, p0, centerp, p1);
1805                 vm_vec_sub(&v2, centerp, p0);
1806                 vm_vec_cross(&perp_vec, &temp_vec, &v2);
1807
1808                 vm_vec_normalize(&perp_vec);
1809
1810                 dist = vm_vec_dist_quick(p0, centerp);
1811                 ratio = dist / radius;
1812
1813                 if (ratio < 2.0f)
1814                         vm_vec_scale_add(&dest_vec, &perp_vec, &v2c, ratio-1.0f);
1815                 else
1816                         vm_vec_scale_add(&dest_vec, &v2c, &perp_vec, (1.0f + 1.0f/ratio));
1817
1818                 vm_vec_scale_add(tan1, p0, &dest_vec, dist + radius);
1819         }
1820 }
1821
1822 //      --------------------------------------------------------------------------
1823 //      Given an object and a point, turn towards the point, resulting in
1824 // approach behavior.
1825 void turn_towards_point(object *objp, vector *point, vector *slide_vec, float bank_override)
1826 {
1827         ai_info *aip;
1828         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1829         
1830         // check if in formation and if not leader, don't change rotvel.z (bank to match leader elsewhere)
1831         if (aip->ai_flags & AIF_FORMATION) {
1832                 if (&Objects[aip->goal_objnum] != objp) {
1833                         float rotvel_z = objp->phys_info.rotvel.z;
1834                         ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1835                         objp->phys_info.rotvel.z = rotvel_z;
1836                 }
1837         } else {
1838                 // normal turn
1839                 ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1840         }
1841 }
1842
1843 //      --------------------------------------------------------------------------
1844 //      Given an object and a point, turn away from the point, resulting in avoidance behavior.
1845 //      Note: Turn away at full speed, not scaled down by skill level.
1846 void turn_away_from_point(object *objp, vector *point, float bank_override)
1847 {
1848         vector  opposite_point;
1849
1850         vm_vec_sub(&opposite_point, &objp->pos, point);
1851         vm_vec_add2(&opposite_point, &objp->pos);
1852
1853         ai_turn_towards_vector(&opposite_point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, NULL, NULL, bank_override, AITTV_FAST);
1854 }
1855
1856
1857 //      --------------------------------------------------------------------------
1858 //      Given an object and a point, turn tangent to the point, resulting in
1859 // a circling behavior.
1860 //      Make object *objp turn around the point *point with a radius of radius.
1861 //      Note that this isn't the same as following a circle of radius radius with
1862 //      center *point, but it should be adequate.
1863 //      Note that if you want to circle an object without hitting it, you should use
1864 //      about twice that object's radius for radius, else you'll certainly bump into it.
1865 //      Return dot product to goal point.
1866 float turn_towards_tangent(object *objp, vector *point, float radius)
1867 {
1868         vector  vec_to_point;
1869         vector  goal_point;
1870         vector  perp_point;                             //      point radius away from *point on vector to objp->pos
1871         vector  up_vec, perp_vec;
1872
1873         vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1874         vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.fvec);
1875         vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1876
1877         vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1878         if (vm_vec_dot(&objp->orient.fvec, &perp_vec) > 0.0f) {
1879                 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, radius);
1880         } else {
1881                 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, -radius);
1882         }
1883
1884 //      Ai_info[Ships[objp->instance].ai_index].goal_point = goal_point;
1885         turn_towards_point(objp, &goal_point, NULL, 0.0f);
1886
1887         vector  v2g;
1888
1889         vm_vec_normalized_dir(&v2g, &goal_point, &objp->pos);
1890         return vm_vec_dot(&objp->orient.fvec, &v2g);
1891 }
1892
1893 float turn_toward_tangent_with_axis(object *objp, object *center_objp, float radius)
1894 {
1895         vector r_vec, theta_vec;
1896         vector center_vec, vec_on_cylinder, sph_r_vec;
1897         float center_obj_z;
1898
1899         // find closest z of center objp
1900         vm_vec_sub(&sph_r_vec, &objp->pos, &center_objp->pos);
1901         center_obj_z = vm_vec_dotprod(&sph_r_vec, &center_objp->orient.fvec);
1902
1903         // find pt on axis with closest z
1904         vm_vec_scale_add(&center_vec, &center_objp->pos, &center_objp->orient.fvec, center_obj_z);
1905
1906         // get r_vec
1907         vm_vec_sub(&r_vec, &objp->pos, &center_vec);
1908 //      float r_mag = vm_vec_normalize_quick(&r_vec);
1909 //      mprintf(("cur_r: %.1f, desired_r: %.1f\n", r_mag, radius));
1910         Assert( (vm_vec_dotprod(&r_vec, &center_objp->orient.fvec) < 0.0001));
1911
1912         // get theta vec - perp to r_vec and z_vec
1913         vm_vec_crossprod(&theta_vec, &center_objp->orient.fvec, &r_vec);
1914
1915 #ifndef NDEBUG
1916         float mag = vm_vec_normalize(&theta_vec);
1917         Assert(mag > 0.9999 && mag < 1.0001);
1918 #endif
1919
1920         vector temp;
1921         vm_vec_crossprod(&temp, &r_vec, &theta_vec);
1922
1923 #ifndef NDEBUG
1924         float dot = vm_vec_dotprod(&temp, &center_objp->orient.fvec);
1925         Assert( dot >0.9999 && dot < 1.0001);
1926 #endif
1927
1928         // find pt on clylinder with closest z
1929         vm_vec_scale_add(&vec_on_cylinder, &center_vec, &r_vec, radius);
1930
1931         vector goal_pt, v2g;
1932         vm_vec_scale_add(&goal_pt, &vec_on_cylinder, &theta_vec, radius);
1933
1934 //      Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pt;
1935         turn_towards_point(objp, &goal_pt, NULL, 0.0f);
1936
1937         vm_vec_normalized_dir(&v2g, &goal_pt, &objp->pos);
1938         return vm_vec_dot(&objp->orient.fvec, &v2g);
1939 }
1940
1941 //      Returns a point radius units away from *point that *objp should turn towards to orbit *point
1942 void get_tangent_point(vector *goal_point, object *objp, vector *point, float radius)
1943 {
1944         vector  vec_to_point;
1945         vector  perp_point;                             //      point radius away from *point on vector to objp->pos
1946         vector  up_vec, perp_vec;
1947
1948         vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1949         vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.fvec);
1950         vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1951         vm_vec_normalize(&perp_vec);
1952
1953         vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1954
1955         if (vm_vec_dot(&objp->orient.fvec, &perp_vec) > 0.0f) {
1956                 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, radius);
1957         } else {
1958                 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, -radius);
1959         }
1960 }
1961
1962 int     Player_attacking_enabled = 1;
1963
1964 // -----------------------------------------------------------------------------
1965 // Determine whether an object is targetable within a nebula
1966 int object_is_targetable(object *target, ship *viewer)
1967 {
1968         int stealth_ship = 0;
1969
1970         // if target is ship, check if visible by team
1971         if (target->type == OBJ_SHIP) {
1972                 stealth_ship = (Ship_info[Ships[target->instance].ship_info_index].flags & SIF_STEALTH);
1973                 if ( ship_is_visible_by_team(target->instance, viewer->team) == 1) {
1974                         return 1;
1975                 }
1976         }
1977
1978         // for AI partially targetable works as fully targetable, except for stealth ship
1979         if (stealth_ship) {
1980                 // if not team targetable, check if within frustrum
1981                 if ( ai_is_stealth_visible(&Objects[viewer->objnum], target) == STEALTH_VISIBLE ) {
1982                         return 1;
1983                 } else {
1984                         return 0;
1985                 }
1986         }
1987
1988         // if not fully targetable by team, check awacs level with viewer
1989         // allow targeting even if only only partially targetable to player
1990         float radar_return = awacs_get_level(target, viewer);
1991         if ( radar_return > 0.4 ) {
1992                 return 1;
1993         } else {
1994                 return 0;
1995         }
1996 }
1997
1998 //      Return number of enemies attacking object objnum
1999 //
2000 // AL 10.26.97: Also include turrets on large ships when couting enemies attacking
2001 int num_enemies_attacking(int objnum)
2002 {
2003         object          *objp;
2004         ship                    *sp;
2005         ship_subsys     *ssp;
2006         ship_obj                *so;
2007         int                     count;
2008
2009         count = 0;
2010
2011         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2012                 objp = &Objects[so->objnum];
2013                 Assert(objp->instance != -1);
2014                 sp = &Ships[objp->instance];
2015
2016                 if (Ai_info[sp->ai_index].target_objnum == objnum)
2017                         count++;
2018
2019                 // consider turrets that may be attacking objnum (but only turrets on SIF_BIG_SHIP ships)
2020                 if ( Ship_info[sp->ship_info_index].flags & SIF_BIG_SHIP ) {
2021
2022                         // loop through all the subsystems, check if turret has objnum as a target
2023                         ssp = GET_FIRST(&sp->subsys_list);
2024                         while ( ssp != END_OF_LIST( &sp->subsys_list ) ) {
2025
2026                                 if ( ssp->system_info->type == SUBSYSTEM_TURRET ) {
2027                                         if ( (ssp->turret_enemy_objnum == objnum) && (ssp->current_hits > 0) ) {
2028                                                 count++;
2029                                         }
2030                                 }
2031                                 ssp = GET_NEXT( ssp );
2032                         } // end while
2033                 }
2034         }
2035
2036         return count;
2037 }
2038
2039 //      Get the team to fire on given an object.
2040 int get_enemy_team_mask(int objnum)
2041 {
2042         int     my_team, enemy_team_mask;
2043
2044         my_team = Ships[Objects[objnum].instance].team;
2045
2046         if (Mission_all_attack) {
2047                 //      All teams attack all teams.
2048                 switch (my_team) {
2049                 case TEAM_FRIENDLY:
2050                         enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2051                         break;
2052                 case TEAM_HOSTILE:
2053                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2054                         break;
2055                 case TEAM_NEUTRAL:
2056                         enemy_team_mask = TEAM_FRIENDLY | TEAM_HOSTILE | TEAM_TRAITOR;
2057                         break;
2058                 case TEAM_UNKNOWN:
2059                         enemy_team_mask = TEAM_HOSTILE;
2060                         break;
2061                 case TEAM_TRAITOR:
2062                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2063                         break;
2064                 default:
2065                         enemy_team_mask = TEAM_HOSTILE;
2066                         Int3();                 //      Illegal value for team!
2067                         break;
2068                 }
2069         } else {
2070                 switch (my_team) {
2071                 case TEAM_FRIENDLY:
2072                         enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2073                         break;
2074                 case TEAM_HOSTILE:
2075                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2076                         break;
2077                 case TEAM_NEUTRAL:
2078                         enemy_team_mask = TEAM_FRIENDLY | TEAM_TRAITOR;
2079                         break;
2080                 case TEAM_UNKNOWN:
2081                         enemy_team_mask = TEAM_HOSTILE;
2082                         break;
2083                 case TEAM_TRAITOR:
2084                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2085                         break;
2086                 default:
2087                         enemy_team_mask = TEAM_HOSTILE;
2088                         Int3();                 //      Illegal value for team!
2089                         break;
2090                 }
2091         }
2092
2093         return enemy_team_mask;
2094 }
2095
2096 //      Scan all the ships in *objp's wing.
2097 //      Return the lowest maximum speed of a ship in the wing.
2098 //      Current maximum speed (based on energy settings) is shipp->current_max_speed
2099 float get_wing_lowest_max_speed(object *objp)
2100 {
2101         ship            *shipp;
2102         ai_info *aip;
2103         float           lowest_max_speed;
2104         int             wingnum;
2105         object  *o;
2106         ship_obj        *so;
2107
2108         Assert(objp->type == OBJ_SHIP);
2109         Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
2110         shipp = &Ships[objp->instance];
2111         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
2112         aip = &Ai_info[shipp->ai_index];
2113
2114         wingnum = aip->wing;
2115
2116         lowest_max_speed = shipp->current_max_speed;
2117
2118         if ( wingnum == -1 )
2119                 return lowest_max_speed;
2120
2121         Assert(wingnum >= 0);
2122
2123         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2124                 o = &Objects[so->objnum];
2125                 ship    *oshipp = &Ships[o->instance];
2126                 ai_info *oaip = &Ai_info[oshipp->ai_index];
2127
2128                 if ((oaip->mode == AIM_WAYPOINTS) && (oaip->wing == wingnum)) {
2129                         //      Note: If a ship in the wing has a super low max speed, probably its engines are disabled.  So, fly along and
2130                         //      ignore the poor guy.
2131                         float   cur_max = oshipp->current_max_speed;
2132
2133                         if (oaip->ai_flags & AIF_DOCKED) {
2134                                 if (oaip->dock_objnum > -1)
2135                                         if (Objects[oaip->dock_objnum].type == OBJ_SHIP) 
2136                                                 cur_max *= o->phys_info.mass/(o->phys_info.mass + Objects[oaip->dock_objnum].phys_info.mass);
2137                         }
2138                                                         
2139                         if ((oshipp->current_max_speed > 5.0f) && (cur_max < lowest_max_speed)) {
2140                                 lowest_max_speed = cur_max;
2141                         }
2142                 }
2143         }
2144
2145         return lowest_max_speed;
2146 }
2147
2148 /*
2149 //      Tell everyone to ignore object objnum.
2150 void set_global_ignore_object(int objnum)
2151 {
2152         int     i;
2153
2154         Assert(Objects[objnum].type == OBJ_SHIP);
2155
2156         nprintf(("AI", "Telling everyone to ignore object %s\n", Ships[Objects[objnum].instance].ship_name));
2157
2158         for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2159                 if (Ignore_objects[i].objnum == -1) {
2160                         Ignore_objects[i].objnum = objnum;
2161                         Ignore_objects[i].signature = Objects[objnum].signature;
2162                         break;
2163                 }
2164         }
2165
2166         if (i == MAX_IGNORE_OBJECTS) {
2167                 //      Couldn't find a free slot, but maybe one of these objects has died.
2168                 for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2169                         int     o = Ignore_objects[i].objnum;
2170                         if (Objects[o].type != OBJ_SHIP)
2171                                 break;          //      Not a ship, so use this slot.
2172                         if (Objects[o].signature != Ignore_objects[i].signature)
2173                                 break;          //      Signatures don't match, so use this slot.
2174                 }
2175
2176                 if (i != MAX_IGNORE_OBJECTS) {
2177                         Ignore_objects[i].objnum = objnum;
2178                         Ignore_objects[i].signature = Objects[objnum].signature;
2179                 } else {
2180                         nprintf(("Warning", "Ignore_objects buffer full.  Stealing a slot to ignore object #%i\n"));
2181                         Int3();
2182
2183                         int     r;
2184
2185                         r = objnum % MAX_IGNORE_OBJECTS;
2186
2187                         Ignore_objects[r].objnum = objnum;
2188                         Ignore_objects[r].signature = Objects[objnum].signature;
2189                 }
2190         }
2191 }
2192
2193 */
2194
2195 //      Determine if object objnum is supposed to be ignored by object with ai_info *aip.
2196 //      Return:
2197 //              TRUE    if objnum is aip->ignore_objnum (and signatures match)
2198 //                              or objnum is in ignore wing
2199 //              FALSE   otherwise
2200 int is_ignore_object(ai_info *aip, int objnum)
2201 {
2202
2203 /*      //      First, scan all objects in global array of objects to be ignored.
2204         for (int i=0; i<MAX_IGNORE_OBJECTS; i++)
2205                 if (Ignore_objects[i].objnum != -1)
2206                         if (objnum == Ignore_objects[i].objnum)
2207                                 if (Objects[Ignore_objects[i].objnum].signature == Ignore_objects[i].signature)
2208                                         return 1;
2209 */
2210
2211         //      Didn't find in global list.  Now check 
2212         if (aip->ignore_objnum == UNUSED_OBJNUM)
2213                 return 0;                                                                       //      Not ignoring anything.
2214         else if (aip->ignore_objnum >= 0) {             //      This means it's ignoring an object, not a wing.
2215                 if (aip->ignore_objnum == objnum) {
2216                         if (Objects[aip->ignore_objnum].signature == aip->ignore_signature) {
2217                                 return 1;
2218                         } else {
2219                                 aip->ignore_objnum = UNUSED_OBJNUM;
2220                                 return 0;
2221                         }
2222                 } else {
2223                         return 0;
2224                 }
2225         } else {                                                                                        //      Ignoring a wing.
2226                 Int3(); // Should never happen.  I thought I removed this behavior! -- MK, 5/17/98
2227                 return 0;
2228 /*              int     ignore_wingnum = -(aip->ignore_objnum + 1);
2229
2230                 Assert(ignore_wingnum < MAX_WINGS);
2231                 Assert(aip->shipnum >= 0);
2232                 return (Ships[Objects[objnum].instance].wingnum == ignore_wingnum);
2233 */      }
2234 }
2235
2236 // -----------------------------------------------------------------------------
2237
2238 // given a ship with bounding box and a point, find the closest point on the bbox
2239 int get_nearest_bbox_point(object *ship_obj, vector *start, vector *box_pt)
2240 {
2241         vector temp, rf_start;
2242         polymodel *pm;
2243         pm = model_get(Ship_info[Ships[ship_obj->instance].ship_info_index].modelnum);
2244
2245         // get start in ship rf
2246         vm_vec_sub(&temp, start, &ship_obj->pos);
2247         vm_vec_rotate(&rf_start, &temp, &ship_obj->orient);
2248
2249         // find box_pt
2250         int inside = project_point_onto_bbox(&pm->mins, &pm->maxs, &rf_start, &temp);
2251
2252         // get box_pt in world rf
2253         vm_vec_unrotate(box_pt, &temp, &ship_obj->orient);
2254         vm_vec_add2(box_pt, &ship_obj->pos);
2255
2256         return inside;
2257 }
2258
2259
2260 typedef struct eval_nearest_objnum {
2261         int     objnum;
2262         object *trial_objp;
2263         int     enemy_team_mask;
2264         int     enemy_wing;
2265         float   range;
2266         int     max_attackers;
2267         int     nearest_objnum;
2268         float   nearest_dist;
2269         int     check_danger_weapon_objnum;
2270 } eval_nearest_objnum;
2271
2272
2273 void evaluate_object_as_nearest_objnum(eval_nearest_objnum *eno)
2274 {
2275         ai_info *aip;
2276         ship_subsys     *attacking_subsystem;
2277
2278         aip = &Ai_info[Ships[Objects[eno->objnum].instance].ai_index];
2279
2280         attacking_subsystem = aip->targeted_subsys;
2281
2282         if ((attacking_subsystem != NULL) || !(eno->trial_objp->flags & OF_PROTECTED)) {
2283                 if ( OBJ_INDEX(eno->trial_objp) != eno->objnum ) {
2284 #ifndef NDEBUG
2285                         if (!Player_attacking_enabled && (eno->trial_objp == Player_obj))
2286                                 return;
2287 #endif
2288                         //      If only supposed to attack ship in a specific wing, don't attack other ships.
2289                         if ((eno->enemy_wing != -1) && (Ships[eno->trial_objp->instance].wingnum != eno->enemy_wing))
2290                                 return;
2291
2292                         //      Don't keep firing at a ship that is in its death throes.
2293                         if (Ships[eno->trial_objp->instance].flags & SF_DYING)
2294                                 return;
2295
2296                         if (is_ignore_object(aip, ((eno->trial_objp)-Objects)))
2297                                 return;
2298
2299                         if (eno->trial_objp->flags & OF_PROTECTED)
2300                                 return;
2301
2302                         if (Ships[eno->trial_objp->instance].flags & SF_ARRIVING)
2303                                 return;
2304
2305                         ship_info *sip = &Ship_info[Ships[eno->trial_objp->instance].ship_info_index];
2306
2307                         if (sip->flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2308                                 return;
2309
2310                         if (Ships[eno->trial_objp->instance].team & eno->enemy_team_mask) {
2311                                 float   dist;
2312                                 int     num_attacking;
2313
2314                                 // Allow targeting of stealth in nebula by his firing at me
2315                                 // This is done for a specific ship, not generally.
2316                                 if ( !eno->check_danger_weapon_objnum ) {
2317                                         // check if can be targeted if inside nebula
2318                                         if ( !object_is_targetable(eno->trial_objp, &Ships[Objects[eno->objnum].instance]) ) {
2319                                                 // check if stealth ship is visible, but not "targetable"
2320                                                 if ( !((sip->flags & SIF_STEALTH) && ai_is_stealth_visible(&Objects[eno->objnum], eno->trial_objp)) ) {
2321                                                         return;
2322                                                 }
2323                                         }
2324                                 }
2325
2326                                 // if objnum is BIG or HUGE, find distance to bbox
2327                                 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
2328                                         vector box_pt;
2329                                         // check if inside bbox
2330                                         int inside = get_nearest_bbox_point(eno->trial_objp, &Objects[eno->objnum].pos, &box_pt);
2331                                         if (inside) {
2332                                                 dist = 10.0f;
2333                                                 // on the box
2334                                         } else {
2335                                                 dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &box_pt);
2336                                         }
2337                                 } else {
2338                                         dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &eno->trial_objp->pos);
2339                                 }
2340                                 
2341                                 //      Make it more likely that fighters (or bombers) will be picked as an enemy by scaling up distance for other types.
2342                                 if ((Ship_info[Ships[eno->trial_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))) {
2343                                         dist = dist * 0.5f;
2344                                 }
2345
2346                                 num_attacking = num_enemies_attacking(eno->trial_objp-Objects);
2347                                 if ((sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) || (num_attacking < eno->max_attackers)) {
2348                                         if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))){
2349                                                 dist *= (float) (num_attacking+2)/2.0f;                         //      prevents lots of ships from attacking same target
2350                                         }
2351
2352                                         if (eno->trial_objp->flags & OF_PLAYER_SHIP){
2353                                                 dist *= 1.0f + (NUM_SKILL_LEVELS - Game_skill_level - 1)/NUM_SKILL_LEVELS;      //      Favor attacking non-players based on skill level.
2354                                         }
2355
2356                                         if (dist < eno->nearest_dist) {
2357                                                 eno->nearest_dist = dist;
2358                                                 eno->nearest_objnum = eno->trial_objp-Objects;
2359                                         }
2360                                 }
2361                         }
2362                 }
2363         }
2364
2365 }
2366
2367
2368 //      Given an object and an enemy team, return the index of the nearest enemy object.
2369 //      Unless aip->targeted_subsys != NULL, don't allow to attack objects
2370 //      with OF_PROTECTED bit set.
2371 //      Ship must be within range "range".
2372 //      Don't attack a ship that already has at least max_attackers attacking it.
2373 int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers)
2374 {
2375         object  *danger_weapon_objp;
2376         ai_info *aip;
2377         ship_obj        *so;
2378
2379         // initialize eno struct
2380         eval_nearest_objnum eno;
2381         eno.enemy_team_mask = enemy_team_mask;
2382         eno.enemy_wing = enemy_wing;
2383         eno.max_attackers = max_attackers;
2384         eno.objnum = objnum;
2385         eno.range = range;
2386         eno.nearest_dist = range;
2387         eno.nearest_objnum = -1;
2388         eno.check_danger_weapon_objnum = 0;
2389
2390         // go through the list of all ships and evaluate as potential targets
2391         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2392                 eno.trial_objp = &Objects[so->objnum];
2393                 evaluate_object_as_nearest_objnum(&eno);
2394
2395         }
2396
2397         // check if danger_weapon_objnum has will show a stealth ship
2398         aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2399         if (aip->danger_weapon_objnum >= 0) {
2400                 danger_weapon_objp = &Objects[aip->danger_weapon_objnum];
2401                 // validate weapon
2402                 if (danger_weapon_objp->signature == aip->danger_weapon_signature) {
2403                         Assert(danger_weapon_objp->type == OBJ_WEAPON);
2404                         // check if parent is a ship
2405                         if (danger_weapon_objp->parent >= 0) {
2406                                 if ( is_object_stealth_ship(&Objects[danger_weapon_objp->parent]) ) {
2407                                         // check if stealthy
2408                                         if ( ai_is_stealth_visible(&Objects[objnum], &Objects[danger_weapon_objp->parent]) != STEALTH_FULLY_TARGETABLE ) {
2409                                                 // check if weapon is laser
2410                                                 if (Weapon_info[Weapons[danger_weapon_objp->instance].weapon_info_index].subtype == WP_LASER) {
2411                                                         // check stealth ship by its laser fire
2412                                                         eno.check_danger_weapon_objnum = 1;
2413                                                         eno.trial_objp = &Objects[danger_weapon_objp->parent];
2414                                                         evaluate_object_as_nearest_objnum(&eno);
2415                                                 }
2416                                         }
2417                                 }
2418                         }
2419                 }
2420         }
2421
2422         //      If only looking for target in certain wing and couldn't find anything in
2423         //      that wing, look for any object.
2424         if ((eno.nearest_objnum == -1) && (enemy_wing != -1)) {
2425                 return get_nearest_objnum(objnum, enemy_team_mask, -1, range, max_attackers);
2426         }
2427
2428         return eno.nearest_objnum;
2429 }
2430
2431 //      Given an object and an enemy team, return the index of the nearest enemy object.
2432 //      Unlike find_enemy or find_nearest_objnum, this doesn't care about things like the protected flag or number
2433 //      of enemies attacking.
2434 //      It is used to find the nearest enemy to determine things like whether to rearm.
2435 int find_nearby_hostile(int objnum, int enemy_team_mask, float range, int *count)
2436 {
2437         int             nearest_objnum;
2438         float           nearest_dist;
2439         object  *objp;
2440         ai_info *aip;
2441         ship_obj        *so;
2442
2443         nearest_objnum = -1;
2444         nearest_dist = range;
2445
2446         aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2447
2448         *count = 0;
2449
2450         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2451                 objp = &Objects[so->objnum];
2452
2453                 if ( OBJ_INDEX(objp) != objnum ) {
2454                         if (Ships[objp->instance].flags & SF_DYING)
2455                                 continue;
2456
2457                         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2458                                 continue;
2459
2460                         if (Ships[objp->instance].team & enemy_team_mask) {
2461                                 float   dist;
2462
2463                                 dist = vm_vec_dist_quick(&Objects[objnum].pos, &objp->pos) - objp->radius*0.75f;
2464                                 
2465                                 if (dist < range) {
2466                                         (*count)++;
2467
2468                                         if (dist < nearest_dist) {
2469                                                 nearest_dist = dist;
2470                                                 nearest_objnum = objp-Objects;
2471                                         }
2472                                 }
2473                         }
2474                 }
2475         }
2476
2477         return nearest_objnum;
2478 }
2479
2480 // return !0 if objp can be considered for a turret target, 0 otherwise
2481 // input:       objp                            =>      object that turret is considering as an enemy
2482 //                              turret_parent   =>      object index for ship that turret sits on
2483 int valid_turret_enemy(object *objp, object *turret_parent)
2484 {
2485         if ( objp == turret_parent ) {
2486                 return 0;
2487         }
2488
2489         if ( objp->type == OBJ_ASTEROID ) {
2490                 return 1;
2491         }
2492
2493         if ( (objp->type == OBJ_SHIP) ) {
2494                 ship *shipp;
2495                 shipp = &Ships[objp->instance];
2496
2497                 // don't fire at ships with protected bit set!!!
2498                 if ( objp->flags & OF_PROTECTED ) {
2499                         return 0;
2500                 }
2501
2502                 if ( !(Ship_info[shipp->ship_info_index].flags & SIF_DO_COLLISION_CHECK)) {
2503                         return 0;
2504                 }
2505
2506                 if (shipp->flags & SF_ARRIVING) {
2507                         return 0;
2508                 }
2509
2510                 return 1;
2511         }
2512
2513         if ( objp->type == OBJ_WEAPON ) {
2514                 if ( Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_BOMB ) {
2515                         if ( obj_team(turret_parent) != Weapons[objp->instance].team ) {
2516                                 return 1;
2517                         }
2518                 }
2519         }
2520
2521         return 0;
2522 }
2523
2524 // return 1 if objp is in fov of the specified turret, tp.  Otherwise return 0.
2525 //      dist = distance from turret to center point of object
2526 int object_in_turret_fov(object *objp, model_subsystem *tp, vector *tvec, vector *tpos, float dist)
2527 {
2528         vector  v2e;
2529         float           dot;
2530         vm_vec_normalized_dir(&v2e, &objp->pos, tpos);
2531         dot = vm_vec_dot(&v2e, tvec);
2532
2533         dot += objp->radius / (dist + objp->radius);
2534
2535         if ( dot >= tp->turret_fov ) {
2536                 return 1;
2537         }
2538
2539         return 0;
2540 }
2541
2542 // return 1 if bomb_objp is headed towards ship_objp
2543 int bomb_headed_towards_ship(object *bomb_objp, object *ship_objp)
2544 {
2545         float           dot;
2546         vector  bomb_to_ship_vector;
2547
2548         vm_vec_normalized_dir(&bomb_to_ship_vector, &ship_objp->pos, &bomb_objp->pos);
2549         dot = vm_vec_dot(&bomb_objp->orient.fvec, &bomb_to_ship_vector);
2550
2551         if ( dot > 0 ) {
2552                 return 1;
2553         }
2554
2555         return 0;
2556 }
2557
2558 // nubmer of live turrets with target_objnum 
2559 int num_turrets_attacking(object *turret_parent, int target_objnum) 
2560 {
2561         ship_subsys *ss;
2562         ship *shipp;
2563         int count = 0;
2564         shipp = &Ships[turret_parent->instance];
2565
2566         Assert(turret_parent->type == OBJ_SHIP);
2567         Assert(Objects[target_objnum].type == OBJ_SHIP);
2568
2569         for (ss=GET_FIRST(&shipp->subsys_list); ss!=END_OF_LIST(&shipp->subsys_list); ss=GET_NEXT(ss)) {
2570                 // check if subsys is alive
2571                 if (ss->current_hits <= 0.0f) {
2572                         continue;
2573                 }
2574
2575                 // check if it's a turret
2576                 if (ss->system_info->type != SUBSYSTEM_TURRET) {
2577                         continue;
2578                 }
2579
2580                 // if the turret is locked
2581                 if(ss->weapons.flags & SW_FLAG_TURRET_LOCK){
2582                         continue;
2583                 }               
2584
2585                 // check if turret is targeting target_objnum
2586                 if (ss->turret_enemy_objnum == target_objnum) {
2587                         count++;
2588                 }
2589         }
2590
2591         return count;
2592 }
2593
2594 float Lethality_range_const = 2.0f;
2595 DCF(lethality_range, "N for modifying range: 1 / (1+N) at 100")
2596 {
2597         dc_get_arg(ARG_FLOAT);
2598         Lethality_range_const = Dc_arg_float;
2599 }
2600
2601 float Player_lethality_bump[NUM_SKILL_LEVELS] = {
2602         // 0.0f, 5.0f, 10.0f, 25.0f, 40.0f
2603         0.0f, 0.0f, 0.0f, 0.0f, 0.0f
2604 };
2605
2606 // evaluate obj as posssible target for turret
2607 void evaluate_obj_as_target(object *objp, eval_enemy_obj_struct *eeo)
2608 {
2609         object  *turret_parent_obj = &Objects[eeo->turret_parent_objnum];
2610         ship            *shipp;
2611         model_subsystem *tp = eeo->turret_subsys->system_info;
2612         float dist;
2613
2614         // Don't look for bombs when weapon system is not ok
2615         if (objp->type == OBJ_WEAPON && !eeo->weapon_system_ok) {
2616                 return;
2617         }
2618
2619         if ( !valid_turret_enemy(objp, turret_parent_obj) ) {
2620                 return;
2621         }
2622
2623 #ifndef NDEBUG
2624         if (!Player_attacking_enabled && (objp == Player_obj)) {
2625                 return;
2626         }
2627 #endif
2628
2629         if ( objp->type == OBJ_SHIP ) {
2630                 shipp = &Ships[objp->instance];
2631
2632                 // check on enemy team
2633                 if ( !(shipp->team & eeo->enemy_team_mask) ) {
2634                         return;
2635                 }
2636
2637                 // check if protected
2638                 if (objp->flags & OF_PROTECTED) {
2639                         return;
2640                 }
2641
2642                 // check if beam protected
2643                 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2644                         if (objp->flags & OF_BEAM_PROTECTED) {
2645                                 return;
2646                         }
2647                 }
2648
2649                 if (eeo->big_only_flag) {
2650                         if (!(Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2651                                 return;
2652                         }
2653                 }
2654
2655                 // check if     turret flagged to only target tagged ships
2656                 if ( (eeo->turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && !ship_is_tagged(objp) ) {
2657                         return;
2658                 }
2659
2660                 // check if valid target in nebula
2661                 if ( !object_is_targetable(objp, &Ships[Objects[eeo->turret_parent_objnum].instance]) ) {
2662                         // BYPASS ocassionally for stealth
2663                         int try_anyway = FALSE;
2664                         if ( is_object_stealth_ship(objp) ) {
2665                                 float turret_stealth_find_chance = 0.5f;
2666                                 float speed_mod = -0.1f + vm_vec_mag_quick(&objp->phys_info.vel) / 70.0f;
2667                                 if (frand() > (turret_stealth_find_chance + speed_mod)) {
2668                                         try_anyway = TRUE;
2669                                 }
2670                         }
2671
2672                         if (!try_anyway) {
2673                                 return;
2674                         }
2675                 }
2676
2677         } else {
2678                 shipp = NULL;
2679         }
2680
2681         // modify dist for BIG|HUGE, getting closest point on bbox, if not inside
2682         dist = vm_vec_dist_quick(eeo->tpos, &objp->pos) - objp->radius;
2683         if (dist < 0.0f) {
2684                 dist = 0.0f;
2685         }
2686
2687         // check if object is a bomb attacking the turret parent
2688         // check if bomb is homing on the turret parent ship
2689         if (objp->type == OBJ_WEAPON) {
2690                 if ( Weapons[objp->instance].homing_object == &Objects[eeo->turret_parent_objnum] ) {
2691                         if ( dist < eeo->nearest_homing_bomb_dist ) {
2692                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2693                                         eeo->nearest_homing_bomb_dist = dist;
2694                                         eeo->nearest_homing_bomb_objnum = OBJ_INDEX(objp);
2695                                 }
2696                         }
2697                 // if not homing, check if bomb is flying towards ship
2698                 } else if ( bomb_headed_towards_ship(objp, &Objects[eeo->turret_parent_objnum]) ) {
2699                         if ( dist < eeo->nearest_bomb_dist ) {
2700                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2701                                         eeo->nearest_bomb_dist = dist;
2702                                         eeo->nearest_bomb_objnum = OBJ_INDEX(objp);
2703                                 }
2704                         }
2705                 }
2706         } // end weapon section
2707
2708         // maybe recalculate dist for big or huge ship
2709 //      if (shipp && (Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2710 //              fvi_ray_boundingbox(min, max, start, direction, hit);
2711 //              dist = vm_vec_dist_quick(hit, tvec);
2712 //      }
2713
2714         // check for nearest attcker
2715         if ( (shipp) && (dist < eeo->weapon_travel_dist) ) {
2716                 ai_info *aip = &Ai_info[shipp->ai_index];
2717
2718                 // modify distance based on number of turrets from my ship attacking enemy (add 10% per turret)
2719                 // dist *= (num_enemies_attacking(OBJ_INDEX(objp))+2)/2;        //      prevents lots of ships from attacking same target
2720                 int num_att_turrets = num_turrets_attacking(turret_parent_obj, OBJ_INDEX(objp));
2721                 dist *= (1.0f + 0.1f*num_att_turrets);
2722
2723                 // return if we're over the cap
2724                 int max_turrets = 3 + Game_skill_level * Game_skill_level;
2725                 if (num_att_turrets > max_turrets) {
2726                         return;
2727                 }
2728
2729                 // modify distance based on lethality of objp to my ship
2730                 float active_lethality = aip->lethality;
2731                 if (objp->flags & OF_PLAYER_SHIP) {
2732                         active_lethality += Player_lethality_bump[Game_skill_level];
2733                 }
2734
2735                 dist /= (1.0f + 0.01f*Lethality_range_const*active_lethality);
2736
2737                 // Make level 2 tagged ships more likely to be targeted
2738                 if (shipp->level2_tag_left > 0.0f) {
2739                         dist *= 0.3f;
2740                 }
2741
2742                 // check if objp is targeting the turret's ship, or if objp has just hit the turret's ship
2743                 if ( aip->target_objnum == eeo->turret_parent_objnum || aip->last_objsig_hit == Objects[eeo->turret_parent_objnum].signature ) {
2744                         // A turret will always target a ship that is attacking itself... self-preservation!
2745                         if ( aip->targeted_subsys == eeo->turret_subsys ) {
2746                                 dist *= 0.5f;   // highest priority
2747                         }
2748                 }
2749
2750                 // maybe update nearest attacker
2751                 if ( dist < eeo->nearest_attacker_dist ) {
2752                         if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2753                                 // 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));
2754                                 eeo->nearest_attacker_dist = dist;
2755                                 eeo->nearest_attacker_objnum = OBJ_INDEX(objp);
2756                         }
2757                 }
2758         } // end ship section
2759 }
2760
2761 // return 0 only if objnum is beam protected and turret is beam turret
2762 int is_target_beam_valid(ship_subsys *turret_subsys, int objnum)
2763 {
2764         // check if turret has beam weapon
2765         model_subsystem *tp = turret_subsys->system_info;
2766
2767         if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2768                 if (Objects[objnum].flags & OF_BEAM_PROTECTED) {
2769                         return 0;
2770                 }
2771
2772                 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_HUGE) {
2773                         if (Objects[objnum].type == OBJ_SHIP && !(Ship_info[Ships[Objects[objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
2774                                 return 0;
2775                         }
2776                 }
2777         }
2778
2779         return 1;
2780 }
2781
2782
2783 //      Given an object and an enemy team, return the index of the nearest enemy object.
2784 //
2785 // input:
2786 //                              turret_parent_objnum    => parent objnum for the turret
2787 //                              turret_subsys                   => pointer to system_info for the turret subsystem
2788 //                              enemy_team_mask         => OR'ed TEAM_ flags for the enemy of the turret parent ship
2789 //                              tpos                                            => position of turret (world coords)
2790 //                              tvec                                            => forward vector of turret (world coords)
2791 //                              current_enemy                   =>      objnum of current turret target
2792 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)
2793 {
2794         float                                   weapon_travel_dist;
2795         int                                     weapon_system_ok;
2796         object                          *objp;
2797         model_subsystem *tp;
2798         eval_enemy_obj_struct eeo;
2799
2800         // list of stuff to go thru
2801         ship_obj                *so;
2802         missile_obj *mo;
2803
2804         tp = turret_subsys->system_info;
2805         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);
2806
2807         // Set flag based on strength of weapons subsystem.  If weapons subsystem is destroyed, don't let turrets fire at bombs
2808         weapon_system_ok = 0;
2809         if ( ship_get_subsystem_strength( &Ships[Objects[turret_parent_objnum].instance], SUBSYSTEM_WEAPONS ) > 0 ) {
2810                 weapon_system_ok = 1;
2811         }
2812
2813         // Initialize eeo struct.
2814         eeo.turret_parent_objnum = turret_parent_objnum;
2815         eeo.weapon_system_ok = weapon_system_ok;
2816         eeo.weapon_travel_dist = weapon_travel_dist;
2817         eeo.big_only_flag = big_only_flag;
2818         eeo.enemy_team_mask = enemy_team_mask;
2819         eeo.current_enemy = current_enemy;
2820         eeo.tpos = tpos;
2821         eeo.tvec = tvec;
2822         eeo.turret_subsys = turret_subsys;
2823
2824         eeo.nearest_attacker_dist = 99999.0f;
2825         eeo.nearest_attacker_objnum = -1;
2826
2827         eeo.nearest_homing_bomb_dist = 99999.0f;
2828         eeo.nearest_homing_bomb_objnum = -1;
2829
2830         eeo.nearest_bomb_dist = 99999.0f;
2831         eeo.nearest_bomb_objnum = -1;
2832
2833         eeo.nearest_dist = 99999.0f;
2834         eeo.nearest_objnum = -1;
2835
2836
2837         // Missile_obj_list
2838         for( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
2839                 objp = &Objects[mo->objnum];
2840                 evaluate_obj_as_target(objp, &eeo);
2841         }
2842         // highest priority
2843         if ( eeo.nearest_homing_bomb_objnum != -1 ) {                                   // highest priority is an incoming homing bomb
2844                 return eeo.nearest_homing_bomb_objnum;
2845         } else if ( eeo.nearest_bomb_objnum != -1 ) {                                   // next highest priority is an incoming dumbfire bomb
2846                 return eeo.nearest_bomb_objnum;
2847         }
2848
2849
2850         // Ship_used_list
2851         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2852                 objp = &Objects[so->objnum];
2853                 evaluate_obj_as_target(objp, &eeo);
2854         }
2855
2856         Assert(eeo.nearest_attacker_objnum < 0 || is_target_beam_valid(turret_subsys, eeo.nearest_attacker_objnum));
2857                 // next highest priority is attacking ship
2858         if ( eeo.nearest_attacker_objnum != -1 ) {                      // next highest priority is an attacking ship
2859                 return eeo.nearest_attacker_objnum;
2860          }
2861
2862
2863 #ifndef FS2_DEMO
2864                 asteroid_obj *ao;
2865         // Asteroid_obj_list
2866         for( ao = GET_FIRST(&Asteroid_obj_list); ao != END_OF_LIST(&Asteroid_obj_list); ao = GET_NEXT(ao) ) {
2867                 objp = &Objects[ao->objnum];
2868                 evaluate_obj_as_target(objp, &eeo);
2869         }
2870 #endif
2871
2872         return eeo.nearest_objnum;                                                                              // lowest priority is the closest enemy objnum
2873 }
2874
2875 //      Return timestamp until a ship can find an enemy.
2876 //      Yes, no parameters.  Based solely on skill level.
2877 int get_enemy_timestamp()
2878 {
2879         return (NUM_SKILL_LEVELS - Game_skill_level) * ( (myrand() % 500) + 500);
2880 }
2881
2882 // -------------------------------------------------------------------
2883 //      Return objnum if enemy found, else return -1;
2884 //      Don't attack a ship that already has at least max_attackers attacking it.
2885 int find_enemy(int objnum, float range, int max_attackers)
2886 {
2887         int     enemy_team_mask;
2888
2889         enemy_team_mask = get_enemy_team_mask(objnum);
2890
2891         //      if target_objnum != -1, use that as goal.
2892         ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2893         if (timestamp_elapsed(aip->choose_enemy_timestamp)) {
2894                 aip->choose_enemy_timestamp = timestamp(get_enemy_timestamp());
2895                 if (aip->target_objnum != -1) {
2896                         int     target_objnum = aip->target_objnum;
2897
2898                         // DKA don't undo object as target in nebula missions.
2899                         // This could cause attack on ship on fringe on nebula to stop if attackee moves our of nebula range.  (BAD)
2900                         if ( (Objects[target_objnum].signature == aip->target_signature) ) {
2901                                 if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
2902                                         if (!(Objects[target_objnum].flags & OF_PROTECTED)) {
2903                                                 // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
2904                                                 return target_objnum;
2905                                         }
2906                                 }
2907                         } else {
2908                                 aip->target_objnum = -1;
2909                                 aip->target_signature = -1;
2910                         }
2911                 }
2912                 return get_nearest_objnum(objnum, enemy_team_mask, aip->enemy_wing, range, max_attackers);
2913         } else {
2914                 aip->target_objnum = -1;
2915                 aip->target_signature = -1;
2916                 return -1;
2917         }
2918
2919 }
2920
2921 int Use_parent_target = 0;
2922 DCF_BOOL(use_parent_target, Use_parent_target)
2923
2924 // -------------------------------------------------------------------
2925 //      Return objnum if enemy found, else return -1;
2926 //
2927 // input:
2928 //                              turret_subsys   => pointer to turret subsystem
2929 //                              objnum                  => parent objnum for the turret
2930 //                              tpos                            => position of turret (world coords)
2931 //                              tvec                            => forward vector of turret (world coords)
2932 //                              current_enemy   =>      objnum of current turret target
2933 int find_turret_enemy(ship_subsys *turret_subsys, int objnum, vector *tpos, vector *tvec, int current_enemy, float fov, int big_only_flag = 0)
2934 {
2935         int                                     enemy_team_mask, enemy_objnum;
2936         model_subsystem *tp;
2937         ship_info                       *sip;
2938
2939         tp = turret_subsys->system_info;
2940         enemy_team_mask = get_enemy_team_mask(objnum);
2941
2942         //      If a small ship and target_objnum != -1, use that as goal.
2943         ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2944         sip = &Ship_info[Ships[Objects[objnum].instance].ship_info_index];
2945
2946         if ((sip->flags & SIF_SMALL_SHIP) && (aip->target_objnum != -1)) {
2947                 int target_objnum = aip->target_objnum;
2948
2949                 if (Objects[target_objnum].signature == aip->target_signature) {
2950                         if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
2951                                 if ( !(Objects[target_objnum].flags & OF_PROTECTED) ) {         // check this flag as well.
2952                                         // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
2953                                         return target_objnum;
2954                                 }
2955                         }
2956                 } else {
2957                         aip->target_objnum = -1;
2958                         aip->target_signature = -1;
2959                 }
2960         // Not small or small with target objnum
2961         } else {
2962                 // maybe use aip->target_objnum as next target
2963                 if ((frand() < 0.8f) && (aip->target_objnum != -1) && Use_parent_target) {
2964
2965                         //check if aip->target_objnum is valid target
2966                         int target_flags = Objects[aip->target_objnum].flags;
2967                         if ( target_flags & OF_PROTECTED ) {
2968                                 // AL 2-27-98: why is a protected ship being targeted?
2969                                 set_target_objnum(aip, -1);
2970                                 return -1;
2971                         }
2972
2973                         // maybe use ship target_objnum if valid for turret
2974                         // check for beam weapon and beam protected
2975                         if ( !((target_flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) ) {
2976                                 if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
2977                                         // check for huge weapon and huge ship
2978                                         if ( !big_only_flag || (Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
2979                                                 // check for tagged only and tagged ship
2980                                                 if ( (turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && ship_is_tagged(&Objects[aip->target_objnum]) ) {
2981                                                         // select new target if aip->target_objnum is out of field of view
2982                                                         vector v2e;
2983                                                         float dot, dist;
2984                                                         dist = vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, tpos);
2985                                                         dot = vm_vec_dot(&v2e, tvec);
2986                                                         //      MODIFY FOR ATTACKING BIG SHIP
2987                                                         // dot += (0.5f * Objects[aip->target_objnum].radius / dist);
2988                                                         if (dot > fov) {
2989                                                                 return aip->target_objnum;
2990                                                         }
2991                                                 }
2992                                         }
2993                                 }
2994                         }
2995                 }
2996         }
2997
2998         enemy_objnum = get_nearest_turret_objnum(objnum, turret_subsys, enemy_team_mask, tpos, tvec, current_enemy, big_only_flag);
2999         if ( enemy_objnum >= 0 ) {
3000                 Assert( !((Objects[enemy_objnum].flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) );
3001                 if ( Objects[enemy_objnum].flags & OF_PROTECTED ) {
3002                         Int3();
3003                         enemy_objnum = aip->target_objnum;
3004                 }
3005         }
3006
3007         return enemy_objnum;
3008 }
3009
3010 //      If issued an order to a ship that's awaiting repair, abort that process.
3011 //      However, do not abort process for an object that is currently being repaired -- let it finish.
3012 void ai_set_goal_maybe_abort_dock(object *objp, ai_info *aip)
3013 {
3014         if (aip->ai_flags & AIF_AWAITING_REPAIR) {
3015                 object  *repair_obj;
3016
3017                 if (aip->dock_objnum == -1) {
3018                         repair_obj = NULL;
3019                 } else {
3020                         repair_obj = &Objects[aip->dock_objnum];
3021                 }
3022                 ai_do_objects_repairing_stuff( objp, repair_obj, REPAIR_INFO_ABORT );
3023         }
3024         aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);    //      Might request again after 30 seconds.
3025 }
3026
3027 void force_avoid_player_check(object *objp, ai_info *aip)
3028 {
3029         if (Ships[objp->instance].team == Player_ship->team){
3030                 aip->avoid_check_timestamp = timestamp(0);              //      Force a check for collision next frame.
3031         }
3032 }
3033
3034 //      --------------------------------------------------------------------------
3035 //      Set *attacked as object to attack for object *attacker
3036 //      If attacked == NULL, then attack any enemy object.
3037 //      Attack point *rel_pos on object.  This is for supporting attacking subsystems.
3038 void ai_attack_object(object *attacker, object *attacked, int priority, ship_subsys *ssp)
3039 {
3040         ai_info *aip;
3041
3042         Assert(attacker != NULL);
3043         Assert(attacker->instance != -1);
3044         Assert(Ships[attacker->instance].ai_index != -1);
3045
3046         aip = &Ai_info[Ships[attacker->instance].ai_index];
3047         force_avoid_player_check(attacker, aip);
3048
3049         aip->ok_to_target_timestamp = timestamp(0);             //      Guarantee we can target.
3050
3051 //      if (!strnicmp(Ships[attacker->instance].ship_name, NOX("Kami"), 4)) {
3052 //              aip->ai_flags |= AIF_KAMIKAZE;
3053 //              aip->ai_flags |= AIF_NO_DYNAMIC;
3054 //      }
3055
3056         if (attacker == attacked) {
3057                 Int3();         //      Bogus!  Who tried to get me to attack myself!  Trace out and fix!
3058                 return;
3059         }
3060
3061         //      Only set to chase if a fighter or bomber, otherwise just return.
3062         if (!(Ship_info[Ships[attacker->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
3063 //              nprintf(("AI","Note: AI ship %s refusing to set AI mode to AIM_CHASE\n", Ships[attacker->instance].ship_name));
3064 //              return;
3065                 nprintf(("AI", "AI ship %s is large ship ordered to attack %s\n", Ships[attacker->instance].ship_name, Ships[attacked->instance].ship_name));
3066         }
3067
3068         //      This is how "engage enemy" gets processed
3069         if (attacked == NULL) {
3070                 aip->choose_enemy_timestamp = timestamp(0);
3071                 // nebula safe
3072                 set_target_objnum(aip, find_enemy(attacker-Objects, 99999.9f, 4));
3073         } else {
3074                 // check if we can see atacked in nebula
3075                 if (aip->target_objnum != attacked - Objects) {
3076                         aip->aspect_locked_time = 0.0f;
3077                 }
3078                 set_target_objnum(aip, attacked - Objects);
3079         }
3080
3081         ai_set_goal_maybe_abort_dock(attacker, aip);
3082         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);     //      No dynamic targeting for 7 seconds.
3083
3084         if (is_ignore_object(aip, aip->target_objnum)) {
3085                 aip->ignore_objnum = UNUSED_OBJNUM;
3086         }
3087
3088         aip->mode = AIM_CHASE;
3089         aip->submode = SM_ATTACK;       // AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
3090                                                                                 //                                       for AIM_CHASE... it may have been not set correctly here
3091         if (ssp == NULL) {
3092                 set_targeted_subsys(aip, NULL, -1);
3093                 if (aip->target_objnum != -1) {
3094                         //nprintf(("AI", "Unprotecting ship %s\n", Ships[Objects[aip->target_objnum].instance].ship_name));
3095                         Objects[aip->target_objnum].flags &= ~OF_PROTECTED;     //      If ship had been protected, unprotect it.
3096                 }
3097         } else {
3098                 Int3(); //      Not supported yet!
3099         }
3100 }
3101
3102 //      --------------------------------------------------------------------------
3103 //      Set *attacked as object to attack for object *attacker
3104 //      Attack point *rel_pos on object.  This is for supporting attacking subsystems.
3105 void ai_attack_wing(object *attacker, int wingnum, int priority)
3106 {
3107         ai_info *aip;
3108
3109         Assert(attacker != NULL);
3110         Assert(attacker->instance != -1);
3111         Assert(Ships[attacker->instance].ai_index != -1);
3112
3113         aip = &Ai_info[Ships[attacker->instance].ai_index];
3114
3115         aip->enemy_wing = wingnum;
3116         aip->mode = AIM_CHASE;
3117         aip->submode = SM_ATTACK;       // AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
3118                                                                                 //                                       for AIM_CHASE... it may have been not set correctly here
3119
3120         aip->ok_to_target_timestamp = timestamp(0);             //      Guarantee we can target.
3121
3122         int count = Wings[wingnum].current_count;
3123         if (count > 0) {
3124                 int     index;
3125
3126                 index = (int) (frand() * count);
3127
3128                 if (index >= count)
3129                         index = 0;
3130
3131                 set_target_objnum(aip, Ships[Wings[wingnum].ship_index[index]].objnum);
3132
3133                 ai_set_goal_maybe_abort_dock(attacker, aip);
3134                 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);     //      No dynamic targeting for 7 seconds.
3135         }
3136 }
3137
3138 //      --------------------------------------------------------------------------
3139 //      Set *evaded as object for *evader to evade.
3140 void ai_evade_object(object *evader, object *evaded, int priority)
3141 {
3142         ai_info *aip;
3143
3144         Assert(evader != NULL);
3145         Assert(evaded != NULL);
3146         Assert(evader->instance != -1);
3147         Assert(Ships[evader->instance].ai_index != -1);
3148
3149         if (evaded == evader) {
3150                 Int3(); //      Bogus!  Who tried to get me to evade myself!  Trace out and fix!
3151                 return;
3152         }
3153
3154         aip = &Ai_info[Ships[evader->instance].ai_index];
3155
3156         set_target_objnum(aip, evaded - Objects);
3157         aip->mode = AIM_EVADE;
3158
3159 }
3160
3161 //      Ignore some object without changing mode.
3162 void ai_ignore_object(object *ignorer, object *ignored, int priority)
3163 {
3164         ai_info *aip;
3165
3166         Assert(ignorer != NULL);
3167         Assert(ignored != NULL);
3168         Assert(ignorer->instance != -1);
3169         Assert(Ships[ignorer->instance].ai_index != -1);
3170         Assert(ignorer != ignored);
3171
3172         aip = &Ai_info[Ships[ignorer->instance].ai_index];
3173
3174         //      MK, 5/17/98, removing ignoring of wings.
3175         //      It's too confusing.  It often causes mysterious behavior in which fighters unexpectedly refuse to attack anything.
3176 /*      if (Ships[ignored->instance].wingnum > -1) {
3177                 int wingnum, i;
3178
3179                 wingnum = Ships[ignored->instance].wingnum;
3180                 aip->ignore_objnum = -(wingnum+1);
3181                 // set protected bit for each ship in a wing
3182                 //      MK, 4/23/98: Only set for fighters if they are the original "ignored" object
3183                 for (i = 0; i < Wings[wingnum].current_count; i++ ) {
3184                         object  *objp;
3185
3186                         objp = &Objects[Ships[Wings[wingnum].ship_index[i]].objnum];
3187                         if (objp != ignored) {
3188                                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))
3189                                         continue;
3190                         }
3191
3192                         Objects[Ships[Wings[wingnum].ship_index[i]].objnum].flags |= OF_PROTECTED;
3193                 }
3194
3195         } else {
3196         */ {
3197                 aip->ignore_objnum = ignored - Objects;
3198                 aip->ignore_signature = ignored->signature;
3199                 aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3200                 ignored->flags |= OF_PROTECTED;                                 // set protected bit of ignored ship.
3201         }
3202
3203 }
3204
3205 //      Ignore some object without changing mode.
3206 void ai_ignore_wing(object *ignorer, int wingnum, int priority)
3207 {
3208         ai_info *aip;
3209
3210         Assert(ignorer != NULL);
3211         Assert(ignorer->instance != -1);
3212         Assert(Ships[ignorer->instance].ai_index != -1);
3213         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
3214
3215         aip = &Ai_info[Ships[ignorer->instance].ai_index];
3216
3217         aip->ignore_objnum = -(wingnum +1);
3218         aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3219 }
3220
3221
3222 //      Add a path point in the global buffer Path_points.
3223 //      modify_index = index in Path_points at which to store path point.
3224 //      If modify_index == -1, then create a new point.
3225 //      If a new point is created (ie, modify_index == -1), then Ppfp is updated.
3226 void add_path_point(vector *pos, int path_num, int path_index, int modify_index)
3227 {
3228         pnode   *pnp;
3229
3230         if (modify_index == -1) {
3231                 Assert(Ppfp-Path_points < MAX_PATH_POINTS-1);
3232                 pnp = Ppfp;
3233                 Ppfp++;
3234         } else {
3235                 Assert((modify_index >= 0) && (modify_index < MAX_PATH_POINTS-1));
3236                 pnp = &Path_points[modify_index];
3237         }
3238
3239         pnp->pos = *pos;
3240         pnp->path_num = path_num;
3241         pnp->path_index = path_index;
3242 }
3243
3244 //      Given two points on a sphere, the center of the sphere and the radius, return a
3245 //      point on the vector through the midpoint of the chord on the sphere.
3246 void bisect_chord(vector *p0, vector *p1, vector *centerp, float radius)
3247 {
3248         vector  tvec;
3249         vector  new_pnt;
3250
3251         vm_vec_add(&tvec, p0, p1);
3252         vm_vec_sub2(&tvec, centerp);
3253         vm_vec_sub2(&tvec, centerp);
3254         if (vm_vec_mag_quick(&tvec) < 0.1f) {
3255                 vm_vec_sub(&tvec, p0, p1);
3256                 if (fl_abs(tvec.x) <= fl_abs(tvec.z)){
3257                         tvec.x = -tvec.z;
3258                 } else {
3259                         tvec.y = -tvec.x;
3260                 }
3261         }
3262
3263         vm_vec_normalize(&tvec);
3264         vm_vec_scale(&tvec, radius);
3265         vm_vec_add(&new_pnt, centerp, &tvec);
3266
3267         add_path_point(&new_pnt, -1, -1, -1);
3268 }
3269                         
3270 //      Create a path from the current position to a goal position.
3271 //      The current position is in the current object and the goal position is
3272 //      in the goal object.
3273 //      It is ok to intersect the current object, but not the goal object.
3274 //      This function is useful for creating a path to an initial point near a large
3275 //      object.
3276 //
3277 // input:       subsys_path:    optional param (default 0), indicates this is a path to a subsystem
3278 void create_path_to_point(vector *curpos, vector *goalpos, object *curobjp, object *goalobjp, int subsys_path)
3279 {
3280         //      If can't cast vector to goalpos, then create an intermediate point.
3281         if (pp_collide(curpos, goalpos, goalobjp, curobjp->radius)) {
3282                 vector  tan1;
3283                 float           radius;
3284
3285                 // If this is a path to a subsystem, use SUBSYS_PATH_DIST as the radius for the object you are
3286                 // trying to avoid.  This is needed since subsystem paths extend out to SUBSYS_PATH_DIST, and we
3287                 // want ships to reach their path destination without flying to points that sit on the radius of
3288                 // a small ship
3289                 radius = goalobjp->radius;
3290                 if (subsys_path) {
3291                         if ( SUBSYS_PATH_DIST > goalobjp->radius ) {
3292                                 radius = SUBSYS_PATH_DIST;
3293                         }
3294                 }
3295
3296                 //      The intermediate point is at the intersection of:
3297                 //              tangent to *goalobjp sphere at point *goalpos
3298                 //              tangent to *goalobjp sphere through point *curpos in plane defined by *curpos, *goalpos, goalobjp->pos
3299                 //      Note, there are two tangents through *curpos, unless *curpos is on the
3300                 //      sphere.  The tangent that causes the nearer intersection (to *goalpos) is chosen.
3301                 get_tangent_point(&tan1, curpos, &goalobjp->pos, goalpos, radius);
3302
3303                 //      If we can't reach tan1 from curpos, insert a new point.
3304                 if (pp_collide(&tan1, curpos, goalobjp, curobjp->radius))
3305                         bisect_chord(curpos, &tan1, &goalobjp->pos, radius);
3306
3307                 add_path_point(&tan1, -1, -1, -1);
3308
3309                 //      If we can't reach goalpos from tan1, insert a new point.
3310                 if (pp_collide(goalpos, &tan1, goalobjp, curobjp->radius))
3311                         bisect_chord(goalpos, &tan1, &goalobjp->pos, radius);
3312         }
3313
3314 }
3315
3316 //      Given an object and a model path, globalize the points on the model
3317 //      and copy into the global path list.
3318 //      If pnp != NULL, then modify, in place, the path points.  This is used to create new
3319 //      globalized points when the base object has moved.
3320 // input:       randomize_pnt   => optional parameter (default value -1), add random vector in sphere to this path point
3321 void copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt)
3322 {
3323         matrix  m;
3324         int             i;
3325         vector  v1;
3326         int             pp_index;               //      index in Path_points at which to store point, if this is a modify-in-place (pnp ! NULL)
3327         int             start_index, finish_index;
3328         
3329         // nprintf(("AI", "Creating path for object %s in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
3330         
3331         //      Initialize pp_index.
3332         //      If pnp == NULL, that means we're creating new points.  If not NULL, then modify in place.
3333         if (pnp == NULL)
3334                 pp_index = -1;                  //      This tells add_path_point to create a new point.
3335         else
3336                 pp_index = 0;                   //      pp_index will get assigned to index in Path_points to reuse.
3337
3338         vm_copy_transpose_matrix(&m, &objp->orient);
3339
3340         if (dir == 1) {
3341                 start_index = 0;
3342                 finish_index = min(count, mp->nverts);
3343         } else {
3344                 Assert(dir == -1);      //      direction must be up by 1 or down by 1 and it's neither!
3345                 start_index = mp->nverts-1;
3346                 finish_index = max(-1, mp->nverts-1-count);
3347         }
3348
3349         int offset = 0;
3350         for (i=start_index; i != finish_index; i += dir) {
3351                 //      Globalize the point.
3352                 vm_vec_rotate(&v1, &mp->verts[i].pos, &m);
3353                 vm_vec_add2(&v1, &objp->pos);
3354
3355                 if ( randomize_pnt == i ) {
3356                         vector v_rand;
3357                         static_randvec(OBJ_INDEX(objp), &v_rand);
3358                         vm_vec_scale(&v_rand, 30.0f);
3359                         vm_vec_add2(&v1, &v_rand);
3360                 }
3361
3362                 if (pp_index != -1)
3363                         pp_index = pnp-Path_points + offset;
3364
3365                 add_path_point(&v1, path_num, i, pp_index);
3366                 offset++;
3367         }
3368 }
3369
3370
3371 //      For pl_objp, create a path along path path_num into mobjp.
3372 //      The tricky part of this problem is creating the entry to the first point on the
3373 //      predefined path.  The points on this entry path are based on the location of Pl_objp
3374 //      relative to the start of the path.
3375 //
3376 // input:
3377 //                              subsys_path:    optional param (default 0), indicating this is a path to a subsystem
3378 void create_model_path(object *pl_objp, object *mobjp, int path_num, int subsys_path)
3379 {       
3380         ship                    *shipp = &Ships[pl_objp->instance];
3381         ai_info         *aip = &Ai_info[shipp->ai_index];
3382
3383         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3384         polymodel       *pm = model_get(osip->modelnum);
3385         int                     num_points;
3386         model_path      *mp;
3387         pnode                   *ppfp_start = Ppfp;
3388         matrix          m;
3389         vector          gp0;
3390
3391         Assert(path_num >= 0);
3392
3393         //      Do garbage collection if necessary.
3394         if (Ppfp-Path_points + 64 > MAX_PATH_POINTS) {
3395                 garbage_collect_path_points();
3396                 ppfp_start = Ppfp;
3397         }
3398
3399         aip->path_start = Ppfp - Path_points;
3400         Assert(path_num < pm->n_paths);
3401         
3402         mp = &pm->paths[path_num];
3403         num_points = mp->nverts;
3404
3405         Assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
3406
3407         vm_copy_transpose_matrix(&m, &mobjp->orient);
3408         vm_vec_rotate(&gp0, &mp->verts[0].pos, &m);
3409         vm_vec_add2(&gp0, &mobjp->pos);
3410
3411         if (pp_collide(&pl_objp->pos, &gp0, mobjp, pl_objp->radius)) {
3412                 vector  perim_point1;
3413                 vector  perim_point2;
3414
3415                 perim_point2 = pl_objp->pos;
3416                 
3417                 //      If object that wants to dock is inside bounding sphere of object it wants to dock with, make it fly out.
3418                 //      Assume it can fly "straight" out to the bounding sphere.
3419                 if (vm_vec_dist_quick(&pl_objp->pos, &mobjp->pos) < mobjp->radius) {
3420                         project_point_to_perimeter(&perim_point2, &mobjp->pos, mobjp->radius, &pl_objp->pos);
3421                         add_path_point(&perim_point2, path_num, -1, -1);
3422                 }
3423
3424                 //      If last point on pre-defined path is inside bounding sphere, create a new point on the surface of the sphere.
3425                 if (vm_vec_dist_quick(&mobjp->pos, &gp0) < mobjp->radius) {
3426                         project_point_to_perimeter(&perim_point1, &mobjp->pos, mobjp->radius, &gp0);
3427                         create_path_to_point(&perim_point2, &perim_point1, pl_objp, mobjp, subsys_path);
3428                         add_path_point(&perim_point1, path_num, -1, -1);
3429                 } else {                //      The predefined path extends outside the sphere.  Create path to that point.
3430                         create_path_to_point(&perim_point2, &gp0, pl_objp, mobjp, subsys_path);
3431                 }
3432         }
3433
3434         // AL 12-31-97: If following a subsystem path, add random vector to second last path point
3435         if ( subsys_path ) {
3436                 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL, mp->nverts-2);
3437         } else {
3438                 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL);
3439         }
3440
3441         aip->path_cur = aip->path_start;
3442         aip->path_dir = PD_FORWARD;
3443         aip->path_objnum = mobjp-Objects;
3444         aip->mp_index = path_num;
3445         aip->path_length = Ppfp - ppfp_start;
3446         aip->path_next_check_time = timestamp(1);
3447
3448         aip->path_goal_obj_hash = create_object_hash(&Objects[aip->path_objnum]);
3449
3450         aip->path_next_create_time = timestamp(1000);   //      OK to try to create one second later
3451         aip->path_create_pos = pl_objp->pos;
3452         aip->path_create_orient = pl_objp->orient;
3453
3454         aip->ai_flags &= ~AIF_USE_EXIT_PATH;                    // ensure this flag is cleared
3455 }
3456
3457 //      For pl_objp, create a path along path path_num into mobjp.
3458 //      The tricky part of this problem is creating the entry to the first point on the
3459 //      predefined path.  The points on this entry path are based on the location of pl_objp
3460 //      relative to the start of the path.
3461 void create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count)
3462 {       
3463         ship                    *shipp = &Ships[pl_objp->instance];
3464         ai_info         *aip = &Ai_info[shipp->ai_index];
3465
3466         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3467         polymodel       *pm = model_get(osip->modelnum);
3468         int                     num_points;
3469         model_path      *mp;
3470         pnode                   *ppfp_start = Ppfp;
3471
3472         aip->path_start = Ppfp - Path_points;
3473         Assert(path_num < pm->n_paths);
3474         
3475         mp = &pm->paths[path_num];
3476         num_points = mp->nverts;
3477
3478         Assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
3479
3480         copy_xlate_model_path_points(mobjp, mp, -1, count, path_num, NULL);
3481
3482         aip->path_cur = aip->path_start;
3483         aip->path_dir = PD_FORWARD;
3484         aip->path_objnum = mobjp-Objects;
3485         aip->mp_index = path_num;
3486         aip->path_length = Ppfp - ppfp_start;
3487         aip->path_next_check_time = timestamp(1);
3488
3489         aip->ai_flags |= AIF_USE_EXIT_PATH;             // mark as exit path, referenced in maybe
3490 }
3491
3492 //      Return true if the vector from curpos to goalpos intersects with any ship other than the ignore objects.
3493 //      Calls pp_collide
3494 int pp_collide_any(vector *curpos, vector *goalpos, float radius, object *ignore_objp1, object *ignore_objp2, int big_only_flag)
3495 {
3496         ship_obj        *so;    
3497
3498         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
3499                 object *objp = &Objects[so->objnum];
3500
3501                 if (big_only_flag) {
3502                         if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
3503                                 continue;
3504                 }
3505
3506                 if ((objp != ignore_objp1) && (objp != ignore_objp2)) {
3507                         if (pp_collide(curpos, goalpos, objp, radius))
3508                                 return OBJ_INDEX(objp);
3509                 }
3510         }
3511
3512         return -1;
3513 }
3514
3515 //      Used to create docking paths and other pre-defined paths through ships.
3516 //      Creates a path in absolute space.
3517 //      Create a path into the object objnum.
3518 //
3519 // input:
3520 //      pl_objp:                        object that will use the path
3521 //      objnum:                 Object to find path to.
3522 //      path_num:               model path index to use
3523 //      exit_flag:              true means this is an exit path in the model
3524 // subsys_path: optional param (default 0) that indicates this is a path to a subsystem
3525 //      Exit:
3526 //      ai_info struct in Pl_objp gets stuffed with information to enable Pl_objp to fly the path.
3527 void ai_find_path(object *pl_objp, int objnum, int path_num, int exit_flag, int subsys_path)
3528 {
3529         ai_info *aip = &Ai_info[Ships[pl_objp->instance].ai_index];
3530
3531         Assert(path_num >= 0);
3532
3533         //      This is test code, find an object with paths.
3534         if (objnum != -1) {
3535                 object  *objp = &Objects[objnum];
3536
3537                 if (objp->type == OBJ_SHIP) {
3538                         polymodel *pm;
3539
3540                         ship    *shipp = &Ships[objp->instance];
3541                         pm = model_get( shipp->modelnum );
3542                         Assert(pm->n_paths > path_num);
3543                         aip->goal_objnum = objp-Objects;
3544                         aip->goal_signature = objp->signature;
3545                         if (exit_flag)
3546                                 create_model_exit_path(pl_objp, objp, path_num);
3547                         else
3548                                 create_model_path(pl_objp, objp, path_num, subsys_path);
3549                         return;
3550                 }
3551
3552         }
3553 }
3554
3555 extern int vector_object_collision(vector *start_pos, vector *end_pos, object *objp, float radius_scale);
3556
3557 //      Maybe make *objp avoid a player object.
3558 //      For now, 4/6/98, only check Player_obj.
3559 //      If player collision would occur, set AIF_AVOIDING_SMALL_SHIP bit in ai_flags.
3560 //      Set aip->avoid_goal_point
3561 int maybe_avoid_player(object *objp, vector *goal_pos)
3562 {
3563         ai_info *aip;
3564         vector  cur_pos, new_goal_pos;
3565         object  *player_objp;
3566         vector  n_vec_to_goal, n_vec_to_player;
3567
3568         aip = &Ai_info[Ships[objp->instance].ai_index];
3569
3570         if (!timestamp_elapsed(aip->avoid_check_timestamp))
3571                 return 0;
3572
3573         player_objp = Player_obj;
3574
3575         float   speed_time;
3576
3577         //      How far two ships could be apart and still collide within one second.
3578         speed_time = player_objp->phys_info.speed + objp->phys_info.speed;
3579
3580         float   obj_obj_dist;
3581
3582         obj_obj_dist = vm_vec_dist_quick(&player_objp->pos, &objp->pos);
3583
3584         if (obj_obj_dist > speed_time*2.0f)
3585                 return 0;
3586
3587         cur_pos = objp->pos;
3588
3589         new_goal_pos = *goal_pos;
3590
3591         float dist = vm_vec_normalized_dir(&n_vec_to_goal, goal_pos, &objp->pos);
3592         vm_vec_normalized_dir(&n_vec_to_player, &player_objp->pos, &objp->pos);
3593
3594         if (dist > speed_time*2.0f) {
3595                 vm_vec_scale_add(&new_goal_pos, &objp->pos, &n_vec_to_goal, 200.0f);
3596         }
3597
3598         if (vector_object_collision(&objp->pos, &new_goal_pos, player_objp, 1.5f)) {
3599                 aip->ai_flags |= AIF_AVOIDING_SMALL_SHIP;
3600
3601                 vector  avoid_vec;
3602
3603                 vm_vec_sub(&avoid_vec, &n_vec_to_goal, &n_vec_to_player);
3604                 if (vm_vec_mag_quick(&avoid_vec) < 0.01f) {
3605                         vm_vec_copy_scale(&avoid_vec, &objp->orient.rvec, frand()-0.5f);
3606                         vm_vec_scale_add2(&avoid_vec, &objp->orient.uvec, frand()-0.5f);
3607                         vm_vec_normalize(&avoid_vec);
3608                 } else {
3609                         vector  tvec1;
3610                         vm_vec_normalize(&avoid_vec);
3611                         vm_vec_crossprod(&tvec1, &n_vec_to_goal, &avoid_vec);
3612                         vm_vec_crossprod(&avoid_vec, &tvec1, &n_vec_to_player);
3613                 }
3614
3615                 //      Now, avoid_vec is a vector perpendicular to the vector to the player and the direction *objp
3616                 //      should fly in to avoid the player while still approaching its goal.
3617                 vm_vec_scale_add(&aip->avoid_goal_point, &player_objp->pos, &avoid_vec, 400.0f);
3618
3619                 aip->avoid_check_timestamp = timestamp(1000);
3620
3621                 return 1;
3622         } else {
3623                 aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
3624                 aip->avoid_check_timestamp = timestamp((int) (obj_obj_dist/200.0f) + 500);
3625
3626                 return 0;
3627         }
3628 }
3629
3630 //      Make object *still_objp enter AIM_STILL mode.
3631 //      Make it point at view_pos.
3632 void ai_stay_still(object *still_objp, vector *view_pos)
3633 {
3634         ship    *shipp;
3635         ai_info *aip;
3636
3637         Assert(still_objp->type == OBJ_SHIP);
3638         Assert((still_objp->instance >= 0) && (still_objp->instance < MAX_OBJECTS));
3639
3640         shipp = &Ships[still_objp->instance];
3641         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
3642
3643         aip = &Ai_info[shipp->ai_index];
3644
3645         aip->mode = AIM_STILL;
3646
3647         //      If view_pos not NULL, point at that point.  Else, point at a point directly in front of ship.  Ie, don't turn.
3648         if (view_pos != NULL)
3649                 aip->goal_point = *view_pos;
3650         else
3651                 vm_vec_scale_add(&aip->goal_point, &still_objp->pos, &still_objp->orient.fvec, 100.0f);
3652 }
3653
3654 // code which is called from ai_dock_with_object and ai_dock to set flags and apprioriate variable
3655 // when two objects have completed docking.  used because we can dock object initially at misison load
3656 // time (meaning that ai_dock() might never get called).  docker has docked with dockee (i.e. docker
3657 // would be a freighter and dockee would be a cargo).
3658 void ai_do_objects_docked_stuff(object *docker, object *dockee)
3659 {
3660         ai_info *aip, *other_aip;
3661
3662         aip = &Ai_info[Ships[docker->instance].ai_index];
3663         other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3664
3665         // set the flags and dock_objnum for both objects
3666         aip->ai_flags |= AIF_DOCKED;
3667         aip->dock_objnum = OBJ_INDEX(dockee);
3668         other_aip->ai_flags |= AIF_DOCKED;
3669         other_aip->dock_objnum = OBJ_INDEX(docker);
3670         aip->dock_signature = dockee->signature;
3671         other_aip->dock_signature = docker->signature;
3672
3673         // add multiplayer hook here to deal with docked objects.  We need to only send information
3674         // about the object that is docking.  Both flags will get updated.
3675         if ( MULTIPLAYER_MASTER )
3676                 send_ai_info_update_packet( docker, AI_UPDATE_DOCK );
3677
3678 }
3679
3680 // code which is called when objects become undocked. Equivalent of above function.
3681 // dockee might not be valid since this code can get called to cleanup after a ship
3682 // has blown up!
3683 void ai_do_objects_undocked_stuff( object *docker, object *dockee )
3684 {
3685         ai_info *aip, *other_aip;
3686
3687         // add multiplayer hook here to deal with undocked objects.  Do it before we
3688         // do anything else.  We don't need to send info for both objects, since we can find
3689         // it be dock_objnum
3690         if ( MULTIPLAYER_MASTER )
3691                 send_ai_info_update_packet( docker, AI_UPDATE_UNDOCK );
3692
3693         aip = &Ai_info[Ships[docker->instance].ai_index];
3694
3695         // set the flags and dock_objnum for both objects
3696         aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3697         aip->dock_objnum = -1;
3698         
3699         if ( dockee != NULL ) {
3700                 other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3701                 other_aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3702                 other_aip->dock_objnum = -1;
3703         }
3704
3705 }
3706
3707
3708 //      --------------------------------------------------------------------------
3709 //      Interface from goals code to AI.
3710 //      Cause *docker to dock with *dockee.
3711 //      priority is priority of goal from goals code.
3712 //      dock_type is:
3713 //              AIDO_DOCK               set goal of docking
3714 //              AIDO_DOCK_NOW   immediately dock, used for ships that need to be docked at mission start
3715 //              AIDO_UNDOCK             set goal of undocking
3716 void ai_dock_with_object(object *docker, object *dockee, int priority, int dock_type, int docker_index, int dockee_index)
3717 {
3718         ai_info         *aip;
3719         polymodel       *pm;
3720         ai_info         *dockee_aip;
3721
3722         Assert(docker != NULL);
3723         Assert(dockee != NULL);
3724         Assert(docker->instance != -1);
3725         Assert(Ships[docker->instance].ai_index != -1);
3726         Assert(Ships[dockee->instance].ai_index != -1);
3727         Assert( docker_index != -1 );
3728         Assert( dockee_index != -1 );
3729
3730         aip = &Ai_info[Ships[docker->instance].ai_index];
3731
3732         if ((aip->ai_flags & AIF_DOCKED) && (dock_type == AIDO_DOCK)) {
3733                 object  *dockee2;
3734                 int             docker_index2, dockee_index2;
3735
3736                 Assert(aip->dock_objnum > -1);
3737                 dockee2 = &Objects[aip->dock_objnum];
3738                 docker_index2 = aip->dock_index;
3739                 dockee_index2 = aip->dockee_index;
3740                 // MWA -- 2/9/98.  use the goal code to undock the ships since goals might need to get removed
3741                 // and that code will do it properly.  I'd actually be surprised if we got into this code anymore
3742                 // since the outer layer goal code should deal with this issue....but who knows...
3743                 ai_add_goal_ship_internal( aip, AI_GOAL_UNDOCK, NULL, -1, -1, 0 );
3744
3745                 // old code below
3746                 //ai_dock_with_object(docker, dockee2, priority, AIDO_UNDOCK, docker_index2, dockee_index2);
3747                 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));
3748                 nprintf(("AI", "...so ship %s will now undock.\n", Ships[docker->instance].ship_name));
3749                 return;
3750         }
3751
3752         dockee_aip = &Ai_info[Ships[dockee->instance].ai_index];
3753
3754         aip->goal_objnum = dockee - Objects;
3755         aip->goal_signature = dockee->signature;
3756
3757         aip->mode = AIM_DOCK;
3758
3759         switch (dock_type) {
3760         case AIDO_DOCK:
3761                 aip->submode = AIS_DOCK_0;
3762                 break;
3763         case AIDO_DOCK_NOW:
3764                 aip->submode = AIS_DOCK_3A;
3765                 break;
3766         case AIDO_UNDOCK:
3767                 aip->submode = AIS_UNDOCK_0;
3768                 break;
3769         default:
3770                 Int3();         //      Bogus dock_type.
3771         }
3772
3773         aip->submode_start_time = Missiontime;
3774         aip->dock_index = docker_index;
3775         aip->dockee_index = dockee_index;
3776
3777         dockee_aip->dock_index = dockee_index;
3778         dockee_aip->dockee_index = docker_index;
3779
3780         // get the path number to the docking point on the dockee.  Each docking point contains a list
3781         // of paths that the point can be reached by.  Pick the first path in the path list for now.
3782         // We only want to do this stuff if we are docking!!!  Be sure to set the path index
3783         if ((dock_type == AIDO_DOCK) || (dock_type == AIDO_DOCK_NOW)) {
3784                 pm = model_get( Ships[dockee->instance].modelnum );
3785                 Assert( pm->docking_bays[dockee_index].num_spline_paths > 0 );
3786
3787                 // only set the dock path index if we are docking.  undocking will assume that dock_path_index
3788                 // already set from some other docking command
3789                 aip->dock_path_index = dockee_index;
3790                 dockee_aip->dock_path_index = docker_index;
3791         }
3792
3793         if (dock_type != AIDO_DOCK_NOW) {
3794                 int path_num;
3795                 //      Note: Second parameter is dock path index.  This should be specified as an
3796                 //      _input_ to this function and passed through.  The path index should be already
3797                 // set for the undock function
3798                 path_num = ai_return_path_num_from_dockbay(dockee, dockee_index);
3799                 ai_find_path(docker, dockee-Objects, path_num, 0);
3800 //              ai_find_path(dockee-Objects, dockee_index, 0);
3801         } else {
3802                 dock_orient_and_approach(docker, dockee, DOA_DOCK_STAY);
3803                 //aip->dock_objnum = OBJ_INDEX(dockee);
3804                 ai_do_objects_docked_stuff( docker, dockee );
3805         }
3806
3807 }
3808
3809 //      Cause a ship to fly its waypoints.
3810 //      flags tells:
3811 //              WPF_REPEAT      Set -> repeat waypoints.
3812 void ai_start_waypoints(object *objp, int waypoint_list_index, int wp_flags)
3813 {
3814         ai_info *aip;
3815
3816         Assert(waypoint_list_index < Num_waypoint_lists);
3817
3818         //nprintf(("AI", "Frame %i: Ship %s instructed to fly waypoint list #%i\n", AI_FrameCount, Ships[objp->instance].ship_name, waypoint_list_index));
3819         aip = &Ai_info[Ships[objp->instance].ai_index];
3820
3821         if ( (aip->mode == AIM_WAYPOINTS) && (aip->wp_index == waypoint_list_index) )
3822                 return;
3823
3824         aip->ai_flags |= AIF_FORMATION_WING;
3825         aip->ai_flags &= ~AIF_FORMATION_OBJECT;
3826         aip->wp_list = waypoint_list_index;
3827         aip->wp_index = 0;
3828         aip->wp_flags = wp_flags;
3829         aip->mode = AIM_WAYPOINTS;
3830
3831         Assert(aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC);
3832 }
3833
3834 //      Make *objp stay within dist units of *other_objp
3835 void ai_do_stay_near(object *objp, object *other_objp, float dist)
3836 {
3837         ai_info *aip;
3838
3839         Assert(objp != other_objp);             //      Bogus!  Told to stay near self.
3840         Assert(objp->type == OBJ_SHIP);
3841         Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
3842
3843         aip = &Ai_info[Ships[objp->instance].ai_index];
3844
3845         aip->mode = AIM_STAY_NEAR;
3846         aip->submode = -1;
3847         aip->stay_near_distance = dist;
3848         aip->goal_objnum = other_objp-Objects;
3849         aip->goal_signature = other_objp->signature;
3850
3851 }
3852
3853 //      Make object *objp form on wing of object *goal_objp
3854 void ai_form_on_wing(object *objp, object *goal_objp)
3855 {
3856         ai_info *aip;
3857         ship                    *shipp;
3858         ship_info       *sip;
3859
3860         // objp == goal_objp sometimes in multiplayer when someone leaves a game -- make a simple
3861         // out for this case.
3862         if ( Game_mode & GM_MULTIPLAYER ) {
3863                 if ( objp == goal_objp ) {
3864                         return;
3865                 }
3866         }
3867
3868         Assert(objp != goal_objp);              //      Bogus!  Told to form on own's wing!
3869
3870         shipp = &Ships[objp->instance];
3871         sip = &Ship_info[shipp->ship_info_index];
3872
3873         //      Only fighters or bombers allowed to form on wing.
3874         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
3875                 nprintf(("AI", "Warning: Ship %s tried to form on player's wing, but not fighter or bomber.\n", shipp->ship_name));
3876                 return;
3877         }
3878
3879         aip = &Ai_info[Ships[objp->instance].ai_index];
3880
3881         aip->ai_flags &= ~AIF_FORMATION_WING;
3882         aip->ai_flags |= AIF_FORMATION_OBJECT;
3883
3884         aip->goal_objnum = goal_objp-Objects;
3885         ai_set_goal_maybe_abort_dock(objp, aip);
3886         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME*4);           //      Super extra long time until can target another ship.
3887
3888 }
3889
3890 //      Given an object and an object on whose wing to form, return slot to use.
3891 //      Optimize:
3892 //              This function is called per object in formation per frame.  Should store slot in ai_info struct.
3893 int ai_formation_object_get_slotnum(int objnum, object *objp)
3894 {
3895         int     slotnum = 1;                    //      Note: Slot #0 means leader, which isn't someone who was told to form-on-wing.
3896         object *o;
3897
3898         for ( o = GET_FIRST(&obj_used_list); o != END_OF_LIST(&obj_used_list); o = GET_NEXT(o) ) {
3899                 if (objp == o)
3900                         break;
3901                 else if (o->type == OBJ_SHIP)
3902                         if (Ai_info[Ships[o->instance].ai_index].ai_flags & AIF_FORMATION_OBJECT)
3903                                 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == objnum)
3904                                         slotnum++;
3905         }
3906
3907         Assert(o != END_OF_LIST(&obj_used_list));       //      Didn't find objp in list of used ships.  Impossible!
3908
3909         return slotnum;
3910 }
3911
3912 #define BIGNUM  100000.0f
3913
3914 int Debug_k = 0;
3915
3916 //      Given an attacker's position and a target's position and velocity, compute the time of
3917 //      intersection of a weapon fired by the attacker with speed weapon_speed.
3918 //      Return this value.  Return value of 0.0f means no collision is possible.
3919 float compute_collision_time(vector *targpos, vector *targvel, vector *attackpos, float weapon_speed)
3920 {
3921         vector  vec_to_target;
3922         float           pos_dot_vel;
3923         float           vel_sqr;
3924         float           discrim;
3925
3926         vm_vec_sub(&vec_to_target, targpos, attackpos);
3927         pos_dot_vel = vm_vec_dot(&vec_to_target, targvel);
3928         vel_sqr = vm_vec_dot(targvel, targvel) - weapon_speed*weapon_speed;
3929         discrim = pos_dot_vel*pos_dot_vel - vel_sqr*vm_vec_dot(&vec_to_target, &vec_to_target);
3930
3931         if (discrim > 0.0f) {
3932                 float   t1, t2, t_solve;
3933
3934                 t1 = (-pos_dot_vel + fl_sqrt(discrim)) / vel_sqr;
3935                 t2 = (-pos_dot_vel - fl_sqrt(discrim)) / vel_sqr;
3936
3937                 t_solve = BIGNUM;
3938
3939                 if (t1 > 0.0f)
3940                         t_solve = t1;
3941                 if ((t2 > 0.0f) && (t2 < t_solve))
3942                         t_solve = t2;
3943
3944                 if (t_solve < BIGNUM-1.0f) {
3945                         return t_solve + Debug_k * flFrametime;
3946                 }
3947         }
3948
3949         return 0.0f;
3950 }
3951
3952
3953 //      --------------------------------------------------------------------------
3954 //      If far away, use player's speed.
3955 //      If in between, lerp between player and laser speed
3956 //      If close, use laser speed.
3957 // Want to know how much time it will take to get to the enemy.
3958 // This function doesn't account for the fact that by the time the player
3959 // (or his laser) gets to the current enemy position, the enemy will have moved.
3960 // This is dealt with in polish_predicted_enemy_pos.
3961 float compute_time_to_enemy(float dist_to_enemy, object *pobjp, object *eobjp)
3962 {
3963         float   time_to_enemy;
3964         float   pl_speed = pobjp->phys_info.speed;
3965         float   max_laser_distance, max_laser_speed;
3966         int     bank_num, weapon_num;
3967         ship    *shipp = &Ships[pobjp->instance];
3968
3969         bank_num = shipp->weapons.current_primary_bank;
3970         weapon_num = shipp->weapons.primary_bank_weapons[bank_num];
3971         max_laser_speed = Weapon_info[weapon_num].max_speed;
3972         max_laser_distance = max_laser_speed * Weapon_info[weapon_num].lifetime;
3973
3974         //      If pretty far away, use player's speed to predict position, else
3975         //      use laser's speed because when close, we care more about hitting
3976         //      with a laser than about causing ship:ship rendezvous.
3977         if (dist_to_enemy > 1.5 * max_laser_distance) {
3978                 if (pl_speed > 0.0f)
3979                         time_to_enemy = dist_to_enemy/pl_speed;
3980                 else
3981                         time_to_enemy = 1.0f;
3982         } else if (dist_to_enemy > 1.1*max_laser_distance) {
3983                 if (pl_speed > 0.1f) {
3984                         float   scale;
3985
3986                         scale = (float) ((dist_to_enemy - max_laser_distance) / max_laser_distance);
3987                 
3988                         time_to_enemy = (float) (dist_to_enemy/(pl_speed * scale + max_laser_speed * (1.0f - scale)));
3989                 } else
3990                         time_to_enemy = 2.0f;
3991         } else
3992                 time_to_enemy = (float) (dist_to_enemy/max_laser_speed);
3993
3994         // return time_to_enemy * (1.0f + Ai_info[Ships[pobjp->instance].ai_index].lead_scale);
3995         return time_to_enemy + flFrametime;
3996 }
3997
3998 //      Stuff *dot and *tts.
3999 //      *dot is always computed.  If dot is less than zero, the magnitude is
4000 //      incorrect, not having been divided by distance.
4001 //      If *dot is > 0.0f, then tts is computed.  This is the time it will take object
4002 //      *objp to get to *pos, assuming it moves right at it.
4003 void fds_aux(float *dot, float *tts, vector *pos, float dtime, object *objp)
4004 {
4005         vector  v2s;
4006
4007         vm_vec_sub(&v2s, pos, &objp->pos);
4008         *dot = vm_vec_dot(&v2s, &objp->orient.fvec);
4009
4010         if (*dot > 0.0f) {
4011                 float   dist;
4012
4013                 dist = vm_vec_dist(&objp->pos, pos);
4014
4015                 if (dist > 0.1f)
4016                         *dot /= dist;
4017                 else
4018                         *dot = 1.0f;
4019
4020                 if (objp->phys_info.speed > 0.1f)
4021                         *tts = dist / objp->phys_info.speed;
4022                 else
4023                         *tts = dist * 100.0f;
4024         }
4025 }
4026
4027 /*
4028 //      Return index of weapon that could hit object *sobjp within dtime seconds.
4029 //      Actual time until impact returned in *atime.
4030 int find_danger_weapon(object *sobjp, float dtime, float *atime, float dot_threshhold)
4031 {
4032         object  *objp, *best_objp = NULL;
4033         float           best_tts = 1000.0f;
4034
4035         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
4036                 if ((objp->type == OBJ_WEAPON) && (sobjp-Objects != objp->parent)) {
4037                         float           dot, tts;
4038                         // vector       psp;            //      Predicted ship position.
4039
4040                         //      Get dot and time to current ship position.
4041                         fds_aux(&dot, &tts, &sobjp->pos, dtime, objp);
4042
4043                         //      If dot and tts are in plausible range, do more expensive stuff.
4044                         if (dot > 0.98f) {
4045 //                              float   dot_from_sobjp;
4046                                 vector  v2e;
4047
4048                                 vm_vec_normalized_dir(&v2e, &objp->pos, &sobjp->pos);
4049 //                              dot_from_sobjp = vm_vec_dot(&sobjp->orient.fvec, &v2e);
4050 //                              if (dot_from_sobjp >= dot_threshhold)
4051                                         if (tts < dtime) {
4052                                                 if (tts < best_tts) {
4053                                                         best_tts = tts;
4054                                                         best_objp = objp;
4055                                                 }
4056                                         }
4057                         }
4058                 }
4059         }
4060
4061         *atime = best_tts;
4062
4063         if (best_objp != NULL)
4064                 return best_objp-Objects;
4065         else
4066                 return -1;
4067 }
4068 */
4069
4070 //      --------------------------------------------------------------------------
4071 void ai_set_positions(object *pl_objp, object *en_objp, ai_info *aip, vector *player_pos, vector *enemy_pos)
4072 {
4073         *player_pos = pl_objp->pos;
4074
4075         if (aip->next_predict_pos_time > Missiontime) {
4076                 *enemy_pos = aip->last_predicted_enemy_pos;
4077         } else {
4078                 *enemy_pos = en_objp->pos;
4079
4080                 aip->next_predict_pos_time = Missiontime + Skill_level_delay[Game_skill_level];
4081                 aip->last_predicted_enemy_pos = *enemy_pos;
4082         }
4083
4084
4085 }
4086
4087 //      --------------------------------------------------------------------------
4088 int find_nearest_waypoint(object *objp)
4089 {
4090         int     i;
4091         float   dist, min_dist, dot;
4092         int     min_ind;
4093         ship    *shipp;
4094         int     wp_listnum;
4095         waypoint_list   *wpl;
4096
4097         shipp = &Ships[objp->instance];
4098         wp_listnum = Ai_info[Ships[objp->instance].ai_index].wp_list;
4099         Assert(wp_listnum > 0);
4100         wpl = &Waypoint_lists[wp_listnum];
4101
4102         min_dist = 999999.0f;
4103         min_ind = -1;
4104
4105         for (i=0; i<wpl->count; i++) {
4106                 dist = vm_vec_dist_quick(&objp->pos, &wpl->waypoints[i]);
4107                 dot = vm_vec_dot_to_point(&objp->orient.fvec, &objp->pos, &wpl->waypoints[i]);
4108                 dist = (float) (dist * (1.25 - dot));
4109                 if (dist < min_dist) {
4110                         min_dist = dist;
4111                         min_ind = i;
4112                 }
4113         }
4114
4115         Assert(min_ind != -1);
4116
4117         return min_ind;
4118 }
4119
4120 //      Given an ai_info struct, by reading current goal and path information,
4121 //      extract base path information and return in pmp and pmpv.
4122 //      Return true if found, else return false.
4123 //      false means the current point is not on the original path.
4124 int get_base_path_info(int path_cur, int goal_objnum, model_path **pmp, mp_vert **pmpv)
4125 {
4126         pnode                   *pn = &Path_points[path_cur];
4127         ship_info       *sip = &Ship_info[Ships[Objects[goal_objnum].instance].ship_info_index];
4128         polymodel       *pm = model_get(sip->modelnum);
4129         static          int     debug_last_index = -1;
4130         *pmpv = NULL;
4131         *pmp = NULL;
4132
4133         if (pn->path_num != -1) {
4134                 *pmp = &pm->paths[pn->path_num];
4135                 if (pn->path_index != -1)
4136                         *pmpv = &(*pmp)->verts[pn->path_index];
4137                 else
4138                         return 0;
4139         } else
4140                 return 0;
4141
4142 /*      if (debug_last_index != *pmpv-(*pmp)->verts) {
4143                 debug_last_index = *pmpv-(*pmp)->verts;
4144                 nprintf(("AI", "Point %i has %i turrets: ", *pmpv-(*pmp)->verts, (*pmpv)->nturrets));
4145                 for (int i=0; i<(*pmpv)->nturrets; i++) {
4146                         nprintf(("AI", "%i ", (*pmpv)->turret_ids[i]));
4147                 }
4148                 nprintf(("AI", "\n"));
4149         }
4150 */
4151         return 1;
4152 }
4153
4154 //      Modify, in place, the points in a global model path.
4155 //      Only modify those points that are defined in the model path.  Don't modify the
4156 //      leadin points, such as those that are necessary to get the model on the path.
4157 void modify_model_path_points(object *objp)
4158 {       
4159         ai_info         *aip = &Ai_info[Ships[objp->instance].ai_index];
4160         object          *mobjp = &Objects[aip->path_objnum];
4161         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
4162         polymodel       *pm = model_get(osip->modelnum);
4163         pnode                   *pnp;
4164         int                     path_num, dir;
4165
4166         Assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
4167
4168         pnp = &Path_points[aip->path_start];
4169         while ((pnp->path_index == -1) && (pnp-Path_points - aip->path_start < aip->path_length))
4170                 pnp++;
4171
4172         path_num = pnp->path_num;
4173         Assert((path_num >= 0) && (path_num < pm->n_paths));
4174         
4175         Assert(pnp->path_index != -1);  //      If this is -1, that means we never found the model path points
4176
4177         dir = 1;
4178         if ( aip->ai_flags & AIF_USE_EXIT_PATH ) {
4179                 dir = -1;
4180         }
4181
4182         copy_xlate_model_path_points(mobjp, &pm->paths[path_num], dir, pm->paths[path_num].nverts, path_num, pnp);
4183 }
4184
4185 //      Return an indication of the distance between two matrices.
4186 //      This is the sum of the distances of their dot products from 1.0f.
4187 float ai_matrix_dist(matrix *mat1, matrix *mat2)
4188 {
4189         float   t;
4190
4191         t =  1.0f - vm_vec_dot(&mat1->fvec, &mat2->fvec);
4192         t += 1.0f - vm_vec_dot(&mat1->uvec, &mat2->uvec);
4193         t += 1.0f - vm_vec_dot(&mat1->rvec, &mat2->rvec);
4194
4195         return t;
4196 }
4197
4198
4199 //      Paths are created in absolute space, so a moving object needs to have model paths within it recreated.
4200 //      This uses the hash functions which means the slightest movement will cause a recreate, though the timestamp
4201 //      prevents this from happening too often.
4202 //      force_recreate_flag TRUE means to recreate regardless of timestamp.
4203 //      Returns TRUE if path recreated.
4204 float maybe_recreate_path(object *objp, ai_info *aip, int force_recreate_flag)
4205 {
4206         int     hashval;
4207
4208         Assert(&Ai_info[Ships[objp->instance].ai_index] == aip);
4209
4210         if ((aip->mode == AIM_BAY_EMERGE) || (aip->mode == AIM_BAY_DEPART))
4211                 if ((OBJ_INDEX(objp) % 4) == (Framecount % 4))
4212                         force_recreate_flag = 1;
4213
4214         //      If no path, that means we don't need one.
4215         if (aip->path_start == -1)
4216                 return 0.0f;
4217
4218         // AL 11-12-97: If AIF_USE_STATIC_PATH is set, don't try to recreate.  This is needed when ships
4219         //                                  emerge from fighter bays.  We don't need to recreate the path.. and in case the 
4220         //              parent ship dies, we still want to be able to continue on the path
4221         if ( aip->ai_flags & AIF_USE_STATIC_PATH ) 
4222                 return 0.0f;
4223
4224         if (force_recreate_flag || timestamp_elapsed(aip->path_next_create_time)) {
4225                 object  *path_objp;
4226
4227                 path_objp = &Objects[aip->path_objnum];
4228
4229                 if ((hashval = create_object_hash(path_objp)) != aip->path_goal_obj_hash) {
4230                         float dist;
4231                         
4232                         dist = vm_vec_dist_quick(&path_objp->pos, &aip->path_create_pos);
4233                         dist += ai_matrix_dist(&path_objp->orient, &aip->path_create_orient) * 25.0f;
4234
4235                         if (force_recreate_flag || (dist > 2.0f)) {
4236                                 aip->path_next_create_time = timestamp(1000);   //      Update again in as little as 1000 milliseconds, ie 1 second.
4237                                 aip->path_goal_obj_hash = hashval;
4238                                 modify_model_path_points(objp);
4239
4240                                 aip->path_create_pos = path_objp->pos;
4241                                 aip->path_create_orient = path_objp->orient;
4242                                 
4243                                 return dist;
4244                         }
4245                 }
4246         }
4247
4248         return 0.0f;
4249 }
4250
4251 //      Set acceleration for ai_dock().
4252 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)
4253 {
4254         float prev_dot_to_goal = aip->prev_dot_to_goal;
4255         
4256         aip->prev_dot_to_goal = dot;
4257
4258         if (objp->phys_info.speed < 0.0f) {
4259                 accelerate_ship(aip, 1.0f/32.0f);
4260         } else if ((prev_dot_to_goal-dot) > 0.01) {
4261                 if (prev_dot_to_goal > dot + 0.05f) {
4262                         accelerate_ship(aip, 0.0f);
4263                 } else {
4264                         change_acceleration(aip, -1.0f);        //      -1.0f means subtract off flFrametime from acceleration value in 0.0..1.0
4265                 }
4266         } else {
4267                 if ((aip->mode == AIM_DOCK) && (dist_to_next < 150.0f) && (aip->path_start + aip->path_length - 2 == aip->path_cur)) {
4268                         set_accel_for_target_speed(objp, sip->max_speed * max(dist_to_next/500.0f, 1.0f));
4269                         //mprintf(("dist = %7.3f, speed = %7.3f\n", dist_to_next, objp->phys_info.speed));
4270                 } else if ((dot_to_next >= dot * .9) || (dist_to_next > 100.0f)) {
4271                         if (dist_to_goal > 200.0f)
4272                                 set_accel_for_target_speed(objp, sip->max_speed * (dot + 1.0f) / 2.0f);
4273                         else {
4274                                 float   xdot;
4275
4276                                 xdot = (dot_to_next + dot)/2.0f;
4277                                 if (xdot < 0.0f)
4278                                         xdot = 0.0f;
4279
4280                                 // AL: if following a path not in dock mode, move full speed
4281                                 if (( aip->mode != AIM_DOCK ) && (dot > 0.9f)) {
4282                                         set_accel_for_target_speed(objp, sip->max_speed*dot*dot*dot);
4283                                 } else {
4284                                         if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4285                                                 //nprintf(("AI", "Target speed = %7.3f\n", dist_to_goal/8.0f));
4286                                                 set_accel_for_target_speed(objp, dist_to_goal/8.0f + 2.0f);
4287                                         } else {
4288                                                 set_accel_for_target_speed(objp, sip->max_speed * (2*xdot + 0.25f)/4.0f);
4289                                         }
4290                                 }
4291                         }
4292                 } else {
4293                         float   xdot;
4294
4295                         xdot = max(dot_to_next, 0.1f);
4296                         if ( aip->mode != AIM_DOCK ) {
4297                                 set_accel_for_target_speed(objp, sip->max_speed);
4298                         } else {
4299                                 float   speed;
4300                                 if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4301                                         speed = dist_to_goal/8.0f + 2.0f;
4302                                 } else if (dist_to_goal < 4*objp->radius + 50.0f) {
4303                                         speed = dist_to_goal/4.0f + 4.0f;
4304                                 } else {
4305                                         speed = sip->max_speed * (3*xdot + 1.0f)/4.0f;
4306                                 }
4307                                 if (aip->mode == AIM_DOCK) {
4308                                         speed = speed * 2.0f + 1.0f;
4309                                         if (aip->goal_objnum != -1) {
4310                                                 speed += Objects[aip->goal_objnum].phys_info.speed;
4311                                         }
4312                                 }
4313
4314                                 set_accel_for_target_speed(objp, speed);
4315                         }
4316                 }
4317         }
4318 }
4319
4320 //      --------------------------------------------------------------------------
4321 //      Follow a path associated with a large object, such as a capital ship.
4322 //      The points defined on the path are in the object's reference frame.
4323 //      The object of interest is goal_objnum.
4324 //      The paths are defined in the model.  The path of interest is wp_list.
4325 //      The next goal point in the path is wp_index.
4326 //      wp_flags contain special information specific to the path.
4327
4328 // The path vertices are defined by model_path structs:
4329 //              typedef struct model_path {
4330 //                      char            name[MAX_NAME_LEN];                                     // name of the subsystem.  Probably displayed on HUD
4331 //                      int             nverts;
4332 //                      vector  *verts;
4333 //              } model_path;
4334
4335 //      The polymodel struct for the object contains the following:
4336 //              int                     n_paths;
4337 //              model_path      *paths;
4338
4339 //      Returns distance to goal point.
4340 float ai_path()
4341 {
4342         polymodel       *pm;
4343         int             num_paths, num_points;
4344         float           dot, dist_to_goal, dist_to_next, speed, dot_to_next;
4345         ship            *shipp = &Ships[Pl_objp->instance];
4346         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4347         ai_info *aip;
4348         vector  nvel_vec;
4349         float           mag, prev_dot_to_goal;
4350         vector  temp_vec, *slop_vec;
4351         object  *gobjp;
4352         ship            *gshipp;
4353         vector  *cvp, *nvp, next_vec, gcvp, gnvp;               //      current and next vertices in global coordinates.
4354
4355         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4356
4357         Assert(aip->goal_objnum != -1);
4358         Assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
4359
4360         gobjp = &Objects[aip->goal_objnum];
4361         gshipp = &Ships[gobjp->instance];
4362
4363         pm = model_get( gshipp->modelnum );
4364         num_paths = pm->n_paths;
4365         Assert(num_paths > 0);
4366
4367         if (aip->path_start == -1) {
4368                 int path_num;
4369                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
4370                 Assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
4371                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
4372         }
4373
4374         // nprintf(("AI", "Frame: %i, Path index = %i/%i\n", AI_FrameCount, aip->path_cur-aip->path_start, aip->path_length));
4375
4376         maybe_recreate_path(Pl_objp, aip, 0);
4377
4378         num_points = aip->path_length;
4379
4380         //      Set cvp and nvp as pointers to current and next vertices of interest on path.
4381         cvp = &Path_points[aip->path_cur].pos;
4382         if ((aip->path_cur + aip->path_dir - aip->path_start < num_points) || (aip->path_cur + aip->path_dir < aip->path_start))
4383                 nvp = &Path_points[aip->path_cur + aip->path_dir].pos;
4384         else {
4385                 //      If this is 0, then path length must be 1 which means we have no direction!
4386                 Assert((aip->path_cur - aip->path_dir >= aip->path_start) && (aip->path_cur - aip->path_dir - aip->path_start < num_points));
4387                 //      Cleanup for above Assert() which we hit too near release. -- MK, 5/24/98.
4388                 if (aip->path_cur - aip->path_dir - aip->path_start >= num_points) {
4389                         if (aip->path_dir == 1)
4390                                 aip->path_cur = aip->path_start;
4391                         else
4392                                 aip->path_cur = aip->path_start + num_points - 1;
4393                 }
4394
4395                 vector  delvec;
4396                 vm_vec_sub(&delvec, cvp, &Path_points[aip->path_cur - aip->path_dir].pos);
4397                 vm_vec_normalize(&delvec);
4398                 vm_vec_scale_add(&next_vec, cvp, &delvec, 10.0f);
4399                 nvp = &next_vec;
4400         }
4401
4402         //      Interrupt if can't get to current goal point.  Debug only.
4403 /*      if (pp_collide(&Pl_objp->pos, cvp, gobjp, Pl_objp->radius)) {
4404                 Int3();
4405         }
4406 */
4407         //      See if can reach next point (as opposed to current point)
4408         //      However, don't do this if docking and next point is last point.
4409         //      That is, we don't want to pursue the last point under control of the
4410         //      path code.  In docking, this is a special hack.
4411         if ((aip->mode != AIM_DOCK) || ((aip->path_cur-aip->path_start) < num_points - 2)) {
4412                 if ((aip->path_cur + aip->path_dir > aip->path_start) && (aip->path_cur + aip->path_dir < aip->path_start + num_points-2)) {
4413                         if ( timestamp_elapsed(aip->path_next_check_time)) {
4414                                 aip->path_next_check_time = timestamp( 3000 );
4415                                 if (!pp_collide(&Pl_objp->pos, nvp, gobjp, 1.1f * Pl_objp->radius)) {
4416                                         cvp = nvp;
4417                                         aip->path_cur += aip->path_dir;
4418                                         nvp = &Path_points[aip->path_cur].pos;
4419                                         //nprintf(("AI", "Reach: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4420                                 }
4421                         }
4422                 }
4423         }
4424
4425         gcvp = *cvp;
4426         gnvp = *nvp;
4427
4428         speed = Pl_objp->phys_info.speed;
4429
4430         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &gcvp);
4431         dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, &gnvp);
4432         //      Can't use fvec, need to use velocity vector because we aren't necessarily
4433         //      moving in the direction we're facing.
4434
4435 //      if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4436         if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4437                 mag = 0.0f;
4438                 vm_vec_zero(&nvel_vec);
4439         } else
4440                 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4441
4442         //      If moving not-very-slowly and sliding, then try to slide at goal, rather than
4443         //      point at goal.
4444         slop_vec = NULL;
4445         if (mag < 1.0f)
4446                 nvel_vec = Pl_objp->orient.fvec;
4447         else if (mag > 5.0f) {
4448                 float   nv_dot;
4449                 nv_dot = vm_vec_dot(&Pl_objp->orient.fvec, &nvel_vec);
4450                 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4451                         slop_vec = &temp_vec;
4452                         vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.fvec);
4453                 }
4454         }
4455
4456         if (dist_to_goal > 0.1f)
4457                 ai_turn_towards_vector(&gcvp, Pl_objp, flFrametime, sip->srotation_time, slop_vec, NULL, 0.0f, 0);
4458
4459         //      Code to control speed is MUCH less forgiving in path following than in waypoint
4460         //      following.  Must be very close to path or might hit objects.
4461         prev_dot_to_goal = aip->prev_dot_to_goal;
4462         dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gcvp);
4463         dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gnvp);
4464
4465         set_accel_for_docking(Pl_objp, aip, dot, dot_to_next, dist_to_next, dist_to_goal, sip);
4466         aip->prev_dot_to_goal = dot;
4467
4468 //mprintf(("Goal index = %i, dist = %7.3f, dot = %7.3f\n", wp_index, dist_to_goal, dot));
4469
4470         //      If moving at a non-tiny velocity, detect attaining path point by its being close to
4471         //      line between previous and current object location.
4472         if ((dist_to_goal < MIN_DIST_TO_WAYPOINT_GOAL) || (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f)) {
4473                 vector  nearest_point;
4474                 float           r, min_dist_to_goal;
4475
4476                 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, &gcvp);
4477
4478                 //      Set min_dist_to_goal = how close must be to waypoint to pick next one.
4479                 //      If docking and this is the second last waypoint, must be very close.
4480                 if ((aip->mode == AIM_DOCK) && (aip->path_cur >= aip->path_length-2))
4481                         min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL;
4482                 else
4483                         min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius;
4484
4485                 if ( (vm_vec_dist_quick(&Pl_objp->pos, &gcvp) < min_dist_to_goal) ||
4486                         ((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, &gcvp) < (MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius))) {
4487                         aip->path_cur += aip->path_dir;
4488                         //nprintf(("AI", " Near: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4489                         if (((aip->path_cur - aip->path_start) > (num_points+1)) || (aip->path_cur < aip->path_start)) {
4490                                 Assert(aip->mode != AIM_DOCK);          //      If docking, should never get this far, getting to last point handled outside ai_path()
4491                                 aip->path_dir = -aip->path_dir;
4492 //                              aip->path_cur += aip->path_dir;
4493                         }
4494                 }
4495         }
4496
4497         return dist_to_goal;
4498 }
4499
4500 void update_min_max(float val, float *min, float *max)
4501 {
4502         if (val < *min)
4503                 *min = val;
4504         else if (val > *max)
4505                 *max = val;
4506 }
4507
4508 //      Stuff bounding box of all enemy objects within "range" units of object *my_objp.
4509 //      Stuff ni min_vec and max_vec.
4510 //      Return value: Number of enemy objects in bounding box.
4511 int get_enemy_team_range(object *my_objp, float range, int enemy_team_mask, vector *min_vec, vector *max_vec)
4512 {
4513         object  *objp;
4514         ship_obj        *so;
4515         int             count = 0;
4516
4517         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4518                 objp = &Objects[so->objnum];
4519                 if (Ships[objp->instance].team & enemy_team_mask) {
4520                         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))
4521                                 if (vm_vec_dist_quick(&my_objp->pos, &objp->pos) < range) {
4522                                         if (count == 0) {
4523                                                 *min_vec = objp->pos;
4524                                                 *max_vec = objp->pos;
4525                                                 count++;
4526                                         } else {
4527                                                 update_min_max(objp->pos.x, &min_vec->x, &max_vec->x);
4528                                                 update_min_max(objp->pos.y, &min_vec->y, &max_vec->y);
4529                                                 update_min_max(objp->pos.z, &min_vec->z, &max_vec->z);
4530                                         }
4531                                 }
4532
4533                 }
4534         }
4535
4536         return count;
4537 }
4538
4539 //      Pick a relatively safe spot for objp to fly to.
4540 //      Problem:
4541 //              Finds a spot away from any enemy within a bounding box.
4542 //              Doesn't verify that "safe spot" is not near some other enemy.
4543 void ai_safety_pick_spot(object *objp)
4544 {
4545         int             objnum;
4546         int             enemy_team_mask;
4547         vector  min_vec, max_vec;
4548         vector  vec_to_center, center;
4549         vector  goal_pos;
4550
4551         objnum = OBJ_INDEX(objp);
4552
4553         enemy_team_mask = get_enemy_team_mask(objnum);
4554
4555         if (get_enemy_team_range(objp, 1000.0f, enemy_team_mask, &min_vec, &max_vec)) {
4556                 vm_vec_avg(&center, &min_vec, &max_vec);
4557                 vm_vec_normalized_dir(&vec_to_center, &center, &objp->pos);
4558
4559                 vm_vec_scale_add(&goal_pos, &center, &vec_to_center, 2000.0f);
4560         } else
4561                 vm_vec_scale_add(&goal_pos, &objp->pos, &objp->orient.fvec, 100.0f);
4562
4563         Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pos;
4564 }
4565
4566 //      Fly to desired safe point.
4567 // Returns distance to that point.
4568 float ai_safety_goto_spot(object *objp)
4569 {
4570         float   dot, dist;
4571         ai_info *aip;
4572         vector  vec_to_goal;
4573         ship_info       *sip;
4574         float   dot_val;
4575
4576         sip = &Ship_info[Ships[objp->instance].ship_info_index];
4577
4578         aip = &Ai_info[Ships[objp->instance].ai_index];
4579         dist = vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
4580         dot = vm_vec_dot(&vec_to_goal, &objp->orient.fvec);
4581
4582         dot_val = (1.1f + dot) / 2.0f;
4583         if (dist > 200.0f) {
4584                 set_accel_for_target_speed(objp, sip->max_speed * dot_val);
4585         } else
4586                 set_accel_for_target_speed(objp, sip->max_speed * dot_val * (dist/200.0f + 0.2f));
4587
4588         return dist;
4589 }
4590
4591 void ai_safety_circle_spot(object *objp)
4592 {
4593         vector  goal_point;
4594         ship_info       *sip;
4595         float           dot;
4596
4597         sip = &Ship_info[Ships[objp->instance].ship_info_index];
4598
4599         goal_point = Ai_info[Ships[objp->instance].ai_index].goal_point;
4600         dot = turn_towards_tangent(objp, &goal_point, 250.0f);  //      Increased from 50 to 250 to make circling not look so wacky.
4601
4602         set_accel_for_target_speed(objp, 0.5f * (1.0f + dot) * sip->max_speed/4.0f);
4603
4604 //      float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
4605 //      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));
4606
4607 }
4608
4609 //      --------------------------------------------------------------------------
4610 void ai_safety()
4611 {
4612         ai_info *aip;
4613
4614         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4615
4616         switch (aip->submode) {
4617         case AISS_1:
4618                 ai_safety_pick_spot(Pl_objp);
4619                 aip->submode = AISS_2;
4620                 aip->submode_start_time = Missiontime;
4621                 break;
4622         case AISS_1a:   //      Pick a safe point because we just got whacked!
4623                 Int3();
4624                 break;
4625         case AISS_2:
4626                 if (ai_safety_goto_spot(Pl_objp) < 25.0f) {
4627                         aip->submode = AISS_3;
4628                         aip->submode_start_time = Missiontime;
4629                 }
4630                 break;
4631         case AISS_3:
4632                 ai_safety_circle_spot(Pl_objp);
4633                 break;
4634         default:
4635                 Int3();         //      Illegal submode for ai_safety();
4636                 break;
4637         }
4638 }
4639
4640 //      --------------------------------------------------------------------------
4641 //      make Pl_objp fly waypoints.
4642 void ai_waypoints()
4643 {
4644         int             wp_index;
4645         vector  *wp_cur, *wp_next;
4646         float           dot, dist_to_goal, dist_to_next, speed, dot_to_next;
4647         ship            *shipp = &Ships[Pl_objp->instance];
4648         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4649         waypoint_list   *wpl;
4650         ai_info *aip;
4651         vector  nvel_vec;
4652         float           mag;
4653         float           prev_dot_to_goal;
4654         vector  temp_vec;
4655         vector  *slop_vec;
4656
4657         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4658
4659         wp_index = aip->wp_index;
4660
4661         if (wp_index == -1) {
4662                 ai_start_waypoints(Pl_objp, 0, WPF_REPEAT);
4663                 wp_index = aip->wp_index;
4664                 aip->wp_dir = 1;
4665         }
4666
4667         wpl = &Waypoint_lists[Ai_info[Ships[Pl_objp->instance].ai_index].wp_list];
4668
4669         Assert(wpl->count);     // What? Is this zero? Probably wp_index never got initialized!
4670
4671         wp_cur = &wpl->waypoints[wp_index];
4672         wp_next = &wpl->waypoints[(wp_index+1) % wpl->count];
4673         speed = Pl_objp->phys_info.speed;
4674
4675         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, wp_cur);
4676         dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, wp_next);
4677
4678         //      Can't use fvec, need to use velocity vector because we aren't necessarily
4679         //      moving in the direction we're facing.
4680         // AL 23-3-98: Account for very small velocities by checking result of vm_vec_mag().
4681         //                                      If we don't vm_vec_copy_normalize() will think it is normalizing a null vector.
4682 //      if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4683         if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4684                 mag = 0.0f;
4685                 vm_vec_zero(&nvel_vec);
4686         } else {
4687                 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4688         }
4689
4690         //      If moving not-very-slowly and sliding, then try to slide at goal, rather than
4691         //      point at goal.
4692         slop_vec = NULL;
4693         if (mag < 1.0f) {
4694                 nvel_vec = Pl_objp->orient.fvec;
4695         } else if (mag > 5.0f) {
4696                 float   nv_dot;
4697                 nv_dot = vm_vec_dot(&Pl_objp->orient.fvec, &nvel_vec);
4698                 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4699                         slop_vec = &temp_vec;
4700                         vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.fvec);
4701                 }
4702         }
4703
4704         //      If a wing leader, take turns more slowly, based on size of wing.
4705         int     scale;
4706
4707         if (Ai_info[Ships[Pl_objp->instance].ai_index].wing >= 0) {
4708                 scale = Wings[Ai_info[Ships[Pl_objp->instance].ai_index].wing].current_count;
4709                 scale = (int) ((scale+1)/2);
4710         } else {
4711                 scale = 1;
4712         }
4713
4714         if (dist_to_goal > 0.1f) {
4715                 ai_turn_towards_vector(wp_cur, Pl_objp, flFrametime, sip->srotation_time*3.0f*scale, slop_vec, NULL, 0.0f, 0);
4716         }
4717
4718         prev_dot_to_goal = aip->prev_dot_to_goal;
4719         dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_cur);
4720         dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_next);
4721         aip->prev_dot_to_goal = dot;
4722
4723         //      If there is no next point on the path, don't care about dot to next.
4724         if (wp_index + 1 >= wpl->count) {
4725                 dot_to_next = dot;
4726         }
4727
4728         // nprintf(("AI", "Wp #%i, dot = %6.3f, next dot = %6.3f, dist = %7.2f\n", wp_index, dot, dot_to_next, dist_to_goal));
4729
4730         if (Pl_objp->phys_info.speed < 0.0f) {
4731                 accelerate_ship(aip, 1.0f/32);
4732         } else if (prev_dot_to_goal > dot+0.01f) {
4733                 //      We are further from pointing at our goal this frame than last frame, so slow down.
4734                 set_accel_for_target_speed(Pl_objp, Pl_objp->phys_info.speed * 0.95f);
4735         } else if (dist_to_goal < 100.0f) {
4736                 float slew_dot = vm_vec_dot(&Pl_objp->orient.fvec, &nvel_vec);
4737                 if (fl_abs(slew_dot) < 0.9f) {
4738                         accelerate_ship(aip, 0.0f);
4739                 } else if (dot < 0.88f + 0.1f*(100.0f - dist_to_goal)/100.0f) {
4740                         accelerate_ship(aip, 0.0f);
4741                 } else {
4742                         accelerate_ship(aip, 0.5f * dot * dot);
4743                 }
4744         } else {
4745                 float   dot1;
4746                 if (dist_to_goal < 250.0f) {
4747                         dot1 = dot*dot*dot;                             //      Very important to be pointing towards goal when nearby.  Note, cubing preserves sign.
4748                 } else {
4749                         if (dot > 0.0f) {
4750                                 dot1 = dot*dot;
4751                         } else {
4752                                 dot1 = dot;
4753                         }
4754                 }
4755
4756                 if (dist_to_goal > 100.0f + Pl_objp->radius * 2) {
4757                         if (dot < 0.2f) {
4758                                 dot1 = 0.2f;
4759                         }
4760                 }
4761
4762                 if (sip->flags & SIF_SMALL_SHIP) {
4763                         set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/5.0f);
4764                 } else {
4765                         set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/10.0f);
4766                 }
4767         }
4768
4769         //      Make sure not travelling too fast for someone to keep up.
4770         float   max_allowed_speed = 9999.9f;
4771
4772         if (shipp->wingnum != -1) {
4773                 max_allowed_speed = 0.9f * get_wing_lowest_max_speed(Pl_objp);
4774         }
4775
4776         // check if waypoint speed cap is set and adjust max speed
4777         if (aip->waypoint_speed_cap > 0) {
4778                 max_allowed_speed = (float) aip->waypoint_speed_cap;
4779         }
4780
4781         if (aip->prev_accel * shipp->current_max_speed > max_allowed_speed) {
4782                 accelerate_ship(aip, max_allowed_speed / shipp->current_max_speed);
4783         }
4784
4785         if (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f) {
4786                 vector  nearest_point;
4787                 float           r;
4788
4789                 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, wp_cur);
4790
4791                 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))) ||
4792                         ((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, wp_cur) < (MIN_DIST_TO_WAYPOINT_GOAL + fl_sqrt(Pl_objp->radius)))) {
4793                         wp_index++;
4794                         if (wp_index >= wpl->count)
4795                                 if (aip->wp_flags & WPF_REPEAT) {
4796                                         wp_index = 0;
4797                                 } else {
4798                                         int treat_as_ship;
4799
4800                                         // when not repeating waypoints -- mark the goal as done and put and entry into the mission log
4801                                         // we must be careful when dealing with wings.  A ship in a wing might be completing
4802                                         // a waypoint for for the entire wing, or it might be completing a goal for itself.  If
4803                                         // for itself and in a wing, treat the completion as we would a ship
4804                                         treat_as_ship = 1;
4805                                         if ( Ships[Pl_objp->instance].wingnum != -1 ) {
4806                                                 int type;
4807
4808                                                 // I don't think that you can fly waypoints as dynamic goals!!!
4809                                                 // -- This is legal, just stupid. -- Assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4810                                                 
4811                                                 //      Clean up from above Assert, just in case we ship without fixing it.  (Encountered by JimB on 2/9/98)
4812                                                 if ( (aip->active_goal == AI_GOAL_NONE) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC) ) {
4813                                                         aip->mode = AIM_NONE;
4814                                                         Int3(); //      Look at the ship, find out of it's supposed to be flying waypoints. -- MK.
4815                                                 }
4816
4817                                                 type = aip->goals[aip->active_goal].type;
4818                                                 if ( (type == AIG_TYPE_EVENT_WING) || (type == AIG_TYPE_PLAYER_WING) ) {
4819                                                         treat_as_ship = 0;
4820                                                 } else {
4821                                                         treat_as_ship = 1;
4822                                                 }
4823                                         }
4824
4825                                         // if the ship is not in a wing, remove the goal and continue on
4826                                         if ( treat_as_ship ) {
4827                                                 ai_mission_goal_complete( aip );                                        // this call should reset the AI mode
4828                                                 mission_log_add_entry(LOG_WAYPOINTS_DONE, Ships[Pl_objp->instance].ship_name, wpl->name, -1 );
4829                                         } else {
4830                                                 // this ship is in a wing.  We must mark the goal as being completed for all ships
4831                                                 // in the wing.  We will also mark an entry in the log that the wing completed the goal
4832                                                 // not the individual ship.
4833                                                 ai_mission_wing_goal_complete( Ships[Pl_objp->instance].wingnum, &(aip->goals[aip->active_goal]) );
4834                                                 mission_log_add_entry( LOG_WAYPOINTS_DONE, Wings[Ships[Pl_objp->instance].wingnum].name, wpl->name, -1 );
4835                                         }
4836                                         //wp_index = wpl->count-1;
4837                                 }
4838
4839                         aip->wp_index = wp_index;
4840                 }
4841         }
4842 }
4843
4844 //      Make Pl_objp avoid En_objp
4845 //      Not like evading.  This is for avoiding a collision!
4846 //      Note, use sliding if available.
4847 void avoid_ship()
4848 {
4849         //      To avoid an object, turn towards right or left vector until facing away from object.
4850         //      To choose right vs. left, pick one that is further from center of avoid object.
4851         //      Keep turning away from until pointing away from ship.
4852         //      Stay in avoid mode until at least 3 enemy ship radii away.
4853
4854         //      Speed setting:
4855         //      If inside sphere, zero speed and turn towards outside.
4856         //      If outside sphere, inside 2x sphere, set speed percent of max to:
4857         //              max(away_dot, (dist-rad)/rad)
4858         //      where away_dot is dot(Pl_objp->fvec, vec_En_objp_to_Pl_objp)
4859
4860         vector  vec_to_enemy;
4861         float           away_dot;
4862         float           dist;
4863         ship            *shipp = &Ships[Pl_objp->instance];
4864         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4865         ai_info *aip = &Ai_info[shipp->ai_index];
4866         vector  player_pos, enemy_pos;
4867
4868         // if we're avoiding a stealth ship, then we know where he is, update with no error
4869         if ( is_object_stealth_ship(En_objp) ) {
4870                 update_ai_stealth_info_with_error(aip/*, 1*/);
4871         }
4872
4873         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
4874         vm_vec_sub(&vec_to_enemy, &enemy_pos, &Pl_objp->pos);
4875
4876         dist = vm_vec_normalize(&vec_to_enemy);
4877         away_dot = -vm_vec_dot(&Pl_objp->orient.fvec, &vec_to_enemy);
4878         
4879         if ((sip->max_vel.x > 0.0f) || (sip->max_vel.y > 0.0f)) {
4880                 if (vm_vec_dot(&Pl_objp->orient.rvec, &vec_to_enemy) > 0.0f) {
4881                         AI_ci.sideways = -1.0f;
4882                 } else {
4883                         AI_ci.sideways = 1.0f;
4884                 }
4885                 if (vm_vec_dot(&Pl_objp->orient.uvec, &vec_to_enemy) > 0.0f) {
4886                         AI_ci.vertical = -1.0f;
4887                 } else {
4888                         AI_ci.vertical = 1.0f;
4889                 }
4890         }               
4891
4892         //nprintf(("AI", "Frame %i: Sliding: %s %s\n", Framecount, AI_ci.sideways < 0 ? "left" : "right", AI_ci.vertical < 0 ? "down" : "up" ));
4893         // nprintf(("AI", "away_dot = %6.3f, dist = %7.2f, dist/radsum = %6.3f\n", away_dot, dist, dist/(Pl_objp->radius + En_objp->radius)));
4894
4895         //      If in front of enemy, turn away from it.
4896         //      If behind enemy, try to get fully behind it.
4897         if (away_dot < 0.0f) {
4898                 turn_away_from_point(Pl_objp, &enemy_pos, Pl_objp->phys_info.speed);
4899         } else {
4900                 vector  goal_pos;
4901
4902                 vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.fvec, -100.0f);
4903                 turn_towards_point(Pl_objp, &goal_pos, NULL, Pl_objp->phys_info.speed);
4904         }
4905
4906         //      Set speed.
4907         float   radsum = Pl_objp->radius + En_objp->radius;
4908
4909         if (dist < radsum)
4910                 accelerate_ship(aip, max(away_dot, 0.2f));
4911         else if (dist < 2*radsum)
4912                 accelerate_ship(aip, max(away_dot, (dist - radsum) / radsum)+0.2f);
4913         else
4914                 accelerate_ship(aip, 1.0f);
4915
4916 }
4917
4918 //      Maybe it's time to resume the previous AI mode in aip->previous_mode.
4919 //      Each type of previous_mode has its own criteria on when to resume.
4920 //      Return true if previous mode was resumed.
4921 int maybe_resume_previous_mode(object *objp, ai_info *aip)
4922 {
4923         //      Only (maybe) resume previous goal if current goal is dynamic.
4924         if (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC)
4925                 return 0;
4926
4927         if (aip->mode == AIM_EVADE_WEAPON) {
4928                 if (timestamp_elapsed(aip->mode_time) || (((aip->nearest_locked_object == -1) || (Objects[aip->nearest_locked_object].type != OBJ_WEAPON)) && (aip->danger_weapon_objnum == -1))) {
4929                         Assert(aip->previous_mode != AIM_EVADE_WEAPON);
4930                         aip->mode = aip->previous_mode;
4931                         aip->submode = aip->previous_submode;
4932                         aip->submode_start_time = Missiontime;
4933                         aip->active_goal = AI_GOAL_NONE;
4934                         aip->mode_time = -1;                    //      Means do forever.
4935                         return 1;
4936                 }
4937         } else if ( aip->previous_mode == AIM_GUARD) {
4938                 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
4939                         object  *guard_objp;
4940                         float   dist;
4941
4942                         guard_objp = &Objects[aip->guard_objnum];
4943                         dist = vm_vec_dist_quick(&guard_objp->pos, &objp->pos);
4944
4945                         //      If guarding ship is far away from guardee and enemy is far away from guardee,
4946                         //      then stop chasing and resume guarding.
4947                         if (dist > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4948                                 if ((En_objp != NULL) && (En_objp->type == OBJ_SHIP)) {
4949                                         if (vm_vec_dist_quick(&guard_objp->pos, &En_objp->pos) > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4950                                                 Assert(aip->previous_mode == AIM_GUARD);
4951                                                 aip->mode = aip->previous_mode;
4952                                                 aip->submode = AIS_GUARD_PATROL;
4953                                                 aip->active_goal = AI_GOAL_NONE;
4954                                                 return 1;
4955                                         }
4956                                 }
4957                         }
4958                 }
4959         }
4960
4961         return 0;
4962
4963 }
4964
4965 //      Call this function if you want something to happen on average every N quarters of a second.
4966 //      The truth value returned by this function will be the same for any given quarter second interval.
4967 //      The value "num" is only passed in to get asynchronous behavior for different objects.
4968 //      modulus == 1 will always return true.
4969 //      modulus == 2 will return true half the time.
4970 //      modulus == 16 will return true for one quarter second interval every four seconds.
4971 int static_rand_timed(int num, int modulus)
4972 {
4973         if (modulus < 2)
4974                 return 1;
4975         else {
4976                 int     t;
4977
4978                 t = Missiontime >> 18;          //      Get time in quarters of a second
4979                 t += num;
4980
4981                 return !(t % modulus);
4982         }
4983 }
4984
4985 //      Maybe fire afterburner based on AI class
4986 int ai_maybe_fire_afterburner(object *objp, ai_info *aip)
4987 {
4988         if (aip->ai_class == 0)
4989                 return 0;               //      Lowest level never aburners away
4990         else  {
4991                 //      Maybe don't afterburner because of a potential collision with the player.
4992                 //      If not multiplayer, near player and player in front, probably don't afterburner.
4993                 if (!(Game_mode & GM_MULTIPLAYER)) {
4994                         if (Ships[objp->instance].team == Player_ship->team) {
4995                                 float   dist;
4996
4997                                 dist = vm_vec_dist_quick(&objp->pos, &Player_obj->pos) - Player_obj->radius - objp->radius;
4998                                 if (dist < 150.0f) {
4999                                         vector  v2p;
5000                                         float           dot;
5001
5002                                         vm_vec_normalized_dir(&v2p, &Player_obj->pos, &objp->pos);
5003                                         dot = vm_vec_dot(&v2p, &objp->orient.fvec);
5004
5005                                         if (dot > 0.0f) {
5006                                                 if (dot * dist > 50.0f)
5007                                                         return 0;
5008                                         }
5009                                 }
5010                         }
5011                 }
5012
5013                 if (aip->ai_class >= Num_ai_classes-2)
5014                         return 1;               //      Highest two levels always aburner away.
5015                 else {
5016                         return static_rand_timed(objp-Objects, Num_ai_classes - aip->ai_class);
5017                 }
5018         }
5019 }
5020
5021 //      Maybe engage afterburner after being hit by an object.
5022 void maybe_afterburner_after_ship_hit(object *objp, ai_info *aip, object *en_objp)
5023 {
5024         //      Only do if facing a little away.
5025         if (en_objp != NULL) {
5026                 vector  v2e;
5027
5028                 vm_vec_normalized_dir(&v2e, &en_objp->pos, &objp->pos);
5029                 if (vm_vec_dot(&v2e, &objp->orient.fvec) > -0.5f)
5030                         return;
5031         }
5032
5033         if (!( objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5034                 if (ai_maybe_fire_afterburner(objp, aip)) {
5035                         afterburners_start(objp);
5036                         aip->afterburner_stop_time = Missiontime + F1_0/2;
5037                 }
5038         }
5039 }
5040
5041 //      Return true if object *objp is an instructor.
5042 //      Is an instructor if name begins INSTRUCTOR_SHIP_NAME else not.
5043 int is_instructor(object *objp)
5044 {
5045         return !strnicmp(Ships[objp->instance].ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME));
5046 }
5047
5048 //      Evade the weapon aip->danger_weapon_objnum
5049 //      If it's not valid, do a quick out.
5050 //      Evade by accelerating hard.
5051 //      If necessary, turn hard left or hard right.
5052 void evade_weapon()
5053 {
5054         object  *weapon_objp = NULL;
5055         object  *unlocked_weapon_objp = NULL, *locked_weapon_objp = NULL;
5056         vector  weapon_pos, player_pos, goal_point;
5057         vector  vec_from_enemy;
5058         float           dot_from_enemy, dot_to_enemy;
5059         float           dist;
5060         ship            *shipp = &Ships[Pl_objp->instance];
5061         ai_info *aip = &Ai_info[shipp->ai_index];
5062
5063         if (is_instructor(Pl_objp))
5064                 return;
5065
5066         //      Make sure we're actually being attacked.
5067         //      Favor locked objects.
5068         if (aip->nearest_locked_object != -1) {
5069                 if (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)
5070                         locked_weapon_objp = &Objects[aip->nearest_locked_object];
5071         }
5072         
5073         if (aip->danger_weapon_objnum != -1)
5074                 if (Objects[aip->danger_weapon_objnum].signature == aip->danger_weapon_signature)
5075                         unlocked_weapon_objp = &Objects[aip->danger_weapon_objnum];
5076                 else
5077                         aip->danger_weapon_objnum = -1;         //      Signatures don't match, so no longer endangered.
5078
5079         if (locked_weapon_objp != NULL) {
5080                 if (unlocked_weapon_objp != NULL) {
5081                         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))
5082                                 weapon_objp = locked_weapon_objp;
5083                         else
5084                                 weapon_objp = unlocked_weapon_objp;
5085                 } else
5086                         weapon_objp = locked_weapon_objp;
5087         } else if (unlocked_weapon_objp != NULL)
5088                 weapon_objp = unlocked_weapon_objp;
5089         else {
5090                 if (aip->mode == AIM_EVADE_WEAPON)
5091                         maybe_resume_previous_mode(Pl_objp, aip);
5092                 return;
5093         }
5094
5095         Assert(weapon_objp != NULL);
5096
5097         if (weapon_objp->type != OBJ_WEAPON) {
5098                 if (aip->mode == AIM_EVADE_WEAPON)
5099                         maybe_resume_previous_mode(Pl_objp, aip);
5100                 return;
5101         }
5102         
5103         weapon_pos = weapon_objp->pos;
5104         player_pos = Pl_objp->pos;
5105
5106         //      Make speed based on skill level, varying at highest skill level, which is harder to hit.
5107         accelerate_ship(aip, 1.0f);
5108
5109         dist = vm_vec_normalized_dir(&vec_from_enemy, &player_pos, &weapon_pos);
5110
5111         dot_to_enemy = -vm_vec_dot(&Pl_objp->orient.fvec, &vec_from_enemy);
5112         dot_from_enemy = vm_vec_dot(&weapon_objp->orient.fvec, &vec_from_enemy);
5113         //nprintf(("AI", "dot from enemy = %7.3f\n", dot_from_enemy));
5114
5115         //      If shot is incoming...
5116         if (dot_from_enemy < 0.3f) {
5117                 if (weapon_objp == unlocked_weapon_objp)
5118                         aip->danger_weapon_objnum = -1;
5119                 return;
5120         } else if (dot_from_enemy > 0.7f) {
5121                 if (dist < 200.0f) {
5122                         if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5123                                 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
5124                                         //nprintf(("AI", "Frame %i, turning on afterburner.\n", AI_FrameCount));
5125                                         afterburners_start(Pl_objp);
5126                                         aip->afterburner_stop_time = Missiontime + F1_0/2;
5127                                 }
5128                         }
5129                 }
5130
5131                 //      If we're sort of pointing towards it...
5132                 if ((dot_to_enemy < -0.5f) || (dot_to_enemy > 0.5f)) {
5133                         float   rdot;
5134
5135                         //      Turn hard left or right, depending on which gets out of way quicker.
5136                         rdot = vm_vec_dot(&Pl_objp->orient.rvec, &vec_from_enemy);
5137
5138                         if ((rdot < -0.5f) || (rdot > 0.5f))
5139                                 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.rvec, -200.0f);
5140                         else
5141                                 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.rvec, 200.0f);
5142
5143                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
5144                 }
5145         }
5146
5147 }
5148
5149 //      Use sliding and backwards moving to face enemy.
5150 //      (Coded 2/20/98.  Works fine, but it's hard to see how to integrate it into the AI system.
5151 //       Typically ships are moving so fast that a little sliding isn't enough to gain an advantage.
5152 //       It's currently used to avoid collisions and could be used to evade weapon fire, but the latter
5153 //       would be frustrating, I think.
5154 //       This function is currently not called.)
5155 void slide_face_ship()
5156 {
5157         ship_info       *sip;
5158
5159         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
5160
5161         //      If can't slide, return.
5162         if ((sip->max_vel.x == 0.0f) && (sip->max_vel.y == 0.0f))
5163                 return;
5164
5165         vector  goal_pos;
5166         float           dot_from_enemy, dot_to_enemy;
5167         vector  vec_from_enemy, vec_to_goal;
5168         float           dist;
5169         float           up, right;
5170         ai_info         *aip;
5171
5172         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
5173
5174         dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
5175
5176         ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
5177
5178         dot_from_enemy = vm_vec_dot(&vec_from_enemy, &En_objp->orient.fvec);
5179         dot_to_enemy = -vm_vec_dot(&vec_from_enemy, &Pl_objp->orient.fvec);
5180
5181         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.rvec) > 0.0f)
5182                 right = 1.0f;
5183         else
5184                 right = -1.0f;
5185
5186         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.uvec) > 0.0f)
5187                 up = 1.0f;
5188         else
5189                 up = -1.0f;
5190
5191         vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.rvec, right * 200.0f);
5192         vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.uvec, up * 200.0f);
5193
5194         vm_vec_normalized_dir(&vec_to_goal, &goal_pos, &Pl_objp->pos);
5195
5196         if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.rvec) > 0.0f)
5197                 AI_ci.sideways = 1.0f;
5198         else
5199                 AI_ci.sideways = -1.0f;
5200
5201         if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.uvec) > 0.0f)
5202                 AI_ci.vertical = 1.0f;
5203         else
5204                 AI_ci.vertical = -1.0f;
5205
5206         if (dist < 200.0f) {
5207                 if (dot_from_enemy < 0.7f)
5208                         accelerate_ship(aip, -1.0f);
5209                 else
5210                         accelerate_ship(aip, dot_from_enemy + 0.5f);
5211         } else {
5212                 if (dot_from_enemy < 0.7f) {
5213                         accelerate_ship(aip, 0.2f);
5214                 } else {
5215                         accelerate_ship(aip, 1.0f);
5216                 }
5217         }
5218 }
5219
5220 //      General code for handling one ship evading another.
5221 //      Problem: This code is also used for avoiding an impending collision.
5222 //      In such a case, it is not good to go to max speed, which is often good
5223 //      for a certain kind of evasion.
5224 void evade_ship()
5225 {
5226         vector  player_pos, enemy_pos, goal_point;
5227         vector  vec_from_enemy;
5228         float           dot_from_enemy;
5229         float           dist;
5230         ship            *shipp = &Ships[Pl_objp->instance];
5231         ship_info       *sip = &Ship_info[shipp->ship_info_index];
5232         ai_info *aip = &Ai_info[shipp->ai_index];
5233         float           bank_override = 0.0f;
5234
5235         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
5236
5237         //      Make speed based on skill level, varying at highest skill level, which is harder to hit.
5238         if (Game_skill_level == NUM_SKILL_LEVELS-1) {
5239                 int     rand_int;
5240                 float   accel_val;
5241
5242                 rand_int = static_rand(Pl_objp-Objects);
5243                 accel_val = (float) (((Missiontime^rand_int) >> 14) & 0x0f)/32.0f + 0.5f;
5244                 accelerate_ship(aip, accel_val);
5245                 //nprintf(("AI", "Accel value = %7.3f\n", accel_val));
5246         } else
5247                 accelerate_ship(aip, (float) (Game_skill_level+2) / (NUM_SKILL_LEVELS+1));
5248
5249         if ((Missiontime - aip->submode_start_time > F1_0/2) && (sip->afterburner_fuel_capacity > 0.0f)) {
5250                 float percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
5251                 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
5252                         afterburners_start(Pl_objp);
5253                         aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
5254                 }
5255         }
5256
5257         vm_vec_sub(&vec_from_enemy, &player_pos, &enemy_pos);
5258
5259         dist = vm_vec_normalize(&vec_from_enemy);
5260         dot_from_enemy = vm_vec_dot(&En_objp->orient.fvec, &vec_from_enemy);
5261
5262         if (dist > 250.0f) {
5263                 vector  gp1, gp2;
5264                 //      If far away from enemy, circle, going to nearer of point far off left or right wing
5265                 vm_vec_scale_add(&gp1, &enemy_pos, &En_objp->orient.rvec, 250.0f);
5266                 vm_vec_scale_add(&gp2, &enemy_pos, &En_objp->orient.rvec, -250.0f);
5267                 if (vm_vec_dist_quick(&gp1, &Pl_objp->pos) < vm_vec_dist_quick(&gp2, &Pl_objp->pos))
5268                         goal_point = gp1;
5269                 else
5270                         goal_point = gp2;
5271         } else if (dot_from_enemy < 0.1f) {
5272                 //      If already close to behind, goal is to get completely behind.
5273                 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.fvec, -1000.0f);
5274         } else if (dot_from_enemy > 0.9f) {
5275                 //      If enemy pointing almost right at self, and self pointing close to enemy, turn away from
5276                 vector  vec_to_enemy;
5277                 float           dot_to_enemy;
5278
5279                 vm_vec_sub(&vec_to_enemy, &enemy_pos, &player_pos);
5280
5281                 vm_vec_normalize(&vec_to_enemy);
5282                 dot_to_enemy = vm_vec_dot(&Pl_objp->orient.fvec, &vec_to_enemy);
5283                 if (dot_to_enemy > 0.75f) {
5284                         //      Used to go to En_objp's right vector, but due to banking while turning, that
5285                         //      caused flying in an odd spiral.
5286                         vm_vec_scale_add(&goal_point, &enemy_pos, &Pl_objp->orient.rvec, 1000.0f);
5287                         if (dist < 100.0f)
5288                                 bank_override = Pl_objp->phys_info.speed; 
5289                 } else {
5290                         bank_override = Pl_objp->phys_info.speed;                       //      In enemy's sights, not pointing at him, twirl away.
5291                         // nprintf(("Mike", " Do sumpin' else."));
5292                         goto evade_ship_l1;
5293                 }
5294         } else {
5295 evade_ship_l1: ;
5296                 if (aip->ai_evasion > myrand()*100.0f/32767.0f) {
5297                         int     temp;
5298                         float   scale;
5299                         float   psrandval;      //      some value close to zero to choose whether to turn right or left.
5300
5301                         psrandval = (float) (((Missiontime >> 14) & 0x0f) - 8); //      Value between -8 and 7
5302                         psrandval = psrandval/16.0f;                                                    //      Value between -1/2 and 1/2 (approx)
5303
5304                         //      If not close to behind, turn towards his right or left vector, whichever won't cross his path.
5305                         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.rvec) > psrandval) {
5306                                 scale = 1000.0f;
5307                         } else {
5308                                 scale = -1000.0f;
5309                         }
5310
5311                         vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.rvec, scale);
5312
5313                         temp = ((Missiontime >> 16) & 0x07);
5314                         temp = ((temp * (temp+1)) % 16)/2 - 4;
5315                         if ((psrandval == 0) && (temp == 0))
5316                                 temp = 3;
5317
5318                         scale = 200.0f * temp;
5319
5320                         vm_vec_scale_add2(&goal_point, &En_objp->orient.uvec, scale);
5321                 } else {
5322                         //      No evasion this frame, but continue with previous turn.
5323                         //      Reason: If you don't, you lose rotational momentum.  Turning every other frame,
5324                         //      and not in between results in a very slow turn because of loss of momentum.
5325                         if ((aip->prev_goal_point.x != 0.0f) || (aip->prev_goal_point.y != 0.0f) || (aip->prev_goal_point.z != 0.0f))
5326                                 goal_point = aip->prev_goal_point;
5327                         else
5328                                 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.rvec, 100.0f);
5329                 }
5330         }
5331
5332         // nprintf(("Mike", "Goal point = %7.1f %7.1f %7.1f\n", goal_point.x, goal_point.y, goal_point.z));
5333         turn_towards_point(Pl_objp, &goal_point, NULL, bank_override);
5334
5335         aip->prev_goal_point = goal_point;
5336 }
5337
5338 //      --------------------------------------------------------------------------
5339 //      Fly in a manner making it difficult for opponent to attack.
5340 void ai_evade()
5341 {
5342         evade_ship();
5343 }
5344
5345 /*
5346 // -------------------------------------------------------------------
5347 //      Refine predicted enemy position because enemy will move while we move
5348 //      towards predicted enemy position.
5349 //      last_delta_vec is stuffed with size of polishing in last step.  This small amount
5350 //      can be used to perturb the predicted position to make firing not be exact.
5351 //      This function will almost always undershoot actual position, assuming both ships
5352 //      are moving at constant speed.  But with even one polishing step, the error should
5353 //      be under 1%. The number of polishing steps is specified in the parameter num_polish_steps.
5354 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)
5355 {
5356         int     iteration;
5357         vector  player_pos = pobjp->pos;
5358         vector  enemy_pos = *predicted_enemy_pos;
5359         physics_info    *en_physp = &eobjp->phys_info;
5360         float           time_to_enemy;
5361         vector  last_predicted_enemy_pos = *predicted_enemy_pos;
5362         
5363         vm_vec_zero(last_delta_vec);
5364
5365         for (iteration=0; iteration < num_polish_steps; iteration++) {
5366                 dist_to_enemy = vm_vec_dist_quick(predicted_enemy_pos, &player_pos);
5367                 time_to_enemy = compute_time_to_enemy(dist_to_enemy, pobjp, eobjp);
5368                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, time_to_enemy);
5369                 vm_vec_sub(last_delta_vec, predicted_enemy_pos, &last_predicted_enemy_pos);
5370                 last_predicted_enemy_pos= *predicted_enemy_pos;
5371         }
5372 }
5373 */
5374
5375 /*
5376 Relevant variables are:
5377         best_dot_to_enemy               best dot product to enemy in last BEST_DOT_TIME seconds
5378         best_dot_to_time                time at which best dot occurred
5379         best_dot_from_enemy     best dot product for enemy to player in last BEST_DOT_TIME seconds
5380         best_dot_from_time      time at which best dot occurred
5381         submode_start_time      time at which we entered the current submode
5382         previous_submode                previous submode, get it?
5383 Legal submodes are:
5384         CONTINUOUS_TURN vector_id {0..3 = right, -right, up, -up}
5385         ATTACK
5386         EVADE_SQUIGGLE
5387         EVADE_BRAKE
5388 */
5389
5390 float   G_collision_time;
5391 vector  G_predicted_pos, G_fire_pos;
5392
5393 /*
5394 void show_firing_diag()
5395 {
5396         float           dot;
5397         vector  v2t;
5398         vector  pos1, pos2;
5399         float           dist;
5400
5401         if (G_collision_time == 0.0f)
5402                 return;
5403
5404         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",
5405                 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));
5406         vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5407         dot = vm_vec_dot(&v2t, &Pl_objp->orient.fvec);
5408         mprintf(("Dot of fvec and vector to predicted position = %10.7f (%7.3f degrees)\n", dot, acos(dot)*180.0f/3.141592654f));
5409
5410         vm_vec_scale_add(&pos1, &En_objp->pos, &En_objp->phys_info.vel, G_collision_time);
5411         vm_vec_scale_add(&pos2, &G_fire_pos, &Pl_objp->orient.fvec, G_collision_time*300.0f);
5412         dist = vm_vec_dist(&pos1, &pos2);
5413
5414         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));
5415 }
5416 */
5417
5418 //      If:
5419 //              flags & WIF_PUNCTURE
5420 //      Then Select a Puncture weapon.
5421 //      Else
5422 //              Select Any ol' weapon.
5423 //      Returns primary_bank index.
5424 int ai_select_primary_weapon(object *objp, object *other_objp, int flags)
5425 {
5426         ship    *shipp = &Ships[objp->instance];
5427         ship_weapon *swp = &shipp->weapons;
5428         ship_info *sip;
5429
5430         //Assert( other_objp != NULL );
5431         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5432
5433         sip = &Ship_info[shipp->ship_info_index];
5434
5435         if (flags & WIF_PUNCTURE) {
5436                 if (swp->current_primary_bank >= 0) {
5437                         int     bank_index;
5438
5439                         bank_index = swp->current_primary_bank;
5440
5441                         if (Weapon_info[swp->primary_bank_weapons[bank_index]].wi_flags & WIF_PUNCTURE) {
5442                                 //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[bank_index]].name));
5443                                 return swp->current_primary_bank;
5444                         }
5445                 }
5446                 for (int i=0; i<swp->num_primary_banks; i++) {
5447                         int     weapon_info_index;
5448
5449                         weapon_info_index = swp->primary_bank_weapons[i];
5450
5451                         if (weapon_info_index > -1){
5452                                 if (Weapon_info[weapon_info_index].wi_flags & WIF_PUNCTURE) {
5453                                         swp->current_primary_bank = i;
5454                                         //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5455                                         return i;
5456                                 }
5457                         }
5458                 }
5459                 
5460                 // AL 26-3-98: If we couldn't find a puncture weapon, pick first available weapon if one isn't active
5461                 if ( swp->current_primary_bank < 0 ) {
5462                         if ( swp->num_primary_banks > 0 ) {
5463                                 swp->current_primary_bank = 0;
5464                         }
5465                 }
5466
5467         } else {                //      Don't need to be using a puncture weapon.
5468                 if (swp->current_primary_bank >= 0) {
5469                         if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)){
5470                                 return swp->current_primary_bank;
5471                         }
5472                 }
5473                 for (int i=0; i<swp->num_primary_banks; i++) {
5474                         if (swp->primary_bank_weapons[i] > -1) {
5475                                 if (!(Weapon_info[swp->primary_bank_weapons[i]].wi_flags & WIF_PUNCTURE)) {
5476                                         swp->current_primary_bank = i;
5477                                         nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5478                                         return i;
5479                                 }
5480                         }
5481                 }
5482                 //      Wasn't able to find a non-puncture weapon.  Stick with what we have.
5483         }
5484
5485         Assert( swp->current_primary_bank != -1 );              // get Alan or Allender
5486
5487         return swp->current_primary_bank;
5488 }
5489
5490 //      --------------------------------------------------------------------------
5491 //      Maybe link primary weapons.
5492 void set_primary_weapon_linkage(object *objp)
5493 {
5494         ship            *shipp;
5495         ai_info *aip;
5496
5497         shipp = &Ships[objp->instance];
5498         aip     = &Ai_info[shipp->ai_index];
5499
5500         shipp->flags &= ~SF_PRIMARY_LINKED;
5501
5502         if (Num_weapons > (int) (MAX_WEAPONS * 0.75f)) {
5503                 if (shipp->flags & SF_PRIMARY_LINKED)
5504                         nprintf(("AI", "Frame %i, ship %s: Unlinking primaries.\n", Framecount, shipp->ship_name));
5505                 shipp->flags &= ~SF_PRIMARY_LINKED;
5506                 return;         //      If low on slots, don't link.
5507         }
5508
5509         shipp->flags &= ~SF_PRIMARY_LINKED;
5510
5511         // AL: ensure target is a ship!
5512         if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
5513                 // If trying to destroy a big ship (i.e., not disable/disarm), always unleash all weapons
5514                 if ( ship_get_SIF(&Ships[Objects[aip->target_objnum].instance]) & SIF_BIG_SHIP) {
5515                         if ( aip->targeted_subsys == NULL ) {
5516                                 shipp->flags |= SF_PRIMARY_LINKED;
5517                                 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5518                                 return;
5519                         }
5520                 }
5521         }
5522
5523         // AL 2-11-98: If ship has a disarm or disable goal, don't link unless both weapons are
5524         //                                      puncture weapons
5525         if ( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) ) {
5526                 if ( aip->goals[aip->active_goal].ai_mode & (AI_GOAL_DISABLE_SHIP|AI_GOAL_DISARM_SHIP) ) {
5527                         ship_weapon     *swp;
5528                         swp = &shipp->weapons;
5529                         // only continue if both primaries are puncture weapons
5530                         if ( swp->num_primary_banks == 2 ) {
5531                                 if ( !(Weapon_info[swp->primary_bank_weapons[0]].wi_flags & WIF_PUNCTURE) ) 
5532                                         return;
5533                                 if ( !(Weapon_info[swp->primary_bank_weapons[1]].wi_flags & WIF_PUNCTURE) ) 
5534                                         return;
5535                         }
5536                 }
5537         }
5538
5539         //      Don't want all ships always linking weapons at start, so asynchronize.
5540         if (Missiontime < i2f(30))
5541                 return;
5542         else if (Missiontime < i2f(120)) {
5543                 int r = static_rand((Missiontime >> 17) ^ OBJ_INDEX(objp));
5544                 if ( (r&3) != 0)
5545                         return;
5546         }
5547
5548         if (shipp->weapon_energy > Link_energy_levels_always[Game_skill_level]) {
5549                 shipp->flags |= SF_PRIMARY_LINKED;
5550         } else if (shipp->weapon_energy > Link_energy_levels_maybe[Game_skill_level]) {
5551                 if (objp->hull_strength < Ship_info[shipp->ship_info_index].initial_hull_strength/3.0f)
5552                         shipp->flags |= SF_PRIMARY_LINKED;
5553         }
5554 }
5555
5556 //      --------------------------------------------------------------------------
5557 //      Fire the current primary weapon.
5558 //      *objp is the object to fire from.
5559 void ai_fire_primary_weapon(object *objp)
5560 {
5561         ship                    *shipp = &Ships[objp->instance];
5562         ship_weapon     *swp = &shipp->weapons;
5563         ship_info       *sip;
5564         ai_info         *aip;
5565         object          *enemy_objp;
5566
5567         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5568         sip = &Ship_info[shipp->ship_info_index];
5569
5570         aip = &Ai_info[shipp->ai_index];
5571
5572         //      If low on slots, fire a little less often.
5573         if (Num_weapons > (int) (0.9f * MAX_WEAPONS)) {
5574                 if (frand() > 0.5f) {
5575                         nprintf(("AI", "Frame %i, %s not fire.\n", Framecount, shipp->ship_name));
5576                         return;
5577                 }
5578         }
5579
5580         if (!Ai_firing_enabled){
5581                 return;
5582         }
5583
5584         if (aip->target_objnum != -1){
5585                 enemy_objp = &Objects[aip->target_objnum];
5586         } else {
5587                 enemy_objp = NULL;
5588         }
5589
5590         if ( (swp->current_primary_bank < 0) || (swp->current_primary_bank >= swp->num_primary_banks) || timestamp_elapsed(aip->primary_select_timestamp)) {
5591                 int     flags = 0;
5592                 // AL 2-11-98: If attacking any subsystem (not just engines), use disrupter weapon
5593 //              if ((aip->targeted_subsys != NULL) && (aip->targeted_subsys->system_info->type == SUBSYSTEM_ENGINE)) {
5594                 if ( aip->targeted_subsys != NULL ) {
5595                         flags = WIF_PUNCTURE;
5596                 }
5597                 ai_select_primary_weapon(objp, enemy_objp, flags);
5598                 ship_primary_changed(shipp);    // AL: maybe send multiplayer information when AI ship changes primaries
5599                 aip->primary_select_timestamp = timestamp(5 * 1000);    //      Maybe change primary weapon five seconds from now.
5600         }
5601
5602         //      If pointing nearly at predicted collision point of target, bash orientation to be perfectly pointing.
5603         float   dot;
5604         vector  v2t;
5605
5606 //      if (!IS_VEC_NULL(&G_predicted_pos)) {
5607         if (!( vm_vec_mag_quick(&G_predicted_pos) < AICODE_SMALL_MAGNITUDE )) {
5608                 if ( !vm_vec_cmp(&G_predicted_pos, &G_fire_pos) ) {
5609                         nprintf(("Warning", "Avoid NULL vector assert.. why are G_predicted_pos and G_fire_pos the same?\n"));
5610                 } else {
5611                         vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5612                         dot = vm_vec_dot(&v2t, &objp->orient.fvec);
5613                         if (dot > .998629534f){ //      if within 3.0 degrees of desired heading, bash
5614                                 vm_vector_2_matrix(&objp->orient, &v2t, &objp->orient.uvec, NULL);
5615                         }
5616                 }
5617         }
5618
5619         //      Make sure not firing at a protected ship unless firing at a live subsystem.
5620         //      Note: This happens every time the ship tries to fire, perhaps every frame.
5621         //      Should be wrapped in a timestamp, same one that enables it to fire, but that is complicated
5622         //      by multiple banks it can fire from.
5623         if (aip->target_objnum != -1) {
5624                 object  *tobjp = &Objects[aip->target_objnum];
5625                 if (tobjp->flags & OF_PROTECTED) {
5626                         if (aip->targeted_subsys != NULL) {
5627                                 int     type;
5628
5629                                 type = aip->targeted_subsys->system_info->type;
5630                                 if (ship_get_subsystem_strength(&Ships[tobjp->instance], type) == 0.0f) {
5631                                         aip->target_objnum = -1;
5632                                         return;
5633                                 }
5634                         } else {
5635                                 aip->target_objnum = -1;
5636                                 return;
5637                         }
5638                 }
5639         }
5640
5641         //      If enemy is protected, not firing a puncture weapon and enemy's hull is low, don't fire.
5642         if ((enemy_objp != NULL) && (enemy_objp->flags & OF_PROTECTED)) {
5643                 // AL: 3-6-98: Check if current_primary_bank is valid
5644                 if ((enemy_objp->hull_strength < 750.0f) && 
5645                         ((aip->targeted_subsys == NULL) || (enemy_objp->hull_strength < aip->targeted_subsys->current_hits + 50.0f)) &&
5646                         (swp->current_primary_bank >= 0) ) {
5647                         if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)) {
5648                                 //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));
5649                                 swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(1000);
5650                                 return;
5651                         }
5652
5653                         /*
5654                         int     num_attacking;
5655                         num_attacking = num_enemies_attacking(enemy_objp-Objects);
5656                         if (enemy_objp->hull_strength / num_attacking < 200.0f) {
5657                                 if (frand() < 0.75f) {
5658                                         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));
5659                                         swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(500);
5660                                         return;
5661                                 }
5662                         }
5663                         */
5664                 }
5665         }
5666
5667         set_primary_weapon_linkage(objp);
5668         
5669         // I think this will properly solve the problem
5670         // fire non-streaming weapons
5671         ship_fire_primary(objp, 0);
5672         
5673         // fire streaming weapons
5674         shipp->flags |= SF_TRIGGER_DOWN;
5675         ship_fire_primary(objp, 1);
5676         shipp->flags &= ~SF_TRIGGER_DOWN;
5677 }
5678
5679 //      --------------------------------------------------------------------------
5680 //      Return number of nearby enemy fighters.
5681 //      threshold is the distance within which a ship is considered near.
5682 //
5683 // input:       enemy_team_mask =>      teams that are considered as an enemy
5684 //                              pos                                     =>      world position to measure ship distances from
5685 //                              threshold                       =>      max distance from pos to be considered "near"
5686 //
5687 // exit:                number of ships within threshold units of pos
5688 int num_nearby_fighters(int enemy_team_mask, vector *pos, float threshold)
5689 {
5690         ship_obj        *so;
5691         object  *ship_objp;
5692         int             count = 0;
5693
5694         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5695
5696                 ship_objp = &Objects[so->objnum];
5697
5698                 if (Ships[ship_objp->instance].team & enemy_team_mask) {
5699                         if (Ship_info[Ships[ship_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) {
5700                                 if (vm_vec_dist_quick(pos, &ship_objp->pos) < threshold)
5701                                         count++;
5702                         }
5703                 }
5704         }
5705
5706         return count;
5707 }
5708
5709 //      --------------------------------------------------------------------------
5710 //      Select secondary weapon to fire.
5711 //      Currently, 1/16/98:
5712 //              If 0 secondary weapons available, return -1
5713 //              If 1 available, use it.
5714 //              If 2 or more, if the current weapon is one of them, stick with it, otherwise choose a random one.
5715 //      priority1 and priority2 are Weapon_info[] bitmasks such as WIF_HOMING_ASPECT.  If any weapon has any bit in priority1
5716 //      set, that weapon will be selected.  If not, apply to priority2.  If neither, return -1, meaning no weapon selected.
5717 //      Note, priorityX have default values of -1, meaning if not set, they will match any weapon.
5718 //      Return value:
5719 //              bank index
5720 //      Should do this:
5721 //              Favor aspect seekers when attacking small ships faraway.
5722 //              Favor rapid fire dumbfire when attacking a large ship.
5723 //              Ignore heat seekers because we're not sure how they'll work.
5724 void ai_select_secondary_weapon(object *objp, ship_weapon *swp, int priority1 = -1, int priority2 = -1)
5725 {
5726         int     num_weapon_types;
5727         int     weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
5728         int     i;
5729         int     ignore_mask;
5730         int     initial_bank;
5731
5732         initial_bank = swp->current_secondary_bank;
5733
5734         //      Ignore bombs unless one of the priorities asks for them to be selected.
5735         if (WIF_HUGE & (priority1 | priority2))
5736                 ignore_mask = 0;
5737         else
5738                 ignore_mask = WIF_HUGE;
5739
5740         if (!(WIF_BOMBER_PLUS & (priority1 | priority2)))
5741                 ignore_mask |= WIF_BOMBER_PLUS;
5742
5743 #ifndef NDEBUG
5744         for (i=0; i<MAX_WEAPON_TYPES; i++) {
5745                 weapon_id_list[i] = -1;
5746                 weapon_bank_list[i] = -1;
5747         }
5748 #endif
5749
5750         //      Stuff weapon_bank_list with bank index of available weapons.
5751         num_weapon_types = get_available_secondary_weapons(objp, weapon_id_list, weapon_bank_list);
5752
5753         int     priority2_index = -1;
5754
5755         for (i=0; i<num_weapon_types; i++) {
5756                 int     wi_flags;
5757
5758                 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5759                 if (!(wi_flags & ignore_mask)) {                                        //      Maybe bombs are illegal.
5760                         if (wi_flags & priority1) {
5761                                 swp->current_secondary_bank = weapon_bank_list[i];                              //      Found first priority, return it.
5762                                 break;
5763                         } else if (wi_flags & priority2)
5764                                 priority2_index = weapon_bank_list[i];  //      Found second priority, but might still find first priority.
5765                 }
5766         }
5767
5768         //      If didn't find anything above, then pick any secondary weapon.
5769         if (i == num_weapon_types) {
5770                 swp->current_secondary_bank = priority2_index;  //      Assume we won't find anything.
5771                 if (priority2_index == -1) {
5772                         for (i=0; i<num_weapon_types; i++) {
5773                                 int     wi_flags;
5774
5775                                 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5776                                 if (!(wi_flags & ignore_mask)) {                                        //      Maybe bombs are illegal.
5777                                         if (swp->secondary_bank_ammo[i] > 0) {
5778                                                 swp->current_secondary_bank = i;
5779                                                 break;
5780                                         }
5781                                 }
5782                         }
5783                 }
5784         }
5785
5786         //      If switched banks, force reacquisition of aspect lock.
5787         if (swp->current_secondary_bank != initial_bank) {
5788                 ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
5789                 
5790                 aip->aspect_locked_time = 0.0f;
5791                 aip->current_target_is_locked = 0;
5792         }
5793
5794
5795         ship_secondary_changed(&Ships[objp->instance]); // AL: let multiplayer know if secondary bank has changed
5796         // nprintf(("AI", "Ship %s selected weapon %s\n", Ships[objp->instance].ship_name, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
5797 }
5798
5799 //      Return number of objects homing on object *target_objp
5800 int compute_num_homing_objects(object *target_objp)
5801 {
5802         object  *objp;
5803         int             count = 0;
5804
5805         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
5806                 if (objp->type == OBJ_WEAPON) {
5807                         if (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_HOMING) {
5808                                 if (Weapons[objp->instance].homing_object == target_objp) {
5809                                         count++;
5810                                 }
5811                         }
5812                 }
5813         }
5814
5815         return count;
5816 }
5817
5818 //      Object *firing_objp just fired weapon weapon_index (index in Weapon_info).
5819 //      If it's a shockwave weapon, tell your team about it!
5820 void ai_maybe_announce_shockwave_weapon(object *firing_objp, int weapon_index)
5821 {
5822         if ((firing_objp->type == OBJ_SHIP) && (Weapon_info[weapon_index].shockwave_speed > 0.0f)) {
5823                 ship_obj        *so;
5824                 int             firing_ship_team;
5825
5826                 firing_ship_team = Ships[firing_objp->instance].team;
5827
5828                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5829                         object  *A = &Objects[so->objnum];
5830                         Assert(A->type == OBJ_SHIP);
5831
5832                         if (Ships[A->instance].team == firing_ship_team) {
5833                                 ai_info *aip = &Ai_info[Ships[A->instance].ai_index];
5834                                 // AL 1-5-98: only avoid shockwave if not docked or repairing
5835                                 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
5836                                         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_WEAPON;
5837                                 }
5838                         }
5839                 }
5840         }
5841 }
5842
5843 //      Return total payload of all incoming missiles.
5844 float compute_incoming_payload(object *target_objp)
5845 {
5846         missile_obj     *mo;
5847         float                   payload = 0.0f;
5848
5849         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
5850                 object  *objp;
5851
5852                 objp = &Objects[mo->objnum];
5853                 Assert(objp->type == OBJ_WEAPON);
5854                 if (Weapons[objp->instance].homing_object == target_objp) {
5855                         payload += Weapon_info[Weapons[objp->instance].weapon_info_index].damage;
5856                 }
5857         }
5858
5859         return payload;
5860 }
5861
5862 //      --------------------------------------------------------------------------
5863 //      Return true if OK for *aip to fire its current weapon at its current target.
5864 //      Only reason this function returns false is:
5865 //              weapon is a homer
5866 //              targeted at player
5867 //                      OR:     player has too many homers targeted at him
5868 //                                      Missiontime in that dead zone in which can't fire at this player
5869 //      Note: If player is attacking a ship, that ship is allowed to fire at player.  Otherwise, we get in a situation in which
5870 //      player is attacking a large ship, but that large ship is not defending itself with missiles.
5871 int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip)
5872 {
5873         int     num_homers = 0;
5874         object  *tobjp = &Objects[target_objnum];
5875
5876         if (target_objnum > -1) {
5877                 // AL 3-4-98: Ensure objp target is a ship first 
5878                 if ( tobjp->type == OBJ_SHIP ) {
5879
5880                         // should not get this far. check if ship is protected from beam and weapon is type beam
5881                         if ( (wip->wi_flags & WIF_BEAM) && (tobjp->flags & OF_BEAM_PROTECTED) ) {
5882                                 Int3();
5883                                 return 0;
5884                         }
5885                         if (Ship_info[Ships[tobjp->instance].ship_info_index].flags & SIF_SMALL_SHIP) {
5886                                 num_homers = compute_num_homing_objects(&Objects[target_objnum]);
5887                         }
5888                 }
5889
5890                 //      If player, maybe fire based on Skill_level and number of incoming weapons.
5891                 //      If non-player, maybe fire based on payload of incoming weapons.
5892                 if (wip->wi_flags & WIF_HOMING) {
5893                         if ((target_objnum > -1) && (tobjp->flags & OF_PLAYER_SHIP)) {
5894                                 if (Ai_info[Ships[tobjp->instance].ai_index].target_objnum != objnum) {
5895                                         //      Don't allow AI ships to fire at player for fixed periods of time based on skill level.
5896                                         //      With 5 skill levels, at Very Easy, they fire in 1/7 of every 10 second interval.
5897                                         //      At Easy, 2/7...at Expert, 5/7
5898                                         int t = ((Missiontime /(65536*10)) ^ target_objnum ^ 0x01) % (NUM_SKILL_LEVELS+2);
5899                                         if (t > Game_skill_level) {
5900                                                 //nprintf(("AI", "Not OK to fire homer at time thing %i\n", t));
5901                                                 return 0;
5902                                         }
5903                                 }
5904                                 //nprintf(("AI", " IS OK to fire homer at time thing %i ***\n", t));
5905                                 int     swarmers = 0;
5906                                 if (wip->wi_flags & WIF_SWARM)
5907                                         swarmers = 2;   //      Note, always want to be able to fire swarmers if no currently incident homers.
5908                                 if (Max_allowed_player_homers[Game_skill_level] < num_homers + swarmers) {
5909                                         return 0;
5910                                 }
5911                         } else if (num_homers > 3) {
5912                                 float   incoming_payload;
5913
5914                                 incoming_payload = compute_incoming_payload(&Objects[target_objnum]);
5915
5916                                 if (incoming_payload > tobjp->hull_strength) {
5917                                         return 0;
5918                                 }
5919                         }
5920                 }
5921         }
5922
5923         return 1;
5924 }
5925
5926 //      --------------------------------------------------------------------------
5927 //      Fire a secondary weapon.
5928 //      Maybe choose to fire a different one.
5929 //      priority1 and priority2 are optional parameters with defaults = -1
5930 int ai_fire_secondary_weapon(object *objp, int priority1, int priority2)
5931 {
5932         ship_weapon *swp;
5933         ship    *shipp;
5934         ship_info *sip;
5935         int             current_bank;
5936         int             rval = 0;
5937
5938 #ifndef NDEBUG
5939         if (!Ai_firing_enabled)
5940                 return rval;
5941 #endif
5942
5943         Assert( objp != NULL );
5944         Assert(objp->type == OBJ_SHIP);
5945         shipp = &Ships[objp->instance];
5946         swp = &shipp->weapons;
5947
5948         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5949         sip = &Ship_info[shipp->ship_info_index];
5950
5951         //      Select secondary weapon.
5952         current_bank = swp->current_secondary_bank; //ai_select_secondary_weapon(objp, swp, priority1, priority2);
5953
5954         //nprintf(("AI", "Frame %i: Current bank = %i, ammo remaining = %i\n", Framecount, current_bank, swp->secondary_bank_ammo[current_bank]));
5955         if (current_bank == -1) {
5956                 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5957                 return rval;
5958         }
5959
5960         Assert(current_bank < shipp->weapons.num_secondary_banks);
5961
5962         weapon_info     *wip = &Weapon_info[shipp->weapons.secondary_bank_weapons[current_bank]];
5963
5964         if ((wip->wi_flags & WIF_HOMING_ASPECT) && (!Ai_info[shipp->ai_index].current_target_is_locked)) {
5965                 //nprintf(("AI", "Not firing secondary weapon because not aspect locked.\n"));
5966                 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
5967         } else if ((wip->wi_flags & WIF_BOMB) || (vm_vec_dist_quick(&objp->pos, &En_objp->pos) > 50.0f)) {
5968                 //      This might look dumb, firing a bomb even if closer than 50 meters, but the reason is, if you're carrying
5969                 //      bombs, delivering them is probably more important than surviving.
5970                 ai_info *aip;
5971
5972                 aip = &Ai_info[shipp->ai_index];
5973                 
5974                 //      Note, maybe don't fire if firing at player and any homers yet fired.
5975                 //      Decreasing chance to fire the more homers are incoming on player.
5976                 if (check_ok_to_fire(OBJ_INDEX(objp), aip->target_objnum, wip)) {
5977                         if (ship_fire_secondary(objp)) {
5978                                 rval = 1;
5979                                 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5980                                 //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));
5981                         }
5982
5983                 } else {
5984                         swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5985                 }
5986         }
5987
5988         return rval;
5989 }
5990
5991 //      Return true if it looks like obj1, if continuing to move along current vector, will
5992 //      collide with obj2.
5993 int might_collide_with_ship(object *obj1, object *obj2, float dot_to_enemy, float dist_to_enemy, float duration)
5994 {
5995         if (obj1->phys_info.speed * duration + 2*(obj1->radius + obj2->radius) > dist_to_enemy)
5996                 if (dot_to_enemy > 0.8f - 2*(obj1->radius + obj2->radius)/dist_to_enemy)
5997                         return objects_will_collide(obj1, obj2, duration, 2.0f);
5998
5999 //              BABY - 
6000 //              CONDITION 1, dist_to_enemy < o1_rad + o2_rad + (obj1.speed + obj2.speed) * time + 50
6001         
6002         return 0;
6003
6004 }
6005
6006 //      --------------------------------------------------------------------------
6007 //      Return true if ship *objp firing a laser believes it will hit a teammate.
6008 int might_hit_teammate(object *firing_objp)
6009 {
6010         int             team;
6011         object  *objp;
6012         ship_obj        *so;
6013
6014         team = Ships[firing_objp->instance].team;
6015
6016         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6017                 objp = &Objects[so->objnum];
6018                 if (Ships[objp->instance].team == team) {
6019                         float           dist, dot;
6020                         vector  vec_to_objp;
6021
6022                         vm_vec_sub(&vec_to_objp, &firing_objp->pos, &objp->pos);
6023                         dist = vm_vec_mag_quick(&vec_to_objp);
6024                         dot = vm_vec_dot(&firing_objp->orient.fvec, &vec_to_objp)/dist;
6025                         if (might_collide_with_ship(firing_objp, objp, dot, dist, 2.0f))
6026                                 return 1;
6027                 }
6028         }
6029
6030         return 0;
6031
6032 }
6033
6034 //int   Team_not_fire_count=0, Team_hit_count = 0;
6035
6036 void render_all_ship_bay_paths(object *objp)
6037 {
6038         int             i,j,color;
6039         ship            *sp = &Ships[objp->instance];
6040         polymodel       *pm;
6041         model_path      *mp;
6042
6043         pm = model_get(sp->modelnum);
6044         vector  global_path_point;
6045         vertex  v, prev_vertex;
6046
6047         if ( pm->ship_bay == NULL )
6048                 return;
6049
6050         for ( i = 0; i < pm->ship_bay->num_paths; i++ ) {
6051                 mp = &pm->paths[pm->ship_bay->paths[i]];
6052
6053                 for ( j = 0; j < mp->nverts; j++ ) {
6054                         vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6055                         vm_vec_add2(&global_path_point, &objp->pos);
6056                         g3_rotate_vertex(&v, &global_path_point);
6057                         color = 255 - j*50;
6058                         if ( color < 50 ) 
6059                                 color = 100;
6060                         gr_set_color(0, color, 0);
6061
6062                         if ( j == mp->nverts-1 ) {
6063                                 gr_set_color(255, 0, 0);
6064                         }
6065
6066                         g3_draw_sphere( &v, 1.5f);
6067
6068                         if ( j > 0 )
6069                                 g3_draw_line(&v, &prev_vertex);
6070
6071                         prev_vertex = v;
6072         
6073                 }
6074         }
6075 }
6076
6077 // debug function to show all path points associated with an object
6078 void render_all_subsys_paths(object *objp)
6079 {
6080         int             i,j,color;
6081         ship            *sp = &Ships[objp->instance];
6082         polymodel       *pm;
6083         model_path      *mp;
6084
6085         pm = model_get(sp->modelnum);
6086         vector  global_path_point;
6087         vertex  v, prev_vertex;
6088
6089         if ( pm->ship_bay == NULL )
6090                 return;
6091
6092         for ( i = 0; i < pm->n_paths; i++ ) {
6093                 mp = &pm->paths[i];
6094                 for ( j = 0; j < mp->nverts; j++ ) {
6095                         vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6096                         vm_vec_add2(&global_path_point, &objp->pos);
6097                         g3_rotate_vertex(&v, &global_path_point);
6098                         color = 255 - j*50;
6099                         if ( color < 50 ) 
6100                                 color = 100;
6101                         gr_set_color(0, color, 0);
6102
6103                         if ( j == mp->nverts-1 ) {
6104                                 gr_set_color(255, 0, 0);
6105                         }
6106
6107                         g3_draw_sphere( &v, 1.5f);
6108
6109                         if ( j > 0 )
6110                                 g3_draw_line(&v, &prev_vertex);
6111
6112                         prev_vertex = v;
6113                 }
6114         }
6115 }
6116
6117 void render_path_points(object *objp)
6118 {
6119         ship            *shipp = &Ships[objp->instance];
6120         ai_info *aip = &Ai_info[shipp->ai_index];
6121         object  *dobjp;
6122         polymodel       *pm;
6123
6124         render_all_subsys_paths(objp);
6125         render_all_ship_bay_paths(objp);
6126
6127         if (aip->goal_objnum < 0)
6128                 return;
6129
6130         dobjp = &Objects[aip->goal_objnum];
6131         pm = model_get(Ships[dobjp->instance].modelnum);
6132         vector  dock_point, global_dock_point;
6133         vertex  v;
6134
6135         ship_model_start(&Objects[aip->goal_objnum]);
6136         if (pm->n_docks) {
6137                 dock_point = pm->docking_bays[0].pnt[0];
6138                 model_find_world_point(&global_dock_point, &dock_point, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6139                 g3_rotate_vertex(&v, &global_dock_point);
6140                 gr_set_color(255, 255, 255);
6141                 g3_draw_sphere( &v, 1.5f);
6142         }
6143
6144         if (aip->path_start != -1) {
6145                 vertex          prev_vertex;
6146                 pnode                   *pp = &Path_points[aip->path_start];
6147                 int                     num_points = aip->path_length;
6148                 int                     i;
6149
6150                 for (i=0; i<num_points; i++) {
6151                         vertex  v0;
6152
6153                         g3_rotate_vertex( &v0, &pp->pos );
6154
6155                         gr_set_color(0, 128, 96);
6156                         if (i != 0)
6157                                 g3_draw_line(&v0, &prev_vertex);
6158
6159                         if (pp-Path_points == aip->path_cur)
6160                                 gr_set_color(255,255,0);
6161                         
6162                         g3_draw_sphere( &v0, 4.5f);
6163
6164                         //      Connect all the turrets that can fire upon this point to this point.
6165 /*                      if (0) { //pp->path_index != -1) {
6166                                 model_path      *pmp;
6167                                 mp_vert         *pmpv;
6168
6169                                 get_base_path_info(pp->path_index, aip->goal_objnum, &pmp, &pmpv);
6170
6171                                 if (pmpv->nturrets) {
6172                                         for (int j = 0; j<pmpv->nturrets; j++) {
6173                                                 vertex  v1;
6174                                                 vector  turret_pos;
6175                                                 ship_subsys     *ssp;
6176
6177                                                 ssp = ship_get_indexed_subsys(&Ships[Objects[aip->goal_objnum].instance], pmpv->turret_ids[j]);
6178
6179 model_find_world_point(&turret_pos, &ssp->system_info->pnt, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6180         
6181                                                 g3_rotate_vertex(&v1, &turret_pos);
6182                                                 gr_set_color(255, 255, 0);
6183                                                 g3_draw_line(&v0, &v1);
6184                                                 g3_draw_sphere( &v1, 1.5f);
6185                                         }
6186                                 }
6187                         } */
6188
6189                         prev_vertex = v0;
6190
6191                         pp++;
6192                 }
6193         }
6194
6195         ship_model_stop(&Objects[aip->goal_objnum]);
6196 }
6197
6198 // Return the distance that the current AI weapon will travel
6199 float ai_get_weapon_dist(ship_weapon *swp)
6200 {
6201         int     bank_num, weapon_num;
6202
6203         bank_num = swp->current_primary_bank;
6204         weapon_num = swp->primary_bank_weapons[bank_num];
6205
6206         //      If weapon_num is illegal, return a reasonable value.  A valid weapon
6207         //      will get selected when this ship tries to fire.
6208         if (weapon_num == -1) {
6209                 // Int3();
6210                 return 1000.0f;
6211         }
6212
6213         return Weapon_info[weapon_num].max_speed * Weapon_info[weapon_num].lifetime;
6214 }
6215
6216 float ai_get_weapon_speed(ship_weapon *swp)
6217 {
6218         int     bank_num, weapon_num;
6219
6220         bank_num = swp->current_primary_bank;
6221         if (bank_num < 0)
6222                 return 100.0f;
6223
6224         weapon_num = swp->primary_bank_weapons[bank_num];
6225
6226         if (weapon_num == -1) {
6227                 //Int3();
6228                 return 100.0f;
6229         }
6230
6231         return Weapon_info[weapon_num].max_speed;
6232 }
6233
6234 //      Compute the predicted position of a ship to be fired upon from a turret.
6235 //      This is based on position of firing gun, enemy object, weapon speed and skill level constraints.
6236 //      Return value in *predicted_enemy_pos.
6237 //      Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6238 //      *pobjp          object firing the weapon
6239 //      *eobjp          object being fired upon
6240 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)
6241 {
6242         ship    *shipp = &Ships[pobjp->instance];
6243         float   range_time;
6244
6245         //weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6246
6247         if (weapon_speed < 1.0f)
6248                 weapon_speed = 1.0f;
6249
6250         range_time = 2.0f;
6251
6252         //      Make it take longer for enemies to get player's allies in range based on skill level.
6253         if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team)
6254                 range_time += In_range_time[Game_skill_level];
6255
6256         //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6257
6258         if (time_enemy_in_range < range_time) {
6259                 float   dist;
6260
6261                 dist = vm_vec_dist_quick(&pobjp->pos, enemy_pos);
6262                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, time_enemy_in_range * dist/weapon_speed);
6263         } else {
6264                 float   collision_time, scale;
6265                 vector  rand_vec;
6266                 ai_info *aip = &Ai_info[shipp->ai_index];
6267
6268                 collision_time = compute_collision_time(enemy_pos, enemy_vel, gun_pos, weapon_speed);
6269
6270                 if (collision_time == 0.0f){
6271                         collision_time = 100.0f;
6272                 }
6273
6274                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, collision_time);
6275                 if (time_enemy_in_range > 2*range_time){
6276                         scale = (1.0f - aip->ai_accuracy) * 4.0f;
6277                 } else {
6278                         scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - time_enemy_in_range/(2*range_time)));
6279                 }               
6280
6281                 static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6282
6283                 vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6284                 G_collision_time = collision_time;
6285                 G_fire_pos = *gun_pos;
6286         }
6287
6288         G_predicted_pos = *predicted_enemy_pos;
6289 }
6290
6291 //      Compute the predicted position of a ship to be fired upon.
6292 //      This is based on current position of firing object, enemy object, relative position of gun on firing object,
6293 //      weapon speed and skill level constraints.
6294 //      Return value in *predicted_enemy_pos.
6295 //      Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6296 void set_predicted_enemy_pos(vector *predicted_enemy_pos, object *pobjp, object *eobjp, ai_info *aip)
6297 {
6298         float   weapon_speed, range_time;
6299         ship    *shipp = &Ships[pobjp->instance];
6300
6301         weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6302         weapon_speed = max(weapon_speed, 1.0f);         // set not less than 1
6303
6304         range_time = 2.0f;
6305
6306         //      Make it take longer for enemies to get player's allies in range based on skill level.
6307         // but don't bias team v. team missions
6308         if ( !((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) ) {
6309                 if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team) {
6310                         range_time += In_range_time[Game_skill_level];
6311                 }
6312         }
6313         //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6314
6315         if (aip->time_enemy_in_range < range_time) {
6316                 float   dist;
6317
6318                 dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6319                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, aip->time_enemy_in_range * dist/weapon_speed);
6320         } else {
6321                 float   collision_time;
6322                 vector  gun_pos, pnt;
6323                 polymodel *po = model_get( Ship_info[shipp->ship_info_index].modelnum );
6324
6325                 //      Compute position of gun in absolute space and use that as fire position.
6326                 if(po->gun_banks != NULL){
6327                         pnt = po->gun_banks[0].pnt[0];
6328                 } else {
6329                         pnt = Objects[shipp->objnum].pos;
6330                 }
6331                 vm_vec_unrotate(&gun_pos, &pnt, &pobjp->orient);
6332                 vm_vec_add2(&gun_pos, &pobjp->pos);
6333
6334                 collision_time = compute_collision_time(&eobjp->pos, &eobjp->phys_info.vel, &gun_pos, weapon_speed);
6335
6336                 if (collision_time == 0.0f) {
6337                         collision_time = 100.0f;
6338                 }
6339
6340                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, collision_time);
6341
6342                 // set globals
6343                 G_collision_time = collision_time;
6344                 G_fire_pos = gun_pos;
6345         }
6346
6347         // Now add error terms (1) regular aim (2) EMP (3) stealth
6348         float scale = 0.0f;
6349         vector rand_vec;
6350
6351         // regular skill level error in aim
6352         if (aip->time_enemy_in_range > 2*range_time) {
6353                 scale = (1.0f - aip->ai_accuracy) * 4.0f;
6354         } else {
6355                 scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - aip->time_enemy_in_range/(2*range_time)));
6356         }
6357
6358         // if this ship is under the effect of an EMP blast, throw his aim off a bit
6359         if (shipp->emp_intensity > 0.0f) {
6360                 // never go lower than 1/2 of the EMP effect max, otherwise things aren't noticeable
6361                 scale += (MAX_EMP_INACCURACY * (shipp->emp_intensity < 0.5f ? 0.5f : shipp->emp_intensity));
6362                 mprintf(("AI miss scale factor (EMP) %f\n",scale));
6363         }
6364
6365         // if stealthy ship, throw his aim off, more when farther away and when dot is small
6366         if ( aip->ai_flags & AIF_STEALTH_PURSIUT ) {
6367                 float dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6368                 vector temp;
6369                 vm_vec_sub(&temp, &eobjp->pos, &pobjp->pos);
6370                 vm_vec_normalize_quick(&temp);
6371                 float dot = vm_vec_dotprod(&temp, &pobjp->orient.fvec);
6372                 float st_err = 3.0f * (1.4f - dot) * (1.0f + dist / (get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST)) * (1 - aip->ai_accuracy);
6373                 scale += st_err;
6374                 // mprintf(("error term: %.1f, total %.1f, dot %.3f\n", st_err, scale, dot));
6375         }
6376
6377         // get a random vector that changes slowly over time (1x / sec)
6378         static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6379
6380         vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6381
6382         // set global
6383         G_predicted_pos = *predicted_enemy_pos;
6384 }
6385
6386 //      Handler of submode for Chase.  Go into a continuous turn for awhile.
6387 void ai_chase_ct()
6388 {
6389         vector          tvec;
6390         ship_info       *sip;
6391         ai_info         *aip;
6392
6393         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6394         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6395         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6396         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6397
6398         //      Make a continuous turn towards any combination of possibly negated
6399         // up and right vectors.
6400         tvec = Pl_objp->pos;
6401
6402         if (aip->submode_parm0 & 0x01)
6403                 vm_vec_add2(&tvec, &Pl_objp->orient.rvec);
6404         if (aip->submode_parm0 & 0x02)
6405                 vm_vec_sub2(&tvec, &Pl_objp->orient.rvec);
6406         if (aip->submode_parm0 & 0x04)
6407                 vm_vec_add2(&tvec, &Pl_objp->orient.uvec);
6408         if (aip->submode_parm0 & 0x08)
6409                 vm_vec_sub2(&tvec, &Pl_objp->orient.uvec);
6410
6411         //      Detect degenerate cases that cause tvec to be same as player pos.
6412         if (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
6413                 aip->submode_parm0 &= 0x05;
6414                 if (aip->submode_parm0 == 0)
6415                         aip->submode_parm0 = 1;
6416                 vm_vec_add2(&tvec, &Pl_objp->orient.rvec);
6417         }
6418
6419         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6420         accelerate_ship(aip, 1.0f);
6421 }
6422
6423 //      ATTACK submode handler for chase mode.
6424 void ai_chase_eb(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
6425 {
6426         vector  _pep;
6427         float           dot_to_enemy, dot_from_enemy;
6428
6429         compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
6430
6431         //      If we're trying to slow down to get behind, then point to turn towards is different.
6432         _pep = *predicted_enemy_pos;
6433         if ((dot_to_enemy > dot_from_enemy + 0.1f) || (dot_to_enemy > 0.9f))
6434                 vm_vec_scale_add(&_pep, &Pl_objp->pos, &En_objp->orient.fvec, 100.0f);
6435
6436         ai_turn_towards_vector(&_pep, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6437
6438         accelerate_ship(aip, 0.0f);
6439 }
6440
6441 //      Return time until weapon_objp might hit ship_objp.
6442 //      Assumes ship_objp is not moving.
6443 //      Returns negative time if not going to hit.
6444 //      This is a very approximate function, but is pretty fast.
6445 float ai_endangered_time(object *ship_objp, object *weapon_objp)
6446 {
6447         float           to_dot, from_dot, dist;
6448
6449         dist = compute_dots(ship_objp, weapon_objp, &to_dot, &from_dot);
6450
6451         //      Note, this is bogus.  It assumes only the weapon is moving.
6452         //      Only proceed if weapon sort of pointing at object and object pointing towards or away from weapon
6453         //      (Ie, if object moving at right angle to weapon, just continue for now...)
6454         if (weapon_objp->phys_info.speed < 1.0f)
6455                 return dist + 1.0f;
6456         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))
6457                 return dist / weapon_objp->phys_info.speed;
6458         else
6459                 return -1.0f;
6460 }
6461
6462 //      Return time until danger weapon could hit this ai object.
6463 //      Return negative time if not endangered.
6464 float ai_endangered_by_weapon(ai_info *aip)
6465 {
6466         object  *weapon_objp;
6467
6468         if (aip->danger_weapon_objnum == -1) {
6469                 return -1.0f;
6470         }
6471
6472         weapon_objp = &Objects[aip->danger_weapon_objnum];
6473
6474         if (weapon_objp->signature != aip->danger_weapon_signature) {
6475                 aip->danger_weapon_objnum = -1;
6476                 return -1.0f;
6477         }
6478
6479         return ai_endangered_time(&Objects[Ships[aip->shipnum].objnum], weapon_objp);
6480 }
6481
6482 //      Return true if this ship is near full strength.
6483 int ai_near_full_strength(object *objp, ship_info *sip)
6484 {
6485         return (objp->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp)/sip->shields > 0.8f);
6486 }
6487                                 
6488 //      Set acceleration while in attack mode.
6489 void attack_set_accel(ai_info *aip, float dist_to_enemy, float dot_to_enemy, float dot_from_enemy)
6490 {
6491         float   speed_ratio;
6492
6493         if (En_objp->phys_info.speed > 1.0f)
6494                 speed_ratio = Pl_objp->phys_info.speed/En_objp->phys_info.speed;
6495         else
6496                 speed_ratio = 5.0f;
6497
6498         //      Sometimes, told to attack slowly.  Allows to get in more hits.
6499         if (aip->ai_flags & AIF_ATTACK_SLOWLY) {
6500                 if ((dist_to_enemy > 200.0f) && (dist_to_enemy < 800.0f)) {
6501                         if ((dot_from_enemy < 0.9f) || ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
6502                                 //nprintf(("AI", " slowly "));
6503                                 accelerate_ship(aip, max(1.0f - (dist_to_enemy-200.0f)/600.0f, 0.1f));
6504                                 return;
6505                         }
6506                 } else
6507                         aip->ai_flags &= ~AIF_ATTACK_SLOWLY;
6508         }
6509
6510         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) {
6511                 //nprintf(("AI", "1"));
6512                 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6513                         if (dist_to_enemy > 800.0f) {
6514                                 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6515                                         float percent_left;
6516                                         ship    *shipp;
6517                                         ship_info *sip;
6518
6519                                         shipp = &Ships[Pl_objp->instance];
6520                                         sip = &Ship_info[shipp->ship_info_index];
6521
6522                                         if (sip->afterburner_fuel_capacity > 0.0f) {
6523                                                 percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
6524                                                 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
6525                                                         afterburners_start(Pl_objp);
6526                                                         aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
6527                                                 }
6528                                         }
6529                                 }
6530                         }
6531                 }
6532
6533                 accelerate_ship(aip, 1.0f);
6534         } else if ((Missiontime - aip->last_hit_time > F1_0*7)
6535                 && (En_objp->phys_info.speed < 10.0f) 
6536                 && (dist_to_enemy > 25.0f) 
6537                 && (dot_to_enemy > 0.8f)
6538                 && (dot_from_enemy < 0.8f)) {
6539                 accelerate_ship(aip, 0.0f);             //      No one attacking us, so don't need to move.
6540         } else if ((dot_from_enemy < 0.25f) && (dot_to_enemy > 0.5f)) {
6541                 set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed);
6542         } else if (Pl_objp->phys_info.speed < 15.0f) {
6543                 accelerate_ship(aip, 1.0f);
6544         } else if (Pl_objp->phys_info.speed > En_objp->phys_info.speed - 1.0f) {
6545                 if (dot_from_enemy > 0.75f)
6546                         accelerate_ship(aip, 1.0f);
6547                 else
6548                         set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed*0.75f + 3.0f);
6549         } else {
6550                 change_acceleration(aip, 0.5f);
6551         }
6552 }
6553
6554 //      Pl_objp (aip) tries to get behind En_objp.
6555 //      New on 2/21/98: If this ship can move backwards and slide, maybe do that to get behind.
6556 void get_behind_ship(ai_info *aip, ship_info *sip, float dist_to_enemy)
6557 {
6558         vector  new_pos;
6559         float           dot;
6560         vector  vec_from_enemy;
6561         float           dist;
6562
6563         dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
6564
6565         vm_vec_scale_add(&new_pos, &En_objp->pos, &En_objp->orient.fvec, -100.0f);              //      Pick point 100 units behind.
6566         ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6567
6568         dot = vm_vec_dot(&vec_from_enemy, &En_objp->orient.fvec);
6569
6570         if (dot > 0.25f) {
6571                 accelerate_ship(aip, 1.0f);
6572         } else {
6573                 accelerate_ship(aip, (dot + 1.0f)/2.0f);
6574         }
6575 }
6576
6577 int avoid_player(object *objp, vector *goal_pos)
6578 {
6579         maybe_avoid_player(Pl_objp, goal_pos);
6580         ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
6581
6582         if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6583                 ship_info *sip = &Ship_info[Ships[objp->instance].ship_info_index];
6584
6585                 if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6586                         ai_turn_towards_vector(&aip->avoid_goal_point, objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6587                         accelerate_ship(aip, 0.5f);
6588                         return 1;
6589                 }
6590         }
6591
6592         return 0;
6593 }
6594
6595 //      Determine if a cylinder of width radius from p0 to p1 will collide with big_objp.
6596 //      If so, stuff *collision_point.
6597 int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6598 {
6599         mc_info mc;
6600
6601         mc.model_num = Ships[big_objp->instance].modelnum;              // Fill in the model to check
6602         mc.orient = &big_objp->orient;                  // The object's orient
6603         mc.pos = &big_objp->pos;                                        // The object's position
6604         mc.p0 = p0;                                                                             // Point 1 of ray to check
6605         mc.p1 = p1;
6606         mc.flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE | MC_SUBMODEL;                                  // flags
6607
6608         mc.radius = radius;
6609
6610         // Only check the 2nd lowest hull object
6611         polymodel *pm = model_get(Ships[big_objp->instance].modelnum);
6612         mc.submodel_num = pm->detail[0]; //pm->submodel->num_details-2];
6613         model_collide(&mc);
6614
6615         if (mc.num_hits)
6616                 *collision_point = mc.hit_point_world;
6617
6618         return mc.num_hits;
6619 }
6620
6621 //      Return true/false if *objp will collide with *big_objp
6622 //      Stuff distance in *distance to collision point if *objp will collide with *big_objp within delta_time seconds.
6623 //      Global collision point stuffed in *collision_point
6624 int will_collide_with_big_ship(object *objp, vector *goal_point, object *big_objp, vector *collision_point, float delta_time)
6625 {
6626         float           radius;
6627         vector  end_pos;
6628
6629         radius = big_objp->radius + delta_time * objp->phys_info.speed;
6630
6631         if (vm_vec_dist_quick(&big_objp->pos, &objp->pos) > radius) {
6632                 return 0;
6633         }
6634
6635         if (goal_point == NULL) {
6636                 vm_vec_scale_add(&end_pos, &objp->pos, &objp->phys_info.vel, delta_time);                                       // Point 2 of ray to check
6637         } else {
6638                 end_pos = *goal_point;
6639         }
6640
6641         return will_collide_pp(&objp->pos, &end_pos, objp->radius, big_objp, collision_point);
6642 }
6643
6644 //      Return true if *objp is expected to collide with a large ship.
6645 //      Stuff global collision point in *collision_point.
6646 //      If *goal_point is not NULL, use that as the point towards which *objp will be flying.  Don't use *objp velocity
6647 //      *ignore_objp will typically be the target this ship is pursuing, either to attack or guard.  We don't want to avoid it.
6648 int will_collide_with_big_ship_all(object *objp, object *ignore_objp, vector *goal_point, vector *collision_point, float *distance, float delta_time)
6649 {
6650         ship_obj        *so;
6651         object  *big_objp;
6652         int             collision_obj_index = -1;
6653         float           min_dist = 999999.9f;
6654         float           collision_time = -1.0f;
6655
6656         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6657                 float   time = 0.0f;
6658                 big_objp = &Objects[so->objnum];
6659
6660                 if (big_objp == ignore_objp)
6661                         continue;
6662
6663                 if (Ship_info[Ships[big_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
6664                         vector  cur_collision_point;
6665                         float           cur_dist;
6666
6667                         if (will_collide_with_big_ship(objp, goal_point, big_objp, &cur_collision_point, delta_time)) {
6668
6669                                 cur_dist = vm_vec_dist(&cur_collision_point, &objp->pos);
6670
6671                                 if (cur_dist < min_dist) {
6672                                         min_dist = cur_dist;
6673                                         *collision_point = cur_collision_point;
6674                                         collision_time = time;
6675                                         collision_obj_index = OBJ_INDEX(big_objp);
6676                                 }
6677                         }
6678                 }
6679         }
6680
6681         *distance = min_dist;
6682         return collision_obj_index;
6683
6684 }
6685
6686 typedef struct {
6687         float           dist;
6688         int             collide;
6689         vector  pos;
6690 } sgoal;
6691
6692 //int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6693 //      Pick a point for *objp to fly towards to avoid a collision with *big_objp at *collision_point
6694 //      Return result in *avoid_pos
6695 void mabs_pick_goal_point(object *objp, object *big_objp, vector *collision_point, vector *avoid_pos)
6696 {
6697         matrix  mat1;
6698         sgoal           goals[4];
6699         vector  v2b;
6700
6701         vm_vec_normalized_dir(&v2b, collision_point, &objp->pos);
6702         vm_vector_2_matrix(&mat1, &v2b, NULL, NULL);
6703
6704         int     found = 0;
6705
6706         //      Try various scales, in 0.5f, 0.75f, 1.0f, 1.25f.
6707         //      First try 0.5f to see if we can find a point that near the center of the target ship, which presumably
6708         //      means less of a turn.
6709         //      Try going as far as 1.25f * radius.
6710         float   s;
6711         for (s=0.5f; s<1.3f; s += 0.25f) {
6712                 int     i;
6713                 for (i=0; i<4; i++) {
6714                         vector p = big_objp->pos;
6715                         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
6716                         float kr = big_objp->radius*s + objp->radius * ((OBJ_INDEX(objp) % 4) ^ 2)/4;
6717                         if (i&1)
6718                                 ku = -ku;
6719                         if (i&2)
6720                                 kr = -kr;
6721                         vm_vec_scale_add2(&p, &mat1.uvec, ku);
6722                         vm_vec_scale_add2(&p, &mat1.rvec, kr);
6723                         goals[i].pos = p;
6724                         goals[i].dist = vm_vec_dist_quick(&objp->pos, &p);
6725                         goals[i].collide = will_collide_pp(&objp->pos, &p, objp->radius, big_objp, collision_point);
6726                         if (!goals[i].collide)
6727                                 found = 1;
6728                 }
6729
6730                 //      If we found a point that doesn't collide, find the nearest one and make that the *avoid_pos.
6731                 if (found) {
6732                         float   min_dist = 9999999.9f;
6733                         int     min_index = -1;
6734
6735                         for (i=0; i<4; i++) {
6736                                 if (!goals[i].collide && (goals[i].dist < min_dist)) {
6737                                         min_dist = goals[i].dist;
6738                                         min_index = i;
6739                                 }
6740                         }
6741
6742                         Assert(i != -1);
6743                         if (i != -1) {
6744                                 *avoid_pos = goals[min_index].pos;
6745                                 return;
6746                         }
6747                 }
6748         }
6749
6750         //      Drat.  We tried and tried and could not find a point that did not cause a collision.
6751         //      Get this dump pilot far away from the problem ship.
6752         vector  away_vec;
6753         vm_vec_normalized_dir(&away_vec, &objp->pos, collision_point);
6754         vm_vec_scale_add(avoid_pos, &objp->pos, &away_vec, big_objp->radius*1.5f);
6755
6756 }
6757
6758 //      Return true if a large ship is being ignored.
6759 int maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vector *goal_point, float delta_time)
6760 {
6761         if (timestamp_elapsed(aip->avoid_check_timestamp)) {
6762                 float           distance;
6763                 vector  collision_point;
6764                 int             ship_num;
6765                 if ((ship_num = will_collide_with_big_ship_all(Pl_objp, ignore_objp, goal_point, &collision_point, &distance, delta_time)) != -1) {
6766                         aip->ai_flags |= AIF_AVOIDING_BIG_SHIP;
6767                         mabs_pick_goal_point(objp, &Objects[ship_num], &collision_point, &aip->avoid_goal_point);
6768                         float dist = vm_vec_dist_quick(&aip->avoid_goal_point, &objp->pos);
6769                         aip->avoid_check_timestamp = timestamp(2000 + min(1000, (int) (dist * 2.0f)));  //      Delay until check again is based on distance to avoid point.
6770                         aip->avoid_ship_num = ship_num;
6771                 } else {
6772                         aip->ai_flags &= ~AIF_AVOIDING_BIG_SHIP;
6773                         aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
6774                         aip->avoid_ship_num = -1;
6775                         aip->avoid_check_timestamp = timestamp(1500);
6776                 }
6777         }
6778         
6779         if (aip->ai_flags & AIF_AVOIDING_BIG_SHIP) {
6780                 ship_info *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6781
6782                 vector  v2g;
6783
6784                 ai_turn_towards_vector(&aip->avoid_goal_point, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6785                 vm_vec_normalized_dir(&v2g, &aip->avoid_goal_point, &Pl_objp->pos);
6786                 float dot = vm_vec_dot(&objp->orient.fvec, &v2g);
6787                 float d2 = (1.0f + dot) * (1.0f + dot);
6788                 accelerate_ship(aip, d2/4.0f);
6789                 return 1;
6790         }
6791
6792         return 0;
6793 }
6794
6795 //      Set desired right vector for ships flying towards another ship.
6796 //      Since this is governed only by vector to target, it causes ships to align bank and look less chaotic.
6797 void compute_desired_rvec(vector *rvec, vector *goal_pos, vector *cur_pos)
6798 {
6799         vector  v2e;
6800
6801         vm_vec_normalized_dir(&v2e, goal_pos, cur_pos);
6802         rvec->x = v2e.z;
6803         rvec->y = 0.0f;
6804         rvec->z = -v2e.x;
6805         if (vm_vec_mag_squared(rvec) < 0.001f)
6806                 rvec->y = 1.0f;
6807 }
6808
6809 // Handler for stealth find submode of Chase.
6810 void ai_stealth_find()
6811 {
6812         ai_info         *aip;
6813         ship_info       *sip;
6814
6815         vector new_pos, vec_to_enemy;
6816         float dist_to_enemy, dot_to_enemy, dot_from_enemy;
6817
6818         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6819         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6820         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6821         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6822
6823         // get time since last seen
6824         int delta_time = (timestamp() - aip->stealth_last_visible_stamp);
6825
6826         // if delta_time is really big, i'm real confused, start sweep
6827         if (delta_time > 10000) {
6828                 aip->submode_parm0 = SM_SF_BAIL;
6829         }
6830
6831         // guestimate new position
6832         vm_vec_scale_add(&new_pos, &aip->stealth_last_pos, &aip->stealth_velocity, (delta_time * 0.001f));
6833
6834         // if I think he's behind me, go to the goal point
6835         if ( aip->submode_parm0 == SM_SF_BEHIND ) {
6836                 new_pos = aip->goal_point;
6837         }
6838
6839         // check for collision with big ships
6840         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &new_pos, 10.0f)) {
6841                 // reset ai submode to chase
6842                 return;
6843         }
6844
6845         // if dist is near max and dot is close to 1, accel, afterburn
6846         vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6847         dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6848         dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.fvec);
6849
6850         // 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
6851         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) ) {
6852                 // do turn around)
6853                 vm_vec_scale_add(&aip->goal_point, &Pl_objp->pos, &Pl_objp->orient.fvec, -300.0f);
6854                 aip->submode_parm0 = SM_SF_BEHIND;
6855                 vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6856                 dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6857                 dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.fvec);
6858         }
6859
6860         if ( (dist_to_enemy > get_skill_stealth_dist_scaler()*STEALTH_MAX_VIEW_DIST) && (dot_to_enemy > 0.94f) ) {              // 20 degree half angle
6861                 // accelerate ship
6862                 accelerate_ship(aip, 1.0f);
6863
6864                 // engage afterburner
6865                 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6866                         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6867                                 afterburners_start(Pl_objp);
6868                                 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
6869                         }
6870                 }
6871
6872                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6873                 return;
6874         }
6875
6876         //      If enemy more than 500 meters away, all ships flying there will tend to match bank.
6877         //      They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
6878         //      to interpolate a matrix rather than just a vector.
6879         if (dist_to_enemy > 500.0f) {
6880                 vector  rvec;
6881                 compute_desired_rvec(&rvec, &new_pos, &Pl_objp->pos);
6882                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0, &rvec);
6883         } else {
6884                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6885         }
6886
6887         dot_from_enemy = -vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.fvec);
6888
6889         attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
6890 }
6891
6892 // -----------------------------------------------------------------------------
6893 // try to find stealth ship by sweeping an area
6894 void ai_stealth_sweep()
6895 {
6896         ai_info         *aip;
6897         ship_info       *sip;
6898
6899         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6900         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6901         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6902         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6903
6904         vector goal_pt;
6905         vector forward, right, up;
6906         int lost_time;
6907
6908         // time since stealth last seen
6909         lost_time = (timestamp() - aip->stealth_last_visible_stamp);
6910
6911         // determine which pt to fly to in sweep by keeping track of parm0
6912         if (aip->submode_parm0 == SM_SS_SET_GOAL) {
6913
6914                 // don't make goal pt more than 2k from current pos
6915                 vm_vec_scale_add(&goal_pt, &aip->stealth_last_pos, &aip->stealth_velocity, (0.001f * lost_time));
6916
6917                 // make box size based on speed of stealth and expected time to intercept (keep box in range 200-500)
6918                 float box_size = vm_vec_mag_quick(&aip->stealth_velocity) * (0.001f * lost_time);
6919                 box_size = min(200.0f, box_size);
6920                 box_size = max(500.0f, box_size);
6921                 aip->stealth_sweep_box_size = box_size;
6922
6923                 aip->goal_point = goal_pt;
6924                 aip->submode_parm0 = SM_SS_BOX0;
6925         }
6926
6927         // GET UP, RIGHT, FORWARD FOR BOX based on stealth ship's velocity
6928         // if velocity changes in stealth mode, then ship is *seen*, and falls out of sweep mode
6929         // if stealth has no velocity make a velocity
6930         if ( vm_vec_mag_quick(&aip->stealth_velocity) < 1 ) {
6931                 vm_vec_rand_vec_quick(&aip->stealth_velocity);
6932         }
6933
6934         // get "right" vector for box
6935         vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_y_vector);
6936
6937         if ( vm_vec_mag_quick(&right) < 0.01 ) {
6938                 vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_z_vector);
6939         }
6940
6941         vm_vec_normalize_quick(&right);
6942
6943         // get forward for box
6944         vm_vec_copy_normalize_quick(&forward, &aip->stealth_velocity);
6945
6946         // get "up" for box
6947         vm_vec_crossprod(&up, &forward, &right);
6948         
6949         // lost far away ahead (do box)
6950         switch(aip->submode_parm0) {
6951         case SM_SS_BOX0:
6952                 goal_pt = aip->goal_point;
6953                 break;
6954
6955         // pt1 -U +R
6956         case SM_SS_LR:
6957                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
6958                 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
6959                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6960                 break;
6961
6962         // pt2 +U -R
6963         case SM_SS_UL:
6964                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
6965                 vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
6966                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6967                 break;
6968
6969         // pt3 back
6970         case SM_SS_BOX1:
6971                 goal_pt = aip->goal_point;
6972                 break;
6973
6974         // pt4 +U +R
6975         case SM_SS_UR:
6976                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
6977                 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
6978                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6979                 break;
6980
6981         // pt5 -U -R
6982         case SM_SS_LL:
6983                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
6984                 vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
6985                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6986                 break;
6987
6988         // pt6 back
6989         case SM_SS_BOX2:
6990                 goal_pt = aip->goal_point;
6991                 break;
6992
6993         default:
6994                 Int3();
6995
6996         }
6997
6998         // when close to goal_pt, update next goal pt
6999         float dist_to_goal = vm_vec_dist(&goal_pt, &Pl_objp->pos);
7000         if (dist_to_goal < 15) {
7001                 aip->submode_parm0++;
7002         }
7003
7004         // check for collision with big ship
7005         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &goal_pt, 10.0f)) {
7006                 // skip to the next pt on box
7007                 aip->submode_parm0++;
7008                 return;
7009         }
7010
7011         ai_turn_towards_vector(&goal_pt, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
7012
7013         float dot = 1.0f;
7014         if (dist_to_goal < 100) {
7015                 vector vec_to_goal;
7016                 vm_vec_normalized_dir(&vec_to_goal, &goal_pt, &Pl_objp->pos);
7017                 dot = vm_vec_dotprod(&vec_to_goal, &Pl_objp->orient.fvec);
7018         }
7019
7020         accelerate_ship(aip, 0.8f*dot);
7021 }
7022
7023 //      ATTACK submode handler for chase mode.
7024 void ai_chase_attack(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
7025 {
7026         int             start_bank;
7027         float           dot_to_enemy, dot_from_enemy; //, time_to_hit;
7028         float           bank_override = 0.0f;
7029
7030         if (avoid_player(Pl_objp, predicted_enemy_pos))
7031                 return;
7032
7033         compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
7034
7035         polymodel *po = model_get( sip->modelnum );
7036
7037         vector  *rel_pos;
7038         float           scale;
7039         vector  randvec;
7040         vector  new_pos;
7041
7042         start_bank = Ships[aip->shipnum].weapons.current_primary_bank;
7043         if (po->n_guns && start_bank != -1 ) {
7044                 rel_pos = &po->gun_banks[start_bank].pnt[0];
7045         } else
7046                 rel_pos = NULL;
7047
7048         //      If ship moving slowly relative to its size, then don't attack its center point.
7049         //      How far from center we attack is based on speed, size and distance to enemy
7050         if (En_objp->radius > En_objp->phys_info.speed) {
7051                 static_randvec(Pl_objp-Objects, &randvec);
7052                 scale = dist_to_enemy/(dist_to_enemy + En_objp->radius) * En_objp->radius;
7053                 scale *= 0.5f * En_objp->radius/(En_objp->phys_info.speed + En_objp->radius);   // scale downward by 1/2 to 1/4
7054                 vm_vec_scale_add(&new_pos, predicted_enemy_pos, &randvec, scale);
7055         } else
7056                 new_pos = *predicted_enemy_pos;
7057
7058         if (dist_to_enemy < 250.0f) {
7059                 if (dot_from_enemy > 0.7f) {
7060                         bank_override = Pl_objp->phys_info.speed;
7061                 }
7062         }
7063
7064         //      If enemy more than 500 meters away, all ships flying there will tend to match bank.
7065         //      They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
7066         //      to interpolate a matrix rather than just a vector.
7067         if (dist_to_enemy > 500.0f) {
7068                 vector  rvec;
7069                 compute_desired_rvec(&rvec, predicted_enemy_pos, &Pl_objp->pos);
7070                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0, &rvec);
7071         } else {
7072                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0);
7073         }
7074
7075         attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
7076 }
7077
7078 //      EVADE_SQUIGGLE submode handler for chase mode.
7079 //      Changed by MK on 5/5/97.
7080 //      Used to evade towards a point off the right or up vector.
7081 //      Now, evade straight away to try to get far away.
7082 //      The squiggling should protect against laser fire.
7083 void ai_chase_es(ai_info *aip, ship_info *sip)
7084 {
7085         vector  tvec;
7086         fix             timeslice;
7087         fix             scale;
7088         float           bank_override = 0.0f;
7089
7090         tvec = Pl_objp->pos;
7091
7092         timeslice = (Missiontime >> 16) & 0x0f;
7093         scale = ((Missiontime >> 16) & 0x0f) << 14;
7094
7095         if (timeslice & 0x01)
7096                 vm_vec_scale_add2(&tvec, &Pl_objp->orient.rvec, f2fl(scale ^ 0x10000));
7097         if (timeslice & 0x02)
7098                 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.rvec, f2fl(scale));
7099         if (timeslice & 0x04)
7100                 vm_vec_scale_add2(&tvec, &Pl_objp->orient.uvec, f2fl(scale ^ 0x10000));
7101         if (timeslice & 0x08)
7102                 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.uvec, f2fl(scale));
7103
7104         while (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
7105                 tvec.x += frand();
7106                 tvec.y += frand();
7107         }
7108
7109         bank_override = Pl_objp->phys_info.speed;
7110
7111         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7112         accelerate_ship(aip, 1.0f);
7113 }
7114
7115 //      Trying to get away from opponent.
7116 void ai_chase_ga(ai_info *aip, ship_info *sip)
7117 {
7118         //      If not near end of this submode, evade squiggly.  If near end, just fly straight for a bit
7119         vector  tvec;
7120         float           bank_override;
7121         vector  vec_from_enemy;
7122
7123         if (En_objp != NULL) {
7124                 vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
7125         } else
7126                 vec_from_enemy = Pl_objp->orient.fvec;
7127
7128         static_randvec(Missiontime >> 15, &tvec);
7129         vm_vec_scale(&tvec, 100.0f);
7130         vm_vec_scale_add2(&tvec, &vec_from_enemy, 300.0f);
7131         vm_vec_add2(&tvec, &Pl_objp->pos);
7132
7133         bank_override = Pl_objp->phys_info.speed;
7134
7135         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7136
7137         accelerate_ship(aip, 2.0f);
7138
7139         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
7140                 if (!(Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
7141                         float percent_left = 100.0f * Ships[Pl_objp->instance].afterburner_fuel / sip->afterburner_fuel_capacity;
7142                         if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
7143                                 afterburners_start(Pl_objp);
7144                                 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7145                         }
7146                         afterburners_start(Pl_objp);
7147                         aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7148                 }
7149         }
7150
7151 }
7152
7153 //      Make object *objp attack subsystem with ID = subnum.
7154 //      Return true if found a subsystem to attack, else return false.
7155 //      Note, can fail if subsystem exists, but has no hits.
7156 int ai_set_attack_subsystem(object *objp, int subnum)
7157 {
7158         ship                    *shipp, *attacker_shipp;
7159         ai_info         *aip;
7160         ship_subsys     *ssp;
7161         object          *attacked_objp;
7162
7163         Assert(objp->type == OBJ_SHIP);
7164         Assert(objp->instance >= 0);
7165
7166         attacker_shipp = &Ships[objp->instance];
7167         Assert(attacker_shipp->ai_index >= 0);
7168
7169         aip = &Ai_info[attacker_shipp->ai_index];
7170
7171         // MWA -- 2/27/98.  Due to AL's changes, target_objnum is now not always valid (at least sometimes
7172         // in terms of goals).  So, bail if we don't have a valid target.
7173         if ( aip->target_objnum == -1 )
7174                 return 0;
7175
7176         attacked_objp = &Objects[aip->target_objnum];
7177         shipp = &Ships[attacked_objp->instance];                //  need to get our target's ship pointer!!!
7178
7179         ssp = ship_get_indexed_subsys(shipp, subnum, &objp->pos);
7180         if (ssp == NULL)
7181                 return 0;
7182
7183         set_targeted_subsys(aip, ssp, aip->target_objnum);
7184         
7185         if (aip->ignore_objnum == aip->target_objnum)
7186                 aip->ignore_objnum = UNUSED_OBJNUM;
7187
7188         // -- Done at caller in ai_process_mission_orders -- attacked_objp->flags |= OF_PROTECTED;
7189
7190         ai_set_goal_maybe_abort_dock(objp, aip);
7191         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7192
7193         return 1;
7194 }
7195
7196 void ai_set_guard_vec(object *objp, object *guard_objp)
7197 {
7198         ai_info *aip;
7199         float   radius;
7200
7201         aip = &Ai_info[Ships[objp->instance].ai_index];
7202
7203         //      Handle case of bogus call in which ship is told to guard self.
7204         Assert(objp != guard_objp);
7205         if (objp == guard_objp) {
7206                 vm_vec_rand_vec_quick(&aip->guard_vec);
7207                 vm_vec_scale(&aip->guard_vec, 100.0f);
7208                 return;
7209         }
7210
7211         // check if guard_objp is BIG
7212         radius = 5.0f * (objp->radius + guard_objp->radius) + 50.0f;
7213         if (radius > 300.0f) {
7214                 radius = guard_objp->radius * 1.25f;
7215         }
7216
7217         vm_vec_sub(&aip->guard_vec, &objp->pos, &guard_objp->pos);
7218
7219         if (vm_vec_mag(&aip->guard_vec) > 3.0f*radius) {
7220                 //      Far away, don't just use vector to object, causes clustering of guard ships.
7221                 vector  tvec, rvec;
7222                 float   mag;
7223                 mag = vm_vec_copy_normalize(&tvec, &aip->guard_vec);
7224                 vm_vec_rand_vec_quick(&rvec);                   
7225                 vm_vec_scale_add2(&tvec, &rvec, 0.5f);
7226                 vm_vec_copy_scale(&aip->guard_vec, &tvec, mag);
7227         }
7228
7229         vm_vec_normalize_quick(&aip->guard_vec);
7230         vm_vec_scale(&aip->guard_vec, radius);
7231 }
7232
7233 //      Make object *objp guard object *other_objp.
7234 //      To be called from the goals code.
7235 void ai_set_guard_wing(object *objp, int wingnum)
7236 {
7237         ship            *shipp;
7238         ai_info *aip;
7239         int             leader_objnum, leader_shipnum;
7240
7241         Assert(wingnum >= 0);
7242
7243         Assert(objp->type == OBJ_SHIP);
7244         Assert(objp->instance >= 0);
7245
7246         // shouldn't set the ai mode for the player
7247         if ( objp == Player_obj ) {
7248                 return;
7249         }
7250
7251         shipp = &Ships[objp->instance];
7252
7253         Assert(shipp->ai_index >= 0);
7254
7255         aip = &Ai_info[shipp->ai_index];
7256         force_avoid_player_check(objp, aip);
7257
7258         ai_set_goal_maybe_abort_dock(objp, aip);
7259         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7260
7261         //      This function is called whenever a guarded ship is destroyed, so this code
7262         //      prevents a ship from trying to guard a non-existent wing.
7263         if (Wings[wingnum].current_count < 1) {
7264                 aip->guard_objnum = -1;
7265                 aip->guard_wingnum = -1;
7266                 aip->mode = AIM_NONE;
7267         } else {
7268                 leader_shipnum = Wings[wingnum].ship_index[0];
7269                 leader_objnum = Ships[leader_shipnum].objnum;
7270
7271                 Assert((leader_objnum >= 0) && (leader_objnum < MAX_OBJECTS));
7272                 //Assert(leader_objnum != objp-Objects);        //      Don't allow ships to guard themselves.
7273                 if (leader_objnum == OBJ_INDEX(objp)) {
7274                         //Int3();       //      Seems illegal, but let's clean up.  Get MikeK.
7275                         return;
7276                 }
7277
7278                 aip->guard_wingnum = wingnum;
7279                 aip->guard_objnum = leader_objnum;
7280                 aip->guard_signature = Objects[leader_objnum].signature;
7281                 aip->mode = AIM_GUARD;
7282                 aip->submode = AIS_GUARD_STATIC;
7283
7284                 ai_set_guard_vec(objp, &Objects[leader_objnum]);
7285         }
7286 }
7287
7288 //      Make object *objp guard object *other_objp.
7289 //      To be called from the goals code.
7290 void ai_set_evade_object(object *objp, object *other_objp)
7291 {
7292         ship            *shipp;
7293         ai_info *aip;
7294         int             other_objnum;
7295
7296         Assert(objp->type == OBJ_SHIP);
7297         Assert(objp->instance >= 0);
7298
7299         shipp = &Ships[objp->instance];
7300
7301         Assert(shipp->ai_index >= 0);
7302
7303         aip = &Ai_info[shipp->ai_index];
7304
7305         other_objnum = OBJ_INDEX(other_objp);
7306         Assert(other_objnum >= 0);
7307
7308         Assert(other_objnum != Ships[aip->shipnum].objnum);     //      make sure not targeting self
7309         aip->target_objnum = other_objnum;
7310
7311         aip->mode = AIM_EVADE;
7312 }
7313
7314 //      Make objp guard other_objp
7315 //      If other_objp is a member of a wing, objp will guard that whole wing
7316 //      UNLESS objp is also a member of the wing!
7317 void ai_set_guard_object(object *objp, object *other_objp)
7318 {
7319         ship            *shipp;
7320         ai_info *aip;
7321         int             other_objnum;
7322
7323         Assert(objp->type == OBJ_SHIP);
7324         Assert(objp->instance >= 0);
7325         Assert(objp != other_objp);
7326
7327         shipp = &Ships[objp->instance];
7328
7329         Assert(shipp->ai_index >= 0);
7330
7331         aip = &Ai_info[shipp->ai_index];
7332         aip->avoid_check_timestamp = timestamp(1);
7333
7334         //      If ship to guard is in a wing, guard that whole wing.
7335         ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
7336         if ((other_aip->wing != -1) && (other_aip->wing != aip->wing)) {
7337                 ai_set_guard_wing(objp, Ai_info[Ships[other_objp->instance].ai_index].wing);
7338         } else {
7339
7340                 other_objnum = other_objp-Objects;
7341
7342                 aip->guard_objnum = other_objnum;
7343                 aip->guard_signature = other_objp->signature;
7344                 aip->guard_wingnum = -1;
7345
7346                 aip->mode = AIM_GUARD;
7347                 aip->submode = AIS_GUARD_STATIC;
7348
7349                 Assert(other_objnum >= 0);      //      Hmm, bogus object and we need its position for guard_vec.
7350
7351                 // vm_vec_sub(&aip->guard_vec, &objp->pos, &Objects[other_objnum].pos);
7352                 ai_set_guard_vec(objp, &Objects[other_objnum]);
7353
7354                 ai_set_goal_maybe_abort_dock(objp, aip);
7355                 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7356         }
7357 }
7358
7359 //      Update the aspect_locked_time field based on whether enemy is in view cone.
7360 //      Also set/clear AIF_SEEK_LOCK.
7361 void update_aspect_lock_information(ai_info *aip, vector *vec_to_enemy, float dist_to_enemy, float enemy_radius)
7362 {
7363         float   dot_to_enemy;
7364         int     num_weapon_types;
7365         int     weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
7366         ship    *shipp;
7367         ship_weapon     *swp;
7368         weapon_info     *wip;
7369
7370         shipp = &Ships[aip->shipnum];
7371         swp = &shipp->weapons;
7372
7373         // AL 3-7-98: This probably should never happen, but check to ensure that current_secondary_bank is valid
7374         if ( (swp->current_secondary_bank < 0) || (swp->current_secondary_bank > swp->num_secondary_banks) ) {
7375                 return;
7376         }
7377
7378         num_weapon_types = get_available_secondary_weapons(Pl_objp, weapon_id_list, weapon_bank_list);
7379
7380         wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
7381
7382         if (num_weapon_types && (wip->wi_flags & WIF_HOMING_ASPECT)) {
7383                 if (dist_to_enemy > 300.0f - min(enemy_radius, 100.0f))
7384                         aip->ai_flags |= AIF_SEEK_LOCK;
7385                 else
7386                         aip->ai_flags &= ~AIF_SEEK_LOCK;
7387
7388                 //      Update locking information for aspect seeking missiles.
7389                 aip->current_target_is_locked = 0;
7390                 dot_to_enemy = vm_vec_dot(vec_to_enemy, &Pl_objp->orient.fvec);
7391
7392                 float   needed_dot = 0.9f - 0.5f * enemy_radius/(dist_to_enemy + enemy_radius); //      Replaced MIN_TRACKABLE_DOT with 0.9f
7393                 if (dot_to_enemy > needed_dot) {
7394                         aip->aspect_locked_time += flFrametime;
7395                         // nprintf(("AI", "+ Lock time = %7.3f\n", aip->aspect_locked_time));
7396                         if (aip->aspect_locked_time >= wip->min_lock_time) {
7397                                 aip->aspect_locked_time = wip->min_lock_time;
7398                                 aip->current_target_is_locked = 1;
7399                         }
7400                 } else {
7401                         aip->aspect_locked_time -= flFrametime*2;
7402                         // nprintf(("AI", "- Lock time = %7.3f\n", aip->aspect_locked_time));
7403                         if (aip->aspect_locked_time < 0.0f)
7404                                 aip->aspect_locked_time = 0.0f;
7405                 }
7406                 //nprintf(("AI", "dot = %7.3f, time = %7.3f\n", dot_to_enemy, aip->aspect_locked_time));
7407         
7408         } else {
7409                 aip->current_target_is_locked = 0;
7410                 aip->aspect_locked_time = 0.0f; // Used to be this, why?: wip->min_lock_time;
7411                 aip->ai_flags &= ~AIF_SEEK_LOCK;
7412         }
7413
7414 }
7415
7416 //      We're in chase mode and we've recently collided with our target.
7417 //      Fly away from it!
7418 void ai_chase_fly_away(object *objp, ai_info *aip)
7419 {
7420         int     abort_flag = 0;
7421
7422         if (aip->ai_flags & AIF_TARGET_COLLISION) {
7423                 aip->ai_flags &= ~AIF_TARGET_COLLISION; //      Don't process this hit again next frame.
7424                 aip->submode = SM_FLY_AWAY;                                     //      Focus on avoiding target
7425                 aip->submode_start_time = Missiontime;
7426         }
7427
7428         if ((aip->target_objnum == -1) || (Objects[aip->target_objnum].signature != aip->target_signature)) {
7429                 abort_flag = 1;
7430         }
7431
7432         if (abort_flag || (Missiontime > aip->submode_start_time + F1_0)) {
7433                 aip->last_attack_time = Missiontime;
7434                 aip->submode = SM_ATTACK;
7435                 aip->submode_start_time = Missiontime;
7436         } else {
7437                 vector  v2e;
7438                 float           dot;
7439
7440                 vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, &objp->pos);
7441
7442                 dot = vm_vec_dot(&objp->orient.fvec, &v2e);
7443                 if (dot < 0.0f)
7444                         accelerate_ship(aip, 1.0f);
7445                 else
7446                         accelerate_ship(aip, 1.0f - dot);
7447                 turn_away_from_point(objp, &Objects[aip->target_objnum].pos, 0.0f);
7448         }
7449 }
7450
7451 //      Return bank index of favored secondary weapon.
7452 //      Return -1 if nothing favored.
7453 //      "favored" means SEXPs have specified the weapon as being good to fire at en_objp.
7454 int has_preferred_secondary(object *objp, object *en_objp, ship_weapon *swp)
7455 {
7456 // int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
7457         int     i;
7458
7459         for (i=0; i<swp->num_secondary_banks; i++) {
7460                 if (swp->secondary_bank_capacity[i] > 0) {
7461                         if (swp->secondary_bank_ammo[i] > 0) {
7462                                 if (is_preferred_weapon(swp->secondary_bank_weapons[i], objp, en_objp) != -1){
7463                                         return i;
7464                                 }
7465                         }
7466                 }
7467         }
7468
7469         return -1;
7470 }
7471
7472 //      Choose which secondary weapon to fire.
7473 //      Note, this is not like ai_select_secondary_weapon().  "choose" means make a choice.
7474 //      "select" means execute an order.  Get it?
7475 //      This function calls ai_select_secondary_weapon() with the characteristics it should search for.
7476 void ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp)
7477 {
7478         float                   subsystem_strength = 0.0f;
7479         int                     is_big_ship, priority1, priority2;
7480         ship_weapon     *swp;
7481         ship_info       *esip;
7482
7483         if ( en_objp->type == OBJ_SHIP ) {
7484                 esip = &Ship_info[Ships[en_objp->instance].ship_info_index];
7485         } else {
7486                 esip = NULL;
7487         }
7488
7489         swp = &Ships[objp->instance].weapons;
7490
7491         // AL 3-5-98: do a quick out if the ship has no secondaries
7492         if ( swp->num_secondary_banks <= 0 ) {
7493                 swp->current_secondary_bank = -1;
7494                 return;
7495         }
7496
7497         int preferred_secondary = has_preferred_secondary(objp, en_objp, swp);
7498
7499         if (preferred_secondary != -1) {
7500                 if (swp->current_secondary_bank != preferred_secondary) {
7501                         aip->current_target_is_locked = 0;
7502                         aip->aspect_locked_time = 0.0f;
7503                         swp->current_secondary_bank = preferred_secondary;
7504                 }
7505                 //nprintf(("AI", "Favored secondary = %s\n", Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7506                 aip->ai_flags |= AIF_UNLOAD_SECONDARIES;
7507         } else {
7508                 aip->ai_flags &= ~AIF_UNLOAD_SECONDARIES;
7509                 if (aip->targeted_subsys) {
7510                         subsystem_strength = aip->targeted_subsys->current_hits;
7511                 }
7512
7513                 if ( esip ) {
7514                         is_big_ship = esip->flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP);
7515                 } else {
7516                         is_big_ship=0;
7517                 }
7518
7519                 if (is_big_ship) {
7520                         priority1 = WIF_HUGE;
7521                         priority2 = WIF_HOMING;
7522                 } else if ( (esip != NULL) && (esip->flags & SIF_BOMBER) ) {
7523                         priority1 = WIF_BOMBER_PLUS;
7524                         priority2 = WIF_HOMING;
7525                 } else if (subsystem_strength > 100.0f) {
7526                         priority1 = WIF_PUNCTURE;
7527                         priority2 = WIF_HOMING;
7528                 } else {
7529                         priority1 = WIF_HOMING;
7530                         priority2 = 0;
7531                 }
7532                 
7533                 ai_select_secondary_weapon(objp, swp, priority1, priority2);
7534         }
7535
7536         // nprintf(("AI", "Frame %i: Chose secondary %s\n", Framecount, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7537 }
7538
7539 //      Return time, in seconds, at which this ship can next fire its current secondary weapon.
7540 float set_secondary_fire_delay(ai_info *aip, ship *shipp, weapon_info *swip)
7541 {
7542         float t = swip->fire_wait;              //      Base delay for this weapon.
7543         if (shipp->team == Player_ship->team) {
7544                 //      On player's team, _lower_ skill level = faster firing
7545                 t = t * (Game_skill_level+2) / (NUM_SKILL_LEVELS);
7546         } else {                //      Not on player's team, higher skill level = faster firing
7547                 t = t * (NUM_SKILL_LEVELS - Game_skill_level+2) / (NUM_SKILL_LEVELS);
7548         }
7549
7550         t += (Num_ai_classes - aip->ai_class + 1) * 0.5f;
7551         t *= frand_range(0.8f, 1.2f);
7552
7553         //      For the missiles that fire fairly quickly, occasionally add an additional substantial delay.
7554         if (t < 5.0f)
7555                 if (frand() < 0.5f)
7556                         t = t * 2.0f + 2.0f;
7557
7558         return t;
7559 }
7560
7561
7562 void ai_chase_big_approach_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7563 {
7564         float dist_to_goal;
7565
7566         // head straight toward him and maybe circle later
7567         vm_vec_avg(goal_pos, &attack_objp->pos, &target_objp->pos);
7568
7569         // get distance to goal
7570         dist_to_goal = vm_vec_dist(goal_pos, &attack_objp->pos);
7571         
7572         // set accel
7573         if (dist_to_goal > 400.0f) {
7574                 *accel = 1.0f;
7575         } else {
7576                 *accel = dist_to_goal/400.0f;
7577         }
7578 }
7579
7580 void ai_chase_big_circle_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7581 {
7582         get_tangent_point(goal_pos, attack_objp, &target_objp->pos, attack_objp->radius + target_objp->radius + 100.0f);
7583
7584         *accel = 1.0f;
7585 }
7586
7587 // get the current and desired horizontal separations between target
7588 void ai_chase_big_get_separations(object *attack_objp, object *target_objp, vector *horz_vec_to_target, float *desired_separation, float *cur_separation)
7589 {
7590         float temp, r_target, r_attacker, h_attacker, h_target;
7591         float perp_dist;
7592         vector vec_to_target;
7593         polymodel *pm;
7594
7595         // get parameters of ships (as cylinders - radius and height)
7596         // get radius of attacker (for rotations about forward)
7597         pm = model_get(Ships[attack_objp->instance].modelnum);
7598         temp = max(pm->maxs.x, pm->maxs.y);
7599         r_attacker = max(-pm->mins.x, -pm->mins.y);
7600         r_attacker = max(temp, r_attacker);
7601         h_attacker = max(-pm->mins.z, pm->maxs.z);
7602
7603         // get radius of target (for rotations about forward)
7604         pm = model_get(Ships[attack_objp->instance].modelnum);
7605         temp = max(pm->maxs.x, pm->maxs.y);
7606         r_target = max(-pm->mins.x, -pm->mins.y);
7607         r_target = max(temp, r_target);
7608         h_target = max(-pm->mins.z, pm->maxs.z);
7609
7610         // find separation between cylinders [if parallel]
7611         vm_vec_sub(&vec_to_target, &attack_objp->pos, &target_objp->pos);
7612
7613         // find the distance between centers along forward direction of ships
7614         perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.fvec);
7615
7616         // subtract off perp component to get "horizontal" separation vector between cylinders [ASSUMING parallel]
7617         vm_vec_scale_add(horz_vec_to_target, &vec_to_target, &target_objp->orient.fvec, -perp_dist);
7618         *cur_separation = vm_vec_mag_quick(horz_vec_to_target);
7619
7620         // choose "optimal" separation of 1000 + r_target + r_attacker
7621         *desired_separation = 1000 + r_target + r_attacker;
7622 }
7623
7624 void ai_chase_big_parallel_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7625 {
7626         int opposing;
7627         float temp, r_target, r_attacker, h_attacker, h_target;
7628         float separation, optimal_separation;
7629         vector  horz_vec_to_target;
7630         polymodel *pm;
7631
7632         // get parameters of ships (as cylinders - radius and height)
7633         // get radius of attacker (for rotations about forward)
7634         pm = model_get(Ships[attack_objp->instance].modelnum);
7635         temp = max(pm->maxs.x, pm->maxs.y);
7636         r_attacker = max(-pm->mins.x, -pm->mins.y);
7637         r_attacker = max(temp, r_attacker);
7638         h_attacker = max(-pm->mins.z, pm->maxs.z);
7639
7640         // get radius of target (for rotations about forward)
7641         pm = model_get(Ships[attack_objp->instance].modelnum);
7642         temp = max(pm->maxs.x, pm->maxs.y);
7643         r_target = max(-pm->mins.x, -pm->mins.y);
7644         r_target = max(temp, r_target);
7645         h_target = max(-pm->mins.z, pm->maxs.z);
7646
7647         // are we opposing (only when other ship is not moving)
7648         opposing = ( vm_vec_dotprod(&attack_objp->orient.fvec, &target_objp->orient.fvec) < 0 );
7649
7650         ai_chase_big_get_separations(attack_objp, target_objp, &horz_vec_to_target, &optimal_separation, &separation);
7651
7652         // choose dist (2000) so that we don't bash
7653         float dist = 2000;
7654         if (opposing) {
7655                 dist = - dist;
7656         }
7657
7658         // set the goal pos as dist forward from target along target forward
7659         vm_vec_scale_add(goal_pos, &target_objp->pos, &target_objp->orient.fvec, dist);
7660         // then add horizontal separation
7661         vm_vec_scale_add2(goal_pos, &horz_vec_to_target, optimal_separation/separation);
7662
7663         // find the distance between centers along forward direction of ships
7664         vector vec_to_target;
7665         vm_vec_sub(&vec_to_target, &target_objp->pos, &attack_objp->pos);
7666         float perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.fvec);
7667
7668         float match_accel = target_objp->phys_info.vel.z / Ship_info[Ships[attack_objp->instance].ship_info_index].max_vel.z;
7669         float length_scale = attack_objp->radius;
7670
7671         // if we're heading toward enemy ship, we want to keep going if we're ahead
7672         if (opposing) {
7673                 perp_dist = -perp_dist;
7674         }
7675
7676         if (perp_dist > 0) {
7677                 // falling behind, so speed up
7678                 *accel = match_accel + (1.0f - match_accel) / length_scale * (perp_dist);
7679         } else {
7680                 // up in front, so slow down
7681                 *accel = match_accel  - match_accel / length_scale * -perp_dist;
7682                 *accel = max(0.0f, *accel);
7683         }
7684
7685 }
7686
7687
7688 //      Return *goal_pos for one cruiser to attack another (big ship).
7689 //      Choose point fairly nearby that is not occupied by another cruiser.
7690 void ai_cruiser_chase_set_goal_pos(vector *goal_pos, object *pl_objp, object *en_objp)
7691 {
7692         ai_info *aip;
7693
7694         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
7695         float accel;
7696
7697         switch (aip->submode) {
7698         case SM_BIG_APPROACH:
7699                 // do approach stuff;
7700                 ai_chase_big_approach_set_goal(goal_pos, pl_objp, en_objp, &accel);
7701                 break;
7702
7703         case SM_BIG_CIRCLE:
7704                 // do circle stuff
7705                 ai_chase_big_circle_set_goal(goal_pos, pl_objp, en_objp, &accel);
7706                 break;
7707
7708         case SM_BIG_PARALLEL:
7709                 // do parallel stuff
7710                 ai_chase_big_parallel_set_goal(goal_pos, pl_objp, en_objp, &accel);
7711                 break;
7712         }
7713 }
7714
7715 int maybe_hack_cruiser_chase_abort()
7716 {
7717         ship                    *shipp = &Ships[Pl_objp->instance];     
7718         ship                    *eshipp = &Ships[En_objp->instance];
7719         ai_info         *aip = &Ai_info[shipp->ai_index];
7720
7721         // mission sm3-08, sathanos chasing collosus
7722         if ( stricmp(Mission_filename, "sm3-08.fs2") == 0 ) {
7723                 if (( stricmp(eshipp->ship_name, "colossus") == 0 ) || ( stricmp(shipp->ship_name, "colossus") == 0 )) {
7724                         // Changed so all big ships attacking the Colossus will not do the chase code.
7725                         // Did this so Beast wouldn't swerve away from Colossus. -- MK, 9/14/99
7726                         //if ( stricmp(shipp->ship_name, "Sathanas") == 0 ) {
7727                                 // do cool hack stuff here
7728                                 ai_clear_ship_goals( aip );
7729                                 aip->mode = AIM_NONE;
7730                                 return 1;
7731                         //}
7732                 }
7733         }
7734
7735         return 0;
7736 }
7737
7738 //      Make a big ship pursue another big ship.
7739 //      (Note, called "ai_cruiser_chase" because we already have ai_chase_big() which means fighter chases big ship.
7740 void ai_cruiser_chase()
7741 {
7742         ship_info       *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7743         ship                    *shipp = &Ships[Pl_objp->instance];     
7744         ai_info         *aip = &Ai_info[shipp->ai_index];
7745
7746         if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7747                 Int3(); //      Hmm, not a very big ship, how did we get in this function?
7748                 aip->mode = AIM_NONE;
7749                 return;
7750         }
7751
7752         if (En_objp->type != OBJ_SHIP) {
7753                 Int3();
7754                 return;
7755         }
7756
7757         if (En_objp->instance < 0) {
7758                 Int3();
7759                 return;
7760         }
7761
7762         ship                    *eshipp;
7763         ship_info       *esip;
7764
7765         eshipp = &Ships[En_objp->instance];
7766         esip = &Ship_info[eshipp->ship_info_index];
7767
7768         if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7769                 // Int3();      //      Hmm, we're big and we're pursuing something other than a big ship?
7770                 aip->mode = AIM_NONE;
7771                 return;
7772         }
7773
7774         vector  goal_pos;
7775         float turn_time = Ship_info[Ships[Pl_objp->instance].ship_info_index].srotation_time;
7776
7777         // kamikaze - ram and explode
7778         if (aip->ai_flags & AIF_KAMIKAZE) {
7779                 ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
7780                 accelerate_ship(aip, 1.0f);
7781         } 
7782         
7783         // really track down and chase
7784         else {
7785                 // check valid submode
7786                 Assert( (aip->submode == SM_ATTACK) || (aip->submode == SM_BIG_APPROACH) || (aip->submode == SM_BIG_CIRCLE) || (aip->submode == SM_BIG_PARALLEL) );
7787
7788                 // just entering, approach enemy ship
7789                 if (aip->submode == SM_ATTACK) {
7790                         aip->submode = SM_BIG_APPROACH;
7791                 }
7792
7793                 // desired accel
7794                 float accel = 0.0f;
7795                 vector *rvecp = NULL;
7796
7797                 switch (aip->submode) {
7798                 case SM_BIG_APPROACH:
7799                         // do approach stuff;
7800                         ai_chase_big_approach_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7801                         // maybe set rvec
7802                         break;
7803
7804                 case SM_BIG_CIRCLE:
7805                         // do circle stuff
7806                         ai_chase_big_circle_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7807                         // maybe set rvec
7808                         break;
7809
7810                 case SM_BIG_PARALLEL:
7811                         // do parallel stuff
7812                         ai_chase_big_parallel_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7813                         //maybe set rvec
7814                         break;
7815                 }
7816
7817
7818                 // now move as desired
7819                 ai_turn_towards_vector(&goal_pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0, rvecp);
7820                 accelerate_ship(aip, accel);
7821
7822
7823                 // maybe switch to new mode
7824                 vector vec_to_enemy;
7825                 float dist_to_enemy;
7826                 int moving = (En_objp->phys_info.vel.z > 0.5f);
7827                 vm_vec_sub(&vec_to_enemy, &En_objp->pos, &Pl_objp->pos);
7828                 dist_to_enemy = vm_vec_mag_quick(&vec_to_enemy);
7829
7830                 switch (aip->submode) {
7831                 case SM_BIG_APPROACH:
7832                         if ( dist_to_enemy < (Pl_objp->radius + En_objp->radius)*1.25f + 200.0f ) {
7833                                 // moving
7834                                 if (moving) {
7835                                         // if within 90 degrees of en forward, go into parallel, otherwise circle
7836                                         if ( vm_vec_dotprod(&En_objp->orient.fvec, &Pl_objp->orient.fvec) > 0 ) {
7837                                                 aip->submode = SM_BIG_PARALLEL;
7838                                         }
7839                                 }
7840
7841                                 // otherwise cirle
7842                                 if ( !maybe_hack_cruiser_chase_abort() ) {
7843                                         aip->submode = SM_BIG_CIRCLE;
7844                                 }
7845                         }
7846                         break;
7847
7848                 case SM_BIG_CIRCLE:
7849                         // moving
7850                         if (moving) {
7851                                 vector temp;
7852                                 float desired_sep, cur_sep;
7853                                 // we're behind the enemy ship
7854                                 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.fvec) > 0) {
7855                                         // and we're turning toward the enemy
7856                                         if (vm_vec_dotprod(&En_objp->orient.fvec, &Pl_objp->orient.fvec) > 0) {
7857                                                 // get separation
7858                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7859                                                 // and the separation is > 0.9 desired
7860                                                 if (cur_sep > 0.9 * desired_sep) {
7861                                                         aip->submode = SM_BIG_PARALLEL;
7862                                                 }
7863                                         }
7864                                 }
7865                         } else {
7866                                 // still
7867                                 vector temp;
7868                                 float desired_sep, cur_sep;
7869                                 // we're behind the enemy ship
7870                                 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.fvec) > 0) {
7871                                         // and we're turning toward the enemy
7872                                         if (vm_vec_dotprod(&En_objp->orient.fvec, &Pl_objp->orient.fvec) > 0) {
7873                                                 // get separation
7874                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7875                                                 //and the separation is [0.9 to 1.1] desired
7876                                                 if ( (cur_sep > 0.9f * desired_sep) ) {
7877                                                         aip->submode = SM_BIG_PARALLEL;
7878                                                 }
7879                                         }
7880                                 }
7881                                 // in front of ship
7882                                 else {
7883                                         // and we're turning toward the enemy
7884                                         if (vm_vec_dotprod(&En_objp->orient.fvec, &Pl_objp->orient.fvec) < 0) {
7885                                                 // get separation
7886                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7887                                                 //and the separation is [0.9 to 1.1] desired
7888                                                 if ( (cur_sep > 0.9f * desired_sep) ) {
7889                                                         aip->submode = SM_BIG_PARALLEL;
7890                                                 }
7891                                         }
7892                                 }
7893                         }
7894                         break;
7895
7896                 case SM_BIG_PARALLEL:
7897                         // we're opposing
7898                         if ( vm_vec_dotprod(&Pl_objp->orient.fvec, &En_objp->orient.fvec) < 0 ) {
7899                                 // and the other ship is moving
7900                                 if (moving) {
7901                                         // and we no longer overlap
7902                                         if ( dist_to_enemy > (0.75 * (En_objp->radius + Pl_objp->radius)) ) {
7903                                                 aip->submode = SM_BIG_APPROACH;
7904                                         }
7905                                 }
7906                         }
7907                         break;
7908                 }
7909         }
7910 }
7911
7912 // --------------------------------------------------------------------------
7913 // Make object Pl_objp chase object En_objp
7914 void ai_chase()
7915 {
7916         float                   dist_to_enemy, time_to_enemy;
7917         float                   dot_to_enemy, dot_from_enemy, real_dot_to_enemy;
7918         vector          player_pos, enemy_pos, predicted_enemy_pos, real_vec_to_enemy, predicted_vec_to_enemy;
7919         ship_info       *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7920         ship                    *shipp = &Ships[Pl_objp->instance];
7921         ship_weapon     *swp = &shipp->weapons;
7922         ai_info         *aip = &Ai_info[shipp->ai_index];
7923         int                     enemy_sip_flags;
7924
7925         if (aip->mode != AIM_CHASE) {
7926                 Int3();
7927         }
7928
7929         if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
7930                 ai_cruiser_chase();
7931                 return;
7932         }
7933
7934         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER | SIF_ESCAPEPOD))) {
7935                 Warning(LOCATION, "Ship %s is not 'small', but is in chase mode.\nSwitching to AI=none.\n", shipp->ship_name);
7936                 aip->mode = AIM_NONE;
7937                 return;
7938         }
7939
7940         //nprintf(("AI", "%7s ", Submode_text[aip->submode]));
7941
7942         if ( En_objp->type == OBJ_SHIP ) {
7943                 enemy_sip_flags = Ship_info[Ships[En_objp->instance].ship_info_index].flags;
7944         } else {
7945                 enemy_sip_flags = 0;
7946         }
7947
7948         if ( enemy_sip_flags > 0 ) {
7949                 if (enemy_sip_flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
7950                         ai_big_chase();
7951                         return;
7952                 }
7953         }
7954
7955         //      If collided with target_objnum last frame, avoid that ship.
7956         //      This should prevent the embarrassing behavior of ships getting stuck on each other
7957         //      as if they were magnetically attracted. -- MK, 11/13/97.
7958         if ((aip->ai_flags & AIF_TARGET_COLLISION) || (aip->submode == SM_FLY_AWAY)) {
7959                 ai_chase_fly_away(Pl_objp, aip);
7960                 return;
7961         }
7962
7963         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
7964         dist_to_enemy = vm_vec_dist_quick(&player_pos, &enemy_pos);
7965         time_to_enemy = compute_time_to_enemy(dist_to_enemy, Pl_objp, En_objp);
7966         vm_vec_sub(&real_vec_to_enemy, &enemy_pos, &player_pos);
7967
7968         vm_vec_normalize(&real_vec_to_enemy);
7969
7970         real_dot_to_enemy = vm_vec_dot(&real_vec_to_enemy, &Pl_objp->orient.fvec);
7971
7972         int is_stealthy_ship = 0;
7973         if ( (enemy_sip_flags > 0) && (enemy_sip_flags & SIF_STEALTH) ) {
7974                 if ( ai_is_stealth_visible(Pl_objp, En_objp) != STEALTH_FULLY_TARGETABLE ) {
7975                         is_stealthy_ship = 1;
7976                 }
7977         }
7978
7979         // Can only acquire lock on a target that isn't hidden from sensors
7980         if ( !(Ships[En_objp->instance].flags & SF_HIDDEN_FROM_SENSORS) && !is_stealthy_ship ) {
7981                 update_aspect_lock_information(aip, &real_vec_to_enemy, dist_to_enemy, En_objp->radius);
7982         } else {
7983                 aip->current_target_is_locked = 0;
7984                 aip->ai_flags &= ~AIF_SEEK_LOCK;
7985         }
7986
7987         //      If seeking lock, try to point directly at ship, else predict position so lasers can hit it.
7988         //      If just acquired target, or target is not in reasonable cone, don't refine believed enemy position.
7989         if ((real_dot_to_enemy < 0.25f) || (aip->target_time < 1.0f) || (aip->ai_flags & AIF_SEEK_LOCK)) {
7990                 predicted_enemy_pos = enemy_pos;
7991         } else {
7992                 //      Set predicted_enemy_pos.
7993                 //      See if attacking a subsystem.
7994                 if (aip->targeted_subsys != NULL) {
7995                         Assert(En_objp->type == OBJ_SHIP);
7996                         ship_info       *esip = &Ship_info[Ships[En_objp->instance].ship_info_index];
7997                         if (get_shield_strength(En_objp)/esip->shields < HULL_DAMAGE_THRESHOLD_PERCENT) {
7998                                 //int   rval;
7999
8000                                 if (aip->targeted_subsys != NULL) {
8001                                         get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
8002                                         predicted_enemy_pos = enemy_pos;
8003                                         predicted_vec_to_enemy = real_vec_to_enemy;
8004                                 } else {
8005                                         set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8006                                         set_target_objnum(aip, -1);
8007                                 }
8008                                 // 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));
8009
8010                         } else {
8011                                 set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8012                                 // nprintf(("AI", "Attacking subsystem: pos = %7.3f %7.3f %7.3f\n", predicted_enemy_pos.x, predicted_enemy_pos.y, predicted_enemy_pos.z));
8013                         }
8014                 } else {
8015                         set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8016                 }
8017         }
8018
8019         vm_vec_sub(&predicted_vec_to_enemy, &predicted_enemy_pos, &player_pos);
8020
8021         vm_vec_normalize(&predicted_vec_to_enemy);
8022
8023         dot_to_enemy = vm_vec_dot(&Pl_objp->orient.fvec, &predicted_vec_to_enemy);
8024         dot_from_enemy= - vm_vec_dot(&En_objp->orient.fvec, &real_vec_to_enemy);
8025
8026         //
8027         //      Set turn and acceleration based on submode.
8028         //
8029         switch (aip->submode) {
8030         case SM_CONTINUOUS_TURN:
8031                 ai_chase_ct();
8032                 break;
8033
8034         case SM_STEALTH_FIND:
8035                 ai_stealth_find();
8036                 break;
8037
8038         case SM_STEALTH_SWEEP:
8039                 ai_stealth_sweep();
8040                 break;
8041
8042         case SM_ATTACK:
8043         case SM_SUPER_ATTACK:
8044         case SM_ATTACK_FOREVER:
8045                 if (vm_vec_dist_quick(&Pl_objp->pos, &predicted_enemy_pos) > 100.0f + En_objp->radius * 2.0f) {
8046                         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &predicted_enemy_pos, 10.0f))
8047                                 return;
8048                 }
8049
8050                 ai_chase_attack(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8051                 break;
8052
8053         case SM_EVADE_SQUIGGLE:
8054                 ai_chase_es(aip, sip);
8055                 break;
8056
8057         case SM_EVADE_BRAKE:
8058                 ai_chase_eb(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8059                 break;
8060
8061         case SM_EVADE:
8062                 evade_ship();
8063                 break;
8064
8065         case SM_AVOID:
8066                 avoid_ship();
8067                 break;
8068
8069         case SM_GET_BEHIND:
8070                 get_behind_ship(aip, sip, dist_to_enemy);
8071                 break;
8072
8073         case SM_GET_AWAY:               //      Used to get away from opponent to prevent endless circling.
8074                 ai_chase_ga(aip, sip);
8075                 break;
8076
8077         case SM_EVADE_WEAPON:
8078                 evade_weapon();
8079                 break;
8080
8081         default:
8082                 // Int3();
8083                 aip->last_attack_time = Missiontime;
8084                 aip->submode = SM_ATTACK;
8085                 aip->submode_start_time = Missiontime;
8086         }
8087
8088         //
8089         //      Maybe choose a new submode.
8090         //
8091         if ( (aip->submode != SM_AVOID) && (aip->submode != SM_ATTACK_FOREVER) ) {
8092                 //      If a very long time since attacked, attack no matter what!
8093                 if ( (aip->submode != SM_SUPER_ATTACK) && (aip->submode != SM_GET_AWAY) && !(aip->ai_flags & AIF_STEALTH_PURSIUT) ) {
8094                         if (Missiontime - aip->last_attack_time > i2f(6)) {
8095                                 aip->submode = SM_SUPER_ATTACK;
8096                                 aip->submode_start_time = Missiontime;
8097                                 aip->last_attack_time = Missiontime;
8098                         }
8099                 }
8100
8101                 //      If a collision is expected, pull out!
8102                 //      If enemy is pointing away and moving a bit, don't worry about collision detection.
8103                 if ((dot_from_enemy > 0.5f) || (En_objp->phys_info.speed < 10.0f)) {
8104                         if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 4.0f)) {
8105                                 if ((Missiontime - aip->last_hit_time > F1_0*4) && (dist_to_enemy < Pl_objp->radius*2 + En_objp->radius*2)) {
8106                                         accelerate_ship(aip, -1.0f);
8107                                 } else {
8108                                         aip->submode = SM_AVOID;
8109                                         aip->submode_start_time = Missiontime;
8110                                 }
8111                         }
8112                 }
8113         }
8114
8115         switch (aip->submode) {
8116         case SM_CONTINUOUS_TURN:
8117                 if (Missiontime - aip->submode_start_time > i2f(3)) {
8118                         aip->last_attack_time = Missiontime;
8119                         aip->submode = SM_ATTACK;
8120                         aip->submode_start_time = Missiontime;
8121                 }
8122                 break;
8123
8124         case SM_ATTACK:
8125                 // if taraget is stealth and stealth not visible, then enter stealth find mode
8126                 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8127                         aip->submode = SM_STEALTH_FIND;
8128                         aip->submode_start_time = Missiontime;
8129                         aip->submode_parm0 = SM_SF_AHEAD;
8130                 } 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)) {
8131                         aip->submode = SM_SUPER_ATTACK;
8132                         aip->submode_start_time = Missiontime;
8133                         aip->last_attack_time = Missiontime;
8134                 } else if ((Missiontime - aip->last_hit_target_time > i2f(6)) &&
8135                         (dist_to_enemy < 500.0f) && (dot_to_enemy < 0.2f) &&
8136                         (frand() < (float) Game_skill_level/NUM_SKILL_LEVELS)) {
8137                         aip->submode = SM_GET_AWAY;
8138                         aip->submode_start_time = Missiontime;
8139                         aip->last_hit_target_time = Missiontime;
8140                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP)
8141                         && (dot_to_enemy < dot_from_enemy)
8142                         && (En_objp->phys_info.speed > 15.0f) 
8143                         && (dist_to_enemy < 200.0f) 
8144                         && (dist_to_enemy > 50.0f)
8145                         && (dot_to_enemy < 0.1f)
8146                         && (Missiontime - aip->submode_start_time > i2f(2))) {
8147                         aip->submode = SM_EVADE_BRAKE;
8148                         aip->submode_start_time = Missiontime;
8149                 } else if ((dot_to_enemy > 0.2f) && (dot_from_enemy > -0.2f) && (dot_from_enemy < 0.1f)) {
8150                         aip->submode = SM_GET_BEHIND;
8151                         aip->submode_start_time = Missiontime;
8152                 } 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)) {
8153                         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;
8154                                 aip->submode_start_time = Missiontime;
8155                                 aip->last_hit_target_time = Missiontime;
8156                         } else {
8157                                 aip->submode = SM_EVADE_SQUIGGLE;
8158                                 aip->submode_start_time = Missiontime;
8159                         }
8160                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP) && (Missiontime - aip->submode_start_time > F1_0*2)) {
8161                         if ((dot_to_enemy < 0.8f) && (dot_from_enemy > dot_to_enemy)) {
8162                                 if (frand() > 0.5f) {
8163                                         aip->submode = SM_CONTINUOUS_TURN;
8164                                         aip->submode_parm0 = myrand() & 0x0f;
8165                                         aip->submode_start_time = Missiontime;
8166                                 } else {
8167                                         aip->submode = SM_EVADE;
8168                                         aip->submode_start_time = Missiontime;
8169                                 }
8170                         } else {
8171                                 aip->submode_start_time = Missiontime;
8172                         }
8173                 }
8174
8175                 aip->last_attack_time = Missiontime;
8176
8177                 break;
8178                 
8179         case SM_EVADE_SQUIGGLE:
8180                 if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > 300.0f)) {
8181                         if ((dist_to_enemy < 100.0f) && (dot_to_enemy < 0.0f) && (dot_from_enemy > 0.5f)) {
8182                                 aip->submode = SM_EVADE_BRAKE;
8183                                 aip->submode_start_time = Missiontime;
8184                         } else {
8185                                 aip->last_attack_time = Missiontime;
8186                                 aip->submode = SM_ATTACK;
8187                                 aip->submode_start_time = Missiontime;
8188                         }
8189                 }
8190                 break;
8191         
8192         case SM_EVADE_BRAKE:
8193                 if ((dist_to_enemy < 15.0f) || (En_objp->phys_info.speed < 10.0f)) {
8194                         aip->submode = SM_AVOID;
8195                         aip->submode_start_time = Missiontime;
8196                 } else if ((dot_to_enemy > 0.9f) || ((dot_from_enemy > 0.9f) && (Missiontime - aip->submode_start_time > i2f(1)))) {
8197                         aip->last_attack_time = Missiontime;
8198                         aip->submode = SM_ATTACK;
8199                         aip->submode_start_time = Missiontime;
8200                 } else if (Missiontime - aip->submode_start_time > i2f(4)) {
8201                         aip->last_attack_time = Missiontime;
8202                         aip->submode = SM_ATTACK;
8203                         aip->submode_start_time = Missiontime;
8204                 }
8205                 break;
8206
8207         case SM_EVADE:
8208                 //      Modified by MK on 5/5/97 to keep trying to regain attack mode.  It's what a human would do.
8209                 if ((dot_to_enemy < 0.2f) && (dot_from_enemy < 0.8f) && (dist_to_enemy < 100.0f) && (En_objp->phys_info.speed > 15.0f)) {
8210                         aip->last_attack_time = Missiontime;
8211                         aip->submode = SM_EVADE_BRAKE;
8212                         aip->submode_start_time = Missiontime;
8213                 } else if (((dot_to_enemy > dot_from_enemy - 0.1f)
8214                         && (Missiontime > aip->submode_start_time + i2f(1)))
8215                         || (dist_to_enemy > 150.0f + 2*(Pl_objp->radius + En_objp->radius))) {
8216                         aip->last_attack_time = Missiontime;
8217                         aip->submode = SM_ATTACK;
8218                         aip->submode_start_time = Missiontime;
8219                 } else if (Missiontime - aip->submode_start_time > i2f(2))
8220                         if (dot_from_enemy > 0.8f) {
8221                                 aip->submode = SM_EVADE_SQUIGGLE;
8222                                 aip->submode_start_time = Missiontime;
8223                         }
8224
8225                 break;
8226
8227         case SM_SUPER_ATTACK:
8228                 // if stealth and invisible, enter stealth find mode
8229                 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8230                         aip->submode = SM_STEALTH_FIND;
8231                         aip->submode_start_time = Missiontime;
8232                         aip->submode_parm0 = SM_SF_AHEAD;
8233                 } 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) )) {
8234                         aip->ai_flags &= ~AIF_ATTACK_SLOWLY;    //      Just in case, clear here.
8235
8236                         switch (myrand() % 5) {
8237                         case 0:
8238                                 aip->submode = SM_CONTINUOUS_TURN;
8239                                 aip->submode_start_time = Missiontime;
8240                                 break;
8241                         case 1:
8242                                 aip->submode_start_time = Missiontime;  //      Stay in super attack mode
8243                                 break;
8244                         case 2:
8245                         case 3:
8246                                 if (frand() < (float) 0.5f * (aip->ai_class + Game_skill_level)/(Num_ai_classes + NUM_SKILL_LEVELS)) {
8247                                         aip->submode = SM_GET_AWAY;
8248                                         aip->submode_start_time = Missiontime;
8249                                 } else {
8250                                         aip->submode = SM_EVADE;
8251                                         aip->submode_start_time = Missiontime;
8252                                 }
8253                                 break;
8254                         case 4:
8255                                 if (dot_from_enemy + (NUM_SKILL_LEVELS - Game_skill_level) * 0.1f > dot_to_enemy) {     //      Less likely to GET_AWAY at lower skill levels.
8256                                         aip->submode = SM_EVADE;
8257                                         aip->submode_start_time = Missiontime;
8258                                 } else {
8259                                         aip->submode = SM_GET_AWAY;
8260                                         aip->submode_start_time = Missiontime;
8261                                 }
8262                                 break;
8263                         default:
8264                                 Int3(); //      Impossible!
8265                         }
8266                 }
8267
8268                 aip->last_attack_time = Missiontime;
8269
8270                 break;
8271
8272         case SM_AVOID:
8273                 if ((dot_to_enemy > -0.2f) && (dist_to_enemy / (dot_to_enemy + 0.3f) < 100.0f)) {
8274                         aip->submode_start_time = Missiontime;
8275                 } else if (Missiontime - aip->submode_start_time > i2f(1)/2)
8276                         if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 3.0f)) {
8277                                 aip->submode_start_time = Missiontime;
8278                         } else {
8279                                 aip->submode = SM_GET_BEHIND;
8280                                 aip->submode_start_time = Missiontime;
8281                         }
8282
8283                 break;
8284
8285         case SM_GET_BEHIND:
8286                 if ((dot_from_enemy < -0.7f) || (Missiontime - aip->submode_start_time > i2f(2))) {
8287                         aip->submode = SM_ATTACK;
8288                         aip->submode_start_time = Missiontime;
8289                         aip->last_attack_time = Missiontime;
8290                 }
8291                 break;
8292
8293         case SM_GET_AWAY:
8294                 if (Missiontime - aip->submode_start_time > i2f(2)) {
8295                         float   rand_dist;
8296
8297                         rand_dist = ((Missiontime >> 17) & 0x03) * 100.0f + 200.0f;     //      Some value in 200..500
8298                         if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > rand_dist) || (dot_from_enemy < 0.4f)) {
8299                                 aip->ai_flags |= AIF_ATTACK_SLOWLY;
8300                                 aip->submode = SM_ATTACK;
8301                                 aip->time_enemy_in_range = 2.0f;                //      Cheat.  Presumably if they were running away from you, they were monitoring you!
8302                                 aip->submode_start_time = Missiontime;
8303                                 aip->last_attack_time = Missiontime;
8304                         }
8305                 }
8306                 break;
8307
8308         case SM_EVADE_WEAPON:
8309                 if (aip->danger_weapon_objnum == -1) {
8310                         aip->submode = SM_ATTACK;
8311                         aip->submode_start_time = Missiontime;
8312                         aip->last_attack_time = Missiontime;
8313                 }
8314                 break;
8315
8316         // Either change to SM_ATTACK or AIM_FIND_STEALTH
8317         case SM_STEALTH_FIND:
8318                 // if time > 5 sec change mode to sweep
8319                 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8320                         aip->submode = SM_ATTACK;
8321                         aip->submode_start_time = Missiontime;
8322                         aip->last_attack_time = Missiontime;
8323                         // sweep if I can't find in 5 sec or bail from find
8324                 } else if ( ((Missiontime - aip->submode_start_time) > i2f(5)) || (aip->submode_parm0 == SM_SF_BAIL) ) {
8325                         // begin sweep mode
8326                         aip->submode = SM_STEALTH_SWEEP;
8327                         aip->submode_start_time = Missiontime;
8328                         aip->last_attack_time = Missiontime;
8329                         aip->submode_parm0 = SM_SS_SET_GOAL;
8330                 }
8331                 break;
8332
8333         case SM_STEALTH_SWEEP:
8334                 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8335                         aip->submode = SM_ATTACK;
8336                         aip->submode_start_time = Missiontime;
8337                         aip->last_attack_time = Missiontime;
8338                 } else if ( (timestamp() - aip->stealth_last_visible_stamp) < 5000 ) {
8339                         // go back to find mode
8340                         aip->submode = SM_STEALTH_FIND;
8341                         aip->submode_start_time = Missiontime;
8342                         aip->submode_parm0 = SM_SF_AHEAD;
8343                 } else if ( /*(Missiontime - aip->submode_start_time) > i2f(30) || */(aip->submode_parm0 == SM_SS_DONE) ) {
8344                         // set target objnum = -1
8345                         set_target_objnum(aip, -1);
8346
8347                         // set submode to attack
8348                         aip->submode = SM_ATTACK;
8349                         aip->submode_start_time = Missiontime;
8350                         aip->last_attack_time = Missiontime;
8351                 }
8352                 break;
8353
8354         case SM_ATTACK_FOREVER: //      Engines blown, just attack.
8355                 break;
8356
8357         default:
8358                 //Int3();
8359                 aip->submode = SM_ATTACK;
8360                 aip->last_attack_time = Missiontime;
8361
8362                 aip->submode_start_time = Missiontime;
8363         }
8364
8365         //
8366         //      Maybe fire primary weapon and update time_enemy_in_range
8367         //
8368         //nprintf(("AI", "time_enemy_in_range = %7.3f, dot = %7.3f\n", aip->time_enemy_in_range, dot_to_enemy));
8369
8370         if (aip->mode != AIM_EVADE) {
8371                 if (dot_to_enemy > 0.95f - 0.5f * En_objp->radius/max(1.0f, En_objp->radius + dist_to_enemy)) {
8372                         aip->time_enemy_in_range += flFrametime;
8373                         
8374                         //      Chance of hitting ship is based on dot product of firing ship's forward vector with vector to ship
8375                         //      and also the size of the target relative to distance to target.
8376                         if (dot_to_enemy > max(0.5f, 0.90f + aip->ai_accuracy/10.0f - En_objp->radius/max(1.0f,dist_to_enemy))) {
8377
8378                                 ship *temp_shipp;
8379                                 ship_weapon *tswp;
8380
8381                                 temp_shipp = &Ships[Pl_objp->instance];
8382                                 tswp = &temp_shipp->weapons;
8383                                 if ( tswp->num_primary_banks > 0 ) {
8384                                         float   scale;
8385                                         Assert(tswp->current_primary_bank < tswp->num_primary_banks);
8386                                         weapon_info     *pwip = &Weapon_info[tswp->primary_bank_weapons[tswp->current_primary_bank]];
8387
8388                                         //      Less likely to fire if far away and moving.
8389                                         scale = pwip->max_speed/(En_objp->phys_info.speed + pwip->max_speed);
8390                                         if (scale > 0.6f)
8391                                                 scale = (scale - 0.6f) * 1.5f;
8392                                         else
8393                                                 scale = 0.0f;
8394                                         if (dist_to_enemy < pwip->max_speed * (1.0f + scale)) {
8395                                                 ai_fire_primary_weapon(Pl_objp);
8396                                         }
8397
8398                                         //      Don't fire secondaries at a protected ship.
8399                                         if (!(En_objp->flags & OF_PROTECTED)) {
8400                                                 ai_choose_secondary_weapon(Pl_objp, aip, En_objp);
8401                                                 int current_bank = tswp->current_secondary_bank;
8402                                                 weapon_info     *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8403
8404                                                 if (current_bank > -1) {
8405                                                         if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8406                                                                 if (timestamp_until(swp->next_secondary_fire_stamp[current_bank]) > swip->fire_wait*1000.0f) {
8407                                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (swip->fire_wait*1000.0f));
8408                                                                 }
8409                                                         }
8410
8411                                                         if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
8412                                                                 if (tswp->current_secondary_bank >= 0) {
8413                                                                         weapon_info     *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8414                                                                         float firing_range;
8415                                                                         
8416                                                                         if (swip->wi_flags & WIF_BOMB)
8417                                                                                 firing_range = swip->max_speed * swip->lifetime * 0.75f;
8418                                                                         else
8419                                                                                 firing_range = swip->max_speed * swip->lifetime * (Game_skill_level + 1 + aip->ai_class/2)/NUM_SKILL_LEVELS;
8420
8421                                                                         // reduce firing range in nebula
8422                                                                         extern int Nebula_sec_range;
8423                                                                         if ((The_mission.flags & MISSION_FLAG_FULLNEB) && Nebula_sec_range) {
8424                                                                                 firing_range *= 0.8f;
8425                                                                         }
8426
8427                                                                         //      If firing a spawn weapon, distance doesn't matter.
8428                                                                         int     spawn_fire = 0;
8429
8430                                                                         if (swip->wi_flags & WIF_SPAWN) {
8431                                                                                 int     count;
8432
8433                                                                                 count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(Pl_objp)), &Pl_objp->pos, 1000.0f);
8434
8435                                                                                 if (count > 3)
8436                                                                                         spawn_fire = 1;
8437                                                                                 else if (count >= 1) {
8438                                                                                         float hull_percent = Pl_objp->hull_strength/sip->initial_hull_strength;
8439
8440                                                                                         if (hull_percent < 0.01f)
8441                                                                                                 hull_percent = 0.01f;
8442
8443                                                                                         if (frand() < 0.25f/(30.0f*hull_percent) * count)       //      With timestamp below, this means could fire in 30 seconds if one enemy.
8444                                                                                                 spawn_fire = 1;
8445                                                                                 }
8446                                                                         }
8447
8448                                                                         if (spawn_fire || (dist_to_enemy < firing_range)) {
8449                                                                                 if (ai_fire_secondary_weapon(Pl_objp)) {
8450                                                                                         //      Only if weapon was fired do we specify time until next fire.  If not fired, done in ai_fire_secondary...
8451                                                                                         float t;
8452                                                                                         
8453                                                                                         if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8454                                                                                                 t = swip->fire_wait;
8455                                                                                         } else {
8456                                                                                                 t = set_secondary_fire_delay(aip, temp_shipp, swip);
8457                                                                                         }
8458                                                                                         //nprintf(("AI", "Next secondary to be fired in %7.3f seconds.\n", t));
8459                                                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
8460                                                                                 }
8461                                                                         } else {
8462                                                                                 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
8463                                                                         }
8464                                                                 }
8465                                                         }
8466                                                 }
8467                                         }
8468                                 }
8469                         }
8470                 } else {
8471                         aip->time_enemy_in_range *= (1.0f - flFrametime);
8472                 }
8473         } else
8474                 aip->time_enemy_in_range *= (1.0f - flFrametime);
8475
8476 }
8477
8478 //      Make the object *objp move so that the point *dp on the object moves towards the point *vp
8479 //      Return distance.
8480 void dock_move_towards_point(object *objp, vector *dp, vector *vp, float speed_scale, float other_obj_speed = 0.0f)
8481 {
8482         physics_info    *pi = &objp->phys_info;
8483         float                           dist;                   //      dist to goal
8484         vector                  v2g;                    //      vector to goal
8485         vector                  abs_pnt;                //      location of dock point, ie objp->pos + db
8486
8487         if (dp == NULL)
8488                 abs_pnt = objp->pos;
8489         else
8490                 vm_vec_add(&abs_pnt, &objp->pos, dp);
8491
8492         dist = vm_vec_dist_quick(vp, &abs_pnt);
8493         if (dist > 0.0f) {
8494                 float   speed;
8495
8496                 dist = vm_vec_normalized_dir(&v2g, vp, &abs_pnt);
8497                 speed = fl_sqrt(dist) * speed_scale;
8498                 if (other_obj_speed < MAX_REPAIR_SPEED*0.75f)
8499                         speed += other_obj_speed;
8500                 else
8501                         speed += MAX_REPAIR_SPEED*0.75f;
8502
8503                 vm_vec_copy_scale(&pi->desired_vel, &v2g, speed);
8504         } else
8505                 vm_vec_zero(&pi->desired_vel);
8506 }
8507
8508 //      Set the orientation in the global reference frame for an object to attain
8509 //      to dock with another object.
8510 //      *dom            resultant global matrix
8511 //      *db_dest        pointer to destination docking bay information
8512 //      *db_src pointer to source docking bay information
8513 //      *dorient        pointer to global orientation of docking bay (ie, the dockee object's orient)
8514 //      *sorient        pointer to global orientation of docker
8515 void set_goal_dock_orient(matrix *dom, dock_bay *db_dest, dock_bay *db_src, matrix *dorient, matrix *sorient)
8516 {
8517         vector  fvec, uvec;
8518         matrix  m1, m2, m3;
8519
8520         //      Compute the global orientation of the docker's (dest) docking bay.
8521         fvec = db_dest->norm[0];
8522         vm_vec_negate(&fvec);
8523
8524         vm_vec_normalized_dir(&uvec, &db_dest->pnt[1], &db_dest->pnt[0]);
8525         vm_vector_2_matrix(&m1, &fvec, &uvec, NULL);
8526
8527         vm_matrix_x_matrix(&m3, dorient, &m1);
8528
8529         //      Compute the matrix given by the source docking bay.
8530         //      Pre-multiply the orientation of the source object (sorient) by the transpose
8531         //      of the docking bay's orientation, ie unrotate the source object's matrix.
8532         fvec = db_src->norm[0];
8533         vm_vec_normalized_dir(&uvec, &db_src->pnt[1], &db_src->pnt[0]);
8534         vm_vector_2_matrix(&m2, &fvec, &uvec, NULL);
8535         vm_transpose(&m2);
8536
8537         vm_matrix_x_matrix(dom, &m3, &m2);
8538 }
8539
8540 #define DOCK_BACKUP_RETURN_VAL  99999.9f
8541
8542 //      Make objp dock with dobjp
8543 //      Returns distance to goal, defined as distance between corresponding dock points, plus 10.0f * rotational velocity vector (DOA_DOCK only)
8544 //      DOA_APPROACH    means   approach point aip->path_cur
8545 //      DOA_DOCK                        means dock
8546 //      DOA_UNDOCK_1    means undock, moving to point nearest dock bay
8547 //      DOA_UNDOCK_2    means undock, moving to point nearest dock bay and facing away from ship
8548 //      DOA_DOCK_STAY   means rigidly maintain position in dock bay.
8549 float dock_orient_and_approach(object *objp, object *dobjp, int dock_mode)
8550 {
8551         ship_info       *sip0, *sip1;
8552         polymodel       *pm0, *pm1;
8553         ai_info         *aip;
8554         matrix          dom, nm;
8555         vector          goal_point, docker_point;
8556         float                   fdist = UNINITIALIZED_VALUE;
8557         int                     docker_index, dockee_index;             // index into docking_bays[] array for objects docking
8558                                                                                                                                 // docker is Pl_objp -- dockee is dobjp
8559         aip = &Ai_info[Ships[objp->instance].ai_index];
8560
8561         //      If dockee has moved much, then path will be recreated.
8562         //      Might need to change state if moved too far.
8563         if ((dock_mode != DOA_DOCK_STAY) && (dock_mode != DOA_DOCK)) {
8564                 if (maybe_recreate_path(objp, &Ai_info[Ships[objp->instance].ai_index], 0) > 5.0f) {
8565 /*                      if (dock_mode == DOA_APPROACH) {
8566                                 return DOCK_BACKUP_RETURN_VAL;
8567                         } else if (dock_mode == DOA_DOCK) {
8568                                 return DOCK_BACKUP_RETURN_VAL;          
8569                         }
8570 */              }
8571         }
8572
8573         objp->phys_info.forward_thrust = 0.0f;          //      Kill thrust so we don't have a sputtering thruster.
8574
8575         sip0 = &Ship_info[Ships[objp->instance].ship_info_index];
8576         sip1 = &Ship_info[Ships[dobjp->instance].ship_info_index];
8577         pm0 = model_get( sip0->modelnum );
8578         pm1 = model_get( sip1->modelnum );
8579
8580         docker_index = aip->dock_index;
8581         dockee_index = aip->dockee_index;
8582
8583         Assert( docker_index >= 0 );
8584         Assert( dockee_index >= 0 );
8585
8586         Assert(pm0->docking_bays[docker_index].num_slots == 2);
8587         Assert(pm1->docking_bays[dockee_index].num_slots == 2);
8588
8589         float speed_scale = 1.0f;
8590         if (sip0->flags & SIF_SUPPORT) {
8591                 speed_scale = 3.0f;
8592         }
8593
8594         switch (dock_mode) {
8595         case DOA_APPROACH:
8596                 {
8597                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8598                         return 9999.9f;
8599                 }
8600                 
8601                 //      Compute the desired global orientation matrix for the docker's station.
8602                 //      That is, the normal vector of the docking station must be the same as the
8603                 //      forward vector and the vector between its two points must be the uvec.
8604                 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8605
8606                 //      Compute new orientation matrix and update rotational velocity.
8607                 vector  w_in, w_out, vel_limit, acc_limit;
8608                 float           tdist, mdist, ss1;
8609
8610                 w_in = objp->phys_info.rotvel;
8611                 vel_limit = objp->phys_info.max_rotvel;
8612                 vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8613                 
8614                 if (sip0->flags & SIF_SUPPORT)
8615                         vm_vec_scale(&acc_limit, 2.0f);
8616
8617                 // 1 at end of line prevent overshoot
8618                 vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit, 1);
8619                 objp->phys_info.rotvel = w_out;
8620                 objp->orient = nm;
8621
8622                 //      Translate towards goal and note distance to goal.
8623                 goal_point = Path_points[aip->path_cur].pos;
8624                 mdist = ai_matrix_dist(&objp->orient, &dom);
8625                 tdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8626
8627                 //      If translation is badly lagging rotation, speed up translation.
8628                 if (mdist > 0.1f) {
8629                         ss1 = tdist/(10.0f * mdist);
8630                         if (ss1 > 2.0f)
8631                                 ss1 = 2.0f;
8632                 } else
8633                         ss1 = 2.0f;
8634
8635                 // nprintf(("AI", "speed scale = %7.3f\n", ss1));
8636                 speed_scale *= 1.0f + ss1;
8637
8638                 dock_move_towards_point(objp, NULL, &goal_point, speed_scale, dobjp->phys_info.speed);
8639
8640                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8641
8642                 //      Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8643                 // nprintf(("AI", "matrix dist = %7.3f, threshold = %7.3f\n", mdist, 2*flFrametime));
8644                 fdist += 2.0f * mdist;
8645
8646                 break;
8647         }
8648         case DOA_DOCK:
8649                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8650                         return 9999.9f;
8651                 }
8652         case DOA_DOCK_STAY:
8653                 //      Compute the desired global orientation matrix for the docker's station.
8654                 //      That is, the normal vector of the docking station must be the same as the
8655                 //      forward vector and the vector between its two points must be the uvec.
8656                 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8657
8658                 //      Compute distance between dock bay points.
8659                 vector  db0, db1, db2, db3;
8660
8661                 vm_vec_unrotate(&db0, &pm0->docking_bays[docker_index].pnt[0], &objp->orient);
8662                 vm_vec_add2(&db0, &objp->pos);
8663
8664                 vm_vec_unrotate(&db1, &pm0->docking_bays[docker_index].pnt[1], &objp->orient);
8665                 vm_vec_add2(&db1, &objp->pos);
8666
8667                 vm_vec_unrotate(&db2, &pm1->docking_bays[dockee_index].pnt[0], &dobjp->orient);
8668                 vm_vec_add2(&db2, &dobjp->pos);
8669
8670                 vm_vec_unrotate(&db3, &pm1->docking_bays[dockee_index].pnt[1], &dobjp->orient);
8671                 vm_vec_add2(&db3, &dobjp->pos);
8672
8673                 vm_vec_avg(&goal_point, &db2, &db3);
8674
8675                 vm_vec_avg(&docker_point, &db0, &db1);
8676                 vm_vec_sub2(&docker_point, &objp->pos);
8677
8678                 if (dock_mode == DOA_DOCK) {
8679                         vector  t1, t2;
8680                         vector  w_in, w_out, vel_limit, acc_limit;
8681
8682                         fdist = vm_vec_dist_quick(vm_vec_avg(&t1, &db0, &db1), vm_vec_avg(&t2, &db2, &db3));
8683
8684                         //      Compute new orientation matrix and update rotational velocity.
8685                         w_in = objp->phys_info.rotvel;
8686                         vel_limit = objp->phys_info.max_rotvel;
8687                         vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8688
8689                         if (sip0->flags & SIF_SUPPORT)
8690                                 vm_vec_scale(&acc_limit, 2.0f);
8691
8692                         vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit);
8693                         objp->phys_info.rotvel = w_out;
8694                         objp->orient = nm;
8695
8696                         //      Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8697                         fdist += 10.0f * vm_vec_mag_quick(&w_out);
8698
8699                         dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale, dobjp->phys_info.speed);
8700                 } else {
8701                         Assert(dock_mode == DOA_DOCK_STAY);
8702                         objp->orient = dom;
8703                         vector  temp;
8704                         vm_vec_sub(&temp, &goal_point, &docker_point);
8705                         vm_vec_sub(&objp->pos, &goal_point, &docker_point);
8706                 }
8707
8708                 break;
8709         case DOA_UNDOCK_1: {
8710                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8711                         return 9999.9f;
8712                 }
8713
8714                 //      Undocking.
8715                 //      Move to point on dock path nearest to dock station.
8716                 Assert(aip->path_length >= 2);
8717                 goal_point = Path_points[aip->path_start + aip->path_length-2].pos;
8718
8719                 vm_vec_zero(&docker_point);
8720                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8721
8722                 dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale);
8723
8724                 break;
8725                           }
8726
8727         case DOA_UNDOCK_2: {
8728                 //      Undocking.
8729                 //      Move to point on dock path nearest to dock station and orient away from big ship.
8730                 int             desired_index;
8731
8732                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8733                         return 9999.9f;
8734                 }
8735
8736                 Assert(aip->path_length >= 2);
8737 //              if (aip->path_length >= 3)
8738 //                      desired_index = aip->path_length-3;
8739 //              else
8740                         desired_index = aip->path_length-2;
8741
8742                 goal_point = Path_points[aip->path_start + desired_index].pos;
8743
8744                 dock_move_towards_point(objp, NULL, &goal_point, speed_scale);
8745
8746                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8747                 break;
8748                           }
8749         case DOA_UNDOCK_3: {
8750                 float           dist, goal_dist;
8751                 vector  away_vec;
8752
8753                 goal_dist = objp->radius + dobjp->radius + 25.0f;
8754
8755                 dist = vm_vec_normalized_dir(&away_vec, &objp->pos, &dobjp->pos);
8756                 vm_vec_scale_add(&goal_point, &dobjp->pos, &away_vec, goal_dist);
8757                 if (vm_vec_dist_quick(&goal_point, &dobjp->pos) < vm_vec_dist_quick(&objp->pos, &dobjp->pos))
8758                         fdist = 0.0f;
8759                 else {
8760                         float   dot, accel;
8761                         float turn_time = Ship_info[Ships[objp->instance].ship_info_index].srotation_time;
8762                         ai_turn_towards_vector(&goal_point, objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
8763
8764                         dot = vm_vec_dot(&objp->orient.fvec, &away_vec);
8765                         accel = 0.1f;
8766                         if (dot > accel)
8767                                 accel = dot;
8768                         if (dist > goal_dist/2)
8769                                 accel *= 1.2f - 0.5f*goal_dist/dist;
8770
8771                         accelerate_ship(aip, accel);
8772                         fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8773                 }
8774
8775                 break;
8776                                                          }
8777         }
8778
8779 #ifndef NDEBUG
8780         //      For debug purposes, compute global orientation of both dock vectors and show
8781         //      how close they are.
8782         vector  d0, d1;
8783
8784         vm_vec_unrotate(&d0, &pm0->docking_bays[docker_index].norm[0], &objp->orient);
8785         vm_vec_unrotate(&d1, &pm1->docking_bays[dockee_index].norm[0], &dobjp->orient);
8786
8787         //nprintf(("AI", "or/app: dist = %7.3f/%7.3f, dot = %7.3f, global dot = %7.3f\n", 
8788         //      vm_vec_dist_quick(&goal_point, &objp->pos), fdist,
8789         //      vm_vec_dot(&objp->orient.fvec, &dom.fvec), 
8790         //      vm_vec_dot(&d0, &d1)));
8791 #endif
8792
8793         // -- Note, A lot of callers don't care about fdist, so OK to return ERROR value: Assert(fdist != UNINITIALIZED_VALUE);
8794         return fdist;
8795
8796 }
8797
8798 void debug_find_guard_object()
8799 {
8800         ship                    *shipp = &Ships[Pl_objp->instance];     
8801         object          *objp;
8802
8803         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
8804                 if ((Pl_objp != objp) && (objp->type == OBJ_SHIP)) {
8805                         if (objp->instance != -1) {
8806                                 if (Ships[objp->instance].team == shipp->team)  {
8807                                         // nprintf(("AI", "Setting guard object for %s to %s\n", shipp->ship_name, Ships[objp->instance].ship_name));
8808                                         ai_set_guard_object(Pl_objp, objp);
8809                                 }
8810                         }
8811                 }
8812         }
8813
8814 }
8815
8816 //      Given an object number, return the number of ships attacking it.
8817 int num_ships_attacking(int objnum)
8818 {
8819         object  *objp;
8820         ship_obj        *so;
8821         int             count = 0;
8822
8823         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8824                 objp = &Objects[so->objnum];
8825                 if (objp->instance != -1) {
8826                         ai_info *aip;
8827                         aip = &Ai_info[Ships[objp->instance].ai_index];
8828
8829                         if ((aip->mode == AIM_CHASE) && (aip->target_objnum == objnum))
8830                                 if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team)
8831                                         count++;
8832                 }
8833         }
8834
8835         return count;
8836 }
8837
8838 //      For all objects attacking object #objnum, remove the one that is farthest away.
8839 //      Do this by resuming previous behavior, if any.  If not, set target_objnum to -1.
8840 void remove_farthest_attacker(int objnum)
8841 {
8842         object  *objp, *objp2, *farthest_objp;
8843         ship_obj        *so;
8844         float           farthest_dist;
8845
8846         objp2 = &Objects[objnum];
8847
8848         farthest_dist = 9999999.9f;
8849         farthest_objp = NULL;
8850
8851         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8852                 objp = &Objects[so->objnum];
8853                 if ( !(objp->flags & OF_PLAYER_SHIP)) {
8854                         if (objp->instance != -1) {
8855                                 ai_info *aip2;
8856
8857                                 aip2 = &Ai_info[Ships[objp->instance].ai_index];
8858
8859                                 if ((aip2->mode == AIM_CHASE) && (aip2->target_objnum == objnum)) {
8860                                         if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team) {
8861                                                 float   dist;
8862
8863                                                 dist = vm_vec_dist_quick(&objp->pos, &objp2->pos);
8864                                                 if (dist < farthest_dist) {
8865                                                         farthest_dist = dist;
8866                                                         farthest_objp = objp;
8867                                                 }
8868                                         }
8869                                 }
8870                         }
8871                 }
8872         }
8873
8874         if (farthest_objp != NULL) {
8875                 ai_info *aip;
8876                 Assert(farthest_objp->type == OBJ_SHIP);
8877                 Assert((farthest_objp->instance > -1) && (farthest_objp->instance < MAX_SHIPS));
8878                 Assert(Ships[farthest_objp->instance].ai_index > -1);
8879
8880                 aip = &Ai_info[Ships[farthest_objp->instance].ai_index];
8881
8882                 if (!maybe_resume_previous_mode(Pl_objp, aip)) {
8883                         //      If already ignoring something under player's orders, don't ignore current target.
8884                         if ((aip->ignore_objnum == UNUSED_OBJNUM) || (aip->ai_flags & AIF_TEMPORARY_IGNORE)) {
8885                                 aip->ignore_objnum = aip->target_objnum;
8886                                 aip->ignore_signature = Objects[aip->target_objnum].signature;
8887                                 aip->ai_flags |= AIF_TEMPORARY_IGNORE;
8888                                 aip->ignore_expire_timestamp = timestamp(((myrand() % 10) + 20) * 1000);        //      OK to attack again in 20 to 24 seconds.
8889                         }
8890                         aip->target_objnum = -1;
8891                         ai_do_default_behavior(farthest_objp);
8892                 }
8893         }
8894 }
8895
8896 // Maybe limit the number of attackers on attack_objnum.  For now, only limit attackers
8897 // in attacked_objnum is the player
8898 // input:       attacked_objnum =>              object index for ship we want to limit attacks on
8899 //
8900 //      exit:                   1       =>      num attackers exceeds maximum, abort
8901 //                                      0       =>      removed the farthest attacker
8902 //                                      -1      =>      nothing was done
8903 int ai_maybe_limit_attackers(int attacked_objnum)
8904 {
8905         int rval=-1;
8906
8907         // limit the number of ships attacking the _player_ only
8908 //      if ( attacked_objnum == OBJ_INDEX(Player_obj) ) {
8909         if ( Objects[attacked_objnum].flags & OF_PLAYER_SHIP) {
8910                 int num_attacking;
8911                 num_attacking = num_ships_attacking(attacked_objnum);
8912
8913                 if (num_attacking == Skill_level_max_attackers[Game_skill_level]) {
8914                         remove_farthest_attacker(attacked_objnum);
8915                         rval=0;
8916                 } else if (num_attacking > Skill_level_max_attackers[Game_skill_level]) {
8917                         rval=1;
8918                 }
8919                 //nprintf(("AI", "Num attacking player = %i\n", num_attacking));
8920         }
8921
8922         return rval;
8923 }
8924
8925 //      Object being guarded by object *guard_objp was hit by object *hitter_objp
8926 void guard_object_was_hit(object *guard_objp, object *hitter_objp)
8927 {
8928         int             hitter_objnum;
8929         ai_info *aip;
8930
8931         aip = &Ai_info[Ships[guard_objp->instance].ai_index];
8932
8933         if (guard_objp == hitter_objp) {
8934                 // Int3();      //      Bogus!  Who tried to get me to attack myself!  Trace out and fix!
8935                 return;
8936         }
8937
8938         if (guard_objp->type == OBJ_GHOST || hitter_objp->type == OBJ_GHOST)
8939                 return;
8940
8941         if (aip->ai_flags & AIF_NO_DYNAMIC)     //      Not allowed to pursue dynamic goals.  So, why are we guarding?
8942                 return;
8943
8944         Assert( (hitter_objp->type == OBJ_SHIP) || (hitter_objp->type == OBJ_ASTEROID) || (hitter_objp->type == OBJ_WEAPON) );
8945
8946         hitter_objnum = OBJ_INDEX(hitter_objp);
8947
8948         if ( hitter_objp->type == OBJ_SHIP ) {
8949                 //      If the hitter object is the ignore object, don't attack it.
8950                 if (is_ignore_object(aip, hitter_objp-Objects))
8951                         return;
8952
8953                 //      If hitter is on same team as me, don't attack him.
8954                 if (Ships[guard_objp->instance].team == Ships[hitter_objp->instance].team)
8955                         return;
8956
8957                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
8958                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
8959                         return;
8960                 }
8961
8962                 // dont attack if you can't see him
8963                 if ( awacs_get_level(hitter_objp, &Ships[aip->shipnum], 1) < 1 ) {
8964                         // if he's a stealth and visible, but not targetable, ok to attack.
8965                         if ( is_object_stealth_ship(hitter_objp) ) {
8966                                 if ( ai_is_stealth_visible(guard_objp, hitter_objp) != STEALTH_VISIBLE ) {
8967                                         return;
8968                                 }
8969                         }
8970                 }
8971         }
8972
8973         if (aip->target_objnum == -1) {
8974                 aip->ok_to_target_timestamp = timestamp(0);
8975         }
8976
8977         if ((aip->submode == AIS_GUARD_PATROL) || (aip->submode == AIS_GUARD_STATIC)) {
8978
8979                 if ( hitter_objp->type == OBJ_SHIP ) {
8980                         if (!(Ship_info[Ships[guard_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
8981                                 return;
8982                         }
8983
8984                         // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
8985                         if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
8986                                 return;
8987                         }
8988                 }
8989
8990                 if (aip->target_objnum != hitter_objnum) {
8991                         aip->aspect_locked_time = 0.0f;
8992                 }
8993
8994                 aip->ok_to_target_timestamp = timestamp(0);
8995
8996                 set_target_objnum(aip, hitter_objnum);
8997                 //if (aip->target_objnum == -1) nprintf(("AI", "Frame %i: Attacking NONE\n",Framecount)); else nprintf(("AI", "Frame %i: Attacking %s\n", Framecount, Ships[Objects[aip->target_objnum].instance].ship_name));
8998                 aip->previous_mode = AIM_GUARD;
8999                 aip->previous_submode = aip->submode;
9000                 aip->mode = AIM_CHASE;
9001                 aip->submode = SM_ATTACK;
9002                 aip->submode_start_time = Missiontime;
9003                 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9004         } else if (aip->previous_mode == AIM_GUARD) {
9005                 if (aip->target_objnum == -1) {
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
9014                         set_target_objnum(aip, hitter_objnum);
9015                 //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));
9016                         aip->mode = AIM_CHASE;
9017                         aip->submode = SM_ATTACK;
9018                         aip->submode_start_time = Missiontime;
9019                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9020                 } else {
9021                         int     num_attacking_cur, num_attacking_new;
9022
9023                         num_attacking_cur = num_ships_attacking(aip->target_objnum);
9024                         if (num_attacking_cur > 1) {
9025                                 num_attacking_new = num_ships_attacking(hitter_objnum);
9026
9027                                 if (num_attacking_new < num_attacking_cur) {
9028
9029                                         if ( hitter_objp->type == OBJ_SHIP ) {
9030                                                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9031                                                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9032                                                         return;
9033                                                 }
9034                                         }
9035                                         set_target_objnum(aip, hitter_objp-Objects);
9036                 //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));
9037                                         aip->mode = AIM_CHASE;
9038                                         aip->submode = SM_ATTACK;
9039                                         aip->submode_start_time = Missiontime;
9040                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9041                                 }
9042                         }
9043                 }
9044         }
9045 }
9046
9047 //      Ship object *hit_objp was hit by ship object *hitter_objp.
9048 //      See if anyone is guarding hit_objp and, if so, do something useful.
9049 void maybe_update_guard_object(object *hit_objp, object *hitter_objp)
9050 {
9051         object  *objp;
9052         ship_obj        *so;
9053
9054         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9055                 objp = &Objects[so->objnum];
9056                 if (objp->instance != -1) {
9057                         ai_info *aip;
9058                         aip = &Ai_info[Ships[objp->instance].ai_index];
9059
9060                         if ((aip->mode == AIM_GUARD) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
9061                                 if (aip->guard_objnum == hit_objp-Objects) {
9062                                         guard_object_was_hit(objp, hitter_objp);
9063                                 } else if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[hit_objp->instance].ai_index].wing)) {
9064                                         guard_object_was_hit(objp, hitter_objp);
9065                                 }
9066                         }
9067                 }
9068         }
9069 }
9070
9071 // Scan missile list looking for bombs homing on guarded_objp
9072 // return 1 if bomb is found (and targeted by guarding_objp), otherwise return 0
9073 int ai_guard_find_nearby_bomb(object *guarding_objp, object *guarded_objp)
9074 {       
9075         missile_obj     *mo;
9076         object          *bomb_objp, *closest_bomb_objp=NULL;
9077         float                   dist, dist_to_guarding_obj,closest_dist_to_guarding_obj=999999.0f;
9078         weapon          *wp;
9079         weapon_info     *wip;
9080
9081         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
9082                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
9083                 bomb_objp = &Objects[mo->objnum];
9084
9085                 wp = &Weapons[bomb_objp->instance];
9086                 wip = &Weapon_info[wp->weapon_info_index];
9087
9088                 if ( !(wip->wi_flags & WIF_BOMB) ) {
9089                         continue;
9090                 }
9091
9092                 if ( wp->homing_object != guarded_objp ) {
9093                         continue;
9094                 }
9095
9096                 dist = vm_vec_dist_quick(&bomb_objp->pos, &guarded_objp->pos);
9097
9098                 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9099                         dist_to_guarding_obj = vm_vec_dist_quick(&bomb_objp->pos, &guarding_objp->pos);
9100                         if ( dist_to_guarding_obj < closest_dist_to_guarding_obj ) {
9101                                 closest_dist_to_guarding_obj = dist_to_guarding_obj;
9102                                 closest_bomb_objp = bomb_objp;
9103                         }
9104                 }
9105         }
9106
9107         if ( closest_bomb_objp ) {
9108                 guard_object_was_hit(guarding_objp, closest_bomb_objp);
9109                 return 1;
9110         }
9111
9112         return 0;
9113 }
9114
9115 //      Scan enemy ships and see if one is near enough to guard object to be pursued.
9116 void ai_guard_find_nearby_ship(object *guarding_objp, object *guarded_objp)
9117 {
9118         ship            *guarding_shipp = &Ships[guarding_objp->instance];
9119         ai_info *guarding_aip = &Ai_info[guarding_shipp->ai_index];
9120         ship_obj        *so;
9121         object  *enemy_objp;
9122         float           dist;
9123
9124         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9125                 enemy_objp = &Objects[so->objnum];
9126
9127                 if (enemy_objp->instance < 0) {
9128                         continue;
9129                 }
9130
9131                 ship    *eshipp = &Ships[enemy_objp->instance];
9132
9133                 //      Don't attack a cargo container or other harmless ships
9134                 if (!(Ship_info[eshipp->ship_info_index].flags & SIF_HARMLESS)) {
9135                         if (guarding_shipp->team != eshipp->team)       {
9136                                 dist = vm_vec_dist_quick(&enemy_objp->pos, &guarded_objp->pos);
9137                                 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9138                                         guard_object_was_hit(guarding_objp, enemy_objp);
9139                                 } else if ((dist < 3000.0f) && (Ai_info[eshipp->ai_index].target_objnum == guarding_aip->guard_objnum)) {
9140                                         //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));
9141                                         guard_object_was_hit(guarding_objp, enemy_objp);
9142                                 }
9143                         }
9144                 }
9145         }
9146 }
9147
9148 // Scan for nearby asteroids.  Favor asteroids which have their collide_objnum set to that of the
9149 // guarded ship.  Also, favor asteroids that are closer to the guarding ship, since it looks cooler
9150 // when a ship blows up an asteroid then goes after the pieces that break off.
9151 void ai_guard_find_nearby_asteroid(object *guarding_objp, object *guarded_objp)
9152 {       
9153         float           dist;
9154
9155         object  *closest_asteroid_objp=NULL, *danger_asteroid_objp=NULL, *asteroid_objp;
9156         float           dist_to_self, closest_danger_asteroid_dist=999999.0f, closest_asteroid_dist=999999.0f;
9157
9158         for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp != END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) {
9159                 if ( asteroid_objp->type == OBJ_ASTEROID ) {
9160                         // Attack asteroid if near guarded ship
9161                         dist = vm_vec_dist_quick(&asteroid_objp->pos, &guarded_objp->pos);
9162                         if ( dist < (MAX_GUARD_DIST + guarded_objp->radius)*2) {
9163                                 dist_to_self = vm_vec_dist_quick(&asteroid_objp->pos, &guarding_objp->pos);
9164                                 if ( OBJ_INDEX(guarded_objp) == asteroid_collide_objnum(asteroid_objp) ) {
9165                                         if( dist_to_self < closest_danger_asteroid_dist ) {
9166                                                 danger_asteroid_objp=asteroid_objp;
9167                                                 closest_danger_asteroid_dist=dist_to_self;
9168                                         }
9169                                 } 
9170                                 if ( dist_to_self < closest_asteroid_dist ) {
9171                                         // only attack if moving slower than own max speed
9172                                         if ( vm_vec_mag_quick(&asteroid_objp->phys_info.vel) < guarding_objp->phys_info.max_vel.z ) {
9173                                                 closest_asteroid_dist = dist_to_self;
9174                                                 closest_asteroid_objp = asteroid_objp;
9175                                         }
9176                                 }
9177                         }
9178                 }
9179         }
9180
9181         if ( danger_asteroid_objp ) {
9182                 guard_object_was_hit(guarding_objp, danger_asteroid_objp);
9183         } else if ( closest_asteroid_objp ) {
9184                 guard_object_was_hit(guarding_objp, closest_asteroid_objp);
9185         }
9186 }
9187
9188 //      Scan potential harmful objects and see if one is near enough to guard object to be pursued.
9189 void ai_guard_find_nearby_object()
9190 {
9191         ship                    *shipp = &Ships[Pl_objp->instance];
9192         ai_info         *aip = &Ai_info[shipp->ai_index];
9193         object          *guardobjp;
9194         int                     bomb_found=0;
9195
9196         guardobjp = &Objects[aip->guard_objnum];
9197         
9198         // highest priority is a bomb fired on guarded ship
9199         bomb_found = ai_guard_find_nearby_bomb(Pl_objp, guardobjp);
9200
9201         if ( !bomb_found ) {
9202                 // check for ships if there are no bombs fired at guarded ship
9203                 ai_guard_find_nearby_ship(Pl_objp, guardobjp);
9204
9205                 // if not attacking anything, go for asteroid close to guarded ship
9206                 if ( (aip->target_objnum == -1) && asteroid_count() ) {
9207                         ai_guard_find_nearby_asteroid(Pl_objp, guardobjp);
9208                 }
9209         }
9210 }
9211
9212 // gets closest point on extended axis of cylinder, r_vec, and radius of cylinder
9213 // returns z of axis_point in cyl_objp reference frame
9214 float get_cylinder_points(object *other_objp, object *cyl_objp, vector *axis_pt, vector *r_vec, float *radius)
9215 {
9216         Assert(other_objp->type == OBJ_SHIP);
9217         Assert(cyl_objp->type == OBJ_SHIP);
9218
9219         // get radius of cylinder
9220         polymodel *pm = model_get(Ships[cyl_objp->instance].modelnum);
9221         float tempx, tempy;
9222         tempx = max(-pm->mins.x, pm->maxs.x);
9223         tempy = max(-pm->mins.y, pm->maxs.y);
9224         *radius = max(tempx, tempy);
9225
9226         // get vec from cylinder to other_obj
9227         vector r_sph;
9228         vm_vec_sub(&r_sph, &other_objp->pos, &cyl_objp->pos);
9229
9230         // get point on axis and on cylinder
9231         // extended_cylinder_z is along extended cylinder
9232         // cylinder_z is capped within cylinder
9233         float extended_cylinder_z = vm_vec_dotprod(&r_sph, &cyl_objp->orient.fvec);
9234
9235         // get pt on axis of extended cylinder
9236         vm_vec_scale_add(axis_pt, &cyl_objp->pos, &cyl_objp->orient.fvec, extended_cylinder_z);
9237
9238         // get r_vec (pos - axis_pt) normalized
9239         vm_vec_normalized_dir(r_vec, &other_objp->pos, axis_pt);
9240
9241         return extended_cylinder_z;
9242 }
9243
9244 // handler for guard behavior when guarding BIG ships
9245 //      When someone has attacked guarded ship, then attack that ship.
9246 // To attack another ship, switch out of guard mode into chase mode.
9247 void ai_big_guard()
9248 {
9249         
9250         ship                    *shipp = &Ships[Pl_objp->instance];
9251         ai_info         *aip = &Ai_info[shipp->ai_index];
9252         object          *guard_objp;
9253
9254         // sanity checks already done in ai_guard()
9255         guard_objp = &Objects[aip->guard_objnum];
9256
9257         switch (aip->submode) {
9258         case AIS_GUARD_STATIC:
9259         case AIS_GUARD_PATROL:
9260                 {
9261                 vector axis_pt, r_vec, theta_vec;
9262                 float radius, extended_z;
9263
9264                 // get random [0 to 1] based on OBJNUM
9265                 float objval = static_randf(Pl_objp-Objects);
9266
9267                 // get position relative to cylinder of guard_objp              
9268                 extended_z = get_cylinder_points(Pl_objp, guard_objp, &axis_pt, &r_vec, &radius);
9269                 vm_vec_crossprod(&theta_vec, &guard_objp->orient.fvec, &r_vec);
9270
9271                 // half ships circle each way
9272                 if (objval > 0.5f) {
9273                         vm_vec_negate(&theta_vec);
9274                 }
9275
9276                 float min_guard_dist = radius + Pl_objp->radius + 50.0f;
9277                 float desired_guard_dist = min_guard_dist + 0.5f * ((1.0f + objval) * MAX_GUARD_DIST);
9278                 float max_guard_dist =     min_guard_dist + 1.0f * ((1.0f + objval) * MAX_GUARD_DIST);
9279
9280                 // get z extents
9281                 float min_z, max_z, length;
9282                 polymodel *pm = model_get(Ships[guard_objp->instance].modelnum);
9283                 min_z = pm->mins.z;
9284                 max_z = pm->maxs.z;
9285                 length = max_z - min_z;
9286
9287                 // get desired z
9288                 // how often to choose new desired_z
9289                 // 1*(64) sec < 2000, 2*(64) < 2-4000 3*(64) > 4-8000, etc (Missiontime >> 22 is 64 sec intervals)
9290                 int time_choose = int(floor(log(length * 0.001) / log(2)));
9291                 float desired_z = min_z + length * static_randf( Pl_objp-Objects ^ (Missiontime >> (22 + time_choose)) );
9292
9293                 // get r from guard_ship
9294                 float cur_guard_rad = vm_vec_dist(&Pl_objp->pos, &axis_pt);
9295
9296                 // is ship within extents of cylinder of ship it is guarding
9297                 int inside = (extended_z > min_z) && (extended_z < min_z + length);
9298
9299                 vector goal_pt;
9300                 // maybe go into orbit mode
9301                 if (cur_guard_rad < max_guard_dist) {
9302                         if ( cur_guard_rad > min_guard_dist ) {
9303                                 if (inside) {
9304                                         // orbit
9305                                         vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, desired_guard_dist);
9306                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9307                                 } else {
9308                                         // move to where I can orbit
9309                                         if (extended_z < min_z) {
9310                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.fvec, min_z);
9311                                         } else {
9312                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.fvec, max_z);
9313                                         }
9314                                         vm_vec_scale_add2(&goal_pt, &r_vec, desired_guard_dist);
9315                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9316                                 }
9317                         } else {
9318                                 // too close for orbit mode
9319                                 if (inside) {
9320                                         // inside (fly straight out and return circle)
9321                                         vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, max_guard_dist);
9322                                 } else {
9323                                         // outside (fly to edge and circle)
9324                                         if (extended_z < min_z) {
9325                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.fvec, min_z);
9326                                         } else {
9327                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.fvec, max_z);
9328                                         }
9329                                         vm_vec_scale_add2(&goal_pt, &r_vec, max_guard_dist);
9330                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9331                                 }
9332                         }
9333
9334                         if (Pl_objp->phys_info.fspeed > 0) {
9335                                 // modify goal_pt to take account moving guard objp
9336                                 float dist = vm_vec_dist_quick(&Pl_objp->pos, &goal_pt);
9337                                 float time = dist / Pl_objp->phys_info.fspeed;
9338                                 vm_vec_scale_add2(&goal_pt, &guard_objp->phys_info.vel, time);
9339
9340                                 // now modify to move to desired z (at a max of 20 m/s)
9341                                 float delta_z = desired_z - extended_z;
9342                                 float v_z = delta_z * 0.2f;
9343                                 if (v_z < -20) {
9344                                         v_z = -20.0f;
9345                                 } else if (v_z > 20) {
9346                                         v_z = 20.0f;
9347                                 }
9348
9349                                 vm_vec_scale_add2(&goal_pt, &guard_objp->orient.fvec, v_z*time);
9350                         }
9351
9352                 } else {
9353                         // cast vector to center of guard_ship adjusted by desired_z
9354                         float delta_z = desired_z - extended_z;
9355                         vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.fvec, delta_z);
9356                 }
9357
9358                 // try not to bump into things along the way
9359                 if ( (cur_guard_rad > max_guard_dist) || (extended_z < min_z) || (extended_z > max_z) ) {
9360                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9361                                 return;
9362                         }
9363
9364                         if (avoid_player(Pl_objp, &goal_pt)) {
9365                                 return;
9366                         }
9367                 } else {
9368                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9369                                 return;
9370                         }
9371                 }
9372
9373                 // got the point, now let's go there
9374                 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);
9375 //              aip->goal_point = goal_pt;
9376                 accelerate_ship(aip, 1.0f);
9377
9378                 //      Periodically, scan for a nearby ship to attack.
9379                 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9380                         ai_guard_find_nearby_object();
9381                 }
9382                 }
9383                 break;
9384
9385         case AIS_GUARD_ATTACK:
9386                 //      The guarded ship has been attacked.  Do something useful!
9387                 ai_chase();
9388                 break;
9389
9390         default:
9391                 //Int3();       //      Illegal submode for Guard mode.
9392                 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9393                 aip->submode = AIS_GUARD_PATROL;
9394                 break;
9395         }
9396 }
9397
9398 //      Main handler for guard behavior.
9399 //      When someone has attacked guarded ship, then attack that ship.
9400 // To attack another ship, switch out of guard mode into chase mode.
9401 void ai_guard()
9402 {
9403         ship                    *shipp = &Ships[Pl_objp->instance];
9404         ai_info         *aip = &Ai_info[shipp->ai_index];
9405         object          *guard_objp;    
9406         ship                    *gshipp;
9407         float                   dist_to_guardobj, dot_to_guardobj;
9408         vector          vec_to_guardobj;
9409
9410         /*      //      Debug code, find an object to guard.
9411         int finding_guard_objnum = 0;   //      Debug code, to see if body of "if" below gets executed. 
9412         if (aip->guard_objnum == -1) {
9413                 finding_guard_objnum = 1;
9414                 debug_find_guard_object();
9415                 if (aip->guard_objnum == -1)
9416                         return;
9417         }
9418 */
9419         if (aip->guard_objnum == -1) {
9420                 aip->mode = AIM_NONE;
9421                 return;
9422         }
9423
9424         Assert(aip->guard_objnum != -1);
9425
9426         guard_objp = &Objects[aip->guard_objnum];
9427
9428         if (guard_objp == Pl_objp) {
9429                 Int3();         //      This seems illegal.  Why is a ship guarding itself?
9430                 aip->guard_objnum = -1;
9431                 return;
9432         }
9433
9434         // check that I have someone to guard
9435         if (guard_objp->instance == -1) {
9436                 return;
9437         }
9438
9439         //      Not sure whether this should be impossible, or a reasonable cleanup condition.
9440         //      For now (3/31/97), it's getting trapped by an Assert, so clean it up.
9441         if (guard_objp->type != OBJ_SHIP) {
9442                 aip->guard_objnum = -1;
9443                 return;
9444         }
9445
9446         // handler for gurad object with BIG radius
9447         if (guard_objp->radius > BIG_GUARD_RADIUS) {
9448                 ai_big_guard();
9449                 return;
9450         }
9451
9452         gshipp = &Ships[guard_objp->instance];
9453
9454         float                   objval;
9455         vector          goal_point;
9456         vector          rel_vec;
9457         float                   dist_to_goal_point, dot_to_goal_point, accel_scale;
9458         vector          v2g, rvec;
9459
9460         // get random [0 to 1] based on OBJNUM
9461         objval = static_randf(Pl_objp-Objects);
9462
9463         switch (aip->submode) {
9464         case AIS_GUARD_STATIC:
9465         case AIS_GUARD_PATROL:
9466                 //      Stay near ship
9467                 dist_to_guardobj = vm_vec_normalized_dir(&vec_to_guardobj, &guard_objp->pos, &Pl_objp->pos);
9468                 dot_to_guardobj = vm_vec_dot(&Pl_objp->orient.fvec, &vec_to_guardobj);
9469
9470                 rel_vec = aip->guard_vec;
9471                 vm_vec_add(&goal_point, &guard_objp->pos, &rel_vec);
9472
9473                 vm_vec_normalized_dir(&v2g, &goal_point, &Pl_objp->pos);
9474                 dist_to_goal_point = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
9475                 dot_to_goal_point = vm_vec_dot(&v2g, &Pl_objp->orient.fvec);
9476                 accel_scale = (1.0f + dot_to_goal_point)/2.0f;
9477
9478                 //      If far away, get closer
9479                 if (dist_to_goal_point > MAX_GUARD_DIST + 1.5 * (Pl_objp->radius + guard_objp->radius)) {
9480                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 5.0f)) {
9481                                 return;
9482                         }
9483
9484                         if (avoid_player(Pl_objp, &goal_point)) {
9485                                 return;
9486                         }
9487
9488                         // quite far away, so try to go straight to 
9489                         compute_desired_rvec(&rvec, &goal_point, &Pl_objp->pos);
9490                         ai_turn_towards_vector(&goal_point, Pl_objp, flFrametime, Ship_info[shipp->ship_info_index].srotation_time, NULL, NULL, 0.0f, 0, &rvec);
9491
9492                         accelerate_ship(aip, accel_scale * (0.25f + dist_to_goal_point/700.0f));
9493                 } else {
9494                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 2.0f)) {
9495                                 return;
9496                         }
9497
9498                         // get max of guard_objp (1) normal speed (2) dock speed
9499                         float speed = guard_objp->phys_info.speed;
9500
9501                         if (guard_objp->type == OBJ_SHIP) {
9502                                 ai_info *guard_aip = &Ai_info[Ships[guard_objp->instance].ai_index];
9503
9504                                 if (guard_aip->dock_objnum != -1) {
9505                                         speed = max(speed, Objects[guard_aip->dock_objnum].phys_info.speed);
9506                                 }
9507                         }
9508                         
9509                         //      Deal with guarding a small object.
9510                         //      If going to guard_vec might cause a collision with guarded object, pick a new guard point.
9511                         if (vm_vec_dot(&v2g, &vec_to_guardobj) > 0.8f) {
9512                                 if (dist_to_guardobj < dist_to_goal_point) {
9513                                         ai_set_guard_vec(Pl_objp, guard_objp);  //      OK to return here.
9514                                         return;
9515                                 }
9516                         } 
9517
9518                         if (speed > 10.0f) {
9519                                 //      If goal ship is moving more than a tiny bit, don't orbit it, get near it.
9520                                 if (vm_vec_dist_quick(&goal_point, &Pl_objp->pos) > 40.0f) {
9521                                         if (vm_vec_dot(&Pl_objp->orient.fvec, &v2g) < 0.0f) {
9522                                                 //      Just slow down, don't turn.
9523                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point/10.0f);
9524                                         } else {
9525                                                 //      Goal point is in front.
9526
9527                                                 //      If close to goal point, don't change direction, just change speed.
9528                                                 if (dist_to_goal_point > Pl_objp->radius + 10.0f) {
9529                                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9530                                                 }
9531                                                 
9532                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + (dist_to_goal_point-40.0f)/20.0f);
9533                                         }
9534                                 } else {
9535                                         if (dot_to_goal_point > 0.8f) {
9536                                                 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9537                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + dist_to_goal_point*0.1f);
9538                                         } else {
9539                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point*0.1f - 1.0f);
9540                                         }
9541                                 }
9542                         // consider guard object STILL
9543                         } else if (guard_objp->radius < 50.0f) {
9544                                 if (dist_to_goal_point > 15.0f) {
9545                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9546                                         set_accel_for_target_speed(Pl_objp, (dist_to_goal_point-10.0f)/2.0f);
9547                                 } else if (Pl_objp->phys_info.speed < 1.0f) {
9548                                         turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9549                                 }
9550                                 //      It's a big ship
9551                         } else if (dist_to_guardobj > MAX_GUARD_DIST + Pl_objp->radius + guard_objp->radius) {
9552                                 //      Orbiting ship, too far away
9553                                 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.0f + objval/2) * guard_objp->radius);
9554                                 accelerate_ship(aip, (1.0f + dot)/2.0f);
9555                         } else if (dist_to_guardobj < Pl_objp->radius + guard_objp->radius) {
9556                                 //      Orbiting ship, got too close
9557                                 turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9558                                 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9559                                         change_acceleration(aip, 0.25f);
9560                                 else
9561                                         accelerate_ship(aip, 0.5f + objval/4.0f);
9562                         } else {
9563                                 //      Orbiting ship, about the right distance away.
9564                                 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.5f + objval/2.0f)*guard_objp->radius);
9565                                 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9566                                         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));
9567                                 else
9568                                         accelerate_ship(aip, 0.5f * (1.0f + dot) * (0.3f + objval/3.0f));
9569                         }
9570                 }
9571
9572                 //      Periodically, scan for a nearby ship to attack.
9573                 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9574                         ai_guard_find_nearby_object();
9575                 }
9576                 break;
9577
9578         case AIS_GUARD_ATTACK:
9579                 //      The guarded ship has been attacked.  Do something useful!
9580                 ai_chase();
9581
9582                 break;
9583         default:
9584                 //Int3();       //      Illegal submode for Guard mode.
9585                 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9586                 aip->submode = AIS_GUARD_PATROL;
9587                 break;
9588         }
9589
9590 }
9591
9592 // Return the object of the ship that the given object is docked
9593 // with.  Currently, we know a ship is docked when his ai_mode is AIM_DOCK,
9594 // and his submode is AIS_DOCK_3.  I suppose that this is likely to change though.
9595 // Also, the objnum that was is passed in may not be the object that actually
9596 // performed the docking maneuver.  This code will account for that case.
9597 object *ai_find_docked_object( object *docker )
9598 {
9599         ai_info *aip;
9600
9601         // we are trying to find the dockee of docker.  (Note that that these terms
9602         // are totally relative to what is passed in as a parameter.)
9603
9604         // first thing to attempt is to check and see if this object is docked with something.
9605         Assert( docker->type == OBJ_SHIP );             // this had probably better be a ship!!!
9606         aip = &Ai_info[Ships[docker->instance].ai_index];
9607         if ( !(aip->ai_flags & AIF_DOCKED) )            // flag not set if not docked with anything
9608                 return NULL;
9609
9610         if ( aip->dock_objnum == -1 ) {
9611                 Int3();                                                                                 // mwa says this is wrong wrong wrong
9612                 ai_do_objects_undocked_stuff( docker, NULL );
9613                 return NULL;
9614         }
9615
9616         return &Objects[aip->dock_objnum];
9617
9618 }
9619
9620
9621 // define for the points subtracted from score for a rearm started on a player.
9622 #define REPAIR_PENALTY          50
9623
9624
9625 // function to clean up ai flags, variables, and other interesting information
9626 // for a ship that was getting repaired.  The how parameter is useful for multiplayer
9627 // only in that it tells us why the repaired ship is being cleaned up.
9628 void ai_do_objects_repairing_stuff( object *repaired_objp, object *repair_objp, int how )
9629 {
9630         ai_info *aip, *repair_aip;
9631         int             stamp = -1;
9632
9633         Assert( repaired_objp->type == OBJ_SHIP);
9634         aip = &Ai_info[Ships[repaired_objp->instance].ai_index];
9635
9636         // multiplayer
9637         int p_index;
9638         p_index = -1;
9639         if(Game_mode & GM_MULTIPLAYER){
9640                 p_index = multi_find_player_by_object(repaired_objp);           
9641         }               
9642         else {          
9643                 if(repaired_objp == Player_obj){
9644                         p_index = Player_num;
9645                 }
9646         }
9647
9648         switch( how ) {
9649         case REPAIR_INFO_BEGIN:
9650                 aip->ai_flags |= AIF_BEING_REPAIRED;
9651                 aip->ai_flags &= ~AIF_AWAITING_REPAIR;
9652                 stamp = timestamp(-1);
9653
9654                 // if this is a player ship, then subtract the repair penalty from this player's score
9655                 if ( repaired_objp->flags & OF_PLAYER_SHIP ) {
9656                         if ( !(Game_mode & GM_MULTIPLAYER) ) {
9657                                 Player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());                    // subtract the penalty
9658                         } else {
9659                                 /*
9660                                 int pnum;
9661
9662                                 // multiplayer game -- find the player, then subtract the score
9663                                 pnum = multi_find_player_by_object( repaired_objp );
9664                                 if ( pnum != -1 ) {
9665                                         Net_players[pnum].player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());
9666
9667                                         // squad war
9668                                         multi_team_maybe_add_score(-(int)(REPAIR_PENALTY * scoring_get_scale_factor()), Net_players[pnum].p_info.team);
9669                                 } else {
9670                                         nprintf(("Network", "Couldn't find player for ship %s for repair penalty\n", Ships[repaired_objp->instance].ship_name));
9671                                 }
9672                                 */
9673                         }
9674                 }
9675                 break;
9676
9677         case REPAIR_INFO_BROKEN:
9678                 aip->ai_flags &= ~AIF_BEING_REPAIRED;
9679                 aip->ai_flags |= AIF_AWAITING_REPAIR;
9680                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9681                 break;
9682
9683         case REPAIR_INFO_END:
9684                 // when only awaiting repair, and the repair is ended, then set dock_objnum to -1.
9685                 if ( aip->ai_flags & AIF_AWAITING_REPAIR ){
9686                         aip->dock_objnum = -1;
9687                 }
9688                 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9689                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9690                 break;
9691
9692         case REPAIR_INFO_QUEUE:
9693                 aip->ai_flags |= AIF_AWAITING_REPAIR;
9694                 if ( aip == Player_ai ){
9695                         hud_support_view_start();
9696                 }
9697                 stamp = timestamp(-1);
9698                 break;
9699
9700         case REPAIR_INFO_ABORT:
9701         case REPAIR_INFO_KILLED:
9702                 // 5/4/98 -- MWA -- Need to set dock objnum to -1 to let code know this guy who was getting
9703                 // repaired (or queued for repair), isn't really going to be docked with anyone anymore.
9704                 aip->dock_objnum = -1;
9705                 aip->ai_flags &= ~AIF_DOCKED;
9706                 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9707                 if (repair_objp != NULL) {
9708                         repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9709                         repair_aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9710                 }               
9711
9712                 if ( p_index >= 0 ) {
9713                         hud_support_view_abort();
9714
9715                         // send appropriate message to player here
9716                         if ( how == REPAIR_INFO_KILLED ){
9717                                 message_send_builtin_to_player( MESSAGE_SUPPORT_KILLED, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9718                         } else {
9719                                 if ( repair_objp ){
9720                                         message_send_builtin_to_player( MESSAGE_REPAIR_ABORTED, &Ships[repair_objp->instance], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9721                                 }
9722                         }
9723                 }
9724
9725                 // add log entry if this is a player
9726                 if ( repaired_objp->flags & OF_PLAYER_SHIP ){
9727                         mission_log_add_entry(LOG_PLAYER_REARM_ABORT, Ships[repaired_objp->instance].ship_name, NULL);
9728                 }
9729
9730                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9731                 break;
9732
9733         case REPAIR_INFO_COMPLETE:
9734                 // clear the being repaired flag -- and 
9735                 if ( p_index >= 0 ) {
9736                         Assert( repair_objp );
9737                         
9738                         hud_support_view_stop();                        
9739
9740                         message_send_builtin_to_player(MESSAGE_REPAIR_DONE, &Ships[repair_objp->instance], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, p_index, -1);
9741                 }
9742                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9743                 break;
9744
9745         case REPAIR_INFO_ONWAY:
9746                 // need to set the dock_signature so that clients in multiplayer games rearm correctly
9747                 Assert( repair_objp );
9748                 aip->dock_signature = repair_objp->signature; 
9749                 aip->dock_objnum = OBJ_INDEX(repair_objp);
9750                 stamp = timestamp(-1);
9751                 break;
9752
9753         default:
9754                 Int3();                 // bogus type of repair info
9755         }
9756
9757         if (repair_objp){
9758                 Ai_info[Ships[repair_objp->instance].ai_index].warp_out_timestamp = stamp;
9759         }
9760
9761         // repair_objp might be NULL is we are cleaning up this mode because of the support ship
9762         // getting killed.
9763         if ( repair_objp ) {
9764                 aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9765                 switch ( how ) {
9766                 case REPAIR_INFO_ONWAY:
9767                         Assert( repaired_objp != NULL );
9768                         aip->goal_objnum = OBJ_INDEX(repaired_objp);
9769                         aip->ai_flags |= AIF_REPAIRING;
9770                         break;
9771
9772                 case REPAIR_INFO_BROKEN:
9773                         break;
9774
9775                 case REPAIR_INFO_END:
9776                 case REPAIR_INFO_ABORT:
9777                 case REPAIR_INFO_KILLED:
9778                         if ( how == REPAIR_INFO_ABORT )
9779                                 aip->goal_objnum = -1;
9780
9781                         aip->ai_flags &= ~AIF_REPAIRING;
9782                         break;
9783                         
9784                 case REPAIR_INFO_QUEUE:
9785                         ai_add_rearm_goal( repaired_objp, repair_objp );
9786                         break;
9787
9788                 case REPAIR_INFO_BEGIN:
9789                 case REPAIR_INFO_COMPLETE:
9790                         break;
9791
9792                 default:
9793                         Int3();         // bogus type of repair info
9794                 }
9795         }
9796
9797         multi_maybe_send_repair_info( repaired_objp, repair_objp, how );
9798 }
9799
9800 //      Cleanup AI stuff for when a ship was supposed to dock with another, but the ship
9801 //      it was supposed to dock with is no longer valid.
9802 void ai_cleanup_dock_mode(ai_info *aip, ship *shipp)
9803 {
9804         object *objp;
9805
9806         objp = &Objects[shipp->objnum];
9807         aip->mode = AIM_NONE;
9808
9809         if (aip->ai_flags & AIF_REPAIRING) {
9810                 Assert( aip->goal_objnum != -1 );
9811                 ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], &Objects[shipp->objnum], REPAIR_INFO_KILLED );
9812         } else if ( aip->ai_flags & AIF_BEING_REPAIRED ) {
9813                 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9814                 Assert( aip->dock_objnum != -1 );
9815                 ai_do_objects_repairing_stuff( &Objects[shipp->objnum], &Objects[aip->dock_objnum], REPAIR_INFO_KILLED );
9816         } else if ( aip->ai_flags & AIF_AWAITING_REPAIR ) {
9817                 // need to find the support ship that has me as a goal_objnum
9818                 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9819                 // MWA -- 3/38/98  Check to see if this guy is queued for a support ship, or there is already
9820                 // one in the mission
9821                 if ( mission_is_repair_scheduled(objp) ) {
9822                         mission_remove_scheduled_repair( objp );                        // this function will notify multiplayer clients.
9823                 } else {
9824                         if ( aip->dock_objnum != -1 )
9825                                 ai_do_objects_repairing_stuff( objp, &Objects[aip->dock_objnum], REPAIR_INFO_ABORT );
9826                         else
9827                                 ai_do_objects_repairing_stuff( objp, NULL, REPAIR_INFO_ABORT );
9828                 }
9829         }
9830
9831         if ( aip->ai_flags & AIF_DOCKED ) {
9832                 ai_info *other_aip;
9833
9834                 Assert( aip->dock_objnum != -1 );
9835
9836                 // if docked, and the dock_objnum is not undocking, force them to near last stage
9837                 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
9838                 if ( (other_aip->mode == AIM_DOCK) && (other_aip->submode < AIS_UNDOCK_3) )
9839                         other_aip->submode = AIS_UNDOCK_3;
9840                 ai_do_objects_undocked_stuff( objp, &Objects[aip->dock_objnum] );
9841         }
9842 }
9843
9844 /*
9845 //      Make dockee_objp shake a bit due to docking.
9846 void ai_dock_shake(object *docker_objp, object *dockee_objp)
9847 {
9848         vector  tangles;
9849         matrix  rotmat, tmp;
9850         float           scale;
9851         angles  *ap;
9852
9853         scale = 0.25f;          //      Compute this based on mass and speed at time of docking.
9854
9855         vm_vec_rand_vec_quick(&tangles);
9856         vm_vec_scale(&tangles, scale);
9857
9858         ap = (angles *) &tangles;
9859
9860         vm_angles_2_matrix(&rotmat, ap);
9861         vm_matrix_x_matrix( &tmp, &dockee_objp->orient, &rotmat );
9862         dockee_objp->orient = tmp;
9863
9864         vm_orthogonalize_matrix(&dockee_objp->orient);
9865
9866         dock_orient_and_approach(docker_objp, dockee_objp, DOA_DOCK_STAY);
9867
9868 }
9869 */
9870
9871 //      Make Pl_objp point at aip->goal_point.
9872 void ai_still()
9873 {
9874         ship    *shipp;
9875         ai_info *aip;
9876
9877         Assert(Pl_objp->type == OBJ_SHIP);
9878         Assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_OBJECTS));
9879
9880         shipp = &Ships[Pl_objp->instance];
9881         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
9882
9883         aip = &Ai_info[shipp->ai_index];
9884
9885         turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
9886 }
9887
9888 //      Make *Pl_objp stay near another ship.
9889 void ai_stay_near()
9890 {
9891         ai_info *aip;
9892         int             goal_objnum;
9893
9894         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
9895
9896         goal_objnum = aip->goal_objnum;
9897
9898         if ((goal_objnum < 0) || (Objects[goal_objnum].type != OBJ_SHIP) || (Objects[goal_objnum].signature != aip->goal_signature)) {
9899                 aip->mode = AIM_NONE;
9900         } else {
9901                 float           dist, max_dist, scale;
9902                 vector  rand_vec, goal_pos, vec_to_goal;
9903                 object  *goal_objp;
9904
9905                 goal_objp = &Objects[goal_objnum];
9906
9907                 //      Make not all ships pursue same point.
9908                 static_randvec(Pl_objp-Objects, &rand_vec);
9909
9910                 //      Make sure point is in front hemisphere (relative to Pl_objp's position.
9911                 vm_vec_sub(&vec_to_goal, &goal_objp->pos, &Pl_objp->pos);
9912                 if (vm_vec_dot(&rand_vec, &vec_to_goal) > 1.0f) {
9913                         vm_vec_negate(&rand_vec);
9914                 }
9915
9916                 //      Scale the random vector by an amount proportional to the distance from Pl_objp to the true goal.
9917                 dist = vm_vec_dist_quick(&goal_objp->pos, &Pl_objp->pos);
9918                 max_dist = aip->stay_near_distance;
9919                 scale = dist - max_dist/2;
9920                 if (scale < 0.0f)
9921                         scale = 0.0f;
9922
9923                 vm_vec_scale_add(&goal_pos, &goal_objp->pos, &rand_vec, scale);
9924
9925                 if (max_dist < Pl_objp->radius + goal_objp->radius + 25.0f)
9926                         max_dist = Pl_objp->radius + goal_objp->radius + 25.0f;
9927
9928                 if (dist > max_dist) {
9929                         turn_towards_point(Pl_objp, &goal_pos, NULL, 0.0f);
9930                         accelerate_ship(aip, dist / max_dist - 0.8f);
9931                 }
9932         
9933         }
9934
9935 }
9936
9937 //      Warn player if dock path is obstructed.
9938 int maybe_dock_obstructed(object *cur_objp, object *goal_objp, int big_only_flag)
9939 {
9940         vector  *goalpos, *curpos;
9941         float           radius;
9942         ai_info *aip;
9943         int             collide_objnum;
9944
9945         aip = &Ai_info[Ships[cur_objp->instance].ai_index];
9946
9947         Ai_info[Ships[goal_objp->instance].ai_index].ai_flags &= ~AIF_REPAIR_OBSTRUCTED;
9948
9949         if (goal_objp != Player_obj)
9950                 return -1;
9951
9952         curpos = &cur_objp->pos;
9953         radius = cur_objp->radius;
9954         goalpos = &Path_points[aip->path_cur].pos;
9955         collide_objnum = pp_collide_any(curpos, goalpos, radius, cur_objp, goal_objp, big_only_flag);
9956
9957         if (collide_objnum != -1)
9958                 Ai_info[Ships[goal_objp->instance].ai_index].ai_flags |= AIF_REPAIR_OBSTRUCTED;
9959
9960         return collide_objnum;
9961 }
9962
9963
9964 int Dock_path_warning_given = 0;
9965
9966 //      Docking behavior.
9967 //      Approach a ship, follow path to docking platform, approach platform, after awhile,
9968 //      undock.
9969 void ai_dock()
9970 {
9971         ship                    *shipp = &Ships[Pl_objp->instance];
9972         ai_info         *aip = &Ai_info[shipp->ai_index];
9973         object          *goal_objp;
9974         ship_info       *sip = &Ship_info[shipp->ship_info_index];
9975
9976         //      Make sure object we're supposed to dock with still exists.
9977         if ((aip->goal_objnum == -1) || (Objects[aip->goal_objnum].signature != aip->goal_signature)) {
9978                 ai_cleanup_dock_mode(aip, shipp);
9979                 return;
9980         }
9981
9982         goal_objp = &Objects[aip->goal_objnum];
9983
9984         //      For docking submodes (ie, not undocking), follow path.  Once at second last
9985         //      point on path (point just before point on dock platform), orient into position.
9986         // For undocking, first mode pushes docked ship straight back from docking point
9987         // second mode turns ship and moves to point on docking radius
9988         switch (aip->submode) {
9989
9990                 //      This mode means to find the path to the docking point.
9991         case AIS_DOCK_0:
9992                 //aip->path_start = -1;
9993                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
9994                 ai_path();
9995                 if (!Dock_path_warning_given && (aip->path_length < 4)) {
9996                         Warning( LOCATION, "Ship '%s' has only %i points on dock path.  Docking will look strange.  Contact Adam.", shipp->ship_name, aip->path_length );
9997                         Dock_path_warning_given = 1;            //      This is on a mission-wide basis, but it's just a hack for now...
9998                 }
9999
10000                 aip->submode = AIS_DOCK_1;
10001                 aip->path_start = -1;
10002                 aip->submode_start_time = Missiontime;
10003                 break;
10004
10005                 //      This mode means to follow the path until just before the end.
10006         case AIS_DOCK_1: {
10007                 float   dist;
10008                 int     r;
10009
10010                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp, 1)) != -1) {
10011                         int     r1;
10012                         if ((r1 = maybe_avoid_big_ship(Pl_objp, goal_objp, aip, &goal_objp->pos, 7.0f)) != 0) {
10013                                 nprintf(("AI", "Support ship %s avoiding large ship %s\n", Ships[Pl_objp->instance].ship_name, Ships[Objects[r1].instance].ship_name));
10014                                 break;
10015                         } /*else {
10016                                 nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10017                                 accelerate_ship(aip, 0.0f);
10018                                 aip->submode = AIS_DOCK_0;
10019                         } */
10020                 } //else {
10021                 {
10022                         dist = ai_path();
10023                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10024                         //nprintf(("AI", "Dock 1: Frame: %i, goal point = %i, dist = %7.3f\n", Framecount, aip->path_cur-aip->path_start, dist));
10025
10026                         if (aip->path_cur-aip->path_start >= aip->path_length-1) {              //      If got this far, advance no matter what.
10027                                 aip->submode = AIS_DOCK_2;
10028                                 aip->submode_start_time = Missiontime;
10029                                 aip->path_cur--;
10030                                 Assert(aip->path_cur-aip->path_start >= 0);
10031                         } else if (aip->path_cur-aip->path_start >= aip->path_length-2) {
10032                                 if (Pl_objp->phys_info.speed > goal_objp->phys_info.speed + 1.5f) {
10033                                         set_accel_for_target_speed(Pl_objp, goal_objp->phys_info.speed);
10034                                 } else {
10035                                         aip->submode = AIS_DOCK_2;
10036                                         aip->submode_start_time = Missiontime;
10037                                 }
10038                         }
10039                 }
10040                 break;
10041                                           }
10042         //      This mode means to drag oneself right to the second last point on the path.
10043         //      Path code allows it to overshoot.
10044         case AIS_DOCK_2: {
10045                 float           dist;
10046                 int     r;
10047
10048                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10049                         nprintf(("AI", "Dock 2: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10050                         accelerate_ship(aip, 0.0f);
10051                         aip->submode = AIS_DOCK_1;
10052                 } else {
10053                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10054                         dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_APPROACH);
10055                         Assert(dist != UNINITIALIZED_VALUE);
10056
10057                         if (dist == DOCK_BACKUP_RETURN_VAL) {
10058                                 int path_num;
10059                                 aip->submode = AIS_DOCK_1;
10060                                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
10061                                 Assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
10062                                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
10063                                 break;
10064                         }
10065
10066                         //nprintf(("AI", "Dock 2: dist = %7.3f\n", vm_vec_dist_quick(&Pl_objp->pos, &goal_point)));
10067                         float   tolerance;
10068                         if (Objects[aip->goal_objnum].flags & OF_PLAYER_SHIP)
10069                                 tolerance = 6*flFrametime + 1.0f;
10070                         else
10071                                 tolerance = 4*flFrametime + 0.5f;
10072
10073                         if ( dist < tolerance) {
10074                                 aip->submode = AIS_DOCK_3;
10075                                 aip->submode_start_time = Missiontime;
10076                                 aip->path_cur++;
10077                         }
10078                 }
10079                 break;
10080                                                   }
10081
10082         case AIS_DOCK_3:
10083         case AIS_DOCK_3A:
10084                 {
10085                 Assert(aip->goal_objnum != -1);
10086                 int     r;
10087
10088                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10089                         nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10090                         accelerate_ship(aip, 0.0f);
10091                         aip->submode = AIS_DOCK_2;
10092                 } else {
10093
10094                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10095                         float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10096                         Assert(dist != UNINITIALIZED_VALUE);
10097
10098                         if (dist == DOCK_BACKUP_RETURN_VAL) {
10099                                 aip->submode = AIS_DOCK_2;
10100                                 break;
10101                         }
10102
10103                         //nprintf(("AI", "Dock 3: dist = %7.3f\n", dist));
10104
10105                         if (dist < 2*flFrametime * (1.0f + fl_sqrt(goal_objp->phys_info.speed))) {
10106                                 // - Removed by MK on 11/7/97, causes errors for ships docked at mission start: maybe_recreate_path(Pl_objp, aip, 1);
10107                                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10108                                 Assert(dist != UNINITIALIZED_VALUE);
10109
10110                                 physics_ship_init(Pl_objp);
10111
10112                                 ai_do_objects_docked_stuff( Pl_objp, goal_objp );
10113
10114                                 if (aip->submode == AIS_DOCK_3) {
10115                                         snd_play_3d( &Snds[SND_DOCK_ATTACH], &Pl_objp->pos, &View_position );
10116                                         hud_maybe_flash_docking_text(Pl_objp);
10117                                         // ai_dock_shake(Pl_objp, goal_objp);
10118
10119                                         if ((Pl_objp == Player_obj) || (goal_objp == Player_obj))
10120                                                 joy_ff_docked();  // shake player's joystick a little
10121                                 }
10122
10123                                 //      If this ship is repairing another ship...
10124                                 if (aip->ai_flags & AIF_REPAIRING) {
10125                                         aip->submode = AIS_DOCK_4;                      //      Special rearming only dock mode.
10126                                         aip->submode_start_time = Missiontime;
10127                                 } else {
10128                                         aip->submode = AIS_DOCK_4A;
10129                                         aip->submode_start_time = Missiontime;
10130                                 }
10131                         }
10132                 }
10133                 break;
10134                 }
10135
10136                 //      Yes, we just sit here.  We wait for further orders.  No, it's not a bug.
10137         case AIS_DOCK_4A:
10138                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10139                 //nprintf(("AI", "."));
10140                 if (aip->active_goal >= 0) {
10141                         mission_log_add_entry(LOG_SHIP_DOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10142
10143                         if (aip->goals[aip->active_goal].ai_mode == AI_GOAL_DOCK) {
10144                                 ai_mission_goal_complete( aip );                                        // Note, this calls ai_set_default_behavior().
10145                         } 
10146                 } else {        //      Can happen for initially docked ships.
10147                         ai_do_default_behavior( &Objects[Ships[aip->shipnum].objnum] );         // do the default behavior
10148                 }
10149                 
10150                 break;
10151
10152         case AIS_DOCK_4: {
10153                 //      This mode is only for rearming/repairing.
10154                 //      The ship that is performing the rearm enters this mode after it docks.
10155                 Assert((aip->goal_objnum >= -1) && (aip->goal_objnum < MAX_OBJECTS));
10156
10157                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10158                 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10159                 Assert(dist != UNINITIALIZED_VALUE);
10160
10161                 object  *goal_objp = &Objects[aip->goal_objnum];
10162                 Assert(goal_objp->type == OBJ_SHIP);
10163                 ship                    *goal_shipp = &Ships[goal_objp->instance];              
10164                 ai_info         *goal_aip = &Ai_info[goal_shipp->ai_index];
10165
10166                 //nprintf(("AI", "Dock 4: dist = %7.3f\n", dist));
10167
10168                 //      Make sure repair has not broken off.
10169                 if (dist > 5.0f) {      //      Oops, too far away!
10170                         if ( goal_aip->ai_flags & AIF_BEING_REPAIRED )
10171                                 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BROKEN);
10172
10173                         if (dist > Pl_objp->radius*2 + goal_objp->radius*2) {
10174                                 //      Got real far away from goal, so move back a couple modes and try again.
10175                                 aip->submode = AIS_DOCK_2;
10176                                 aip->submode_start_time = Missiontime;
10177                         }
10178                 } else {
10179                         if ( goal_aip->ai_flags & AIF_AWAITING_REPAIR )
10180                                 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BEGIN );
10181                 }
10182
10183                 break;
10184                                                   }
10185
10186         case AIS_UNDOCK_0: {
10187                 int path_num;
10188                 //      First stage of undocking.
10189
10190                 //nprintf(("AI", "Undock 0:\n"));
10191
10192                 aip->submode = AIS_UNDOCK_1;
10193                 aip->submode_start_time = Missiontime;
10194                 if (aip->dock_objnum == -1) {
10195                         aip->submode = AIS_UNDOCK_3;
10196                 } else {
10197
10198                         // set up the path points for the undocking procedure.  dock_path_index member should
10199                         // have gotten set in the docking code.
10200                         Assert( aip->dock_path_index != -1 );
10201                         path_num = ai_return_path_num_from_dockbay(goal_objp, aip->dock_path_index);
10202                         ai_find_path(Pl_objp, goal_objp-Objects, path_num, 0);
10203
10204                         // Play a ship docking detach sound
10205                         snd_play_3d( &Snds[SND_DOCK_DETACH], &Pl_objp->pos, &View_position );
10206                 }
10207                 break;
10208                                                          }
10209         case AIS_UNDOCK_1: {
10210                 //      Using thrusters, exit from dock station to nearest next dock path point.
10211                 float   dist;
10212                 
10213                 //nprintf(("AI", "Undock 1: time in this mode = %7.3f\n", f2fl(Missiontime - aip->submode_start_time)));
10214
10215                 if (Missiontime - aip->submode_start_time < REARM_BREAKOFF_DELAY) {
10216                         break;          //      Waiting for one second to elapse to let detach sound effect play out.
10217                 }
10218                 else {  // AL - added 05/16/97.  Hack to play depart sound.  Will probably take out.
10219                                         // Assumes that the submode_start_time is not used for AIS_UNDOCK_1 anymore
10220                         if ( aip->submode_start_time != 0 )
10221                                 snd_play_3d( &Snds[SND_DOCK_DEPART], &Pl_objp->pos, &View_position );
10222                         aip->submode_start_time = 0;
10223                 }
10224
10225                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_1);
10226                 Assert(dist != UNINITIALIZED_VALUE);
10227
10228                 float dist_to_dock_obj = vm_vec_dist_quick(&Pl_objp->pos, &Objects[aip->goal_objnum].pos);
10229
10230                 //      Move to within 0.1 units of second last point on path before orienting, or just plain far away from docked-to ship.
10231                 //      This allows undock to complete if first ship flies away.
10232                 if ((dist < 2*flFrametime) || (dist_to_dock_obj > 2*Pl_objp->radius)) {
10233                         aip->submode = AIS_UNDOCK_2;
10234                         aip->submode_start_time = Missiontime;
10235                 }
10236                 break;
10237                                                          }
10238         case AIS_UNDOCK_2: {
10239                 float dist;
10240                 ai_info *other_aip;
10241
10242                 // get pointer to docked object's aip to reset flags, etc
10243                 Assert( aip->dock_objnum != -1 );
10244                 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
10245
10246                 //      Second stage of undocking.
10247                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_2);
10248                 Assert(dist != UNINITIALIZED_VALUE);
10249
10250
10251                 //nprintf(("AI", "Undock 2: dist = %7.3f\n", dist));
10252                 
10253                 //      If at goal point, or quite far away from dock object
10254                 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) ) {
10255                         // reset the dock flags.  If rearm/repair, reset rearm repair flags for those ships as well.
10256                         if ( sip->flags & SIF_SUPPORT ) {
10257                                 ai_do_objects_repairing_stuff( &Objects[aip->dock_objnum], Pl_objp, REPAIR_INFO_END );
10258                         }
10259
10260                         // clear out flags for AIF_DOCKED for both objects.
10261                         ai_do_objects_undocked_stuff( Pl_objp, goal_objp );
10262                         physics_ship_init(Pl_objp);
10263                         aip->submode = AIS_UNDOCK_3;                            //      The do-nothing mode, until another order is issued
10264
10265                         //aip->ai_flags &= ~AIF_DOCKED;         //      @MK, 9/18/97
10266                         //other_aip->ai_flags &= ~AIF_DOCKED;
10267                         //aip->dock_objnum = -1;                                        // invalidate who obj is docked with
10268                         //other_aip->dock_objnum = -1;                  // MWA 10/07/97 invalide docked objects dock_objnum value as well
10269
10270                         // don't add undock log entries for support ships.
10271                         if ( !(sip->flags & SIF_SUPPORT) )
10272                                 mission_log_add_entry(LOG_SHIP_UNDOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10273
10274                 }
10275                 break;
10276                 }
10277         case AIS_UNDOCK_3: {
10278                 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_3);
10279                 Assert(dist != UNINITIALIZED_VALUE);
10280
10281                 if (dist < Pl_objp->radius/2 + 5.0f) {
10282                         aip->submode = AIS_UNDOCK_4;
10283                 }
10284
10285                 // possible that this flag hasn't been cleared yet.  When aborting a rearm, this submode might
10286                 // be entered directly.
10287                 if ( (sip->flags & SIF_SUPPORT) && (aip->ai_flags & AIF_REPAIRING) ) {
10288                         ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], Pl_objp, REPAIR_INFO_ABORT );
10289                 }
10290
10291                 break;
10292                                                  }
10293         case AIS_UNDOCK_4: {
10294                 ai_info *other_aip;
10295
10296                 // MWA 10/07/97  I'm slightly confused by the dual use of goal_objnum and dock_objnum.  Seems to me
10297                 // that goal_objnum and dock_objnum are the same through this whole docking/undocking process, although
10298                 // I could be wrong.  dock_objnum was reset in undock_2 submode so try to use goal_objnum here to
10299                 // get other ships ai_info pointer
10300                 Assert( aip->goal_objnum != -1 );
10301                 other_aip = &Ai_info[Ships[Objects[aip->goal_objnum].instance].ai_index];
10302
10303                 aip->mode = AIM_NONE;
10304                 aip->dock_path_index = -1;              // invalidate the docking path index
10305
10306                 // these flags should have been cleared long ago!
10307                 // Get Allender if you hit one of these!!!!!
10308                 // removed by allender on 2/16 since a ship may be docked with some other ship, but still be the
10309                 // goal_objnum of this ship ending it's undocking mode.
10310                 //Assert( !(aip->ai_flags & AIF_DOCKED) );
10311                 //Assert( !(other_aip->ai_flags & AIF_DOCKED) );
10312                 //Assert( !(aip->ai_flags & AIF_REPAIRING) );
10313                 //Assert( !(other_aip->ai_flags & AIF_BEING_REPAIRED) );
10314                 //Assert( !(other_aip->ai_flags & AIF_AWAITING_REPAIR) );
10315
10316                 // only call mission goal complete if this was indeed an undock goal
10317                 if ( aip->active_goal > -1 ) {
10318                         if ( aip->goals[aip->active_goal].ai_mode == AI_GOAL_UNDOCK )
10319                                 ai_mission_goal_complete( aip );                        // this call should reset the AI mode
10320                         //else
10321                         //      aip->active_goal = -1;                                          // this ensures that this ship might get new goal
10322                 }
10323
10324                 break;
10325                                                          }
10326         default:
10327                 Int3(); //      Error, bogus submode
10328         }
10329
10330 }
10331
10332 // TURRET BEGIN
10333
10334 //      Given an object and a turret on that object, return the global position and forward vector
10335 //      of the turret.   The gun normal is the unrotated gun normal, (the center of the FOV cone), not
10336 // the actual gun normal given using the current turret heading.  But it _is_ rotated into the model's orientation
10337 //      in global space.
10338 void ship_get_global_turret_info(object *objp, model_subsystem *tp, vector *gpos, vector *gvec)
10339 {
10340         matrix  m;
10341         vm_copy_transpose_matrix(&m, &objp->orient);
10342 //      vm_vec_rotate(gpos, &tp->turret_avg_firing_point, &m);
10343         vm_vec_rotate(gpos, &tp->pnt, &m);
10344         vm_vec_add2(gpos, &objp->pos);
10345         vm_vec_rotate(gvec, &tp->turret_norm, &m);      
10346 }
10347
10348 // Given an object and a turret on that object, return the actual firing point of the gun
10349 // and its normal.   This uses the current turret angles.  We are keeping track of which
10350 // gun to fire next in the ship specific info for this turret subobject.  Use this info
10351 // to determine which position to fire from next.
10352 //      Stuffs:
10353 //              *gpos: absolute position of gun firing point
10354 //              *gvec: vector fro *gpos to *targetp
10355 void ship_get_global_turret_gun_info(object *objp, ship_subsys *ssp, vector *gpos, vector *gvec, int use_angles, vector *targetp)
10356 {
10357         vector * gun_pos;
10358         model_subsystem *tp = ssp->system_info;
10359
10360         ship_model_start(objp);
10361
10362         gun_pos = &tp->turret_firing_point[ssp->turret_next_fire_pos % tp->turret_num_firing_points];
10363
10364         model_find_world_point(gpos, gun_pos, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10365
10366         if (use_angles)
10367                 model_find_world_dir(gvec, &tp->turret_norm, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10368         else {
10369                 //vector        gun_pos2;
10370                 //vm_vec_add(&gun_pos2, gpos, gun_pos);
10371                 vm_vec_normalized_dir(gvec, targetp, gpos);
10372         }
10373
10374         ship_model_stop(objp);  
10375 }
10376
10377 //      Rotate a turret towards an enemy.
10378 //      Return TRUE if caller should use angles in subsequent rotations.
10379 //      Some obscure model thing only John Slagel knows about.
10380 //      Sets predicted enemy position.
10381 //      If the turret (*ss) has a subsystem targeted, the subsystem is used as the predicted point.
10382 int aifft_rotate_turret(ship *shipp, int parent_objnum, ship_subsys *ss, object *objp, object *lep, vector *predicted_enemy_pos, vector *gvec)
10383 {
10384         if (ss->turret_enemy_objnum != -1)      {
10385                 model_subsystem *tp = ss->system_info;
10386                 vector  gun_pos, gun_vec;
10387                 float           weapon_speed;
10388                 float           weapon_system_strength;
10389
10390                 //      weapon_system_strength scales time enemy in range in 0..1.  So, the lower this is, the worse the aiming will be.
10391                 weapon_system_strength = ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS);
10392
10393                 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gun_pos, &gun_vec);
10394
10395                 weapon_speed = Weapon_info[tp->turret_weapon_type].max_speed;
10396                 float weapon_travel_dist = weapon_speed * Weapon_info[tp->turret_weapon_type].lifetime;
10397
10398                 vector  enemy_point;
10399                 if (ss->targeted_subsys != NULL) {
10400                         if (ss->turret_enemy_objnum != -1) {
10401                                 vm_vec_unrotate(&enemy_point, &ss->targeted_subsys->system_info->pnt, &Objects[ss->turret_enemy_objnum].orient);
10402                                 vm_vec_add2(&enemy_point, &Objects[ss->turret_enemy_objnum].pos);
10403                         }
10404                 } else {
10405                         if ((lep->type == OBJ_SHIP) && (Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
10406                                 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));
10407                         } else {
10408                                 enemy_point = lep->pos;
10409                         }
10410                 }
10411
10412                 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);
10413
10414                 if (weapon_system_strength < 0.7f) {
10415                         vector  rand_vec;
10416
10417                         static_randvec(Missiontime >> 18, &rand_vec);   //      Return same random number for two seconds.
10418                         //      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.
10419                         vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, (1.0f - weapon_system_strength)*1.5f * lep->radius);
10420                 }
10421
10422                 vector  v2e;
10423                 vm_vec_normalized_dir(&v2e, predicted_enemy_pos, &gun_pos);
10424                 if (vm_vec_dot(&v2e, gvec) > tp->turret_fov) {
10425                         int     rval;
10426
10427                         rval = model_rotate_gun(shipp->modelnum, ss->system_info, &Objects[parent_objnum].orient, 
10428                                 &ss->submodel_info_1.angs, &ss->submodel_info_2.angs,
10429                                 &Objects[parent_objnum].pos, predicted_enemy_pos);
10430                 }
10431         }
10432
10433         return 0;
10434 }
10435
10436 //      Determine if subsystem *enemy_subsysp is hittable from objp.
10437 //      If so, return dot product of vector from point abs_gunposp to *enemy_subsysp
10438 float   aifft_compute_turret_dot(object *objp, object *enemy_objp, vector *abs_gunposp, ship_subsys *turret_subsysp, ship_subsys *enemy_subsysp)
10439 {
10440         float   dot_out;
10441         vector  subobj_pos, vector_out;
10442
10443         vm_vec_unrotate(&subobj_pos, &enemy_subsysp->system_info->pnt, &enemy_objp->orient);
10444         vm_vec_add2(&subobj_pos, &enemy_objp->pos);
10445
10446         if (ship_subsystem_in_sight(enemy_objp, enemy_subsysp, abs_gunposp, &subobj_pos, 1, &dot_out, &vector_out)) {
10447                 vector  turret_norm;
10448
10449                 vm_vec_rotate(&turret_norm, &turret_subsysp->system_info->turret_norm, &objp->orient);
10450                 return vm_vec_dot(&turret_norm, &vector_out);
10451         } else
10452                 return -1.0f;
10453
10454 }
10455
10456 #define MAX_AIFFT_TURRETS                       60
10457 ship_subsys *aifft_list[MAX_AIFFT_TURRETS];
10458 float aifft_rank[MAX_AIFFT_TURRETS];
10459 int aifft_list_size = 0;
10460 int aifft_max_checks = 5;
10461 DCF(mf, "")
10462 {
10463         dc_get_arg(ARG_INT);
10464         aifft_max_checks = Dc_arg_int;
10465 }
10466
10467
10468 //      Pick a subsystem to attack on enemy_objp.
10469 //      Only pick one if enemy_objp is a big ship or a capital ship.
10470 //      Returns dot product from turret to subsystem in *dot_out
10471 ship_subsys *aifft_find_turret_subsys(object *objp, ship_subsys *ssp, object *enemy_objp, float *dot_out)
10472 {
10473         ship    *eshipp, *shipp;
10474         ship_info       *esip;
10475         ship_subsys     *best_subsysp = NULL;
10476         float dot;
10477
10478         Assert(enemy_objp->type == OBJ_SHIP);
10479
10480         eshipp = &Ships[enemy_objp->instance];
10481         esip = &Ship_info[eshipp->ship_info_index];
10482
10483         shipp = &Ships[objp->instance];
10484
10485         float   best_dot = 0.0f;
10486         *dot_out = best_dot;
10487
10488         //      Compute absolute gun position.
10489         vector  abs_gun_pos;
10490         vm_vec_unrotate(&abs_gun_pos, &ssp->system_info->pnt, &objp->orient);
10491         vm_vec_add2(&abs_gun_pos, &objp->pos);
10492
10493         //      Only pick a turret to attack on large ships.
10494         if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
10495                 return best_subsysp;
10496
10497         // Make sure big or huge ship *actually* has subsystems  (ie, knossos)
10498         if (esip->n_subsystems == 0) {
10499                 return best_subsysp;
10500         }
10501
10502         // first build up a list subsystems to traverse
10503         ship_subsys     *pss;
10504         aifft_list_size = 0;
10505         for ( pss = GET_FIRST(&eshipp->subsys_list); pss !=END_OF_LIST(&eshipp->subsys_list); pss = GET_NEXT(pss) ) {
10506                 model_subsystem *psub = pss->system_info;
10507
10508                 // if we've reached max turrets bail
10509                 if(aifft_list_size >= MAX_AIFFT_TURRETS){
10510                         break;
10511                 }
10512
10513                 // Don't process destroyed objects
10514                 if ( pss->current_hits <= 0.0f ){
10515                         continue;
10516                 }
10517                 
10518                 switch (psub->type) {
10519                 case SUBSYSTEM_WEAPONS:
10520                         aifft_list[aifft_list_size] = pss;
10521                         aifft_rank[aifft_list_size++] = 1.4f;
10522                         break;
10523
10524                 case SUBSYSTEM_TURRET:
10525                         aifft_list[aifft_list_size] = pss;
10526                         aifft_rank[aifft_list_size++] = 1.2f;
10527                         break;
10528
10529                 case SUBSYSTEM_SENSORS:
10530                 case SUBSYSTEM_ENGINE:
10531                         aifft_list[aifft_list_size] = pss;
10532                         aifft_rank[aifft_list_size++] = 1.0f;
10533                         break;
10534                 }
10535         }
10536
10537         // DKA:  6/28/99 all subsystems can be destroyed.
10538         //Assert(aifft_list_size > 0);
10539         if (aifft_list_size == 0) {
10540                 return best_subsysp;
10541         }
10542
10543         // determine a stride value so we're not checking too many turrets
10544         int stride = aifft_list_size > aifft_max_checks ? aifft_list_size / aifft_max_checks : 1;
10545         if(stride <= 0){
10546                 stride = 1;
10547         }
10548         int offset = (int)frand_range(0.0f, (float)(aifft_list_size % stride));
10549         int idx;
10550         for(idx=offset; idx<aifft_list_size; idx+=stride){
10551                 dot = aifft_compute_turret_dot(objp, enemy_objp, &abs_gun_pos, ssp, aifft_list[idx]);                   
10552
10553                 if (dot* aifft_rank[idx] > best_dot) {
10554                         best_dot = dot*aifft_rank[idx];
10555                         best_subsysp = aifft_list[idx];
10556                 }
10557         }
10558
10559         Assert(best_subsysp != &eshipp->subsys_list);
10560
10561         *dot_out = best_dot;
10562         return best_subsysp;
10563 }
10564
10565 // Set active weapon for turret
10566 void ai_turret_select_default_weapon(ship_subsys *turret)
10567 {
10568         ship_weapon *twp;
10569
10570         twp = &turret->weapons;
10571
10572         // If a primary weapon is available, select it
10573         if ( twp->num_primary_banks > 0 ) {
10574                 turret->system_info->turret_weapon_type = twp->primary_bank_weapons[0];
10575         } else if ( twp->num_secondary_banks > 0 ) {
10576                 turret->system_info->turret_weapon_type = twp->secondary_bank_weapons[0];
10577         }
10578 }
10579
10580 // return !0 if the specified target should scan for a new target, otherwise return 0
10581 int turret_should_pick_new_target(ship_subsys *turret)
10582 {
10583 //      int target_type;
10584
10585         if ( timestamp_elapsed(turret->turret_next_enemy_check_stamp) ) {
10586                 return 1;
10587         }
10588
10589         return 0;
10590
10591 /*
10592         if ( turret->turret_enemy_objnum == -1 ) {
10593                 return 1;
10594         }
10595                 
10596         target_type = Objects[turret->turret_enemy_objnum].type;
10597         if ( (target_type != OBJ_SHIP) && (target_type != OBJ_ASTEROID) ) {
10598                 return 1;
10599         }
10600
10601         return 0;
10602 */
10603 }
10604
10605 // Set the next fire timestamp for a turret, based on weapon type and ai class
10606 void turret_set_next_fire_timestamp(ship_subsys *turret, ai_info *aip)
10607 {
10608         float   wait;
10609         int     weapon_id;
10610
10611         weapon_id = turret->system_info->turret_weapon_type;
10612
10613         wait = Weapon_info[weapon_id].fire_wait * 1000.0f;
10614
10615         // make side even for team vs. team
10616         if ((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) {
10617                 // flak guns need to fire more rapidly
10618                 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10619                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10620                         wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10621                 } else {
10622                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10623                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10624                 }
10625         } else {
10626                 // flak guns need to fire more rapidly
10627                 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10628                         if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10629                                 wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10630                         } else {
10631                                 wait *= Ship_fire_delay_scale_hostile[Game_skill_level] * 0.5f;
10632                         }       
10633                         wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10634
10635                 } else if (Weapon_info[weapon_id].wi_flags & WIF_HUGE) {
10636                         // make huge weapons fire independently of team
10637                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10638                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10639                 } else {
10640                         // give team friendly an advantage
10641                         if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10642                                 wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10643                         } else {
10644                                 wait *= Ship_fire_delay_scale_hostile[Game_skill_level];
10645                         }       
10646                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10647                 }
10648         }
10649
10650         // vary wait time +/- 10%
10651         wait *= frand_range(0.9f, 1.1f);
10652         turret->turret_next_fire_stamp = timestamp((int) wait);
10653 }
10654
10655 // Decide  if a turret should launch an aspect seeking missile
10656 int turret_should_fire_aspect(ship_subsys *turret, float dot, int weapon_class)
10657 {
10658         weapon_info *wip;
10659
10660         wip = &Weapon_info[weapon_class];
10661
10662         if ( (dot > AICODE_TURRET_DUMBFIRE_ANGLE) && (turret->turret_time_enemy_in_range >= min(wip->min_lock_time,AICODE_TURRET_MAX_TIME_IN_RANGE)) ) {
10663                 return 1;
10664         }
10665
10666         return 0;
10667 }
10668
10669 // Update how long current target has been in this turrets range
10670 void turret_update_enemy_in_range(ship_subsys *turret, float seconds)
10671 {
10672         turret->turret_time_enemy_in_range += seconds;
10673
10674         if ( turret->turret_time_enemy_in_range < 0.0f ) {
10675                 turret->turret_time_enemy_in_range = 0.0f;
10676         }
10677
10678         if ( turret->turret_time_enemy_in_range > AICODE_TURRET_MAX_TIME_IN_RANGE ) {
10679                 turret->turret_time_enemy_in_range = AICODE_TURRET_MAX_TIME_IN_RANGE;
10680         }
10681 }
10682
10683
10684
10685 // Fire a weapon from a turret
10686 void turret_fire_weapon(ship_subsys *turret, int parent_objnum, vector *turret_pos, vector *turret_fvec, vector *predicted_pos = NULL)
10687 {
10688         matrix  turret_orient;
10689         int             turret_weapon_class, weapon_objnum;
10690         ai_info *parent_aip;
10691         ship            *parent_ship;
10692         beam_fire_info fire_info;
10693         float flak_range = 0.0f;
10694
10695         parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
10696         parent_ship = &Ships[Objects[parent_objnum].instance];
10697         turret_weapon_class = turret->system_info->turret_weapon_type;
10698
10699         if (check_ok_to_fire(parent_objnum, turret->turret_enemy_objnum, &Weapon_info[turret_weapon_class])) {
10700                 vm_vector_2_matrix(&turret_orient, turret_fvec, NULL, NULL);
10701                 turret->turret_last_fire_direction = *turret_fvec;
10702
10703                 // set next fire timestamp for the turret
10704                 turret_set_next_fire_timestamp(turret, parent_aip);
10705
10706                 // if this weapon is a beam weapon, handle it specially
10707                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM){
10708                         // if this beam isn't free to fire
10709                         if (!(turret->weapons.flags & SW_FLAG_BEAM_FREE)) {
10710                                 Int3(); // should never get this far
10711                                 return;
10712                         }
10713
10714                         // stuff beam firing info
10715                         memset(&fire_info, 0, sizeof(beam_fire_info));
10716                         fire_info.accuracy = 1.0f;
10717                         fire_info.beam_info_index = turret_weapon_class;
10718                         fire_info.beam_info_override = NULL;
10719                         fire_info.shooter = &Objects[parent_objnum];
10720                         fire_info.target = &Objects[turret->turret_enemy_objnum];
10721                         fire_info.target_subsys = NULL;
10722                         fire_info.turret = turret;
10723
10724                         // fire a beam weapon
10725                         beam_fire(&fire_info);
10726                 } else {
10727
10728                         // don't fire swarm, but set up swarm info
10729                         if (Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM) {
10730                                 turret_swarm_set_up_info(parent_objnum, turret, turret_weapon_class);
10731                                 return;
10732                         } else {
10733                                 weapon_objnum = weapon_create( turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10734                                 weapon_set_tracking_info(weapon_objnum, parent_objnum, turret->turret_enemy_objnum, 1, turret->targeted_subsys);                
10735                         }
10736
10737                         //nprintf(("AI", "Turret_time_enemy_in_range = %7.3f\n", ss->turret_time_enemy_in_range));              
10738                         if (weapon_objnum != -1) {
10739                                 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10740                                 // AL 1-6-97: Store pointer to turret subsystem
10741                                 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10742
10743                                 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10744                                         // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10745                                         if ( parent_objnum != OBJ_INDEX(Player_obj) ) {                                         
10746                                                 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], turret_pos, &View_position );                                          
10747                                         }
10748                                 }               
10749
10750                                 // if the gun is a flak gun
10751                                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){                       
10752                                         // show a muzzle flash
10753                                         flak_muzzle_flash(turret_pos, turret_fvec, turret_weapon_class);
10754
10755                                         // pick a firing range so that it detonates properly                    
10756                                         flak_pick_range(&Objects[weapon_objnum], predicted_pos, ship_get_subsystem_strength(parent_ship, SUBSYSTEM_WEAPONS));
10757
10758                                         // determine what that range was
10759                                         flak_range = flak_get_range(&Objects[weapon_objnum]);
10760                                 }
10761
10762                                 // in multiplayer (and the master), then send a turret fired packet.
10763                                 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10764                                         int subsys_index;
10765
10766                                         subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10767                                         Assert( subsys_index != -1 );
10768                                         if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){                       
10769                                                 send_flak_fired_packet( parent_objnum, subsys_index, weapon_objnum, flak_range );
10770                                         } else {
10771                                                 send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10772                                         }
10773                                 }
10774                         }
10775                 }
10776         } else {
10777                 float wait = 1000.0f * frand_range(0.9f, 1.1f);
10778                 turret->turret_next_fire_stamp = timestamp((int) wait);
10779         }
10780 }
10781
10782 void turret_swarm_fire_from_turret(ship_subsys *turret, int parent_objnum, int target_objnum, ship_subsys *target_subsys)
10783 {
10784         int turret_weapon_class, weapon_objnum;
10785         matrix turret_orient;
10786         vector turret_pos, turret_fvec;
10787
10788         // parent not alive, quick out.
10789         if (Objects[parent_objnum].type != OBJ_SHIP) {
10790                 return;
10791         }
10792
10793         //      change firing point
10794         ship_get_global_turret_gun_info(&Objects[parent_objnum], turret, &turret_pos, &turret_fvec, 1, NULL);
10795         turret->turret_next_fire_pos++;
10796
10797         // get class [index into Weapon_info array
10798         turret_weapon_class = turret->system_info->turret_weapon_type;
10799         Assert(Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM);
10800
10801         // make turret_orient from turret_fvec -- turret->turret_last_fire_direction
10802         vm_vector_2_matrix(&turret_orient, &turret_fvec, NULL, NULL);
10803
10804         // create weapon and homing info
10805         weapon_objnum = weapon_create(&turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10806         weapon_set_tracking_info(weapon_objnum, parent_objnum, target_objnum, 1, target_subsys);
10807
10808         // do other cool stuff if weapon is created.
10809         if (weapon_objnum > -1) {
10810                 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10811                 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10812
10813                 // maybe sound
10814                 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10815                         // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10816                         if ( parent_objnum != OBJ_INDEX(Player_obj) ) {
10817                                 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], &turret_pos, &View_position );
10818                         }
10819                 }
10820                 
10821                 // in multiplayer (and the master), then send a turret fired packet.
10822                 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10823                         int subsys_index;
10824
10825                         subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10826                         Assert( subsys_index != -1 );
10827                         send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10828                 }
10829         }
10830 }
10831
10832 int Num_ai_firing = 0;
10833 int Num_find_turret_enemy = 0;
10834 int Num_turrets_fired = 0;
10835 //      Given a turret tp and its parent parent_objnum, fire from the turret at its enemy.
10836 void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
10837 {
10838         float           weapon_firing_range;
10839         vector  v2e;
10840         object  *lep;           //      Last enemy pointer
10841         model_subsystem *tp = ss->system_info;
10842         int             use_angles, turret_weapon_class;
10843         vector  predicted_enemy_pos;
10844         object  *objp;
10845         ai_info *aip;
10846
10847         if (!Ai_firing_enabled) {
10848                 return;
10849         }
10850
10851         if (ss->current_hits < 0.0f) {
10852                 return;
10853         }
10854
10855         if ( ship_subsys_disrupted(ss) ){               // AL 1/19/98: Make sure turret isn't suffering disruption effects
10856                 return;
10857         }
10858
10859         // Check turret free
10860         if (ss->weapons.flags & SW_FLAG_TURRET_LOCK) {
10861                 return;
10862         }
10863
10864         // If beam weapon, check beam free
10865         if ( (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) && !(ss->weapons.flags & SW_FLAG_BEAM_FREE) ) {
10866                 return;
10867         }
10868
10869         Assert( shipp->objnum == parent_objnum );
10870
10871         if ( tp->turret_weapon_type < 0 ){
10872                 return;
10873         }
10874
10875         // Monitor number of calls to ai_fire_from_turret
10876         Num_ai_firing++;
10877
10878         turret_weapon_class = tp->turret_weapon_type;
10879
10880         // AL 09/14/97: ensure ss->turret_enemy_objnum != -1 before setting lep
10881         if ( (ss->turret_enemy_objnum >= 0 && ss->turret_enemy_objnum < MAX_OBJECTS) && (ss->turret_enemy_sig == Objects[ss->turret_enemy_objnum].signature)) {
10882                 lep = &Objects[ss->turret_enemy_objnum];
10883
10884                 // MK -- here is where turret is targeting a bomb.  I simply return for now.  We should force
10885                 // a target change -- or better yet, never pick a weapon when this turret has a "huge" weapon
10886                 // loaded.
10887
10888                 // we only care about targets which are ships.
10889                 //if ( lep->type != OBJ_SHIP )
10890                 //      return;
10891
10892                 //      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.
10893                 if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HUGE ) {
10894                         if ( lep->type != OBJ_SHIP ) {
10895                                 return;
10896                         }
10897                         if ( !(Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
10898                                 return;
10899                         }
10900                 }
10901
10902                 // If targeting protected or beam protected ship, don't fire.  Reset enemy objnum
10903                 if (lep->type == OBJ_SHIP) {
10904                         // Check if we're targeting a protected ship
10905                         if (lep->flags & OF_PROTECTED) {
10906                                 ss->turret_enemy_objnum = -1;
10907                                 ss->turret_time_enemy_in_range = 0.0f;
10908                                 return;
10909                         }
10910
10911                         // Check if we're targeting a beam protected ship with a beam weapon
10912                         if ( (lep->flags & OF_BEAM_PROTECTED) && (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) ) {
10913                                 ss->turret_enemy_objnum = -1;
10914                                 ss->turret_time_enemy_in_range = 0.0f;
10915                                 return;
10916                         }
10917                 }
10918         } else {
10919                 ss->turret_enemy_objnum = -1;
10920                 lep = NULL;
10921         }
10922         
10923         Assert((parent_objnum >= 0) && (parent_objnum < MAX_OBJECTS));
10924         objp = &Objects[parent_objnum];
10925         Assert(objp->type == OBJ_SHIP);
10926         aip = &Ai_info[Ships[objp->instance].ai_index];
10927
10928         // Use the turret info for all guns, not one gun in particular.
10929         vector   gvec, gpos;
10930         ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
10931
10932         // Rotate the turret even if time hasn't elapsed, since it needs to turn to face its target.
10933         use_angles = aifft_rotate_turret(shipp, parent_objnum, ss, objp, lep, &predicted_enemy_pos, &gvec);
10934
10935         if ( !timestamp_elapsed(ss->turret_next_fire_stamp)){
10936                 return;
10937         }
10938
10939         // Don't try to fire beyond weapon_limit_range
10940         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);
10941
10942         // if beam weapon in nebula and target not tagged, decrase firing range
10943         extern int Nebula_sec_range;
10944         if (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) {
10945                 if ( !((shipp->tag_left > 0) || (shipp->level2_tag_left > 0)) ) {
10946                         if (Nebula_sec_range) {
10947                                 weapon_firing_range *= float(BEAM_NEBULA_RANGE_REDUCE_FACTOR);
10948                         }
10949                 }
10950         }
10951
10952         if (ss->turret_enemy_objnum != -1) {
10953                 float dist_to_enemy = vm_vec_normalized_dir(&v2e, &predicted_enemy_pos, &gpos) - lep->radius;
10954                 if (dist_to_enemy > weapon_firing_range) {
10955                         ss->turret_enemy_objnum = -1;           //      Force picking of new enemy.
10956                 }
10957         }
10958
10959         // Turret spawn weapons are a special case.  They fire if there are enough enemies in the 
10960         // immediate area (not necessarily in the turret fov).
10961         if ( Weapon_info[turret_weapon_class].wi_flags & WIF_SPAWN ) {
10962                 int num_ships_nearby;
10963                 num_ships_nearby = num_nearby_fighters(get_enemy_team_mask(parent_objnum), &gpos, 1500.0f);
10964                 if (( num_ships_nearby >= 3 ) || ((num_ships_nearby >= 2) && (frand() < 0.1f))) {
10965                         turret_fire_weapon(ss, parent_objnum, &gpos, &ss->turret_last_fire_direction);
10966                 } else {
10967                         ss->turret_next_fire_stamp = timestamp(1000);   //      Regardless of firing rate, don't check whether should fire for awhile.
10968                 }
10969                 return;
10970         }
10971
10972         //      Maybe pick a new enemy.
10973         if ( turret_should_pick_new_target(ss) ) {
10974                 Num_find_turret_enemy++;
10975                 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);
10976                 Assert(objnum < 0 || is_target_beam_valid(ss, objnum));
10977
10978                 if (objnum != -1) {
10979                         if (ss->turret_enemy_objnum == -1) {
10980                                 ss->turret_enemy_objnum = objnum;
10981                                 ss->turret_enemy_sig = Objects[objnum].signature;
10982                                 // why return?
10983                                 return;
10984                         } else {
10985                                 ss->turret_enemy_objnum = objnum;
10986                                 ss->turret_enemy_sig = Objects[objnum].signature;
10987                         }
10988                 } else {
10989                         ss->turret_enemy_objnum = -1;
10990                 }
10991
10992                 if (ss->turret_enemy_objnum != -1) {
10993                         float   dot = 1.0f;
10994                         lep = &Objects[ss->turret_enemy_objnum];
10995                         if ( lep->type == OBJ_SHIP ) {
10996                                 ss->targeted_subsys = aifft_find_turret_subsys(objp, ss, lep, &dot);                            
10997                         }
10998                         ss->turret_next_enemy_check_stamp = timestamp((int) (max(dot, 0.5f)*2000.0f) + 1000);
10999                 } else {
11000                         ss->turret_next_enemy_check_stamp = timestamp((int) (2000.0f * frand_range(0.9f, 1.1f)));       //      Check every two seconds
11001                 }
11002         }
11003
11004         //      If still don't have an enemy, return.  Or, if enemy is protected, return.
11005         if (ss->turret_enemy_objnum != -1) {
11006                 //      Don't shoot at ship we're going to dock with.
11007                 if (ss->turret_enemy_objnum == aip->dock_objnum) {
11008                         ss->turret_enemy_objnum = -1;
11009                         return;
11010                 }
11011
11012                 if (Objects[ss->turret_enemy_objnum].flags & OF_PROTECTED) {
11013                         //      This can happen if the enemy was selected before it became protected.
11014                         ss->turret_enemy_objnum = -1;
11015                         return;
11016                 }
11017                 lep = &Objects[ss->turret_enemy_objnum];
11018         } else {
11019                 if (timestamp_until(ss->turret_next_fire_stamp) < 500) {
11020                         ss->turret_next_fire_stamp = timestamp(500);
11021                 }
11022                 return;
11023         }
11024
11025         if ( lep == NULL ){
11026                 return;
11027         }
11028
11029         Assert(ss->turret_enemy_objnum != -1);
11030
11031         float dot = vm_vec_dot(&v2e, &gvec);
11032
11033         if (dot > tp->turret_fov ) {
11034                 // Ok, the turret is lined up... now line up a particular gun.
11035                 int ok_to_fire = 0;
11036                 float dist_to_enemy;
11037
11038                 // We're ready to fire... now get down to specifics, like where is the
11039                 // actual gun point and normal, not just the one for whole turret.
11040                 ship_get_global_turret_gun_info(&Objects[parent_objnum], ss, &gpos, &gvec, use_angles, &predicted_enemy_pos);
11041                 ss->turret_next_fire_pos++;
11042
11043                 // Fire in the direction the turret is facing, not right at the target regardless of turret dir.
11044                 vm_vec_sub(&v2e, &predicted_enemy_pos, &gpos);
11045                 dist_to_enemy = vm_vec_normalize(&v2e);
11046                 dot = vm_vec_dot(&v2e, &gvec);
11047
11048                 // 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
11049                 // and make them less lethal
11050                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){
11051                         flak_jitter_aim(&v2e, dist_to_enemy, ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS));
11052                 }
11053
11054                 // Fire if:
11055                 //              dumbfire and nearly pointing at target.
11056                 //              heat seeking and target in a fairly wide cone.
11057                 //              aspect seeking and target is locked.
11058                 turret_weapon_class = tp->turret_weapon_type;
11059
11060                 // if dumbfire (lasers and non-homing missiles)
11061                 if ( !(Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING) ) {
11062                         if ((dist_to_enemy < 75.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11063                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11064                                 ok_to_fire = 1;
11065                         }
11066                 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_HEAT ) {     // if heat seekers
11067                         if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_HEATSEEK_ANGLE )) {
11068                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11069                                 ok_to_fire = 1;
11070                         }
11071                 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_ASPECT ) {   // if aspect seeker
11072                         if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11073                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11074                         }
11075                         if ( turret_should_fire_aspect(ss, dot, turret_weapon_class) ) {
11076                                 ok_to_fire = 1;
11077                         }
11078                 }
11079
11080                 if ( ok_to_fire ) {
11081                         Num_turrets_fired++;
11082                         
11083                         turret_fire_weapon(ss, parent_objnum, &gpos, &v2e, &predicted_enemy_pos);                                               
11084                 } else {
11085                         turret_update_enemy_in_range(ss, -4*Weapon_info[tp->turret_weapon_type].fire_wait);
11086                         ss->turret_next_fire_stamp = timestamp(500);
11087                 }
11088         } else {
11089                 // Lost him!
11090                 ss->turret_enemy_objnum = -1;           //      Reset enemy objnum, find a new one next frame.
11091                 ss->turret_time_enemy_in_range = 0.0f;
11092         }
11093 }
11094
11095 // TURRET END
11096
11097 #ifndef NDEBUG
11098 #define MAX_AI_DEBUG_RENDER_STUFF       100
11099 typedef struct ai_render_stuff {
11100         ship_subsys     *ss;
11101         int                     parent_objnum;
11102 } ai_render_stuff;
11103
11104 ai_render_stuff AI_debug_render_stuff[MAX_AI_DEBUG_RENDER_STUFF];
11105
11106 int     Num_AI_debug_render_stuff = 0;
11107
11108 void ai_debug_render_stuff()
11109 {
11110         vertex  vert1, vert2;
11111         vector  gpos2;
11112         int             i;
11113
11114         for (i=0; i<Num_AI_debug_render_stuff; i++) {
11115                 ship_subsys     *ss;
11116                 int     parent_objnum;
11117                 vector  gpos, gvec;
11118                 model_subsystem *tp;
11119
11120                 ss = AI_debug_render_stuff[i].ss;
11121                 tp = ss->system_info;
11122
11123                 parent_objnum = AI_debug_render_stuff[i].parent_objnum;
11124
11125                 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
11126                 g3_rotate_vertex(&vert1, &gpos);
11127                 vm_vec_scale_add(&gpos2, &gpos, &gvec, 20.0f);
11128                 g3_rotate_vertex(&vert2, &gpos2);
11129                 gr_set_color(0, 0, 255);
11130                 g3_draw_sphere(&vert1, 2.0f);
11131                 gr_set_color(255, 0, 255);
11132                 g3_draw_sphere(&vert2, 2.0f);
11133                 g3_draw_line(&vert1, &vert2);
11134         }
11135
11136         // draw from beta to its goal point
11137 /*      for (i=0; i<6; i++) {
11138                 ai_info *aip = &Ai_info[i];
11139                 gr_set_color(0, 0, 255);
11140                 g3_rotate_vertex(&vert1, &Objects[i].pos);
11141                 g3_rotate_vertex(&vert2, &aip->goal_point);
11142                 g3_draw_line(&vert1, &vert2);
11143         } */
11144         
11145
11146         Num_AI_debug_render_stuff = 0;
11147 }
11148
11149 #endif
11150
11151 #ifndef NDEBUG
11152 int     Msg_count_4996 = 0;
11153 #endif
11154
11155 //      --------------------------------------------------------------------------
11156 // Process subobjects of object objnum.
11157 //      Deal with engines disabled.
11158 void process_subobjects(int objnum)
11159 {
11160         model_subsystem *psub;
11161         ship_subsys     *pss;
11162         object  *objp = &Objects[objnum];
11163         ship            *shipp = &Ships[objp->instance];
11164         ai_info *aip = &Ai_info[shipp->ai_index];
11165         ship_info       *sip = &Ship_info[shipp->ship_info_index];
11166
11167         for ( pss = GET_FIRST(&shipp->subsys_list); pss !=END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
11168                 psub = pss->system_info;
11169
11170                 // Don't process destroyed objects
11171                 if ( pss->current_hits <= 0.0f ) 
11172                         continue;
11173
11174                 switch (psub->type) {
11175                 case SUBSYSTEM_TURRET:
11176                         if ( psub->turret_num_firing_points > 0 )       {
11177                                 ai_fire_from_turret(shipp, pss, objnum);
11178                         } else {
11179 #ifndef NDEBUG
11180                                 if (!Msg_count_4996) {
11181                                         Warning( LOCATION, "Ship '%s' has turrets with no guns!\nProbably a model problem, so get an artist!", shipp->ship_name );
11182                                         Msg_count_4996++;
11183                                 }
11184 #endif
11185                                 }
11186                         break;
11187
11188                 case SUBSYSTEM_ENGINE:
11189                 case SUBSYSTEM_NAVIGATION:
11190                 case SUBSYSTEM_COMMUNICATION:
11191                 case SUBSYSTEM_WEAPONS:
11192                 case SUBSYSTEM_SENSORS:
11193                 case SUBSYSTEM_UNKNOWN:
11194                         break;
11195
11196                 // next set of subsystems may rotation
11197                 case SUBSYSTEM_RADAR:
11198                 case SUBSYSTEM_SOLAR:
11199                 case SUBSYSTEM_GAS_COLLECT:
11200                 case SUBSYSTEM_ACTIVATION:
11201                         break;
11202                 default:
11203                         Error(LOCATION, "Illegal subsystem type.\n");
11204                 }
11205
11206                 // do solar/radar/gas/activator rotation here
11207                 if ( psub->flags & MSS_FLAG_ROTATES )   {
11208                         if (psub->flags & MSS_FLAG_STEPPED_ROTATE       ) {
11209                                 submodel_stepped_rotate(psub, &pss->submodel_info_1);
11210                         } else {
11211                                 submodel_rotate(psub, &pss->submodel_info_1 );
11212                         }
11213                 }
11214
11215         }
11216
11217         //      Deal with a ship with blown out engines.
11218         if (ship_get_subsystem_strength(shipp, SUBSYSTEM_ENGINE) == 0.0f) {
11219                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
11220                         // AL: Only attack forever if not trying to depart to a docking bay.  Need to have this in, since
11221                         //     a ship may get repaired... and it should still try to depart.  Since docking bay departures
11222                         //     are not handled as goals, we don't want to leave the AIM_BAY_DEPART mode.
11223                         if ( aip->mode != AIM_BAY_DEPART ) {
11224                                 ai_attack_object(objp, NULL, 99, NULL);         //      Regardless of current mode, enter attack mode.
11225                                 aip->submode = SM_ATTACK_FOREVER;                               //      Never leave attack submode, don't avoid, evade, etc.
11226                         }
11227                 }
11228         }
11229
11230
11231 }
11232
11233 //      Given an object and the wing it's in, return its index in the wing list.
11234 //      This defines its location in the wing formation.
11235 //      If the object can't be found in the wing, return -1.
11236 //      *objp           object of interest
11237 //      wingnum the wing *objp is in
11238 int get_wing_index(object *objp, int wingnum)
11239 {
11240         wing    *wingp;
11241         int     i;
11242
11243         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11244
11245         wingp = &Wings[wingnum];
11246
11247         for (i=wingp->current_count-1; i>=0; i--)
11248                 if ( objp->instance == wingp->ship_index[i] )
11249                         break;
11250
11251         return i;               //      Note, returns -1 if string not found.
11252 }
11253
11254 //      Given a wing, return a pointer to the object of its leader.
11255 //      Asserts if object not found.
11256 //      Currently, the wing leader is defined as the first object in the wing.
11257 //      wingnum         Wing number in Wings array.
11258 //      If wing leader is disabled, swap it with another ship.
11259 object * get_wing_leader(int wingnum)
11260 {
11261         wing            *wingp;
11262         int             ship_num;
11263
11264         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11265
11266         wingp = &Wings[wingnum];
11267
11268         Assert(wingp->current_count != 0);                      //      Make sure there is a leader
11269
11270         ship_num = wingp->ship_index[0];
11271
11272         //      If this ship is disabled, try another ship in the wing.
11273         int n = 0;
11274         while (ship_get_subsystem_strength(&Ships[ship_num], SUBSYSTEM_ENGINE) == 0.0f) {
11275                 n++;
11276                 if (n >= wingp->current_count)
11277                         break;  
11278                 ship_num = wingp->ship_index[n];
11279         }
11280
11281         if (( n != 0) && (n != wingp->current_count)) {
11282                 int t = wingp->ship_index[0];
11283                 wingp->ship_index[0] = wingp->ship_index[n];
11284                 wingp->ship_index[n] = t;
11285         }
11286
11287         return &Objects[Ships[ship_num].objnum];
11288 }
11289
11290 #define DEFAULT_WING_X_DELTA            1.0f
11291 #define DEFAULT_WING_Y_DELTA            0.25f
11292 #define DEFAULT_WING_Z_DELTA            0.75f
11293 #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))
11294 // next constant is higher that MAX_SHIPS_IN_WINGS to deal with forming on player's wing
11295 #define MAX_FORMATION_ROWS              4
11296
11297 //      Given a position in a wing, return the desired location of the ship relative to the leader
11298 //      *_delta_vec             OUTPUT.  delta vector based on wing_index
11299 //      wing_index              position in wing.
11300 void get_wing_delta(vector *_delta_vec, int wing_index)
11301 {
11302         int     wi0;
11303
11304         Assert(wing_index >= 0);
11305
11306         int     k, row, column;
11307
11308         int bank = wing_index / (MAX_FORMATION_ROWS*(MAX_FORMATION_ROWS+1)/2);
11309         wi0 = wing_index % (MAX_FORMATION_ROWS * (MAX_FORMATION_ROWS+1)/2);
11310
11311         k = 0;
11312         for (row=1; row<MAX_FORMATION_ROWS+1; row++) {
11313                 k += row;
11314                 if (wi0 < k)
11315                         break;
11316         }
11317
11318         row--;
11319         column = wi0 - k + row + 1;
11320
11321         _delta_vec->x = ((float) column - (float) row/2.0f) * DEFAULT_WING_X_DELTA/DEFAULT_WING_MAG;
11322         _delta_vec->y = ((float)row + (float)bank*2.25f) * DEFAULT_WING_Y_DELTA/DEFAULT_WING_MAG;
11323         _delta_vec->z = - ((float)row + 0.5f * (float) bank) * DEFAULT_WING_Z_DELTA/DEFAULT_WING_MAG;
11324         
11325 }
11326
11327 //      Compute the largest radius of a ship in a *objp's wing.
11328 float gwlr_1(object *objp, ai_info *aip)
11329 {
11330         int             wingnum = aip->wing;
11331         float           max_radius;
11332         object  *o;
11333         ship_obj        *so;
11334
11335         Assert(wingnum >= 0);
11336
11337         max_radius = objp->radius;
11338
11339         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11340                 o = &Objects[so->objnum];
11341                 if (Ai_info[Ships[o->instance].ai_index].wing == wingnum)
11342                         if (o->radius > max_radius)
11343                                 max_radius = o->radius;
11344         }
11345
11346         return max_radius;
11347 }
11348
11349 //      Compute the largest radius of a ship forming on *objp's wing.
11350 float gwlr_object_1(object *objp, ai_info *aip)
11351 {
11352         float           max_radius;
11353         object  *o;
11354         ship_obj        *so;
11355
11356         max_radius = objp->radius;
11357
11358         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11359                 o = &Objects[so->objnum];
11360                 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == OBJ_INDEX(objp))
11361                         if (o->radius > max_radius)
11362                                 max_radius = o->radius;
11363         }
11364
11365         return max_radius;
11366 }
11367
11368 //      For the wing that *objp is part of, return the largest ship radius in that wing.
11369 float get_wing_largest_radius(object *objp, int formation_object_flag)
11370 {
11371         ship            *shipp;
11372         ai_info *aip;
11373
11374         Assert(objp->type == OBJ_SHIP);
11375         Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
11376         shipp = &Ships[objp->instance];
11377         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11378         aip = &Ai_info[shipp->ai_index];
11379
11380         if (formation_object_flag) {
11381                 return gwlr_object_1(objp, aip);
11382         } else {
11383                 return gwlr_1(objp, aip);
11384         }
11385
11386 }
11387
11388 float Wing_y_scale = 2.0f;
11389 float Wing_scale = 1.0f;
11390 DCF(wing_y_scale, "")
11391 {
11392         dc_get_arg(ARG_FLOAT);
11393         Wing_y_scale = Dc_arg_float;
11394 }
11395
11396 DCF(wing_scale, "")
11397 {
11398         dc_get_arg(ARG_FLOAT);
11399         Wing_scale = Dc_arg_float;
11400 }
11401
11402 // Given a wing leader and a position in the wing formation, return the desired absolute location to fly to.
11403 //      Returns result in *result_pos.
11404 void get_absolute_wing_pos(vector *result_pos, object *leader_objp, int wing_index, int formation_object_flag)
11405 {
11406         vector  wing_delta, rotated_wing_delta;
11407         float           wing_spread_size;
11408
11409         get_wing_delta(&wing_delta, wing_index);                //      Desired location in leader's reference frame
11410
11411         wing_spread_size = max(50.0f, 3.0f * get_wing_largest_radius(leader_objp, formation_object_flag) + 15.0f);
11412
11413         // for player obj (1) move ships up 20% (2) scale formation up 20%
11414         if (leader_objp->flags & OF_PLAYER_SHIP) {
11415                 wing_delta.y *= Wing_y_scale;
11416                 wing_spread_size *= Wing_scale;
11417         }
11418
11419         vm_vec_scale(&wing_delta, wing_spread_size * (1.0f + leader_objp->phys_info.speed/70.0f));
11420
11421         vm_vec_unrotate(&rotated_wing_delta, &wing_delta, &leader_objp->orient);        //      Rotate into leader's reference.
11422
11423         vm_vec_add(result_pos, &leader_objp->pos, &rotated_wing_delta); //      goal_point is absolute 3-space point.
11424 }
11425
11426 #ifndef NDEBUG
11427 int Debug_render_wing_phantoms;
11428
11429 void render_wing_phantoms(object *objp)
11430 {
11431         int             i;
11432         ship            *shipp;
11433         ai_info *aip;
11434         int             wingnum;
11435         int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11436         vector  goal_point;
11437         
11438         Assert(objp->type == OBJ_SHIP);
11439         Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11440
11441         shipp = &Ships[objp->instance];
11442         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11443
11444         aip = &Ai_info[shipp->ai_index];
11445
11446         wingnum = aip->wing;
11447
11448         if (wingnum == -1)
11449                 return;
11450
11451         wing_index = get_wing_index(objp, wingnum);
11452
11453         //      If this ship is NOT the leader, abort.
11454         if (wing_index != 0)
11455                 return;
11456
11457         for (i=0; i<32; i++)
11458                 if (Debug_render_wing_phantoms & (1 << i)) {
11459                         get_absolute_wing_pos(&goal_point, objp, i, 0);
11460         
11461                         vertex  vert;
11462                         gr_set_color(255, 0, 128);
11463                         g3_rotate_vertex(&vert, &goal_point);
11464                         g3_draw_sphere(&vert, 2.0f);
11465                 }
11466
11467         Debug_render_wing_phantoms = 0;
11468
11469 }
11470
11471 void render_wing_phantoms_all()
11472 {
11473         object  *objp;
11474         ship_obj        *so;
11475
11476         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11477                 ship            *shipp;
11478                 ai_info *aip;
11479                 int             wingnum;
11480                 int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11481
11482                 objp = &Objects[so->objnum];
11483                 
11484                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11485                 shipp = &Ships[objp->instance];
11486                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11487
11488                 aip = &Ai_info[shipp->ai_index];
11489
11490                 wingnum = aip->wing;
11491
11492                 if (wingnum == -1)
11493                         continue;
11494
11495                 wing_index = get_wing_index(objp, wingnum);
11496
11497                 //      If this ship is NOT the leader, abort.
11498                 if (wing_index != 0)
11499                         continue;
11500                 
11501                 render_wing_phantoms(objp);
11502
11503                 return;
11504         }
11505 }
11506
11507 #endif
11508
11509 //      Hook from goals code to AI.
11510 //      Force a wing to fly in formation.
11511 //      Sets AIF_FORMATION bit in ai_flags.
11512 //      wingnum         Wing to force to fly in formation
11513 void ai_fly_in_formation(int wingnum)
11514 {
11515         object  *objp;
11516         ship            *shipp;
11517         ship_obj        *so;
11518
11519         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11520                 objp = &Objects[so->objnum];
11521                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11522
11523                 shipp = &Ships[objp->instance];
11524                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11525
11526                 if (Ai_info[shipp->ai_index].wing == wingnum) {
11527                         Ai_info[shipp->ai_index].ai_flags |= AIF_FORMATION_WING;
11528                         Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_OBJECT;
11529                 }
11530         }
11531 }
11532
11533 //      Hook from goals code to AI.
11534 //      Force a wing to abandon formation flying.
11535 //      Clears AIF_FORMATION bit in ai_flags.
11536 //      wingnum         Wing to force to fly in formation
11537 void ai_disband_formation(int wingnum)
11538 {
11539         object  *objp;
11540         ship            *shipp;
11541         ship_obj        *so;
11542
11543         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11544                 objp = &Objects[so->objnum];
11545                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11546
11547                 shipp = &Ships[objp->instance];
11548                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11549
11550                 if (Ai_info[shipp->ai_index].wing == wingnum) {
11551                         Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_WING;
11552                 }
11553         }
11554 }
11555
11556 float   Leader_chaos = 0.0f;
11557 int Chaos_frame = -1;
11558
11559 //      Return true if objp is flying in an erratic manner
11560 //      Only true if objp is a player
11561 int formation_is_leader_chaotic(object *objp)
11562 {
11563         if (Game_mode & GM_MULTIPLAYER)
11564                 return 0;
11565
11566         if (objp != Player_obj)
11567                 return 0;
11568
11569         if (Framecount != Chaos_frame) {
11570                 float   speed_scale;
11571                 float   fdot, udot;
11572
11573                 speed_scale = 3.0f + objp->phys_info.speed * 0.1f;
11574
11575                 fdot = 5.0f * (1.0f - vm_vec_dot(&objp->orient.fvec, &objp->last_orient.fvec)) * flFrametime;
11576                 udot = 8.0f * (1.0f - vm_vec_dot(&objp->orient.uvec, &objp->last_orient.uvec)) * flFrametime;
11577
11578                 Leader_chaos += fdot * speed_scale + udot * speed_scale;
11579
11580                 Leader_chaos *= (1.0f - flFrametime*0.2f);
11581
11582                 if (Leader_chaos < 0.0f)
11583                         Leader_chaos = 0.0f;
11584                 else if (Leader_chaos > 1.7f)
11585                         Leader_chaos = 1.7f;
11586
11587                 //nprintf(("AI", "Frame %i: chaos = %7.4f\n", Framecount, Leader_chaos));
11588
11589                 Chaos_frame = Framecount;
11590         }
11591
11592         return (Leader_chaos > 1.0f);
11593 }
11594
11595 // Fly in formation.
11596 //      Make Pl_objp assume its proper place in formation.
11597 //      If the leader of the wing is doing something stupid, like fighting a battle,
11598 //      then the poor sap wingmates will be in for a "world of hurt"
11599 //      Return TRUE if we need to process this object's normal mode
11600 int ai_formation()
11601 {
11602         object  *leader_objp;
11603         ship            *shipp;
11604         ai_info *aip, *laip;
11605         int             wingnum;
11606         int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11607         int             player_wing;    // index of the players wingnum
11608         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;
11609         float           dot_to_goal, dist_to_goal, leader_speed;
11610
11611         Assert(Pl_objp->type == OBJ_SHIP);
11612         Assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_SHIPS));
11613
11614         shipp = &Ships[Pl_objp->instance];
11615
11616         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11617
11618         aip = &Ai_info[shipp->ai_index];
11619
11620         Assert((aip->ai_flags & AIF_FORMATION) != AIF_FORMATION);       //      Make sure not both types of formation flying in effect.
11621
11622         //      Determine which kind of formation flying.
11623         //      If tracking an object, not in waypoint mode:
11624         if (aip->ai_flags & AIF_FORMATION_OBJECT) {
11625                 if ((aip->goal_objnum < 0) || (aip->goal_objnum >= MAX_OBJECTS)) {
11626                         aip->ai_flags &= ~AIF_FORMATION_OBJECT;
11627                         return 1;
11628                 }
11629                 
11630                 wing_index = ai_formation_object_get_slotnum(aip->goal_objnum, Pl_objp);
11631                 leader_objp = &Objects[aip->goal_objnum];
11632         } else {        //      Formation flying in waypoint mode.
11633                 Assert(aip->ai_flags & AIF_FORMATION_WING);
11634                 if (aip->mode != AIM_WAYPOINTS) {
11635                         aip->ai_flags &= ~AIF_FORMATION_WING;
11636                         return 1;
11637                 }
11638
11639                 wingnum = aip->wing;
11640
11641                 if (wingnum == -1)
11642                         return 1;
11643
11644                 // disable formation flying for any ship in the players wing
11645                 player_wing = Ships[Player_obj->instance].wingnum;
11646                 if ( (player_wing != -1) && (wingnum == player_wing) )
11647                         return 1;
11648
11649                 wing_index = get_wing_index(Pl_objp, wingnum);
11650
11651                 leader_objp = get_wing_leader(wingnum);
11652
11653         }
11654
11655         //      If docked with a ship in this wing, only the more massive one actually flies in formation.
11656         if (aip->dock_objnum != -1) {
11657                 object  *other_objp = &Objects[aip->dock_objnum];
11658                 ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
11659
11660                 if (aip->wing == other_aip->wing) {
11661                         if (Pl_objp->phys_info.mass < other_objp->phys_info.mass)
11662                                 return 0;
11663                         else if (Pl_objp->phys_info.mass == other_objp->phys_info.mass) {
11664                                 if (Pl_objp->signature < other_objp->signature)
11665                                         return 0;
11666                         }
11667                 }
11668         }
11669
11670         Assert(leader_objp != NULL);
11671         laip = &Ai_info[Ships[leader_objp->instance].ai_index];
11672
11673         //      Make sure we're really in this wing.
11674         if (wing_index == -1)
11675                 return 1;
11676
11677         //      If this ship is the leader, abort, as he doesn't have to follow anyone.
11678         if (wing_index == 0) {
11679                 // nprintf(("AI", "Hmm, wing leader %s in ai_formation for no good reason.\n", shipp->ship_name));
11680                 return 1;
11681         }
11682
11683         if (aip->mode == AIM_WAYPOINTS) {
11684                 aip->wp_list = laip->wp_list;
11685                 if (laip->wp_index < Waypoint_lists[laip->wp_list].count)
11686                         aip->wp_index = laip->wp_index;
11687                 else
11688                         aip->wp_index = Waypoint_lists[laip->wp_list].count - 1;
11689                 aip->wp_flags = laip->wp_flags;
11690                 aip->wp_dir = laip->wp_dir;
11691         }
11692
11693         #ifndef NDEBUG
11694         Debug_render_wing_phantoms |= (1 << wing_index);
11695         #endif
11696
11697         leader_speed = leader_objp->phys_info.speed;
11698         vector leader_vec = leader_objp->phys_info.vel;
11699
11700         get_absolute_wing_pos(&goal_point, leader_objp, wing_index, aip->ai_flags & AIF_FORMATION_OBJECT);
11701         vm_vec_scale_add(&future_goal_point_5, &goal_point, &leader_vec, 10.0f);
11702         vm_vec_scale_add(&future_goal_point_2, &goal_point, &leader_vec, 5.0f);
11703         vm_vec_scale_add(&future_goal_point_x, &goal_point, &leader_objp->orient.fvec, 10.0f);  //      used when very close to destination
11704         vm_vec_scale_add(&future_goal_point_1000x, &goal_point, &leader_objp->orient.fvec, 1000.0f);    //      used when very close to destination
11705
11706         //      Now, get information telling this object how to turn and accelerate to get to its
11707         //      desired location.
11708         vm_vec_sub(&vec_to_goal, &goal_point, &Pl_objp->pos);
11709         if ( vm_vec_mag_quick(&vec_to_goal) < AICODE_SMALL_MAGNITUDE )
11710                 vec_to_goal.x += 0.1f;
11711
11712         vm_vec_copy_normalize(&dir_to_goal, &vec_to_goal);
11713         //dot_to_goal = vm_vec_dot(&dir_to_goal, &leader_objp->orient.fvec);
11714         dot_to_goal = vm_vec_dot(&dir_to_goal, &Pl_objp->orient.fvec);
11715         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &goal_point);
11716         float   dist_to_goal_2 = vm_vec_dist_quick(&Pl_objp->pos, &future_goal_point_2);
11717
11718         // 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));
11719
11720         int     chaotic_leader = 0;
11721
11722         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.
11723
11724         if (dist_to_goal > 500.0f) {
11725                 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11726                 accelerate_ship(aip, 1.0f);
11727         } else if (dist_to_goal > 200.0f) {
11728                 if (dot_to_goal > -0.5f) {
11729                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11730                         float range_speed = shipp->current_max_speed - leader_speed;
11731                         if (range_speed > 0.0f)
11732                                 set_accel_for_target_speed(Pl_objp, leader_speed + range_speed * (dist_to_goal+100.0f)/500.0f);
11733                         else
11734                                 set_accel_for_target_speed(Pl_objp, shipp->current_max_speed);
11735                 } else {
11736                         turn_towards_point(Pl_objp, &future_goal_point_5, NULL, 0.0f);
11737                         if (leader_speed > 10.0f)
11738                                 set_accel_for_target_speed(Pl_objp, leader_speed *(1.0f + dot_to_goal));
11739                         else
11740                                 set_accel_for_target_speed(Pl_objp, 10.0f);
11741                 }
11742         } else {
11743                 vector  v2f2;
11744                 float   dot_to_f2;
11745                 float   dist_to_f2;
11746
11747                 dist_to_f2 = vm_vec_normalized_dir(&v2f2, &future_goal_point_2, &Pl_objp->pos);
11748                 dot_to_f2 = vm_vec_dot(&v2f2, &Pl_objp->orient.fvec);
11749
11750                 //      Leader flying like a maniac.  Don't try hard to form on wing.
11751                 if (chaotic_leader) {
11752                         turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11753                         set_accel_for_target_speed(Pl_objp, min(leader_speed*0.8f, 20.0f));
11754                 } else if (dist_to_goal > 75.0f) {
11755                         turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11756                         float   delta_speed;
11757                         float range_speed = shipp->current_max_speed - leader_speed;
11758                         if (range_speed > 0.0f)
11759                                 delta_speed = dist_to_goal_2/500.0f * range_speed;
11760                         else
11761                                 delta_speed = shipp->current_max_speed - leader_speed;
11762                         if (dot_to_goal < 0.0f) {
11763                                 delta_speed = -delta_speed;
11764                                 if (-delta_speed > leader_speed/2)
11765                                         delta_speed = -leader_speed/2;
11766                         }
11767
11768                         if (leader_speed < 5.0f)
11769                                 if (delta_speed < 5.0f)
11770                                         delta_speed = 5.0f;
11771
11772                         float scale = dot_to_f2;
11773                         if (scale < 0.1f)
11774                                 scale = 0.0f;
11775                         else
11776                                 scale *= scale;
11777
11778                         set_accel_for_target_speed(Pl_objp, scale * (leader_speed + delta_speed));
11779                 } else {
11780                         //nprintf(("AI", "Dot = %7.3f\n", dot_to_goal));
11781
11782                         if (leader_speed < 5.0f) {
11783                                 //      Leader very slow.  If not close to goal point, get very close.  Note, keep trying to get close unless
11784                                 //      moving very slowly, else momentum can carry far away from goal.
11785
11786                                 if ((dist_to_goal > 10.0f) || ((Pl_objp->phys_info.speed > leader_speed + 2.5f) && (dot_to_goal > 0.5f))) {
11787                                         //nprintf(("MK", "(1) "));
11788                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11789                                         set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/10.0f);
11790                                 } else {
11791                                         if (Pl_objp->phys_info.speed < 0.5f) {
11792                                                 //nprintf(("MK", "(2) "));
11793                                                 turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11794                                         } else {
11795                                                 //nprintf(("MK", "(3) "));
11796                                         }
11797                                         set_accel_for_target_speed(Pl_objp, leader_speed);
11798                                 }
11799                                 //nprintf(("MK", "dist: %7.3f, dot: %6.3f, speed: %7.3f\n", dist_to_goal, dot_to_goal, Pl_objp->phys_info.speed));
11800                         } else if (dist_to_goal > 10.0f) {
11801                                 float   dv;
11802
11803                                 future_goal_point_2;
11804
11805                                 turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11806
11807                                 if (dist_to_goal > 25.0f) {
11808                                         if (dot_to_goal < 0.3f)
11809                                                 dv = -0.1f;
11810                                         else
11811                                                 dv = dot_to_goal - 0.2f;
11812
11813                                         set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/5.0f * dv);
11814                                 } else {
11815                                         set_accel_for_target_speed(Pl_objp, leader_speed + 1.5f * dot_to_goal - 1.0f);
11816                                 }
11817                         } else {
11818                                 if (Pl_objp->phys_info.speed < 0.1f)
11819                                         turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11820                                 else
11821                                         turn_towards_point(Pl_objp, &future_goal_point_x, NULL, 0.0f);
11822                                 set_accel_for_target_speed(Pl_objp, 0.0f);
11823                         }
11824                 }
11825
11826         }
11827
11828         //      See how different this ship's bank is relative to wing leader
11829         float   up_dot = vm_vec_dot(&leader_objp->orient.uvec, &Pl_objp->orient.uvec);
11830         if (up_dot < 0.996f) {
11831                 vector  w_out;
11832                 matrix  new_orient;
11833                 vector  angular_accel;
11834
11835                 vm_vec_copy_scale(&angular_accel, &Pl_objp->phys_info.max_rotvel, 0.2f);
11836                 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);
11837
11838         //      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)));
11839                 Pl_objp->orient = new_orient;
11840                 Pl_objp->phys_info.rotvel = w_out;
11841         //      Pl_objp->phys_info.desired_rotvel = w_out;
11842         } else {
11843                 Pl_objp->phys_info.rotvel.z = 0.0f;
11844         }
11845
11846         return 0;
11847 }
11848
11849 //      Return index of object repairing object objnum.
11850 int find_repairing_objnum(int objnum)
11851 {
11852         object          *objp;
11853         ship                    *shipp;
11854         ship_info       *sip;
11855         ship_obj                *so;
11856
11857         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11858                 objp = &Objects[so->objnum];
11859
11860                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11861
11862                 shipp = &Ships[objp->instance];
11863                 sip = &Ship_info[shipp->ship_info_index];
11864
11865                 if (sip->flags & SIF_SUPPORT) {
11866                         ai_info *aip;
11867
11868                         aip = &Ai_info[shipp->ai_index];
11869
11870                         if (aip->goal_objnum == objnum) {
11871                                 return objp-Objects;
11872                         }
11873                 }
11874         }
11875
11876         return -1;
11877 }
11878
11879 //      If object *objp is being repaired, deal with it!
11880 void ai_do_repair_frame(object *objp, ai_info *aip, float frametime)
11881 {
11882         if (Ships[objp->instance].team == TEAM_TRAITOR) {
11883                 ai_abort_rearm_request(objp);
11884                 return;
11885         }
11886
11887         if (aip->ai_flags & (AIF_BEING_REPAIRED | AIF_AWAITING_REPAIR)) {
11888                 int     dock_objnum;
11889                 ai_info *repair_aip;
11890
11891                 dock_objnum = aip->dock_objnum; // find_repairing_objnum(objp-Objects);
11892                 //Assert(dock_objnum != -1);
11893                 if (dock_objnum == -1)
11894                         return;
11895                 if (Objects[dock_objnum].signature != aip->dock_signature) {
11896                         Int3();         //      Curious -- object numbers match, but signatures do not.
11897                                                         //      Must mean original repair ship died and was replaced by current ship.
11898                         return;
11899                 }
11900         
11901                 repair_aip = &Ai_info[Ships[Objects[dock_objnum].instance].ai_index];
11902                 //Assert(repair_aip->mode == AIM_DOCK);
11903
11904                 if (aip->ai_flags & AIF_BEING_REPAIRED) {
11905                         // Assert(repair_aip->submode == AIS_DOCK_4);
11906
11907                         //      Wait awhile into the mode to synchronize with sound effect.
11908                         if (Missiontime - repair_aip->submode_start_time > REARM_SOUND_DELAY) {
11909                                 int repaired;
11910
11911                                 repaired = ship_do_rearm_frame( objp, frametime );              // hook to do missile rearming
11912
11913                                 //      See if fully repaired.  If so, cause process to stop.
11914                                 if ( repaired && (repair_aip->submode == AIS_DOCK_4)) {
11915
11916                                         repair_aip->submode = AIS_UNDOCK_0;
11917                                         repair_aip->submode_start_time = Missiontime;
11918
11919                                         // if repairing player object -- tell him done with repair
11920                                         if ( !MULTIPLAYER_CLIENT ){
11921                                                 ai_do_objects_repairing_stuff( objp, &Objects[dock_objnum], REPAIR_INFO_COMPLETE );
11922                                         }
11923                                 }
11924                         }
11925                 } else if (aip->ai_flags & AIF_AWAITING_REPAIR) {
11926                         //      If this ship has been awaiting repair for 90+ seconds, abort.
11927                         if ( !MULTIPLAYER_CLIENT ) {
11928                                 if ((Game_mode & GM_MULTIPLAYER) || (objp != Player_obj)) {
11929                                         if ((repair_aip->goal_objnum == OBJ_INDEX(objp)) && (timestamp_elapsed(aip->abort_rearm_timestamp))) {
11930                                                 ai_abort_rearm_request(objp);
11931                                                 aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);
11932                                         }
11933                                 }
11934                         }
11935                 }
11936         } else {
11937                 // AL 11-24-97: If this is the player ship, ensure the repair sound isn't playing.  We need to
11938                 //              do this check, since this is a looping sound, and may continue on if rearm/repair
11939                 //              finishes abnormally once sound begins looping.
11940                 if ( objp == Player_obj ) {
11941                         player_stop_repair_sound();
11942                 }
11943         }
11944 }
11945
11946 //      Shell around dock_orient_and_approach to detect whether dock process should be aborted.
11947 //      obj1 is the ship performing the repair.
11948 //      obj2 is the ship being repaired.
11949 void call_doa(object *obj1, object *obj2, ship_info *sip1)
11950 {
11951         if (sip1->flags & SIF_SUPPORT) {
11952                 if (obj2->phys_info.speed > MAX_REPAIR_SPEED) {
11953
11954                         // call the ai_abort rearm request code
11955                         ai_abort_rearm_request( obj2 );
11956                 } else
11957                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11958         } else {
11959                 if (Ship_info[Ships[obj1->instance].ship_info_index].flags & SIF_CARGO)
11960                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11961                 else if (Ship_info[Ships[obj2->instance].ship_info_index].flags & SIF_CARGO)
11962                         dock_orient_and_approach(obj2, obj1, DOA_DOCK_STAY);
11963                 else {
11964                         //mprintf(("Warning: Not sure, but making %s [%s] move to stay docked with %s [%s]\n",
11965                         //      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));
11966                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11967
11968                 }
11969         }
11970
11971 }
11972
11973 //      Maybe launch a countermeasure.
11974 //      Also, detect a supposed homing missile that no longer exists.
11975 void ai_maybe_launch_cmeasure(object *objp, ai_info *aip)
11976 {
11977         float                   dist;
11978         ship_info       *sip;
11979         ship                    *shipp;
11980
11981         shipp = &Ships[objp->instance];
11982         sip = &Ship_info[shipp->ship_info_index];
11983
11984         if (!(sip->flags & (SIF_SMALL_SHIP | SIF_TRANSPORT)))
11985                 return;
11986
11987         if (!shipp->cmeasure_count)
11988                 return;
11989
11990         if ( !timestamp_elapsed(shipp->cmeasure_fire_stamp) )
11991                 return;
11992
11993         //      If not on player's team and Skill_level + ai_class is low, never fire a countermeasure.  The ship is too dumb.
11994         if (shipp->team != Player_ship->team) {
11995                 if (Game_skill_level + aip->ai_class < 4){
11996                         return;
11997                 }
11998         }
11999
12000         if ((aip->nearest_locked_object != -1) && (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)) {
12001                 object  *weapon_objp;
12002                 weapon  *weaponp;
12003                 weapon_info     *wip;
12004
12005                 weapon_objp = &Objects[aip->nearest_locked_object];
12006                 weaponp = &Weapons[weapon_objp->instance];
12007                 wip = &Weapon_info[weaponp->weapon_info_index];
12008
12009                 if ((dist = vm_vec_dist_quick(&objp->pos, &weapon_objp->pos)) < weapon_objp->phys_info.speed*2.0f) {
12010         
12011                         aip->nearest_locked_distance = dist;
12012                         //      Verify that this object is really homing on us.
12013                         object  *weapon_objp;
12014
12015                         weapon_objp = &Objects[aip->nearest_locked_object];
12016
12017                         float   fire_chance;
12018
12019                         //      For ships on player's team, have constant, average chance to fire.
12020                         //      For enemies, increasing chance with higher skill level.
12021                         if (shipp->team == Player_ship->team)
12022                                 fire_chance = Cmeasure_fire_chance[NUM_SKILL_LEVELS/2];
12023                         else
12024                                 fire_chance = Cmeasure_fire_chance[Game_skill_level];
12025
12026                         //      Decrease chance to fire at lower ai class.
12027                         fire_chance *= (float) aip->ai_class/Num_ai_classes;
12028
12029                         float r = frand();
12030                         if (fire_chance < r) {
12031                                 //nprintf(("AI", "Not firing countermeasure due to skill level: %7.3f < %7.3f\n", fire_chance, r));
12032                                 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.
12033                                 return;
12034                         }
12035
12036                         if (weapon_objp->type == OBJ_WEAPON) {
12037                                 if (weapon_objp->instance >= 0) {
12038                                         //nprintf(("AI", "Firing countermeasure at time t=%7.3f\n", f2fl(Missiontime)));
12039                                         ship_launch_countermeasure(objp);
12040                                         shipp->cmeasure_fire_stamp = timestamp(2*CMEASURE_WAIT);
12041                                         return;
12042                                 }
12043                         }
12044         
12045                 }
12046         }
12047
12048         return;
12049 }
12050
12051 //      --------------------------------------------------------------------------
12052 void ai_preprocess_ignore_objnum(object *objp, ai_info *aip)
12053 {
12054 //      if (aip->ignore_objnum == UNUSED_OBJNUM)
12055 //              return;
12056
12057         if (aip->ai_flags & AIF_TEMPORARY_IGNORE) {
12058                 if (timestamp_elapsed(aip->ignore_expire_timestamp)) {
12059                         aip->ignore_objnum = UNUSED_OBJNUM;
12060                 }
12061         }
12062
12063         if (is_ignore_object(aip, aip->goal_objnum)) {
12064                 aip->goal_objnum = -1;
12065                 // AL 12-11-97: If in STRAFE mode, we need to ensure that target_objnum is also
12066                 //              set to -1
12067                 if ( aip->mode == AIM_STRAFE ) {
12068                         aip->target_objnum = -1;
12069                 }
12070         }
12071
12072         if (is_ignore_object(aip, aip->target_objnum))
12073                 aip->target_objnum = -1;
12074 }
12075
12076 /*
12077 void ai_safety_circle_spot()
12078 {
12079         vector  goal_point;
12080         ship_info       *sip;
12081
12082         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12083
12084         goal_point = Ai_info[Ships[Pl_objp->instance].ai_index].goal_point;
12085         turn_towards_tangent(Pl_objp, &goal_point, 50.0f);
12086
12087         set_accel_for_target_speed(Pl_objp, sip->max_speed/4.0f);
12088
12089 //      float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
12090 //      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));
12091
12092 }
12093 */
12094
12095 #define CHASE_CIRCLE_DIST               100.0f
12096
12097 void ai_chase_circle(object *objp)
12098 {
12099         float           dist_to_goal;
12100         float           target_speed;
12101         vector  goal_point;
12102         ship_info       *sip;
12103         ai_info         *aip;
12104
12105         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12106
12107         target_speed = sip->max_speed/4.0f;
12108         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12109
12110         Assert(vm_vec_mag(&aip->goal_point) >= 0.0f);           //      Supposedly detects bogus vector
12111
12112         goal_point = aip->goal_point;
12113
12114         if (aip->ignore_objnum == UNUSED_OBJNUM) {
12115                 dist_to_goal = vm_vec_dist_quick(&aip->goal_point, &objp->pos);
12116
12117                 if (dist_to_goal > 2*CHASE_CIRCLE_DIST) {
12118                         vector  vec_to_goal;
12119                         //      Too far from circle goal, create a new goal point.
12120                         vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
12121                         vm_vec_scale_add(&aip->goal_point, &objp->pos, &vec_to_goal, CHASE_CIRCLE_DIST);
12122                 }
12123
12124                 goal_point = aip->goal_point;
12125         } else if (is_ignore_object(aip, aip->ignore_objnum)) {
12126                 object  *ignore_objp = &Objects[aip->ignore_objnum];
12127
12128                 vector  tvec1;
12129                 float           dist;
12130
12131                 dist = vm_vec_normalized_dir(&tvec1, &Pl_objp->pos, &ignore_objp->pos);
12132
12133                 if (dist < ignore_objp->radius*2 + 1500.0f) {
12134                         vm_vec_scale_add(&goal_point, &Pl_objp->pos, &tvec1, ignore_objp->radius*2 + 1400.0f);
12135                         if (dist < ignore_objp->radius*2 + 1300.0f)
12136                                 target_speed = sip->max_speed * (1.25f - dist/(ignore_objp->radius*2 + 1500.0f));
12137                 }
12138         }
12139
12140         Assert(vm_vec_mag(&aip->goal_point) >= 0.0f);           //      Supposedly detects bogus vector
12141
12142         turn_towards_tangent(Pl_objp, &goal_point, 10*objp->radius + 200.0f);
12143
12144         set_accel_for_target_speed(Pl_objp, target_speed);
12145
12146 }
12147
12148 #define SHIELD_BALANCE_RATE     0.2f            //      0.1f -> takes 10 seconds to equalize shield.
12149
12150 //      Transfer shield energy to most recently hit section from others.
12151 void ai_transfer_shield(object *objp, int quadrant_num)
12152 {
12153         int     i;
12154         float   transfer_amount;
12155         float   transfer_delta;
12156         ship_info       *sip;
12157         float   max_quadrant_strength;
12158
12159         sip = &Ship_info[Ships[objp->instance].ship_info_index];
12160         max_quadrant_strength = sip->shields/MAX_SHIELD_SECTIONS;
12161
12162         transfer_amount = 0.0f;
12163         transfer_delta = (SHIELD_BALANCE_RATE/2) * max_quadrant_strength;
12164
12165         if (objp->shields[quadrant_num] + (MAX_SHIELD_SECTIONS-1)*transfer_delta > max_quadrant_strength)
12166                 transfer_delta = (max_quadrant_strength - objp->shields[quadrant_num])/(MAX_SHIELD_SECTIONS-1);
12167
12168         for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12169                 if (i != quadrant_num) {
12170                         if (objp->shields[i] >= transfer_delta) {
12171                                 objp->shields[i] -= transfer_delta;
12172                                 transfer_amount += transfer_delta;
12173                         } else {
12174                                 transfer_amount += objp->shields[i];
12175                                 objp->shields[i] = 0.0f;
12176                         }
12177                 }
12178
12179         objp->shields[quadrant_num] += transfer_amount;
12180 }
12181
12182 void ai_balance_shield(object *objp)
12183 {
12184         int     i;
12185         float   shield_strength_avg;
12186         float   delta;
12187
12188
12189         shield_strength_avg = get_shield_strength(objp)/MAX_SHIELD_SECTIONS;
12190
12191         delta = SHIELD_BALANCE_RATE * shield_strength_avg;
12192
12193         for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12194                 if (objp->shields[i] < shield_strength_avg) {
12195                         add_shield_strength(objp, delta);
12196                         if (objp->shields[i] > shield_strength_avg)
12197                                 objp->shields[i] = shield_strength_avg;
12198                 } else {
12199                         add_shield_strength(objp, -delta);
12200                         if (objp->shields[i] < shield_strength_avg)
12201                                 objp->shields[i] = shield_strength_avg;
12202                 }
12203 }
12204
12205 //      Manage the shield for this ship.
12206 //      Try to max out the side that was most recently hit.
12207 void ai_manage_shield(object *objp, ai_info *aip)
12208 {
12209         ship_info *sip;
12210
12211         sip = &Ship_info[Ships[objp->instance].ship_info_index];
12212
12213         if (timestamp_elapsed(aip->shield_manage_timestamp)) {
12214                 float           delay;
12215
12216                 //      Scale time until next manage shield based on Skill_level.
12217                 //      Ships on player's team are treated as if Skill_level is average.
12218                 if (Ships[objp->instance].team != Player_ship->team){
12219                         delay = Shield_manage_delays[Game_skill_level];
12220                 } else {
12221                         delay = Shield_manage_delays[NUM_SKILL_LEVELS/2];
12222                 }
12223
12224                 //      Scale between 1x and 3x based on ai_class
12225                 delay = delay + delay * (float) (3*(Num_ai_classes - aip->ai_class - 1) / (Num_ai_classes - 1));
12226                 aip->shield_manage_timestamp = timestamp((int) (delay * 1000.0f));
12227
12228                 if (sip->flags & SIF_SMALL_SHIP) {
12229                         if (Missiontime - aip->last_hit_time < F1_0*10)
12230                                 ai_transfer_shield(objp, aip->last_hit_quadrant);
12231                         else
12232                                 ai_balance_shield(objp);
12233                 }
12234
12235                 // 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]));
12236         }
12237 }
12238
12239 //      See if object *objp should evade an incoming missile.
12240 //      *aip is the ai_info pointer within *objp.
12241 void ai_maybe_evade_locked_missile(object *objp, ai_info *aip)
12242 {
12243         ship                    *shipp;
12244         ship_info       *sip;
12245
12246         shipp = &Ships[objp->instance];
12247         sip = &Ship_info[shipp->ship_info_index];
12248
12249         //      Only small ships evade an incoming missile.  Why would a capital ship try to swerve?
12250         if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12251                 return;
12252         }
12253
12254         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
12255                 return;
12256         }
12257
12258         if (aip->nearest_locked_object != -1) {
12259                 object  *missile_objp;
12260
12261                 missile_objp = &Objects[aip->nearest_locked_object];
12262
12263                 if (Weapons[missile_objp->instance].homing_object != objp) {
12264                         //nprintf(("AI", "\nMissile lost home!"));
12265                         aip->nearest_locked_object = -1;
12266                         return;
12267                 }
12268
12269                 if ((missile_objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[missile_objp->instance].weapon_info_index].wi_flags & WIF_HOMING)) {
12270                         float dist = vm_vec_dist_quick(&missile_objp->pos, &objp->pos);
12271                         float dist2 = 4.0f  * vm_vec_mag_quick(&missile_objp->phys_info.vel);                   
12272                         if (dist < dist2) {
12273                                 switch (aip->mode) {
12274                                 //      If in AIM_STRAFE mode, don't evade if parent of weapon is targeted ship.
12275                                 case AIM_STRAFE:
12276                                         if ((missile_objp->parent != -1) && (missile_objp->parent == aip->target_objnum)) {
12277                                                 ;
12278                                         } else {
12279                                                 ;               //      Alan -- If you want to handle incoming weapons from someone other than the ship
12280                                                                 //      the strafing ship is attacking, do it here.
12281                                         }
12282                                         break;
12283                                 case AIM_CHASE:
12284                                         //      Don't always go into evade weapon mode.  Usually, a countermeasure gets launched.
12285                                         // If low on countermeasures, more likely to try to evade.  If 8+, never evade due to low cmeasures.
12286                                         if (((((Missiontime >> 18) ^ OBJ_INDEX(objp)) & 3) == 0) || 
12287                                                 (objp->phys_info.speed < 40.0f) ||
12288                                                 (frand() < 1.0f - (float) shipp->cmeasure_count/8.0f)) {
12289                                                 if (aip->submode != SM_ATTACK_FOREVER) {        //      SM_ATTACK_FOREVER means engines blown.
12290                                                         aip->submode = SM_EVADE_WEAPON;
12291                                                         aip->submode_start_time = Missiontime;
12292                                                 }
12293                                         }
12294                                         break;
12295                                 case AIM_DOCK:  //      Ships in dock mode can evade iif they are not currently repairing or docked.
12296                                         if (aip->ai_flags & (AIF_REPAIRING | AIF_DOCKED))
12297                                                 break;
12298                                 case AIM_GUARD:
12299                                         //      If in guard mode and far away from guard object, don't pursue guy that hit me.
12300                                         if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12301                                                 if (vm_vec_dist_quick(&objp->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12302                                                         return;
12303                                                 }
12304                                         }
12305                                 case AIM_EVADE:
12306                                 case AIM_GET_BEHIND:
12307                                 case AIM_STAY_NEAR:
12308                                 case AIM_STILL:
12309                                 case AIM_AVOID:
12310                                 case AIM_WAYPOINTS:
12311                                 case AIM_NONE:
12312                                 case AIM_BIGSHIP:
12313                                 case AIM_PATH:
12314                                 case AIM_BE_REARMED:
12315                                 case AIM_SAFETY:
12316                                 case AIM_BAY_EMERGE:
12317                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12318                                         aip->previous_mode = aip->mode;
12319                                         aip->previous_submode = aip->submode;
12320                                         aip->mode = AIM_EVADE_WEAPON;
12321                                         aip->submode = -1;
12322                                         aip->submode_start_time = Missiontime;
12323                                         aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Max time to evade.
12324                                         //nprintf(("AI", "%s Evade weapon in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
12325                                         break;
12326                                 case AIM_EVADE_WEAPON:          //      Note: We don't want to change mode on another evasion, or previous_mode will get bashed.
12327                                 case AIM_PLAY_DEAD:
12328                                 case AIM_BAY_DEPART:
12329                                 case AIM_SENTRYGUN:
12330                                         break;
12331                                 case AIM_WARP_OUT:
12332                                         break;
12333                                 default:
12334                                         Int3();                 //      Hey, what mode is it?
12335                                         break;
12336                                 }
12337                         }
12338                 } else {
12339                         aip->nearest_locked_object = -1;
12340                 }
12341         }
12342 }
12343
12344 //      Maybe evade a dumbfire weapon that was fired when Pl_objp was targeted.
12345 //      Have an 80% chance of evading in a second
12346 void maybe_evade_dumbfire_weapon(ai_info *aip)
12347 {
12348         //      Only small ships evade an incoming missile.  Why would a capital ship try to swerve?
12349         if (!(Ship_info[Ships[Pl_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12350                 return;
12351         }
12352
12353         //      Make sure in a mode in which we evade dumbfire weapons.
12354         switch (aip->mode) {
12355         case AIM_CHASE:
12356                 if (aip->submode == SM_ATTACK_FOREVER) {
12357                         return;
12358                 }
12359         case AIM_GUARD:
12360                 //      If in guard mode and far away from guard object, don't pursue guy that hit me.
12361                 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12362                         if (vm_vec_dist_quick(&Objects[Ships[aip->shipnum].objnum].pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12363                                 return;
12364                         }
12365                 }
12366         case AIM_STILL:
12367         case AIM_STAY_NEAR:
12368         case AIM_EVADE:
12369         case AIM_GET_BEHIND:
12370         case AIM_AVOID:
12371         case AIM_PATH:
12372         case AIM_NONE:
12373         case AIM_WAYPOINTS:
12374         case AIM_SAFETY:
12375                 break;
12376         case AIM_STRAFE:
12377         case AIM_BIGSHIP:
12378         case AIM_DOCK:
12379         case AIM_PLAY_DEAD:
12380         case AIM_EVADE_WEAPON:
12381         case AIM_BAY_EMERGE:
12382         case AIM_BAY_DEPART:
12383         case AIM_SENTRYGUN:
12384         case AIM_WARP_OUT:
12385                 return;
12386         default:
12387                 Int3(); //      Bogus mode!
12388                 return;
12389         }
12390
12391         if (is_instructor(&Objects[Ships[aip->shipnum].objnum]))
12392                 return; //      Instructor doesn't evade.
12393
12394         float t = ai_endangered_by_weapon(aip);
12395         if ((t > 0.0f) && (t < 1.0f)) {
12396         // Check if this weapon is from a large ship Pl_objp is attacking... if so, enter strafe mode
12397                 if ( ai_big_maybe_enter_strafe_mode(Pl_objp, aip->danger_weapon_objnum) ) {
12398                         return;
12399                 }
12400
12401                 switch (aip->mode) {
12402                 case AIM_CHASE:
12403                         switch (aip->submode) {
12404                         case SM_EVADE:
12405                         case SM_ATTACK_FOREVER:
12406                         case SM_AVOID:
12407                         case SM_GET_AWAY:
12408                         case SM_EVADE_WEAPON:
12409                                 break;
12410                         default:
12411                                 if (ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
12412                                         //mprintf(("Ship %s entered super mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12413                                         aip->submode = SM_SUPER_ATTACK;
12414                                         aip->submode_start_time = Missiontime;
12415                                         aip->last_attack_time = Missiontime;
12416                                 } else {
12417                                         //mprintf(("Ship %s entered dumbfire evade mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12418                                         aip->submode = SM_EVADE_WEAPON;
12419                                         aip->submode_start_time = Missiontime;
12420                                 }
12421                                 break;
12422                         }
12423                         break;
12424                 case AIM_GUARD:
12425                 case AIM_STILL:
12426                 case AIM_STAY_NEAR:
12427                 case AIM_EVADE:
12428                 case AIM_GET_BEHIND:
12429                 case AIM_AVOID:
12430                 case AIM_PATH:
12431                 case AIM_NONE:
12432                 case AIM_WAYPOINTS:
12433                 case AIM_SAFETY:
12434                         if (!(aip->ai_flags & (AIF_NO_DYNAMIC | AIF_KAMIKAZE)) && (Ship_info[Ships[aip->shipnum].ship_info_index].flags & SIF_SMALL_SHIP)) {
12435                                 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12436                                 aip->previous_mode = aip->mode;
12437                                 aip->previous_submode = aip->submode;
12438                                 aip->mode = AIM_EVADE_WEAPON;
12439                                 aip->submode = -1;
12440                                 aip->submode_start_time = Missiontime;
12441                                 aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Evade for up to five seconds.
12442                         }
12443                         break;
12444                 case AIM_STRAFE:
12445                 case AIM_BIGSHIP:
12446                 case AIM_DOCK:
12447                 case AIM_PLAY_DEAD:
12448                 case AIM_EVADE_WEAPON:
12449                 case AIM_BAY_EMERGE:
12450                 case AIM_BAY_DEPART:
12451                 case AIM_SENTRYGUN:
12452                         break;
12453                 default:
12454                         Int3(); //      Bogus mode!
12455                 }
12456         }
12457 }
12458
12459 // determine what path to use when emerging from a fighter bay
12460 // input:       pl_objp =>      pointer to object for ship that is arriving
12461 //                              pos             =>      output parameter, it is the starting world pos for path choosen
12462 //                              fvec            =>      output parameter, this is the forward vector that ship has when arriving
12463 //
12464 // exit:                -1              =>      path could not be located
12465 //                               0              => success
12466 int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, vector *pos, vector *fvec)
12467 {
12468         int                     path_index, sb_path_index;
12469         ship                    *parent_sp = NULL;
12470         polymodel       *pm;
12471         ai_info         *aip;
12472         ship_bay                *sb;
12473         pnode                   *pnp;
12474         vector          *next_point;
12475
12476         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12477
12478         if ( parent_objnum == -1 ) {
12479                 Int3();
12480                 return -1;
12481         }
12482
12483         parent_sp = &Ships[Objects[parent_objnum].instance];
12484
12485         Assert(parent_sp != NULL);
12486         pm = model_get( parent_sp->modelnum );
12487         sb = pm->ship_bay;
12488
12489         if ( sb == NULL ) 
12490                 return -1;
12491
12492         if ( sb->num_paths <= 0 ) 
12493                 return -1;
12494
12495         // try to find a bay path that is not taken
12496         path_index = -1;
12497         sb_path_index = Ai_last_arrive_path++;
12498
12499         if ( sb_path_index >= sb->num_paths ) {
12500                 sb_path_index=0;
12501                 Ai_last_arrive_path=0;
12502         }
12503
12504         path_index = sb->paths[sb_path_index];
12505         if ( path_index == -1 ) 
12506                 return -1;
12507
12508         // create the path for pl_objp to follow
12509         create_model_exit_path(pl_objp, &Objects[parent_objnum], path_index, pm->paths[path_index].nverts);
12510         
12511         // Set this flag, so we don't bother recreating the path... we won't need to update the path
12512         // that has just been created.
12513 //      aip->ai_flags |= AIF_USE_STATIC_PATH;
12514
12515         // now return to the caller what the starting world pos and starting fvec for the ship will be
12516         Assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
12517         pnp = &Path_points[aip->path_start];
12518         *pos = pnp->pos;
12519
12520         // calc the forward vector using the starting two points of the path
12521         pnp = &Path_points[aip->path_start+1];
12522         next_point = &pnp->pos;
12523         vm_vec_normalized_dir(fvec, next_point, pos);
12524
12525         // record the parent objnum, since we'll need it once we're done with following the path
12526         aip->goal_objnum = parent_objnum;
12527         aip->goal_signature = Objects[parent_objnum].signature;
12528         aip->mode = AIM_BAY_EMERGE;
12529         aip->submode_start_time = Missiontime;
12530
12531         // set up starting vel
12532         vector vel;
12533         float speed;
12534         speed = Ship_info[Ships[pl_objp->instance].ship_info_index].max_speed;
12535         vel = *fvec;
12536         vm_vec_scale( &vel, speed );
12537         pl_objp->phys_info.vel = vel;
12538         pl_objp->phys_info.desired_vel = vel;
12539         pl_objp->phys_info.prev_ramp_vel.x = 0.0f;
12540         pl_objp->phys_info.prev_ramp_vel.y = 0.0f;
12541         pl_objp->phys_info.prev_ramp_vel.z = speed;
12542         pl_objp->phys_info.forward_thrust = 0.0f;               // How much the forward thruster is applied.  0-1.
12543
12544         return 0;       
12545 }
12546
12547 // clean up path data used for emerging from a fighter bay
12548 void ai_emerge_bay_path_cleanup(ai_info *aip)
12549 {
12550         aip->path_start = -1;
12551         aip->path_cur = -1;
12552         aip->path_length = 0;
12553         aip->mode = AIM_NONE;
12554 }
12555
12556 // handler for AIM_BAY_EMERGE
12557 void ai_bay_emerge()
12558 {
12559         ai_info *aip;
12560         int             parent_died=0;
12561
12562         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12563
12564         // if no path to follow, leave this mode
12565         if ( aip->path_start < 0 ) {
12566                 aip->mode = AIM_NONE;
12567                 return;
12568         }
12569
12570         // ensure parent ship is still alive
12571         if ( aip->goal_objnum < 0 ) {
12572                 parent_died=1;
12573         } 
12574         if ( !parent_died ) {
12575                 if ( Objects[aip->goal_objnum].signature != aip->goal_signature ) {
12576                         parent_died=1;
12577                 }
12578         }
12579
12580         if ( !parent_died ) {
12581                 Assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
12582                 if ( Ships[Objects[aip->goal_objnum].instance].flags & SF_DYING ) {
12583                         parent_died = 1;
12584                 }
12585         }
12586
12587         if ( parent_died ) {
12588                 ai_emerge_bay_path_cleanup(aip);
12589                 return;
12590         }
12591
12592         // follow the path to the final point
12593         ai_path();
12594
12595         // New test: must have been in AI_EMERGE mode for at least 10 seconds, and be a minimum distance from the start point
12596         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)) {
12597                 // erase path
12598                 ai_emerge_bay_path_cleanup(aip);
12599         }
12600
12601         // 2-25-99: Need this check to fix an assert for supercap ships... maybe we'll only do this check for supercaps 
12602         if (aip->path_cur > (aip->path_start+aip->path_length-1)) {
12603                 ai_emerge_bay_path_cleanup(aip);
12604         }       
12605 }
12606
12607 // Select the closest depart path
12608 //
12609 //      input:  aip     =>              ai info pointer to ship seeking to depart
12610 //                              pm              =>              pointer to polymodel for the ship contining the ship bay/depart paths
12611 //
12612 // exit:                >=0     =>              ship bay path index for depart path (ie index into sb->paths[])
12613 //                              -1              =>              no path could be found
12614 //
12615 // NOTE: this function should only be used for calculating closest depart paths for ai mode
12616 //                      AI_BAY_DEPART.  It tries to find the closest path that isn't already in use
12617 int ai_find_closest_depart_path(ai_info *aip, polymodel *pm)
12618 {
12619         int                     i, j, best_path, best_free_path;
12620         float                   dist, min_dist, min_free_dist;
12621         vector          *source;
12622         model_path      *mp;
12623         ship_bay                *sb;
12624
12625         sb = pm->ship_bay;
12626
12627         best_free_path = best_path = -1;
12628         min_free_dist = min_dist = 1e20f;
12629         Assert(aip->shipnum >= 0);
12630         source = &Objects[Ships[aip->shipnum].objnum].pos;
12631
12632         for ( i = 0; i < sb->num_paths; i++ ) {
12633
12634
12635                 mp = &pm->paths[sb->paths[i]];
12636                 for ( j = 0; j < mp->nverts; j++ ) {
12637                         dist = vm_vec_dist_squared(source, &mp->verts[j].pos);
12638
12639                         if ( dist < min_dist ) {
12640                                 min_dist = dist;
12641                                 best_path = i;
12642                         }
12643
12644                         // If this is a free path
12645                         if ( !(sb->depart_flags & (1<<i)) ) {
12646                                 if ( dist < min_free_dist ) {
12647                                         min_free_dist = dist;
12648                                         best_free_path = i;
12649                                 }
12650                         }
12651                 }
12652         }
12653
12654         if ( best_free_path >= 0 ) {
12655                 return best_free_path;          
12656         }
12657
12658         return best_path;
12659 }
12660
12661 // determine what path to use when trying to depart to a fighter bay
12662 // NOTE: this should be called when AIM_BAY_DEPART mode is set
12663 //
12664 // input:       pl_objp =>      pointer to object for ship that is departing
12665 //
12666 // exit:                -1      =>      could not find depart path
12667 //                              0       => found depart path
12668 int ai_acquire_depart_path(object *pl_objp, int parent_objnum)
12669 {
12670         int                     objnum, path_index;
12671         polymodel       *pm;
12672         ai_info         *aip;
12673         ship                    *sp;
12674         ship_bay                *sb;
12675
12676         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12677
12678         if ( parent_objnum == -1 ) {
12679                 ship_obj        *so;
12680
12681                 // for now just locate a captial ship on the same team:
12682                 so = GET_FIRST(&Ship_obj_list);
12683                 objnum = -1;
12684                 while(so != END_OF_LIST(&Ship_obj_list)){
12685                         sp = &Ships[Objects[so->objnum].instance];
12686                         if ( (Ship_info[sp->ship_info_index].flags & (SIF_HUGE_SHIP)) && (sp->team == Ships[pl_objp->instance].team) ) {
12687                                 objnum = so->objnum;
12688                                 break;
12689                         }
12690                         so = GET_NEXT(so);
12691                 } 
12692         } else {
12693                 objnum = parent_objnum;
12694         }
12695
12696         aip->path_start = -1;
12697
12698         if ( objnum == -1 )
12699                 return -1;
12700
12701         pm = model_get( Ships[Objects[objnum].instance].modelnum );
12702         sb = pm->ship_bay;
12703
12704         if ( sb == NULL ) 
12705                 return -1;
12706         if ( sb->num_paths <= 0 ) 
12707                 return -1;
12708
12709 /*
12710         
12711         path_index = -1;
12712         for ( i = 0; i < sb->num_paths; i++ ) {
12713                 if ( !(sb->depart_flags & (1<<i)) ) {
12714                         sb->depart_flags |= (1<<i);
12715                         path_index = sb->paths[i];
12716                         aip->submode_parm0 = i;                 // use mode-specific parameter to record ship bay path index
12717                         break;
12718                 }
12719         }
12720 */
12721         
12722         // take the closest path we can find
12723         int ship_bay_path;
12724         ship_bay_path = ai_find_closest_depart_path(aip, pm);
12725         path_index = sb->paths[ship_bay_path];
12726         aip->submode_parm0 = ship_bay_path;
12727         sb->depart_flags |= (1<<ship_bay_path);
12728
12729         if ( path_index == -1 ) {
12730                 return -1;
12731         }
12732
12733         Assert(pm->n_paths > path_index);
12734         ai_find_path(pl_objp, objnum, path_index, 0);
12735
12736         // Set this flag, so we don't bother recreating the path... we won't need to update the path
12737         // that has just been created.
12738         aip->ai_flags &= ~AIF_USE_STATIC_PATH;
12739
12740         aip->goal_objnum = objnum;
12741         aip->goal_signature = Objects[objnum].signature;
12742         aip->mode = AIM_BAY_DEPART;
12743
12744         Ships[pl_objp->instance].flags |= SF_DEPART_DOCKBAY;
12745         return 0;
12746 }
12747
12748 // handler for AIM_BAY_DEPART
12749 void ai_bay_depart()
12750 {
12751         ai_info *aip;
12752
12753         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12754
12755         // if no path to follow, leave this mode
12756         if ( aip->path_start < 0 ) {
12757                 aip->mode = AIM_NONE;
12758                 return;
12759         }
12760
12761         // check if parent ship still exists, if not abort depart 
12762         if ( aip->goal_signature != Objects[aip->goal_objnum].signature ) {
12763                 aip->mode = AIM_NONE;
12764                 return;
12765         }
12766
12767         // follow the path to the final point
12768         ai_path();
12769
12770         // if the final point is reached, let default AI take over
12771         if ( aip->path_cur >= (aip->path_start+aip->path_length) ) {
12772                 polymodel       *pm;
12773                 ship_bay                *sb;
12774
12775                 pm = model_get( Ships[Objects[aip->goal_objnum].instance].modelnum );
12776                 sb = pm->ship_bay;
12777                 if ( sb != NULL ) {
12778                         sb->depart_flags &= ~(1<<aip->submode_parm0);
12779                 }
12780
12781                 // make ship disappear
12782                 Pl_objp->flags |= OF_SHOULD_BE_DEAD;
12783                 ship_departed( Pl_objp->instance );
12784
12785                 // clean up path stuff
12786                 aip->path_start = -1;
12787                 aip->path_cur = -1;
12788                 aip->path_length = 0;
12789                 aip->mode = AIM_NONE;
12790         }
12791 }
12792
12793 // Handler for AIM_SENTRYGUN.  This AI mode is for sentry guns only (ie floating turrets).
12794 void ai_sentrygun()
12795 {
12796         // Nothing to do here.  Turret firing is handled via process_subobjects().
12797         // If you want the sentry guns to do anything beyond firing their turrets at enemies, add it here!
12798 }
12799
12800 //      --------------------------------------------------------------------------
12801 //      Execute behavior given by aip->mode.
12802 void ai_execute_behavior(ai_info *aip)
12803 {
12804         switch (aip->mode) {
12805         case AIM_CHASE:
12806                 if (En_objp) {
12807                         ai_chase();
12808                 } else if (aip->submode == SM_EVADE_WEAPON) {
12809                         evade_weapon();
12810                         // maybe reset submode
12811                         if (aip->danger_weapon_objnum == -1) {
12812                                 aip->submode = SM_ATTACK;
12813                                 aip->submode_start_time = Missiontime;
12814                                 aip->last_attack_time = Missiontime;
12815                         }
12816                 } else {
12817                         //      Don't circle if this is the instructor.
12818                         ship    *shipp = &Ships[aip->shipnum];
12819                         ship_info       *sip = &Ship_info[shipp->ship_info_index];
12820
12821                         if (strnicmp(shipp->ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME))) {
12822                                 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
12823                                         aip->mode = AIM_NONE;
12824                                 } else {
12825                                         ai_chase_circle(Pl_objp);
12826                                 }
12827                         }
12828                 }
12829                 break;
12830         case AIM_EVADE:
12831                 if (En_objp) {
12832                         ai_evade();
12833                 } else {
12834                         vector  tvec;
12835                         vm_vec_scale_add(&tvec, &Pl_objp->pos, &Pl_objp->orient.rvec, 100.0f);
12836                         turn_towards_point(Pl_objp, &tvec, NULL, 0.0f);
12837                         accelerate_ship(aip, 0.5f);
12838                 }
12839                 break;
12840         case AIM_STILL:
12841                 ai_still();
12842                 break;
12843         case AIM_STAY_NEAR:
12844                 ai_stay_near();
12845                 break;
12846         case AIM_GUARD:
12847                 ai_guard();
12848                 break;
12849         case AIM_WAYPOINTS:
12850                 ai_waypoints();
12851                 break;
12852         case AIM_DOCK:
12853                 ai_dock();
12854                 break;
12855         case AIM_NONE:
12856                 // ai_formation();
12857                 break;
12858         case AIM_BIGSHIP:
12859                 ai_big_ship(Pl_objp);
12860                 break;
12861         case AIM_PATH: {
12862                 int path_num;
12863                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], 0);
12864                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
12865                 ai_path();
12866                 break;
12867         }
12868         case AIM_SAFETY:
12869                 ai_safety();
12870                 break;
12871         case AIM_EVADE_WEAPON:
12872                 evade_weapon();
12873                 break;
12874         case AIM_STRAFE:
12875                 if (En_objp) {
12876                         Assert(En_objp->type == OBJ_SHIP);
12877                         ai_big_strafe();        // strafe a big ship
12878                 } else {
12879                         aip->mode = AIM_NONE;
12880                 }
12881                 break;
12882         case AIM_BAY_EMERGE:
12883                 ai_bay_emerge();
12884                 break;
12885         case AIM_BAY_DEPART:
12886                 ai_bay_depart();
12887                 break;
12888         case AIM_SENTRYGUN:
12889                 ai_sentrygun();
12890                 break;
12891         case AIM_WARP_OUT:
12892                 break;          //      Note, handled directly from ai_frame().
12893         default:
12894                 Int3();         //      This should never happen -- MK, 5/12/97 
12895                 break;
12896         }
12897
12898         if ( !(ship_get_SIF(aip->shipnum) & SIF_NOT_FLYABLE) ) {
12899                 maybe_evade_dumbfire_weapon(aip);
12900         }
12901 }
12902
12903 //      Auxiliary function for maybe_request_support.
12904 //      Return 1 if subsystem "type" is worthy of repair, else return 0.
12905 //      Since subsystems cannot be repaired if they are at 0 strength, don't return 1 if subsystem is dead.
12906 int mrs_subsystem(ship *shipp, int type)
12907 {
12908         float   t;
12909
12910         t = ship_get_subsystem_strength(shipp, type);
12911
12912         if (t > 0.0f) {
12913                 return (int) ((1.0f - t) * 3);
12914         } else {
12915                 return 3;
12916         }
12917 }
12918
12919 //      Return number of ships on *objp's team that are currently rearming.
12920 int num_allies_rearming(object *objp)
12921 {
12922         ship_obj        *so;
12923         int             team;
12924         int             count = 0;
12925
12926         team = Ships[objp->instance].team;
12927
12928         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
12929                 object  *A;
12930                 
12931                 Assert (so->objnum != -1);
12932                 A = &Objects[so->objnum];
12933
12934                 if (Ships[A->instance].team == team) {
12935                         if (Ai_info[Ships[A->instance].ai_index].ai_flags & (AIF_REPAIRING | AIF_AWAITING_REPAIR)) {
12936                                 count++;
12937                         }
12938                 }
12939         }
12940
12941         return count;
12942 }
12943
12944
12945 //      Maybe ship *objp should request support (rearm/repair).
12946 //      If it does, return TRUE, else return FALSE.
12947 int maybe_request_support(object *objp)
12948 {
12949         ship_info       *sip;
12950         ship                    *shipp;
12951         ai_info         *aip;
12952         int                     desire;
12953
12954         Assert(objp->type == OBJ_SHIP);
12955         shipp = &Ships[objp->instance];
12956         aip = &Ai_info[shipp->ai_index];
12957         sip = &Ship_info[shipp->ship_info_index];
12958
12959         if (!timestamp_elapsed(aip->next_rearm_request_timestamp))
12960                 return 0;
12961
12962         //      Only fighters and bombers request support.
12963         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER)))
12964                 return 0;
12965
12966         //      A ship that is currently awaiting does not need support!
12967         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
12968                 return 0;
12969
12970         if (!is_support_allowed(objp))
12971                 return 0;
12972
12973         //if (shipp->team != TEAM_FRIENDLY)
12974         //      return 0;
12975
12976         //      Compute a desire value.
12977         //      Desire of 0 means no reason to request support.
12978         //      1 is slight, 2 more, etc.  Maximum is around 20.  Anything larger than 3 is pretty strong.
12979         desire = 0;
12980
12981         //      Set desire based on hull strength.
12982         //      No: We no longer repair hull, so this would cause repeated repair requests.
12983         //desire += 6 - (int) ((objp->hull_strength/sip->initial_hull_strength) * 6.0f);
12984
12985         //      Set desire based on key subsystems.
12986         desire += 2*mrs_subsystem(shipp, SUBSYSTEM_ENGINE);     //      Note, disabled engine forces repair request, regardless of nearby enemies.
12987         desire += mrs_subsystem(shipp, SUBSYSTEM_COMMUNICATION);
12988         desire += mrs_subsystem(shipp, SUBSYSTEM_WEAPONS);
12989         desire += mrs_subsystem(shipp, SUBSYSTEM_SENSORS);
12990
12991         //      Set desire based on percentage of secondary weapons.
12992         ship_weapon *swp = &shipp->weapons;
12993
12994         for ( int i = 0; i < swp->num_secondary_banks; i++ ) {
12995                 if (swp->secondary_bank_start_ammo[i] > 0) {
12996 //                      float r = (float) swp->secondary_bank_ammo[i]*Weapon_info[swp->secondary_bank_weapons[i]].cargo_size/swp->secondary_bank_capacity[i];
12997                         float r = (float) swp->secondary_bank_ammo[i]/swp->secondary_bank_start_ammo[i];
12998                         desire += (int) ((1.0f - r) * 3.0f);
12999                 }
13000         }
13001
13002         //      If no reason to repair, don't bother to see if it's safe to repair.
13003         if (desire == 0){
13004                 return 0;
13005         }
13006
13007         //      Compute danger threshold.
13008         //      Balance this with desire and maybe request support.
13009         if (ai_good_time_to_rearm( objp )) {
13010                 ai_issue_rearm_request(objp);
13011                 return 1;
13012         } else if (num_allies_rearming(objp) < 2) {
13013                 if (desire >= 8) {      //      guarantees disabled will cause repair request
13014                         ai_issue_rearm_request(objp);
13015                 } else if (desire >= 3) {               //      >= 3 means having a single subsystem fully blown will cause repair.
13016                         int     count;
13017                         int objnum = find_nearby_hostile(OBJ_INDEX(objp), get_enemy_team_mask(OBJ_INDEX(objp)), 2000.0f, &count);
13018
13019                         if ((objnum == -1) || (count < 2) || (vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos) > 3000.0f*count/desire)) {
13020                                 ai_issue_rearm_request(objp);
13021                                 return 1;
13022                         } else {
13023                                 //nprintf(("AI", "Would like to rearm, but enemy only %7.3f units away.\n", vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos)));
13024                         }
13025                 }
13026         }
13027
13028         return 0;
13029
13030 }
13031
13032 void ai_set_mode_warp_out(object *objp, ai_info *aip)
13033 {
13034         ai_abort_rearm_request(objp);
13035         if (aip->mode != AIM_WARP_OUT) {
13036                 aip->mode = AIM_WARP_OUT;
13037                 aip->submode = AIS_WARP_1;
13038         }
13039 }
13040
13041 //      Maybe warp ship out.
13042 //      Shivan and HoL fighter/bomber warp out if their weapons subsystems have been destroyed.
13043 void ai_maybe_warp_out(object *objp)
13044 {
13045         ship    *shipp;
13046
13047         // don't do anything if in a training mission.
13048         if ( The_mission.game_type & MISSION_TYPE_TRAINING )
13049                 return;
13050
13051         Assert(objp->type == OBJ_SHIP);
13052
13053         shipp = &Ships[objp->instance];
13054         ai_info *aip = &Ai_info[shipp->ai_index];
13055
13056         if (aip->mode == AIM_WARP_OUT)
13057                 return;
13058
13059         //      If a support ship with no goals and low hull, warp out.  Be sure that there are no pending goals
13060         // in the support ships ai_goal array.  Just process this ships goals.
13061         ship_info       *sip = &Ship_info[shipp->ship_info_index];
13062         if (sip->flags & SIF_SUPPORT) {
13063                 if ( timestamp_elapsed(aip->warp_out_timestamp) ) {
13064                         ai_process_mission_orders( OBJ_INDEX(objp), aip );
13065                         if ( (aip->dock_objnum == -1) && (objp->hull_strength/sip->initial_hull_strength < 0.25f) ) {
13066                                 ai_set_mode_warp_out(objp, aip);
13067                         }
13068                 }
13069         }
13070
13071         //      Friendly don't warp out, they'll eventually request support.
13072         if (shipp->team == TEAM_FRIENDLY)
13073                 return;
13074
13075         if (!(shipp->flags & SF_DEPARTING)) {
13076                 ship_info       *sip;
13077
13078                 sip = &Ship_info[shipp->ship_info_index];
13079                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
13080                         if (aip->warp_out_timestamp == 0) {
13081                                 //if (ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS) == 0.0f) {
13082                                 //      aip->warp_out_timestamp = timestamp(((myrand() % 10) + 10) * 1000);
13083                                 //}
13084                         } else if (timestamp_elapsed(aip->warp_out_timestamp)) {
13085                                 ai_set_mode_warp_out(objp, aip);
13086                         }
13087                 }
13088         }
13089 }
13090
13091 //      Warp this ship out.
13092 void ai_warp_out(object *objp)
13093 {
13094         // if dying, don't warp out.
13095         if (Ships[objp->instance].flags & SF_DYING) {
13096                 return;
13097         }
13098
13099         ai_info *aip;
13100
13101         aip = &Ai_info[Ships[objp->instance].ai_index];
13102
13103         switch (aip->submode) {
13104         case AIS_WARP_1:
13105                 aip->force_warp_time = timestamp(10*1000);      //      Try to avoid a collision for up to ten seconds.
13106                 aip->submode = AIS_WARP_2;
13107                 break;
13108         case AIS_WARP_2:                        //      Make sure won't collide with any object.
13109                 if (timestamp_elapsed(aip->force_warp_time) || !collide_predict_large_ship(objp, objp->radius*2.0f + 100.0f)) {
13110                         aip->submode = AIS_WARP_3;
13111
13112                         // maybe recalculate collision pairs.
13113                         if (ship_get_warp_speed(objp) > ship_get_max_speed(&Ships[objp->instance])) {
13114                                 // recalculate collision pairs
13115                                 OBJ_RECALC_PAIRS(objp); 
13116                         }
13117
13118                         aip->force_warp_time = timestamp(4*1000);               //      Try to attain target speed for up to 4 seconds.
13119                 } else {
13120                         vector  goal_point;
13121                         vm_vec_scale_add(&goal_point, &objp->pos, &objp->orient.uvec, 100.0f);
13122                         turn_towards_point(objp, &goal_point, NULL, 0.0f);
13123                         accelerate_ship(aip, 0.0f);
13124                 }
13125                 break;
13126         case AIS_WARP_3:
13127                 //      Rampup desired_vel in here from current to desired velocity and set PF_USE_VEL. (not sure this is the right flag)
13128                 //      desired velocity is computed in shipfx_calculate_warp_time().  See shipfx#572 for sample code.
13129                 float   speed, goal_speed;
13130                 float shipfx_calculate_warp_speed(object*);
13131                 goal_speed = shipfx_calculate_warp_speed(objp);
13132
13133                 // HUGE ships go immediately to AIS_WARP_4
13134                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_HUGE_SHIP) {
13135                         aip->submode = AIS_WARP_4;
13136                         break;
13137                 }
13138                 //compute_warpout_stuff(objp, &goal_speed, &warp_time, &warp_pos);
13139                 //goal_speed = 80.0f;
13140                 //set_accel_for_target_speed(objp, 40.0f);
13141                 // 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
13142                 speed = goal_speed * flFrametime + objp->phys_info.speed * (1.0f - flFrametime);
13143                 vm_vec_copy_scale(&objp->phys_info.vel, &objp->orient.fvec, speed);
13144                 objp->phys_info.desired_vel = objp->phys_info.vel;
13145                 // nprintf(("AI", "Frame %i, speed = %7.3f, goal = %7.3f\n", Framecount, vm_vec_mag_quick(&objp->phys_info.vel), goal_speed));
13146                 if (timestamp_elapsed(aip->force_warp_time) || (fl_abs(objp->phys_info.speed - goal_speed) < 2.0f))
13147                         aip->submode = AIS_WARP_4;
13148                 break;
13149         case AIS_WARP_4: {
13150                 shipfx_warpout_start(objp);
13151                 aip->submode = AIS_WARP_5;
13152                 break;
13153         }
13154         case AIS_WARP_5:
13155                 break;
13156         default:
13157                 Int3();         //      Illegal submode for warping out.
13158         }
13159 }
13160
13161 //      Return object index of weapon that could produce a shockwave that should be known about to *objp.
13162 //      Return nearest one.
13163 int ai_find_shockwave_weapon(object *objp, ai_info *aip)
13164 {
13165         missile_obj     *mo;
13166         float   nearest_dist = 999999.9f;
13167         int     nearest_index = -1;
13168
13169         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
13170                 object          *A;
13171                 weapon          *wp;
13172                 weapon_info     *wip;
13173         
13174                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
13175                 A = &Objects[mo->objnum];
13176
13177                 Assert(A->type == OBJ_WEAPON);
13178                 Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
13179                 wp = &Weapons[A->instance];
13180                 wip = &Weapon_info[wp->weapon_info_index];
13181                 Assert( wip->subtype == WP_MISSILE );
13182
13183                 if (wip->shockwave_speed > 0.0f) {
13184                         float   dist;
13185
13186                         dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13187                         if (dist < nearest_dist) {
13188                                 nearest_dist = dist;
13189                                 nearest_index = mo->objnum;
13190                         }
13191                 }
13192         }
13193
13194         return nearest_index;
13195
13196 }
13197
13198 #define EVADE_SHOCKWAVE_DAMAGE_THRESHOLD                100.0f
13199
13200 //      Tell all ships to avoid a big ship that is blowing up.
13201 //      Only avoid if shockwave is fairly large.
13202 //      OK to tell everyone to avoid.  If they're too far away, that gets cleaned up in the frame interval.
13203 void ai_announce_ship_dying(object *dying_objp)
13204 {
13205         float damage = ship_get_exp_damage(dying_objp);
13206         if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {
13207                 ship_obj        *so;
13208
13209                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13210                         if (Ship_info[Ships[Objects[so->objnum].instance].ship_info_index].flags & (SIF_SMALL_SHIP | SIF_FREIGHTER)) {
13211                                 ai_info *aip;
13212
13213                                 aip = &Ai_info[Ships[Objects[so->objnum].instance].ai_index];
13214
13215                                 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
13216                                         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_SHIP;
13217                                 }
13218                         }
13219                 }
13220         }
13221 }
13222
13223
13224 //      Return object index of weapon that could produce a shockwave that should be known about to *objp.
13225 //      Return nearest one.
13226 int ai_find_shockwave_ship(object *objp, ai_info *aip)
13227 {
13228         ship_obj        *so;
13229         float   nearest_dist = 999999.9f;
13230         int     nearest_index = -1;
13231
13232         for ( so = GET_NEXT(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13233                 object          *A;
13234                 ship                    *shipp;
13235         
13236                 Assert(so->objnum >= 0 && so->objnum < MAX_OBJECTS);
13237                 A = &Objects[so->objnum];
13238
13239                 Assert(A->type == OBJ_SHIP);
13240                 Assert((A->instance >= 0) && (A->instance < MAX_SHIPS));
13241                 shipp = &Ships[A->instance];
13242                 //      Only look at objects in the process of dying.
13243                 if (shipp->flags & SF_DYING) {
13244                         float damage = ship_get_exp_damage(objp);
13245
13246                         if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {               //      Only evade quite large blasts
13247                                 float   dist;
13248
13249                                 dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13250                                 if (dist < nearest_dist) {
13251                                         nearest_dist = dist;
13252                                         nearest_index = so->objnum;
13253                                 }
13254                         }
13255                 }
13256         }
13257
13258         return nearest_index;
13259
13260 }
13261
13262 int aas_1(object *objp, ai_info *aip, vector *safe_pos)
13263 {
13264         // MAKE SURE safe_pos DOES NOT TAKE US TOWARD THE A SHIP WE'RE ATTACKING.
13265         if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_WEAPON) {
13266                 //      If we don't currently know of a weapon to avoid, try to find one.
13267                 //      If we can't find one, then clear the bit so we don't keep coming here.
13268                 if (aip->shockwave_object == -1) {
13269                         int shockwave_weapon = ai_find_shockwave_weapon(objp, aip);
13270                         if (shockwave_weapon == -1) {
13271                                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13272                                 return 0;
13273                         } else {
13274                                 aip->shockwave_object = shockwave_weapon;
13275                         }
13276                 }
13277
13278                 //      OK, we have reason to believe we should avoid aip->shockwave_object.
13279                 Assert(aip->shockwave_object > -1);
13280                 object  *weapon_objp = &Objects[aip->shockwave_object];
13281                 if (weapon_objp->type != OBJ_WEAPON) {
13282                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13283                         aip->shockwave_object = -1;
13284                         return 0;
13285                 }
13286
13287                 weapon  *weaponp = &Weapons[weapon_objp->instance];
13288                 weapon_info     *wip = &Weapon_info[weaponp->weapon_info_index];
13289                 object *target_ship_obj = NULL;
13290
13291                 if (wip->shockwave_speed == 0.0f) {
13292                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13293                         aip->shockwave_object = -1;
13294                         return 0;
13295                 }
13296
13297                 float   danger_dist;
13298                 vector  expected_pos;           //      Position at which we expect the weapon to detonate.
13299                 int             pos_set = 0;
13300
13301                 danger_dist = wip->outer_radius;
13302                 //      Set predicted position of detonation.
13303                 //      If an aspect locked missile, assume it will detonate at the homing position.
13304                 //      If not, which is not possible in a default FreeSpace weapon, then predict it will detonate at some
13305                 //      time in the future, this time based on max lifetime and life left.
13306                 if (wip->wi_flags & WIF_HOMING_ASPECT) {
13307                         expected_pos = weaponp->homing_pos;
13308                         if (weaponp->homing_object && weaponp->homing_object->type == OBJ_SHIP) {
13309                                 target_ship_obj = weaponp->homing_object;
13310                         }
13311                         pos_set = 1;
13312                         if (IS_VEC_NULL(&weaponp->homing_pos)) {
13313                                 pos_set = 0;
13314                                 if (weaponp->target_num != -1) {
13315                                         if (Objects[weaponp->target_num].type == OBJ_SHIP) {
13316                                                 target_ship_obj = &Objects[weaponp->target_num];
13317                                                 expected_pos = target_ship_obj->pos;
13318                                                 pos_set = 1;
13319                                         }
13320                                 }
13321                         }
13322                 }
13323
13324                 if (!pos_set) {
13325                         float   time_scale;
13326
13327                         if (wip->lifetime - weaponp->lifeleft > 5.0f) {
13328                                 time_scale = 1.0f;
13329                         } else {
13330                                 time_scale = weaponp->lifeleft/2.0f;
13331                         }
13332
13333                         vm_vec_scale_add(&expected_pos, &weapon_objp->pos, &weapon_objp->orient.fvec, time_scale);
13334                 }
13335
13336                 //      See if too far away to care about shockwave.
13337                 if (vm_vec_dist_quick(&objp->pos, &expected_pos) > danger_dist*2.0f) {
13338                         //aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13339                         return 0;
13340                 } else {
13341                         // try to find a safe position
13342                         vector vec_from_exp;
13343                         float dir = 1.0f;
13344                         vm_vec_sub(&vec_from_exp, &objp->pos, &expected_pos);
13345                         float dot = vm_vec_dotprod(&vec_from_exp, &weapon_objp->orient.fvec);
13346                         if (dot > -30) {
13347                                 // if we're already on the other side of the explosion, don't try to fly behind it
13348                                 dir = -1.0f;
13349                         }
13350
13351                         //      Fly towards a point behind the weapon.
13352                         vm_vec_scale_add(safe_pos, &weapon_objp->pos, &weapon_objp->orient.fvec, -50000.0f*dir);
13353
13354                         // verify safe_pos will not make us collide with our target objnum, else try 2 other vecs
13355                         // don't bang your head, else go
13356 //                      int go_safe = FALSE;
13357                         int go_safe = TRUE;
13358 /*                      if (target_ship_obj) {
13359                                 if (pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius)) {
13360                                         // try up to 2 other random directions
13361                                         vector dir_vec, rand_vec;
13362                                         int idx;
13363                                         for (idx=0; idx<2; idx++) {
13364                                                 vm_vec_rand_vec_quick(&rand_vec);
13365                                                 vm_vec_scale_add(&dir_vec, &weapon_objp->orient.fvec, &rand_vec, 0.5f);
13366                                                 vm_vec_scale_add(safe_pos, &weapon_objp->pos, &dir_vec, -50000.0f*dir);
13367                                                 if ( !pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius) ) {
13368                                                         go_safe = TRUE;
13369                                                         break;
13370                                                 }
13371                                         }
13372                                 } else { // direct path is safe
13373                                         go_safe = TRUE;
13374                                 }
13375                         } else { // no target_obj_ship
13376                                 go_safe = TRUE;
13377                         } */
13378
13379                         if (go_safe) {
13380                                 return 1;
13381                         } else {
13382                                 // can't figure out a good way to go
13383                                 return 0;
13384                         }
13385                 }
13386         } else if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_SHIP) {
13387                 if (aip->shockwave_object == -1) {
13388                         int shockwave_ship = ai_find_shockwave_ship(objp, aip);
13389                         if (shockwave_ship == -1) {
13390                                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13391                                 return 0;
13392                         } else {
13393                                 aip->shockwave_object = shockwave_ship;
13394                         }
13395                 }
13396
13397                 Assert(aip->shockwave_object > -1);
13398                 object  *ship_objp = &Objects[aip->shockwave_object];
13399                 if (ship_objp == objp) {
13400                         aip->shockwave_object = -1;
13401                         return 0;
13402                 }
13403
13404                 if (ship_objp->type != OBJ_SHIP) {
13405                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13406                         return 0;
13407                 }
13408
13409                 //      Optimize note! Don't really have to normalize.  We only need a point away from the blowing-up ship.
13410                 vector safe_vec;
13411
13412                 vm_vec_normalized_dir(&safe_vec, &objp->pos, &ship_objp->pos);
13413                 vm_vec_scale_add(safe_pos, &ship_objp->pos, &safe_vec, 50000.0f);       //      Fly away from the ship.
13414
13415                 float outer_rad = ship_get_exp_outer_rad(ship_objp);
13416
13417                 if (vm_vec_dist_quick(&objp->pos, &ship_objp->pos) > outer_rad*1.5f) {
13418                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13419                         return 0;
13420                 }
13421
13422                 return 1;
13423
13424         } else {
13425                 Int3(); //      Illegal -- supposedly avoiding a shockwave, but neither ship nor weapon.  What is it!?
13426         }
13427
13428         return 0;
13429 }
13430
13431 /*
13432 int rct_done = 0;
13433
13434 void rand_chance_test()
13435 {
13436         int     i;
13437         float   frametime;
13438
13439         if (rct_done)
13440                 return;
13441
13442         rct_done = 1;
13443
13444         for (frametime=0.02f; frametime<0.25f; frametime *= 1.25f) {
13445                 float   chance;
13446
13447                 nprintf(("AI", "%6.4f: ", frametime));
13448                 for (chance=0.25f; chance<2.5f; chance += 0.25f) {
13449                         int count = 0;
13450
13451                         for (i=0; i<100.0f/frametime; i++) {
13452                                 if (rand_chance(frametime, chance))
13453                                         count++;
13454                         }
13455                         nprintf(("AI", "%3i ", count));
13456                 }
13457                 nprintf(("AI", "\n"));
13458         }
13459 }
13460 */
13461
13462 //      --------------------------------------------------------------------------
13463 //      Make object *objp avoid the nearest dangerous shockwave-producing weapon.
13464 //      If it looks like there is no valid shockwave-producing weapon then clear the AIF_AVOID_SHOCKWAVE_WEAPON bit in ai_flags and return.
13465 //      Return 1 if avoiding a shockwave, else return 0.
13466 int ai_avoid_shockwave(object *objp, ai_info *aip)
13467 {
13468         vector  safe_pos;
13469
13470         //rand_chance_test();
13471         // BIG|HUGE do not respond to shockwaves
13472         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
13473                 // don't come here again
13474                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE;
13475                 return 0;
13476         }
13477
13478         //      Don't all react right away.
13479         if (!(aip->ai_flags & AIF_AVOID_SHOCKWAVE_STARTED))
13480                 if (!rand_chance(flFrametime, (float) aip->ai_class/4.0f + 0.25f))      //      Chance to avoid in 1 second is 0.25 + ai_class/4
13481                         return 0;
13482
13483         if (!aas_1(objp, aip, &safe_pos)) {
13484                 aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13485                 return 0;
13486         }
13487
13488         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13489
13490         //      OK, evade the shockwave!
13491         turn_towards_point(objp, &safe_pos, NULL, 0.0f);
13492         vector  vec_to_safe_pos;
13493         float           dot_to_goal;
13494
13495         vm_vec_normalized_dir(&vec_to_safe_pos, &safe_pos, &objp->pos);
13496
13497         dot_to_goal = vm_vec_dot(&objp->orient.fvec, &vec_to_safe_pos);
13498         if (dot_to_goal < -0.5f)
13499                 accelerate_ship(aip, 0.3f);
13500         else {
13501                 accelerate_ship(aip, 1.0f + dot_to_goal);
13502                 if (dot_to_goal > 0.2f) {
13503                         if (!(objp->phys_info.flags & PF_AFTERBURNER_ON )) {
13504                                 afterburners_start(objp);
13505                                 aip->afterburner_stop_time = Missiontime + 2*F1_0;
13506                         }
13507                 }
13508         }
13509
13510         return 1;
13511 }
13512
13513 //      Awaiting repair.  Be useful.
13514 //      Probably fly towards incoming repair ship.
13515 //      Return true if this ship is close to being repaired, else return false.
13516 int ai_await_repair_frame(object *objp, ai_info *aip)
13517 {
13518         if (!(aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)))
13519                 return 0;
13520
13521         if (aip->dock_objnum == -1)
13522                 return 0;
13523
13524         ship    *shipp;
13525         ship_info       *sip;
13526
13527         shipp = &Ships[Objects[aip->dock_objnum].instance];
13528         sip = &Ship_info[shipp->ship_info_index];
13529
13530         aip->ai_flags &= ~AIF_FORMATION_OBJECT; //      Prevents endless rotation.
13531
13532         if (!(sip->flags & SIF_SUPPORT))
13533                 return 0;
13534
13535         vector  goal_point;
13536         object  *repair_objp;
13537
13538         repair_objp = &Objects[aip->dock_objnum];
13539
13540         if (Ships[repair_objp->instance].team == TEAM_TRAITOR) {
13541                 ai_abort_rearm_request(repair_objp);
13542                 return 0;
13543         }
13544
13545         vm_vec_scale_add(&goal_point, &repair_objp->pos, &repair_objp->orient.uvec, -50.0f);    //      Fly towards point below repair ship.
13546
13547         vector  vtr;
13548         float dist = vm_vec_normalized_dir(&vtr, &goal_point, &objp->pos);
13549         float dot = vm_vec_dot(&vtr, &objp->orient.fvec);
13550
13551         if (dist > 200.0f) {
13552                 //nprintf(("AI", "%s flying towards %s for repair, dist = %7.3f\n", Ships[objp->instance].ship_name, &Ships[repair_objp->instance].ship_name, dist));
13553                 accelerate_ship(aip, (0.9f + dot) * dist/1500.0f);
13554                 turn_towards_point(objp, &goal_point, NULL, 0.0f);
13555         } else {
13556                 accelerate_ship(aip, 0.0f);
13557                 //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));
13558         }
13559
13560         return 1;
13561 }
13562
13563 //      Maybe cause this ship to self-destruct.
13564 //      Currently, any small ship (SIF_SMALL_SHIP) that has been disabled will self-destruct after awhile.
13565 //      Maybe should only do this if they are preventing their wing from re-entering.
13566 void ai_maybe_self_destruct(object *objp, ai_info *aip)
13567 {
13568         //      Friendly ships can be repaired, so no self-destruct.
13569         //      In multiplayer, just don't self-destruct.  I figured there would be a problem. -- MK, 3/19/98.
13570         if ((Ships[objp->instance].team == TEAM_FRIENDLY) || (Game_mode & GM_MULTIPLAYER))
13571                 return;
13572
13573         //      Small ships in a wing blow themselves up after awhile if engine or weapons system has been destroyed.
13574         //      Reason: Don't want them to prevent a re-emergence of the wing.
13575         //      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
13576         //      mission would be broken.
13577         if ((Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP) && (Ships[objp->instance].wingnum != -1)) {
13578                 if ((ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) ||
13579                         (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_WEAPONS) <= 0.0f)) {
13580                         if (aip->self_destruct_timestamp < 0)
13581                                 aip->self_destruct_timestamp = timestamp(90 * 1000);    //      seconds until self-destruct
13582                 } else {
13583                         aip->self_destruct_timestamp = -1;
13584                 }
13585
13586                 if (aip->self_destruct_timestamp < 0) {
13587                         return;
13588                 }
13589
13590                 if (timestamp_elapsed(aip->self_destruct_timestamp)) {
13591                         ship_apply_local_damage( objp, objp, &objp->pos, objp->hull_strength*flFrametime + 1.0f, MISS_SHIELDS);
13592                 }
13593         }
13594 }
13595
13596 // Determine if pl_objp needs a new target, called from ai_frame()
13597 int ai_need_new_target(object *pl_objp, int target_objnum)
13598 {
13599         object *objp;
13600
13601         if ( target_objnum < 0 ) {
13602                 return 1;
13603         }
13604
13605         objp = &Objects[target_objnum];
13606
13607         if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID) && (objp->type != OBJ_WEAPON) ) {
13608                 return 1;
13609         }
13610
13611         if ( objp->type == OBJ_SHIP ) {
13612                 if ( Ships[objp->instance].flags & SF_DYING ) {
13613                         return 1;
13614                 } else if (Ships[objp->instance].team == Ships[pl_objp->instance].team)
13615                         return 1;
13616         }
13617
13618         return 0;
13619 }
13620
13621 //      If *objp is recovering from a collision with a big ship, handle it.
13622 //      Return true if recovering.
13623 int maybe_big_ship_collide_recover_frame(object *objp, ai_info *aip)
13624 {
13625         float   dot, dist;
13626         vector  v2g;
13627         
13628         if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1) {
13629                 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);
13630                 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_1, &objp->pos);
13631                 dot = vm_vec_dot(&objp->orient.fvec, &v2g);
13632                 accelerate_ship(aip, dot);
13633
13634                 //      If close to desired point, or 15+ seconds since entered this mode, continue to next mode.
13635                 if ((timestamp_until(aip->big_recover_timestamp) < -15*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13636                         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_1;
13637                         aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13638                 }
13639
13640                 return 1;
13641
13642         } else if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_2) {
13643                 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);
13644                 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_2, &objp->pos);
13645                 dot = vm_vec_dot(&objp->orient.fvec, &v2g);
13646                 accelerate_ship(aip, dot);
13647
13648                 //      If close to desired point, or 30+ seconds since started avoiding collision, done avoiding.
13649                 if ((timestamp_until(aip->big_recover_timestamp) < -30*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13650                         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13651                         aip->ai_flags &= ~AIF_TARGET_COLLISION;
13652                 }
13653
13654                 return 1;
13655         }
13656
13657         if (aip->ai_flags & AIF_TARGET_COLLISION) {
13658                 aip->ai_flags &= ~AIF_TARGET_COLLISION;
13659         }
13660         return 0;
13661 }
13662
13663 void validate_mode_submode(ai_info *aip)
13664 {
13665         switch (aip->mode) {
13666         case AIM_CHASE:
13667                 // check valid submode
13668                 switch (aip->submode) {
13669                 case SM_CONTINUOUS_TURN:
13670                 case SM_ATTACK:
13671                 case SM_EVADE_SQUIGGLE:
13672                 case SM_EVADE_BRAKE:    
13673                 case SM_EVADE:          
13674                 case SM_SUPER_ATTACK:
13675                 case SM_AVOID:  
13676                 case SM_GET_BEHIND:
13677                 case SM_GET_AWAY:               
13678                 case SM_EVADE_WEAPON:
13679                 case SM_FLY_AWAY:       
13680                 case SM_ATTACK_FOREVER:
13681                         break;
13682                 default:
13683                         Int3();
13684                 }
13685                 break;
13686
13687         case AIM_STRAFE:
13688                 // check valid submode
13689                 switch(aip->submode) {
13690                 case AIS_STRAFE_ATTACK:
13691                 case AIS_STRAFE_AVOID:
13692                 case AIS_STRAFE_RETREAT1:
13693                 case AIS_STRAFE_RETREAT2:
13694                 case AIS_STRAFE_POSITION:
13695                         break;
13696                 default:
13697                         Int3();
13698                 }
13699                 break;
13700         }
13701 }
13702
13703 //      --------------------------------------------------------------------------
13704 // Process AI object "objnum".
13705 void ai_frame(int objnum)
13706 {
13707         ship            *shipp = &Ships[Objects[objnum].instance];
13708         ai_info *aip = &Ai_info[shipp->ai_index];
13709         int             target_objnum;
13710
13711 //      validate_mode_submode(aip);
13712
13713         Assert((aip->mode != AIM_WAYPOINTS) || (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC));
13714
13715         // Set globals defining the current object and its enemy object.
13716         Pl_objp = &Objects[objnum];
13717
13718         if (aip->mode == AIM_WARP_OUT) {
13719                 ai_warp_out(Pl_objp);
13720                 return;
13721         }
13722
13723 /*      //      HACK! TEST! REMOVE ME!
13724         if (Ship_info[shipp->ship_info_index].flags & SIF_BIG_SHIP)
13725                 if (shipp->team == Player_ship->team)
13726                         aip->mode = AIM_CHASE;
13727 */
13728
13729 //      if (!strnicmp(Ships[Pl_objp->instance].ship_name, "cancer", 6))
13730 //              nprintf(("AI", "Ship %s: mode = %s, submode = %i\n", Ships[Pl_objp->instance].ship_name, Mode_text[aip->mode], aip->submode));
13731
13732         ai_maybe_self_destruct(Pl_objp, aip);
13733
13734 //      if ( timestamp_elapsed(aip->goal_check_time) ) {
13735                 ai_process_mission_orders( objnum, aip );
13736 //              aip->goal_check_time = timestamp_rand(1000,2000);
13737 //      }
13738
13739         //      Avoid a shockwave, if necessary.  If a shockwave and rearming, stop rearming.
13740         if (aip->ai_flags & AIF_AVOID_SHOCKWAVE) {
13741                 if (ai_avoid_shockwave(Pl_objp, aip)) {
13742                         aip->ai_flags &= ~(AIF_BIG_SHIP_COLLIDE_RECOVER_1 | AIF_BIG_SHIP_COLLIDE_RECOVER_2);
13743                         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
13744                                 ai_abort_rearm_request(Pl_objp);
13745                         return;
13746                 }
13747         } else {
13748                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_STARTED;
13749         }
13750
13751         // moved call to ai_do_repair frame here from below because of the subsequent if statment returning
13752         // if the ship is getting repaired
13753         //      If waiting to be repaired, just stop and sit.
13754         ai_do_repair_frame(Pl_objp, aip, flFrametime);
13755         if ((aip->ai_flags & AIF_AWAITING_REPAIR) || (aip->ai_flags & AIF_BEING_REPAIRED)) {
13756                 if (ai_await_repair_frame(Pl_objp, aip))
13757                         return;
13758         }
13759
13760         if (aip->mode == AIM_PLAY_DEAD)
13761                 return;
13762
13763         //      If recovering from a collision with a big ship, don't continue.
13764         if (maybe_big_ship_collide_recover_frame(Pl_objp, aip))
13765                 return;
13766
13767         ai_preprocess_ignore_objnum(Pl_objp, aip);
13768         target_objnum = set_target_objnum(aip, aip->target_objnum);
13769
13770         // 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));
13771
13772         Assert(objnum != target_objnum);
13773
13774         ai_manage_shield(Pl_objp, aip);
13775         
13776         if ( maybe_request_support(Pl_objp) ) {
13777                 if ( Ships[Pl_objp->instance].flags & SF_FROM_PLAYER_WING ) {
13778                         ship_maybe_tell_about_rearm(shipp);
13779                 }
13780         }
13781
13782         ai_maybe_warp_out(Pl_objp);
13783
13784 /*
13785         //      If this ship is attacking an object's subsystems and someone else destroyed
13786         //      the subsystem, it could continue attacking the ship.  Need to invalidate the objnum.
13787         if (target_objnum >= 0)
13788                 if (Objects[target_objnum].flags & OF_PROTECTED) {
13789                         // if (aip->targeted_subsys != NULL)
13790                         //      ; //nprintf(("AI", "subsys hits = %7.3f\n", aip->targeted_subsys->current_hits));
13791
13792                         if ((aip->targeted_subsys == NULL) || (aip->targeted_subsys->current_hits <= 0.0f)) {
13793                                 target_objnum = -1;
13794                                 aip->target_objnum = -1;
13795                         }
13796                 }
13797 */
13798
13799
13800         //      Find an enemy if don't already have one.
13801         En_objp = NULL;
13802         if ( ai_need_new_target(Pl_objp, target_objnum) ) {
13803                 if ((aip->mode != AIM_EVADE_WEAPON) && (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
13804                         aip->resume_goal_time = -1;
13805                         aip->active_goal = AI_GOAL_NONE;
13806                 } else if (aip->resume_goal_time == -1) {
13807                         // AL 12-9-97: Don't allow cargo and navbuoys to set their aip->target_objnum
13808                         if ( !(Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS) ) {
13809                                 target_objnum = find_enemy(objnum, MAX_ENEMY_DISTANCE, Skill_level_max_attackers[Game_skill_level]);            //      Attack up to 25K units away.
13810                                 if (target_objnum != -1) {
13811                                         if (aip->target_objnum != target_objnum)
13812                                                 aip->aspect_locked_time = 0.0f;
13813                                         set_target_objnum(aip, target_objnum);
13814                                         En_objp = &Objects[target_objnum];
13815                                 }
13816                         }
13817                 }
13818         } else if (target_objnum >= 0) {
13819                 En_objp = &Objects[target_objnum];
13820         }
13821
13822         // set base stealth info each frame
13823         aip->ai_flags &= ~AIF_STEALTH_PURSIUT;
13824         if (En_objp && En_objp->type == OBJ_SHIP) {
13825                 if (Ship_info[Ships[En_objp->instance].ship_info_index].flags & SIF_STEALTH) {
13826                         int stealth_state = ai_is_stealth_visible(Pl_objp, En_objp);
13827                         float dist = vm_vec_dist_quick(&En_objp->pos, &Pl_objp->pos);
13828
13829                         if (stealth_state != STEALTH_FULLY_TARGETABLE) {
13830                                 aip->ai_flags |= AIF_STEALTH_PURSIUT;
13831                         }
13832
13833                         if ( (stealth_state == STEALTH_FULLY_TARGETABLE) || (stealth_state == STEALTH_VISIBLE) ) {
13834                                 aip->stealth_last_visible_stamp = timestamp();
13835                                 aip->stealth_last_cheat_visible_stamp = aip->stealth_last_visible_stamp;
13836                                 aip->stealth_last_pos = En_objp->pos;
13837                                 aip->stealth_velocity = En_objp->phys_info.vel;
13838                         } else if (dist < 100) {
13839                                 // get cheat timestamp
13840                                 aip->stealth_last_cheat_visible_stamp = timestamp();
13841
13842                                 // set approximate pos and vel, with increasing error as time from last_visible_stamp increases
13843                                 update_ai_stealth_info_with_error(aip/*, 0*/);
13844                         }
13845                 }
13846         }
13847
13848         /*      if ((Pl_objp != NULL) && (En_objp != NULL)) {
13849                 slide_face_ship();
13850                 return;
13851         }
13852 */
13853         // AL 12-10-97: ensure that cargo and navbuoys aip->target_objnum is always -1.
13854         if ( Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS ) {
13855                 aip->target_objnum = -1;
13856         }
13857
13858         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)) {
13859                 mprintf(("Warning: Object and its enemy have same position.  Object #%i\n", Pl_objp-Objects));
13860                 En_objp = NULL;
13861         }
13862
13863         if (aip->mode == AIM_CHASE) {
13864                 if (En_objp == NULL) {
13865                         aip->active_goal = -1;
13866                 }
13867         }
13868
13869         //      If there is a goal to resume and enough time has elapsed, resume the goal.
13870         if ((aip->resume_goal_time > 0) && (aip->resume_goal_time < Missiontime)) {
13871                 aip->active_goal = AI_GOAL_NONE;
13872                 aip->resume_goal_time = -1;
13873                 target_objnum = find_enemy(objnum, 2000.0f, Skill_level_max_attackers[Game_skill_level]);
13874                 if (target_objnum != -1) {
13875                         if (aip->target_objnum != target_objnum) {
13876                                 aip->aspect_locked_time = 0.0f;
13877                         }
13878                         set_target_objnum(aip, target_objnum);
13879                 }
13880         }
13881
13882         // check if targeted subsystem has been destroyed, if so, move onto another subsystem
13883         // if trying to disable or disarm the target
13884         if ((En_objp != NULL) && ( aip->targeted_subsys != NULL )) {
13885                 Assert(En_objp->type == OBJ_SHIP);
13886                 if ( aip->targeted_subsys->current_hits <= 0.0f ) {
13887                         int subsys_type;
13888
13889                         if ( aip->goals[0].ai_mode == AI_GOAL_DISABLE_SHIP ) {
13890                                 subsys_type = SUBSYSTEM_ENGINE;
13891                         } else if ( aip->goals[0].ai_mode == AI_GOAL_DISARM_SHIP ) {
13892                                 subsys_type = SUBSYSTEM_TURRET;
13893                         } else {
13894                                 subsys_type = -1;
13895                         }
13896
13897                         if ( subsys_type != -1 ) {
13898                                 ship_subsys *new_subsys;
13899                                 new_subsys = ship_return_next_subsys(&Ships[En_objp->instance], subsys_type, &Pl_objp->pos);
13900                                 if ( new_subsys != NULL ) {
13901                                         set_targeted_subsys(aip, new_subsys, aip->target_objnum);
13902                                 } else {
13903                                         // AL 12-16-97: no more subsystems to attack... reset targeting info
13904                                         aip->target_objnum = -1;
13905                                         set_targeted_subsys(aip, NULL, -1);
13906                                 }
13907                         } else {
13908                                 // targeted subsys is destroyed, so stop attacking it
13909                                 set_targeted_subsys(aip, NULL, -1);
13910                         }
13911                 }
13912         }
13913
13914         ai_maybe_launch_cmeasure(Pl_objp, aip);
13915         ai_maybe_evade_locked_missile(Pl_objp, aip);
13916
13917         aip->target_time += flFrametime;
13918
13919         int in_formation = 0;
13920         if (aip->ai_flags & AIF_FORMATION) {
13921                 in_formation = !ai_formation();
13922         }
13923
13924         if ( !in_formation ) {
13925                 ai_execute_behavior(aip);
13926         }
13927
13928         process_subobjects(objnum);
13929         maybe_resume_previous_mode(Pl_objp, aip);
13930         
13931         if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON ) {
13932                 if (Missiontime > aip->afterburner_stop_time) {
13933                         //nprintf(("AI", "Frame %i, turning off afterburner.\n", AI_FrameCount));
13934                         afterburners_stop(Pl_objp);
13935                 }
13936         }
13937 //      validate_mode_submode(aip);
13938 }
13939
13940 int Waypoints_created = 0;
13941
13942 //      Find the ship with the name *name in the Ship_info array.
13943 int find_ship_name(char *name)
13944 {
13945         int     i;
13946
13947         for (i=0; i<Num_ship_types; i++)
13948                 if (!strcmp(Ship_info[i].name, name))
13949                         return i;
13950
13951         return -1;
13952 }
13953
13954 void create_waypoints()
13955 {
13956         int     i, j, z;
13957
13958         // Waypoints_created = 1;
13959
13960         if (Waypoints_created)
13961                 return;
13962
13963         for (j=0; j<Num_waypoint_lists; j++)
13964                 for (i=0; i<Waypoint_lists[j].count; i++) {
13965                         z = obj_create(OBJ_WAYPOINT, 0, j * 65536 + i, NULL,
13966                                 &Waypoint_lists[j].waypoints[i], 0.0f, OF_RENDERS);
13967                 }
13968
13969         Waypoints_created = 1;
13970 }
13971
13972 int Last_ai_obj = -1;
13973
13974 void ai_process( object * obj, int ai_index, float frametime )
13975 {
13976 //      if (Ships[obj->instance].flags & SF_DYING)
13977 //              nprintf(("AI", "Frame: %i Ship %s is dying!\n", Framecount, Ships[obj->instance].ship_name));
13978
13979         if (obj->flags & OF_SHOULD_BE_DEAD)
13980                 return;
13981
13982         // 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.
13983         if ((Ships[obj->instance].flags & SF_DYING ) && !(Ship_info[Ships[obj->instance].ship_info_index].flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP))){
13984                 return;
13985         }
13986
13987         int rfc = 1;            //      Assume will be Reading Flying Controls.
13988
13989         Assert( obj->type == OBJ_SHIP );
13990         Assert( ai_index >= 0 );
13991
13992         init_ship_info();
13993
13994         create_waypoints();
13995
13996         AI_frametime = frametime;
13997         if (obj-Objects <= Last_ai_obj) {
13998                 AI_FrameCount++;
13999         }
14000
14001         memset( &AI_ci, 0, sizeof(AI_ci) );
14002
14003         ai_frame(obj-Objects);
14004
14005         AI_ci.pitch = 0.0f;
14006         AI_ci.bank = 0.0f;
14007         AI_ci.heading = 0.0f;
14008
14009         // the ships maximum velocity now depends on the energy flowing to engines
14010         obj->phys_info.max_vel.z = Ships[obj->instance].current_max_speed;
14011         ai_info *aip = &Ai_info[Ships[obj->instance].ai_index];
14012
14013         //      In certain circumstances, the AI says don't fly in the normal way.
14014         //      One circumstance is in docking and undocking, when the ship is moving
14015         //      under thruster control.
14016         switch (aip->mode) {
14017         case AIM_DOCK:
14018                 if ((aip->submode >= AIS_DOCK_2) && (aip->submode != AIS_UNDOCK_3))
14019                         rfc = 0;
14020                 break;
14021         case AIM_WARP_OUT:
14022                 if (aip->submode >= AIS_WARP_3)
14023                         rfc = 0;
14024                 break;
14025 //      case AIM_NONE:
14026 //              if (aip->submode == AIS_NONE_FORMATION)
14027 //                      rfc = 0;
14028 //              break;
14029         default:
14030                 break;
14031         }
14032
14033         if (rfc == 1) {
14034                 vector copy_desired_rotvel = obj->phys_info.rotvel;
14035                 physics_read_flying_controls( &obj->orient, &obj->phys_info, &AI_ci, frametime);
14036                 // if obj is in formation and not flight leader, don't update rotvel
14037                 if (aip->ai_flags & AIF_FORMATION) {
14038                         if (&Objects[aip->goal_objnum] != obj) {
14039                                 obj->phys_info.desired_rotvel = copy_desired_rotvel;
14040                                 obj->phys_info.rotvel = copy_desired_rotvel;
14041                         }
14042                 }
14043         }
14044
14045         Last_ai_obj = obj-Objects;
14046 }
14047
14048 //      Initialize ai_info struct of object objnum.
14049 void init_ai_object(int objnum)
14050 {
14051         int     ship_index, ai_index;
14052         ai_info *aip;
14053         int ship_type;
14054         object  *objp;
14055         vector  near_vec;                       //      A vector nearby and mainly in front of this object.
14056
14057         objp = &Objects[objnum];
14058         ship_index = objp->instance;
14059         ai_index = Ships[ship_index].ai_index;
14060         Assert((ai_index >= 0) && (ai_index < MAX_AI_INFO));
14061
14062         aip = &Ai_info[ai_index];
14063
14064         ship_type = Ships[ship_index].ship_info_index;
14065
14066         vm_vec_scale_add(&near_vec, &objp->pos, &objp->orient.fvec, 100.0f);
14067         vm_vec_scale_add2(&near_vec, &objp->orient.rvec, 10.0f);
14068
14069         // Things that shouldn't have to get initialized, but initialize them just in case!
14070         aip->ai_flags = 0;
14071         aip->previous_mode = AIM_NONE;
14072         aip->mode_time = -1;
14073         aip->target_objnum = -1;
14074         aip->target_signature = -1;
14075         aip->previous_target_objnum = -1;
14076         aip->target_time = 0.0f;
14077         aip->enemy_wing = -1;
14078         aip->attacker_objnum = -1;
14079         aip->goal_objnum = -1;
14080         aip->goal_signature = -1;
14081         aip->guard_objnum = -1;
14082         aip->guard_signature = -1;
14083         aip->guard_wingnum = -1;
14084         aip->dock_signature = -1;
14085         aip->submode = 0;
14086         aip->previous_submode = 0;
14087         aip->best_dot_to_enemy = -1.0f;
14088         aip->best_dot_from_enemy = -1.0f;
14089         aip->best_dot_to_time = 0;
14090         aip->best_dot_from_time = 0;
14091         aip->submode_start_time = 0;
14092         aip->submode_parm0 = 0;
14093         aip->active_goal = -1;
14094         aip->goal_check_time = timestamp(0);
14095         aip->last_predicted_enemy_pos = near_vec;
14096         aip->prev_goal_point = near_vec;
14097         aip->goal_point = near_vec;
14098         aip->time_enemy_in_range = 0.0f;
14099         aip->last_attack_time = 0;
14100         aip->last_hit_time = 0;
14101         aip->last_hit_quadrant = 0;
14102         aip->hitter_objnum = -1;
14103         aip->hitter_signature = -1;
14104         aip->resume_goal_time = -1;
14105         aip->prev_accel = 0.0f;
14106         aip->prev_dot_to_goal = 0.0f;
14107
14108         aip->ignore_objnum = UNUSED_OBJNUM;
14109         aip->ignore_signature = -1;
14110
14111         // aip->mode = AIM_NONE;
14112
14113         // End of Things that shouldn't have to get initialized, but initialize them just in case!
14114
14115         aip->ai_courage = Ai_classes[Ship_info[ship_type].ai_class].ai_courage[Game_skill_level];
14116         aip->ai_patience = Ai_classes[Ship_info[ship_type].ai_class].ai_patience[Game_skill_level];
14117         aip->ai_evasion = Ai_classes[Ship_info[ship_type].ai_class].ai_evasion[Game_skill_level];
14118         aip->ai_accuracy = Ai_classes[Ship_info[ship_type].ai_class].ai_accuracy[Game_skill_level];
14119
14120         if (Num_waypoint_lists > 0) {
14121                 aip->wp_index = -1;
14122                 aip->wp_list = -1;
14123         } else {
14124                 aip->wp_index = -1;
14125                 aip->wp_list = -1;
14126         }
14127
14128         aip->attacker_objnum = -1;
14129         aip->goal_signature = -1;
14130
14131         Objects[objnum].phys_info.prev_fvec = Objects[objnum].orient.fvec;
14132
14133         aip->last_predicted_enemy_pos.x = 0.0f; //      Says this value needs to be recomputed!
14134         aip->time_enemy_in_range = 0.0f;
14135
14136         aip->resume_goal_time = -1;                                     //      Say there is no goal to resume.
14137
14138         aip->active_goal = -1;
14139         aip->path_start = -1;
14140         aip->path_goal_dist = -1;
14141         aip->path_length = 0;
14142         aip->path_subsystem_next_check = 1;
14143         aip->dock_path_index = -1;
14144         aip->dock_index = -1;
14145         aip->dock_objnum = -1;
14146
14147         aip->danger_weapon_objnum = -1;
14148         aip->danger_weapon_signature = -1;
14149
14150         aip->lead_scale = 0.0f;
14151         aip->last_hit_target_time = Missiontime;
14152
14153         aip->nearest_locked_object = -1;
14154         aip->nearest_locked_distance = 99999.0f;
14155
14156         aip->targeted_subsys = NULL;
14157         aip->last_subsys_target = NULL;
14158         aip->targeted_subsys_parent = -1;
14159
14160         // The next two fields are used to time the rearming to allow useful sound effects for missile rearming
14161         aip->rearm_first_missile = TRUE;                //      flag to indicate that next missile to load is the first missile
14162         aip->rearm_release_delay = 0;                   //      timestamp to delay the separation of docked ships after rearm
14163
14164         aip->next_predict_pos_time = 0;
14165
14166         aip->afterburner_stop_time = 0;
14167         aip->last_objsig_hit = -1;                              // object signature of the ship most recently hit by aip
14168
14169         aip->path_next_create_time = timestamp(1);
14170         aip->path_create_pos = Objects[objnum].pos;
14171         aip->path_create_orient = Objects[objnum].orient;
14172
14173         aip->ignore_expire_timestamp = timestamp(1);
14174         aip->warp_out_timestamp = 0;
14175         aip->next_rearm_request_timestamp = timestamp(1);
14176         aip->primary_select_timestamp = timestamp(1);
14177         aip->secondary_select_timestamp = timestamp(1);
14178         aip->scan_for_enemy_timestamp = timestamp(1);
14179
14180         aip->choose_enemy_timestamp = timestamp(3*(NUM_SKILL_LEVELS-Game_skill_level) * ((rand_alt() % 500) + 500));
14181
14182         aip->shockwave_object = -1;
14183         aip->shield_manage_timestamp = timestamp(1);
14184         aip->self_destruct_timestamp = -1;      //      This is a flag that we have not yet set this.
14185         aip->ok_to_target_timestamp = timestamp(1);
14186         aip->pick_big_attack_point_timestamp = timestamp(1);
14187         vm_vec_zero(&aip->big_attack_point);
14188
14189         aip->avoid_check_timestamp = timestamp(1);
14190
14191         aip->abort_rearm_timestamp = -1;
14192
14193         // artillery stuff
14194         aip->artillery_objnum = -1;
14195         aip->artillery_sig = -1;        
14196
14197         // waypoint speed cap
14198         aip->waypoint_speed_cap = -1;
14199
14200         // set lethality to enemy team
14201         aip->lethality = 0.0f;
14202 }
14203
14204 void init_ai_objects()
14205 {
14206         int     i;
14207
14208         for (i=0; i<num_objects; i++){
14209                 if (Objects[i].type == OBJ_SHIP){
14210                         init_ai_object(i);
14211                 }
14212         }
14213 }
14214
14215 void init_ai_system()
14216 {
14217         // MWA -- removed next line of code on 11/12/97.  When a ship is created
14218         // it calls init_ai_object() on it's objnum.  Doing this init at the point where
14219         // this function gets called messes things up.
14220         //init_ai_objects();
14221
14222         Ppfp = Path_points;
14223         Waypoints_created = 0;
14224
14225         Dock_path_warning_given = 0;
14226
14227 /*      for (int i=0; i<MAX_IGNORE_OBJECTS; i++) {
14228                 Ignore_objects[i].objnum = -1;
14229                 Ignore_objects[i].signature = -1;
14230         }
14231 */
14232
14233 }
14234
14235 void ai_set_default_behavior(object *obj, int classnum)
14236 {
14237         ai_info *aip;
14238
14239         Assert(obj != NULL);
14240         Assert(obj->instance != -1);
14241         Assert(Ships[obj->instance].ai_index != -1);
14242
14243         aip = &Ai_info[Ships[obj->instance].ai_index];
14244
14245         aip->behavior = classnum;
14246
14247 }
14248
14249 void ai_do_default_behavior(object *obj)
14250 {
14251         ai_info *aip;
14252         int             ship_flags;
14253
14254         Assert(obj != NULL);
14255         Assert(obj->instance != -1);
14256         Assert(Ships[obj->instance].ai_index != -1);
14257
14258         aip = &Ai_info[Ships[obj->instance].ai_index];
14259
14260         ship_flags = Ship_info[Ships[obj->instance].ship_info_index].flags;
14261         if (!is_instructor(obj) && (ship_flags & (SIF_FIGHTER | SIF_BOMBER))) {
14262                 int enemy_objnum = find_enemy(OBJ_INDEX(obj), 1000.0f, Skill_level_max_attackers[Game_skill_level]);
14263                 set_target_objnum(aip, enemy_objnum);
14264                 aip->mode = AIM_CHASE;
14265                 aip->submode = SM_ATTACK;
14266         } else if (ship_flags & (SIF_SUPPORT)) {
14267                 aip->mode = AIM_SAFETY;
14268                 aip->submode = AISS_1;
14269                 aip->ai_flags &= ~(AIF_REPAIRING);
14270         } else if ( ship_flags & SIF_SENTRYGUN ) {
14271                 aip->mode = AIM_SENTRYGUN;
14272         } else {
14273                 aip->mode = AIM_NONE;
14274         }
14275         
14276         aip->submode_start_time = Missiontime;
14277         aip->active_goal = AI_GOAL_NONE;
14278 }
14279
14280 #define FRIENDLY_DAMAGE_THRESHOLD       50.0f           //      Display a message at this threshold.  Note, this gets scaled by Skill_level
14281
14282 // send the given message from objp.  called from the maybe_process_friendly_hit
14283 // code below when a message must get send to the player when he fires on friendlies
14284 void process_friendly_hit_message( int message, object *objp )
14285 {
14286         int index;
14287
14288         // no traitor in multiplayer
14289         if(Game_mode & GM_MULTIPLAYER){
14290                 return;
14291         }
14292
14293         // don't send this message if a player ship was hit.
14294         if ( objp->flags & OF_PLAYER_SHIP ){
14295                 return;
14296         }
14297
14298         // check if objp is a cargo contianer -- if so, then find a new ship to send the message
14299         index = objp->instance;
14300         if ( !(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER|SIF_BOMBER)) ){
14301                 index = -1;
14302         }
14303
14304         // if the message is "oops" (the don't hit me message), always make come from Terran command
14305         if ( message == MESSAGE_OOPS ){
14306                 index = -1;
14307         }
14308
14309         if ( index >= 0){
14310                 message_send_builtin_to_player( message, &Ships[index], MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14311         } else {
14312                 message_send_builtin_to_player( message, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14313         }
14314 }
14315
14316 extern  void ship_set_subsystem_strength( ship *shipp, int type, float strength );
14317
14318 //      Object *objp_weapon, fired by *objp_hitter, hit object *objp_ship.
14319 void maybe_process_friendly_hit(object *objp_hitter, object *objp_hit, object *objp_weapon)
14320 {
14321         // no turning traitor in multiplayer
14322         if ( Game_mode & GM_MULTIPLAYER ) {
14323                 return;
14324         }
14325
14326         // ditto if mission says no traitors allowed
14327         if (The_mission.flags & MISSION_FLAG_NO_TRAITOR) {
14328                 return;
14329         }
14330
14331         if ((objp_hitter == Player_obj) && (Player_ship->team == TEAM_FRIENDLY)) {
14332
14333                 // AL 12-4-97: It is possible the Player is a OBJ_GHOST at this point.  If so, bail out.
14334                 if ( objp_hitter->type != OBJ_SHIP ) {
14335                         return;
14336                 }
14337
14338                 Assert(objp_hitter->type == OBJ_SHIP);
14339                 Assert(objp_hit->type == OBJ_SHIP);
14340                 Assert(objp_weapon->type == OBJ_WEAPON);
14341
14342                 ship    *shipp_hitter = &Ships[objp_hitter->instance];
14343                 ship    *shipp_hit = &Ships[objp_hit->instance];
14344
14345                 if (shipp_hitter->team != shipp_hit->team) {
14346                         return;
14347                 }
14348
14349                 // get the player
14350                 player *pp = &Players[Player_num];
14351
14352                 // wacky stuff here
14353                 if (pp->friendly_hits != 0) {
14354                         float   time_since_last_hit = f2fl(Missiontime - pp->friendly_last_hit_time);
14355                         if ((time_since_last_hit >= 0.0f) && (time_since_last_hit < 10000.0f)) {
14356                                 if (time_since_last_hit > 60.0f) {
14357                                         pp->friendly_hits = 0;
14358                                         pp->friendly_damage = 0.0f;
14359                                 } else if (time_since_last_hit > 2.0f) {
14360                                         pp->friendly_hits -= (int) time_since_last_hit/2;
14361                                         pp->friendly_damage -= time_since_last_hit;
14362                                 }
14363
14364                                 if (pp->friendly_damage < 0.0f) {
14365                                         pp->friendly_damage = 0.0f;
14366                                 }
14367
14368                                 if (pp->friendly_hits < 0) {
14369                                         pp->friendly_hits = 0;
14370                                 }
14371                         }
14372                 }
14373
14374                 float   damage;         //      Damage done by weapon.  Gets scaled down based on size of ship.
14375
14376                 damage = Weapon_info[Weapons[objp_weapon->instance].weapon_info_index].damage;
14377                 
14378                 // wacky stuff here
14379                 ship_info *sip = &Ship_info[Ships[objp_hit->instance].ship_info_index];
14380                 if (sip->initial_hull_strength > 1000.0f) {
14381                         float factor = sip->initial_hull_strength / 1000.0f;
14382                         factor = min(100.0f, factor);
14383                         damage /= factor;
14384                 }
14385
14386                 //      Don't penalize much at all for hitting cargo
14387                 if (sip->flags & (SIF_CARGO | SIF_SENTRYGUN)) {
14388                         damage /= 10.0f;
14389                 }
14390
14391                 //      Hit ship, but not targeting it, so it's not so heinous, maybe an accident.
14392                 if (Ai_info[shipp_hitter->ai_index].target_objnum != OBJ_INDEX(objp_hit)) {
14393                         damage /= 5.0f;
14394                 }
14395
14396                 pp->friendly_last_hit_time = Missiontime;
14397                 pp->friendly_hits++;
14398
14399                 // cap damage and number of hits done this frame
14400                 float accredited_damage = min(MAX_BURST_DAMAGE, pp->damage_this_burst + damage) - pp->damage_this_burst;
14401                 pp->friendly_damage += accredited_damage;
14402                 pp->damage_this_burst += accredited_damage;
14403
14404                 // Done with adjustments to damage.  Evaluate based on current friendly_damage
14405                 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 ));
14406                 
14407                 if (is_instructor(objp_hit)) {
14408                         // it's not nice to hit your instructor
14409                         if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD) {
14410                                 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_ATTACK, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14411                                 pp->last_warning_message_time = Missiontime;
14412                                 ship_set_subsystem_strength( Player_ship, SUBSYSTEM_WEAPONS, 0.0f);
14413
14414                                 training_fail();
14415
14416                                 //      Instructor warp out.
14417                                 ai_set_mode_warp_out(objp_hit, &Ai_info[Ships[objp_hit->instance].ai_index]);
14418                                 gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START_FORCED );     //      Force player to warp out.
14419
14420                                 //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) );
14421                                 //ship_apply_global_damage( objp_hitter, objp_hit, NULL, 1.0f );
14422                         } else if (Missiontime - pp->last_warning_message_time > F1_0*4) {
14423                                 // warning every 4 sec
14424                                 // use NULL as the message sender here since it is the Terran Command persona
14425                                 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_HIT, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14426                                 pp->last_warning_message_time = Missiontime;
14427                         }
14428
14429                 // not nice to hit your friends
14430                 } else if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f)) {
14431                         process_friendly_hit_message( MESSAGE_HAMMER_SWINE, objp_hit );
14432                         mission_goal_fail_all();
14433                         ai_abort_rearm_request( Player_obj );
14434
14435                         Player_ship->team = TEAM_TRAITOR;
14436
14437                 } else if ((damage > frand()) && (Missiontime - pp->last_warning_message_time > F1_0*4) && (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD)) {
14438                         // no closer than 4 sec intervals
14439                         //      Note: (damage > frand()) added on 12/9/97 by MK.  Since damage is now scaled down for big ships, we could get too
14440                         //      many warnings.  Kind of tedious.  frand() returns a value in 0..1, so this won't affect legit hits.
14441                         process_friendly_hit_message( MESSAGE_OOPS, objp_hit );
14442                         pp->last_warning_message_time = Missiontime;
14443                 }
14444         }
14445 }
14446
14447 //      Maybe make ship with ai_info *aip attack hitter_objnum as a dynamic goal
14448 void maybe_set_dynamic_chase(ai_info *aip, int hitter_objnum)
14449 {
14450         Assert(Ship_info[Ships[aip->shipnum].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER));
14451
14452         // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
14453         if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
14454                 return;
14455         }
14456
14457         // only set as target if can be targeted.
14458         if (awacs_get_level(&Objects[hitter_objnum], &Ships[aip->shipnum], 1) < 1) {
14459                 return;
14460         }
14461
14462         if (aip->target_objnum != hitter_objnum)
14463                 aip->aspect_locked_time = 0.0f;
14464         set_target_objnum(aip, hitter_objnum);
14465         aip->resume_goal_time = Missiontime + i2f(20);  //      Only chase up to 20 seconds.
14466         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14467
14468         set_targeted_subsys(aip, NULL, -1);             //      Say not attacking any particular subsystem.
14469
14470         aip->previous_submode = aip->mode;
14471         aip->mode = AIM_CHASE;
14472         aip->submode = SM_ATTACK;
14473 }
14474
14475
14476 //      Return true if *objp has armed an aspect seeking bomb.
14477 //      This function written so a ship with an important bomb to fire will willingly take hits in the face to fire its bomb.
14478 int firing_aspect_seeking_bomb(object *objp)
14479 {
14480         ship    *shipp;
14481         int     bank_index;
14482         ship_weapon     *swp;
14483
14484         shipp = &Ships[objp->instance];
14485
14486         swp = &shipp->weapons;
14487
14488         bank_index = swp->current_secondary_bank;
14489
14490         if (bank_index != -1)
14491                 if (swp->secondary_bank_ammo[bank_index] > 0) {
14492                         if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_BOMB) {
14493                                 if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_HOMING_ASPECT) {
14494                                         return 1;
14495                                 }
14496                         }
14497                 }
14498
14499         return 0;
14500 }
14501
14502 //      *objp collided with big ship *big_objp at global point *collide_pos
14503 //      Make it fly away from the collision point.
14504 // collision_normal is NULL, when a collision is imminent and we just want to bug out.
14505 void big_ship_collide_recover_start(object *objp, object *big_objp, vector *collide_pos, vector *collision_normal)
14506 {
14507         ai_info *aip;
14508
14509         Assert(objp->type == OBJ_SHIP);
14510
14511         aip = &Ai_info[Ships[objp->instance].ai_index];
14512
14513         if (!timestamp_elapsed(aip->big_recover_timestamp) && (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1))
14514                 return;
14515
14516         //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)));
14517         if (collision_normal) {
14518                 aip->big_recover_timestamp = timestamp(2000);
14519                 aip->big_collision_normal = *collision_normal;
14520         //      nprintf(("AI", " normal\n"));
14521         } else {
14522                 aip->big_recover_timestamp = timestamp(500);
14523         //      nprintf(("AI", " no normal\n"));
14524         }
14525
14526
14527         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
14528         aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_1;
14529
14530 //      vector  out_vec;
14531 //      vm_vec_normalized_dir(&out_vec, &objp->pos, collide_pos);
14532
14533         // big_recover_pos_1 is 100 m out along normal
14534         vector direction;
14535         if (collision_normal) {
14536                 direction = *collision_normal;
14537         } else {
14538                 vm_vec_copy_scale(&direction, &objp->orient.fvec, -1.0f);
14539         }
14540         vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &direction, 100.0f);
14541
14542         // go out 200 m from box closest box point
14543         get_world_closest_box_point_with_delta(&aip->big_recover_pos_2, big_objp, &aip->big_recover_pos_1, NULL, 300.0f);
14544
14545         accelerate_ship(aip, 0.0f);
14546 /*
14547         if (vm_vec_dot(collision_normal, &objp->orient.fvec) > 0.5f) {
14548 //              vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14549 //              vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.uvec, big_objp->radius/2.0f);
14550 //              vm_vec_scale_add(&aip->big_recover_pos_2, &objp->pos, &out_vec, big_objp->radius*2.0f);
14551                 accelerate_ship(aip, 2.0f);
14552         } else {
14553 //              vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14554 //              vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.uvec, big_objp->radius/2.0f);
14555                 accelerate_ship(aip, 0.0f);
14556         } */
14557 }
14558
14559 float max_lethality = 0.0f;
14560
14561 void ai_update_lethality(object *ship_obj, object *other_obj, float damage)
14562 {
14563         Assert(ship_obj->type == OBJ_SHIP);
14564         Assert(other_obj->type == OBJ_WEAPON || other_obj->type == OBJ_SHOCKWAVE);
14565         int dont_count = FALSE;
14566
14567         int parent = other_obj->parent;
14568         if (Objects[parent].type == OBJ_SHIP) {
14569                 if (Objects[parent].signature == other_obj->parent_sig) {
14570
14571                         // check damage done to enemy team
14572                         if (Ships[ship_obj->instance].team != Ships[Objects[parent].instance].team) {
14573
14574                                 // other is weapon
14575                                 if (other_obj->type == OBJ_WEAPON) {
14576                                         weapon *wp = &Weapons[other_obj->instance];
14577                                         weapon_info *wif = &Weapon_info[wp->weapon_info_index];
14578
14579                                         // if parent is BIG|HUGE, don't count beam
14580                                         if (Ship_info[Ships[Objects[parent].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
14581                                                 if (wif->wi_flags & WIF_BEAM) {
14582                                                         dont_count = TRUE;
14583                                                 }
14584                                         }
14585                                 }
14586
14587                                 if (!dont_count) {
14588                                         float lethality = 0.025f * damage;      // 2 cyclops (@2000) put you at 100 lethality
14589
14590                                         // increase lethality weapon's parent ship
14591                                         ai_info *aip = &Ai_info[Ships[Objects[parent].instance].ai_index];
14592                                         aip->lethality += lethality;
14593                                         aip->lethality = min(110.0f, aip->lethality);
14594                                         // if you hit, don;t be less than 0
14595                                         aip->lethality = max(0.0f, aip->lethality);
14596
14597 //                                      if (aip->lethality > max_lethality) {
14598 //                                              max_lethality = aip->lethality;
14599 //                                              mprintf(("new lethalilty high: %.1f\n", max_lethality));
14600 //                                      }
14601
14602                                         // if parent is player, show his lethality
14603 //                                      if (Objects[parent].flags & OF_PLAYER_SHIP) {
14604 //                                              mprintf(("Player lethality: %.1f\n", aip->lethality));
14605 //                                      }
14606                                 }
14607                         }
14608                 }
14609         }
14610 }
14611
14612
14613 //      Object *objp_ship was hit by either weapon *objp_weapon or collided into by ship hit_objp at point *hitpos.
14614 void ai_ship_hit(object *objp_ship, object *hit_objp, vector *hitpos, int shield_quadrant, vector *hit_normal)
14615 {
14616         int             hitter_objnum = -2;
14617         object  *objp_hitter = NULL;
14618         ship            *shipp;
14619         ai_info *aip, *hitter_aip;
14620
14621         shipp = &Ships[objp_ship->instance];
14622         aip = &Ai_info[shipp->ai_index];
14623
14624         if (objp_ship->flags & OF_PLAYER_SHIP)
14625                 return;
14626
14627         if ((aip->mode == AIM_WARP_OUT) || (aip->mode == AIM_PLAY_DEAD))
14628                 return;
14629
14630         if (hit_objp->type == OBJ_SHIP) {
14631                 //      If the object that this ship collided with is a big ship
14632                 if (Ship_info[Ships[hit_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
14633                         //      And the current object is _not_ a big ship
14634                         if (!(Ship_info[Ships[objp_ship->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
14635                                 //      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.
14636                                 big_ship_collide_recover_start(objp_ship, hit_objp, hitpos, hit_normal);
14637                         }
14638                 }
14639         }
14640
14641         if (hit_objp->type == OBJ_WEAPON) {
14642                 //      Make sure the object that fired this weapon is still alive.  If not, abort.
14643                 // Assert(hit_objp->parent >= 0);
14644                 if(hit_objp->parent < 0){
14645                         return;
14646                 }
14647                 if ( hit_objp->parent_sig != Objects[hit_objp->parent].signature ){
14648                         return;
14649                 }
14650
14651                 //      Hit by a protected ship, don't attack it.
14652                 if (Objects[hit_objp->parent].flags & OF_PROTECTED) {
14653                         if ((Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) && (aip->target_objnum == -1)) {
14654                                 if (aip->mode == AIM_CHASE) {
14655                                         if (aip->submode != SM_EVADE_WEAPON) {
14656                                                 aip->mode = AIM_CHASE;
14657                                                 aip->submode = SM_EVADE_WEAPON;
14658                                                 aip->submode_start_time = Missiontime;
14659                                         }
14660                                 } else if (aip->mode != AIM_EVADE_WEAPON) {
14661                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14662                                         aip->previous_mode = aip->mode;
14663                                         aip->previous_submode = aip->submode;
14664                                         aip->mode = AIM_EVADE_WEAPON;
14665                                         aip->submode = -1;
14666                                         aip->submode_start_time = Missiontime;
14667                                         aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Evade for up to five seconds.
14668                                 }
14669
14670                         }
14671                         return;
14672                 }
14673
14674                 hitter_objnum = hit_objp->parent;
14675                 Assert((hitter_objnum >= 0) && (hitter_objnum < MAX_OBJECTS));
14676                 objp_hitter = &Objects[hitter_objnum];
14677                 maybe_process_friendly_hit(objp_hitter, objp_ship, hit_objp);           //      Deal with player's friendly fire.
14678
14679                 if ( (shipp->team & TEAM_FRIENDLY) && !(Game_mode & GM_MULTIPLAYER) ) {
14680                         ship_maybe_ask_for_help(shipp);
14681                 }
14682         } else if (hit_objp->type == OBJ_SHIP) {
14683                 if (shipp->team == Ships[hit_objp->instance].team)              //      Don't have AI react to collisions between teammates.
14684                         return;
14685                 objp_hitter = hit_objp;
14686                 hitter_objnum = hit_objp-Objects;
14687         } else {
14688                 Int3(); //      Hmm, what kind of object hit this if not weapon or ship?  Get MikeK.
14689                 return;
14690         }
14691
14692         //      Collided into a protected ship, don't attack it.
14693         if (hit_objp->flags & OF_PROTECTED)
14694                 return;
14695
14696         Assert(objp_hitter != NULL);
14697         hitter_aip = &Ai_info[Ships[objp_hitter->instance].ai_index];
14698         hitter_aip->last_hit_target_time = Missiontime;
14699         
14700         // store the object signature of objp_ship into ai_info, since we want to track the last ship hit by 'hitter_objnum'
14701         hitter_aip->last_objsig_hit = objp_ship->signature; 
14702
14703         aip->last_hit_time = Missiontime;
14704
14705         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
14706                 return;
14707
14708         //      If this ship is awaiting repair, abort!
14709         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)) {
14710                 ship_info       *sip = &Ship_info[shipp->ship_info_index];
14711
14712                 if (objp_ship->hull_strength/sip->initial_hull_strength < 0.3f) {
14713                         //      No, only abort if hull below a certain level.
14714                         aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP/2);  //      Might request again after 15 seconds.
14715                         if ( !(objp_ship->flags & OF_PLAYER_SHIP) )                                             // mwa -- don't abort rearm for a player
14716                                 ai_abort_rearm_request(objp_ship);
14717                 }
14718         }
14719
14720         //      If firing a bomb, ignore enemy fire so we can gain lock drop the bomb.
14721         //      Only ignore fire if aspect_locked_time > 0.5f, as this means we're in range.
14722         if (firing_aspect_seeking_bomb(objp_ship)) {
14723                 if ((aip->ai_flags & AIF_SEEK_LOCK) && (aip->aspect_locked_time > 0.1f))
14724                         return;
14725         }
14726
14727         //      If in AIM_STRAFE mode and got hit by target, maybe attack turret if appropriate
14728         if (aip->mode == AIM_STRAFE) {
14729                 Assert(hitter_objnum != -2);
14730                 if (aip->target_objnum == hitter_objnum) {
14731                         if ( hit_objp->type == OBJ_WEAPON ) {
14732                                 ai_big_strafe_maybe_attack_turret(objp_ship, hit_objp);
14733                         }
14734                         return;
14735                 }
14736                 else {
14737                                 // AL 11-10-97:
14738                         ;       // do nothing here, we'll attack this hitter if it is a fighter or bomber (this is handled
14739                                 // in code later in this function
14740                 }
14741         }
14742
14743         if (objp_ship == Player_obj)
14744                 return;         //      We don't do AI for the player.
14745
14746         maybe_update_guard_object(objp_ship, objp_hitter);
14747
14748         //      Big ships don't go any further.
14749         if (!(Ship_info[shipp->ship_info_index].flags & SIF_SMALL_SHIP))
14750                 return;
14751
14752         //      If the hitter object is the ignore object, don't attack it.
14753         ship_info       *sip = &Ship_info[shipp->ship_info_index];
14754         if ((is_ignore_object(aip, objp_hitter-Objects)) && (sip->flags & (SIF_BOMBER | SIF_FIGHTER))) {
14755                 if (aip->mode == AIM_NONE) {
14756                         aip->mode = AIM_CHASE;  //      This will cause the ship to move, if not attack.
14757                         aip->submode = SM_EVADE;
14758                 }
14759                 return;
14760         }
14761
14762         //      Maybe abort based on mode.
14763         switch (aip->mode) {
14764         case AIM_CHASE:
14765                 if (aip->submode == SM_ATTACK_FOREVER)
14766                         return;
14767
14768                 if ( hit_objp->type == OBJ_WEAPON ) {
14769                         if ( ai_big_maybe_enter_strafe_mode(objp_ship, OBJ_INDEX(hit_objp), 1) )
14770                                 return;
14771                 }
14772
14773         case AIM_GUARD:
14774                 //      If in guard mode and far away from guard object, don't pursue guy that hit me.
14775                         if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
14776                                 if (vm_vec_dist_quick(&objp_ship->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
14777                                         return;
14778                                 }
14779                         }
14780         case AIM_STILL:
14781         case AIM_STAY_NEAR:
14782                 // Note: Dealt with above, at very top.  case AIM_PLAY_DEAD:
14783         case AIM_STRAFE:
14784                 break;
14785         case AIM_EVADE_WEAPON:
14786         case AIM_EVADE:
14787         case AIM_GET_BEHIND:
14788         case AIM_AVOID:
14789         case AIM_DOCK:
14790         case AIM_BIGSHIP:
14791         case AIM_PATH:
14792         case AIM_NONE:
14793         case AIM_BAY_DEPART:
14794         case AIM_SENTRYGUN:
14795                 return;
14796         case AIM_BAY_EMERGE:
14797                 // If just leaving the docking bay, don't react to enemy fire... just keep flying away from docking bay
14798                 if ( (Missiontime - aip->submode_start_time) < 5*F1_0 ) {
14799                         return;
14800                 }
14801                 break;
14802         case AIM_WAYPOINTS:
14803                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER))
14804                         break;
14805                 else
14806                         return;
14807                 break;
14808         case AIM_SAFETY:
14809                 if ((aip->submode != AISS_1) || (Missiontime - aip->submode_start_time > i2f(1))) {
14810                         aip->submode = AISS_1;
14811                         aip->submode_start_time = Missiontime;
14812                 }
14813                 return;
14814                 break;
14815         case AIM_WARP_OUT:
14816                 return;
14817                 break;
14818         default:
14819                 Int3(); //      Bogus mode!
14820         }
14821
14822         if (timestamp_elapsed(aip->ok_to_target_timestamp))
14823                 aip->ai_flags &= ~AIF_FORMATION;                        //      If flying in formation, bug out!
14824
14825         aip->hitter_objnum = hitter_objnum;
14826         aip->hitter_signature = Objects[hitter_objnum].signature;
14827
14828         //      If the hitter is not on the same team as the hittee, do some stuff.
14829         if (shipp->team != Ships[objp_hitter->instance].team) {
14830                 //nprintf(("AI", "Object %i attacking %i, who just hit him!\n", objp_ship-Objects, hitter_objnum));
14831
14832                 if ((hitter_objnum != aip->target_objnum) && (sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
14833                         maybe_set_dynamic_chase(aip, hitter_objnum);
14834                         maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14835                 } else {
14836                         if ((aip->mode == AIM_CHASE) && ((objp_ship->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp_ship)/sip->shields > 0.8f))) {
14837                                 switch (aip->submode) {
14838                                 case SM_ATTACK:
14839                                 case SM_SUPER_ATTACK:
14840                                 case SM_GET_AWAY:
14841                                         break;
14842                                 default:
14843                                         if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
14844                                                 maybe_set_dynamic_chase(aip, hitter_objnum);
14845                                         }
14846                                         maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14847                                         break;
14848                                 }
14849                         } else if (aip->mode == AIM_CHASE) {
14850                                 switch (aip->submode) {
14851                                 case SM_ATTACK:
14852                                         aip->submode = SM_EVADE;
14853                                         aip->submode_start_time = Missiontime;
14854                                         break;
14855                                 case SM_SUPER_ATTACK:
14856                                         if (Missiontime - aip->submode_start_time > i2f(1)) {
14857                                                 aip->submode = SM_EVADE;
14858                                                 aip->submode_start_time = Missiontime;
14859                                         }
14860                                         break;
14861                                 case SM_EVADE_BRAKE:
14862                                         break;
14863                                 case SM_EVADE_SQUIGGLE:
14864                                         aip->submode = SM_EVADE;
14865                                         aip->submode_start_time = Missiontime;
14866                                         break;
14867                                 default:
14868                                         if (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) {
14869                                                 maybe_set_dynamic_chase(aip, hitter_objnum);
14870                                                 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14871                                         }
14872
14873                                         break;
14874                                 }
14875                         } else {
14876                                 // AL 3-15-98: Prevent escape pods from entering chase mode
14877                                 if ( (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) ) {
14878                                         maybe_set_dynamic_chase(aip, hitter_objnum);
14879                                 }
14880                                 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14881                         }
14882                 }
14883         }
14884 }
14885
14886 //      Ship shipnum has been destroyed.
14887 //      Cleanup.
14888 // the parameter 'method' is used to tell is this ship was destroyed or it departed normally.
14889 // This function will get called in either case, and there are things that should be done if
14890 // the ship actually gets destroyed which shouldn't get done if it departed.
14891 void ai_ship_destroy(int shipnum, int method)
14892 {
14893         int             objnum;
14894         object  *other_objp;
14895         ship            *shipp;
14896         ship_obj        *so;
14897         ai_info *dead_aip;
14898
14899         Assert((shipnum >= 0) && (shipnum < MAX_SHIPS));
14900         objnum = Ships[shipnum].objnum;
14901         dead_aip = &Ai_info[Ships[shipnum].ai_index];
14902
14903         // if I was getting repaired, or awaiting repair, then cleanup the repair mode.  When awaiting repair, the dock_objnum
14904         // is -1.  When the support ship is on the way, the dock_objnum >= 0 (points to support ship).
14905         if ( dead_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14906                 if ( dead_aip->dock_objnum >= 0 )
14907                         ai_do_objects_repairing_stuff( &Objects[objnum], &Objects[dead_aip->dock_objnum], REPAIR_INFO_END);
14908                 else
14909                         ai_do_objects_repairing_stuff( &Objects[objnum], NULL, REPAIR_INFO_END );
14910         }
14911
14912         //      For all objects that had this ship as a target, wipe it out, forcing find of a new enemy.
14913         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
14914                 other_objp = &Objects[so->objnum];
14915                 Assert(other_objp->instance != -1);
14916
14917                 shipp = &Ships[other_objp->instance];
14918                 Assert(shipp->ai_index != -1);
14919
14920                 ai_info *aip = &Ai_info[shipp->ai_index];
14921
14922                 // MWA 2/11/98
14923                 // code commented out below is taken care of in ai_cleanup_dock_mode when gets called when the
14924                 // support ship starts it's death roll.
14925
14926                 //      If the destroyed ship was on its way to repair the current ship
14927                 if (aip->dock_objnum == objnum) {
14928
14929                         // clean up the flags for any kind of docking mode.  If aip was part of a goal of dock/undock
14930                         // then it will get cleaned up by the goal code.
14931                         ai_do_objects_undocked_stuff( other_objp, NULL );
14932
14933                         if ( aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14934                                 int abort_reason;
14935                                 if ( method == SEF_DEPARTED ) {
14936                                         abort_reason = REPAIR_INFO_ABORT;
14937                                 } else {
14938                                         abort_reason = REPAIR_INFO_KILLED;
14939                                 }
14940                                 ai_do_objects_repairing_stuff( other_objp, NULL, abort_reason );
14941                         }
14942                 }
14943
14944                 if (aip->target_objnum == objnum) {
14945                         set_target_objnum(aip, -1);
14946                         //      If this ship had a dynamic goal of chasing the dead ship, clear the dynamic goal.
14947                         if (aip->resume_goal_time != -1)
14948                                 aip->active_goal = AI_GOAL_NONE;
14949                 }
14950
14951                 if (aip->goal_objnum == objnum) {
14952                         aip->goal_objnum = -1;
14953                         aip->goal_signature = -1;
14954                 }
14955
14956                 if (aip->guard_objnum == objnum) {
14957                         aip->guard_objnum = -1;
14958                         aip->guard_signature = -1;
14959                 }
14960
14961                 if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[Objects[objnum].instance].ai_index].wing)) {
14962                         if (aip->guard_wingnum != aip->wing)
14963                                 ai_set_guard_wing(other_objp, aip->guard_wingnum);
14964                 }
14965
14966                 if (aip->hitter_objnum == objnum)
14967                         aip->hitter_objnum = -1;
14968
14969         }
14970
14971 }
14972
14973 /*
14974 //      Interface function to goals code.
14975 //      Make object *objp fly to point *vp and warp out.
14976 void ai_warp_out(object *objp, vector *vp)
14977 {
14978         ai_info *aip;
14979
14980         aip = &Ai_info[Ships[objp->instance].ai_index];
14981
14982         if (aip->mode != AIM_WARP_OUT) {
14983                 ai_set_mode_warp_out(objp, aip);
14984         }
14985         float   dist;
14986         float   dot;
14987         vector  v2v;
14988         ai_info *aip;
14989
14990         dist = vm_vec_normalized_dir(&v2v, vp, &objp->pos);
14991
14992         if (dist < objp->radius + 5.0f) {
14993
14994                 // Start the warp out effect 
14995                 shipfx_warpout_start(objp);
14996
14997         } else {
14998                 dot = vm_vec_dot(&objp->orient.fvec, &v2v);
14999
15000                 aip = &Ai_info[Ships[objp->instance].ai_index];
15001
15002                 if (dist > 500.0f)
15003                         accelerate_ship(aip, 1.0f);
15004                 else
15005                         accelerate_ship(aip, (3*dot + 1.0f)/4.0f);
15006
15007                 turn_towards_point(objp, vp, NULL, 0.0f);
15008         }
15009 }
15010 */
15011
15012
15013 //      Do stuff at start of deathroll.
15014 void ai_deathroll_start(object *ship_obj)
15015 {
15016         ai_info *aip;
15017         ship            *shipp, *other_ship;
15018
15019         shipp = &Ships[ship_obj->instance];
15020         aip = &Ai_info[shipp->ai_index];
15021
15022         // mark object we are docked with so we can do damage and separate during deathroll
15023         // keep dock_objnum_when_dead from being changed if already set (only allow to be set when -1)
15024         if (Ships[ship_obj->instance].dock_objnum_when_dead == -1) {
15025                 Ships[ship_obj->instance].dock_objnum_when_dead = aip->dock_objnum;
15026                 // set other_ship dock_objnum_when_dead, if other_ship exits.
15027                 if (Ships[ship_obj->instance].dock_objnum_when_dead != -1) {
15028                         other_ship = &Ships[Objects[aip->dock_objnum].instance];
15029                         other_ship->dock_objnum_when_dead = shipp->objnum;
15030                 }
15031         }
15032
15033         ai_cleanup_dock_mode(aip, shipp);
15034
15035         aip->mode = AIM_NONE;
15036 }
15037
15038 //      Object *requester_objp tells rearm ship to abort rearm.
15039 //      Returns true if it succeeded, else false.
15040 //      To succeed means you were previously rearming.
15041 int ai_abort_rearm_request(object *requester_objp)
15042 {
15043         ship            *requester_shipp;
15044         ai_info *requester_aip;
15045
15046         Assert(requester_objp->type == OBJ_SHIP);
15047         if(requester_objp->type != OBJ_SHIP){
15048                 return 0;
15049         }
15050         Assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));      
15051         if((requester_objp->instance < 0) || (requester_objp->instance >= MAX_SHIPS)){
15052                 return 0;
15053         }
15054         requester_shipp = &Ships[requester_objp->instance];
15055         Assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));          
15056         if((requester_shipp->ai_index < 0) || (requester_shipp->ai_index >= MAX_AI_INFO)){
15057                 return 0;
15058         }       
15059         requester_aip = &Ai_info[requester_shipp->ai_index];
15060         
15061         if (requester_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)){
15062
15063                 // dock_objnum is always valid once a rearm repair has been requested.  It points to the
15064                 // ship that is coming to repair me.
15065                 if (requester_aip->dock_objnum != -1) {
15066                         object  *repair_objp;
15067                         ai_info *repair_aip;
15068
15069                         repair_objp = &Objects[requester_aip->dock_objnum];
15070                         repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
15071
15072                         //      Make sure signatures match.  This prevents nasty bugs in which an object
15073                         //      that was repairing another is destroyed and is replaced by another ship
15074                         //      before this code comes around.
15075                         if (repair_objp->signature == requester_aip->dock_signature) {
15076
15077                                 Assert( repair_objp->type == OBJ_SHIP );
15078
15079                                 // if support ship is in the process of undocking, don't do anything.
15080                                 if ( repair_aip->submode < AIS_UNDOCK_0 ) {
15081                                         ai_do_objects_repairing_stuff( requester_objp, repair_objp, REPAIR_INFO_ABORT );
15082
15083                                         if ( repair_aip->submode == AIS_DOCK_4 )
15084                                                 repair_aip->submode = AIS_UNDOCK_0;
15085                                         else
15086                                                 repair_aip->submode = AIS_UNDOCK_3;
15087
15088                                         repair_aip->submode_start_time = Missiontime;
15089                                 } else {
15090                                         nprintf(("AI", "Not aborting rearm since already undocking\n"));
15091                                 }
15092                         }
15093                 } else {
15094                         // setting these flags is the safe things to do.  There may not be a corresponding repair
15095                         // ship for this guys since a repair ship may be currently repairing someone else.
15096                         ai_do_objects_repairing_stuff( requester_objp, NULL, REPAIR_INFO_ABORT );
15097
15098                         // try and remove this guy from an arriving support ship.
15099                         mission_remove_scheduled_repair(requester_objp);
15100                 }
15101
15102                 return 1;
15103         } else if ( requester_aip->ai_flags & AIF_REPAIRING ) {
15104                 // a support ship can request to abort when he is told to do something else (like warp out).
15105                 // see if this support ships goal_objnum is valid.  If so, then issue this ai_abort comment
15106                 // for the ship that he is enroute to repair
15107                 if ( requester_aip->goal_objnum != -1 ) {
15108                         int val;
15109
15110                         val = ai_abort_rearm_request( &Objects[requester_aip->goal_objnum] );
15111                         return val;
15112                 }
15113         }
15114
15115         return 0;
15116 }
15117
15118 // function which gets called from ai-issue_rearm_request and from code in missionparse.cpp
15119 // to actually issue the rearm goal (support_obj to rearm requester_obj);
15120 void ai_add_rearm_goal( object *requester_objp, object *support_objp )
15121 {
15122         ship *support_shipp, *requester_shipp;
15123         ai_info *support_aip, *requester_aip;
15124
15125         support_shipp = &Ships[support_objp->instance];
15126         requester_shipp = &Ships[requester_objp->instance];
15127         requester_aip = &Ai_info[requester_shipp->ai_index];
15128
15129         Assert( support_shipp->ai_index != -1 );
15130         support_aip = &Ai_info[support_shipp->ai_index];
15131
15132         // if the requester is a player object, issue the order as the squadmate messaging code does.  Doing so
15133         // ensures that the player get a higher priority!
15134         requester_aip->ai_flags |= AIF_AWAITING_REPAIR; //      Tell that I'm awaiting repair.
15135         if ( requester_objp->flags & OF_PLAYER_SHIP )
15136                 ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_REARM_REPAIR, -1, requester_shipp->ship_name, support_aip );
15137         else
15138                 ai_add_goal_ship_internal( support_aip, AI_GOAL_REARM_REPAIR, requester_shipp->ship_name, -1, -1 );
15139
15140 }
15141
15142 //      Object *requester_objp requests rearming.
15143 //      Returns objnum of ship coming to repair requester on success
15144 //      Success means you found someone to rearm you and you weren't previously rearming.
15145 int ai_issue_rearm_request(object *requester_objp)
15146 {
15147         object  *objp;
15148         ship            *requester_shipp;
15149         ai_info *requester_aip;
15150
15151         Assert(requester_objp->type == OBJ_SHIP);
15152         Assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));
15153         requester_shipp = &Ships[requester_objp->instance];
15154         Assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));
15155         requester_aip = &Ai_info[requester_shipp->ai_index];
15156         
15157         //      Make sure not already awaiting repair.
15158         if (requester_aip->ai_flags & AIF_AWAITING_REPAIR) {
15159                 nprintf(("AI", "Ship %s already awaiting rearm by ship %s.\n", requester_shipp->ship_name, &Ships[Objects[requester_aip->dock_objnum].instance].ship_name));    
15160                 return -1;
15161         }
15162
15163         if ( !is_support_allowed(requester_objp) )
15164                 return -1;
15165
15166         //nprintf(("AI", "Ship %s requesting rearming.\n", requester_shipp->ship_name));
15167         requester_aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);  //      Might request again after this much time.
15168
15169         // call ship_find_repair_ship to get a support ship.  If none is found, then we will warp one in.  This
15170         // function will return the next available ship which can repair requester
15171         objp = ship_find_repair_ship( requester_objp );
15172         ai_do_objects_repairing_stuff( requester_objp, objp, REPAIR_INFO_QUEUE );
15173         if ( objp ) {
15174
15175                 // MWA 5/14/98 -- moved next item into the ai_do_objects_repairing_stuff function so that clients
15176                 // would properly update their hud support view
15177                 //ai_add_rearm_goal( requester_objp, objp );
15178                 return OBJ_INDEX(objp);
15179
15180         } else {
15181                 // call to warp in repair ship!!!!  for now, warp in any number of ships needed.  Should cap it to
15182                 // some reasonable max (or let support ships warp out).  We should assume here that ship_find_repair_ship()
15183                 // would have returned a valid object if there are too many support ships already in the mission
15184                 mission_warp_in_support_ship( requester_objp );
15185
15186                 return -1;
15187         }
15188
15189 }
15190
15191 // make objp rearm and repair goal_objp
15192 void ai_rearm_repair( object *objp, object  *goal_objp, int priority, int docker_index, int dockee_index )
15193 {
15194         ai_info *aip, *goal_aip;
15195
15196         aip = &Ai_info[Ships[objp->instance].ai_index];
15197         aip->goal_objnum = goal_objp-Objects;
15198
15199         // nprintf(("AI", "Ship %s preparing to rearm ship %s.\n", shipp->ship_name, requester_shipp->ship_name));
15200
15201         ai_dock_with_object(objp, goal_objp, priority, AIDO_DOCK, docker_index, dockee_index);
15202         aip->ai_flags |= AIF_REPAIRING;                                         //      Tell that repair guy is busy trying to repair someone.
15203
15204         goal_aip = &Ai_info[Ships[goal_objp->instance].ai_index];
15205         goal_aip->dock_objnum = objp-Objects;           //      Tell which object is coming to repair.
15206         goal_aip->dock_signature = objp->signature;
15207
15208         ai_do_objects_repairing_stuff( goal_objp, objp, REPAIR_INFO_ONWAY );
15209
15210         goal_aip->abort_rearm_timestamp = timestamp(NEXT_REARM_TIMESTAMP*3/2);
15211 }
15212
15213 // Given a dockee object and the index of the dockbay for that object (ie the dockbay index
15214 // into polymodel->dockbays[] for the model associated with the object), return the index
15215 // of a path_num associated with than dockbay (this is an index into polymodel->paths[])
15216 int ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index)
15217 {
15218         if ( dockbay_index < 0 || dockee_objp == NULL ) {
15219                 Int3();         // should never happen
15220                 return -1;
15221         }
15222
15223         if ( dockee_objp->type == OBJ_SHIP ) {
15224                 int                     path_num;
15225                 polymodel       *pm;
15226
15227                 pm = model_get( Ships[dockee_objp->instance].modelnum );
15228
15229                 // sanity checks
15230                 Assert(pm->n_docks > dockbay_index);
15231                 Assert(pm->docking_bays[dockbay_index].num_spline_paths > 0);
15232                 Assert(pm->docking_bays[dockbay_index].splines != NULL);
15233                 if(pm->n_docks <= dockbay_index){
15234                         return -1;
15235                 }
15236                 if(pm->docking_bays[dockbay_index].num_spline_paths <= 0){
15237                         return -1;
15238                 }
15239                 if(pm->docking_bays[dockbay_index].splines == NULL){
15240                         return -1;
15241                 }
15242
15243                 // We only need to return one path for the dockbay, so return the first
15244                 path_num = pm->docking_bays[dockbay_index].splines[0];
15245                 return path_num;
15246         } else {
15247                 return -1;
15248         }
15249 }
15250
15251 //      Actually go ahead and fire the synaptics.
15252 void cheat_fire_synaptic(object *objp, ship *shipp, ai_info *aip)
15253 {
15254         ship_weapon     *swp;
15255         swp = &shipp->weapons;
15256         int     current_bank = swp->current_secondary_bank;
15257
15258         ai_select_secondary_weapon(objp, swp, WIF_SPAWN, 0);
15259         if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
15260                 if (ship_fire_secondary(objp)) {
15261                         nprintf(("AI", "ship %s cheat fired synaptic!\n", shipp->ship_name));
15262                         swp->next_secondary_fire_stamp[current_bank] = timestamp(2500);
15263                 }
15264         }
15265 }
15266
15267 //      For the subspace mission (sm3-09a)
15268 //              for delta wing
15269 //                      if they're sufficiently far into the mission
15270 //                              if they're near one or more enemies
15271 //                                      every so often
15272 //                                              fire a synaptic if they have one.
15273 void maybe_cheat_fire_synaptic(object *objp, ai_info *aip)
15274 {
15275         //      Only do in subspace missions.
15276         if ( The_mission.flags & MISSION_FLAG_SUBSPACE )        {
15277                 ship    *shipp;
15278                 int     num, time;
15279
15280                 shipp = &Ships[objp->instance];
15281
15282                 if (!(strnicmp(shipp->ship_name, NOX("delta"), 5))) {
15283                         num = shipp->ship_name[6] - '1';
15284
15285                         if ((num >= 0) && (num <= 3)) {
15286                                 time = Missiontime >> 16;       //      Convert to seconds.
15287
15288                                 time -= 2*60;   //      Subtract off two minutes.
15289
15290                                 if (time > 0) {
15291                                         int modulus = 17 + num*3;
15292
15293                                         if ((time % modulus) < 2) {
15294                                                 int count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(objp)), &objp->pos, 1500.0f);
15295
15296                                                 if (count > 0) {
15297                                                         cheat_fire_synaptic(objp, shipp, aip);
15298                                                 }
15299                                         }
15300                                 }
15301                         }
15302                 }
15303         }
15304
15305 }
15306