]> icculus.org git repositories - taylor/freespace2.git/blob - src/ship/aicode.cpp
ryan's struct patch for gcc 2.95
[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.5  2002/06/17 06:33:10  relnev
19  * ryan's struct patch for gcc 2.95
20  *
21  * Revision 1.4  2002/06/09 04:41:26  relnev
22  * added copyright header
23  *
24  * Revision 1.3  2002/06/01 07:12:34  relnev
25  * a few NDEBUG updates.
26  *
27  * removed a few warnings.
28  *
29  * Revision 1.2  2002/05/03 13:34:33  theoddone33
30  * More stuff compiles
31  *
32  * Revision 1.1.1.1  2002/05/03 03:28:10  root
33  * Initial import.
34  *
35  * 
36  * 107   9/15/99 4:42a Mikek
37  * Make any big ship attacking Colossus, or Colossus attacking any large
38  * ship not use big cruiser movement code.
39  * 
40  * 106   9/15/99 3:28a Jimb
41  * Make all big ships in sm3-08 not do cruiser chase code when attacking
42  * Colossus.  Added so Beast doesn't swerve away from Colossus.
43  * 
44  * 105   9/14/99 4:18p Andsager
45  * hack for mission sm3-08 to abort cruiser_chase as sathanas is about to
46  * begin circling colossus.
47  * 
48  * 104   9/08/99 10:44p Andsager
49  * Make HUGE ships not die when warping out, after warp effect started.
50  * 
51  * 103   9/03/99 11:40p Mikek
52  * Comment out an annoying nprintf().
53  * 
54  * 102   9/01/99 11:26p Dave
55  * Fixed release build warnings.
56  * 
57  * 101   9/01/99 9:12p Mikek
58  * Make it a boatload harder to become a traitor from hitting a large
59  * ship.
60  * 
61  * 100   9/01/99 4:01p Andsager
62  * Make sure BIG|HUGE ships do not respond to shockwaves
63  * 
64  * 99    9/01/99 10:09a Dave
65  * Pirate bob.
66  * 
67  * 98    8/31/99 4:24p Andsager
68  * Reduce collisions when attacking big ships.
69  * 
70  * 97    8/31/99 7:33a Mikek
71  * Improvements in formation flying, less silly behavior, especially when
72  * leader is moving very slowly.
73  * 
74  * 96    8/31/99 5:48a Mikek
75  * Making ships not overshoot so much in formation flying.  Intermediate
76  * checkin.
77  * 
78  * 95    8/30/99 12:03a Mikek
79  * Make guard behavior much less annoying.  Guarders don't get quite so
80  * close and they try to avoid striking the target they are guarding.
81  * 
82  * 94    8/29/99 4:18p Andsager
83  * New "burst" limit for friendly damage.  Also credit more damage done
84  * against large friendly ships.
85  * 
86  * 93    8/28/99 7:29p Dave
87  * Fixed wingmen persona messaging. Make sure locked turrets don't count
88  * towards the # attacking a player.
89  * 
90  * 92    8/26/99 10:46p Andsager
91  * Apply shockwave damage to lethality.
92  * 
93  * 91    8/26/99 8:52p Dave
94  * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
95  * 
96  * 90    8/26/99 5:14p Andsager
97  * 
98  * 89    8/24/99 8:55p Dave
99  * Make sure nondimming pixels work properly in tech menu.
100  * 
101  * 88    8/23/99 6:21p Jefff
102  * added "no traitor" option to missions (and fred)
103  * 
104  * 87    8/20/99 3:36p Andsager
105  * Make sure we don;t miss stealth sweep points.
106  * 
107  * 86    8/16/99 8:21a Andsager
108  * fix link error
109  * 
110  * 85    8/16/99 8:19a Andsager
111  * Add project_point_onto_bbox() to fvi and include in aicode
112  * 
113  * 84    8/15/99 1:30p Dave
114  * Removed some bounding box code because of link errors. Assuming needed
115  * function just needs to get checked in by DaveA.
116  * 
117  * 83    8/15/99 11:59a Andsager
118  * For targing big/huge ships, find nearest distance to bbox, not center.
119  * 
120  * 82    8/13/99 2:20p Andsager
121  * Add speed modification to chances turret will find stealth ship
122  * 
123  * 81    8/13/99 10:49a Andsager
124  * Knossos and HUGE ship warp out.  HUGE ship warp in.  Stealth search
125  * modes dont collide big ships.
126  * 
127  * 80    8/10/99 5:02p Andsager
128  * Fix bug where AI gets stuck in SM_EVADE_WEAPON with no target.
129  * 
130  * 79    8/10/99 11:58a Andsager
131  * Allow turrets to sometimes see stealth.
132  * 
133  * 78    7/31/99 2:57p Dave
134  * Scaled flak aim and jitter by weapon subsystem strength.
135  * 
136  * 77    7/27/99 10:33p Andsager
137  * improve ai for attacking stealth.  reduced jitter in aim.  reduced
138  * error in position when avoiding.  skill level support for attacking
139  * stealth.  Made target error same for team vs. team.
140  * 
141  * 76    7/27/99 10:49a Andsager
142  * Make turret fire rate independent of team for HUGE turrets, and also
143  * for mult team vs. team.
144  * 
145  * 75    7/26/99 12:14p Andsager
146  * Apply cap to how much slower a transport flies with cargo.  Remove
147  * limit on waypoint speed for training.  Enemy ai get stealth exact pos
148  * when stealth fires
149  * 
150  * 74    7/20/99 1:49p Dave
151  * Peter Drake build. Fixed some release build warnings.
152  * 
153  * 73    7/19/99 2:13p Dave
154  * Added some new strings for Heiko.
155  * 
156  * 72    7/19/99 12:02p Andsager
157  * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
158  * only blow up subsystem if its strength is > 0
159  * 
160  * 71    7/15/99 9:20a Andsager
161  * FS2_DEMO initial checkin
162  * 
163  * 70    7/14/99 1:44p Andsager
164  * modify ai_guard for BIG ships to circle around the long axis
165  * 
166  * 69    7/09/99 5:54p Dave
167  * Seperated cruiser types into individual types. Added tons of new
168  * briefing icons. Campaign screen.
169  * 
170  * 68    7/08/99 4:32p Andsager
171  * fix bug with turret-tagged-only
172  * 
173  * 67    7/08/99 12:06p Andsager
174  * Add turret-tagged-only and turret-tagged-clear sexp.
175  * 
176  * 66    7/02/99 3:49p Andsager
177  * Remove debug code.  Allow targeting of stealth from any weapon it
178  * fires.
179  * 
180  * 65    7/02/99 2:01p Andsager
181  * Fix bug where big ship tries to evade dumpfire weapon.
182  * 
183  * 64    7/02/99 10:58a Andsager
184  * Put in big ship - big ship attack mode.  Modify stealth sweep ai.
185  * 
186  * 63    6/30/99 5:53p Dave
187  * Put in new anti-camper code.
188  * 
189  * 62    6/28/99 3:22p Anoop
190  * Fix turret optimization, where ship may not have any valid subsystems
191  * (all blown off).
192  * 
193  * 61    6/25/99 5:56p Andsager
194  * First real pass on stealth ai.
195  * 
196  * 60    6/25/99 3:08p Dave
197  * Multiple flyby sounds.
198  * 
199  * 59    6/25/99 1:12p Danw
200  * DKA:  Make sure big ship has subsystems before trying to target them.
201  * 
202  * 58    6/25/99 10:56a Johnson
203  * Fixed dumb ai code.
204  * 
205  * 57    6/24/99 5:15p Dave
206  * Make sure stride is always at least one for checking turret subsystem
207  * targets.
208  * 
209  * 56    6/24/99 4:59p Dave
210  * Significant speedups to turret firing.
211  * 
212  * 55    6/23/99 5:51p Andsager
213  * Add waypoint-cap-speed.  Checkin stealth ai - inactive.
214  * 
215  * 54    6/16/99 10:21a Dave
216  * Added send-message-list sexpression.
217  * 
218  * 53    6/15/99 9:25a Andsager
219  * Make guard and dynamic chase (who hit you) work with stealth
220  * 
221  * 52    6/14/99 3:21p Andsager
222  * Allow collisions between ship and its debris.  Fix up collision pairs
223  * when large ship is warping out.
224  * 
225  * 51    6/14/99 10:45a Dave
226  * Made beam weapons specify accuracy by skill level in the weapons.tbl
227  * 
228  * 50    6/03/99 8:11a Andsager
229  * 
230  * 49    6/02/99 5:41p Andsager
231  * Reduce range of secondary weapons not fired from turrets in nebula.
232  * Reduce range of beams fired from turrrets in nebula
233  * 
234  * 48    6/02/99 3:23p Andsager
235  * Make AI aware of team visibility.  Allow player targeting with team
236  * visibility info.  Make stealth ships not targetable by AI in nebula
237  * unless tagged.
238  * 
239  * 47    6/02/99 12:52p Andsager
240  * Added team-wide ship visibility.  Implemented for player.
241  * 
242  * 46    6/01/99 8:35p Dave
243  * Finished lockarm weapons. Added proper supercap weapons/damage. Added
244  * awacs-set-radius sexpression.
245  * 
246  * 45    5/28/99 5:35p Andsager
247  * Make ai nebula aware
248  * 
249  * 44    5/24/99 9:55a Dave
250  * Fixed stream weapon ai firing problem. ick.
251  * 
252  * 43    5/20/99 7:00p Dave
253  * Added alternate type names for ships. Changed swarm missile table
254  * entries.
255  * 
256  * 42    5/18/99 1:30p Dave
257  * Added muzzle flash table stuff.
258  * 
259  * 41    5/12/99 2:55p Andsager
260  * Implemented level 2 tag as priority in turret object selection
261  * 
262  * 40    5/12/99 10:42a Andsager
263  * Fix turret bug allowing HUGE turrets to fire at fighters
264  * 
265  * 39    5/06/99 11:46a Andsager
266  * Bug fixes.  Don't get into illegal strafe submode.  Don't choose turret
267  * enemy objnum for beam protected.
268  * 
269  * 38    5/03/99 10:50p Andsager
270  * Make Asteroid_obj_list.  Change get_nearest_turret_objnum() to use
271  * Asteroid_obj_list, Ship_obj_list and Missile_obj_list vs.
272  * obj_used_list.
273  * 
274  * 37    4/29/99 2:29p Dave
275  * Made flak work much better in multiplayer.
276  * 
277  * 36    4/28/99 11:36p Dave
278  * Tweaked up subspace missile strike a bit,
279  * 
280  * 35    4/28/99 3:11p Andsager
281  * Stagger turret weapon fire times.  Make turrets smarter when target is
282  * protected or beam protected.  Add weaopn range to weapon info struct.
283  * 
284  * 34    4/26/99 10:58a Andsager
285  * Add OF_BEAM_PROTECTED flag to keep object from being targeted for zing.
286  * 
287  * 33    4/23/99 12:12p Andsager
288  * Modify wing positions when player is wing leader to prevent some
289  * collisions.
290  * 
291  * 32    4/23/99 12:01p Johnson
292  * Added SIF_HUGE_SHIP
293  * 
294  * 31    4/22/99 11:06p Dave
295  * Final pass at beam weapons. Solidified a lot of stuff. All that remains
296  * now is to tweak and fix bugs as they come up. No new beam weapon
297  * features.
298  * 
299  * 30    4/20/99 6:39p Dave
300  * Almost done with artillery targeting. Added support for downloading
301  * images on the PXO screen.
302  * 
303  * 29    4/20/99 3:40p Andsager
304  * Changes to big ship ai.  Uses bounding box as limit where to fly to
305  * when flying away.
306  * 
307  * 28    4/16/99 5:54p Dave
308  * Support for on/off style "stream" weapons. Real early support for
309  * target-painting lasers.
310  * 
311  * 27    4/02/99 9:55a Dave
312  * Added a few more options in the weapons.tbl for beam weapons. Attempt
313  * at putting "pain" packets into multiplayer.
314  * 
315  * 26    3/28/99 5:58p Dave
316  * Added early demo code. Make objects move. Nice and framerate
317  * independant, but not much else. Don't use yet unless you're me :)
318  * 
319  * 25    3/19/99 9:51a Dave
320  * Checkin to repair massive source safe crash. Also added support for
321  * pof-style nebulae, and some new weapons code.
322  * 
323  * 24    3/08/99 7:03p Dave
324  * First run of new object update system. Looks very promising.
325  * 
326  * 23    3/05/99 3:55p Anoop
327  * Handle some asserts properly.
328  * 
329  * 22    3/04/99 6:09p Dave
330  * Added in sexpressions for firing beams and checking for if a ship is
331  * tagged.
332  * 
333  * 21    3/02/99 9:25p Dave
334  * Added a bunch of model rendering debug code. Started work on fixing
335  * beam weapon wacky firing.
336  * 
337  * 20    2/25/99 2:32p Anoop
338  * (Alan). Fixed ai path following code for AI_BAY_EMERGE. Put in sanity
339  * check so that when the last point on the path is reached, it finishes.
340  * 
341  * 19    2/19/99 2:11p Anoop
342  * Put in some nice handling code for wacky support ship problems (like no
343  * docking paths)
344  * 
345  * 18    2/17/99 2:11p Dave
346  * First full run of squad war. All freespace and tracker side stuff
347  * works.
348  * 
349  * 17    2/11/99 5:22p Andsager
350  * Fixed bugs, generalized block Sexp_variables
351  * 
352  * 16    1/29/99 5:07p Dave
353  * Fixed multiplayer stuff. Put in multiplayer support for rapid fire
354  * missiles.
355  * 
356  * 15    1/29/99 2:25p Andsager
357  * Added turret_swarm_missiles
358  * 
359  * 14    1/27/99 9:56a Dave
360  * Temporary checkin of beam weapons for Dan to make cool sounds.
361  * 
362  * 13    1/24/99 11:37p Dave
363  * First full rev of beam weapons. Very customizable. Removed some bogus
364  * Int3()'s in low level net code.
365  * 
366  * 12    1/21/99 10:44a Dave
367  * More beam weapon stuff. Put in warmdown time.
368  * 
369  * 11    1/12/99 5:45p Dave
370  * Moved weapon pipeline in multiplayer to almost exclusively client side.
371  * Very good results. Bandwidth goes down, playability goes up for crappy
372  * connections. Fixed object update problem for ship subsystems.
373  * 
374  * 10    1/08/99 2:08p Dave
375  * Fixed software rendering for pofview. Super early support for AWACS and
376  * beam weapons.
377  * 
378  * 9     12/23/98 2:53p Andsager
379  * Added ship activation and gas collection subsystems, removed bridge
380  * 
381  * 8     11/12/98 12:13a Dave
382  * Tidied code up for multiplayer test. Put in network support for flak
383  * guns.
384  * 
385  * 7     11/05/98 5:55p Dave
386  * Big pass at reducing #includes
387  * 
388  * 6     10/26/98 9:42a Dave
389  * Early flak gun support.
390  * 
391  * 5     10/23/98 3:51p Dave
392  * Full support for tstrings.tbl and foreign languages. All that remains
393  * is to make it active in Fred.
394  * 
395  * 4     10/20/98 1:39p Andsager
396  * Make so sparks follow animated ship submodels.  Modify
397  * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
398  * submodel_num.  Add submodel_num to multiplayer hit packet.
399  * 
400  * 3     10/13/98 9:29a Dave
401  * Started neatening up freespace.h. Many variables renamed and
402  * reorganized. Added AlphaColors.[h,cpp]
403  * 
404  * 2     10/07/98 10:53a Dave
405  * Initial checkin.
406  * 
407  * 1     10/07/98 10:51a Dave
408  * 
409  * 
410  * $NoKeywords: $
411  */
412
413 // This module contains the actual AI code that does interesting stuff
414 // to objects.   The code in Ai.cpp is just for bookeeping, allocating
415 // ai slots and linking them to ships.
416
417 #include "pstypes.h"
418 #include "fix.h"
419 #include "linklist.h"
420 #include "object.h"
421 #include "physics.h"
422 #include "vecmat.h"
423 #include "ship.h"
424 #include "model.h"
425 #include "2d.h"
426 #include "3d.h"
427 #include "ai.h"
428 #include "floating.h"
429 #include "player.h"
430 #include "freespace.h"
431 #include "weapon.h"
432 #include "missiongoals.h"
433 #include "missionlog.h"
434 #include "timer.h"
435 #include "sound.h"
436 #include "aigoals.h"
437 #include "gamesnd.h"
438 #include "hudmessage.h"
439 #include "missionmessage.h"
440 #include "cmeasure.h"
441 #include "staticrand.h"
442 #include "multimsgs.h"
443 #include "afterburner.h"
444 #include "hudets.h"
445 #include "shipfx.h"
446 #include "shiphit.h"
447 #include "aibig.h"
448 #include "multiutil.h"
449 #include "hud.h"
450 #include "objcollide.h"
451 #include "asteroid.h"
452 #include "hudlock.h"
453 #include "missiontraining.h"
454 #include "gamesequence.h"
455 #include "joy_ff.h"
456 #include "localize.h"
457 #include "flak.h"
458 #include "beam.h"
459 #include "multi.h"
460 #include "swarm.h"
461 #include "multi_team.h"
462 #include "awacs.h"
463 #include "fvi.h"
464
465 #ifndef PLAT_UNIX
466 #pragma optimize("", off)
467 #pragma auto_inline(off)
468 #endif
469
470 #define UNINITIALIZED_VALUE     -99999.9f
471
472 #define INSTRUCTOR_SHIP_NAME NOX("instructor")
473
474 #define AICODE_SMALL_MAGNITUDE  0.001f          // cosider a vector NULL if mag is less than this
475
476 #define NEXT_REARM_TIMESTAMP (60*1000)                  //      Ships will re-request rearm, typically, after this long.
477
478 #define BEAM_NEBULA_RANGE_REDUCE_FACTOR         0.8
479
480 // AIM_CHASE submode defines
481 // SM_STEALTH_FIND
482 #define SM_SF_AHEAD             0
483 #define SM_SF_BEHIND    1
484 #define SM_SF_BAIL              2
485
486 // SM_STEALTH_SWEEP
487 #define SM_SS_SET_GOAL  -1
488 #define SM_SS_BOX0              0
489 #define SM_SS_LR                        1
490 #define SM_SS_UL                        2
491 #define SM_SS_BOX1              3
492 #define SM_SS_UR                        4
493 #define SM_SS_LL                        5
494 #define SM_SS_BOX2              6
495 #define SM_SS_DONE              7
496
497 //XSTR:OFF
498
499 char *Mode_text[MAX_AI_BEHAVIORS] = {
500         "CHASE",
501         "EVADE",
502         "GET_BEHIND",
503         "CHASE_LONG",
504         "SQUIGGLE",
505         "GUARD",
506         "AVOID",
507         "WAYPOINTS",
508         "DOCK",
509         "NONE",
510         "BIGSHIP",
511         "PATH",
512         "BE_REARMED",
513         "SAFETY",
514         "EV_WEAPON",
515         "STRAFE",
516         "PLAY_DEAD",
517         "BAY_EMERGE",
518         "BAY_DEPART",
519         "SENTRYGUN",
520         "WARP_OUT",
521 };
522
523 //      Submode text is only valid for CHASE mode.
524 char *Submode_text[] = {
525 "undefined",
526 "CONT_TURN",
527 "ATTACK   ",
528 "E_SQUIG  ",
529 "E_BRAKE  ",
530 "EVADE    ",
531 "SUP_ATTAK",
532 "AVOID    ",
533 "BEHIND   ",
534 "GET_AWAY ",
535 "E_WEAPON ",
536 "FLY_AWAY ",
537 "ATK_4EVER",
538 "STLTH_FND",
539 "STLTH_SWP",
540 "BIG_APPR",
541 "BIG_CIRC",
542 "BIG_PARL"
543 };
544
545 char *Strafe_submode_text[5] = {
546 "ATTACK",
547 "AVOID",
548 "RETREAT1",
549 "RETREAT2",
550 "POSITION"
551 };
552 //XSTR:ON
553
554 /*
555 //      Used for global ignore of objects.  If an object appears in the Ignore_objects array,
556 //      no one will attack it.
557 #define MAX_IGNORE_OBJECTS      16
558 typedef struct {
559         int     objnum;
560         int     signature;
561 } ignore_object;
562
563 ignore_object   Ignore_objects[MAX_IGNORE_OBJECTS];
564 */
565
566 typedef struct eval_enemy_obj_struct {
567         int                     turret_parent_objnum;                   // parent of turret
568         float                   weapon_travel_dist;                             // max targeting range of turret weapon
569         int                     enemy_team_mask;
570         int                     weapon_system_ok;                                       // is the weapon subsystem of turret ship ok
571         int                     big_only_flag;                                          // turret fires only at big and huge ships
572         vector          *tpos;
573         vector          *tvec;
574         ship_subsys *turret_subsys;
575         int                     current_enemy;
576
577
578         float                   nearest_attacker_dist;                  // nearest ship 
579         int                     nearest_attacker_objnum;
580
581         float                   nearest_homing_bomb_dist;               // nearest homing bomb
582         int                     nearest_homing_bomb_objnum;
583
584         float                   nearest_bomb_dist;                              // nearest non-homing bomb
585         int                     nearest_bomb_objnum;
586
587         float                   nearest_dist;                                           // nearest ship attacking this turret
588         int                     nearest_objnum;
589 }       eval_enemy_obj_struct;
590
591
592 control_info    AI_ci;
593
594 object *Pl_objp;
595 object *En_objp;
596
597 waypoint_list Waypoint_lists[MAX_WAYPOINT_LISTS];
598
599 // How close a turret has to be point at its target before it
600 // can fire.  If the dot of the gun normal and the vector from gun
601 // to target is greater than this, the turret fires.  The smaller
602 // the sloppier the shooting.
603 #define AICODE_TURRET_DUMBFIRE_ANGLE            (0.8f)  
604 #define AICODE_TURRET_HEATSEEK_ANGLE            (0.7f)  
605 #define AICODE_TURRET_MAX_TIME_IN_RANGE (5.0f)
606
607 #define REARM_SOUND_DELAY               (3*F1_0)                //      Amount of time to delay rearm/repair after mode start
608 #define REARM_BREAKOFF_DELAY    (3*F1_0)                //      Amount of time to wait after fully rearmed to breakoff.
609
610 #define MIN_DIST_TO_WAYPOINT_GOAL       5.0f
611 #define MAX_GUARD_DIST                                  250.0f
612 #define BIG_GUARD_RADIUS                                500.0f
613
614 #define MAX_EVADE_TIME                  (15 * 1000)     //      Max time to evade a weapon.
615
616 // defines for repair ship stuff.
617 #define MAX_REPAIR_SPEED                        25.0f
618 #define MAX_UNDOCK_ABORT_SPEED  2.0f
619
620 // defines for EMP effect stuff
621 #define MAX_EMP_INACCURACY              50.0f
622
623 // defines for stealth
624 #define MAX_STEALTH_INACCURACY  50.0f           // at max view dist
625 #define STEALTH_MAX_VIEW_DIST   400             // dist at which 1) stealth no longer visible 2) firing inaccuracy is greatest
626 #define STEALTH_VIEW_CONE_DOT   0.707           // (half angle of 45 degrees)
627
628
629 ai_class        Ai_classes[MAX_AI_CLASSES];
630 int     Ai_firing_enabled = 1;
631 int     Num_ai_classes;
632
633 int     AI_FrameCount = 0;
634 int     Ship_info_inited = 0;
635 int     AI_watch_object = 0; // Debugging, object to spew debug info for.
636 int     Num_waypoint_lists = 0;
637 int     Mission_all_attack = 0;                                 //      !0 means all teams attack all teams.
638
639 char *Skill_level_names(int level, int translate)
640 {
641         char *str = NULL;
642
643         #if NUM_SKILL_LEVELS != 5
644         #error Number of skill levels is wrong!
645         #endif
646
647         if(translate){
648                 switch( level ) {
649                 case 0:
650                         str = XSTR("Very Easy", 469);
651                         break;
652                 case 1:
653                         str = XSTR("Easy", 470);
654                         break;
655                 case 2:
656                         str = XSTR("Medium", 471);
657                         break;
658                 case 3:
659                         str = XSTR("Hard", 472);
660                         break;
661                 case 4:
662                         str = XSTR("Insane", 473);
663                         break;
664                 default:        
665                         Int3();
666                 }
667         } else {
668                 switch( level ) {
669                 case 0:
670                         str = NOX("Very Easy");
671                         break;
672                 case 1:
673                         str = NOX("Easy");
674                         break;
675                 case 2:
676                         str = NOX("Medium");
677                         break;
678                 case 3:
679                         str = NOX("Hard");
680                         break;
681                 case 4:
682                         str = NOX("Insane");
683                         break;
684                 default:        
685                         Int3();
686                 }
687         }
688
689         return str;
690 }
691
692 #define DELAY_TARGET_TIME       (12*1000)               //      time in milliseconds until a ship can target a new enemy after an order.
693
694 //      Make enemy ships turn more slowly at lower skill levels.
695 float   Turn_time_skill_level_scale[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.6f, 1.3f, 1.0f};
696
697 //      Maximum number of simultaneous homing weapons on player based on skill level.
698 int     Max_allowed_player_homers[NUM_SKILL_LEVELS] = {2, 3, 4, 7, 99};
699
700 //      Number of ships that can attack another ship at a given skill level.
701 int     Skill_level_max_attackers[NUM_SKILL_LEVELS] = {2, 3, 4, 5, 99};
702
703 //      How long until next predict position.
704 fix Skill_level_delay[NUM_SKILL_LEVELS] = {2*F1_0, 3*F1_0/2, 4*F1_0/3, F1_0/2, 0};
705
706 //      AI ships link primary weapons if energy levels greater than the following amounts:
707 float   Link_energy_levels_always[NUM_SKILL_LEVELS] = {100.0f, 80.0f, 60.0f, 40.0f, 20.0f};     //      always link
708 float   Link_energy_levels_maybe[NUM_SKILL_LEVELS] = {90.0f, 60.0f, 40.0f, 20.0f, 10.0f};       //      link if hull strength low
709
710 //      Seconds to add to time it takes to get enemy in range.  Only for player's enemies.
711 float   In_range_time[NUM_SKILL_LEVELS] = {2.0f, 1.4f, 0.75f, 0.0f, -1.0f};
712
713 //      No matter what, a random unit vector gets scaled by this amount in firing at an enemy.
714 //      Note that for shorter in-range times, these values get scaled, so a value of 0.5f is meaningful.
715 float   Aiming_error[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.3f, 0.7f, 0.2f};
716
717 //      Chance a countermeasure will be fired based on skill level.
718 float Cmeasure_fire_chance[NUM_SKILL_LEVELS] = {0.2f, 0.3f, 0.5f, 0.9f, 1.1f};  //      Note, this gets scaled by ai_class
719
720 float Shield_manage_delays[NUM_SKILL_LEVELS] = {5.0f, 4.0f, 2.5f, 1.2f, 0.1f};
721
722 // accuracy we feed into the beam weapons based upon skill system
723 // float Beam_accuracy[NUM_SKILL_LEVELS] = {2.0f, 1.5f, 1.0f, 0.7f, 0.4f};
724
725 extern float Ship_fire_delay_scale_hostile[NUM_SKILL_LEVELS];
726 extern float Ship_fire_delay_scale_friendly[NUM_SKILL_LEVELS];
727
728 pnode           Path_points[MAX_PATH_POINTS];
729 pnode           *Ppfp;                  //      Free pointer in path points.
730
731 float   AI_frametime;
732
733 char *Ai_class_names[MAX_AI_CLASSES];
734
735 // global for rearm status for teams
736 int Ai_friendly_rearm_timestamp, Ai_hostile_rearm_timestamp, Ai_neutral_rearm_timestamp, Ai_traitor_rearm_timestamp, Ai_unknown_rearm_timestamp;
737
738 // globals for dealing with when to fire huge secondary weapons
739 #define MAX_HUGE_SECONDARY_INFO 10
740
741 typedef struct {
742         int team;
743         int weapon_index;
744         int max_fire_count;
745         char    *shipname;
746 } huge_fire_info;
747
748 huge_fire_info Ai_huge_fire_info[MAX_HUGE_SECONDARY_INFO];
749
750 int Ai_last_arrive_path;        // index of ship_bay path used by last arrival from a fighter bay
751
752 // forward declarations
753 int     ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index);
754 void    create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count=1);
755 void    copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt=-1);
756
757 // ai_set_rearm_status takes a team (friendly, hostile, neutral) and a time.  This function
758 // sets the timestamp used to tell is it is a good time for this team to rearm.  Once the timestamp
759 // is no longer valid, then rearming is not a "good time"
760 // not safe.  Called from sexpression code.
761 void ai_set_rearm_status( int team, int time )
762 {
763         Assert( time >= 0 );
764
765         switch (team) {
766         case TEAM_FRIENDLY:
767                 Ai_friendly_rearm_timestamp = timestamp( time * 1000 );
768                 break;
769         case TEAM_HOSTILE:
770                 Ai_hostile_rearm_timestamp = timestamp( time * 1000 );
771                 break;
772         case TEAM_NEUTRAL:
773                 Ai_neutral_rearm_timestamp = timestamp( time * 1000 );
774                 break;
775         case TEAM_TRAITOR:
776                 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
777                 break;
778         case TEAM_UNKNOWN:
779                 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
780                 break;
781         default:
782                 Int3();
783                 break;
784         }
785 }
786
787 // int ai_good_time_to_rearm returns true(1) or false(0) if it is "safe" for the given
788 // object to rearm.  "safe" is currently defined by the mission designer using the good/bad
789 // time to rearm sexpressions.  This status is currently team based.  This function could
790 // be easily expended to further the definition of "safe"
791 int ai_good_time_to_rearm( object *objp )
792 {
793         int team, status;
794
795         Assert(objp->type == OBJ_SHIP);
796         team = Ships[objp->instance].team;
797         status = 0;
798
799         switch(team) {
800         case TEAM_FRIENDLY:
801                 status = timestamp_valid(Ai_friendly_rearm_timestamp);
802                 break;
803         case TEAM_HOSTILE:
804                 status = timestamp_valid(Ai_hostile_rearm_timestamp);
805                 break;
806         case TEAM_NEUTRAL:
807                 status = timestamp_valid(Ai_neutral_rearm_timestamp);
808                 break;
809         case TEAM_TRAITOR:
810                 status = timestamp_valid(Ai_traitor_rearm_timestamp);
811                 break;
812         case TEAM_UNKNOWN:
813                 status = timestamp_valid(Ai_unknown_rearm_timestamp);
814                 break;
815         default:
816                 Int3();
817                 break;
818         }
819
820         return status;
821 }
822
823 // functions to deal with letting the ai know about good times to fire powerful secondary
824 // weapons.
825
826 // this function is entry point from sexpression code to set internal data for use by ai code.
827 void ai_good_secondary_time( int team, int weapon_index, int max_fire_count, char *shipname )
828 {
829         int i, index;
830
831         // find an open slot to put this data
832         for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
833                 if ( Ai_huge_fire_info[i].weapon_index == -1 )
834                         break;
835         }
836
837         Assert( i < MAX_HUGE_SECONDARY_INFO );                  // we've run out of room
838
839         Ai_huge_fire_info[i].weapon_index = weapon_index;
840         Ai_huge_fire_info[i].team = team;
841         Ai_huge_fire_info[i].max_fire_count = max_fire_count;
842
843         Ai_huge_fire_info[i].shipname = ai_get_goal_ship_name( shipname, &index );
844 }
845
846 // function called internally to the ai code to tell whether or not weapon_num can be fired
847 // from firer_objp at target_objp.  This function will resolve the team for the firer.
848 // returns:
849 //              -1  -- when conditions don't allow firer to fire weapon_num on target_objp
850 //              >=0 -- when conditions allow firer to fire.  Return value is max number of weapon_nums
851 //           which can be fired on target_objp
852 int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
853 {
854         int i, firer_team, target_signature;
855         ship *firer_ship;
856         huge_fire_info *hfi = NULL;
857
858         Assert( firer_objp->type == OBJ_SHIP );
859         firer_ship = &Ships[firer_objp->instance];
860         firer_team = firer_ship->team;
861
862         // get target object's signature and try to find it in the list.
863         target_signature = target_objp->signature;
864         for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
865                 int ship_index, signature;
866
867                 hfi = &Ai_huge_fire_info[i];
868                 if ( hfi->weapon_index == -1 )
869                         continue;
870
871                 ship_index = ship_name_lookup( hfi->shipname );
872                 if ( ship_index == -1 )
873                         continue;
874
875                 signature = Objects[Ships[ship_index].objnum].signature;
876
877                 // sigatures, weapon_index, and team must match
878                 if ( (signature == target_signature) && (hfi->weapon_index == weapon_num) && (hfi->team == firer_team) )
879                         break;
880         }
881
882         // return -1 if not found
883         if ( i == MAX_HUGE_SECONDARY_INFO )
884                 return -1;
885
886         // otherwise, we can return the max number of weapons we can fire against target_objps
887
888         return hfi->max_fire_count;
889 }
890
891 // function to clear out secondary firing infomration between levels
892 void ai_init_secondary_info()
893 {
894         int i;
895
896         // clear out the data for dealing with when ai ships can fire huge secondary weapons
897         for (i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
898                 Ai_huge_fire_info[i].weapon_index = -1;
899                 Ai_huge_fire_info[i].team = -1;
900                 Ai_huge_fire_info[i].max_fire_count = -1;
901                 Ai_huge_fire_info[i].shipname = NULL;
902         }
903 }
904
905
906 //      Garbage collect the Path_points buffer.
907 //      Scans all objects, looking for used Path_points records.
908 //      Compresses Path_points buffer, updating aip->path_start and aip->path_cur indices.
909 //      Updates Ppfp to point to first free record.
910 //      This function is fairly fast.  Its worst-case running time is proportional to
911 //      3*MAX_PATH_POINTS + MAX_OBJECTS
912 //      Things to do to optimize this function:
913 //              1. if (t != 0) xlt++; can be replaced by xlt += t; assuming t can only be 0 or 1.
914 //              2. When pp_xlate is getting stuffed the first time, note highest index and use that 
915 //                      instead of MAX_PATH_POINTS in following two for loops.
916 void garbage_collect_path_points()
917 {
918         int     i;
919         int     pp_xlate[MAX_PATH_POINTS];
920         object  *A;
921         ship_obj        *so;
922
923         //      Scan all objects and create Path_points xlate table.
924         for (i=0; i<MAX_PATH_POINTS; i++)
925                 pp_xlate[i] = 0;
926
927         //      in pp_xlate, mark all used Path_point records
928         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
929                 A = &Objects[so->objnum];
930                 ship    *shipp = &Ships[A->instance];
931                 if (shipp->ai_index != -1) {
932                         ai_info *aip = &Ai_info[shipp->ai_index];
933
934                         if ((aip->path_length > 0) && (aip->path_start > -1)) {
935
936                                 for (int i=aip->path_start; i<aip->path_start + aip->path_length; i++) {
937                                         Assert(pp_xlate[i] == 0);       //      If this is not 0, then two paths use this point!
938                                         pp_xlate[i] = 1;
939                                 }
940                         }
941                 }
942         }
943
944         //      Now, stuff xlate index in pp_xlate.  This is the number to translate any path_start
945         //      or path_cur index to.
946         int     xlt = 0;
947         for (i=0; i<MAX_PATH_POINTS; i++) {
948                 int     t = pp_xlate[i];
949
950                 pp_xlate[i] = xlt;
951                 if (t != 0)
952                         xlt++;
953         }
954         
955         //      Update global Path_points free pointer.
956         Ppfp = &Path_points[xlt];
957
958         //      Now, using pp_xlate, fixup all aip->path_cur and aip->path_start indices
959         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
960                 A = &Objects[so->objnum];
961                 ship    *shipp = &Ships[A->instance];
962                 if (shipp->ai_index != -1) {
963                         ai_info *aip = &Ai_info[shipp->ai_index];
964
965                         if ((aip->path_length > 0) && (aip->path_start > -1)) {
966                                 Assert(aip->path_start < MAX_PATH_POINTS);
967                                 aip->path_start = pp_xlate[aip->path_start];
968
969                                 Assert((aip->path_cur >= 0) && (aip->path_cur < MAX_PATH_POINTS));
970                                 aip->path_cur = pp_xlate[aip->path_cur];
971                         }
972                 }
973         }
974
975         //      Now, compress the buffer.
976         for (i=0; i<MAX_PATH_POINTS; i++)
977                 if (i != pp_xlate[i])
978                         Path_points[pp_xlate[i]] = Path_points[i];
979
980 }
981
982 //      Hash two values together, return result.
983 //      Hash function: curval shifted right circular by one, newval xored in.
984 int hash(unsigned int curval, int newval)
985 {
986         int     addval = curval & 1;
987
988         curval >>= 1;
989         if (addval)
990                 curval |= 0x80000000;
991         curval ^= newval;
992
993         return curval;
994 }
995
996 //      Hash some information in an object together.
997 //      On 2/20/97, the information is position and orientation.
998 int create_object_hash(object *objp)
999 {
1000         int     *ip;
1001         unsigned int    hashval = 0;
1002         int     i;
1003
1004         ip = (int *) &objp->orient;
1005
1006         for (i=0; i<9; i++) {
1007                 hashval = hash(hashval, *ip);
1008                 ip++;
1009         }
1010
1011         ip = (int *) &objp->pos;
1012
1013         for (i=0; i<3; i++) {
1014                 hashval = hash(hashval, *ip);
1015                 ip++;
1016         }
1017
1018         return hashval;
1019 }
1020
1021 //      Stuff a list of NUM_SKILL_LEVELS floats at *plist.
1022 void parse_float_list(float *plist)
1023 {
1024         int     i;
1025
1026         for (i=0; i<NUM_SKILL_LEVELS; i++) {
1027                 stuff_float(&plist[i]);
1028         }
1029 }
1030
1031 void parse_ai_class()
1032 {
1033         ai_class        *aicp = &Ai_classes[Num_ai_classes];
1034
1035         required_string("$Name:");
1036         stuff_string(aicp->name, F_NAME, NULL);
1037
1038         Ai_class_names[Num_ai_classes] = aicp->name;
1039
1040         required_string("$accuracy:");
1041         parse_float_list(aicp->ai_accuracy);
1042
1043         required_string("$evasion:");
1044         parse_float_list(aicp->ai_evasion);
1045
1046         required_string("$courage:");
1047         parse_float_list(aicp->ai_courage);
1048
1049         required_string("$patience:");
1050         parse_float_list(aicp->ai_patience);
1051 }
1052
1053 void parse_aitbl()
1054 {
1055         // open localization
1056         lcl_ext_open();
1057
1058         read_file_text("ai.tbl");
1059
1060         reset_parse();
1061
1062         Num_ai_classes = 0;
1063
1064         required_string("#AI Classes");
1065
1066         while (required_string_either("#End", "$Name:")) {
1067                 Assert( Num_ai_classes < MAX_AI_CLASSES);
1068
1069                 parse_ai_class();
1070
1071                 Num_ai_classes++;
1072         }
1073
1074         // close localization
1075         lcl_ext_close();
1076 }
1077
1078 LOCAL int ai_inited = 0;
1079
1080 //========================= BOOK-KEEPING FUNCTIONS =======================
1081
1082 // Called once at game start-up
1083 void ai_init()
1084 {
1085         if ( !ai_inited )       {
1086                 // Do the first time initialization stuff here
1087                 int     rval;
1088
1089                 if ((rval = setjmp(parse_abort)) != 0) {
1090                         Error(LOCATION, "Error parsing 'ai.tbl'\r\nError code = %i.\r\n", rval);
1091                 } else {                        
1092                         parse_aitbl();                  
1093                 }
1094
1095                 ai_inited = 1;
1096         }
1097
1098         init_semirand();
1099         
1100         ai_level_init();
1101 }
1102
1103 // this inits the ai.  You should be able to call this between
1104 // levels to reset everything.
1105 void ai_level_init()
1106 {
1107         int i;
1108  
1109         // Do the stuff to reset all ai stuff here
1110         for (i=0; i<MAX_AI_INFO ; i++) {
1111                 Ai_info[i].shipnum = -1;
1112         }
1113         Ai_goal_signature = 0;
1114         Ai_friendly_rearm_timestamp = timestamp(-1);
1115         Ai_hostile_rearm_timestamp = timestamp(-1);
1116         Ai_neutral_rearm_timestamp = timestamp(-1);
1117         Ai_traitor_rearm_timestamp = timestamp(-1);
1118
1119         // clear out the stuff needed for AI firing powerful secondary weapons
1120         ai_init_secondary_info();
1121
1122         Ai_last_arrive_path=0;
1123 }
1124
1125 // BEGIN STEALTH
1126 // -----------------------------------------------------------------------------
1127 // Check if object is a stealth ship
1128 int is_object_stealth_ship(object* objp)
1129 {
1130         if (objp->type == OBJ_SHIP) {
1131                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_STEALTH) {
1132                         return 1;
1133                 }
1134         }
1135
1136         // not stealth ship
1137         return 0;
1138 }
1139
1140 // -----------------------------------------------------------------------------
1141 // Init necessary ai info for new stealth target
1142 void init_ai_stealth_info(ai_info *aip, object *stealth_objp)
1143 {
1144         Assert(is_object_stealth_ship(stealth_objp));
1145
1146         // set necessary ai info for new stealth target
1147         aip->stealth_last_pos = stealth_objp->pos;
1148         aip->stealth_velocity = stealth_objp->phys_info.vel;
1149         aip->stealth_last_visible_stamp = timestamp();
1150 }
1151
1152 // -----------------------------------------------------------------------------
1153 // Check whether Pl_objp can see a stealth ship object
1154 #define STEALTH_INVISIBLE                       0
1155 #define STEALTH_VISIBLE                         1
1156 #define STEALTH_FULLY_TARGETABLE        2
1157
1158 float get_skill_stealth_dist_scaler()
1159 {
1160         // return dist scaler based on skill level
1161         switch (Game_skill_level) {
1162         case 0: // very easy
1163                 return 0.65f;
1164
1165         case 1: // easy
1166                 return 0.9f;
1167
1168         case 2: // medium
1169                 return 1.0f;
1170
1171         case 3: // hard
1172                 return 1.1f;
1173
1174         case 4: // insane
1175                 return 1.3f;
1176
1177         default:
1178                 Int3();
1179         }
1180
1181         return 1.0f;
1182 }
1183
1184 float get_skill_stealth_dot_scaler()
1185 {
1186         // return multiplier on dot based on skill level
1187         switch (Game_skill_level) {
1188         case 0: // very easy
1189                 return 1.3f;
1190
1191         case 1: // easy
1192                 return 1.1f;
1193
1194         case 2: // medium
1195                 return 1.0f;
1196
1197         case 3: // hard
1198                 return 0.9f;
1199
1200         case 4: // insane
1201                 return 0.7f;
1202
1203         default:
1204                 Int3();
1205         }
1206
1207         return 1.0f;
1208 }
1209
1210 int ai_is_stealth_visible(object *viewer_objp, object *stealth_objp)
1211 {
1212         ship *shipp;
1213         vector vec_to_stealth;
1214         float dot_to_stealth, dist_to_stealth, max_stealth_dist;
1215
1216         Assert(stealth_objp->type == OBJ_SHIP);
1217         shipp = &Ships[stealth_objp->instance];
1218         Assert(viewer_objp->type == OBJ_SHIP);
1219
1220         // check if stealth ship
1221         Assert(Ship_info[shipp->ship_info_index].flags & SIF_STEALTH);
1222
1223         // check if in neb and below awac level for visible
1224         if ( !ship_is_visible_by_team(stealth_objp->instance, Ships[viewer_objp->instance].team) ) {
1225                 vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &viewer_objp->pos);
1226                 dist_to_stealth = vm_vec_mag_quick(&vec_to_stealth);
1227                 dot_to_stealth = vm_vec_dotprod(&viewer_objp->orient.v.fvec, &vec_to_stealth) / dist_to_stealth;
1228
1229                 // get max dist at which stealth is visible
1230                 max_stealth_dist = get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST;
1231
1232                 // now check if within view frustrum
1233                 float needed_dot_to_stealth;
1234                 if (dist_to_stealth < 100) {
1235                         needed_dot_to_stealth = 0.0f;
1236                 } else {
1237                         needed_dot_to_stealth = get_skill_stealth_dot_scaler() * float(STEALTH_VIEW_CONE_DOT) * (dist_to_stealth / max_stealth_dist);
1238                 }
1239                 if (dot_to_stealth > needed_dot_to_stealth) {
1240                         if (dist_to_stealth < max_stealth_dist) {
1241                                 return STEALTH_VISIBLE;
1242                         }
1243                 }
1244
1245                 // not within frustrum
1246                 return STEALTH_INVISIBLE;
1247         }
1248
1249         // visible by awacs level
1250         return STEALTH_FULLY_TARGETABLE;
1251 }
1252
1253 // END STEALTH
1254
1255 //      Compute dot product of direction vector and forward vector.
1256 //      Direction vector is vector from one object to other object.
1257 //      Forward vector is the forward vector of the ship.
1258 //      If from_dot == NULL, don't fill it in.
1259 float compute_dots(object *objp, object *other_objp, float *to_dot, float *from_dot)
1260 {
1261         vector  v2o;
1262         float           dist;
1263
1264         dist = vm_vec_normalized_dir(&v2o, &other_objp->pos, &objp->pos);
1265
1266         *to_dot = vm_vec_dot(&objp->orient.v.fvec, &v2o);
1267
1268         if (from_dot != NULL)
1269                 *from_dot = - vm_vec_dot(&other_objp->orient.v.fvec, &v2o);
1270
1271         return dist;
1272 }
1273
1274 // -----------------------------------------------------------------------------
1275 // update estimated stealth info
1276 // this is a "cheat" update
1277 // error increases with time not seen, true distance away, dot to enemey
1278 // this is done only if we can not see the stealth target
1279 // need to infer its position either by weapon fire pos or last know pos
1280 void update_ai_stealth_info_with_error(ai_info *aip/*, int no_error*/)
1281 {
1282         object *ship;
1283         object *stealth_objp;
1284         /*
1285         float error_time_mult, error_dist_mult, error_dot_mult, error_mult;
1286         float pos_error, vel_error;
1287         vector error_vec, vec_to_stealth;
1288         float dist_to_stealth, dot_to_stealth;
1289         float delta_time, delta_capped;
1290         */
1291
1292         // make sure I am targeting a stealth ship
1293         Assert( is_object_stealth_ship(&Objects[aip->target_objnum]) );
1294         stealth_objp = &Objects[aip->target_objnum];
1295
1296         // my_ship
1297         ship = &Objects[Ships[aip->shipnum].objnum];
1298
1299         // if update is due to weapon fire, get exact stealth position
1300 //      if (no_error) {
1301         aip->stealth_last_pos = stealth_objp->pos;
1302         aip->stealth_velocity = stealth_objp->phys_info.vel;
1303         aip->stealth_last_visible_stamp = timestamp();
1304 //              return;
1305 //      }
1306 /*
1307         // get time since last seen
1308         delta_time = 0.001f * (timestamp() - aip->stealth_last_visible_stamp);
1309
1310         // we don't want our "cheat" guess to more off than what we would get from extrapolating from last visible
1311         // only update if stealth info is "old"
1312         if ( (delta_time) < 0.5 ) {
1313                 return;
1314         }
1315
1316         // find vec_to_stealth and dist
1317         vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &ship->pos);
1318         dist_to_stealth = vm_vec_normalize_quick(&vec_to_stealth);
1319         dot_to_stealth = vm_vec_dotprod(&vec_to_stealth, &ship->orient.v.fvec);
1320
1321         // put cap on time
1322         delta_capped = delta_time;
1323         if (delta_time > 5.0) {
1324                 delta_capped = 5.0f;
1325         }
1326
1327         // erorr_time_mult (for 0-5) -> (1-6)
1328         error_time_mult = (1.0f + delta_capped);
1329
1330         // error_dot_mult (-1 to 1) -> (1-3)
1331         error_dot_mult = (2 - dot_to_stealth);
1332
1333         // error_dist_mult (0-1000+) -> (1-4)
1334         error_dist_mult = dist_to_stealth * 4.0f * 0.001f;
1335         if (error_dist_mult < 1) {
1336                 error_dist_mult = 1.0f;
1337         } else if (error_dist_mult > 4) {
1338                 error_dist_mult = 4.0f;
1339         }
1340
1341         // multiply error out
1342         error_mult = error_time_mult * error_dot_mult * error_dist_mult;
1343
1344         float base_pos_error = 10;
1345         float base_vel_error = 2;
1346
1347         // find the position and velocity error magnitude;
1348         pos_error = base_pos_error * error_mult;
1349         vel_error = base_vel_error * error_mult;
1350
1351         // get an error that changes slowly over time
1352         static_randvec( ((int)aip ^ (Missiontime >> 18)) & 7, &error_vec);
1353         vm_vec_zero(&error_vec);
1354
1355         // update pos and vel with error
1356         vm_vec_scale_add(&aip->stealth_velocity, &stealth_objp->phys_info.vel, &error_vec, vel_error);
1357
1358         // revise last "known" position to arrive at last pos with given error
1359         vm_vec_scale_add(&aip->stealth_last_pos, &stealth_objp->pos, &error_vec, pos_error);
1360         vm_vec_scale_add2(&aip->stealth_last_pos, &aip->stealth_velocity, -(0.001f * delta_time));
1361         */
1362 }
1363
1364 //      Update danger_weapon_objnum and signature in ai_info to say this weapon is to be avoided.
1365 void ai_update_danger_weapon(int attacked_objnum, int weapon_objnum)
1366 {
1367         object  *objp, *weapon_objp;
1368         ai_info *aip;
1369         float           old_dist, new_dist;
1370         float           old_dot, new_dot;
1371         object  *old_weapon_objp = NULL;
1372
1373         if ((attacked_objnum == -1) || (weapon_objnum == -1)) {
1374                 return;
1375         }
1376
1377         objp = &Objects[attacked_objnum];
1378
1379         // AL 2-24-98: If this isn't a ship, we don't need to worry about updating weapon_objnum (ie it would be
1380         //                                      an asteroid or bomb).
1381         if ( objp->type != OBJ_SHIP ) {
1382                 return;
1383         }
1384
1385         weapon_objp = &Objects[weapon_objnum];
1386
1387         aip = &Ai_info[Ships[objp->instance].ai_index];
1388
1389         // if my taraget is a stealth ship and is not visible
1390         if (aip->target_objnum >= 0) {
1391                 if ( is_object_stealth_ship(&Objects[aip->target_objnum]) ) {
1392                         if ( ai_is_stealth_visible(objp, &Objects[aip->target_objnum]) == STEALTH_INVISIBLE ) {
1393                                 // and the weapon is coming from that stealth ship
1394                                 if (weapon_objp->parent == aip->target_objnum) {
1395                                         // update my position estimate for stealth ship
1396                                         update_ai_stealth_info_with_error(aip/*, 1*/);
1397                                 }
1398                         }
1399                 }
1400         }
1401
1402         if (aip->danger_weapon_objnum != -1) {
1403                 old_weapon_objp = &Objects[aip->danger_weapon_objnum];
1404                 if ((old_weapon_objp->type == OBJ_WEAPON) && (old_weapon_objp->signature == aip->danger_weapon_signature)) {
1405                         ;
1406                 } else {
1407                         aip->danger_weapon_objnum = -1;
1408                 }
1409         }
1410
1411         new_dist = compute_dots(weapon_objp, objp, &new_dot, NULL);
1412
1413         if (aip->danger_weapon_objnum == -1) {
1414                 if (new_dist < 1500.0f) {
1415                         if (new_dot > 0.5f) {
1416                                 aip->danger_weapon_objnum = weapon_objnum;
1417                                 aip->danger_weapon_signature = weapon_objp->signature;
1418                         }
1419                 }
1420         } else {
1421                 Assert(old_weapon_objp != NULL);
1422                 old_dist = compute_dots(old_weapon_objp, objp, &old_dot, NULL);
1423         
1424                 if (old_dot < 0.5f) {
1425                         aip->danger_weapon_objnum = -1;
1426                         old_dist = 9999.9f;
1427                 }
1428
1429                 if ((new_dot > 0.5f) && (new_dot > old_dot-0.01f)) {
1430                         if (new_dist < old_dist) {
1431                                 aip->danger_weapon_objnum = weapon_objnum;
1432                                 aip->danger_weapon_signature = weapon_objp->signature;
1433                         }
1434                 }
1435         }
1436 }
1437
1438 //      If rvec != NULL, use it to match bank by calling vm_matrix_interpolate.
1439 //      (rvec defaults to NULL)
1440 void ai_turn_towards_vector(vector *dest, object *objp, 
1441                                                                          float frametime, float turn_time, vector *slide_vec, vector *rel_pos, float bank_override, int flags, vector *rvec)
1442 {
1443         //matrix        goal_orient;
1444         matrix  curr_orient;
1445         vector  vel_in, vel_out, desired_fvec, src;
1446         float           delta_time;
1447         physics_info    *pip;
1448         vector  vel_limit, acc_limit;
1449         float           delta_bank;
1450
1451         //      Don't allow a ship to turn if it has no engine strength.
1452         // AL 3-12-98: objp may not always be a ship!
1453         if ( objp->type == OBJ_SHIP ) {
1454                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f)
1455                         return;
1456         }
1457                         
1458         //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));
1459         pip = &objp->phys_info;
1460
1461         vel_in = pip->rotvel;
1462         curr_orient = objp->orient;
1463         delta_time = flFrametime;
1464
1465         Assert(turn_time > 0.0f);
1466         
1467         //      Scale turn_time based on skill level and team.
1468         if (!(flags & AITTV_FAST)){
1469                 if (objp->type == OBJ_SHIP){
1470                         if (Ships[objp->instance].team != Ships[Player_obj->instance].team){
1471                                 turn_time *= Turn_time_skill_level_scale[Game_skill_level];
1472                         }
1473                 }
1474         }
1475
1476         //      Set max turn rate.
1477         vel_limit.xyz.x = 2*PI/turn_time;
1478         vel_limit.xyz.y = 2*PI/turn_time;
1479         vel_limit.xyz.z = 2*PI/turn_time;
1480
1481         //      Set rate at which ship can accelerate to its rotational velocity.
1482         //      For now, weapons just go much faster.
1483         acc_limit = vel_limit;
1484         if (objp->type == OBJ_WEAPON)
1485                 vm_vec_scale(&acc_limit, 8.0f);
1486
1487         src = objp->pos;
1488
1489         if (rel_pos != NULL) {
1490                 vector  gun_point;
1491                 vm_vec_unrotate(&gun_point, rel_pos, &objp->orient);
1492                 vm_vec_add2(&src, &gun_point);
1493         }
1494
1495         vm_vec_normalized_dir(&desired_fvec, dest, &src);
1496
1497         //      Since ship isn't necessarily moving in the direction it's pointing, sometimes it's better
1498         //      to be moving towards goal rather than just pointing.  So, if slide_vec is !NULL, try to
1499         //      make ship move towards goal, not point at goal.
1500         if (slide_vec != NULL) {
1501                 vm_vec_add2(&desired_fvec, slide_vec);
1502                 vm_vec_normalize(&desired_fvec);
1503         }
1504
1505         //      Should be more general case here.  Currently, anything that is not a weapon will bank when it turns.
1506         if (objp->type == OBJ_WEAPON)
1507                 delta_bank = 0.0f;
1508         else if ((bank_override) && (Ships[objp->instance].team & opposing_team_mask(Player_ship->team))) {     //      Theoretically, this will only happen for Shivans.
1509                 delta_bank = bank_override;
1510                 //nprintf(("AI", "%i: %7.3f\n", Framecount, bank_override));
1511         } else {
1512                 delta_bank = vm_vec_dot(&curr_orient.v.rvec, &objp->last_orient.v.rvec);
1513                 delta_bank = 100.0f * (1.0f - delta_bank);
1514                 if (vm_vec_dot(&objp->last_orient.v.fvec, &objp->orient.v.rvec) < 0.0f)
1515                         delta_bank = -delta_bank;
1516
1517                 //nprintf(("AI", "%s: Frame %i: delta bank = %7.3f\n", Ships[objp->instance].ship_name, Framecount, delta_bank));
1518         }
1519
1520         //      Dave Andsager: The non-indented lines here are debug code to help you track down the problem in the physics
1521         //      that is causing ships to inexplicably rotate very far.  If you hit the Int3(), set the next statement to be
1522         //      the one marked "HERE".  (Do this clicking the cursor there, then right clicking.  Choose the right option.)
1523         //      This will allow you to rerun vm_forward_interpolate() with the values that caused the error.
1524         //      Note, you'll need to enable the Int3() about ten lines below.
1525 #ifndef NDEBUG
1526 vector tvec = objp->orient.v.fvec;
1527 vector  vel_in_copy;
1528 matrix  objp_orient_copy;
1529
1530 vel_in_copy = vel_in;
1531 objp_orient_copy = objp->orient;
1532
1533 vel_in = vel_in_copy;   //      HERE
1534 objp->orient = objp_orient_copy;
1535 #endif
1536         if (rvec != NULL) {
1537                 matrix  out_orient, goal_orient;
1538
1539                 vm_vector_2_matrix(&goal_orient, &desired_fvec, NULL, rvec);
1540                 vm_matrix_interpolate(&goal_orient, &curr_orient, &vel_in, delta_time, &out_orient, &vel_out, &vel_limit, &acc_limit);
1541                 objp->orient = out_orient;
1542         } else {
1543                 vm_forward_interpolate(&desired_fvec, &curr_orient, &vel_in, delta_time, delta_bank, &objp->orient, &vel_out, &vel_limit, &acc_limit);
1544         }
1545 #ifndef NDEBUG
1546 if (!((objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE))) {
1547         if (delta_time < 0.25f && vm_vec_dot(&objp->orient.v.fvec, &tvec) < 0.1f)
1548                 Int3(); //      Get Andsager.  A ship has turned too far in one frame.
1549 }
1550 #endif
1551
1552         pip->rotvel = vel_out;
1553 }
1554
1555 void init_ship_info()
1556 {
1557         int     i;
1558
1559         if (Ship_info_inited)
1560                 return;
1561
1562         for (i=0; i<MAX_SHIP_TYPES; i++) {
1563                 Ship_info[i].min_speed = - Ship_info[i].max_rear_vel;
1564                 Ship_info[i].max_accel = Ship_info[i].max_vel.xyz.z;
1565         }
1566
1567         Ship_info_inited = 1;
1568
1569 }
1570
1571 //      Set aip->target_objnum to objnum
1572 //      Update aip->previous_target_objnum.
1573 //      If new target (objnum) is different than old target, reset target_time.
1574 int set_target_objnum(ai_info *aip, int objnum)
1575 {
1576 /*
1577         char    old_name[32], new_name[32];
1578
1579         if (!timestamp_elapsed(aip->ok_to_target_timestamp))
1580                 return aip->target_objnum;
1581
1582         if (Player_ship && (Ships[aip->shipnum].team == Player_ship->team)) {
1583                 if (aip->target_objnum == -1)
1584                         strcpy(old_name, "none");
1585                 else
1586                         strcpy(old_name, Ships[Objects[aip->target_objnum].instance].ship_name);
1587
1588                 if (objnum == -1)
1589                         strcpy(new_name, "none");
1590                 else
1591                         strcpy(new_name, Ships[Objects[objnum].instance].ship_name);
1592
1593                 nprintf(("AI", "Ship %s changing target from %s to %s\n", Ships[aip->shipnum].ship_name, old_name, new_name));
1594         }
1595 */
1596
1597         // AL 2-25-97: Ensure that a protected ship isn't being set as a target (for non-players only)
1598         /*
1599         if ( objnum >= 0 ) {
1600                 if ( !(Objects[Ships[aip->shipnum].objnum].flags & OF_PLAYER_SHIP) ) {
1601                         if ( Objects[objnum].flags & OF_PROTECTED ) {
1602                                 // AL 2-26-97: removing Int3() until issue with setting OF_PROTECTED in ai_set_attack_subsystem()
1603                                 //Int3();                                                               // this should not happen
1604                                 return aip->target_objnum;              // don't change targets
1605                         }
1606                 }
1607         }
1608         */
1609
1610         if ((aip != Player_ai) && (!timestamp_elapsed(aip->ok_to_target_timestamp))) {
1611                 return aip->target_objnum;
1612         }
1613
1614         if (aip->target_objnum == objnum) {
1615                 aip->previous_target_objnum = aip->target_objnum;
1616         } else {
1617                 aip->previous_target_objnum = aip->target_objnum;
1618
1619                 // ignore this assert if a multiplayer observer
1620                 if((Game_mode & GM_MULTIPLAYER) && (aip == Player_ai) && (Player_obj->type == OBJ_OBSERVER)){
1621                 } else {
1622                         Assert(objnum != Ships[aip->shipnum].objnum);   //      make sure not targeting self
1623                 }
1624
1625                 // if stealth target, init ai_info for stealth
1626                 if ( (objnum > 0) && is_object_stealth_ship(&Objects[objnum]) ) {
1627                         init_ai_stealth_info(aip, &Objects[objnum]);
1628                 }
1629
1630                 aip->target_objnum = objnum;
1631                 aip->target_time = 0.0f;
1632                 aip->target_signature = Objects[objnum].signature;
1633                 // clear targeted subsystem
1634                 set_targeted_subsys(aip, NULL, -1);
1635         }
1636         
1637         return aip->target_objnum;
1638 }
1639
1640 int ai_select_primary_weapon(object *objp, object *other_objp, int flags);
1641
1642 //      Make new_subsys the targeted subsystem of ship *aip.
1643 ship_subsys *set_targeted_subsys(ai_info *aip, ship_subsys *new_subsys, int parent_objnum)
1644 {
1645         Assert(aip != NULL);
1646
1647         aip->last_subsys_target = aip->targeted_subsys;
1648         aip->targeted_subsys = new_subsys;
1649         aip->targeted_subsys_parent = parent_objnum;
1650
1651         if ( new_subsys ) {
1652                 // Make new_subsys target
1653                 if (new_subsys->system_info->type == SUBSYSTEM_ENGINE) {
1654                         if ( aip != Player_ai ) {
1655                                 ai_select_primary_weapon(&Objects[Ships[aip->shipnum].objnum], &Objects[parent_objnum], WIF_PUNCTURE);
1656                                 ship_primary_changed(&Ships[aip->shipnum]);     // AL: maybe send multiplayer information when AI ship changes primaries
1657                         }
1658                 }
1659
1660                 if ( aip == Player_ai ) {
1661                         hud_lock_reset(0.5f);
1662                 }
1663
1664         } else {
1665                 // Cleanup any subsys path information if it exists
1666                 ai_big_subsys_path_cleanup(aip);
1667         }
1668         
1669         return aip->targeted_subsys;
1670 }                                                                                         
1671
1672 // called to init the data for single ai object.  At this point,
1673 // the ship and the object and the ai_info are are correctly
1674 // linked together. Ai_info[ai_index].shipnum is the only valid field 
1675 // in ai_info.
1676 //      This is called right when the object is parsed, so you can't assume much
1677 //      has been initialized.  For example, wings, waypoints, goals are probably
1678 //      not yet loaded. --MK, 10/8/96
1679 void ai_object_init(object * obj, int ai_index)
1680 {
1681         ai_info *aip;
1682         Assert(ai_index >= 0 && ai_index < MAX_AI_INFO);
1683
1684         aip = &Ai_info[ai_index];
1685
1686         aip->type = 0;          //      0 means not in use.
1687         aip->wing = -1;         //      Member of what wing? -1 means none.
1688         aip->ai_class = Ship_info[Ships[obj->instance].ship_info_index].ai_class;
1689         aip->behavior = AIM_NONE;
1690 }
1691
1692 //      If *aip is docked, set max acceleration to A->mass/(A->mass + B->mass) where A is *aip and B is dock object
1693 void adjust_accel_for_docking(ai_info *aip)
1694 {
1695         if (aip->dock_objnum != -1) {
1696                 object  *obj2p = &Objects[aip->dock_objnum];
1697                 object  *obj1p;
1698
1699                 obj1p = &Objects[Ships[aip->shipnum].objnum];
1700
1701                 if (obj2p->signature == aip->dock_signature) {
1702                         float   ratio;
1703
1704                         ratio = obj1p->phys_info.mass / (obj1p->phys_info.mass + obj2p->phys_info.mass);
1705
1706                         // put cap on how much ship can slow down
1707                         if (ratio < 0.8) {
1708                                 ratio = 0.8f;
1709                         }
1710
1711                         if (AI_ci.forward > ratio) {
1712                                 AI_ci.forward = ratio;
1713                         }
1714                 }
1715         }
1716 }
1717
1718 // -------------------------------------------------------------------
1719 void accelerate_ship(ai_info *aip, float accel)
1720 {
1721         aip->prev_accel = accel;
1722         AI_ci.forward = accel;
1723         adjust_accel_for_docking(aip);
1724 }
1725
1726 //      --------------------------------------------------------------------------
1727 void change_acceleration(ai_info *aip, float delta_accel)
1728 {
1729         float   new_accel;
1730
1731         if (delta_accel < 0.0f) {
1732                 if (aip->prev_accel > 0.0f)
1733                         aip->prev_accel = 0.0f;
1734         } else if (aip->prev_accel < 0.0f)
1735                 aip->prev_accel = 0.0f;
1736
1737         new_accel = aip->prev_accel + delta_accel * flFrametime;
1738
1739         if (new_accel > 1.0f)
1740                 new_accel = 1.0f;
1741         else if (new_accel < -1.0f)
1742                 new_accel = -1.0f;
1743         
1744         aip->prev_accel = new_accel;
1745
1746         AI_ci.forward = new_accel;
1747         adjust_accel_for_docking(aip);
1748 }
1749
1750 void set_accel_for_target_speed(object *objp, float tspeed)
1751 {
1752         float   max_speed;
1753         ai_info *aip;
1754
1755         aip = &Ai_info[Ships[objp->instance].ai_index];
1756
1757         max_speed = Ships[objp->instance].current_max_speed;
1758
1759         AI_ci.forward = tspeed/max_speed;
1760         aip->prev_accel = AI_ci.forward;
1761
1762         adjust_accel_for_docking(aip);
1763 }
1764
1765 //      Stuff perim_point with a point on the perimeter of the sphere defined by object *objp
1766 //      on the vector from the center of *objp through the point *vp.
1767 void project_point_to_perimeter(vector *perim_point, vector *pos, float radius, vector *vp)
1768 {
1769         vector  v1;
1770         float           mag;
1771
1772         vm_vec_sub(&v1, vp, pos);
1773         mag = vm_vec_mag(&v1);
1774
1775         if (mag == 0.0f) {
1776                 Warning(LOCATION, "projectable point is at center of sphere.");
1777                 vm_vec_make(&v1, 0.0f, radius, 0.0f);
1778         } else {
1779                 vm_vec_normalize(&v1);
1780                 vm_vec_scale(&v1, 1.1f * radius + 10.0f);
1781         }
1782
1783         vm_vec_add2(&v1, pos);
1784         *perim_point = v1;
1785 }
1786
1787 //      Stuff tan1 with tangent point on sphere.  tan1 is point nearer to *p1
1788 //      *p0 is point through which tangents pass.
1789 //      *centerp is center of sphere.
1790 //      *p1 is another point in space to define the plane in which tan1, tan2 reside.
1791 //      radius is the radius of the sphere.
1792 //      Note, this is a very approximate function just for AI.
1793 //      Note also: On 12/26/96, p1 is used to define the plane perpendicular to that which
1794 //      contains the tangent point.
1795 void get_tangent_point(vector *tan1, vector *p0, vector *centerp, vector *p1, float radius)
1796 {
1797         vector  dest_vec, v2c, perp_vec, temp_vec, v2;
1798         float           dist, ratio;
1799
1800         //      Detect condition of point inside sphere.
1801         if (vm_vec_dist(p0, centerp) < radius)
1802                 project_point_to_perimeter(tan1, centerp, radius, p0);
1803         else {
1804                 vm_vec_normalized_dir(&v2c, centerp, p0);
1805
1806                 //      Compute perpendicular vector using p0, centerp, p1
1807                 vm_vec_normal(&temp_vec, p0, centerp, p1);
1808                 vm_vec_sub(&v2, centerp, p0);
1809                 vm_vec_cross(&perp_vec, &temp_vec, &v2);
1810
1811                 vm_vec_normalize(&perp_vec);
1812
1813                 dist = vm_vec_dist_quick(p0, centerp);
1814                 ratio = dist / radius;
1815
1816                 if (ratio < 2.0f)
1817                         vm_vec_scale_add(&dest_vec, &perp_vec, &v2c, ratio-1.0f);
1818                 else
1819                         vm_vec_scale_add(&dest_vec, &v2c, &perp_vec, (1.0f + 1.0f/ratio));
1820
1821                 vm_vec_scale_add(tan1, p0, &dest_vec, dist + radius);
1822         }
1823 }
1824
1825 //      --------------------------------------------------------------------------
1826 //      Given an object and a point, turn towards the point, resulting in
1827 // approach behavior.
1828 void turn_towards_point(object *objp, vector *point, vector *slide_vec, float bank_override)
1829 {
1830         ai_info *aip;
1831         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1832         
1833         // check if in formation and if not leader, don't change rotvel.xyz.z (bank to match leader elsewhere)
1834         if (aip->ai_flags & AIF_FORMATION) {
1835                 if (&Objects[aip->goal_objnum] != objp) {
1836                         float rotvel_z = objp->phys_info.rotvel.xyz.z;
1837                         ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1838                         objp->phys_info.rotvel.xyz.z = rotvel_z;
1839                 }
1840         } else {
1841                 // normal turn
1842                 ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1843         }
1844 }
1845
1846 //      --------------------------------------------------------------------------
1847 //      Given an object and a point, turn away from the point, resulting in avoidance behavior.
1848 //      Note: Turn away at full speed, not scaled down by skill level.
1849 void turn_away_from_point(object *objp, vector *point, float bank_override)
1850 {
1851         vector  opposite_point;
1852
1853         vm_vec_sub(&opposite_point, &objp->pos, point);
1854         vm_vec_add2(&opposite_point, &objp->pos);
1855
1856         ai_turn_towards_vector(&opposite_point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, NULL, NULL, bank_override, AITTV_FAST);
1857 }
1858
1859
1860 //      --------------------------------------------------------------------------
1861 //      Given an object and a point, turn tangent to the point, resulting in
1862 // a circling behavior.
1863 //      Make object *objp turn around the point *point with a radius of radius.
1864 //      Note that this isn't the same as following a circle of radius radius with
1865 //      center *point, but it should be adequate.
1866 //      Note that if you want to circle an object without hitting it, you should use
1867 //      about twice that object's radius for radius, else you'll certainly bump into it.
1868 //      Return dot product to goal point.
1869 float turn_towards_tangent(object *objp, vector *point, float radius)
1870 {
1871         vector  vec_to_point;
1872         vector  goal_point;
1873         vector  perp_point;                             //      point radius away from *point on vector to objp->pos
1874         vector  up_vec, perp_vec;
1875
1876         vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1877         vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.v.fvec);
1878         vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1879
1880         vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1881         if (vm_vec_dot(&objp->orient.v.fvec, &perp_vec) > 0.0f) {
1882                 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, radius);
1883         } else {
1884                 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, -radius);
1885         }
1886
1887 //      Ai_info[Ships[objp->instance].ai_index].goal_point = goal_point;
1888         turn_towards_point(objp, &goal_point, NULL, 0.0f);
1889
1890         vector  v2g;
1891
1892         vm_vec_normalized_dir(&v2g, &goal_point, &objp->pos);
1893         return vm_vec_dot(&objp->orient.v.fvec, &v2g);
1894 }
1895
1896 float turn_toward_tangent_with_axis(object *objp, object *center_objp, float radius)
1897 {
1898         vector r_vec, theta_vec;
1899         vector center_vec, vec_on_cylinder, sph_r_vec;
1900         float center_obj_z;
1901
1902         // find closest z of center objp
1903         vm_vec_sub(&sph_r_vec, &objp->pos, &center_objp->pos);
1904         center_obj_z = vm_vec_dotprod(&sph_r_vec, &center_objp->orient.v.fvec);
1905
1906         // find pt on axis with closest z
1907         vm_vec_scale_add(&center_vec, &center_objp->pos, &center_objp->orient.v.fvec, center_obj_z);
1908
1909         // get r_vec
1910         vm_vec_sub(&r_vec, &objp->pos, &center_vec);
1911 //      float r_mag = vm_vec_normalize_quick(&r_vec);
1912 //      mprintf(("cur_r: %.1f, desired_r: %.1f\n", r_mag, radius));
1913         Assert( (vm_vec_dotprod(&r_vec, &center_objp->orient.v.fvec) < 0.0001));
1914
1915         // get theta vec - perp to r_vec and z_vec
1916         vm_vec_crossprod(&theta_vec, &center_objp->orient.v.fvec, &r_vec);
1917
1918 #ifndef NDEBUG
1919         float mag = vm_vec_normalize(&theta_vec);
1920         Assert(mag > 0.9999 && mag < 1.0001);
1921 #endif
1922
1923         vector temp;
1924         vm_vec_crossprod(&temp, &r_vec, &theta_vec);
1925
1926 #ifndef NDEBUG
1927         float dot = vm_vec_dotprod(&temp, &center_objp->orient.v.fvec);
1928         Assert( dot >0.9999 && dot < 1.0001);
1929 #endif
1930
1931         // find pt on clylinder with closest z
1932         vm_vec_scale_add(&vec_on_cylinder, &center_vec, &r_vec, radius);
1933
1934         vector goal_pt, v2g;
1935         vm_vec_scale_add(&goal_pt, &vec_on_cylinder, &theta_vec, radius);
1936
1937 //      Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pt;
1938         turn_towards_point(objp, &goal_pt, NULL, 0.0f);
1939
1940         vm_vec_normalized_dir(&v2g, &goal_pt, &objp->pos);
1941         return vm_vec_dot(&objp->orient.v.fvec, &v2g);
1942 }
1943
1944 //      Returns a point radius units away from *point that *objp should turn towards to orbit *point
1945 void get_tangent_point(vector *goal_point, object *objp, vector *point, float radius)
1946 {
1947         vector  vec_to_point;
1948         vector  perp_point;                             //      point radius away from *point on vector to objp->pos
1949         vector  up_vec, perp_vec;
1950
1951         vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1952         vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.v.fvec);
1953         vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1954         vm_vec_normalize(&perp_vec);
1955
1956         vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1957
1958         if (vm_vec_dot(&objp->orient.v.fvec, &perp_vec) > 0.0f) {
1959                 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, radius);
1960         } else {
1961                 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, -radius);
1962         }
1963 }
1964
1965 int     Player_attacking_enabled = 1;
1966
1967 // -----------------------------------------------------------------------------
1968 // Determine whether an object is targetable within a nebula
1969 int object_is_targetable(object *target, ship *viewer)
1970 {
1971         int stealth_ship = 0;
1972
1973         // if target is ship, check if visible by team
1974         if (target->type == OBJ_SHIP) {
1975                 stealth_ship = (Ship_info[Ships[target->instance].ship_info_index].flags & SIF_STEALTH);
1976                 if ( ship_is_visible_by_team(target->instance, viewer->team) == 1) {
1977                         return 1;
1978                 }
1979         }
1980
1981         // for AI partially targetable works as fully targetable, except for stealth ship
1982         if (stealth_ship) {
1983                 // if not team targetable, check if within frustrum
1984                 if ( ai_is_stealth_visible(&Objects[viewer->objnum], target) == STEALTH_VISIBLE ) {
1985                         return 1;
1986                 } else {
1987                         return 0;
1988                 }
1989         }
1990
1991         // if not fully targetable by team, check awacs level with viewer
1992         // allow targeting even if only only partially targetable to player
1993         float radar_return = awacs_get_level(target, viewer);
1994         if ( radar_return > 0.4 ) {
1995                 return 1;
1996         } else {
1997                 return 0;
1998         }
1999 }
2000
2001 //      Return number of enemies attacking object objnum
2002 //
2003 // AL 10.26.97: Also include turrets on large ships when couting enemies attacking
2004 int num_enemies_attacking(int objnum)
2005 {
2006         object          *objp;
2007         ship                    *sp;
2008         ship_subsys     *ssp;
2009         ship_obj                *so;
2010         int                     count;
2011
2012         count = 0;
2013
2014         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2015                 objp = &Objects[so->objnum];
2016                 Assert(objp->instance != -1);
2017                 sp = &Ships[objp->instance];
2018
2019                 if (Ai_info[sp->ai_index].target_objnum == objnum)
2020                         count++;
2021
2022                 // consider turrets that may be attacking objnum (but only turrets on SIF_BIG_SHIP ships)
2023                 if ( Ship_info[sp->ship_info_index].flags & SIF_BIG_SHIP ) {
2024
2025                         // loop through all the subsystems, check if turret has objnum as a target
2026                         ssp = GET_FIRST(&sp->subsys_list);
2027                         while ( ssp != END_OF_LIST( &sp->subsys_list ) ) {
2028
2029                                 if ( ssp->system_info->type == SUBSYSTEM_TURRET ) {
2030                                         if ( (ssp->turret_enemy_objnum == objnum) && (ssp->current_hits > 0) ) {
2031                                                 count++;
2032                                         }
2033                                 }
2034                                 ssp = GET_NEXT( ssp );
2035                         } // end while
2036                 }
2037         }
2038
2039         return count;
2040 }
2041
2042 //      Get the team to fire on given an object.
2043 int get_enemy_team_mask(int objnum)
2044 {
2045         int     my_team, enemy_team_mask;
2046
2047         my_team = Ships[Objects[objnum].instance].team;
2048
2049         if (Mission_all_attack) {
2050                 //      All teams attack all teams.
2051                 switch (my_team) {
2052                 case TEAM_FRIENDLY:
2053                         enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2054                         break;
2055                 case TEAM_HOSTILE:
2056                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2057                         break;
2058                 case TEAM_NEUTRAL:
2059                         enemy_team_mask = TEAM_FRIENDLY | TEAM_HOSTILE | TEAM_TRAITOR;
2060                         break;
2061                 case TEAM_UNKNOWN:
2062                         enemy_team_mask = TEAM_HOSTILE;
2063                         break;
2064                 case TEAM_TRAITOR:
2065                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2066                         break;
2067                 default:
2068                         enemy_team_mask = TEAM_HOSTILE;
2069                         Int3();                 //      Illegal value for team!
2070                         break;
2071                 }
2072         } else {
2073                 switch (my_team) {
2074                 case TEAM_FRIENDLY:
2075                         enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2076                         break;
2077                 case TEAM_HOSTILE:
2078                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2079                         break;
2080                 case TEAM_NEUTRAL:
2081                         enemy_team_mask = TEAM_FRIENDLY | TEAM_TRAITOR;
2082                         break;
2083                 case TEAM_UNKNOWN:
2084                         enemy_team_mask = TEAM_HOSTILE;
2085                         break;
2086                 case TEAM_TRAITOR:
2087                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2088                         break;
2089                 default:
2090                         enemy_team_mask = TEAM_HOSTILE;
2091                         Int3();                 //      Illegal value for team!
2092                         break;
2093                 }
2094         }
2095
2096         return enemy_team_mask;
2097 }
2098
2099 //      Scan all the ships in *objp's wing.
2100 //      Return the lowest maximum speed of a ship in the wing.
2101 //      Current maximum speed (based on energy settings) is shipp->current_max_speed
2102 float get_wing_lowest_max_speed(object *objp)
2103 {
2104         ship            *shipp;
2105         ai_info *aip;
2106         float           lowest_max_speed;
2107         int             wingnum;
2108         object  *o;
2109         ship_obj        *so;
2110
2111         Assert(objp->type == OBJ_SHIP);
2112         Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
2113         shipp = &Ships[objp->instance];
2114         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
2115         aip = &Ai_info[shipp->ai_index];
2116
2117         wingnum = aip->wing;
2118
2119         lowest_max_speed = shipp->current_max_speed;
2120
2121         if ( wingnum == -1 )
2122                 return lowest_max_speed;
2123
2124         Assert(wingnum >= 0);
2125
2126         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2127                 o = &Objects[so->objnum];
2128                 ship    *oshipp = &Ships[o->instance];
2129                 ai_info *oaip = &Ai_info[oshipp->ai_index];
2130
2131                 if ((oaip->mode == AIM_WAYPOINTS) && (oaip->wing == wingnum)) {
2132                         //      Note: If a ship in the wing has a super low max speed, probably its engines are disabled.  So, fly along and
2133                         //      ignore the poor guy.
2134                         float   cur_max = oshipp->current_max_speed;
2135
2136                         if (oaip->ai_flags & AIF_DOCKED) {
2137                                 if (oaip->dock_objnum > -1)
2138                                         if (Objects[oaip->dock_objnum].type == OBJ_SHIP) 
2139                                                 cur_max *= o->phys_info.mass/(o->phys_info.mass + Objects[oaip->dock_objnum].phys_info.mass);
2140                         }
2141                                                         
2142                         if ((oshipp->current_max_speed > 5.0f) && (cur_max < lowest_max_speed)) {
2143                                 lowest_max_speed = cur_max;
2144                         }
2145                 }
2146         }
2147
2148         return lowest_max_speed;
2149 }
2150
2151 /*
2152 //      Tell everyone to ignore object objnum.
2153 void set_global_ignore_object(int objnum)
2154 {
2155         int     i;
2156
2157         Assert(Objects[objnum].type == OBJ_SHIP);
2158
2159         nprintf(("AI", "Telling everyone to ignore object %s\n", Ships[Objects[objnum].instance].ship_name));
2160
2161         for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2162                 if (Ignore_objects[i].objnum == -1) {
2163                         Ignore_objects[i].objnum = objnum;
2164                         Ignore_objects[i].signature = Objects[objnum].signature;
2165                         break;
2166                 }
2167         }
2168
2169         if (i == MAX_IGNORE_OBJECTS) {
2170                 //      Couldn't find a free slot, but maybe one of these objects has died.
2171                 for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2172                         int     o = Ignore_objects[i].objnum;
2173                         if (Objects[o].type != OBJ_SHIP)
2174                                 break;          //      Not a ship, so use this slot.
2175                         if (Objects[o].signature != Ignore_objects[i].signature)
2176                                 break;          //      Signatures don't match, so use this slot.
2177                 }
2178
2179                 if (i != MAX_IGNORE_OBJECTS) {
2180                         Ignore_objects[i].objnum = objnum;
2181                         Ignore_objects[i].signature = Objects[objnum].signature;
2182                 } else {
2183                         nprintf(("Warning", "Ignore_objects buffer full.  Stealing a slot to ignore object #%i\n"));
2184                         Int3();
2185
2186                         int     r;
2187
2188                         r = objnum % MAX_IGNORE_OBJECTS;
2189
2190                         Ignore_objects[r].objnum = objnum;
2191                         Ignore_objects[r].signature = Objects[objnum].signature;
2192                 }
2193         }
2194 }
2195
2196 */
2197
2198 //      Determine if object objnum is supposed to be ignored by object with ai_info *aip.
2199 //      Return:
2200 //              TRUE    if objnum is aip->ignore_objnum (and signatures match)
2201 //                              or objnum is in ignore wing
2202 //              FALSE   otherwise
2203 int is_ignore_object(ai_info *aip, int objnum)
2204 {
2205
2206 /*      //      First, scan all objects in global array of objects to be ignored.
2207         for (int i=0; i<MAX_IGNORE_OBJECTS; i++)
2208                 if (Ignore_objects[i].objnum != -1)
2209                         if (objnum == Ignore_objects[i].objnum)
2210                                 if (Objects[Ignore_objects[i].objnum].signature == Ignore_objects[i].signature)
2211                                         return 1;
2212 */
2213
2214         //      Didn't find in global list.  Now check 
2215         if (aip->ignore_objnum == UNUSED_OBJNUM)
2216                 return 0;                                                                       //      Not ignoring anything.
2217         else if (aip->ignore_objnum >= 0) {             //      This means it's ignoring an object, not a wing.
2218                 if (aip->ignore_objnum == objnum) {
2219                         if (Objects[aip->ignore_objnum].signature == aip->ignore_signature) {
2220                                 return 1;
2221                         } else {
2222                                 aip->ignore_objnum = UNUSED_OBJNUM;
2223                                 return 0;
2224                         }
2225                 } else {
2226                         return 0;
2227                 }
2228         } else {                                                                                        //      Ignoring a wing.
2229                 Int3(); // Should never happen.  I thought I removed this behavior! -- MK, 5/17/98
2230                 return 0;
2231 /*              int     ignore_wingnum = -(aip->ignore_objnum + 1);
2232
2233                 Assert(ignore_wingnum < MAX_WINGS);
2234                 Assert(aip->shipnum >= 0);
2235                 return (Ships[Objects[objnum].instance].wingnum == ignore_wingnum);
2236 */      }
2237 }
2238
2239 // -----------------------------------------------------------------------------
2240
2241 // given a ship with bounding box and a point, find the closest point on the bbox
2242 int get_nearest_bbox_point(object *ship_obj, vector *start, vector *box_pt)
2243 {
2244         vector temp, rf_start;
2245         polymodel *pm;
2246         pm = model_get(Ship_info[Ships[ship_obj->instance].ship_info_index].modelnum);
2247
2248         // get start in ship rf
2249         vm_vec_sub(&temp, start, &ship_obj->pos);
2250         vm_vec_rotate(&rf_start, &temp, &ship_obj->orient);
2251
2252         // find box_pt
2253         int inside = project_point_onto_bbox(&pm->mins, &pm->maxs, &rf_start, &temp);
2254
2255         // get box_pt in world rf
2256         vm_vec_unrotate(box_pt, &temp, &ship_obj->orient);
2257         vm_vec_add2(box_pt, &ship_obj->pos);
2258
2259         return inside;
2260 }
2261
2262
2263 typedef struct eval_nearest_objnum {
2264         int     objnum;
2265         object *trial_objp;
2266         int     enemy_team_mask;
2267         int     enemy_wing;
2268         float   range;
2269         int     max_attackers;
2270         int     nearest_objnum;
2271         float   nearest_dist;
2272         int     check_danger_weapon_objnum;
2273 } eval_nearest_objnum;
2274
2275
2276 void evaluate_object_as_nearest_objnum(eval_nearest_objnum *eno)
2277 {
2278         ai_info *aip;
2279         ship_subsys     *attacking_subsystem;
2280
2281         aip = &Ai_info[Ships[Objects[eno->objnum].instance].ai_index];
2282
2283         attacking_subsystem = aip->targeted_subsys;
2284
2285         if ((attacking_subsystem != NULL) || !(eno->trial_objp->flags & OF_PROTECTED)) {
2286                 if ( OBJ_INDEX(eno->trial_objp) != eno->objnum ) {
2287 #ifndef NDEBUG
2288                         if (!Player_attacking_enabled && (eno->trial_objp == Player_obj))
2289                                 return;
2290 #endif
2291                         //      If only supposed to attack ship in a specific wing, don't attack other ships.
2292                         if ((eno->enemy_wing != -1) && (Ships[eno->trial_objp->instance].wingnum != eno->enemy_wing))
2293                                 return;
2294
2295                         //      Don't keep firing at a ship that is in its death throes.
2296                         if (Ships[eno->trial_objp->instance].flags & SF_DYING)
2297                                 return;
2298
2299                         if (is_ignore_object(aip, ((eno->trial_objp)-Objects)))
2300                                 return;
2301
2302                         if (eno->trial_objp->flags & OF_PROTECTED)
2303                                 return;
2304
2305                         if (Ships[eno->trial_objp->instance].flags & SF_ARRIVING)
2306                                 return;
2307
2308                         ship_info *sip = &Ship_info[Ships[eno->trial_objp->instance].ship_info_index];
2309
2310                         if (sip->flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2311                                 return;
2312
2313                         if (Ships[eno->trial_objp->instance].team & eno->enemy_team_mask) {
2314                                 float   dist;
2315                                 int     num_attacking;
2316
2317                                 // Allow targeting of stealth in nebula by his firing at me
2318                                 // This is done for a specific ship, not generally.
2319                                 if ( !eno->check_danger_weapon_objnum ) {
2320                                         // check if can be targeted if inside nebula
2321                                         if ( !object_is_targetable(eno->trial_objp, &Ships[Objects[eno->objnum].instance]) ) {
2322                                                 // check if stealth ship is visible, but not "targetable"
2323                                                 if ( !((sip->flags & SIF_STEALTH) && ai_is_stealth_visible(&Objects[eno->objnum], eno->trial_objp)) ) {
2324                                                         return;
2325                                                 }
2326                                         }
2327                                 }
2328
2329                                 // if objnum is BIG or HUGE, find distance to bbox
2330                                 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
2331                                         vector box_pt;
2332                                         // check if inside bbox
2333                                         int inside = get_nearest_bbox_point(eno->trial_objp, &Objects[eno->objnum].pos, &box_pt);
2334                                         if (inside) {
2335                                                 dist = 10.0f;
2336                                                 // on the box
2337                                         } else {
2338                                                 dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &box_pt);
2339                                         }
2340                                 } else {
2341                                         dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &eno->trial_objp->pos);
2342                                 }
2343                                 
2344                                 //      Make it more likely that fighters (or bombers) will be picked as an enemy by scaling up distance for other types.
2345                                 if ((Ship_info[Ships[eno->trial_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))) {
2346                                         dist = dist * 0.5f;
2347                                 }
2348
2349                                 num_attacking = num_enemies_attacking(eno->trial_objp-Objects);
2350                                 if ((sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) || (num_attacking < eno->max_attackers)) {
2351                                         if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))){
2352                                                 dist *= (float) (num_attacking+2)/2.0f;                         //      prevents lots of ships from attacking same target
2353                                         }
2354
2355                                         if (eno->trial_objp->flags & OF_PLAYER_SHIP){
2356                                                 dist *= 1.0f + (NUM_SKILL_LEVELS - Game_skill_level - 1)/NUM_SKILL_LEVELS;      //      Favor attacking non-players based on skill level.
2357                                         }
2358
2359                                         if (dist < eno->nearest_dist) {
2360                                                 eno->nearest_dist = dist;
2361                                                 eno->nearest_objnum = eno->trial_objp-Objects;
2362                                         }
2363                                 }
2364                         }
2365                 }
2366         }
2367
2368 }
2369
2370
2371 //      Given an object and an enemy team, return the index of the nearest enemy object.
2372 //      Unless aip->targeted_subsys != NULL, don't allow to attack objects
2373 //      with OF_PROTECTED bit set.
2374 //      Ship must be within range "range".
2375 //      Don't attack a ship that already has at least max_attackers attacking it.
2376 int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers)
2377 {
2378         object  *danger_weapon_objp;
2379         ai_info *aip;
2380         ship_obj        *so;
2381
2382         // initialize eno struct
2383         eval_nearest_objnum eno;
2384         eno.enemy_team_mask = enemy_team_mask;
2385         eno.enemy_wing = enemy_wing;
2386         eno.max_attackers = max_attackers;
2387         eno.objnum = objnum;
2388         eno.range = range;
2389         eno.nearest_dist = range;
2390         eno.nearest_objnum = -1;
2391         eno.check_danger_weapon_objnum = 0;
2392
2393         // go through the list of all ships and evaluate as potential targets
2394         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2395                 eno.trial_objp = &Objects[so->objnum];
2396                 evaluate_object_as_nearest_objnum(&eno);
2397
2398         }
2399
2400         // check if danger_weapon_objnum has will show a stealth ship
2401         aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2402         if (aip->danger_weapon_objnum >= 0) {
2403                 danger_weapon_objp = &Objects[aip->danger_weapon_objnum];
2404                 // validate weapon
2405                 if (danger_weapon_objp->signature == aip->danger_weapon_signature) {
2406                         Assert(danger_weapon_objp->type == OBJ_WEAPON);
2407                         // check if parent is a ship
2408                         if (danger_weapon_objp->parent >= 0) {
2409                                 if ( is_object_stealth_ship(&Objects[danger_weapon_objp->parent]) ) {
2410                                         // check if stealthy
2411                                         if ( ai_is_stealth_visible(&Objects[objnum], &Objects[danger_weapon_objp->parent]) != STEALTH_FULLY_TARGETABLE ) {
2412                                                 // check if weapon is laser
2413                                                 if (Weapon_info[Weapons[danger_weapon_objp->instance].weapon_info_index].subtype == WP_LASER) {
2414                                                         // check stealth ship by its laser fire
2415                                                         eno.check_danger_weapon_objnum = 1;
2416                                                         eno.trial_objp = &Objects[danger_weapon_objp->parent];
2417                                                         evaluate_object_as_nearest_objnum(&eno);
2418                                                 }
2419                                         }
2420                                 }
2421                         }
2422                 }
2423         }
2424
2425         //      If only looking for target in certain wing and couldn't find anything in
2426         //      that wing, look for any object.
2427         if ((eno.nearest_objnum == -1) && (enemy_wing != -1)) {
2428                 return get_nearest_objnum(objnum, enemy_team_mask, -1, range, max_attackers);
2429         }
2430
2431         return eno.nearest_objnum;
2432 }
2433
2434 //      Given an object and an enemy team, return the index of the nearest enemy object.
2435 //      Unlike find_enemy or find_nearest_objnum, this doesn't care about things like the protected flag or number
2436 //      of enemies attacking.
2437 //      It is used to find the nearest enemy to determine things like whether to rearm.
2438 int find_nearby_hostile(int objnum, int enemy_team_mask, float range, int *count)
2439 {
2440         int             nearest_objnum;
2441         float           nearest_dist;
2442         object  *objp;
2443         ai_info *aip;
2444         ship_obj        *so;
2445
2446         nearest_objnum = -1;
2447         nearest_dist = range;
2448
2449         aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2450
2451         *count = 0;
2452
2453         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2454                 objp = &Objects[so->objnum];
2455
2456                 if ( OBJ_INDEX(objp) != objnum ) {
2457                         if (Ships[objp->instance].flags & SF_DYING)
2458                                 continue;
2459
2460                         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2461                                 continue;
2462
2463                         if (Ships[objp->instance].team & enemy_team_mask) {
2464                                 float   dist;
2465
2466                                 dist = vm_vec_dist_quick(&Objects[objnum].pos, &objp->pos) - objp->radius*0.75f;
2467                                 
2468                                 if (dist < range) {
2469                                         (*count)++;
2470
2471                                         if (dist < nearest_dist) {
2472                                                 nearest_dist = dist;
2473                                                 nearest_objnum = objp-Objects;
2474                                         }
2475                                 }
2476                         }
2477                 }
2478         }
2479
2480         return nearest_objnum;
2481 }
2482
2483 // return !0 if objp can be considered for a turret target, 0 otherwise
2484 // input:       objp                            =>      object that turret is considering as an enemy
2485 //                              turret_parent   =>      object index for ship that turret sits on
2486 int valid_turret_enemy(object *objp, object *turret_parent)
2487 {
2488         if ( objp == turret_parent ) {
2489                 return 0;
2490         }
2491
2492         if ( objp->type == OBJ_ASTEROID ) {
2493                 return 1;
2494         }
2495
2496         if ( (objp->type == OBJ_SHIP) ) {
2497                 ship *shipp;
2498                 shipp = &Ships[objp->instance];
2499
2500                 // don't fire at ships with protected bit set!!!
2501                 if ( objp->flags & OF_PROTECTED ) {
2502                         return 0;
2503                 }
2504
2505                 if ( !(Ship_info[shipp->ship_info_index].flags & SIF_DO_COLLISION_CHECK)) {
2506                         return 0;
2507                 }
2508
2509                 if (shipp->flags & SF_ARRIVING) {
2510                         return 0;
2511                 }
2512
2513                 return 1;
2514         }
2515
2516         if ( objp->type == OBJ_WEAPON ) {
2517                 if ( Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_BOMB ) {
2518                         if ( obj_team(turret_parent) != Weapons[objp->instance].team ) {
2519                                 return 1;
2520                         }
2521                 }
2522         }
2523
2524         return 0;
2525 }
2526
2527 // return 1 if objp is in fov of the specified turret, tp.  Otherwise return 0.
2528 //      dist = distance from turret to center point of object
2529 int object_in_turret_fov(object *objp, model_subsystem *tp, vector *tvec, vector *tpos, float dist)
2530 {
2531         vector  v2e;
2532         float           dot;
2533         vm_vec_normalized_dir(&v2e, &objp->pos, tpos);
2534         dot = vm_vec_dot(&v2e, tvec);
2535
2536         dot += objp->radius / (dist + objp->radius);
2537
2538         if ( dot >= tp->turret_fov ) {
2539                 return 1;
2540         }
2541
2542         return 0;
2543 }
2544
2545 // return 1 if bomb_objp is headed towards ship_objp
2546 int bomb_headed_towards_ship(object *bomb_objp, object *ship_objp)
2547 {
2548         float           dot;
2549         vector  bomb_to_ship_vector;
2550
2551         vm_vec_normalized_dir(&bomb_to_ship_vector, &ship_objp->pos, &bomb_objp->pos);
2552         dot = vm_vec_dot(&bomb_objp->orient.v.fvec, &bomb_to_ship_vector);
2553
2554         if ( dot > 0 ) {
2555                 return 1;
2556         }
2557
2558         return 0;
2559 }
2560
2561 // nubmer of live turrets with target_objnum 
2562 int num_turrets_attacking(object *turret_parent, int target_objnum) 
2563 {
2564         ship_subsys *ss;
2565         ship *shipp;
2566         int count = 0;
2567         shipp = &Ships[turret_parent->instance];
2568
2569         Assert(turret_parent->type == OBJ_SHIP);
2570         Assert(Objects[target_objnum].type == OBJ_SHIP);
2571
2572         for (ss=GET_FIRST(&shipp->subsys_list); ss!=END_OF_LIST(&shipp->subsys_list); ss=GET_NEXT(ss)) {
2573                 // check if subsys is alive
2574                 if (ss->current_hits <= 0.0f) {
2575                         continue;
2576                 }
2577
2578                 // check if it's a turret
2579                 if (ss->system_info->type != SUBSYSTEM_TURRET) {
2580                         continue;
2581                 }
2582
2583                 // if the turret is locked
2584                 if(ss->weapons.flags & SW_FLAG_TURRET_LOCK){
2585                         continue;
2586                 }               
2587
2588                 // check if turret is targeting target_objnum
2589                 if (ss->turret_enemy_objnum == target_objnum) {
2590                         count++;
2591                 }
2592         }
2593
2594         return count;
2595 }
2596
2597 float Lethality_range_const = 2.0f;
2598 DCF(lethality_range, "N for modifying range: 1 / (1+N) at 100")
2599 {
2600         dc_get_arg(ARG_FLOAT);
2601         Lethality_range_const = Dc_arg_float;
2602 }
2603
2604 float Player_lethality_bump[NUM_SKILL_LEVELS] = {
2605         // 0.0f, 5.0f, 10.0f, 25.0f, 40.0f
2606         0.0f, 0.0f, 0.0f, 0.0f, 0.0f
2607 };
2608
2609 // evaluate obj as posssible target for turret
2610 void evaluate_obj_as_target(object *objp, eval_enemy_obj_struct *eeo)
2611 {
2612         object  *turret_parent_obj = &Objects[eeo->turret_parent_objnum];
2613         ship            *shipp;
2614         model_subsystem *tp = eeo->turret_subsys->system_info;
2615         float dist;
2616
2617         // Don't look for bombs when weapon system is not ok
2618         if (objp->type == OBJ_WEAPON && !eeo->weapon_system_ok) {
2619                 return;
2620         }
2621
2622         if ( !valid_turret_enemy(objp, turret_parent_obj) ) {
2623                 return;
2624         }
2625
2626 #ifndef NDEBUG
2627         if (!Player_attacking_enabled && (objp == Player_obj)) {
2628                 return;
2629         }
2630 #endif
2631
2632         if ( objp->type == OBJ_SHIP ) {
2633                 shipp = &Ships[objp->instance];
2634
2635                 // check on enemy team
2636                 if ( !(shipp->team & eeo->enemy_team_mask) ) {
2637                         return;
2638                 }
2639
2640                 // check if protected
2641                 if (objp->flags & OF_PROTECTED) {
2642                         return;
2643                 }
2644
2645                 // check if beam protected
2646                 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2647                         if (objp->flags & OF_BEAM_PROTECTED) {
2648                                 return;
2649                         }
2650                 }
2651
2652                 if (eeo->big_only_flag) {
2653                         if (!(Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2654                                 return;
2655                         }
2656                 }
2657
2658                 // check if     turret flagged to only target tagged ships
2659                 if ( (eeo->turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && !ship_is_tagged(objp) ) {
2660                         return;
2661                 }
2662
2663                 // check if valid target in nebula
2664                 if ( !object_is_targetable(objp, &Ships[Objects[eeo->turret_parent_objnum].instance]) ) {
2665                         // BYPASS ocassionally for stealth
2666                         int try_anyway = FALSE;
2667                         if ( is_object_stealth_ship(objp) ) {
2668                                 float turret_stealth_find_chance = 0.5f;
2669                                 float speed_mod = -0.1f + vm_vec_mag_quick(&objp->phys_info.vel) / 70.0f;
2670                                 if (frand() > (turret_stealth_find_chance + speed_mod)) {
2671                                         try_anyway = TRUE;
2672                                 }
2673                         }
2674
2675                         if (!try_anyway) {
2676                                 return;
2677                         }
2678                 }
2679
2680         } else {
2681                 shipp = NULL;
2682         }
2683
2684         // modify dist for BIG|HUGE, getting closest point on bbox, if not inside
2685         dist = vm_vec_dist_quick(eeo->tpos, &objp->pos) - objp->radius;
2686         if (dist < 0.0f) {
2687                 dist = 0.0f;
2688         }
2689
2690         // check if object is a bomb attacking the turret parent
2691         // check if bomb is homing on the turret parent ship
2692         if (objp->type == OBJ_WEAPON) {
2693                 if ( Weapons[objp->instance].homing_object == &Objects[eeo->turret_parent_objnum] ) {
2694                         if ( dist < eeo->nearest_homing_bomb_dist ) {
2695                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2696                                         eeo->nearest_homing_bomb_dist = dist;
2697                                         eeo->nearest_homing_bomb_objnum = OBJ_INDEX(objp);
2698                                 }
2699                         }
2700                 // if not homing, check if bomb is flying towards ship
2701                 } else if ( bomb_headed_towards_ship(objp, &Objects[eeo->turret_parent_objnum]) ) {
2702                         if ( dist < eeo->nearest_bomb_dist ) {
2703                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2704                                         eeo->nearest_bomb_dist = dist;
2705                                         eeo->nearest_bomb_objnum = OBJ_INDEX(objp);
2706                                 }
2707                         }
2708                 }
2709         } // end weapon section
2710
2711         // maybe recalculate dist for big or huge ship
2712 //      if (shipp && (Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2713 //              fvi_ray_boundingbox(min, max, start, direction, hit);
2714 //              dist = vm_vec_dist_quick(hit, tvec);
2715 //      }
2716
2717         // check for nearest attcker
2718         if ( (shipp) && (dist < eeo->weapon_travel_dist) ) {
2719                 ai_info *aip = &Ai_info[shipp->ai_index];
2720
2721                 // modify distance based on number of turrets from my ship attacking enemy (add 10% per turret)
2722                 // dist *= (num_enemies_attacking(OBJ_INDEX(objp))+2)/2;        //      prevents lots of ships from attacking same target
2723                 int num_att_turrets = num_turrets_attacking(turret_parent_obj, OBJ_INDEX(objp));
2724                 dist *= (1.0f + 0.1f*num_att_turrets);
2725
2726                 // return if we're over the cap
2727                 int max_turrets = 3 + Game_skill_level * Game_skill_level;
2728                 if (num_att_turrets > max_turrets) {
2729                         return;
2730                 }
2731
2732                 // modify distance based on lethality of objp to my ship
2733                 float active_lethality = aip->lethality;
2734                 if (objp->flags & OF_PLAYER_SHIP) {
2735                         active_lethality += Player_lethality_bump[Game_skill_level];
2736                 }
2737
2738                 dist /= (1.0f + 0.01f*Lethality_range_const*active_lethality);
2739
2740                 // Make level 2 tagged ships more likely to be targeted
2741                 if (shipp->level2_tag_left > 0.0f) {
2742                         dist *= 0.3f;
2743                 }
2744
2745                 // check if objp is targeting the turret's ship, or if objp has just hit the turret's ship
2746                 if ( aip->target_objnum == eeo->turret_parent_objnum || aip->last_objsig_hit == Objects[eeo->turret_parent_objnum].signature ) {
2747                         // A turret will always target a ship that is attacking itself... self-preservation!
2748                         if ( aip->targeted_subsys == eeo->turret_subsys ) {
2749                                 dist *= 0.5f;   // highest priority
2750                         }
2751                 }
2752
2753                 // maybe update nearest attacker
2754                 if ( dist < eeo->nearest_attacker_dist ) {
2755                         if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2756                                 // 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));
2757                                 eeo->nearest_attacker_dist = dist;
2758                                 eeo->nearest_attacker_objnum = OBJ_INDEX(objp);
2759                         }
2760                 }
2761         } // end ship section
2762 }
2763
2764 // return 0 only if objnum is beam protected and turret is beam turret
2765 int is_target_beam_valid(ship_subsys *turret_subsys, int objnum)
2766 {
2767         // check if turret has beam weapon
2768         model_subsystem *tp = turret_subsys->system_info;
2769
2770         if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2771                 if (Objects[objnum].flags & OF_BEAM_PROTECTED) {
2772                         return 0;
2773                 }
2774
2775                 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_HUGE) {
2776                         if (Objects[objnum].type == OBJ_SHIP && !(Ship_info[Ships[Objects[objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
2777                                 return 0;
2778                         }
2779                 }
2780         }
2781
2782         return 1;
2783 }
2784
2785
2786 //      Given an object and an enemy team, return the index of the nearest enemy object.
2787 //
2788 // input:
2789 //                              turret_parent_objnum    => parent objnum for the turret
2790 //                              turret_subsys                   => pointer to system_info for the turret subsystem
2791 //                              enemy_team_mask         => OR'ed TEAM_ flags for the enemy of the turret parent ship
2792 //                              tpos                                            => position of turret (world coords)
2793 //                              tvec                                            => forward vector of turret (world coords)
2794 //                              current_enemy                   =>      objnum of current turret target
2795 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)
2796 {
2797         float                                   weapon_travel_dist;
2798         int                                     weapon_system_ok;
2799         object                          *objp;
2800         model_subsystem *tp;
2801         eval_enemy_obj_struct eeo;
2802
2803         // list of stuff to go thru
2804         ship_obj                *so;
2805         missile_obj *mo;
2806
2807         tp = turret_subsys->system_info;
2808         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);
2809
2810         // Set flag based on strength of weapons subsystem.  If weapons subsystem is destroyed, don't let turrets fire at bombs
2811         weapon_system_ok = 0;
2812         if ( ship_get_subsystem_strength( &Ships[Objects[turret_parent_objnum].instance], SUBSYSTEM_WEAPONS ) > 0 ) {
2813                 weapon_system_ok = 1;
2814         }
2815
2816         // Initialize eeo struct.
2817         eeo.turret_parent_objnum = turret_parent_objnum;
2818         eeo.weapon_system_ok = weapon_system_ok;
2819         eeo.weapon_travel_dist = weapon_travel_dist;
2820         eeo.big_only_flag = big_only_flag;
2821         eeo.enemy_team_mask = enemy_team_mask;
2822         eeo.current_enemy = current_enemy;
2823         eeo.tpos = tpos;
2824         eeo.tvec = tvec;
2825         eeo.turret_subsys = turret_subsys;
2826
2827         eeo.nearest_attacker_dist = 99999.0f;
2828         eeo.nearest_attacker_objnum = -1;
2829
2830         eeo.nearest_homing_bomb_dist = 99999.0f;
2831         eeo.nearest_homing_bomb_objnum = -1;
2832
2833         eeo.nearest_bomb_dist = 99999.0f;
2834         eeo.nearest_bomb_objnum = -1;
2835
2836         eeo.nearest_dist = 99999.0f;
2837         eeo.nearest_objnum = -1;
2838
2839
2840         // Missile_obj_list
2841         for( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
2842                 objp = &Objects[mo->objnum];
2843                 evaluate_obj_as_target(objp, &eeo);
2844         }
2845         // highest priority
2846         if ( eeo.nearest_homing_bomb_objnum != -1 ) {                                   // highest priority is an incoming homing bomb
2847                 return eeo.nearest_homing_bomb_objnum;
2848         } else if ( eeo.nearest_bomb_objnum != -1 ) {                                   // next highest priority is an incoming dumbfire bomb
2849                 return eeo.nearest_bomb_objnum;
2850         }
2851
2852
2853         // Ship_used_list
2854         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2855                 objp = &Objects[so->objnum];
2856                 evaluate_obj_as_target(objp, &eeo);
2857         }
2858
2859         Assert(eeo.nearest_attacker_objnum < 0 || is_target_beam_valid(turret_subsys, eeo.nearest_attacker_objnum));
2860                 // next highest priority is attacking ship
2861         if ( eeo.nearest_attacker_objnum != -1 ) {                      // next highest priority is an attacking ship
2862                 return eeo.nearest_attacker_objnum;
2863          }
2864
2865
2866 #ifndef FS2_DEMO
2867                 asteroid_obj *ao;
2868         // Asteroid_obj_list
2869         for( ao = GET_FIRST(&Asteroid_obj_list); ao != END_OF_LIST(&Asteroid_obj_list); ao = GET_NEXT(ao) ) {
2870                 objp = &Objects[ao->objnum];
2871                 evaluate_obj_as_target(objp, &eeo);
2872         }
2873 #endif
2874
2875         return eeo.nearest_objnum;                                                                              // lowest priority is the closest enemy objnum
2876 }
2877
2878 //      Return timestamp until a ship can find an enemy.
2879 //      Yes, no parameters.  Based solely on skill level.
2880 int get_enemy_timestamp()
2881 {
2882         return (NUM_SKILL_LEVELS - Game_skill_level) * ( (myrand() % 500) + 500);
2883 }
2884
2885 // -------------------------------------------------------------------
2886 //      Return objnum if enemy found, else return -1;
2887 //      Don't attack a ship that already has at least max_attackers attacking it.
2888 int find_enemy(int objnum, float range, int max_attackers)
2889 {
2890         int     enemy_team_mask;
2891
2892         enemy_team_mask = get_enemy_team_mask(objnum);
2893
2894         //      if target_objnum != -1, use that as goal.
2895         ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2896         if (timestamp_elapsed(aip->choose_enemy_timestamp)) {
2897                 aip->choose_enemy_timestamp = timestamp(get_enemy_timestamp());
2898                 if (aip->target_objnum != -1) {
2899                         int     target_objnum = aip->target_objnum;
2900
2901                         // DKA don't undo object as target in nebula missions.
2902                         // This could cause attack on ship on fringe on nebula to stop if attackee moves our of nebula range.  (BAD)
2903                         if ( (Objects[target_objnum].signature == aip->target_signature) ) {
2904                                 if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
2905                                         if (!(Objects[target_objnum].flags & OF_PROTECTED)) {
2906                                                 // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
2907                                                 return target_objnum;
2908                                         }
2909                                 }
2910                         } else {
2911                                 aip->target_objnum = -1;
2912                                 aip->target_signature = -1;
2913                         }
2914                 }
2915                 return get_nearest_objnum(objnum, enemy_team_mask, aip->enemy_wing, range, max_attackers);
2916         } else {
2917                 aip->target_objnum = -1;
2918                 aip->target_signature = -1;
2919                 return -1;
2920         }
2921
2922 }
2923
2924 int Use_parent_target = 0;
2925 DCF_BOOL(use_parent_target, Use_parent_target)
2926
2927 // -------------------------------------------------------------------
2928 //      Return objnum if enemy found, else return -1;
2929 //
2930 // input:
2931 //                              turret_subsys   => pointer to turret subsystem
2932 //                              objnum                  => parent objnum for the turret
2933 //                              tpos                            => position of turret (world coords)
2934 //                              tvec                            => forward vector of turret (world coords)
2935 //                              current_enemy   =>      objnum of current turret target
2936 int find_turret_enemy(ship_subsys *turret_subsys, int objnum, vector *tpos, vector *tvec, int current_enemy, float fov, int big_only_flag = 0)
2937 {
2938         int                                     enemy_team_mask, enemy_objnum;
2939         model_subsystem *tp;
2940         ship_info                       *sip;
2941
2942         tp = turret_subsys->system_info;
2943         enemy_team_mask = get_enemy_team_mask(objnum);
2944
2945         //      If a small ship and target_objnum != -1, use that as goal.
2946         ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2947         sip = &Ship_info[Ships[Objects[objnum].instance].ship_info_index];
2948
2949         if ((sip->flags & SIF_SMALL_SHIP) && (aip->target_objnum != -1)) {
2950                 int target_objnum = aip->target_objnum;
2951
2952                 if (Objects[target_objnum].signature == aip->target_signature) {
2953                         if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
2954                                 if ( !(Objects[target_objnum].flags & OF_PROTECTED) ) {         // check this flag as well.
2955                                         // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
2956                                         return target_objnum;
2957                                 }
2958                         }
2959                 } else {
2960                         aip->target_objnum = -1;
2961                         aip->target_signature = -1;
2962                 }
2963         // Not small or small with target objnum
2964         } else {
2965                 // maybe use aip->target_objnum as next target
2966                 if ((frand() < 0.8f) && (aip->target_objnum != -1) && Use_parent_target) {
2967
2968                         //check if aip->target_objnum is valid target
2969                         int target_flags = Objects[aip->target_objnum].flags;
2970                         if ( target_flags & OF_PROTECTED ) {
2971                                 // AL 2-27-98: why is a protected ship being targeted?
2972                                 set_target_objnum(aip, -1);
2973                                 return -1;
2974                         }
2975
2976                         // maybe use ship target_objnum if valid for turret
2977                         // check for beam weapon and beam protected
2978                         if ( !((target_flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) ) {
2979                                 if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
2980                                         // check for huge weapon and huge ship
2981                                         if ( !big_only_flag || (Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
2982                                                 // check for tagged only and tagged ship
2983                                                 if ( (turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && ship_is_tagged(&Objects[aip->target_objnum]) ) {
2984                                                         // select new target if aip->target_objnum is out of field of view
2985                                                         vector v2e;
2986                                                         float dot, dist;
2987                                                         dist = vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, tpos);
2988                                                         dot = vm_vec_dot(&v2e, tvec);
2989                                                         //      MODIFY FOR ATTACKING BIG SHIP
2990                                                         // dot += (0.5f * Objects[aip->target_objnum].radius / dist);
2991                                                         if (dot > fov) {
2992                                                                 return aip->target_objnum;
2993                                                         }
2994                                                 }
2995                                         }
2996                                 }
2997                         }
2998                 }
2999         }
3000
3001         enemy_objnum = get_nearest_turret_objnum(objnum, turret_subsys, enemy_team_mask, tpos, tvec, current_enemy, big_only_flag);
3002         if ( enemy_objnum >= 0 ) {
3003                 Assert( !((Objects[enemy_objnum].flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) );
3004                 if ( Objects[enemy_objnum].flags & OF_PROTECTED ) {
3005                         Int3();
3006                         enemy_objnum = aip->target_objnum;
3007                 }
3008         }
3009
3010         return enemy_objnum;
3011 }
3012
3013 //      If issued an order to a ship that's awaiting repair, abort that process.
3014 //      However, do not abort process for an object that is currently being repaired -- let it finish.
3015 void ai_set_goal_maybe_abort_dock(object *objp, ai_info *aip)
3016 {
3017         if (aip->ai_flags & AIF_AWAITING_REPAIR) {
3018                 object  *repair_obj;
3019
3020                 if (aip->dock_objnum == -1) {
3021                         repair_obj = NULL;
3022                 } else {
3023                         repair_obj = &Objects[aip->dock_objnum];
3024                 }
3025                 ai_do_objects_repairing_stuff( objp, repair_obj, REPAIR_INFO_ABORT );
3026         }
3027         aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);    //      Might request again after 30 seconds.
3028 }
3029
3030 void force_avoid_player_check(object *objp, ai_info *aip)
3031 {
3032         if (Ships[objp->instance].team == Player_ship->team){
3033                 aip->avoid_check_timestamp = timestamp(0);              //      Force a check for collision next frame.
3034         }
3035 }
3036
3037 //      --------------------------------------------------------------------------
3038 //      Set *attacked as object to attack for object *attacker
3039 //      If attacked == NULL, then attack any enemy object.
3040 //      Attack point *rel_pos on object.  This is for supporting attacking subsystems.
3041 void ai_attack_object(object *attacker, object *attacked, int priority, ship_subsys *ssp)
3042 {
3043         ai_info *aip;
3044
3045         Assert(attacker != NULL);
3046         Assert(attacker->instance != -1);
3047         Assert(Ships[attacker->instance].ai_index != -1);
3048
3049         aip = &Ai_info[Ships[attacker->instance].ai_index];
3050         force_avoid_player_check(attacker, aip);
3051
3052         aip->ok_to_target_timestamp = timestamp(0);             //      Guarantee we can target.
3053
3054 //      if (!strnicmp(Ships[attacker->instance].ship_name, NOX("Kami"), 4)) {
3055 //              aip->ai_flags |= AIF_KAMIKAZE;
3056 //              aip->ai_flags |= AIF_NO_DYNAMIC;
3057 //      }
3058
3059         if (attacker == attacked) {
3060                 Int3();         //      Bogus!  Who tried to get me to attack myself!  Trace out and fix!
3061                 return;
3062         }
3063
3064         //      Only set to chase if a fighter or bomber, otherwise just return.
3065         if (!(Ship_info[Ships[attacker->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
3066 //              nprintf(("AI","Note: AI ship %s refusing to set AI mode to AIM_CHASE\n", Ships[attacker->instance].ship_name));
3067 //              return;
3068                 nprintf(("AI", "AI ship %s is large ship ordered to attack %s\n", Ships[attacker->instance].ship_name, Ships[attacked->instance].ship_name));
3069         }
3070
3071         //      This is how "engage enemy" gets processed
3072         if (attacked == NULL) {
3073                 aip->choose_enemy_timestamp = timestamp(0);
3074                 // nebula safe
3075                 set_target_objnum(aip, find_enemy(attacker-Objects, 99999.9f, 4));
3076         } else {
3077                 // check if we can see atacked in nebula
3078                 if (aip->target_objnum != attacked - Objects) {
3079                         aip->aspect_locked_time = 0.0f;
3080                 }
3081                 set_target_objnum(aip, attacked - Objects);
3082         }
3083
3084         ai_set_goal_maybe_abort_dock(attacker, aip);
3085         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);     //      No dynamic targeting for 7 seconds.
3086
3087         if (is_ignore_object(aip, aip->target_objnum)) {
3088                 aip->ignore_objnum = UNUSED_OBJNUM;
3089         }
3090
3091         aip->mode = AIM_CHASE;
3092         aip->submode = SM_ATTACK;       // AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
3093                                                                                 //                                       for AIM_CHASE... it may have been not set correctly here
3094         if (ssp == NULL) {
3095                 set_targeted_subsys(aip, NULL, -1);
3096                 if (aip->target_objnum != -1) {
3097                         //nprintf(("AI", "Unprotecting ship %s\n", Ships[Objects[aip->target_objnum].instance].ship_name));
3098                         Objects[aip->target_objnum].flags &= ~OF_PROTECTED;     //      If ship had been protected, unprotect it.
3099                 }
3100         } else {
3101                 Int3(); //      Not supported yet!
3102         }
3103 }
3104
3105 //      --------------------------------------------------------------------------
3106 //      Set *attacked as object to attack for object *attacker
3107 //      Attack point *rel_pos on object.  This is for supporting attacking subsystems.
3108 void ai_attack_wing(object *attacker, int wingnum, int priority)
3109 {
3110         ai_info *aip;
3111
3112         Assert(attacker != NULL);
3113         Assert(attacker->instance != -1);
3114         Assert(Ships[attacker->instance].ai_index != -1);
3115
3116         aip = &Ai_info[Ships[attacker->instance].ai_index];
3117
3118         aip->enemy_wing = wingnum;
3119         aip->mode = AIM_CHASE;
3120         aip->submode = SM_ATTACK;       // AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
3121                                                                                 //                                       for AIM_CHASE... it may have been not set correctly here
3122
3123         aip->ok_to_target_timestamp = timestamp(0);             //      Guarantee we can target.
3124
3125         int count = Wings[wingnum].current_count;
3126         if (count > 0) {
3127                 int     index;
3128
3129                 index = (int) (frand() * count);
3130
3131                 if (index >= count)
3132                         index = 0;
3133
3134                 set_target_objnum(aip, Ships[Wings[wingnum].ship_index[index]].objnum);
3135
3136                 ai_set_goal_maybe_abort_dock(attacker, aip);
3137                 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);     //      No dynamic targeting for 7 seconds.
3138         }
3139 }
3140
3141 //      --------------------------------------------------------------------------
3142 //      Set *evaded as object for *evader to evade.
3143 void ai_evade_object(object *evader, object *evaded, int priority)
3144 {
3145         ai_info *aip;
3146
3147         Assert(evader != NULL);
3148         Assert(evaded != NULL);
3149         Assert(evader->instance != -1);
3150         Assert(Ships[evader->instance].ai_index != -1);
3151
3152         if (evaded == evader) {
3153                 Int3(); //      Bogus!  Who tried to get me to evade myself!  Trace out and fix!
3154                 return;
3155         }
3156
3157         aip = &Ai_info[Ships[evader->instance].ai_index];
3158
3159         set_target_objnum(aip, evaded - Objects);
3160         aip->mode = AIM_EVADE;
3161
3162 }
3163
3164 //      Ignore some object without changing mode.
3165 void ai_ignore_object(object *ignorer, object *ignored, int priority)
3166 {
3167         ai_info *aip;
3168
3169         Assert(ignorer != NULL);
3170         Assert(ignored != NULL);
3171         Assert(ignorer->instance != -1);
3172         Assert(Ships[ignorer->instance].ai_index != -1);
3173         Assert(ignorer != ignored);
3174
3175         aip = &Ai_info[Ships[ignorer->instance].ai_index];
3176
3177         //      MK, 5/17/98, removing ignoring of wings.
3178         //      It's too confusing.  It often causes mysterious behavior in which fighters unexpectedly refuse to attack anything.
3179 /*      if (Ships[ignored->instance].wingnum > -1) {
3180                 int wingnum, i;
3181
3182                 wingnum = Ships[ignored->instance].wingnum;
3183                 aip->ignore_objnum = -(wingnum+1);
3184                 // set protected bit for each ship in a wing
3185                 //      MK, 4/23/98: Only set for fighters if they are the original "ignored" object
3186                 for (i = 0; i < Wings[wingnum].current_count; i++ ) {
3187                         object  *objp;
3188
3189                         objp = &Objects[Ships[Wings[wingnum].ship_index[i]].objnum];
3190                         if (objp != ignored) {
3191                                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))
3192                                         continue;
3193                         }
3194
3195                         Objects[Ships[Wings[wingnum].ship_index[i]].objnum].flags |= OF_PROTECTED;
3196                 }
3197
3198         } else {
3199         */ {
3200                 aip->ignore_objnum = ignored - Objects;
3201                 aip->ignore_signature = ignored->signature;
3202                 aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3203                 ignored->flags |= OF_PROTECTED;                                 // set protected bit of ignored ship.
3204         }
3205
3206 }
3207
3208 //      Ignore some object without changing mode.
3209 void ai_ignore_wing(object *ignorer, int wingnum, int priority)
3210 {
3211         ai_info *aip;
3212
3213         Assert(ignorer != NULL);
3214         Assert(ignorer->instance != -1);
3215         Assert(Ships[ignorer->instance].ai_index != -1);
3216         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
3217
3218         aip = &Ai_info[Ships[ignorer->instance].ai_index];
3219
3220         aip->ignore_objnum = -(wingnum +1);
3221         aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3222 }
3223
3224
3225 //      Add a path point in the global buffer Path_points.
3226 //      modify_index = index in Path_points at which to store path point.
3227 //      If modify_index == -1, then create a new point.
3228 //      If a new point is created (ie, modify_index == -1), then Ppfp is updated.
3229 void add_path_point(vector *pos, int path_num, int path_index, int modify_index)
3230 {
3231         pnode   *pnp;
3232
3233         if (modify_index == -1) {
3234                 Assert(Ppfp-Path_points < MAX_PATH_POINTS-1);
3235                 pnp = Ppfp;
3236                 Ppfp++;
3237         } else {
3238                 Assert((modify_index >= 0) && (modify_index < MAX_PATH_POINTS-1));
3239                 pnp = &Path_points[modify_index];
3240         }
3241
3242         pnp->pos = *pos;
3243         pnp->path_num = path_num;
3244         pnp->path_index = path_index;
3245 }
3246
3247 //      Given two points on a sphere, the center of the sphere and the radius, return a
3248 //      point on the vector through the midpoint of the chord on the sphere.
3249 void bisect_chord(vector *p0, vector *p1, vector *centerp, float radius)
3250 {
3251         vector  tvec;
3252         vector  new_pnt;
3253
3254         vm_vec_add(&tvec, p0, p1);
3255         vm_vec_sub2(&tvec, centerp);
3256         vm_vec_sub2(&tvec, centerp);
3257         if (vm_vec_mag_quick(&tvec) < 0.1f) {
3258                 vm_vec_sub(&tvec, p0, p1);
3259                 if (fl_abs(tvec.xyz.x) <= fl_abs(tvec.xyz.z)){
3260                         tvec.xyz.x = -tvec.xyz.z;
3261                 } else {
3262                         tvec.xyz.y = -tvec.xyz.x;
3263                 }
3264         }
3265
3266         vm_vec_normalize(&tvec);
3267         vm_vec_scale(&tvec, radius);
3268         vm_vec_add(&new_pnt, centerp, &tvec);
3269
3270         add_path_point(&new_pnt, -1, -1, -1);
3271 }
3272                         
3273 //      Create a path from the current position to a goal position.
3274 //      The current position is in the current object and the goal position is
3275 //      in the goal object.
3276 //      It is ok to intersect the current object, but not the goal object.
3277 //      This function is useful for creating a path to an initial point near a large
3278 //      object.
3279 //
3280 // input:       subsys_path:    optional param (default 0), indicates this is a path to a subsystem
3281 void create_path_to_point(vector *curpos, vector *goalpos, object *curobjp, object *goalobjp, int subsys_path)
3282 {
3283         //      If can't cast vector to goalpos, then create an intermediate point.
3284         if (pp_collide(curpos, goalpos, goalobjp, curobjp->radius)) {
3285                 vector  tan1;
3286                 float           radius;
3287
3288                 // If this is a path to a subsystem, use SUBSYS_PATH_DIST as the radius for the object you are
3289                 // trying to avoid.  This is needed since subsystem paths extend out to SUBSYS_PATH_DIST, and we
3290                 // want ships to reach their path destination without flying to points that sit on the radius of
3291                 // a small ship
3292                 radius = goalobjp->radius;
3293                 if (subsys_path) {
3294                         if ( SUBSYS_PATH_DIST > goalobjp->radius ) {
3295                                 radius = SUBSYS_PATH_DIST;
3296                         }
3297                 }
3298
3299                 //      The intermediate point is at the intersection of:
3300                 //              tangent to *goalobjp sphere at point *goalpos
3301                 //              tangent to *goalobjp sphere through point *curpos in plane defined by *curpos, *goalpos, goalobjp->pos
3302                 //      Note, there are two tangents through *curpos, unless *curpos is on the
3303                 //      sphere.  The tangent that causes the nearer intersection (to *goalpos) is chosen.
3304                 get_tangent_point(&tan1, curpos, &goalobjp->pos, goalpos, radius);
3305
3306                 //      If we can't reach tan1 from curpos, insert a new point.
3307                 if (pp_collide(&tan1, curpos, goalobjp, curobjp->radius))
3308                         bisect_chord(curpos, &tan1, &goalobjp->pos, radius);
3309
3310                 add_path_point(&tan1, -1, -1, -1);
3311
3312                 //      If we can't reach goalpos from tan1, insert a new point.
3313                 if (pp_collide(goalpos, &tan1, goalobjp, curobjp->radius))
3314                         bisect_chord(goalpos, &tan1, &goalobjp->pos, radius);
3315         }
3316
3317 }
3318
3319 //      Given an object and a model path, globalize the points on the model
3320 //      and copy into the global path list.
3321 //      If pnp != NULL, then modify, in place, the path points.  This is used to create new
3322 //      globalized points when the base object has moved.
3323 // input:       randomize_pnt   => optional parameter (default value -1), add random vector in sphere to this path point
3324 void copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt)
3325 {
3326         matrix  m;
3327         int             i;
3328         vector  v1;
3329         int             pp_index;               //      index in Path_points at which to store point, if this is a modify-in-place (pnp ! NULL)
3330         int             start_index, finish_index;
3331         
3332         // nprintf(("AI", "Creating path for object %s in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
3333         
3334         //      Initialize pp_index.
3335         //      If pnp == NULL, that means we're creating new points.  If not NULL, then modify in place.
3336         if (pnp == NULL)
3337                 pp_index = -1;                  //      This tells add_path_point to create a new point.
3338         else
3339                 pp_index = 0;                   //      pp_index will get assigned to index in Path_points to reuse.
3340
3341         vm_copy_transpose_matrix(&m, &objp->orient);
3342
3343         if (dir == 1) {
3344                 start_index = 0;
3345                 finish_index = min(count, mp->nverts);
3346         } else {
3347                 Assert(dir == -1);      //      direction must be up by 1 or down by 1 and it's neither!
3348                 start_index = mp->nverts-1;
3349                 finish_index = max(-1, mp->nverts-1-count);
3350         }
3351
3352         int offset = 0;
3353         for (i=start_index; i != finish_index; i += dir) {
3354                 //      Globalize the point.
3355                 vm_vec_rotate(&v1, &mp->verts[i].pos, &m);
3356                 vm_vec_add2(&v1, &objp->pos);
3357
3358                 if ( randomize_pnt == i ) {
3359                         vector v_rand;
3360                         static_randvec(OBJ_INDEX(objp), &v_rand);
3361                         vm_vec_scale(&v_rand, 30.0f);
3362                         vm_vec_add2(&v1, &v_rand);
3363                 }
3364
3365                 if (pp_index != -1)
3366                         pp_index = pnp-Path_points + offset;
3367
3368                 add_path_point(&v1, path_num, i, pp_index);
3369                 offset++;
3370         }
3371 }
3372
3373
3374 //      For pl_objp, create a path along path path_num into mobjp.
3375 //      The tricky part of this problem is creating the entry to the first point on the
3376 //      predefined path.  The points on this entry path are based on the location of Pl_objp
3377 //      relative to the start of the path.
3378 //
3379 // input:
3380 //                              subsys_path:    optional param (default 0), indicating this is a path to a subsystem
3381 void create_model_path(object *pl_objp, object *mobjp, int path_num, int subsys_path)
3382 {       
3383         ship                    *shipp = &Ships[pl_objp->instance];
3384         ai_info         *aip = &Ai_info[shipp->ai_index];
3385
3386         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3387         polymodel       *pm = model_get(osip->modelnum);
3388         int                     num_points;
3389         model_path      *mp;
3390         pnode                   *ppfp_start = Ppfp;
3391         matrix          m;
3392         vector          gp0;
3393
3394         Assert(path_num >= 0);
3395
3396         //      Do garbage collection if necessary.
3397         if (Ppfp-Path_points + 64 > MAX_PATH_POINTS) {
3398                 garbage_collect_path_points();
3399                 ppfp_start = Ppfp;
3400         }
3401
3402         aip->path_start = Ppfp - Path_points;
3403         Assert(path_num < pm->n_paths);
3404         
3405         mp = &pm->paths[path_num];
3406         num_points = mp->nverts;
3407
3408         Assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
3409
3410         vm_copy_transpose_matrix(&m, &mobjp->orient);
3411         vm_vec_rotate(&gp0, &mp->verts[0].pos, &m);
3412         vm_vec_add2(&gp0, &mobjp->pos);
3413
3414         if (pp_collide(&pl_objp->pos, &gp0, mobjp, pl_objp->radius)) {
3415                 vector  perim_point1;
3416                 vector  perim_point2;
3417
3418                 perim_point2 = pl_objp->pos;
3419                 
3420                 //      If object that wants to dock is inside bounding sphere of object it wants to dock with, make it fly out.
3421                 //      Assume it can fly "straight" out to the bounding sphere.
3422                 if (vm_vec_dist_quick(&pl_objp->pos, &mobjp->pos) < mobjp->radius) {
3423                         project_point_to_perimeter(&perim_point2, &mobjp->pos, mobjp->radius, &pl_objp->pos);
3424                         add_path_point(&perim_point2, path_num, -1, -1);
3425                 }
3426
3427                 //      If last point on pre-defined path is inside bounding sphere, create a new point on the surface of the sphere.
3428                 if (vm_vec_dist_quick(&mobjp->pos, &gp0) < mobjp->radius) {
3429                         project_point_to_perimeter(&perim_point1, &mobjp->pos, mobjp->radius, &gp0);
3430                         create_path_to_point(&perim_point2, &perim_point1, pl_objp, mobjp, subsys_path);
3431                         add_path_point(&perim_point1, path_num, -1, -1);
3432                 } else {                //      The predefined path extends outside the sphere.  Create path to that point.
3433                         create_path_to_point(&perim_point2, &gp0, pl_objp, mobjp, subsys_path);
3434                 }
3435         }
3436
3437         // AL 12-31-97: If following a subsystem path, add random vector to second last path point
3438         if ( subsys_path ) {
3439                 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL, mp->nverts-2);
3440         } else {
3441                 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL);
3442         }
3443
3444         aip->path_cur = aip->path_start;
3445         aip->path_dir = PD_FORWARD;
3446         aip->path_objnum = mobjp-Objects;
3447         aip->mp_index = path_num;
3448         aip->path_length = Ppfp - ppfp_start;
3449         aip->path_next_check_time = timestamp(1);
3450
3451         aip->path_goal_obj_hash = create_object_hash(&Objects[aip->path_objnum]);
3452
3453         aip->path_next_create_time = timestamp(1000);   //      OK to try to create one second later
3454         aip->path_create_pos = pl_objp->pos;
3455         aip->path_create_orient = pl_objp->orient;
3456
3457         aip->ai_flags &= ~AIF_USE_EXIT_PATH;                    // ensure this flag is cleared
3458 }
3459
3460 //      For pl_objp, create a path along path path_num into mobjp.
3461 //      The tricky part of this problem is creating the entry to the first point on the
3462 //      predefined path.  The points on this entry path are based on the location of pl_objp
3463 //      relative to the start of the path.
3464 void create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count)
3465 {       
3466         ship                    *shipp = &Ships[pl_objp->instance];
3467         ai_info         *aip = &Ai_info[shipp->ai_index];
3468
3469         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3470         polymodel       *pm = model_get(osip->modelnum);
3471         int                     num_points;
3472         model_path      *mp;
3473         pnode                   *ppfp_start = Ppfp;
3474
3475         aip->path_start = Ppfp - Path_points;
3476         Assert(path_num < pm->n_paths);
3477         
3478         mp = &pm->paths[path_num];
3479         num_points = mp->nverts;
3480
3481         Assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
3482
3483         copy_xlate_model_path_points(mobjp, mp, -1, count, path_num, NULL);
3484
3485         aip->path_cur = aip->path_start;
3486         aip->path_dir = PD_FORWARD;
3487         aip->path_objnum = mobjp-Objects;
3488         aip->mp_index = path_num;
3489         aip->path_length = Ppfp - ppfp_start;
3490         aip->path_next_check_time = timestamp(1);
3491
3492         aip->ai_flags |= AIF_USE_EXIT_PATH;             // mark as exit path, referenced in maybe
3493 }
3494
3495 //      Return true if the vector from curpos to goalpos intersects with any ship other than the ignore objects.
3496 //      Calls pp_collide
3497 int pp_collide_any(vector *curpos, vector *goalpos, float radius, object *ignore_objp1, object *ignore_objp2, int big_only_flag)
3498 {
3499         ship_obj        *so;    
3500
3501         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
3502                 object *objp = &Objects[so->objnum];
3503
3504                 if (big_only_flag) {
3505                         if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
3506                                 continue;
3507                 }
3508
3509                 if ((objp != ignore_objp1) && (objp != ignore_objp2)) {
3510                         if (pp_collide(curpos, goalpos, objp, radius))
3511                                 return OBJ_INDEX(objp);
3512                 }
3513         }
3514
3515         return -1;
3516 }
3517
3518 //      Used to create docking paths and other pre-defined paths through ships.
3519 //      Creates a path in absolute space.
3520 //      Create a path into the object objnum.
3521 //
3522 // input:
3523 //      pl_objp:                        object that will use the path
3524 //      objnum:                 Object to find path to.
3525 //      path_num:               model path index to use
3526 //      exit_flag:              true means this is an exit path in the model
3527 // subsys_path: optional param (default 0) that indicates this is a path to a subsystem
3528 //      Exit:
3529 //      ai_info struct in Pl_objp gets stuffed with information to enable Pl_objp to fly the path.
3530 void ai_find_path(object *pl_objp, int objnum, int path_num, int exit_flag, int subsys_path)
3531 {
3532         ai_info *aip = &Ai_info[Ships[pl_objp->instance].ai_index];
3533
3534         Assert(path_num >= 0);
3535
3536         //      This is test code, find an object with paths.
3537         if (objnum != -1) {
3538                 object  *objp = &Objects[objnum];
3539
3540                 if (objp->type == OBJ_SHIP) {
3541                         polymodel *pm;
3542
3543                         ship    *shipp = &Ships[objp->instance];
3544                         pm = model_get( shipp->modelnum );
3545                         Assert(pm->n_paths > path_num);
3546                         aip->goal_objnum = objp-Objects;
3547                         aip->goal_signature = objp->signature;
3548                         if (exit_flag)
3549                                 create_model_exit_path(pl_objp, objp, path_num);
3550                         else
3551                                 create_model_path(pl_objp, objp, path_num, subsys_path);
3552                         return;
3553                 }
3554
3555         }
3556 }
3557
3558 extern int vector_object_collision(vector *start_pos, vector *end_pos, object *objp, float radius_scale);
3559
3560 //      Maybe make *objp avoid a player object.
3561 //      For now, 4/6/98, only check Player_obj.
3562 //      If player collision would occur, set AIF_AVOIDING_SMALL_SHIP bit in ai_flags.
3563 //      Set aip->avoid_goal_point
3564 int maybe_avoid_player(object *objp, vector *goal_pos)
3565 {
3566         ai_info *aip;
3567         vector  cur_pos, new_goal_pos;
3568         object  *player_objp;
3569         vector  n_vec_to_goal, n_vec_to_player;
3570
3571         aip = &Ai_info[Ships[objp->instance].ai_index];
3572
3573         if (!timestamp_elapsed(aip->avoid_check_timestamp))
3574                 return 0;
3575
3576         player_objp = Player_obj;
3577
3578         float   speed_time;
3579
3580         //      How far two ships could be apart and still collide within one second.
3581         speed_time = player_objp->phys_info.speed + objp->phys_info.speed;
3582
3583         float   obj_obj_dist;
3584
3585         obj_obj_dist = vm_vec_dist_quick(&player_objp->pos, &objp->pos);
3586
3587         if (obj_obj_dist > speed_time*2.0f)
3588                 return 0;
3589
3590         cur_pos = objp->pos;
3591
3592         new_goal_pos = *goal_pos;
3593
3594         float dist = vm_vec_normalized_dir(&n_vec_to_goal, goal_pos, &objp->pos);
3595         vm_vec_normalized_dir(&n_vec_to_player, &player_objp->pos, &objp->pos);
3596
3597         if (dist > speed_time*2.0f) {
3598                 vm_vec_scale_add(&new_goal_pos, &objp->pos, &n_vec_to_goal, 200.0f);
3599         }
3600
3601         if (vector_object_collision(&objp->pos, &new_goal_pos, player_objp, 1.5f)) {
3602                 aip->ai_flags |= AIF_AVOIDING_SMALL_SHIP;
3603
3604                 vector  avoid_vec;
3605
3606                 vm_vec_sub(&avoid_vec, &n_vec_to_goal, &n_vec_to_player);
3607                 if (vm_vec_mag_quick(&avoid_vec) < 0.01f) {
3608                         vm_vec_copy_scale(&avoid_vec, &objp->orient.v.rvec, frand()-0.5f);
3609                         vm_vec_scale_add2(&avoid_vec, &objp->orient.v.uvec, frand()-0.5f);
3610                         vm_vec_normalize(&avoid_vec);
3611                 } else {
3612                         vector  tvec1;
3613                         vm_vec_normalize(&avoid_vec);
3614                         vm_vec_crossprod(&tvec1, &n_vec_to_goal, &avoid_vec);
3615                         vm_vec_crossprod(&avoid_vec, &tvec1, &n_vec_to_player);
3616                 }
3617
3618                 //      Now, avoid_vec is a vector perpendicular to the vector to the player and the direction *objp
3619                 //      should fly in to avoid the player while still approaching its goal.
3620                 vm_vec_scale_add(&aip->avoid_goal_point, &player_objp->pos, &avoid_vec, 400.0f);
3621
3622                 aip->avoid_check_timestamp = timestamp(1000);
3623
3624                 return 1;
3625         } else {
3626                 aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
3627                 aip->avoid_check_timestamp = timestamp((int) (obj_obj_dist/200.0f) + 500);
3628
3629                 return 0;
3630         }
3631 }
3632
3633 //      Make object *still_objp enter AIM_STILL mode.
3634 //      Make it point at view_pos.
3635 void ai_stay_still(object *still_objp, vector *view_pos)
3636 {
3637         ship    *shipp;
3638         ai_info *aip;
3639
3640         Assert(still_objp->type == OBJ_SHIP);
3641         Assert((still_objp->instance >= 0) && (still_objp->instance < MAX_OBJECTS));
3642
3643         shipp = &Ships[still_objp->instance];
3644         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
3645
3646         aip = &Ai_info[shipp->ai_index];
3647
3648         aip->mode = AIM_STILL;
3649
3650         //      If view_pos not NULL, point at that point.  Else, point at a point directly in front of ship.  Ie, don't turn.
3651         if (view_pos != NULL)
3652                 aip->goal_point = *view_pos;
3653         else
3654                 vm_vec_scale_add(&aip->goal_point, &still_objp->pos, &still_objp->orient.v.fvec, 100.0f);
3655 }
3656
3657 // code which is called from ai_dock_with_object and ai_dock to set flags and apprioriate variable
3658 // when two objects have completed docking.  used because we can dock object initially at misison load
3659 // time (meaning that ai_dock() might never get called).  docker has docked with dockee (i.e. docker
3660 // would be a freighter and dockee would be a cargo).
3661 void ai_do_objects_docked_stuff(object *docker, object *dockee)
3662 {
3663         ai_info *aip, *other_aip;
3664
3665         aip = &Ai_info[Ships[docker->instance].ai_index];
3666         other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3667
3668         // set the flags and dock_objnum for both objects
3669         aip->ai_flags |= AIF_DOCKED;
3670         aip->dock_objnum = OBJ_INDEX(dockee);
3671         other_aip->ai_flags |= AIF_DOCKED;
3672         other_aip->dock_objnum = OBJ_INDEX(docker);
3673         aip->dock_signature = dockee->signature;
3674         other_aip->dock_signature = docker->signature;
3675
3676         // add multiplayer hook here to deal with docked objects.  We need to only send information
3677         // about the object that is docking.  Both flags will get updated.
3678         if ( MULTIPLAYER_MASTER )
3679                 send_ai_info_update_packet( docker, AI_UPDATE_DOCK );
3680
3681 }
3682
3683 // code which is called when objects become undocked. Equivalent of above function.
3684 // dockee might not be valid since this code can get called to cleanup after a ship
3685 // has blown up!
3686 void ai_do_objects_undocked_stuff( object *docker, object *dockee )
3687 {
3688         ai_info *aip, *other_aip;
3689
3690         // add multiplayer hook here to deal with undocked objects.  Do it before we
3691         // do anything else.  We don't need to send info for both objects, since we can find
3692         // it be dock_objnum
3693         if ( MULTIPLAYER_MASTER )
3694                 send_ai_info_update_packet( docker, AI_UPDATE_UNDOCK );
3695
3696         aip = &Ai_info[Ships[docker->instance].ai_index];
3697
3698         // set the flags and dock_objnum for both objects
3699         aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3700         aip->dock_objnum = -1;
3701         
3702         if ( dockee != NULL ) {
3703                 other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3704                 other_aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3705                 other_aip->dock_objnum = -1;
3706         }
3707
3708 }
3709
3710
3711 //      --------------------------------------------------------------------------
3712 //      Interface from goals code to AI.
3713 //      Cause *docker to dock with *dockee.
3714 //      priority is priority of goal from goals code.
3715 //      dock_type is:
3716 //              AIDO_DOCK               set goal of docking
3717 //              AIDO_DOCK_NOW   immediately dock, used for ships that need to be docked at mission start
3718 //              AIDO_UNDOCK             set goal of undocking
3719 void ai_dock_with_object(object *docker, object *dockee, int priority, int dock_type, int docker_index, int dockee_index)
3720 {
3721         ai_info         *aip;
3722         polymodel       *pm;
3723         ai_info         *dockee_aip;
3724
3725         Assert(docker != NULL);
3726         Assert(dockee != NULL);
3727         Assert(docker->instance != -1);
3728         Assert(Ships[docker->instance].ai_index != -1);
3729         Assert(Ships[dockee->instance].ai_index != -1);
3730         Assert( docker_index != -1 );
3731         Assert( dockee_index != -1 );
3732
3733         aip = &Ai_info[Ships[docker->instance].ai_index];
3734
3735         if ((aip->ai_flags & AIF_DOCKED) && (dock_type == AIDO_DOCK)) {
3736                 object  *dockee2;
3737                 int             docker_index2, dockee_index2;
3738
3739                 Assert(aip->dock_objnum > -1);
3740                 dockee2 = &Objects[aip->dock_objnum];
3741                 docker_index2 = aip->dock_index;
3742                 dockee_index2 = aip->dockee_index;
3743                 // MWA -- 2/9/98.  use the goal code to undock the ships since goals might need to get removed
3744                 // and that code will do it properly.  I'd actually be surprised if we got into this code anymore
3745                 // since the outer layer goal code should deal with this issue....but who knows...
3746                 ai_add_goal_ship_internal( aip, AI_GOAL_UNDOCK, NULL, -1, -1, 0 );
3747
3748                 // old code below
3749                 //ai_dock_with_object(docker, dockee2, priority, AIDO_UNDOCK, docker_index2, dockee_index2);
3750                 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));
3751                 nprintf(("AI", "...so ship %s will now undock.\n", Ships[docker->instance].ship_name));
3752                 return;
3753         }
3754
3755         dockee_aip = &Ai_info[Ships[dockee->instance].ai_index];
3756
3757         aip->goal_objnum = dockee - Objects;
3758         aip->goal_signature = dockee->signature;
3759
3760         aip->mode = AIM_DOCK;
3761
3762         switch (dock_type) {
3763         case AIDO_DOCK:
3764                 aip->submode = AIS_DOCK_0;
3765                 break;
3766         case AIDO_DOCK_NOW:
3767                 aip->submode = AIS_DOCK_3A;
3768                 break;
3769         case AIDO_UNDOCK:
3770                 aip->submode = AIS_UNDOCK_0;
3771                 break;
3772         default:
3773                 Int3();         //      Bogus dock_type.
3774         }
3775
3776         aip->submode_start_time = Missiontime;
3777         aip->dock_index = docker_index;
3778         aip->dockee_index = dockee_index;
3779
3780         dockee_aip->dock_index = dockee_index;
3781         dockee_aip->dockee_index = docker_index;
3782
3783         // get the path number to the docking point on the dockee.  Each docking point contains a list
3784         // of paths that the point can be reached by.  Pick the first path in the path list for now.
3785         // We only want to do this stuff if we are docking!!!  Be sure to set the path index
3786         if ((dock_type == AIDO_DOCK) || (dock_type == AIDO_DOCK_NOW)) {
3787                 pm = model_get( Ships[dockee->instance].modelnum );
3788                 Assert( pm->docking_bays[dockee_index].num_spline_paths > 0 );
3789
3790                 // only set the dock path index if we are docking.  undocking will assume that dock_path_index
3791                 // already set from some other docking command
3792                 aip->dock_path_index = dockee_index;
3793                 dockee_aip->dock_path_index = docker_index;
3794         }
3795
3796         if (dock_type != AIDO_DOCK_NOW) {
3797                 int path_num;
3798                 //      Note: Second parameter is dock path index.  This should be specified as an
3799                 //      _input_ to this function and passed through.  The path index should be already
3800                 // set for the undock function
3801                 path_num = ai_return_path_num_from_dockbay(dockee, dockee_index);
3802                 ai_find_path(docker, dockee-Objects, path_num, 0);
3803 //              ai_find_path(dockee-Objects, dockee_index, 0);
3804         } else {
3805                 dock_orient_and_approach(docker, dockee, DOA_DOCK_STAY);
3806                 //aip->dock_objnum = OBJ_INDEX(dockee);
3807                 ai_do_objects_docked_stuff( docker, dockee );
3808         }
3809
3810 }
3811
3812 //      Cause a ship to fly its waypoints.
3813 //      flags tells:
3814 //              WPF_REPEAT      Set -> repeat waypoints.
3815 void ai_start_waypoints(object *objp, int waypoint_list_index, int wp_flags)
3816 {
3817         ai_info *aip;
3818
3819         Assert(waypoint_list_index < Num_waypoint_lists);
3820
3821         //nprintf(("AI", "Frame %i: Ship %s instructed to fly waypoint list #%i\n", AI_FrameCount, Ships[objp->instance].ship_name, waypoint_list_index));
3822         aip = &Ai_info[Ships[objp->instance].ai_index];
3823
3824         if ( (aip->mode == AIM_WAYPOINTS) && (aip->wp_index == waypoint_list_index) )
3825                 return;
3826
3827         aip->ai_flags |= AIF_FORMATION_WING;
3828         aip->ai_flags &= ~AIF_FORMATION_OBJECT;
3829         aip->wp_list = waypoint_list_index;
3830         aip->wp_index = 0;
3831         aip->wp_flags = wp_flags;
3832         aip->mode = AIM_WAYPOINTS;
3833
3834         Assert(aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC);
3835 }
3836
3837 //      Make *objp stay within dist units of *other_objp
3838 void ai_do_stay_near(object *objp, object *other_objp, float dist)
3839 {
3840         ai_info *aip;
3841
3842         Assert(objp != other_objp);             //      Bogus!  Told to stay near self.
3843         Assert(objp->type == OBJ_SHIP);
3844         Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
3845
3846         aip = &Ai_info[Ships[objp->instance].ai_index];
3847
3848         aip->mode = AIM_STAY_NEAR;
3849         aip->submode = -1;
3850         aip->stay_near_distance = dist;
3851         aip->goal_objnum = other_objp-Objects;
3852         aip->goal_signature = other_objp->signature;
3853
3854 }
3855
3856 //      Make object *objp form on wing of object *goal_objp
3857 void ai_form_on_wing(object *objp, object *goal_objp)
3858 {
3859         ai_info *aip;
3860         ship                    *shipp;
3861         ship_info       *sip;
3862
3863         // objp == goal_objp sometimes in multiplayer when someone leaves a game -- make a simple
3864         // out for this case.
3865         if ( Game_mode & GM_MULTIPLAYER ) {
3866                 if ( objp == goal_objp ) {
3867                         return;
3868                 }
3869         }
3870
3871         Assert(objp != goal_objp);              //      Bogus!  Told to form on own's wing!
3872
3873         shipp = &Ships[objp->instance];
3874         sip = &Ship_info[shipp->ship_info_index];
3875
3876         //      Only fighters or bombers allowed to form on wing.
3877         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
3878                 nprintf(("AI", "Warning: Ship %s tried to form on player's wing, but not fighter or bomber.\n", shipp->ship_name));
3879                 return;
3880         }
3881
3882         aip = &Ai_info[Ships[objp->instance].ai_index];
3883
3884         aip->ai_flags &= ~AIF_FORMATION_WING;
3885         aip->ai_flags |= AIF_FORMATION_OBJECT;
3886
3887         aip->goal_objnum = goal_objp-Objects;
3888         ai_set_goal_maybe_abort_dock(objp, aip);
3889         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME*4);           //      Super extra long time until can target another ship.
3890
3891 }
3892
3893 //      Given an object and an object on whose wing to form, return slot to use.
3894 //      Optimize:
3895 //              This function is called per object in formation per frame.  Should store slot in ai_info struct.
3896 int ai_formation_object_get_slotnum(int objnum, object *objp)
3897 {
3898         int     slotnum = 1;                    //      Note: Slot #0 means leader, which isn't someone who was told to form-on-wing.
3899         object *o;
3900
3901         for ( o = GET_FIRST(&obj_used_list); o != END_OF_LIST(&obj_used_list); o = GET_NEXT(o) ) {
3902                 if (objp == o)
3903                         break;
3904                 else if (o->type == OBJ_SHIP)
3905                         if (Ai_info[Ships[o->instance].ai_index].ai_flags & AIF_FORMATION_OBJECT)
3906                                 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == objnum)
3907                                         slotnum++;
3908         }
3909
3910         Assert(o != END_OF_LIST(&obj_used_list));       //      Didn't find objp in list of used ships.  Impossible!
3911
3912         return slotnum;
3913 }
3914
3915 #define BIGNUM  100000.0f
3916
3917 int Debug_k = 0;
3918
3919 //      Given an attacker's position and a target's position and velocity, compute the time of
3920 //      intersection of a weapon fired by the attacker with speed weapon_speed.
3921 //      Return this value.  Return value of 0.0f means no collision is possible.
3922 float compute_collision_time(vector *targpos, vector *targvel, vector *attackpos, float weapon_speed)
3923 {
3924         vector  vec_to_target;
3925         float           pos_dot_vel;
3926         float           vel_sqr;
3927         float           discrim;
3928
3929         vm_vec_sub(&vec_to_target, targpos, attackpos);
3930         pos_dot_vel = vm_vec_dot(&vec_to_target, targvel);
3931         vel_sqr = vm_vec_dot(targvel, targvel) - weapon_speed*weapon_speed;
3932         discrim = pos_dot_vel*pos_dot_vel - vel_sqr*vm_vec_dot(&vec_to_target, &vec_to_target);
3933
3934         if (discrim > 0.0f) {
3935                 float   t1, t2, t_solve;
3936
3937                 t1 = (-pos_dot_vel + fl_sqrt(discrim)) / vel_sqr;
3938                 t2 = (-pos_dot_vel - fl_sqrt(discrim)) / vel_sqr;
3939
3940                 t_solve = BIGNUM;
3941
3942                 if (t1 > 0.0f)
3943                         t_solve = t1;
3944                 if ((t2 > 0.0f) && (t2 < t_solve))
3945                         t_solve = t2;
3946
3947                 if (t_solve < BIGNUM-1.0f) {
3948                         return t_solve + Debug_k * flFrametime;
3949                 }
3950         }
3951
3952         return 0.0f;
3953 }
3954
3955
3956 //      --------------------------------------------------------------------------
3957 //      If far away, use player's speed.
3958 //      If in between, lerp between player and laser speed
3959 //      If close, use laser speed.
3960 // Want to know how much time it will take to get to the enemy.
3961 // This function doesn't account for the fact that by the time the player
3962 // (or his laser) gets to the current enemy position, the enemy will have moved.
3963 // This is dealt with in polish_predicted_enemy_pos.
3964 float compute_time_to_enemy(float dist_to_enemy, object *pobjp, object *eobjp)
3965 {
3966         float   time_to_enemy;
3967         float   pl_speed = pobjp->phys_info.speed;
3968         float   max_laser_distance, max_laser_speed;
3969         int     bank_num, weapon_num;
3970         ship    *shipp = &Ships[pobjp->instance];
3971
3972         bank_num = shipp->weapons.current_primary_bank;
3973         weapon_num = shipp->weapons.primary_bank_weapons[bank_num];
3974         max_laser_speed = Weapon_info[weapon_num].max_speed;
3975         max_laser_distance = max_laser_speed * Weapon_info[weapon_num].lifetime;
3976
3977         //      If pretty far away, use player's speed to predict position, else
3978         //      use laser's speed because when close, we care more about hitting
3979         //      with a laser than about causing ship:ship rendezvous.
3980         if (dist_to_enemy > 1.5 * max_laser_distance) {
3981                 if (pl_speed > 0.0f)
3982                         time_to_enemy = dist_to_enemy/pl_speed;
3983                 else
3984                         time_to_enemy = 1.0f;
3985         } else if (dist_to_enemy > 1.1*max_laser_distance) {
3986                 if (pl_speed > 0.1f) {
3987                         float   scale;
3988
3989                         scale = (float) ((dist_to_enemy - max_laser_distance) / max_laser_distance);
3990                 
3991                         time_to_enemy = (float) (dist_to_enemy/(pl_speed * scale + max_laser_speed * (1.0f - scale)));
3992                 } else
3993                         time_to_enemy = 2.0f;
3994         } else
3995                 time_to_enemy = (float) (dist_to_enemy/max_laser_speed);
3996
3997         // return time_to_enemy * (1.0f + Ai_info[Ships[pobjp->instance].ai_index].lead_scale);
3998         return time_to_enemy + flFrametime;
3999 }
4000
4001 //      Stuff *dot and *tts.
4002 //      *dot is always computed.  If dot is less than zero, the magnitude is
4003 //      incorrect, not having been divided by distance.
4004 //      If *dot is > 0.0f, then tts is computed.  This is the time it will take object
4005 //      *objp to get to *pos, assuming it moves right at it.
4006 void fds_aux(float *dot, float *tts, vector *pos, float dtime, object *objp)
4007 {
4008         vector  v2s;
4009
4010         vm_vec_sub(&v2s, pos, &objp->pos);
4011         *dot = vm_vec_dot(&v2s, &objp->orient.v.fvec);
4012
4013         if (*dot > 0.0f) {
4014                 float   dist;
4015
4016                 dist = vm_vec_dist(&objp->pos, pos);
4017
4018                 if (dist > 0.1f)
4019                         *dot /= dist;
4020                 else
4021                         *dot = 1.0f;
4022
4023                 if (objp->phys_info.speed > 0.1f)
4024                         *tts = dist / objp->phys_info.speed;
4025                 else
4026                         *tts = dist * 100.0f;
4027         }
4028 }
4029
4030 /*
4031 //      Return index of weapon that could hit object *sobjp within dtime seconds.
4032 //      Actual time until impact returned in *atime.
4033 int find_danger_weapon(object *sobjp, float dtime, float *atime, float dot_threshhold)
4034 {
4035         object  *objp, *best_objp = NULL;
4036         float           best_tts = 1000.0f;
4037
4038         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
4039                 if ((objp->type == OBJ_WEAPON) && (sobjp-Objects != objp->parent)) {
4040                         float           dot, tts;
4041                         // vector       psp;            //      Predicted ship position.
4042
4043                         //      Get dot and time to current ship position.
4044                         fds_aux(&dot, &tts, &sobjp->pos, dtime, objp);
4045
4046                         //      If dot and tts are in plausible range, do more expensive stuff.
4047                         if (dot > 0.98f) {
4048 //                              float   dot_from_sobjp;
4049                                 vector  v2e;
4050
4051                                 vm_vec_normalized_dir(&v2e, &objp->pos, &sobjp->pos);
4052 //                              dot_from_sobjp = vm_vec_dot(&sobjp->orient.v.fvec, &v2e);
4053 //                              if (dot_from_sobjp >= dot_threshhold)
4054                                         if (tts < dtime) {
4055                                                 if (tts < best_tts) {
4056                                                         best_tts = tts;
4057                                                         best_objp = objp;
4058                                                 }
4059                                         }
4060                         }
4061                 }
4062         }
4063
4064         *atime = best_tts;
4065
4066         if (best_objp != NULL)
4067                 return best_objp-Objects;
4068         else
4069                 return -1;
4070 }
4071 */
4072
4073 //      --------------------------------------------------------------------------
4074 void ai_set_positions(object *pl_objp, object *en_objp, ai_info *aip, vector *player_pos, vector *enemy_pos)
4075 {
4076         *player_pos = pl_objp->pos;
4077
4078         if (aip->next_predict_pos_time > Missiontime) {
4079                 *enemy_pos = aip->last_predicted_enemy_pos;
4080         } else {
4081                 *enemy_pos = en_objp->pos;
4082
4083                 aip->next_predict_pos_time = Missiontime + Skill_level_delay[Game_skill_level];
4084                 aip->last_predicted_enemy_pos = *enemy_pos;
4085         }
4086
4087
4088 }
4089
4090 //      --------------------------------------------------------------------------
4091 int find_nearest_waypoint(object *objp)
4092 {
4093         int     i;
4094         float   dist, min_dist, dot;
4095         int     min_ind;
4096         ship    *shipp;
4097         int     wp_listnum;
4098         waypoint_list   *wpl;
4099
4100         shipp = &Ships[objp->instance];
4101         wp_listnum = Ai_info[Ships[objp->instance].ai_index].wp_list;
4102         Assert(wp_listnum > 0);
4103         wpl = &Waypoint_lists[wp_listnum];
4104
4105         min_dist = 999999.0f;
4106         min_ind = -1;
4107
4108         for (i=0; i<wpl->count; i++) {
4109                 dist = vm_vec_dist_quick(&objp->pos, &wpl->waypoints[i]);
4110                 dot = vm_vec_dot_to_point(&objp->orient.v.fvec, &objp->pos, &wpl->waypoints[i]);
4111                 dist = (float) (dist * (1.25 - dot));
4112                 if (dist < min_dist) {
4113                         min_dist = dist;
4114                         min_ind = i;
4115                 }
4116         }
4117
4118         Assert(min_ind != -1);
4119
4120         return min_ind;
4121 }
4122
4123 //      Given an ai_info struct, by reading current goal and path information,
4124 //      extract base path information and return in pmp and pmpv.
4125 //      Return true if found, else return false.
4126 //      false means the current point is not on the original path.
4127 int get_base_path_info(int path_cur, int goal_objnum, model_path **pmp, mp_vert **pmpv)
4128 {
4129         pnode                   *pn = &Path_points[path_cur];
4130         ship_info       *sip = &Ship_info[Ships[Objects[goal_objnum].instance].ship_info_index];
4131         polymodel       *pm = model_get(sip->modelnum);
4132         static          int     debug_last_index = -1;
4133         *pmpv = NULL;
4134         *pmp = NULL;
4135
4136         if (pn->path_num != -1) {
4137                 *pmp = &pm->paths[pn->path_num];
4138                 if (pn->path_index != -1)
4139                         *pmpv = &(*pmp)->verts[pn->path_index];
4140                 else
4141                         return 0;
4142         } else
4143                 return 0;
4144
4145 /*      if (debug_last_index != *pmpv-(*pmp)->verts) {
4146                 debug_last_index = *pmpv-(*pmp)->verts;
4147                 nprintf(("AI", "Point %i has %i turrets: ", *pmpv-(*pmp)->verts, (*pmpv)->nturrets));
4148                 for (int i=0; i<(*pmpv)->nturrets; i++) {
4149                         nprintf(("AI", "%i ", (*pmpv)->turret_ids[i]));
4150                 }
4151                 nprintf(("AI", "\n"));
4152         }
4153 */
4154         return 1;
4155 }
4156
4157 //      Modify, in place, the points in a global model path.
4158 //      Only modify those points that are defined in the model path.  Don't modify the
4159 //      leadin points, such as those that are necessary to get the model on the path.
4160 void modify_model_path_points(object *objp)
4161 {       
4162         ai_info         *aip = &Ai_info[Ships[objp->instance].ai_index];
4163         object          *mobjp = &Objects[aip->path_objnum];
4164         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
4165         polymodel       *pm = model_get(osip->modelnum);
4166         pnode                   *pnp;
4167         int                     path_num, dir;
4168
4169         Assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
4170
4171         pnp = &Path_points[aip->path_start];
4172         while ((pnp->path_index == -1) && (pnp-Path_points - aip->path_start < aip->path_length))
4173                 pnp++;
4174
4175         path_num = pnp->path_num;
4176         Assert((path_num >= 0) && (path_num < pm->n_paths));
4177         
4178         Assert(pnp->path_index != -1);  //      If this is -1, that means we never found the model path points
4179
4180         dir = 1;
4181         if ( aip->ai_flags & AIF_USE_EXIT_PATH ) {
4182                 dir = -1;
4183         }
4184
4185         copy_xlate_model_path_points(mobjp, &pm->paths[path_num], dir, pm->paths[path_num].nverts, path_num, pnp);
4186 }
4187
4188 //      Return an indication of the distance between two matrices.
4189 //      This is the sum of the distances of their dot products from 1.0f.
4190 float ai_matrix_dist(matrix *mat1, matrix *mat2)
4191 {
4192         float   t;
4193
4194         t =  1.0f - vm_vec_dot(&mat1->v.fvec, &mat2->v.fvec);
4195         t += 1.0f - vm_vec_dot(&mat1->v.uvec, &mat2->v.uvec);
4196         t += 1.0f - vm_vec_dot(&mat1->v.rvec, &mat2->v.rvec);
4197
4198         return t;
4199 }
4200
4201
4202 //      Paths are created in absolute space, so a moving object needs to have model paths within it recreated.
4203 //      This uses the hash functions which means the slightest movement will cause a recreate, though the timestamp
4204 //      prevents this from happening too often.
4205 //      force_recreate_flag TRUE means to recreate regardless of timestamp.
4206 //      Returns TRUE if path recreated.
4207 float maybe_recreate_path(object *objp, ai_info *aip, int force_recreate_flag)
4208 {
4209         int     hashval;
4210
4211         Assert(&Ai_info[Ships[objp->instance].ai_index] == aip);
4212
4213         if ((aip->mode == AIM_BAY_EMERGE) || (aip->mode == AIM_BAY_DEPART))
4214                 if ((OBJ_INDEX(objp) % 4) == (Framecount % 4))
4215                         force_recreate_flag = 1;
4216
4217         //      If no path, that means we don't need one.
4218         if (aip->path_start == -1)
4219                 return 0.0f;
4220
4221         // AL 11-12-97: If AIF_USE_STATIC_PATH is set, don't try to recreate.  This is needed when ships
4222         //                                  emerge from fighter bays.  We don't need to recreate the path.. and in case the 
4223         //              parent ship dies, we still want to be able to continue on the path
4224         if ( aip->ai_flags & AIF_USE_STATIC_PATH ) 
4225                 return 0.0f;
4226
4227         if (force_recreate_flag || timestamp_elapsed(aip->path_next_create_time)) {
4228                 object  *path_objp;
4229
4230                 path_objp = &Objects[aip->path_objnum];
4231
4232                 if ((hashval = create_object_hash(path_objp)) != aip->path_goal_obj_hash) {
4233                         float dist;
4234                         
4235                         dist = vm_vec_dist_quick(&path_objp->pos, &aip->path_create_pos);
4236                         dist += ai_matrix_dist(&path_objp->orient, &aip->path_create_orient) * 25.0f;
4237
4238                         if (force_recreate_flag || (dist > 2.0f)) {
4239                                 aip->path_next_create_time = timestamp(1000);   //      Update again in as little as 1000 milliseconds, ie 1 second.
4240                                 aip->path_goal_obj_hash = hashval;
4241                                 modify_model_path_points(objp);
4242
4243                                 aip->path_create_pos = path_objp->pos;
4244                                 aip->path_create_orient = path_objp->orient;
4245                                 
4246                                 return dist;
4247                         }
4248                 }
4249         }
4250
4251         return 0.0f;
4252 }
4253
4254 //      Set acceleration for ai_dock().
4255 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)
4256 {
4257         float prev_dot_to_goal = aip->prev_dot_to_goal;
4258         
4259         aip->prev_dot_to_goal = dot;
4260
4261         if (objp->phys_info.speed < 0.0f) {
4262                 accelerate_ship(aip, 1.0f/32.0f);
4263         } else if ((prev_dot_to_goal-dot) > 0.01) {
4264                 if (prev_dot_to_goal > dot + 0.05f) {
4265                         accelerate_ship(aip, 0.0f);
4266                 } else {
4267                         change_acceleration(aip, -1.0f);        //      -1.0f means subtract off flFrametime from acceleration value in 0.0..1.0
4268                 }
4269         } else {
4270                 if ((aip->mode == AIM_DOCK) && (dist_to_next < 150.0f) && (aip->path_start + aip->path_length - 2 == aip->path_cur)) {
4271                         set_accel_for_target_speed(objp, sip->max_speed * max(dist_to_next/500.0f, 1.0f));
4272                         //mprintf(("dist = %7.3f, speed = %7.3f\n", dist_to_next, objp->phys_info.speed));
4273                 } else if ((dot_to_next >= dot * .9) || (dist_to_next > 100.0f)) {
4274                         if (dist_to_goal > 200.0f)
4275                                 set_accel_for_target_speed(objp, sip->max_speed * (dot + 1.0f) / 2.0f);
4276                         else {
4277                                 float   xdot;
4278
4279                                 xdot = (dot_to_next + dot)/2.0f;
4280                                 if (xdot < 0.0f)
4281                                         xdot = 0.0f;
4282
4283                                 // AL: if following a path not in dock mode, move full speed
4284                                 if (( aip->mode != AIM_DOCK ) && (dot > 0.9f)) {
4285                                         set_accel_for_target_speed(objp, sip->max_speed*dot*dot*dot);
4286                                 } else {
4287                                         if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4288                                                 //nprintf(("AI", "Target speed = %7.3f\n", dist_to_goal/8.0f));
4289                                                 set_accel_for_target_speed(objp, dist_to_goal/8.0f + 2.0f);
4290                                         } else {
4291                                                 set_accel_for_target_speed(objp, sip->max_speed * (2*xdot + 0.25f)/4.0f);
4292                                         }
4293                                 }
4294                         }
4295                 } else {
4296                         float   xdot;
4297
4298                         xdot = max(dot_to_next, 0.1f);
4299                         if ( aip->mode != AIM_DOCK ) {
4300                                 set_accel_for_target_speed(objp, sip->max_speed);
4301                         } else {
4302                                 float   speed;
4303                                 if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4304                                         speed = dist_to_goal/8.0f + 2.0f;
4305                                 } else if (dist_to_goal < 4*objp->radius + 50.0f) {
4306                                         speed = dist_to_goal/4.0f + 4.0f;
4307                                 } else {
4308                                         speed = sip->max_speed * (3*xdot + 1.0f)/4.0f;
4309                                 }
4310                                 if (aip->mode == AIM_DOCK) {
4311                                         speed = speed * 2.0f + 1.0f;
4312                                         if (aip->goal_objnum != -1) {
4313                                                 speed += Objects[aip->goal_objnum].phys_info.speed;
4314                                         }
4315                                 }
4316
4317                                 set_accel_for_target_speed(objp, speed);
4318                         }
4319                 }
4320         }
4321 }
4322
4323 //      --------------------------------------------------------------------------
4324 //      Follow a path associated with a large object, such as a capital ship.
4325 //      The points defined on the path are in the object's reference frame.
4326 //      The object of interest is goal_objnum.
4327 //      The paths are defined in the model.  The path of interest is wp_list.
4328 //      The next goal point in the path is wp_index.
4329 //      wp_flags contain special information specific to the path.
4330
4331 // The path vertices are defined by model_path structs:
4332 //              typedef struct model_path {
4333 //                      char            name[MAX_NAME_LEN];                                     // name of the subsystem.  Probably displayed on HUD
4334 //                      int             nverts;
4335 //                      vector  *verts;
4336 //              } model_path;
4337
4338 //      The polymodel struct for the object contains the following:
4339 //              int                     n_paths;
4340 //              model_path      *paths;
4341
4342 //      Returns distance to goal point.
4343 float ai_path()
4344 {
4345         polymodel       *pm;
4346         int             num_paths, num_points;
4347         float           dot, dist_to_goal, dist_to_next, speed, dot_to_next;
4348         ship            *shipp = &Ships[Pl_objp->instance];
4349         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4350         ai_info *aip;
4351         vector  nvel_vec;
4352         float           mag, prev_dot_to_goal;
4353         vector  temp_vec, *slop_vec;
4354         object  *gobjp;
4355         ship            *gshipp;
4356         vector  *cvp, *nvp, next_vec, gcvp, gnvp;               //      current and next vertices in global coordinates.
4357
4358         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4359
4360         Assert(aip->goal_objnum != -1);
4361         Assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
4362
4363         gobjp = &Objects[aip->goal_objnum];
4364         gshipp = &Ships[gobjp->instance];
4365
4366         pm = model_get( gshipp->modelnum );
4367         num_paths = pm->n_paths;
4368         Assert(num_paths > 0);
4369
4370         if (aip->path_start == -1) {
4371                 int path_num;
4372                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
4373                 Assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
4374                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
4375         }
4376
4377         // nprintf(("AI", "Frame: %i, Path index = %i/%i\n", AI_FrameCount, aip->path_cur-aip->path_start, aip->path_length));
4378
4379         maybe_recreate_path(Pl_objp, aip, 0);
4380
4381         num_points = aip->path_length;
4382
4383         //      Set cvp and nvp as pointers to current and next vertices of interest on path.
4384         cvp = &Path_points[aip->path_cur].pos;
4385         if ((aip->path_cur + aip->path_dir - aip->path_start < num_points) || (aip->path_cur + aip->path_dir < aip->path_start))
4386                 nvp = &Path_points[aip->path_cur + aip->path_dir].pos;
4387         else {
4388                 //      If this is 0, then path length must be 1 which means we have no direction!
4389                 Assert((aip->path_cur - aip->path_dir >= aip->path_start) && (aip->path_cur - aip->path_dir - aip->path_start < num_points));
4390                 //      Cleanup for above Assert() which we hit too near release. -- MK, 5/24/98.
4391                 if (aip->path_cur - aip->path_dir - aip->path_start >= num_points) {
4392                         if (aip->path_dir == 1)
4393                                 aip->path_cur = aip->path_start;
4394                         else
4395                                 aip->path_cur = aip->path_start + num_points - 1;
4396                 }
4397
4398                 vector  delvec;
4399                 vm_vec_sub(&delvec, cvp, &Path_points[aip->path_cur - aip->path_dir].pos);
4400                 vm_vec_normalize(&delvec);
4401                 vm_vec_scale_add(&next_vec, cvp, &delvec, 10.0f);
4402                 nvp = &next_vec;
4403         }
4404
4405         //      Interrupt if can't get to current goal point.  Debug only.
4406 /*      if (pp_collide(&Pl_objp->pos, cvp, gobjp, Pl_objp->radius)) {
4407                 Int3();
4408         }
4409 */
4410         //      See if can reach next point (as opposed to current point)
4411         //      However, don't do this if docking and next point is last point.
4412         //      That is, we don't want to pursue the last point under control of the
4413         //      path code.  In docking, this is a special hack.
4414         if ((aip->mode != AIM_DOCK) || ((aip->path_cur-aip->path_start) < num_points - 2)) {
4415                 if ((aip->path_cur + aip->path_dir > aip->path_start) && (aip->path_cur + aip->path_dir < aip->path_start + num_points-2)) {
4416                         if ( timestamp_elapsed(aip->path_next_check_time)) {
4417                                 aip->path_next_check_time = timestamp( 3000 );
4418                                 if (!pp_collide(&Pl_objp->pos, nvp, gobjp, 1.1f * Pl_objp->radius)) {
4419                                         cvp = nvp;
4420                                         aip->path_cur += aip->path_dir;
4421                                         nvp = &Path_points[aip->path_cur].pos;
4422                                         //nprintf(("AI", "Reach: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4423                                 }
4424                         }
4425                 }
4426         }
4427
4428         gcvp = *cvp;
4429         gnvp = *nvp;
4430
4431         speed = Pl_objp->phys_info.speed;
4432
4433         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &gcvp);
4434         dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, &gnvp);
4435         //      Can't use fvec, need to use velocity vector because we aren't necessarily
4436         //      moving in the direction we're facing.
4437
4438 //      if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4439         if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4440                 mag = 0.0f;
4441                 vm_vec_zero(&nvel_vec);
4442         } else
4443                 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4444
4445         //      If moving not-very-slowly and sliding, then try to slide at goal, rather than
4446         //      point at goal.
4447         slop_vec = NULL;
4448         if (mag < 1.0f)
4449                 nvel_vec = Pl_objp->orient.v.fvec;
4450         else if (mag > 5.0f) {
4451                 float   nv_dot;
4452                 nv_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4453                 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4454                         slop_vec = &temp_vec;
4455                         vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.v.fvec);
4456                 }
4457         }
4458
4459         if (dist_to_goal > 0.1f)
4460                 ai_turn_towards_vector(&gcvp, Pl_objp, flFrametime, sip->srotation_time, slop_vec, NULL, 0.0f, 0);
4461
4462         //      Code to control speed is MUCH less forgiving in path following than in waypoint
4463         //      following.  Must be very close to path or might hit objects.
4464         prev_dot_to_goal = aip->prev_dot_to_goal;
4465         dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gcvp);
4466         dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gnvp);
4467
4468         set_accel_for_docking(Pl_objp, aip, dot, dot_to_next, dist_to_next, dist_to_goal, sip);
4469         aip->prev_dot_to_goal = dot;
4470
4471 //mprintf(("Goal index = %i, dist = %7.3f, dot = %7.3f\n", wp_index, dist_to_goal, dot));
4472
4473         //      If moving at a non-tiny velocity, detect attaining path point by its being close to
4474         //      line between previous and current object location.
4475         if ((dist_to_goal < MIN_DIST_TO_WAYPOINT_GOAL) || (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f)) {
4476                 vector  nearest_point;
4477                 float           r, min_dist_to_goal;
4478
4479                 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, &gcvp);
4480
4481                 //      Set min_dist_to_goal = how close must be to waypoint to pick next one.
4482                 //      If docking and this is the second last waypoint, must be very close.
4483                 if ((aip->mode == AIM_DOCK) && (aip->path_cur >= aip->path_length-2))
4484                         min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL;
4485                 else
4486                         min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius;
4487
4488                 if ( (vm_vec_dist_quick(&Pl_objp->pos, &gcvp) < min_dist_to_goal) ||
4489                         ((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, &gcvp) < (MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius))) {
4490                         aip->path_cur += aip->path_dir;
4491                         //nprintf(("AI", " Near: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4492                         if (((aip->path_cur - aip->path_start) > (num_points+1)) || (aip->path_cur < aip->path_start)) {
4493                                 Assert(aip->mode != AIM_DOCK);          //      If docking, should never get this far, getting to last point handled outside ai_path()
4494                                 aip->path_dir = -aip->path_dir;
4495 //                              aip->path_cur += aip->path_dir;
4496                         }
4497                 }
4498         }
4499
4500         return dist_to_goal;
4501 }
4502
4503 void update_min_max(float val, float *min, float *max)
4504 {
4505         if (val < *min)
4506                 *min = val;
4507         else if (val > *max)
4508                 *max = val;
4509 }
4510
4511 //      Stuff bounding box of all enemy objects within "range" units of object *my_objp.
4512 //      Stuff ni min_vec and max_vec.
4513 //      Return value: Number of enemy objects in bounding box.
4514 int get_enemy_team_range(object *my_objp, float range, int enemy_team_mask, vector *min_vec, vector *max_vec)
4515 {
4516         object  *objp;
4517         ship_obj        *so;
4518         int             count = 0;
4519
4520         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4521                 objp = &Objects[so->objnum];
4522                 if (Ships[objp->instance].team & enemy_team_mask) {
4523                         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))
4524                                 if (vm_vec_dist_quick(&my_objp->pos, &objp->pos) < range) {
4525                                         if (count == 0) {
4526                                                 *min_vec = objp->pos;
4527                                                 *max_vec = objp->pos;
4528                                                 count++;
4529                                         } else {
4530                                                 update_min_max(objp->pos.xyz.x, &min_vec->xyz.x, &max_vec->xyz.x);
4531                                                 update_min_max(objp->pos.xyz.y, &min_vec->xyz.y, &max_vec->xyz.y);
4532                                                 update_min_max(objp->pos.xyz.z, &min_vec->xyz.z, &max_vec->xyz.z);
4533                                         }
4534                                 }
4535
4536                 }
4537         }
4538
4539         return count;
4540 }
4541
4542 //      Pick a relatively safe spot for objp to fly to.
4543 //      Problem:
4544 //              Finds a spot away from any enemy within a bounding box.
4545 //              Doesn't verify that "safe spot" is not near some other enemy.
4546 void ai_safety_pick_spot(object *objp)
4547 {
4548         int             objnum;
4549         int             enemy_team_mask;
4550         vector  min_vec, max_vec;
4551         vector  vec_to_center, center;
4552         vector  goal_pos;
4553
4554         objnum = OBJ_INDEX(objp);
4555
4556         enemy_team_mask = get_enemy_team_mask(objnum);
4557
4558         if (get_enemy_team_range(objp, 1000.0f, enemy_team_mask, &min_vec, &max_vec)) {
4559                 vm_vec_avg(&center, &min_vec, &max_vec);
4560                 vm_vec_normalized_dir(&vec_to_center, &center, &objp->pos);
4561
4562                 vm_vec_scale_add(&goal_pos, &center, &vec_to_center, 2000.0f);
4563         } else
4564                 vm_vec_scale_add(&goal_pos, &objp->pos, &objp->orient.v.fvec, 100.0f);
4565
4566         Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pos;
4567 }
4568
4569 //      Fly to desired safe point.
4570 // Returns distance to that point.
4571 float ai_safety_goto_spot(object *objp)
4572 {
4573         float   dot, dist;
4574         ai_info *aip;
4575         vector  vec_to_goal;
4576         ship_info       *sip;
4577         float   dot_val;
4578
4579         sip = &Ship_info[Ships[objp->instance].ship_info_index];
4580
4581         aip = &Ai_info[Ships[objp->instance].ai_index];
4582         dist = vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
4583         dot = vm_vec_dot(&vec_to_goal, &objp->orient.v.fvec);
4584
4585         dot_val = (1.1f + dot) / 2.0f;
4586         if (dist > 200.0f) {
4587                 set_accel_for_target_speed(objp, sip->max_speed * dot_val);
4588         } else
4589                 set_accel_for_target_speed(objp, sip->max_speed * dot_val * (dist/200.0f + 0.2f));
4590
4591         return dist;
4592 }
4593
4594 void ai_safety_circle_spot(object *objp)
4595 {
4596         vector  goal_point;
4597         ship_info       *sip;
4598         float           dot;
4599
4600         sip = &Ship_info[Ships[objp->instance].ship_info_index];
4601
4602         goal_point = Ai_info[Ships[objp->instance].ai_index].goal_point;
4603         dot = turn_towards_tangent(objp, &goal_point, 250.0f);  //      Increased from 50 to 250 to make circling not look so wacky.
4604
4605         set_accel_for_target_speed(objp, 0.5f * (1.0f + dot) * sip->max_speed/4.0f);
4606
4607 //      float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
4608 //      nprintf(("AI", "Ship %s circling %7.3f %7.3f %7.3f.  Distance = %7.3f\n", Ships[Pl_objp->instance].ship_name, goal_point.xyz.x, goal_point.xyz.y, goal_point.xyz.z, dist));
4609
4610 }
4611
4612 //      --------------------------------------------------------------------------
4613 void ai_safety()
4614 {
4615         ai_info *aip;
4616
4617         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4618
4619         switch (aip->submode) {
4620         case AISS_1:
4621                 ai_safety_pick_spot(Pl_objp);
4622                 aip->submode = AISS_2;
4623                 aip->submode_start_time = Missiontime;
4624                 break;
4625         case AISS_1a:   //      Pick a safe point because we just got whacked!
4626                 Int3();
4627                 break;
4628         case AISS_2:
4629                 if (ai_safety_goto_spot(Pl_objp) < 25.0f) {
4630                         aip->submode = AISS_3;
4631                         aip->submode_start_time = Missiontime;
4632                 }
4633                 break;
4634         case AISS_3:
4635                 ai_safety_circle_spot(Pl_objp);
4636                 break;
4637         default:
4638                 Int3();         //      Illegal submode for ai_safety();
4639                 break;
4640         }
4641 }
4642
4643 //      --------------------------------------------------------------------------
4644 //      make Pl_objp fly waypoints.
4645 void ai_waypoints()
4646 {
4647         int             wp_index;
4648         vector  *wp_cur, *wp_next;
4649         float           dot, dist_to_goal, dist_to_next, speed, dot_to_next;
4650         ship            *shipp = &Ships[Pl_objp->instance];
4651         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4652         waypoint_list   *wpl;
4653         ai_info *aip;
4654         vector  nvel_vec;
4655         float           mag;
4656         float           prev_dot_to_goal;
4657         vector  temp_vec;
4658         vector  *slop_vec;
4659
4660         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4661
4662         wp_index = aip->wp_index;
4663
4664         if (wp_index == -1) {
4665                 ai_start_waypoints(Pl_objp, 0, WPF_REPEAT);
4666                 wp_index = aip->wp_index;
4667                 aip->wp_dir = 1;
4668         }
4669
4670         wpl = &Waypoint_lists[Ai_info[Ships[Pl_objp->instance].ai_index].wp_list];
4671
4672         Assert(wpl->count);     // What? Is this zero? Probably wp_index never got initialized!
4673
4674         wp_cur = &wpl->waypoints[wp_index];
4675         wp_next = &wpl->waypoints[(wp_index+1) % wpl->count];
4676         speed = Pl_objp->phys_info.speed;
4677
4678         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, wp_cur);
4679         dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, wp_next);
4680
4681         //      Can't use fvec, need to use velocity vector because we aren't necessarily
4682         //      moving in the direction we're facing.
4683         // AL 23-3-98: Account for very small velocities by checking result of vm_vec_mag().
4684         //                                      If we don't vm_vec_copy_normalize() will think it is normalizing a null vector.
4685 //      if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4686         if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4687                 mag = 0.0f;
4688                 vm_vec_zero(&nvel_vec);
4689         } else {
4690                 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4691         }
4692
4693         //      If moving not-very-slowly and sliding, then try to slide at goal, rather than
4694         //      point at goal.
4695         slop_vec = NULL;
4696         if (mag < 1.0f) {
4697                 nvel_vec = Pl_objp->orient.v.fvec;
4698         } else if (mag > 5.0f) {
4699                 float   nv_dot;
4700                 nv_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4701                 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4702                         slop_vec = &temp_vec;
4703                         vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.v.fvec);
4704                 }
4705         }
4706
4707         //      If a wing leader, take turns more slowly, based on size of wing.
4708         int     scale;
4709
4710         if (Ai_info[Ships[Pl_objp->instance].ai_index].wing >= 0) {
4711                 scale = Wings[Ai_info[Ships[Pl_objp->instance].ai_index].wing].current_count;
4712                 scale = (int) ((scale+1)/2);
4713         } else {
4714                 scale = 1;
4715         }
4716
4717         if (dist_to_goal > 0.1f) {
4718                 ai_turn_towards_vector(wp_cur, Pl_objp, flFrametime, sip->srotation_time*3.0f*scale, slop_vec, NULL, 0.0f, 0);
4719         }
4720
4721         prev_dot_to_goal = aip->prev_dot_to_goal;
4722         dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_cur);
4723         dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_next);
4724         aip->prev_dot_to_goal = dot;
4725
4726         //      If there is no next point on the path, don't care about dot to next.
4727         if (wp_index + 1 >= wpl->count) {
4728                 dot_to_next = dot;
4729         }
4730
4731         // nprintf(("AI", "Wp #%i, dot = %6.3f, next dot = %6.3f, dist = %7.2f\n", wp_index, dot, dot_to_next, dist_to_goal));
4732
4733         if (Pl_objp->phys_info.speed < 0.0f) {
4734                 accelerate_ship(aip, 1.0f/32);
4735         } else if (prev_dot_to_goal > dot+0.01f) {
4736                 //      We are further from pointing at our goal this frame than last frame, so slow down.
4737                 set_accel_for_target_speed(Pl_objp, Pl_objp->phys_info.speed * 0.95f);
4738         } else if (dist_to_goal < 100.0f) {
4739                 float slew_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4740                 if (fl_abs(slew_dot) < 0.9f) {
4741                         accelerate_ship(aip, 0.0f);
4742                 } else if (dot < 0.88f + 0.1f*(100.0f - dist_to_goal)/100.0f) {
4743                         accelerate_ship(aip, 0.0f);
4744                 } else {
4745                         accelerate_ship(aip, 0.5f * dot * dot);
4746                 }
4747         } else {
4748                 float   dot1;
4749                 if (dist_to_goal < 250.0f) {
4750                         dot1 = dot*dot*dot;                             //      Very important to be pointing towards goal when nearby.  Note, cubing preserves sign.
4751                 } else {
4752                         if (dot > 0.0f) {
4753                                 dot1 = dot*dot;
4754                         } else {
4755                                 dot1 = dot;
4756                         }
4757                 }
4758
4759                 if (dist_to_goal > 100.0f + Pl_objp->radius * 2) {
4760                         if (dot < 0.2f) {
4761                                 dot1 = 0.2f;
4762                         }
4763                 }
4764
4765                 if (sip->flags & SIF_SMALL_SHIP) {
4766                         set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/5.0f);
4767                 } else {
4768                         set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/10.0f);
4769                 }
4770         }
4771
4772         //      Make sure not travelling too fast for someone to keep up.
4773         float   max_allowed_speed = 9999.9f;
4774
4775         if (shipp->wingnum != -1) {
4776                 max_allowed_speed = 0.9f * get_wing_lowest_max_speed(Pl_objp);
4777         }
4778
4779         // check if waypoint speed cap is set and adjust max speed
4780         if (aip->waypoint_speed_cap > 0) {
4781                 max_allowed_speed = (float) aip->waypoint_speed_cap;
4782         }
4783
4784         if (aip->prev_accel * shipp->current_max_speed > max_allowed_speed) {
4785                 accelerate_ship(aip, max_allowed_speed / shipp->current_max_speed);
4786         }
4787
4788         if (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f) {
4789                 vector  nearest_point;
4790                 float           r;
4791
4792                 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, wp_cur);
4793
4794                 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))) ||
4795                         ((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, wp_cur) < (MIN_DIST_TO_WAYPOINT_GOAL + fl_sqrt(Pl_objp->radius)))) {
4796                         wp_index++;
4797                         if (wp_index >= wpl->count)
4798                                 if (aip->wp_flags & WPF_REPEAT) {
4799                                         wp_index = 0;
4800                                 } else {
4801                                         int treat_as_ship;
4802
4803                                         // when not repeating waypoints -- mark the goal as done and put and entry into the mission log
4804                                         // we must be careful when dealing with wings.  A ship in a wing might be completing
4805                                         // a waypoint for for the entire wing, or it might be completing a goal for itself.  If
4806                                         // for itself and in a wing, treat the completion as we would a ship
4807                                         treat_as_ship = 1;
4808                                         if ( Ships[Pl_objp->instance].wingnum != -1 ) {
4809                                                 int type;
4810
4811                                                 // I don't think that you can fly waypoints as dynamic goals!!!
4812                                                 // -- This is legal, just stupid. -- Assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4813                                                 
4814                                                 //      Clean up from above Assert, just in case we ship without fixing it.  (Encountered by JimB on 2/9/98)
4815                                                 if ( (aip->active_goal == AI_GOAL_NONE) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC) ) {
4816                                                         aip->mode = AIM_NONE;
4817                                                         Int3(); //      Look at the ship, find out of it's supposed to be flying waypoints. -- MK.
4818                                                 }
4819
4820                                                 type = aip->goals[aip->active_goal].type;
4821                                                 if ( (type == AIG_TYPE_EVENT_WING) || (type == AIG_TYPE_PLAYER_WING) ) {
4822                                                         treat_as_ship = 0;
4823                                                 } else {
4824                                                         treat_as_ship = 1;
4825                                                 }
4826                                         }
4827
4828                                         // if the ship is not in a wing, remove the goal and continue on
4829                                         if ( treat_as_ship ) {
4830                                                 ai_mission_goal_complete( aip );                                        // this call should reset the AI mode
4831                                                 mission_log_add_entry(LOG_WAYPOINTS_DONE, Ships[Pl_objp->instance].ship_name, wpl->name, -1 );
4832                                         } else {
4833                                                 // this ship is in a wing.  We must mark the goal as being completed for all ships
4834                                                 // in the wing.  We will also mark an entry in the log that the wing completed the goal
4835                                                 // not the individual ship.
4836                                                 ai_mission_wing_goal_complete( Ships[Pl_objp->instance].wingnum, &(aip->goals[aip->active_goal]) );
4837                                                 mission_log_add_entry( LOG_WAYPOINTS_DONE, Wings[Ships[Pl_objp->instance].wingnum].name, wpl->name, -1 );
4838                                         }
4839                                         //wp_index = wpl->count-1;
4840                                 }
4841
4842                         aip->wp_index = wp_index;
4843                 }
4844         }
4845 }
4846
4847 //      Make Pl_objp avoid En_objp
4848 //      Not like evading.  This is for avoiding a collision!
4849 //      Note, use sliding if available.
4850 void avoid_ship()
4851 {
4852         //      To avoid an object, turn towards right or left vector until facing away from object.
4853         //      To choose right vs. left, pick one that is further from center of avoid object.
4854         //      Keep turning away from until pointing away from ship.
4855         //      Stay in avoid mode until at least 3 enemy ship radii away.
4856
4857         //      Speed setting:
4858         //      If inside sphere, zero speed and turn towards outside.
4859         //      If outside sphere, inside 2x sphere, set speed percent of max to:
4860         //              max(away_dot, (dist-rad)/rad)
4861         //      where away_dot is dot(Pl_objp->v.fvec, vec_En_objp_to_Pl_objp)
4862
4863         vector  vec_to_enemy;
4864         float           away_dot;
4865         float           dist;
4866         ship            *shipp = &Ships[Pl_objp->instance];
4867         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4868         ai_info *aip = &Ai_info[shipp->ai_index];
4869         vector  player_pos, enemy_pos;
4870
4871         // if we're avoiding a stealth ship, then we know where he is, update with no error
4872         if ( is_object_stealth_ship(En_objp) ) {
4873                 update_ai_stealth_info_with_error(aip/*, 1*/);
4874         }
4875
4876         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
4877         vm_vec_sub(&vec_to_enemy, &enemy_pos, &Pl_objp->pos);
4878
4879         dist = vm_vec_normalize(&vec_to_enemy);
4880         away_dot = -vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_to_enemy);
4881         
4882         if ((sip->max_vel.xyz.x > 0.0f) || (sip->max_vel.xyz.y > 0.0f)) {
4883                 if (vm_vec_dot(&Pl_objp->orient.v.rvec, &vec_to_enemy) > 0.0f) {
4884                         AI_ci.sideways = -1.0f;
4885                 } else {
4886                         AI_ci.sideways = 1.0f;
4887                 }
4888                 if (vm_vec_dot(&Pl_objp->orient.v.uvec, &vec_to_enemy) > 0.0f) {
4889                         AI_ci.vertical = -1.0f;
4890                 } else {
4891                         AI_ci.vertical = 1.0f;
4892                 }
4893         }               
4894
4895         //nprintf(("AI", "Frame %i: Sliding: %s %s\n", Framecount, AI_ci.sideways < 0 ? "left" : "right", AI_ci.vertical < 0 ? "down" : "up" ));
4896         // nprintf(("AI", "away_dot = %6.3f, dist = %7.2f, dist/radsum = %6.3f\n", away_dot, dist, dist/(Pl_objp->radius + En_objp->radius)));
4897
4898         //      If in front of enemy, turn away from it.
4899         //      If behind enemy, try to get fully behind it.
4900         if (away_dot < 0.0f) {
4901                 turn_away_from_point(Pl_objp, &enemy_pos, Pl_objp->phys_info.speed);
4902         } else {
4903                 vector  goal_pos;
4904
4905                 vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.fvec, -100.0f);
4906                 turn_towards_point(Pl_objp, &goal_pos, NULL, Pl_objp->phys_info.speed);
4907         }
4908
4909         //      Set speed.
4910         float   radsum = Pl_objp->radius + En_objp->radius;
4911
4912         if (dist < radsum)
4913                 accelerate_ship(aip, max(away_dot, 0.2f));
4914         else if (dist < 2*radsum)
4915                 accelerate_ship(aip, max(away_dot, (dist - radsum) / radsum)+0.2f);
4916         else
4917                 accelerate_ship(aip, 1.0f);
4918
4919 }
4920
4921 //      Maybe it's time to resume the previous AI mode in aip->previous_mode.
4922 //      Each type of previous_mode has its own criteria on when to resume.
4923 //      Return true if previous mode was resumed.
4924 int maybe_resume_previous_mode(object *objp, ai_info *aip)
4925 {
4926         //      Only (maybe) resume previous goal if current goal is dynamic.
4927         if (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC)
4928                 return 0;
4929
4930         if (aip->mode == AIM_EVADE_WEAPON) {
4931                 if (timestamp_elapsed(aip->mode_time) || (((aip->nearest_locked_object == -1) || (Objects[aip->nearest_locked_object].type != OBJ_WEAPON)) && (aip->danger_weapon_objnum == -1))) {
4932                         Assert(aip->previous_mode != AIM_EVADE_WEAPON);
4933                         aip->mode = aip->previous_mode;
4934                         aip->submode = aip->previous_submode;
4935                         aip->submode_start_time = Missiontime;
4936                         aip->active_goal = AI_GOAL_NONE;
4937                         aip->mode_time = -1;                    //      Means do forever.
4938                         return 1;
4939                 }
4940         } else if ( aip->previous_mode == AIM_GUARD) {
4941                 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
4942                         object  *guard_objp;
4943                         float   dist;
4944
4945                         guard_objp = &Objects[aip->guard_objnum];
4946                         dist = vm_vec_dist_quick(&guard_objp->pos, &objp->pos);
4947
4948                         //      If guarding ship is far away from guardee and enemy is far away from guardee,
4949                         //      then stop chasing and resume guarding.
4950                         if (dist > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4951                                 if ((En_objp != NULL) && (En_objp->type == OBJ_SHIP)) {
4952                                         if (vm_vec_dist_quick(&guard_objp->pos, &En_objp->pos) > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4953                                                 Assert(aip->previous_mode == AIM_GUARD);
4954                                                 aip->mode = aip->previous_mode;
4955                                                 aip->submode = AIS_GUARD_PATROL;
4956                                                 aip->active_goal = AI_GOAL_NONE;
4957                                                 return 1;
4958                                         }
4959                                 }
4960                         }
4961                 }
4962         }
4963
4964         return 0;
4965
4966 }
4967
4968 //      Call this function if you want something to happen on average every N quarters of a second.
4969 //      The truth value returned by this function will be the same for any given quarter second interval.
4970 //      The value "num" is only passed in to get asynchronous behavior for different objects.
4971 //      modulus == 1 will always return true.
4972 //      modulus == 2 will return true half the time.
4973 //      modulus == 16 will return true for one quarter second interval every four seconds.
4974 int static_rand_timed(int num, int modulus)
4975 {
4976         if (modulus < 2)
4977                 return 1;
4978         else {
4979                 int     t;
4980
4981                 t = Missiontime >> 18;          //      Get time in quarters of a second
4982                 t += num;
4983
4984                 return !(t % modulus);
4985         }
4986 }
4987
4988 //      Maybe fire afterburner based on AI class
4989 int ai_maybe_fire_afterburner(object *objp, ai_info *aip)
4990 {
4991         if (aip->ai_class == 0)
4992                 return 0;               //      Lowest level never aburners away
4993         else  {
4994                 //      Maybe don't afterburner because of a potential collision with the player.
4995                 //      If not multiplayer, near player and player in front, probably don't afterburner.
4996                 if (!(Game_mode & GM_MULTIPLAYER)) {
4997                         if (Ships[objp->instance].team == Player_ship->team) {
4998                                 float   dist;
4999
5000                                 dist = vm_vec_dist_quick(&objp->pos, &Player_obj->pos) - Player_obj->radius - objp->radius;
5001                                 if (dist < 150.0f) {
5002                                         vector  v2p;
5003                                         float           dot;
5004
5005                                         vm_vec_normalized_dir(&v2p, &Player_obj->pos, &objp->pos);
5006                                         dot = vm_vec_dot(&v2p, &objp->orient.v.fvec);
5007
5008                                         if (dot > 0.0f) {
5009                                                 if (dot * dist > 50.0f)
5010                                                         return 0;
5011                                         }
5012                                 }
5013                         }
5014                 }
5015
5016                 if (aip->ai_class >= Num_ai_classes-2)
5017                         return 1;               //      Highest two levels always aburner away.
5018                 else {
5019                         return static_rand_timed(objp-Objects, Num_ai_classes - aip->ai_class);
5020                 }
5021         }
5022 }
5023
5024 //      Maybe engage afterburner after being hit by an object.
5025 void maybe_afterburner_after_ship_hit(object *objp, ai_info *aip, object *en_objp)
5026 {
5027         //      Only do if facing a little away.
5028         if (en_objp != NULL) {
5029                 vector  v2e;
5030
5031                 vm_vec_normalized_dir(&v2e, &en_objp->pos, &objp->pos);
5032                 if (vm_vec_dot(&v2e, &objp->orient.v.fvec) > -0.5f)
5033                         return;
5034         }
5035
5036         if (!( objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5037                 if (ai_maybe_fire_afterburner(objp, aip)) {
5038                         afterburners_start(objp);
5039                         aip->afterburner_stop_time = Missiontime + F1_0/2;
5040                 }
5041         }
5042 }
5043
5044 //      Return true if object *objp is an instructor.
5045 //      Is an instructor if name begins INSTRUCTOR_SHIP_NAME else not.
5046 int is_instructor(object *objp)
5047 {
5048         return !strnicmp(Ships[objp->instance].ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME));
5049 }
5050
5051 //      Evade the weapon aip->danger_weapon_objnum
5052 //      If it's not valid, do a quick out.
5053 //      Evade by accelerating hard.
5054 //      If necessary, turn hard left or hard right.
5055 void evade_weapon()
5056 {
5057         object  *weapon_objp = NULL;
5058         object  *unlocked_weapon_objp = NULL, *locked_weapon_objp = NULL;
5059         vector  weapon_pos, player_pos, goal_point;
5060         vector  vec_from_enemy;
5061         float           dot_from_enemy, dot_to_enemy;
5062         float           dist;
5063         ship            *shipp = &Ships[Pl_objp->instance];
5064         ai_info *aip = &Ai_info[shipp->ai_index];
5065
5066         if (is_instructor(Pl_objp))
5067                 return;
5068
5069         //      Make sure we're actually being attacked.
5070         //      Favor locked objects.
5071         if (aip->nearest_locked_object != -1) {
5072                 if (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)
5073                         locked_weapon_objp = &Objects[aip->nearest_locked_object];
5074         }
5075         
5076         if (aip->danger_weapon_objnum != -1)
5077                 if (Objects[aip->danger_weapon_objnum].signature == aip->danger_weapon_signature)
5078                         unlocked_weapon_objp = &Objects[aip->danger_weapon_objnum];
5079                 else
5080                         aip->danger_weapon_objnum = -1;         //      Signatures don't match, so no longer endangered.
5081
5082         if (locked_weapon_objp != NULL) {
5083                 if (unlocked_weapon_objp != NULL) {
5084                         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))
5085                                 weapon_objp = locked_weapon_objp;
5086                         else
5087                                 weapon_objp = unlocked_weapon_objp;
5088                 } else
5089                         weapon_objp = locked_weapon_objp;
5090         } else if (unlocked_weapon_objp != NULL)
5091                 weapon_objp = unlocked_weapon_objp;
5092         else {
5093                 if (aip->mode == AIM_EVADE_WEAPON)
5094                         maybe_resume_previous_mode(Pl_objp, aip);
5095                 return;
5096         }
5097
5098         Assert(weapon_objp != NULL);
5099
5100         if (weapon_objp->type != OBJ_WEAPON) {
5101                 if (aip->mode == AIM_EVADE_WEAPON)
5102                         maybe_resume_previous_mode(Pl_objp, aip);
5103                 return;
5104         }
5105         
5106         weapon_pos = weapon_objp->pos;
5107         player_pos = Pl_objp->pos;
5108
5109         //      Make speed based on skill level, varying at highest skill level, which is harder to hit.
5110         accelerate_ship(aip, 1.0f);
5111
5112         dist = vm_vec_normalized_dir(&vec_from_enemy, &player_pos, &weapon_pos);
5113
5114         dot_to_enemy = -vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_from_enemy);
5115         dot_from_enemy = vm_vec_dot(&weapon_objp->orient.v.fvec, &vec_from_enemy);
5116         //nprintf(("AI", "dot from enemy = %7.3f\n", dot_from_enemy));
5117
5118         //      If shot is incoming...
5119         if (dot_from_enemy < 0.3f) {
5120                 if (weapon_objp == unlocked_weapon_objp)
5121                         aip->danger_weapon_objnum = -1;
5122                 return;
5123         } else if (dot_from_enemy > 0.7f) {
5124                 if (dist < 200.0f) {
5125                         if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5126                                 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
5127                                         //nprintf(("AI", "Frame %i, turning on afterburner.\n", AI_FrameCount));
5128                                         afterburners_start(Pl_objp);
5129                                         aip->afterburner_stop_time = Missiontime + F1_0/2;
5130                                 }
5131                         }
5132                 }
5133
5134                 //      If we're sort of pointing towards it...
5135                 if ((dot_to_enemy < -0.5f) || (dot_to_enemy > 0.5f)) {
5136                         float   rdot;
5137
5138                         //      Turn hard left or right, depending on which gets out of way quicker.
5139                         rdot = vm_vec_dot(&Pl_objp->orient.v.rvec, &vec_from_enemy);
5140
5141                         if ((rdot < -0.5f) || (rdot > 0.5f))
5142                                 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.v.rvec, -200.0f);
5143                         else
5144                                 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.v.rvec, 200.0f);
5145
5146                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
5147                 }
5148         }
5149
5150 }
5151
5152 //      Use sliding and backwards moving to face enemy.
5153 //      (Coded 2/20/98.  Works fine, but it's hard to see how to integrate it into the AI system.
5154 //       Typically ships are moving so fast that a little sliding isn't enough to gain an advantage.
5155 //       It's currently used to avoid collisions and could be used to evade weapon fire, but the latter
5156 //       would be frustrating, I think.
5157 //       This function is currently not called.)
5158 void slide_face_ship()
5159 {
5160         ship_info       *sip;
5161
5162         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
5163
5164         //      If can't slide, return.
5165         if ((sip->max_vel.xyz.x == 0.0f) && (sip->max_vel.xyz.y == 0.0f))
5166                 return;
5167
5168         vector  goal_pos;
5169         float           dot_from_enemy, dot_to_enemy;
5170         vector  vec_from_enemy, vec_to_goal;
5171         float           dist;
5172         float           up, right;
5173         ai_info         *aip;
5174
5175         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
5176
5177         dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
5178
5179         ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
5180
5181         dot_from_enemy = vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.fvec);
5182         dot_to_enemy = -vm_vec_dot(&vec_from_enemy, &Pl_objp->orient.v.fvec);
5183
5184         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.rvec) > 0.0f)
5185                 right = 1.0f;
5186         else
5187                 right = -1.0f;
5188
5189         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.uvec) > 0.0f)
5190                 up = 1.0f;
5191         else
5192                 up = -1.0f;
5193
5194         vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.rvec, right * 200.0f);
5195         vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.uvec, up * 200.0f);
5196
5197         vm_vec_normalized_dir(&vec_to_goal, &goal_pos, &Pl_objp->pos);
5198
5199         if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.v.rvec) > 0.0f)
5200                 AI_ci.sideways = 1.0f;
5201         else
5202                 AI_ci.sideways = -1.0f;
5203
5204         if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.v.uvec) > 0.0f)
5205                 AI_ci.vertical = 1.0f;
5206         else
5207                 AI_ci.vertical = -1.0f;
5208
5209         if (dist < 200.0f) {
5210                 if (dot_from_enemy < 0.7f)
5211                         accelerate_ship(aip, -1.0f);
5212                 else
5213                         accelerate_ship(aip, dot_from_enemy + 0.5f);
5214         } else {
5215                 if (dot_from_enemy < 0.7f) {
5216                         accelerate_ship(aip, 0.2f);
5217                 } else {
5218                         accelerate_ship(aip, 1.0f);
5219                 }
5220         }
5221 }
5222
5223 //      General code for handling one ship evading another.
5224 //      Problem: This code is also used for avoiding an impending collision.
5225 //      In such a case, it is not good to go to max speed, which is often good
5226 //      for a certain kind of evasion.
5227 void evade_ship()
5228 {
5229         vector  player_pos, enemy_pos, goal_point;
5230         vector  vec_from_enemy;
5231         float           dot_from_enemy;
5232         float           dist;
5233         ship            *shipp = &Ships[Pl_objp->instance];
5234         ship_info       *sip = &Ship_info[shipp->ship_info_index];
5235         ai_info *aip = &Ai_info[shipp->ai_index];
5236         float           bank_override = 0.0f;
5237
5238         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
5239
5240         //      Make speed based on skill level, varying at highest skill level, which is harder to hit.
5241         if (Game_skill_level == NUM_SKILL_LEVELS-1) {
5242                 int     rand_int;
5243                 float   accel_val;
5244
5245                 rand_int = static_rand(Pl_objp-Objects);
5246                 accel_val = (float) (((Missiontime^rand_int) >> 14) & 0x0f)/32.0f + 0.5f;
5247                 accelerate_ship(aip, accel_val);
5248                 //nprintf(("AI", "Accel value = %7.3f\n", accel_val));
5249         } else
5250                 accelerate_ship(aip, (float) (Game_skill_level+2) / (NUM_SKILL_LEVELS+1));
5251
5252         if ((Missiontime - aip->submode_start_time > F1_0/2) && (sip->afterburner_fuel_capacity > 0.0f)) {
5253                 float percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
5254                 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
5255                         afterburners_start(Pl_objp);
5256                         aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
5257                 }
5258         }
5259
5260         vm_vec_sub(&vec_from_enemy, &player_pos, &enemy_pos);
5261
5262         dist = vm_vec_normalize(&vec_from_enemy);
5263         dot_from_enemy = vm_vec_dot(&En_objp->orient.v.fvec, &vec_from_enemy);
5264
5265         if (dist > 250.0f) {
5266                 vector  gp1, gp2;
5267                 //      If far away from enemy, circle, going to nearer of point far off left or right wing
5268                 vm_vec_scale_add(&gp1, &enemy_pos, &En_objp->orient.v.rvec, 250.0f);
5269                 vm_vec_scale_add(&gp2, &enemy_pos, &En_objp->orient.v.rvec, -250.0f);
5270                 if (vm_vec_dist_quick(&gp1, &Pl_objp->pos) < vm_vec_dist_quick(&gp2, &Pl_objp->pos))
5271                         goal_point = gp1;
5272                 else
5273                         goal_point = gp2;
5274         } else if (dot_from_enemy < 0.1f) {
5275                 //      If already close to behind, goal is to get completely behind.
5276                 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.fvec, -1000.0f);
5277         } else if (dot_from_enemy > 0.9f) {
5278                 //      If enemy pointing almost right at self, and self pointing close to enemy, turn away from
5279                 vector  vec_to_enemy;
5280                 float           dot_to_enemy;
5281
5282                 vm_vec_sub(&vec_to_enemy, &enemy_pos, &player_pos);
5283
5284                 vm_vec_normalize(&vec_to_enemy);
5285                 dot_to_enemy = vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_to_enemy);
5286                 if (dot_to_enemy > 0.75f) {
5287                         //      Used to go to En_objp's right vector, but due to banking while turning, that
5288                         //      caused flying in an odd spiral.
5289                         vm_vec_scale_add(&goal_point, &enemy_pos, &Pl_objp->orient.v.rvec, 1000.0f);
5290                         if (dist < 100.0f)
5291                                 bank_override = Pl_objp->phys_info.speed; 
5292                 } else {
5293                         bank_override = Pl_objp->phys_info.speed;                       //      In enemy's sights, not pointing at him, twirl away.
5294                         // nprintf(("Mike", " Do sumpin' else."));
5295                         goto evade_ship_l1;
5296                 }
5297         } else {
5298 evade_ship_l1: ;
5299                 if (aip->ai_evasion > myrand()*100.0f/32767.0f) {
5300                         int     temp;
5301                         float   scale;
5302                         float   psrandval;      //      some value close to zero to choose whether to turn right or left.
5303
5304                         psrandval = (float) (((Missiontime >> 14) & 0x0f) - 8); //      Value between -8 and 7
5305                         psrandval = psrandval/16.0f;                                                    //      Value between -1/2 and 1/2 (approx)
5306
5307                         //      If not close to behind, turn towards his right or left vector, whichever won't cross his path.
5308                         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.rvec) > psrandval) {
5309                                 scale = 1000.0f;
5310                         } else {
5311                                 scale = -1000.0f;
5312                         }
5313
5314                         vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.rvec, scale);
5315
5316                         temp = ((Missiontime >> 16) & 0x07);
5317                         temp = ((temp * (temp+1)) % 16)/2 - 4;
5318                         if ((psrandval == 0) && (temp == 0))
5319                                 temp = 3;
5320
5321                         scale = 200.0f * temp;
5322
5323                         vm_vec_scale_add2(&goal_point, &En_objp->orient.v.uvec, scale);
5324                 } else {
5325                         //      No evasion this frame, but continue with previous turn.
5326                         //      Reason: If you don't, you lose rotational momentum.  Turning every other frame,
5327                         //      and not in between results in a very slow turn because of loss of momentum.
5328                         if ((aip->prev_goal_point.xyz.x != 0.0f) || (aip->prev_goal_point.xyz.y != 0.0f) || (aip->prev_goal_point.xyz.z != 0.0f))
5329                                 goal_point = aip->prev_goal_point;
5330                         else
5331                                 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.rvec, 100.0f);
5332                 }
5333         }
5334
5335         // nprintf(("Mike", "Goal point = %7.1f %7.1f %7.1f\n", goal_point.xyz.x, goal_point.xyz.y, goal_point.xyz.z));
5336         turn_towards_point(Pl_objp, &goal_point, NULL, bank_override);
5337
5338         aip->prev_goal_point = goal_point;
5339 }
5340
5341 //      --------------------------------------------------------------------------
5342 //      Fly in a manner making it difficult for opponent to attack.
5343 void ai_evade()
5344 {
5345         evade_ship();
5346 }
5347
5348 /*
5349 // -------------------------------------------------------------------
5350 //      Refine predicted enemy position because enemy will move while we move
5351 //      towards predicted enemy position.
5352 //      last_delta_vec is stuffed with size of polishing in last step.  This small amount
5353 //      can be used to perturb the predicted position to make firing not be exact.
5354 //      This function will almost always undershoot actual position, assuming both ships
5355 //      are moving at constant speed.  But with even one polishing step, the error should
5356 //      be under 1%. The number of polishing steps is specified in the parameter num_polish_steps.
5357 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)
5358 {
5359         int     iteration;
5360         vector  player_pos = pobjp->pos;
5361         vector  enemy_pos = *predicted_enemy_pos;
5362         physics_info    *en_physp = &eobjp->phys_info;
5363         float           time_to_enemy;
5364         vector  last_predicted_enemy_pos = *predicted_enemy_pos;
5365         
5366         vm_vec_zero(last_delta_vec);
5367
5368         for (iteration=0; iteration < num_polish_steps; iteration++) {
5369                 dist_to_enemy = vm_vec_dist_quick(predicted_enemy_pos, &player_pos);
5370                 time_to_enemy = compute_time_to_enemy(dist_to_enemy, pobjp, eobjp);
5371                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, time_to_enemy);
5372                 vm_vec_sub(last_delta_vec, predicted_enemy_pos, &last_predicted_enemy_pos);
5373                 last_predicted_enemy_pos= *predicted_enemy_pos;
5374         }
5375 }
5376 */
5377
5378 /*
5379 Relevant variables are:
5380         best_dot_to_enemy               best dot product to enemy in last BEST_DOT_TIME seconds
5381         best_dot_to_time                time at which best dot occurred
5382         best_dot_from_enemy     best dot product for enemy to player in last BEST_DOT_TIME seconds
5383         best_dot_from_time      time at which best dot occurred
5384         submode_start_time      time at which we entered the current submode
5385         previous_submode                previous submode, get it?
5386 Legal submodes are:
5387         CONTINUOUS_TURN vector_id {0..3 = right, -right, up, -up}
5388         ATTACK
5389         EVADE_SQUIGGLE
5390         EVADE_BRAKE
5391 */
5392
5393 float   G_collision_time;
5394 vector  G_predicted_pos, G_fire_pos;
5395
5396 /*
5397 void show_firing_diag()
5398 {
5399         float           dot;
5400         vector  v2t;
5401         vector  pos1, pos2;
5402         float           dist;
5403
5404         if (G_collision_time == 0.0f)
5405                 return;
5406
5407         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",
5408                 Pl_objp->pos.xyz.x, Pl_objp->pos.xyz.y, Pl_objp->pos.xyz.z, (float) Missiontime/1000.0f, G_collision_time, G_predicted_pos.xyz.x, G_predicted_pos.xyz.y, G_predicted_pos.xyz.z));
5409         vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5410         dot = vm_vec_dot(&v2t, &Pl_objp->orient.v.fvec);
5411         mprintf(("Dot of v.fvec and vector to predicted position = %10.7f (%7.3f degrees)\n", dot, acos(dot)*180.0f/3.141592654f));
5412
5413         vm_vec_scale_add(&pos1, &En_objp->pos, &En_objp->phys_info.vel, G_collision_time);
5414         vm_vec_scale_add(&pos2, &G_fire_pos, &Pl_objp->orient.v.fvec, G_collision_time*300.0f);
5415         dist = vm_vec_dist(&pos1, &pos2);
5416
5417         mprintf(("Enemy, laser pos, distance: [%5.1f %5.1f %5.1f]  [%5.1f %5.1f %5.1f]  %6.2f\n", pos1.xyz.x, pos1.xyz.y, pos1.xyz.z, pos2.xyz.x, pos2.xyz.y, pos2.xyz.z, dist));
5418 }
5419 */
5420
5421 //      If:
5422 //              flags & WIF_PUNCTURE
5423 //      Then Select a Puncture weapon.
5424 //      Else
5425 //              Select Any ol' weapon.
5426 //      Returns primary_bank index.
5427 int ai_select_primary_weapon(object *objp, object *other_objp, int flags)
5428 {
5429         ship    *shipp = &Ships[objp->instance];
5430         ship_weapon *swp = &shipp->weapons;
5431         ship_info *sip;
5432
5433         //Assert( other_objp != NULL );
5434         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5435
5436         sip = &Ship_info[shipp->ship_info_index];
5437
5438         if (flags & WIF_PUNCTURE) {
5439                 if (swp->current_primary_bank >= 0) {
5440                         int     bank_index;
5441
5442                         bank_index = swp->current_primary_bank;
5443
5444                         if (Weapon_info[swp->primary_bank_weapons[bank_index]].wi_flags & WIF_PUNCTURE) {
5445                                 //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[bank_index]].name));
5446                                 return swp->current_primary_bank;
5447                         }
5448                 }
5449                 for (int i=0; i<swp->num_primary_banks; i++) {
5450                         int     weapon_info_index;
5451
5452                         weapon_info_index = swp->primary_bank_weapons[i];
5453
5454                         if (weapon_info_index > -1){
5455                                 if (Weapon_info[weapon_info_index].wi_flags & WIF_PUNCTURE) {
5456                                         swp->current_primary_bank = i;
5457                                         //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5458                                         return i;
5459                                 }
5460                         }
5461                 }
5462                 
5463                 // AL 26-3-98: If we couldn't find a puncture weapon, pick first available weapon if one isn't active
5464                 if ( swp->current_primary_bank < 0 ) {
5465                         if ( swp->num_primary_banks > 0 ) {
5466                                 swp->current_primary_bank = 0;
5467                         }
5468                 }
5469
5470         } else {                //      Don't need to be using a puncture weapon.
5471                 if (swp->current_primary_bank >= 0) {
5472                         if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)){
5473                                 return swp->current_primary_bank;
5474                         }
5475                 }
5476                 for (int i=0; i<swp->num_primary_banks; i++) {
5477                         if (swp->primary_bank_weapons[i] > -1) {
5478                                 if (!(Weapon_info[swp->primary_bank_weapons[i]].wi_flags & WIF_PUNCTURE)) {
5479                                         swp->current_primary_bank = i;
5480                                         nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5481                                         return i;
5482                                 }
5483                         }
5484                 }
5485                 //      Wasn't able to find a non-puncture weapon.  Stick with what we have.
5486         }
5487
5488         Assert( swp->current_primary_bank != -1 );              // get Alan or Allender
5489
5490         return swp->current_primary_bank;
5491 }
5492
5493 //      --------------------------------------------------------------------------
5494 //      Maybe link primary weapons.
5495 void set_primary_weapon_linkage(object *objp)
5496 {
5497         ship            *shipp;
5498         ai_info *aip;
5499
5500         shipp = &Ships[objp->instance];
5501         aip     = &Ai_info[shipp->ai_index];
5502
5503         shipp->flags &= ~SF_PRIMARY_LINKED;
5504
5505         if (Num_weapons > (int) (MAX_WEAPONS * 0.75f)) {
5506                 if (shipp->flags & SF_PRIMARY_LINKED)
5507                         nprintf(("AI", "Frame %i, ship %s: Unlinking primaries.\n", Framecount, shipp->ship_name));
5508                 shipp->flags &= ~SF_PRIMARY_LINKED;
5509                 return;         //      If low on slots, don't link.
5510         }
5511
5512         shipp->flags &= ~SF_PRIMARY_LINKED;
5513
5514         // AL: ensure target is a ship!
5515         if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
5516                 // If trying to destroy a big ship (i.e., not disable/disarm), always unleash all weapons
5517                 if ( ship_get_SIF(&Ships[Objects[aip->target_objnum].instance]) & SIF_BIG_SHIP) {
5518                         if ( aip->targeted_subsys == NULL ) {
5519                                 shipp->flags |= SF_PRIMARY_LINKED;
5520                                 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5521                                 return;
5522                         }
5523                 }
5524         }
5525
5526         // AL 2-11-98: If ship has a disarm or disable goal, don't link unless both weapons are
5527         //                                      puncture weapons
5528         if ( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) ) {
5529                 if ( aip->goals[aip->active_goal].ai_mode & (AI_GOAL_DISABLE_SHIP|AI_GOAL_DISARM_SHIP) ) {
5530                         ship_weapon     *swp;
5531                         swp = &shipp->weapons;
5532                         // only continue if both primaries are puncture weapons
5533                         if ( swp->num_primary_banks == 2 ) {
5534                                 if ( !(Weapon_info[swp->primary_bank_weapons[0]].wi_flags & WIF_PUNCTURE) ) 
5535                                         return;
5536                                 if ( !(Weapon_info[swp->primary_bank_weapons[1]].wi_flags & WIF_PUNCTURE) ) 
5537                                         return;
5538                         }
5539                 }
5540         }
5541
5542         //      Don't want all ships always linking weapons at start, so asynchronize.
5543         if (Missiontime < i2f(30))
5544                 return;
5545         else if (Missiontime < i2f(120)) {
5546                 int r = static_rand((Missiontime >> 17) ^ OBJ_INDEX(objp));
5547                 if ( (r&3) != 0)
5548                         return;
5549         }
5550
5551         if (shipp->weapon_energy > Link_energy_levels_always[Game_skill_level]) {
5552                 shipp->flags |= SF_PRIMARY_LINKED;
5553         } else if (shipp->weapon_energy > Link_energy_levels_maybe[Game_skill_level]) {
5554                 if (objp->hull_strength < Ship_info[shipp->ship_info_index].initial_hull_strength/3.0f)
5555                         shipp->flags |= SF_PRIMARY_LINKED;
5556         }
5557 }
5558
5559 //      --------------------------------------------------------------------------
5560 //      Fire the current primary weapon.
5561 //      *objp is the object to fire from.
5562 void ai_fire_primary_weapon(object *objp)
5563 {
5564         ship                    *shipp = &Ships[objp->instance];
5565         ship_weapon     *swp = &shipp->weapons;
5566         ship_info       *sip;
5567         ai_info         *aip;
5568         object          *enemy_objp;
5569
5570         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5571         sip = &Ship_info[shipp->ship_info_index];
5572
5573         aip = &Ai_info[shipp->ai_index];
5574
5575         //      If low on slots, fire a little less often.
5576         if (Num_weapons > (int) (0.9f * MAX_WEAPONS)) {
5577                 if (frand() > 0.5f) {
5578                         nprintf(("AI", "Frame %i, %s not fire.\n", Framecount, shipp->ship_name));
5579                         return;
5580                 }
5581         }
5582
5583         if (!Ai_firing_enabled){
5584                 return;
5585         }
5586
5587         if (aip->target_objnum != -1){
5588                 enemy_objp = &Objects[aip->target_objnum];
5589         } else {
5590                 enemy_objp = NULL;
5591         }
5592
5593         if ( (swp->current_primary_bank < 0) || (swp->current_primary_bank >= swp->num_primary_banks) || timestamp_elapsed(aip->primary_select_timestamp)) {
5594                 int     flags = 0;
5595                 // AL 2-11-98: If attacking any subsystem (not just engines), use disrupter weapon
5596 //              if ((aip->targeted_subsys != NULL) && (aip->targeted_subsys->system_info->type == SUBSYSTEM_ENGINE)) {
5597                 if ( aip->targeted_subsys != NULL ) {
5598                         flags = WIF_PUNCTURE;
5599                 }
5600                 ai_select_primary_weapon(objp, enemy_objp, flags);
5601                 ship_primary_changed(shipp);    // AL: maybe send multiplayer information when AI ship changes primaries
5602                 aip->primary_select_timestamp = timestamp(5 * 1000);    //      Maybe change primary weapon five seconds from now.
5603         }
5604
5605         //      If pointing nearly at predicted collision point of target, bash orientation to be perfectly pointing.
5606         float   dot;
5607         vector  v2t;
5608
5609 //      if (!IS_VEC_NULL(&G_predicted_pos)) {
5610         if (!( vm_vec_mag_quick(&G_predicted_pos) < AICODE_SMALL_MAGNITUDE )) {
5611                 if ( !vm_vec_cmp(&G_predicted_pos, &G_fire_pos) ) {
5612                         nprintf(("Warning", "Avoid NULL vector assert.. why are G_predicted_pos and G_fire_pos the same?\n"));
5613                 } else {
5614                         vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5615                         dot = vm_vec_dot(&v2t, &objp->orient.v.fvec);
5616                         if (dot > .998629534f){ //      if within 3.0 degrees of desired heading, bash
5617                                 vm_vector_2_matrix(&objp->orient, &v2t, &objp->orient.v.uvec, NULL);
5618                         }
5619                 }
5620         }
5621
5622         //      Make sure not firing at a protected ship unless firing at a live subsystem.
5623         //      Note: This happens every time the ship tries to fire, perhaps every frame.
5624         //      Should be wrapped in a timestamp, same one that enables it to fire, but that is complicated
5625         //      by multiple banks it can fire from.
5626         if (aip->target_objnum != -1) {
5627                 object  *tobjp = &Objects[aip->target_objnum];
5628                 if (tobjp->flags & OF_PROTECTED) {
5629                         if (aip->targeted_subsys != NULL) {
5630                                 int     type;
5631
5632                                 type = aip->targeted_subsys->system_info->type;
5633                                 if (ship_get_subsystem_strength(&Ships[tobjp->instance], type) == 0.0f) {
5634                                         aip->target_objnum = -1;
5635                                         return;
5636                                 }
5637                         } else {
5638                                 aip->target_objnum = -1;
5639                                 return;
5640                         }
5641                 }
5642         }
5643
5644         //      If enemy is protected, not firing a puncture weapon and enemy's hull is low, don't fire.
5645         if ((enemy_objp != NULL) && (enemy_objp->flags & OF_PROTECTED)) {
5646                 // AL: 3-6-98: Check if current_primary_bank is valid
5647                 if ((enemy_objp->hull_strength < 750.0f) && 
5648                         ((aip->targeted_subsys == NULL) || (enemy_objp->hull_strength < aip->targeted_subsys->current_hits + 50.0f)) &&
5649                         (swp->current_primary_bank >= 0) ) {
5650                         if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)) {
5651                                 //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));
5652                                 swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(1000);
5653                                 return;
5654                         }
5655
5656                         /*
5657                         int     num_attacking;
5658                         num_attacking = num_enemies_attacking(enemy_objp-Objects);
5659                         if (enemy_objp->hull_strength / num_attacking < 200.0f) {
5660                                 if (frand() < 0.75f) {
5661                                         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));
5662                                         swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(500);
5663                                         return;
5664                                 }
5665                         }
5666                         */
5667                 }
5668         }
5669
5670         set_primary_weapon_linkage(objp);
5671         
5672         // I think this will properly solve the problem
5673         // fire non-streaming weapons
5674         ship_fire_primary(objp, 0);
5675         
5676         // fire streaming weapons
5677         shipp->flags |= SF_TRIGGER_DOWN;
5678         ship_fire_primary(objp, 1);
5679         shipp->flags &= ~SF_TRIGGER_DOWN;
5680 }
5681
5682 //      --------------------------------------------------------------------------
5683 //      Return number of nearby enemy fighters.
5684 //      threshold is the distance within which a ship is considered near.
5685 //
5686 // input:       enemy_team_mask =>      teams that are considered as an enemy
5687 //                              pos                                     =>      world position to measure ship distances from
5688 //                              threshold                       =>      max distance from pos to be considered "near"
5689 //
5690 // exit:                number of ships within threshold units of pos
5691 int num_nearby_fighters(int enemy_team_mask, vector *pos, float threshold)
5692 {
5693         ship_obj        *so;
5694         object  *ship_objp;
5695         int             count = 0;
5696
5697         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5698
5699                 ship_objp = &Objects[so->objnum];
5700
5701                 if (Ships[ship_objp->instance].team & enemy_team_mask) {
5702                         if (Ship_info[Ships[ship_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) {
5703                                 if (vm_vec_dist_quick(pos, &ship_objp->pos) < threshold)
5704                                         count++;
5705                         }
5706                 }
5707         }
5708
5709         return count;
5710 }
5711
5712 //      --------------------------------------------------------------------------
5713 //      Select secondary weapon to fire.
5714 //      Currently, 1/16/98:
5715 //              If 0 secondary weapons available, return -1
5716 //              If 1 available, use it.
5717 //              If 2 or more, if the current weapon is one of them, stick with it, otherwise choose a random one.
5718 //      priority1 and priority2 are Weapon_info[] bitmasks such as WIF_HOMING_ASPECT.  If any weapon has any bit in priority1
5719 //      set, that weapon will be selected.  If not, apply to priority2.  If neither, return -1, meaning no weapon selected.
5720 //      Note, priorityX have default values of -1, meaning if not set, they will match any weapon.
5721 //      Return value:
5722 //              bank index
5723 //      Should do this:
5724 //              Favor aspect seekers when attacking small ships faraway.
5725 //              Favor rapid fire dumbfire when attacking a large ship.
5726 //              Ignore heat seekers because we're not sure how they'll work.
5727 void ai_select_secondary_weapon(object *objp, ship_weapon *swp, int priority1 = -1, int priority2 = -1)
5728 {
5729         int     num_weapon_types;
5730         int     weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
5731         int     i;
5732         int     ignore_mask;
5733         int     initial_bank;
5734
5735         initial_bank = swp->current_secondary_bank;
5736
5737         //      Ignore bombs unless one of the priorities asks for them to be selected.
5738         if (WIF_HUGE & (priority1 | priority2))
5739                 ignore_mask = 0;
5740         else
5741                 ignore_mask = WIF_HUGE;
5742
5743         if (!(WIF_BOMBER_PLUS & (priority1 | priority2)))
5744                 ignore_mask |= WIF_BOMBER_PLUS;
5745
5746 #ifndef NDEBUG
5747         for (i=0; i<MAX_WEAPON_TYPES; i++) {
5748                 weapon_id_list[i] = -1;
5749                 weapon_bank_list[i] = -1;
5750         }
5751 #endif
5752
5753         //      Stuff weapon_bank_list with bank index of available weapons.
5754         num_weapon_types = get_available_secondary_weapons(objp, weapon_id_list, weapon_bank_list);
5755
5756         int     priority2_index = -1;
5757
5758         for (i=0; i<num_weapon_types; i++) {
5759                 int     wi_flags;
5760
5761                 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5762                 if (!(wi_flags & ignore_mask)) {                                        //      Maybe bombs are illegal.
5763                         if (wi_flags & priority1) {
5764                                 swp->current_secondary_bank = weapon_bank_list[i];                              //      Found first priority, return it.
5765                                 break;
5766                         } else if (wi_flags & priority2)
5767                                 priority2_index = weapon_bank_list[i];  //      Found second priority, but might still find first priority.
5768                 }
5769         }
5770
5771         //      If didn't find anything above, then pick any secondary weapon.
5772         if (i == num_weapon_types) {
5773                 swp->current_secondary_bank = priority2_index;  //      Assume we won't find anything.
5774                 if (priority2_index == -1) {
5775                         for (i=0; i<num_weapon_types; i++) {
5776                                 int     wi_flags;
5777
5778                                 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5779                                 if (!(wi_flags & ignore_mask)) {                                        //      Maybe bombs are illegal.
5780                                         if (swp->secondary_bank_ammo[i] > 0) {
5781                                                 swp->current_secondary_bank = i;
5782                                                 break;
5783                                         }
5784                                 }
5785                         }
5786                 }
5787         }
5788
5789         //      If switched banks, force reacquisition of aspect lock.
5790         if (swp->current_secondary_bank != initial_bank) {
5791                 ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
5792                 
5793                 aip->aspect_locked_time = 0.0f;
5794                 aip->current_target_is_locked = 0;
5795         }
5796
5797
5798         ship_secondary_changed(&Ships[objp->instance]); // AL: let multiplayer know if secondary bank has changed
5799         // nprintf(("AI", "Ship %s selected weapon %s\n", Ships[objp->instance].ship_name, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
5800 }
5801
5802 //      Return number of objects homing on object *target_objp
5803 int compute_num_homing_objects(object *target_objp)
5804 {
5805         object  *objp;
5806         int             count = 0;
5807
5808         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
5809                 if (objp->type == OBJ_WEAPON) {
5810                         if (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_HOMING) {
5811                                 if (Weapons[objp->instance].homing_object == target_objp) {
5812                                         count++;
5813                                 }
5814                         }
5815                 }
5816         }
5817
5818         return count;
5819 }
5820
5821 //      Object *firing_objp just fired weapon weapon_index (index in Weapon_info).
5822 //      If it's a shockwave weapon, tell your team about it!
5823 void ai_maybe_announce_shockwave_weapon(object *firing_objp, int weapon_index)
5824 {
5825         if ((firing_objp->type == OBJ_SHIP) && (Weapon_info[weapon_index].shockwave_speed > 0.0f)) {
5826                 ship_obj        *so;
5827                 int             firing_ship_team;
5828
5829                 firing_ship_team = Ships[firing_objp->instance].team;
5830
5831                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5832                         object  *A = &Objects[so->objnum];
5833                         Assert(A->type == OBJ_SHIP);
5834
5835                         if (Ships[A->instance].team == firing_ship_team) {
5836                                 ai_info *aip = &Ai_info[Ships[A->instance].ai_index];
5837                                 // AL 1-5-98: only avoid shockwave if not docked or repairing
5838                                 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
5839                                         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_WEAPON;
5840                                 }
5841                         }
5842                 }
5843         }
5844 }
5845
5846 //      Return total payload of all incoming missiles.
5847 float compute_incoming_payload(object *target_objp)
5848 {
5849         missile_obj     *mo;
5850         float                   payload = 0.0f;
5851
5852         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
5853                 object  *objp;
5854
5855                 objp = &Objects[mo->objnum];
5856                 Assert(objp->type == OBJ_WEAPON);
5857                 if (Weapons[objp->instance].homing_object == target_objp) {
5858                         payload += Weapon_info[Weapons[objp->instance].weapon_info_index].damage;
5859                 }
5860         }
5861
5862         return payload;
5863 }
5864
5865 //      --------------------------------------------------------------------------
5866 //      Return true if OK for *aip to fire its current weapon at its current target.
5867 //      Only reason this function returns false is:
5868 //              weapon is a homer
5869 //              targeted at player
5870 //                      OR:     player has too many homers targeted at him
5871 //                                      Missiontime in that dead zone in which can't fire at this player
5872 //      Note: If player is attacking a ship, that ship is allowed to fire at player.  Otherwise, we get in a situation in which
5873 //      player is attacking a large ship, but that large ship is not defending itself with missiles.
5874 int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip)
5875 {
5876         int     num_homers = 0;
5877         object  *tobjp = &Objects[target_objnum];
5878
5879         if (target_objnum > -1) {
5880                 // AL 3-4-98: Ensure objp target is a ship first 
5881                 if ( tobjp->type == OBJ_SHIP ) {
5882
5883                         // should not get this far. check if ship is protected from beam and weapon is type beam
5884                         if ( (wip->wi_flags & WIF_BEAM) && (tobjp->flags & OF_BEAM_PROTECTED) ) {
5885                                 Int3();
5886                                 return 0;
5887                         }
5888                         if (Ship_info[Ships[tobjp->instance].ship_info_index].flags & SIF_SMALL_SHIP) {
5889                                 num_homers = compute_num_homing_objects(&Objects[target_objnum]);
5890                         }
5891                 }
5892
5893                 //      If player, maybe fire based on Skill_level and number of incoming weapons.
5894                 //      If non-player, maybe fire based on payload of incoming weapons.
5895                 if (wip->wi_flags & WIF_HOMING) {
5896                         if ((target_objnum > -1) && (tobjp->flags & OF_PLAYER_SHIP)) {
5897                                 if (Ai_info[Ships[tobjp->instance].ai_index].target_objnum != objnum) {
5898                                         //      Don't allow AI ships to fire at player for fixed periods of time based on skill level.
5899                                         //      With 5 skill levels, at Very Easy, they fire in 1/7 of every 10 second interval.
5900                                         //      At Easy, 2/7...at Expert, 5/7
5901                                         int t = ((Missiontime /(65536*10)) ^ target_objnum ^ 0x01) % (NUM_SKILL_LEVELS+2);
5902                                         if (t > Game_skill_level) {
5903                                                 //nprintf(("AI", "Not OK to fire homer at time thing %i\n", t));
5904                                                 return 0;
5905                                         }
5906                                 }
5907                                 //nprintf(("AI", " IS OK to fire homer at time thing %i ***\n", t));
5908                                 int     swarmers = 0;
5909                                 if (wip->wi_flags & WIF_SWARM)
5910                                         swarmers = 2;   //      Note, always want to be able to fire swarmers if no currently incident homers.
5911                                 if (Max_allowed_player_homers[Game_skill_level] < num_homers + swarmers) {
5912                                         return 0;
5913                                 }
5914                         } else if (num_homers > 3) {
5915                                 float   incoming_payload;
5916
5917                                 incoming_payload = compute_incoming_payload(&Objects[target_objnum]);
5918
5919                                 if (incoming_payload > tobjp->hull_strength) {
5920                                         return 0;
5921                                 }
5922                         }
5923                 }
5924         }
5925
5926         return 1;
5927 }
5928
5929 //      --------------------------------------------------------------------------
5930 //      Fire a secondary weapon.
5931 //      Maybe choose to fire a different one.
5932 //      priority1 and priority2 are optional parameters with defaults = -1
5933 int ai_fire_secondary_weapon(object *objp, int priority1, int priority2)
5934 {
5935         ship_weapon *swp;
5936         ship    *shipp;
5937         ship_info *sip;
5938         int             current_bank;
5939         int             rval = 0;
5940
5941 #ifndef NDEBUG
5942         if (!Ai_firing_enabled)
5943                 return rval;
5944 #endif
5945
5946         Assert( objp != NULL );
5947         Assert(objp->type == OBJ_SHIP);
5948         shipp = &Ships[objp->instance];
5949         swp = &shipp->weapons;
5950
5951         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5952         sip = &Ship_info[shipp->ship_info_index];
5953
5954         //      Select secondary weapon.
5955         current_bank = swp->current_secondary_bank; //ai_select_secondary_weapon(objp, swp, priority1, priority2);
5956
5957         //nprintf(("AI", "Frame %i: Current bank = %i, ammo remaining = %i\n", Framecount, current_bank, swp->secondary_bank_ammo[current_bank]));
5958         if (current_bank == -1) {
5959                 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5960                 return rval;
5961         }
5962
5963         Assert(current_bank < shipp->weapons.num_secondary_banks);
5964
5965         weapon_info     *wip = &Weapon_info[shipp->weapons.secondary_bank_weapons[current_bank]];
5966
5967         if ((wip->wi_flags & WIF_HOMING_ASPECT) && (!Ai_info[shipp->ai_index].current_target_is_locked)) {
5968                 //nprintf(("AI", "Not firing secondary weapon because not aspect locked.\n"));
5969                 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
5970         } else if ((wip->wi_flags & WIF_BOMB) || (vm_vec_dist_quick(&objp->pos, &En_objp->pos) > 50.0f)) {
5971                 //      This might look dumb, firing a bomb even if closer than 50 meters, but the reason is, if you're carrying
5972                 //      bombs, delivering them is probably more important than surviving.
5973                 ai_info *aip;
5974
5975                 aip = &Ai_info[shipp->ai_index];
5976                 
5977                 //      Note, maybe don't fire if firing at player and any homers yet fired.
5978                 //      Decreasing chance to fire the more homers are incoming on player.
5979                 if (check_ok_to_fire(OBJ_INDEX(objp), aip->target_objnum, wip)) {
5980                         if (ship_fire_secondary(objp)) {
5981                                 rval = 1;
5982                                 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5983                                 //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));
5984                         }
5985
5986                 } else {
5987                         swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5988                 }
5989         }
5990
5991         return rval;
5992 }
5993
5994 //      Return true if it looks like obj1, if continuing to move along current vector, will
5995 //      collide with obj2.
5996 int might_collide_with_ship(object *obj1, object *obj2, float dot_to_enemy, float dist_to_enemy, float duration)
5997 {
5998         if (obj1->phys_info.speed * duration + 2*(obj1->radius + obj2->radius) > dist_to_enemy)
5999                 if (dot_to_enemy > 0.8f - 2*(obj1->radius + obj2->radius)/dist_to_enemy)
6000                         return objects_will_collide(obj1, obj2, duration, 2.0f);
6001
6002 //              BABY - 
6003 //              CONDITION 1, dist_to_enemy < o1_rad + o2_rad + (obj1.speed + obj2.speed) * time + 50
6004         
6005         return 0;
6006
6007 }
6008
6009 //      --------------------------------------------------------------------------
6010 //      Return true if ship *objp firing a laser believes it will hit a teammate.
6011 int might_hit_teammate(object *firing_objp)
6012 {
6013         int             team;
6014         object  *objp;
6015         ship_obj        *so;
6016
6017         team = Ships[firing_objp->instance].team;
6018
6019         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6020                 objp = &Objects[so->objnum];
6021                 if (Ships[objp->instance].team == team) {
6022                         float           dist, dot;
6023                         vector  vec_to_objp;
6024
6025                         vm_vec_sub(&vec_to_objp, &firing_objp->pos, &objp->pos);
6026                         dist = vm_vec_mag_quick(&vec_to_objp);
6027                         dot = vm_vec_dot(&firing_objp->orient.v.fvec, &vec_to_objp)/dist;
6028                         if (might_collide_with_ship(firing_objp, objp, dot, dist, 2.0f))
6029                                 return 1;
6030                 }
6031         }
6032
6033         return 0;
6034
6035 }
6036
6037 //int   Team_not_fire_count=0, Team_hit_count = 0;
6038
6039 void render_all_ship_bay_paths(object *objp)
6040 {
6041         int             i,j,color;
6042         ship            *sp = &Ships[objp->instance];
6043         polymodel       *pm;
6044         model_path      *mp;
6045
6046         pm = model_get(sp->modelnum);
6047         vector  global_path_point;
6048         vertex  v, prev_vertex;
6049
6050         if ( pm->ship_bay == NULL )
6051                 return;
6052
6053         for ( i = 0; i < pm->ship_bay->num_paths; i++ ) {
6054                 mp = &pm->paths[pm->ship_bay->paths[i]];
6055
6056                 for ( j = 0; j < mp->nverts; j++ ) {
6057                         vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6058                         vm_vec_add2(&global_path_point, &objp->pos);
6059                         g3_rotate_vertex(&v, &global_path_point);
6060                         color = 255 - j*50;
6061                         if ( color < 50 ) 
6062                                 color = 100;
6063                         gr_set_color(0, color, 0);
6064
6065                         if ( j == mp->nverts-1 ) {
6066                                 gr_set_color(255, 0, 0);
6067                         }
6068
6069                         g3_draw_sphere( &v, 1.5f);
6070
6071                         if ( j > 0 )
6072                                 g3_draw_line(&v, &prev_vertex);
6073
6074                         prev_vertex = v;
6075         
6076                 }
6077         }
6078 }
6079
6080 // debug function to show all path points associated with an object
6081 void render_all_subsys_paths(object *objp)
6082 {
6083         int             i,j,color;
6084         ship            *sp = &Ships[objp->instance];
6085         polymodel       *pm;
6086         model_path      *mp;
6087
6088         pm = model_get(sp->modelnum);
6089         vector  global_path_point;
6090         vertex  v, prev_vertex;
6091
6092         if ( pm->ship_bay == NULL )
6093                 return;
6094
6095         for ( i = 0; i < pm->n_paths; i++ ) {
6096                 mp = &pm->paths[i];
6097                 for ( j = 0; j < mp->nverts; j++ ) {
6098                         vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6099                         vm_vec_add2(&global_path_point, &objp->pos);
6100                         g3_rotate_vertex(&v, &global_path_point);
6101                         color = 255 - j*50;
6102                         if ( color < 50 ) 
6103                                 color = 100;
6104                         gr_set_color(0, color, 0);
6105
6106                         if ( j == mp->nverts-1 ) {
6107                                 gr_set_color(255, 0, 0);
6108                         }
6109
6110                         g3_draw_sphere( &v, 1.5f);
6111
6112                         if ( j > 0 )
6113                                 g3_draw_line(&v, &prev_vertex);
6114
6115                         prev_vertex = v;
6116                 }
6117         }
6118 }
6119
6120 void render_path_points(object *objp)
6121 {
6122         ship            *shipp = &Ships[objp->instance];
6123         ai_info *aip = &Ai_info[shipp->ai_index];
6124         object  *dobjp;
6125         polymodel       *pm;
6126
6127         render_all_subsys_paths(objp);
6128         render_all_ship_bay_paths(objp);
6129
6130         if (aip->goal_objnum < 0)
6131                 return;
6132
6133         dobjp = &Objects[aip->goal_objnum];
6134         pm = model_get(Ships[dobjp->instance].modelnum);
6135         vector  dock_point, global_dock_point;
6136         vertex  v;
6137
6138         ship_model_start(&Objects[aip->goal_objnum]);
6139         if (pm->n_docks) {
6140                 dock_point = pm->docking_bays[0].pnt[0];
6141                 model_find_world_point(&global_dock_point, &dock_point, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6142                 g3_rotate_vertex(&v, &global_dock_point);
6143                 gr_set_color(255, 255, 255);
6144                 g3_draw_sphere( &v, 1.5f);
6145         }
6146
6147         if (aip->path_start != -1) {
6148                 vertex          prev_vertex;
6149                 pnode                   *pp = &Path_points[aip->path_start];
6150                 int                     num_points = aip->path_length;
6151                 int                     i;
6152
6153                 for (i=0; i<num_points; i++) {
6154                         vertex  v0;
6155
6156                         g3_rotate_vertex( &v0, &pp->pos );
6157
6158                         gr_set_color(0, 128, 96);
6159                         if (i != 0)
6160                                 g3_draw_line(&v0, &prev_vertex);
6161
6162                         if (pp-Path_points == aip->path_cur)
6163                                 gr_set_color(255,255,0);
6164                         
6165                         g3_draw_sphere( &v0, 4.5f);
6166
6167                         //      Connect all the turrets that can fire upon this point to this point.
6168 /*                      if (0) { //pp->path_index != -1) {
6169                                 model_path      *pmp;
6170                                 mp_vert         *pmpv;
6171
6172                                 get_base_path_info(pp->path_index, aip->goal_objnum, &pmp, &pmpv);
6173
6174                                 if (pmpv->nturrets) {
6175                                         for (int j = 0; j<pmpv->nturrets; j++) {
6176                                                 vertex  v1;
6177                                                 vector  turret_pos;
6178                                                 ship_subsys     *ssp;
6179
6180                                                 ssp = ship_get_indexed_subsys(&Ships[Objects[aip->goal_objnum].instance], pmpv->turret_ids[j]);
6181
6182 model_find_world_point(&turret_pos, &ssp->system_info->pnt, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6183         
6184                                                 g3_rotate_vertex(&v1, &turret_pos);
6185                                                 gr_set_color(255, 255, 0);
6186                                                 g3_draw_line(&v0, &v1);
6187                                                 g3_draw_sphere( &v1, 1.5f);
6188                                         }
6189                                 }
6190                         } */
6191
6192                         prev_vertex = v0;
6193
6194                         pp++;
6195                 }
6196         }
6197
6198         ship_model_stop(&Objects[aip->goal_objnum]);
6199 }
6200
6201 // Return the distance that the current AI weapon will travel
6202 float ai_get_weapon_dist(ship_weapon *swp)
6203 {
6204         int     bank_num, weapon_num;
6205
6206         bank_num = swp->current_primary_bank;
6207         weapon_num = swp->primary_bank_weapons[bank_num];
6208
6209         //      If weapon_num is illegal, return a reasonable value.  A valid weapon
6210         //      will get selected when this ship tries to fire.
6211         if (weapon_num == -1) {
6212                 // Int3();
6213                 return 1000.0f;
6214         }
6215
6216         return Weapon_info[weapon_num].max_speed * Weapon_info[weapon_num].lifetime;
6217 }
6218
6219 float ai_get_weapon_speed(ship_weapon *swp)
6220 {
6221         int     bank_num, weapon_num;
6222
6223         bank_num = swp->current_primary_bank;
6224         if (bank_num < 0)
6225                 return 100.0f;
6226
6227         weapon_num = swp->primary_bank_weapons[bank_num];
6228
6229         if (weapon_num == -1) {
6230                 //Int3();
6231                 return 100.0f;
6232         }
6233
6234         return Weapon_info[weapon_num].max_speed;
6235 }
6236
6237 //      Compute the predicted position of a ship to be fired upon from a turret.
6238 //      This is based on position of firing gun, enemy object, weapon speed and skill level constraints.
6239 //      Return value in *predicted_enemy_pos.
6240 //      Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6241 //      *pobjp          object firing the weapon
6242 //      *eobjp          object being fired upon
6243 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)
6244 {
6245         ship    *shipp = &Ships[pobjp->instance];
6246         float   range_time;
6247
6248         //weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6249
6250         if (weapon_speed < 1.0f)
6251                 weapon_speed = 1.0f;
6252
6253         range_time = 2.0f;
6254
6255         //      Make it take longer for enemies to get player's allies in range based on skill level.
6256         if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team)
6257                 range_time += In_range_time[Game_skill_level];
6258
6259         //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6260
6261         if (time_enemy_in_range < range_time) {
6262                 float   dist;
6263
6264                 dist = vm_vec_dist_quick(&pobjp->pos, enemy_pos);
6265                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, time_enemy_in_range * dist/weapon_speed);
6266         } else {
6267                 float   collision_time, scale;
6268                 vector  rand_vec;
6269                 ai_info *aip = &Ai_info[shipp->ai_index];
6270
6271                 collision_time = compute_collision_time(enemy_pos, enemy_vel, gun_pos, weapon_speed);
6272
6273                 if (collision_time == 0.0f){
6274                         collision_time = 100.0f;
6275                 }
6276
6277                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, collision_time);
6278                 if (time_enemy_in_range > 2*range_time){
6279                         scale = (1.0f - aip->ai_accuracy) * 4.0f;
6280                 } else {
6281                         scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - time_enemy_in_range/(2*range_time)));
6282                 }               
6283
6284                 static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6285
6286                 vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6287                 G_collision_time = collision_time;
6288                 G_fire_pos = *gun_pos;
6289         }
6290
6291         G_predicted_pos = *predicted_enemy_pos;
6292 }
6293
6294 //      Compute the predicted position of a ship to be fired upon.
6295 //      This is based on current position of firing object, enemy object, relative position of gun on firing object,
6296 //      weapon speed and skill level constraints.
6297 //      Return value in *predicted_enemy_pos.
6298 //      Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6299 void set_predicted_enemy_pos(vector *predicted_enemy_pos, object *pobjp, object *eobjp, ai_info *aip)
6300 {
6301         float   weapon_speed, range_time;
6302         ship    *shipp = &Ships[pobjp->instance];
6303
6304         weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6305         weapon_speed = max(weapon_speed, 1.0f);         // set not less than 1
6306
6307         range_time = 2.0f;
6308
6309         //      Make it take longer for enemies to get player's allies in range based on skill level.
6310         // but don't bias team v. team missions
6311         if ( !((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) ) {
6312                 if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team) {
6313                         range_time += In_range_time[Game_skill_level];
6314                 }
6315         }
6316         //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6317
6318         if (aip->time_enemy_in_range < range_time) {
6319                 float   dist;
6320
6321                 dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6322                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, aip->time_enemy_in_range * dist/weapon_speed);
6323         } else {
6324                 float   collision_time;
6325                 vector  gun_pos, pnt;
6326                 polymodel *po = model_get( Ship_info[shipp->ship_info_index].modelnum );
6327
6328                 //      Compute position of gun in absolute space and use that as fire position.
6329                 if(po->gun_banks != NULL){
6330                         pnt = po->gun_banks[0].pnt[0];
6331                 } else {
6332                         pnt = Objects[shipp->objnum].pos;
6333                 }
6334                 vm_vec_unrotate(&gun_pos, &pnt, &pobjp->orient);
6335                 vm_vec_add2(&gun_pos, &pobjp->pos);
6336
6337                 collision_time = compute_collision_time(&eobjp->pos, &eobjp->phys_info.vel, &gun_pos, weapon_speed);
6338
6339                 if (collision_time == 0.0f) {
6340                         collision_time = 100.0f;
6341                 }
6342
6343                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, collision_time);
6344
6345                 // set globals
6346                 G_collision_time = collision_time;
6347                 G_fire_pos = gun_pos;
6348         }
6349
6350         // Now add error terms (1) regular aim (2) EMP (3) stealth
6351         float scale = 0.0f;
6352         vector rand_vec;
6353
6354         // regular skill level error in aim
6355         if (aip->time_enemy_in_range > 2*range_time) {
6356                 scale = (1.0f - aip->ai_accuracy) * 4.0f;
6357         } else {
6358                 scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - aip->time_enemy_in_range/(2*range_time)));
6359         }
6360
6361         // if this ship is under the effect of an EMP blast, throw his aim off a bit
6362         if (shipp->emp_intensity > 0.0f) {
6363                 // never go lower than 1/2 of the EMP effect max, otherwise things aren't noticeable
6364                 scale += (MAX_EMP_INACCURACY * (shipp->emp_intensity < 0.5f ? 0.5f : shipp->emp_intensity));
6365                 mprintf(("AI miss scale factor (EMP) %f\n",scale));
6366         }
6367
6368         // if stealthy ship, throw his aim off, more when farther away and when dot is small
6369         if ( aip->ai_flags & AIF_STEALTH_PURSIUT ) {
6370                 float dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6371                 vector temp;
6372                 vm_vec_sub(&temp, &eobjp->pos, &pobjp->pos);
6373                 vm_vec_normalize_quick(&temp);
6374                 float dot = vm_vec_dotprod(&temp, &pobjp->orient.v.fvec);
6375                 float st_err = 3.0f * (1.4f - dot) * (1.0f + dist / (get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST)) * (1 - aip->ai_accuracy);
6376                 scale += st_err;
6377                 // mprintf(("error term: %.1f, total %.1f, dot %.3f\n", st_err, scale, dot));
6378         }
6379
6380         // get a random vector that changes slowly over time (1x / sec)
6381         static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6382
6383         vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6384
6385         // set global
6386         G_predicted_pos = *predicted_enemy_pos;
6387 }
6388
6389 //      Handler of submode for Chase.  Go into a continuous turn for awhile.
6390 void ai_chase_ct()
6391 {
6392         vector          tvec;
6393         ship_info       *sip;
6394         ai_info         *aip;
6395
6396         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6397         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6398         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6399         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6400
6401         //      Make a continuous turn towards any combination of possibly negated
6402         // up and right vectors.
6403         tvec = Pl_objp->pos;
6404
6405         if (aip->submode_parm0 & 0x01)
6406                 vm_vec_add2(&tvec, &Pl_objp->orient.v.rvec);
6407         if (aip->submode_parm0 & 0x02)
6408                 vm_vec_sub2(&tvec, &Pl_objp->orient.v.rvec);
6409         if (aip->submode_parm0 & 0x04)
6410                 vm_vec_add2(&tvec, &Pl_objp->orient.v.uvec);
6411         if (aip->submode_parm0 & 0x08)
6412                 vm_vec_sub2(&tvec, &Pl_objp->orient.v.uvec);
6413
6414         //      Detect degenerate cases that cause tvec to be same as player pos.
6415         if (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
6416                 aip->submode_parm0 &= 0x05;
6417                 if (aip->submode_parm0 == 0)
6418                         aip->submode_parm0 = 1;
6419                 vm_vec_add2(&tvec, &Pl_objp->orient.v.rvec);
6420         }
6421
6422         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6423         accelerate_ship(aip, 1.0f);
6424 }
6425
6426 //      ATTACK submode handler for chase mode.
6427 void ai_chase_eb(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
6428 {
6429         vector  _pep;
6430         float           dot_to_enemy, dot_from_enemy;
6431
6432         compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
6433
6434         //      If we're trying to slow down to get behind, then point to turn towards is different.
6435         _pep = *predicted_enemy_pos;
6436         if ((dot_to_enemy > dot_from_enemy + 0.1f) || (dot_to_enemy > 0.9f))
6437                 vm_vec_scale_add(&_pep, &Pl_objp->pos, &En_objp->orient.v.fvec, 100.0f);
6438
6439         ai_turn_towards_vector(&_pep, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6440
6441         accelerate_ship(aip, 0.0f);
6442 }
6443
6444 //      Return time until weapon_objp might hit ship_objp.
6445 //      Assumes ship_objp is not moving.
6446 //      Returns negative time if not going to hit.
6447 //      This is a very approximate function, but is pretty fast.
6448 float ai_endangered_time(object *ship_objp, object *weapon_objp)
6449 {
6450         float           to_dot, from_dot, dist;
6451
6452         dist = compute_dots(ship_objp, weapon_objp, &to_dot, &from_dot);
6453
6454         //      Note, this is bogus.  It assumes only the weapon is moving.
6455         //      Only proceed if weapon sort of pointing at object and object pointing towards or away from weapon
6456         //      (Ie, if object moving at right angle to weapon, just continue for now...)
6457         if (weapon_objp->phys_info.speed < 1.0f)
6458                 return dist + 1.0f;
6459         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))
6460                 return dist / weapon_objp->phys_info.speed;
6461         else
6462                 return -1.0f;
6463 }
6464
6465 //      Return time until danger weapon could hit this ai object.
6466 //      Return negative time if not endangered.
6467 float ai_endangered_by_weapon(ai_info *aip)
6468 {
6469         object  *weapon_objp;
6470
6471         if (aip->danger_weapon_objnum == -1) {
6472                 return -1.0f;
6473         }
6474
6475         weapon_objp = &Objects[aip->danger_weapon_objnum];
6476
6477         if (weapon_objp->signature != aip->danger_weapon_signature) {
6478                 aip->danger_weapon_objnum = -1;
6479                 return -1.0f;
6480         }
6481
6482         return ai_endangered_time(&Objects[Ships[aip->shipnum].objnum], weapon_objp);
6483 }
6484
6485 //      Return true if this ship is near full strength.
6486 int ai_near_full_strength(object *objp, ship_info *sip)
6487 {
6488         return (objp->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp)/sip->shields > 0.8f);
6489 }
6490                                 
6491 //      Set acceleration while in attack mode.
6492 void attack_set_accel(ai_info *aip, float dist_to_enemy, float dot_to_enemy, float dot_from_enemy)
6493 {
6494         float   speed_ratio;
6495
6496         if (En_objp->phys_info.speed > 1.0f)
6497                 speed_ratio = Pl_objp->phys_info.speed/En_objp->phys_info.speed;
6498         else
6499                 speed_ratio = 5.0f;
6500
6501         //      Sometimes, told to attack slowly.  Allows to get in more hits.
6502         if (aip->ai_flags & AIF_ATTACK_SLOWLY) {
6503                 if ((dist_to_enemy > 200.0f) && (dist_to_enemy < 800.0f)) {
6504                         if ((dot_from_enemy < 0.9f) || ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
6505                                 //nprintf(("AI", " slowly "));
6506                                 accelerate_ship(aip, max(1.0f - (dist_to_enemy-200.0f)/600.0f, 0.1f));
6507                                 return;
6508                         }
6509                 } else
6510                         aip->ai_flags &= ~AIF_ATTACK_SLOWLY;
6511         }
6512
6513         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) {
6514                 //nprintf(("AI", "1"));
6515                 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6516                         if (dist_to_enemy > 800.0f) {
6517                                 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6518                                         float percent_left;
6519                                         ship    *shipp;
6520                                         ship_info *sip;
6521
6522                                         shipp = &Ships[Pl_objp->instance];
6523                                         sip = &Ship_info[shipp->ship_info_index];
6524
6525                                         if (sip->afterburner_fuel_capacity > 0.0f) {
6526                                                 percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
6527                                                 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
6528                                                         afterburners_start(Pl_objp);
6529                                                         aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
6530                                                 }
6531                                         }
6532                                 }
6533                         }
6534                 }
6535
6536                 accelerate_ship(aip, 1.0f);
6537         } else if ((Missiontime - aip->last_hit_time > F1_0*7)
6538                 && (En_objp->phys_info.speed < 10.0f) 
6539                 && (dist_to_enemy > 25.0f) 
6540                 && (dot_to_enemy > 0.8f)
6541                 && (dot_from_enemy < 0.8f)) {
6542                 accelerate_ship(aip, 0.0f);             //      No one attacking us, so don't need to move.
6543         } else if ((dot_from_enemy < 0.25f) && (dot_to_enemy > 0.5f)) {
6544                 set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed);
6545         } else if (Pl_objp->phys_info.speed < 15.0f) {
6546                 accelerate_ship(aip, 1.0f);
6547         } else if (Pl_objp->phys_info.speed > En_objp->phys_info.speed - 1.0f) {
6548                 if (dot_from_enemy > 0.75f)
6549                         accelerate_ship(aip, 1.0f);
6550                 else
6551                         set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed*0.75f + 3.0f);
6552         } else {
6553                 change_acceleration(aip, 0.5f);
6554         }
6555 }
6556
6557 //      Pl_objp (aip) tries to get behind En_objp.
6558 //      New on 2/21/98: If this ship can move backwards and slide, maybe do that to get behind.
6559 void get_behind_ship(ai_info *aip, ship_info *sip, float dist_to_enemy)
6560 {
6561         vector  new_pos;
6562         float           dot;
6563         vector  vec_from_enemy;
6564         float           dist;
6565
6566         dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
6567
6568         vm_vec_scale_add(&new_pos, &En_objp->pos, &En_objp->orient.v.fvec, -100.0f);            //      Pick point 100 units behind.
6569         ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6570
6571         dot = vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.fvec);
6572
6573         if (dot > 0.25f) {
6574                 accelerate_ship(aip, 1.0f);
6575         } else {
6576                 accelerate_ship(aip, (dot + 1.0f)/2.0f);
6577         }
6578 }
6579
6580 int avoid_player(object *objp, vector *goal_pos)
6581 {
6582         maybe_avoid_player(Pl_objp, goal_pos);
6583         ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
6584
6585         if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6586                 ship_info *sip = &Ship_info[Ships[objp->instance].ship_info_index];
6587
6588                 if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6589                         ai_turn_towards_vector(&aip->avoid_goal_point, objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6590                         accelerate_ship(aip, 0.5f);
6591                         return 1;
6592                 }
6593         }
6594
6595         return 0;
6596 }
6597
6598 //      Determine if a cylinder of width radius from p0 to p1 will collide with big_objp.
6599 //      If so, stuff *collision_point.
6600 int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6601 {
6602         mc_info mc;
6603
6604         mc.model_num = Ships[big_objp->instance].modelnum;              // Fill in the model to check
6605         mc.orient = &big_objp->orient;                  // The object's orient
6606         mc.pos = &big_objp->pos;                                        // The object's position
6607         mc.p0 = p0;                                                                             // Point 1 of ray to check
6608         mc.p1 = p1;
6609         mc.flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE | MC_SUBMODEL;                                  // flags
6610
6611         mc.radius = radius;
6612
6613         // Only check the 2nd lowest hull object
6614         polymodel *pm = model_get(Ships[big_objp->instance].modelnum);
6615         mc.submodel_num = pm->detail[0]; //pm->submodel->num_details-2];
6616         model_collide(&mc);
6617
6618         if (mc.num_hits)
6619                 *collision_point = mc.hit_point_world;
6620
6621         return mc.num_hits;
6622 }
6623
6624 //      Return true/false if *objp will collide with *big_objp
6625 //      Stuff distance in *distance to collision point if *objp will collide with *big_objp within delta_time seconds.
6626 //      Global collision point stuffed in *collision_point
6627 int will_collide_with_big_ship(object *objp, vector *goal_point, object *big_objp, vector *collision_point, float delta_time)
6628 {
6629         float           radius;
6630         vector  end_pos;
6631
6632         radius = big_objp->radius + delta_time * objp->phys_info.speed;
6633
6634         if (vm_vec_dist_quick(&big_objp->pos, &objp->pos) > radius) {
6635                 return 0;
6636         }
6637
6638         if (goal_point == NULL) {
6639                 vm_vec_scale_add(&end_pos, &objp->pos, &objp->phys_info.vel, delta_time);                                       // Point 2 of ray to check
6640         } else {
6641                 end_pos = *goal_point;
6642         }
6643
6644         return will_collide_pp(&objp->pos, &end_pos, objp->radius, big_objp, collision_point);
6645 }
6646
6647 //      Return true if *objp is expected to collide with a large ship.
6648 //      Stuff global collision point in *collision_point.
6649 //      If *goal_point is not NULL, use that as the point towards which *objp will be flying.  Don't use *objp velocity
6650 //      *ignore_objp will typically be the target this ship is pursuing, either to attack or guard.  We don't want to avoid it.
6651 int will_collide_with_big_ship_all(object *objp, object *ignore_objp, vector *goal_point, vector *collision_point, float *distance, float delta_time)
6652 {
6653         ship_obj        *so;
6654         object  *big_objp;
6655         int             collision_obj_index = -1;
6656         float           min_dist = 999999.9f;
6657         float           collision_time = -1.0f;
6658
6659         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6660                 float   time = 0.0f;
6661                 big_objp = &Objects[so->objnum];
6662
6663                 if (big_objp == ignore_objp)
6664                         continue;
6665
6666                 if (Ship_info[Ships[big_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
6667                         vector  cur_collision_point;
6668                         float           cur_dist;
6669
6670                         if (will_collide_with_big_ship(objp, goal_point, big_objp, &cur_collision_point, delta_time)) {
6671
6672                                 cur_dist = vm_vec_dist(&cur_collision_point, &objp->pos);
6673
6674                                 if (cur_dist < min_dist) {
6675                                         min_dist = cur_dist;
6676                                         *collision_point = cur_collision_point;
6677                                         collision_time = time;
6678                                         collision_obj_index = OBJ_INDEX(big_objp);
6679                                 }
6680                         }
6681                 }
6682         }
6683
6684         *distance = min_dist;
6685         return collision_obj_index;
6686
6687 }
6688
6689 typedef struct {
6690         float           dist;
6691         int             collide;
6692         vector  pos;
6693 } sgoal;
6694
6695 //int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6696 //      Pick a point for *objp to fly towards to avoid a collision with *big_objp at *collision_point
6697 //      Return result in *avoid_pos
6698 void mabs_pick_goal_point(object *objp, object *big_objp, vector *collision_point, vector *avoid_pos)
6699 {
6700         matrix  mat1;
6701         sgoal           goals[4];
6702         vector  v2b;
6703
6704         vm_vec_normalized_dir(&v2b, collision_point, &objp->pos);
6705         vm_vector_2_matrix(&mat1, &v2b, NULL, NULL);
6706
6707         int     found = 0;
6708
6709         //      Try various scales, in 0.5f, 0.75f, 1.0f, 1.25f.
6710         //      First try 0.5f to see if we can find a point that near the center of the target ship, which presumably
6711         //      means less of a turn.
6712         //      Try going as far as 1.25f * radius.
6713         float   s;
6714         for (s=0.5f; s<1.3f; s += 0.25f) {
6715                 int     i;
6716                 for (i=0; i<4; i++) {
6717                         vector p = big_objp->pos;
6718                         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
6719                         float kr = big_objp->radius*s + objp->radius * ((OBJ_INDEX(objp) % 4) ^ 2)/4;
6720                         if (i&1)
6721                                 ku = -ku;
6722                         if (i&2)
6723                                 kr = -kr;
6724                         vm_vec_scale_add2(&p, &mat1.v.uvec, ku);
6725                         vm_vec_scale_add2(&p, &mat1.v.rvec, kr);
6726                         goals[i].pos = p;
6727                         goals[i].dist = vm_vec_dist_quick(&objp->pos, &p);
6728                         goals[i].collide = will_collide_pp(&objp->pos, &p, objp->radius, big_objp, collision_point);
6729                         if (!goals[i].collide)
6730                                 found = 1;
6731                 }
6732
6733                 //      If we found a point that doesn't collide, find the nearest one and make that the *avoid_pos.
6734                 if (found) {
6735                         float   min_dist = 9999999.9f;
6736                         int     min_index = -1;
6737
6738                         for (i=0; i<4; i++) {
6739                                 if (!goals[i].collide && (goals[i].dist < min_dist)) {
6740                                         min_dist = goals[i].dist;
6741                                         min_index = i;
6742                                 }
6743                         }
6744
6745                         Assert(i != -1);
6746                         if (i != -1) {
6747                                 *avoid_pos = goals[min_index].pos;
6748                                 return;
6749                         }
6750                 }
6751         }
6752
6753         //      Drat.  We tried and tried and could not find a point that did not cause a collision.
6754         //      Get this dump pilot far away from the problem ship.
6755         vector  away_vec;
6756         vm_vec_normalized_dir(&away_vec, &objp->pos, collision_point);
6757         vm_vec_scale_add(avoid_pos, &objp->pos, &away_vec, big_objp->radius*1.5f);
6758
6759 }
6760
6761 //      Return true if a large ship is being ignored.
6762 int maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vector *goal_point, float delta_time)
6763 {
6764         if (timestamp_elapsed(aip->avoid_check_timestamp)) {
6765                 float           distance;
6766                 vector  collision_point;
6767                 int             ship_num;
6768                 if ((ship_num = will_collide_with_big_ship_all(Pl_objp, ignore_objp, goal_point, &collision_point, &distance, delta_time)) != -1) {
6769                         aip->ai_flags |= AIF_AVOIDING_BIG_SHIP;
6770                         mabs_pick_goal_point(objp, &Objects[ship_num], &collision_point, &aip->avoid_goal_point);
6771                         float dist = vm_vec_dist_quick(&aip->avoid_goal_point, &objp->pos);
6772                         aip->avoid_check_timestamp = timestamp(2000 + min(1000, (int) (dist * 2.0f)));  //      Delay until check again is based on distance to avoid point.
6773                         aip->avoid_ship_num = ship_num;
6774                 } else {
6775                         aip->ai_flags &= ~AIF_AVOIDING_BIG_SHIP;
6776                         aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
6777                         aip->avoid_ship_num = -1;
6778                         aip->avoid_check_timestamp = timestamp(1500);
6779                 }
6780         }
6781         
6782         if (aip->ai_flags & AIF_AVOIDING_BIG_SHIP) {
6783                 ship_info *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6784
6785                 vector  v2g;
6786
6787                 ai_turn_towards_vector(&aip->avoid_goal_point, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6788                 vm_vec_normalized_dir(&v2g, &aip->avoid_goal_point, &Pl_objp->pos);
6789                 float dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
6790                 float d2 = (1.0f + dot) * (1.0f + dot);
6791                 accelerate_ship(aip, d2/4.0f);
6792                 return 1;
6793         }
6794
6795         return 0;
6796 }
6797
6798 //      Set desired right vector for ships flying towards another ship.
6799 //      Since this is governed only by vector to target, it causes ships to align bank and look less chaotic.
6800 void compute_desired_rvec(vector *rvec, vector *goal_pos, vector *cur_pos)
6801 {
6802         vector  v2e;
6803
6804         vm_vec_normalized_dir(&v2e, goal_pos, cur_pos);
6805         rvec->xyz.x = v2e.xyz.z;
6806         rvec->xyz.y = 0.0f;
6807         rvec->xyz.z = -v2e.xyz.x;
6808         if (vm_vec_mag_squared(rvec) < 0.001f)
6809                 rvec->xyz.y = 1.0f;
6810 }
6811
6812 // Handler for stealth find submode of Chase.
6813 void ai_stealth_find()
6814 {
6815         ai_info         *aip;
6816         ship_info       *sip;
6817
6818         vector new_pos, vec_to_enemy;
6819         float dist_to_enemy, dot_to_enemy, dot_from_enemy;
6820
6821         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6822         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6823         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6824         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6825
6826         // get time since last seen
6827         int delta_time = (timestamp() - aip->stealth_last_visible_stamp);
6828
6829         // if delta_time is really big, i'm real confused, start sweep
6830         if (delta_time > 10000) {
6831                 aip->submode_parm0 = SM_SF_BAIL;
6832         }
6833
6834         // guestimate new position
6835         vm_vec_scale_add(&new_pos, &aip->stealth_last_pos, &aip->stealth_velocity, (delta_time * 0.001f));
6836
6837         // if I think he's behind me, go to the goal point
6838         if ( aip->submode_parm0 == SM_SF_BEHIND ) {
6839                 new_pos = aip->goal_point;
6840         }
6841
6842         // check for collision with big ships
6843         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &new_pos, 10.0f)) {
6844                 // reset ai submode to chase
6845                 return;
6846         }
6847
6848         // if dist is near max and dot is close to 1, accel, afterburn
6849         vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6850         dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6851         dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.v.fvec);
6852
6853         // 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
6854         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) ) {
6855                 // do turn around)
6856                 vm_vec_scale_add(&aip->goal_point, &Pl_objp->pos, &Pl_objp->orient.v.fvec, -300.0f);
6857                 aip->submode_parm0 = SM_SF_BEHIND;
6858                 vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6859                 dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6860                 dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.v.fvec);
6861         }
6862
6863         if ( (dist_to_enemy > get_skill_stealth_dist_scaler()*STEALTH_MAX_VIEW_DIST) && (dot_to_enemy > 0.94f) ) {              // 20 degree half angle
6864                 // accelerate ship
6865                 accelerate_ship(aip, 1.0f);
6866
6867                 // engage afterburner
6868                 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6869                         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6870                                 afterburners_start(Pl_objp);
6871                                 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
6872                         }
6873                 }
6874
6875                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6876                 return;
6877         }
6878
6879         //      If enemy more than 500 meters away, all ships flying there will tend to match bank.
6880         //      They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
6881         //      to interpolate a matrix rather than just a vector.
6882         if (dist_to_enemy > 500.0f) {
6883                 vector  rvec;
6884                 compute_desired_rvec(&rvec, &new_pos, &Pl_objp->pos);
6885                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0, &rvec);
6886         } else {
6887                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6888         }
6889
6890         dot_from_enemy = -vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec);
6891
6892         attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
6893 }
6894
6895 // -----------------------------------------------------------------------------
6896 // try to find stealth ship by sweeping an area
6897 void ai_stealth_sweep()
6898 {
6899         ai_info         *aip;
6900         ship_info       *sip;
6901
6902         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6903         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6904         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6905         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6906
6907         vector goal_pt;
6908         vector forward, right, up;
6909         int lost_time;
6910
6911         // time since stealth last seen
6912         lost_time = (timestamp() - aip->stealth_last_visible_stamp);
6913
6914         // determine which pt to fly to in sweep by keeping track of parm0
6915         if (aip->submode_parm0 == SM_SS_SET_GOAL) {
6916
6917                 // don't make goal pt more than 2k from current pos
6918                 vm_vec_scale_add(&goal_pt, &aip->stealth_last_pos, &aip->stealth_velocity, (0.001f * lost_time));
6919
6920                 // make box size based on speed of stealth and expected time to intercept (keep box in range 200-500)
6921                 float box_size = vm_vec_mag_quick(&aip->stealth_velocity) * (0.001f * lost_time);
6922                 box_size = min(200.0f, box_size);
6923                 box_size = max(500.0f, box_size);
6924                 aip->stealth_sweep_box_size = box_size;
6925
6926                 aip->goal_point = goal_pt;
6927                 aip->submode_parm0 = SM_SS_BOX0;
6928         }
6929
6930         // GET UP, RIGHT, FORWARD FOR BOX based on stealth ship's velocity
6931         // if velocity changes in stealth mode, then ship is *seen*, and falls out of sweep mode
6932         // if stealth has no velocity make a velocity
6933         if ( vm_vec_mag_quick(&aip->stealth_velocity) < 1 ) {
6934                 vm_vec_rand_vec_quick(&aip->stealth_velocity);
6935         }
6936
6937         // get "right" vector for box
6938         vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_y_vector);
6939
6940         if ( vm_vec_mag_quick(&right) < 0.01 ) {
6941                 vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_z_vector);
6942         }
6943
6944         vm_vec_normalize_quick(&right);
6945
6946         // get forward for box
6947         vm_vec_copy_normalize_quick(&forward, &aip->stealth_velocity);
6948
6949         // get "up" for box
6950         vm_vec_crossprod(&up, &forward, &right);
6951         
6952         // lost far away ahead (do box)
6953         switch(aip->submode_parm0) {
6954         case SM_SS_BOX0:
6955                 goal_pt = aip->goal_point;
6956                 break;
6957
6958         // pt1 -U +R
6959         case SM_SS_LR:
6960                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
6961                 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
6962                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6963                 break;
6964
6965         // pt2 +U -R
6966         case SM_SS_UL:
6967                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
6968                 vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
6969                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6970                 break;
6971
6972         // pt3 back
6973         case SM_SS_BOX1:
6974                 goal_pt = aip->goal_point;
6975                 break;
6976
6977         // pt4 +U +R
6978         case SM_SS_UR:
6979                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
6980                 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
6981                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6982                 break;
6983
6984         // pt5 -U -R
6985         case SM_SS_LL:
6986                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
6987                 vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
6988                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6989                 break;
6990
6991         // pt6 back
6992         case SM_SS_BOX2:
6993                 goal_pt = aip->goal_point;
6994                 break;
6995
6996         default:
6997                 Int3();
6998
6999         }
7000
7001         // when close to goal_pt, update next goal pt
7002         float dist_to_goal = vm_vec_dist(&goal_pt, &Pl_objp->pos);
7003         if (dist_to_goal < 15) {
7004                 aip->submode_parm0++;
7005         }
7006
7007         // check for collision with big ship
7008         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &goal_pt, 10.0f)) {
7009                 // skip to the next pt on box
7010                 aip->submode_parm0++;
7011                 return;
7012         }
7013
7014         ai_turn_towards_vector(&goal_pt, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
7015
7016         float dot = 1.0f;
7017         if (dist_to_goal < 100) {
7018                 vector vec_to_goal;
7019                 vm_vec_normalized_dir(&vec_to_goal, &goal_pt, &Pl_objp->pos);
7020                 dot = vm_vec_dotprod(&vec_to_goal, &Pl_objp->orient.v.fvec);
7021         }
7022
7023         accelerate_ship(aip, 0.8f*dot);
7024 }
7025
7026 //      ATTACK submode handler for chase mode.
7027 void ai_chase_attack(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
7028 {
7029         int             start_bank;
7030         float           dot_to_enemy, dot_from_enemy; //, time_to_hit;
7031         float           bank_override = 0.0f;
7032
7033         if (avoid_player(Pl_objp, predicted_enemy_pos))
7034                 return;
7035
7036         compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
7037
7038         polymodel *po = model_get( sip->modelnum );
7039
7040         vector  *rel_pos;
7041         float           scale;
7042         vector  randvec;
7043         vector  new_pos;
7044
7045         start_bank = Ships[aip->shipnum].weapons.current_primary_bank;
7046         if (po->n_guns && start_bank != -1 ) {
7047                 rel_pos = &po->gun_banks[start_bank].pnt[0];
7048         } else
7049                 rel_pos = NULL;
7050
7051         //      If ship moving slowly relative to its size, then don't attack its center point.
7052         //      How far from center we attack is based on speed, size and distance to enemy
7053         if (En_objp->radius > En_objp->phys_info.speed) {
7054                 static_randvec(Pl_objp-Objects, &randvec);
7055                 scale = dist_to_enemy/(dist_to_enemy + En_objp->radius) * En_objp->radius;
7056                 scale *= 0.5f * En_objp->radius/(En_objp->phys_info.speed + En_objp->radius);   // scale downward by 1/2 to 1/4
7057                 vm_vec_scale_add(&new_pos, predicted_enemy_pos, &randvec, scale);
7058         } else
7059                 new_pos = *predicted_enemy_pos;
7060
7061         if (dist_to_enemy < 250.0f) {
7062                 if (dot_from_enemy > 0.7f) {
7063                         bank_override = Pl_objp->phys_info.speed;
7064                 }
7065         }
7066
7067         //      If enemy more than 500 meters away, all ships flying there will tend to match bank.
7068         //      They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
7069         //      to interpolate a matrix rather than just a vector.
7070         if (dist_to_enemy > 500.0f) {
7071                 vector  rvec;
7072                 compute_desired_rvec(&rvec, predicted_enemy_pos, &Pl_objp->pos);
7073                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0, &rvec);
7074         } else {
7075                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0);
7076         }
7077
7078         attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
7079 }
7080
7081 //      EVADE_SQUIGGLE submode handler for chase mode.
7082 //      Changed by MK on 5/5/97.
7083 //      Used to evade towards a point off the right or up vector.
7084 //      Now, evade straight away to try to get far away.
7085 //      The squiggling should protect against laser fire.
7086 void ai_chase_es(ai_info *aip, ship_info *sip)
7087 {
7088         vector  tvec;
7089         fix             timeslice;
7090         fix             scale;
7091         float           bank_override = 0.0f;
7092
7093         tvec = Pl_objp->pos;
7094
7095         timeslice = (Missiontime >> 16) & 0x0f;
7096         scale = ((Missiontime >> 16) & 0x0f) << 14;
7097
7098         if (timeslice & 0x01)
7099                 vm_vec_scale_add2(&tvec, &Pl_objp->orient.v.rvec, f2fl(scale ^ 0x10000));
7100         if (timeslice & 0x02)
7101                 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.v.rvec, f2fl(scale));
7102         if (timeslice & 0x04)
7103                 vm_vec_scale_add2(&tvec, &Pl_objp->orient.v.uvec, f2fl(scale ^ 0x10000));
7104         if (timeslice & 0x08)
7105                 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.v.uvec, f2fl(scale));
7106
7107         while (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
7108                 tvec.xyz.x += frand();
7109                 tvec.xyz.y += frand();
7110         }
7111
7112         bank_override = Pl_objp->phys_info.speed;
7113
7114         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7115         accelerate_ship(aip, 1.0f);
7116 }
7117
7118 //      Trying to get away from opponent.
7119 void ai_chase_ga(ai_info *aip, ship_info *sip)
7120 {
7121         //      If not near end of this submode, evade squiggly.  If near end, just fly straight for a bit
7122         vector  tvec;
7123         float           bank_override;
7124         vector  vec_from_enemy;
7125
7126         if (En_objp != NULL) {
7127                 vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
7128         } else
7129                 vec_from_enemy = Pl_objp->orient.v.fvec;
7130
7131         static_randvec(Missiontime >> 15, &tvec);
7132         vm_vec_scale(&tvec, 100.0f);
7133         vm_vec_scale_add2(&tvec, &vec_from_enemy, 300.0f);
7134         vm_vec_add2(&tvec, &Pl_objp->pos);
7135
7136         bank_override = Pl_objp->phys_info.speed;
7137
7138         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7139
7140         accelerate_ship(aip, 2.0f);
7141
7142         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
7143                 if (!(Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
7144                         float percent_left = 100.0f * Ships[Pl_objp->instance].afterburner_fuel / sip->afterburner_fuel_capacity;
7145                         if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
7146                                 afterburners_start(Pl_objp);
7147                                 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7148                         }
7149                         afterburners_start(Pl_objp);
7150                         aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7151                 }
7152         }
7153
7154 }
7155
7156 //      Make object *objp attack subsystem with ID = subnum.
7157 //      Return true if found a subsystem to attack, else return false.
7158 //      Note, can fail if subsystem exists, but has no hits.
7159 int ai_set_attack_subsystem(object *objp, int subnum)
7160 {
7161         ship                    *shipp, *attacker_shipp;
7162         ai_info         *aip;
7163         ship_subsys     *ssp;
7164         object          *attacked_objp;
7165
7166         Assert(objp->type == OBJ_SHIP);
7167         Assert(objp->instance >= 0);
7168
7169         attacker_shipp = &Ships[objp->instance];
7170         Assert(attacker_shipp->ai_index >= 0);
7171
7172         aip = &Ai_info[attacker_shipp->ai_index];
7173
7174         // MWA -- 2/27/98.  Due to AL's changes, target_objnum is now not always valid (at least sometimes
7175         // in terms of goals).  So, bail if we don't have a valid target.
7176         if ( aip->target_objnum == -1 )
7177                 return 0;
7178
7179         attacked_objp = &Objects[aip->target_objnum];
7180         shipp = &Ships[attacked_objp->instance];                //  need to get our target's ship pointer!!!
7181
7182         ssp = ship_get_indexed_subsys(shipp, subnum, &objp->pos);
7183         if (ssp == NULL)
7184                 return 0;
7185
7186         set_targeted_subsys(aip, ssp, aip->target_objnum);
7187         
7188         if (aip->ignore_objnum == aip->target_objnum)
7189                 aip->ignore_objnum = UNUSED_OBJNUM;
7190
7191         // -- Done at caller in ai_process_mission_orders -- attacked_objp->flags |= OF_PROTECTED;
7192
7193         ai_set_goal_maybe_abort_dock(objp, aip);
7194         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7195
7196         return 1;
7197 }
7198
7199 void ai_set_guard_vec(object *objp, object *guard_objp)
7200 {
7201         ai_info *aip;
7202         float   radius;
7203
7204         aip = &Ai_info[Ships[objp->instance].ai_index];
7205
7206         //      Handle case of bogus call in which ship is told to guard self.
7207         Assert(objp != guard_objp);
7208         if (objp == guard_objp) {
7209                 vm_vec_rand_vec_quick(&aip->guard_vec);
7210                 vm_vec_scale(&aip->guard_vec, 100.0f);
7211                 return;
7212         }
7213
7214         // check if guard_objp is BIG
7215         radius = 5.0f * (objp->radius + guard_objp->radius) + 50.0f;
7216         if (radius > 300.0f) {
7217                 radius = guard_objp->radius * 1.25f;
7218         }
7219
7220         vm_vec_sub(&aip->guard_vec, &objp->pos, &guard_objp->pos);
7221
7222         if (vm_vec_mag(&aip->guard_vec) > 3.0f*radius) {
7223                 //      Far away, don't just use vector to object, causes clustering of guard ships.
7224                 vector  tvec, rvec;
7225                 float   mag;
7226                 mag = vm_vec_copy_normalize(&tvec, &aip->guard_vec);
7227                 vm_vec_rand_vec_quick(&rvec);                   
7228                 vm_vec_scale_add2(&tvec, &rvec, 0.5f);
7229                 vm_vec_copy_scale(&aip->guard_vec, &tvec, mag);
7230         }
7231
7232         vm_vec_normalize_quick(&aip->guard_vec);
7233         vm_vec_scale(&aip->guard_vec, radius);
7234 }
7235
7236 //      Make object *objp guard object *other_objp.
7237 //      To be called from the goals code.
7238 void ai_set_guard_wing(object *objp, int wingnum)
7239 {
7240         ship            *shipp;
7241         ai_info *aip;
7242         int             leader_objnum, leader_shipnum;
7243
7244         Assert(wingnum >= 0);
7245
7246         Assert(objp->type == OBJ_SHIP);
7247         Assert(objp->instance >= 0);
7248
7249         // shouldn't set the ai mode for the player
7250         if ( objp == Player_obj ) {
7251                 return;
7252         }
7253
7254         shipp = &Ships[objp->instance];
7255
7256         Assert(shipp->ai_index >= 0);
7257
7258         aip = &Ai_info[shipp->ai_index];
7259         force_avoid_player_check(objp, aip);
7260
7261         ai_set_goal_maybe_abort_dock(objp, aip);
7262         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7263
7264         //      This function is called whenever a guarded ship is destroyed, so this code
7265         //      prevents a ship from trying to guard a non-existent wing.
7266         if (Wings[wingnum].current_count < 1) {
7267                 aip->guard_objnum = -1;
7268                 aip->guard_wingnum = -1;
7269                 aip->mode = AIM_NONE;
7270         } else {
7271                 leader_shipnum = Wings[wingnum].ship_index[0];
7272                 leader_objnum = Ships[leader_shipnum].objnum;
7273
7274                 Assert((leader_objnum >= 0) && (leader_objnum < MAX_OBJECTS));
7275                 //Assert(leader_objnum != objp-Objects);        //      Don't allow ships to guard themselves.
7276                 if (leader_objnum == OBJ_INDEX(objp)) {
7277                         //Int3();       //      Seems illegal, but let's clean up.  Get MikeK.
7278                         return;
7279                 }
7280
7281                 aip->guard_wingnum = wingnum;
7282                 aip->guard_objnum = leader_objnum;
7283                 aip->guard_signature = Objects[leader_objnum].signature;
7284                 aip->mode = AIM_GUARD;
7285                 aip->submode = AIS_GUARD_STATIC;
7286
7287                 ai_set_guard_vec(objp, &Objects[leader_objnum]);
7288         }
7289 }
7290
7291 //      Make object *objp guard object *other_objp.
7292 //      To be called from the goals code.
7293 void ai_set_evade_object(object *objp, object *other_objp)
7294 {
7295         ship            *shipp;
7296         ai_info *aip;
7297         int             other_objnum;
7298
7299         Assert(objp->type == OBJ_SHIP);
7300         Assert(objp->instance >= 0);
7301
7302         shipp = &Ships[objp->instance];
7303
7304         Assert(shipp->ai_index >= 0);
7305
7306         aip = &Ai_info[shipp->ai_index];
7307
7308         other_objnum = OBJ_INDEX(other_objp);
7309         Assert(other_objnum >= 0);
7310
7311         Assert(other_objnum != Ships[aip->shipnum].objnum);     //      make sure not targeting self
7312         aip->target_objnum = other_objnum;
7313
7314         aip->mode = AIM_EVADE;
7315 }
7316
7317 //      Make objp guard other_objp
7318 //      If other_objp is a member of a wing, objp will guard that whole wing
7319 //      UNLESS objp is also a member of the wing!
7320 void ai_set_guard_object(object *objp, object *other_objp)
7321 {
7322         ship            *shipp;
7323         ai_info *aip;
7324         int             other_objnum;
7325
7326         Assert(objp->type == OBJ_SHIP);
7327         Assert(objp->instance >= 0);
7328         Assert(objp != other_objp);
7329
7330         shipp = &Ships[objp->instance];
7331
7332         Assert(shipp->ai_index >= 0);
7333
7334         aip = &Ai_info[shipp->ai_index];
7335         aip->avoid_check_timestamp = timestamp(1);
7336
7337         //      If ship to guard is in a wing, guard that whole wing.
7338         ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
7339         if ((other_aip->wing != -1) && (other_aip->wing != aip->wing)) {
7340                 ai_set_guard_wing(objp, Ai_info[Ships[other_objp->instance].ai_index].wing);
7341         } else {
7342
7343                 other_objnum = other_objp-Objects;
7344
7345                 aip->guard_objnum = other_objnum;
7346                 aip->guard_signature = other_objp->signature;
7347                 aip->guard_wingnum = -1;
7348
7349                 aip->mode = AIM_GUARD;
7350                 aip->submode = AIS_GUARD_STATIC;
7351
7352                 Assert(other_objnum >= 0);      //      Hmm, bogus object and we need its position for guard_vec.
7353
7354                 // vm_vec_sub(&aip->guard_vec, &objp->pos, &Objects[other_objnum].pos);
7355                 ai_set_guard_vec(objp, &Objects[other_objnum]);
7356
7357                 ai_set_goal_maybe_abort_dock(objp, aip);
7358                 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7359         }
7360 }
7361
7362 //      Update the aspect_locked_time field based on whether enemy is in view cone.
7363 //      Also set/clear AIF_SEEK_LOCK.
7364 void update_aspect_lock_information(ai_info *aip, vector *vec_to_enemy, float dist_to_enemy, float enemy_radius)
7365 {
7366         float   dot_to_enemy;
7367         int     num_weapon_types;
7368         int     weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
7369         ship    *shipp;
7370         ship_weapon     *swp;
7371         weapon_info     *wip;
7372
7373         shipp = &Ships[aip->shipnum];
7374         swp = &shipp->weapons;
7375
7376         // AL 3-7-98: This probably should never happen, but check to ensure that current_secondary_bank is valid
7377         if ( (swp->current_secondary_bank < 0) || (swp->current_secondary_bank > swp->num_secondary_banks) ) {
7378                 return;
7379         }
7380
7381         num_weapon_types = get_available_secondary_weapons(Pl_objp, weapon_id_list, weapon_bank_list);
7382
7383         wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
7384
7385         if (num_weapon_types && (wip->wi_flags & WIF_HOMING_ASPECT)) {
7386                 if (dist_to_enemy > 300.0f - min(enemy_radius, 100.0f))
7387                         aip->ai_flags |= AIF_SEEK_LOCK;
7388                 else
7389                         aip->ai_flags &= ~AIF_SEEK_LOCK;
7390
7391                 //      Update locking information for aspect seeking missiles.
7392                 aip->current_target_is_locked = 0;
7393                 dot_to_enemy = vm_vec_dot(vec_to_enemy, &Pl_objp->orient.v.fvec);
7394
7395                 float   needed_dot = 0.9f - 0.5f * enemy_radius/(dist_to_enemy + enemy_radius); //      Replaced MIN_TRACKABLE_DOT with 0.9f
7396                 if (dot_to_enemy > needed_dot) {
7397                         aip->aspect_locked_time += flFrametime;
7398                         // nprintf(("AI", "+ Lock time = %7.3f\n", aip->aspect_locked_time));
7399                         if (aip->aspect_locked_time >= wip->min_lock_time) {
7400                                 aip->aspect_locked_time = wip->min_lock_time;
7401                                 aip->current_target_is_locked = 1;
7402                         }
7403                 } else {
7404                         aip->aspect_locked_time -= flFrametime*2;
7405                         // nprintf(("AI", "- Lock time = %7.3f\n", aip->aspect_locked_time));
7406                         if (aip->aspect_locked_time < 0.0f)
7407                                 aip->aspect_locked_time = 0.0f;
7408                 }
7409                 //nprintf(("AI", "dot = %7.3f, time = %7.3f\n", dot_to_enemy, aip->aspect_locked_time));
7410         
7411         } else {
7412                 aip->current_target_is_locked = 0;
7413                 aip->aspect_locked_time = 0.0f; // Used to be this, why?: wip->min_lock_time;
7414                 aip->ai_flags &= ~AIF_SEEK_LOCK;
7415         }
7416
7417 }
7418
7419 //      We're in chase mode and we've recently collided with our target.
7420 //      Fly away from it!
7421 void ai_chase_fly_away(object *objp, ai_info *aip)
7422 {
7423         int     abort_flag = 0;
7424
7425         if (aip->ai_flags & AIF_TARGET_COLLISION) {
7426                 aip->ai_flags &= ~AIF_TARGET_COLLISION; //      Don't process this hit again next frame.
7427                 aip->submode = SM_FLY_AWAY;                                     //      Focus on avoiding target
7428                 aip->submode_start_time = Missiontime;
7429         }
7430
7431         if ((aip->target_objnum == -1) || (Objects[aip->target_objnum].signature != aip->target_signature)) {
7432                 abort_flag = 1;
7433         }
7434
7435         if (abort_flag || (Missiontime > aip->submode_start_time + F1_0)) {
7436                 aip->last_attack_time = Missiontime;
7437                 aip->submode = SM_ATTACK;
7438                 aip->submode_start_time = Missiontime;
7439         } else {
7440                 vector  v2e;
7441                 float           dot;
7442
7443                 vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, &objp->pos);
7444
7445                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2e);
7446                 if (dot < 0.0f)
7447                         accelerate_ship(aip, 1.0f);
7448                 else
7449                         accelerate_ship(aip, 1.0f - dot);
7450                 turn_away_from_point(objp, &Objects[aip->target_objnum].pos, 0.0f);
7451         }
7452 }
7453
7454 //      Return bank index of favored secondary weapon.
7455 //      Return -1 if nothing favored.
7456 //      "favored" means SEXPs have specified the weapon as being good to fire at en_objp.
7457 int has_preferred_secondary(object *objp, object *en_objp, ship_weapon *swp)
7458 {
7459 // int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
7460         int     i;
7461
7462         for (i=0; i<swp->num_secondary_banks; i++) {
7463                 if (swp->secondary_bank_capacity[i] > 0) {
7464                         if (swp->secondary_bank_ammo[i] > 0) {
7465                                 if (is_preferred_weapon(swp->secondary_bank_weapons[i], objp, en_objp) != -1){
7466                                         return i;
7467                                 }
7468                         }
7469                 }
7470         }
7471
7472         return -1;
7473 }
7474
7475 //      Choose which secondary weapon to fire.
7476 //      Note, this is not like ai_select_secondary_weapon().  "choose" means make a choice.
7477 //      "select" means execute an order.  Get it?
7478 //      This function calls ai_select_secondary_weapon() with the characteristics it should search for.
7479 void ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp)
7480 {
7481         float                   subsystem_strength = 0.0f;
7482         int                     is_big_ship, priority1, priority2;
7483         ship_weapon     *swp;
7484         ship_info       *esip;
7485
7486         if ( en_objp->type == OBJ_SHIP ) {
7487                 esip = &Ship_info[Ships[en_objp->instance].ship_info_index];
7488         } else {
7489                 esip = NULL;
7490         }
7491
7492         swp = &Ships[objp->instance].weapons;
7493
7494         // AL 3-5-98: do a quick out if the ship has no secondaries
7495         if ( swp->num_secondary_banks <= 0 ) {
7496                 swp->current_secondary_bank = -1;
7497                 return;
7498         }
7499
7500         int preferred_secondary = has_preferred_secondary(objp, en_objp, swp);
7501
7502         if (preferred_secondary != -1) {
7503                 if (swp->current_secondary_bank != preferred_secondary) {
7504                         aip->current_target_is_locked = 0;
7505                         aip->aspect_locked_time = 0.0f;
7506                         swp->current_secondary_bank = preferred_secondary;
7507                 }
7508                 //nprintf(("AI", "Favored secondary = %s\n", Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7509                 aip->ai_flags |= AIF_UNLOAD_SECONDARIES;
7510         } else {
7511                 aip->ai_flags &= ~AIF_UNLOAD_SECONDARIES;
7512                 if (aip->targeted_subsys) {
7513                         subsystem_strength = aip->targeted_subsys->current_hits;
7514                 }
7515
7516                 if ( esip ) {
7517                         is_big_ship = esip->flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP);
7518                 } else {
7519                         is_big_ship=0;
7520                 }
7521
7522                 if (is_big_ship) {
7523                         priority1 = WIF_HUGE;
7524                         priority2 = WIF_HOMING;
7525                 } else if ( (esip != NULL) && (esip->flags & SIF_BOMBER) ) {
7526                         priority1 = WIF_BOMBER_PLUS;
7527                         priority2 = WIF_HOMING;
7528                 } else if (subsystem_strength > 100.0f) {
7529                         priority1 = WIF_PUNCTURE;
7530                         priority2 = WIF_HOMING;
7531                 } else {
7532                         priority1 = WIF_HOMING;
7533                         priority2 = 0;
7534                 }
7535                 
7536                 ai_select_secondary_weapon(objp, swp, priority1, priority2);
7537         }
7538
7539         // nprintf(("AI", "Frame %i: Chose secondary %s\n", Framecount, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7540 }
7541
7542 //      Return time, in seconds, at which this ship can next fire its current secondary weapon.
7543 float set_secondary_fire_delay(ai_info *aip, ship *shipp, weapon_info *swip)
7544 {
7545         float t = swip->fire_wait;              //      Base delay for this weapon.
7546         if (shipp->team == Player_ship->team) {
7547                 //      On player's team, _lower_ skill level = faster firing
7548                 t = t * (Game_skill_level+2) / (NUM_SKILL_LEVELS);
7549         } else {                //      Not on player's team, higher skill level = faster firing
7550                 t = t * (NUM_SKILL_LEVELS - Game_skill_level+2) / (NUM_SKILL_LEVELS);
7551         }
7552
7553         t += (Num_ai_classes - aip->ai_class + 1) * 0.5f;
7554         t *= frand_range(0.8f, 1.2f);
7555
7556         //      For the missiles that fire fairly quickly, occasionally add an additional substantial delay.
7557         if (t < 5.0f)
7558                 if (frand() < 0.5f)
7559                         t = t * 2.0f + 2.0f;
7560
7561         return t;
7562 }
7563
7564
7565 void ai_chase_big_approach_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7566 {
7567         float dist_to_goal;
7568
7569         // head straight toward him and maybe circle later
7570         vm_vec_avg(goal_pos, &attack_objp->pos, &target_objp->pos);
7571
7572         // get distance to goal
7573         dist_to_goal = vm_vec_dist(goal_pos, &attack_objp->pos);
7574         
7575         // set accel
7576         if (dist_to_goal > 400.0f) {
7577                 *accel = 1.0f;
7578         } else {
7579                 *accel = dist_to_goal/400.0f;
7580         }
7581 }
7582
7583 void ai_chase_big_circle_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7584 {
7585         get_tangent_point(goal_pos, attack_objp, &target_objp->pos, attack_objp->radius + target_objp->radius + 100.0f);
7586
7587         *accel = 1.0f;
7588 }
7589
7590 // get the current and desired horizontal separations between target
7591 void ai_chase_big_get_separations(object *attack_objp, object *target_objp, vector *horz_vec_to_target, float *desired_separation, float *cur_separation)
7592 {
7593         float temp, r_target, r_attacker, h_attacker, h_target;
7594         float perp_dist;
7595         vector vec_to_target;
7596         polymodel *pm;
7597
7598         // get parameters of ships (as cylinders - radius and height)
7599         // get radius of attacker (for rotations about forward)
7600         pm = model_get(Ships[attack_objp->instance].modelnum);
7601         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7602         r_attacker = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7603         r_attacker = max(temp, r_attacker);
7604         h_attacker = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7605
7606         // get radius of target (for rotations about forward)
7607         pm = model_get(Ships[attack_objp->instance].modelnum);
7608         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7609         r_target = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7610         r_target = max(temp, r_target);
7611         h_target = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7612
7613         // find separation between cylinders [if parallel]
7614         vm_vec_sub(&vec_to_target, &attack_objp->pos, &target_objp->pos);
7615
7616         // find the distance between centers along forward direction of ships
7617         perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.v.fvec);
7618
7619         // subtract off perp component to get "horizontal" separation vector between cylinders [ASSUMING parallel]
7620         vm_vec_scale_add(horz_vec_to_target, &vec_to_target, &target_objp->orient.v.fvec, -perp_dist);
7621         *cur_separation = vm_vec_mag_quick(horz_vec_to_target);
7622
7623         // choose "optimal" separation of 1000 + r_target + r_attacker
7624         *desired_separation = 1000 + r_target + r_attacker;
7625 }
7626
7627 void ai_chase_big_parallel_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7628 {
7629         int opposing;
7630         float temp, r_target, r_attacker, h_attacker, h_target;
7631         float separation, optimal_separation;
7632         vector  horz_vec_to_target;
7633         polymodel *pm;
7634
7635         // get parameters of ships (as cylinders - radius and height)
7636         // get radius of attacker (for rotations about forward)
7637         pm = model_get(Ships[attack_objp->instance].modelnum);
7638         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7639         r_attacker = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7640         r_attacker = max(temp, r_attacker);
7641         h_attacker = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7642
7643         // get radius of target (for rotations about forward)
7644         pm = model_get(Ships[attack_objp->instance].modelnum);
7645         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7646         r_target = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7647         r_target = max(temp, r_target);
7648         h_target = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7649
7650         // are we opposing (only when other ship is not moving)
7651         opposing = ( vm_vec_dotprod(&attack_objp->orient.v.fvec, &target_objp->orient.v.fvec) < 0 );
7652
7653         ai_chase_big_get_separations(attack_objp, target_objp, &horz_vec_to_target, &optimal_separation, &separation);
7654
7655         // choose dist (2000) so that we don't bash
7656         float dist = 2000;
7657         if (opposing) {
7658                 dist = - dist;
7659         }
7660
7661         // set the goal pos as dist forward from target along target forward
7662         vm_vec_scale_add(goal_pos, &target_objp->pos, &target_objp->orient.v.fvec, dist);
7663         // then add horizontal separation
7664         vm_vec_scale_add2(goal_pos, &horz_vec_to_target, optimal_separation/separation);
7665
7666         // find the distance between centers along forward direction of ships
7667         vector vec_to_target;
7668         vm_vec_sub(&vec_to_target, &target_objp->pos, &attack_objp->pos);
7669         float perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.v.fvec);
7670
7671         float match_accel = target_objp->phys_info.vel.xyz.z / Ship_info[Ships[attack_objp->instance].ship_info_index].max_vel.xyz.z;
7672         float length_scale = attack_objp->radius;
7673
7674         // if we're heading toward enemy ship, we want to keep going if we're ahead
7675         if (opposing) {
7676                 perp_dist = -perp_dist;
7677         }
7678
7679         if (perp_dist > 0) {
7680                 // falling behind, so speed up
7681                 *accel = match_accel + (1.0f - match_accel) / length_scale * (perp_dist);
7682         } else {
7683                 // up in front, so slow down
7684                 *accel = match_accel  - match_accel / length_scale * -perp_dist;
7685                 *accel = max(0.0f, *accel);
7686         }
7687
7688 }
7689
7690
7691 //      Return *goal_pos for one cruiser to attack another (big ship).
7692 //      Choose point fairly nearby that is not occupied by another cruiser.
7693 void ai_cruiser_chase_set_goal_pos(vector *goal_pos, object *pl_objp, object *en_objp)
7694 {
7695         ai_info *aip;
7696
7697         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
7698         float accel;
7699
7700         switch (aip->submode) {
7701         case SM_BIG_APPROACH:
7702                 // do approach stuff;
7703                 ai_chase_big_approach_set_goal(goal_pos, pl_objp, en_objp, &accel);
7704                 break;
7705
7706         case SM_BIG_CIRCLE:
7707                 // do circle stuff
7708                 ai_chase_big_circle_set_goal(goal_pos, pl_objp, en_objp, &accel);
7709                 break;
7710
7711         case SM_BIG_PARALLEL:
7712                 // do parallel stuff
7713                 ai_chase_big_parallel_set_goal(goal_pos, pl_objp, en_objp, &accel);
7714                 break;
7715         }
7716 }
7717
7718 int maybe_hack_cruiser_chase_abort()
7719 {
7720         ship                    *shipp = &Ships[Pl_objp->instance];     
7721         ship                    *eshipp = &Ships[En_objp->instance];
7722         ai_info         *aip = &Ai_info[shipp->ai_index];
7723
7724         // mission sm3-08, sathanos chasing collosus
7725         if ( stricmp(Mission_filename, "sm3-08.fs2") == 0 ) {
7726                 if (( stricmp(eshipp->ship_name, "colossus") == 0 ) || ( stricmp(shipp->ship_name, "colossus") == 0 )) {
7727                         // Changed so all big ships attacking the Colossus will not do the chase code.
7728                         // Did this so Beast wouldn't swerve away from Colossus. -- MK, 9/14/99
7729                         //if ( stricmp(shipp->ship_name, "Sathanas") == 0 ) {
7730                                 // do cool hack stuff here
7731                                 ai_clear_ship_goals( aip );
7732                                 aip->mode = AIM_NONE;
7733                                 return 1;
7734                         //}
7735                 }
7736         }
7737
7738         return 0;
7739 }
7740
7741 //      Make a big ship pursue another big ship.
7742 //      (Note, called "ai_cruiser_chase" because we already have ai_chase_big() which means fighter chases big ship.
7743 void ai_cruiser_chase()
7744 {
7745         ship_info       *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7746         ship                    *shipp = &Ships[Pl_objp->instance];     
7747         ai_info         *aip = &Ai_info[shipp->ai_index];
7748
7749         if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7750                 Int3(); //      Hmm, not a very big ship, how did we get in this function?
7751                 aip->mode = AIM_NONE;
7752                 return;
7753         }
7754
7755         if (En_objp->type != OBJ_SHIP) {
7756                 Int3();
7757                 return;
7758         }
7759
7760         if (En_objp->instance < 0) {
7761                 Int3();
7762                 return;
7763         }
7764
7765         ship                    *eshipp;
7766         ship_info       *esip;
7767
7768         eshipp = &Ships[En_objp->instance];
7769         esip = &Ship_info[eshipp->ship_info_index];
7770
7771         if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7772                 // Int3();      //      Hmm, we're big and we're pursuing something other than a big ship?
7773                 aip->mode = AIM_NONE;
7774                 return;
7775         }
7776
7777         vector  goal_pos;
7778         float turn_time = Ship_info[Ships[Pl_objp->instance].ship_info_index].srotation_time;
7779
7780         // kamikaze - ram and explode
7781         if (aip->ai_flags & AIF_KAMIKAZE) {
7782                 ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
7783                 accelerate_ship(aip, 1.0f);
7784         } 
7785         
7786         // really track down and chase
7787         else {
7788                 // check valid submode
7789                 Assert( (aip->submode == SM_ATTACK) || (aip->submode == SM_BIG_APPROACH) || (aip->submode == SM_BIG_CIRCLE) || (aip->submode == SM_BIG_PARALLEL) );
7790
7791                 // just entering, approach enemy ship
7792                 if (aip->submode == SM_ATTACK) {
7793                         aip->submode = SM_BIG_APPROACH;
7794                 }
7795
7796                 // desired accel
7797                 float accel = 0.0f;
7798                 vector *rvecp = NULL;
7799
7800                 switch (aip->submode) {
7801                 case SM_BIG_APPROACH:
7802                         // do approach stuff;
7803                         ai_chase_big_approach_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7804                         // maybe set rvec
7805                         break;
7806
7807                 case SM_BIG_CIRCLE:
7808                         // do circle stuff
7809                         ai_chase_big_circle_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7810                         // maybe set rvec
7811                         break;
7812
7813                 case SM_BIG_PARALLEL:
7814                         // do parallel stuff
7815                         ai_chase_big_parallel_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7816                         //maybe set rvec
7817                         break;
7818                 }
7819
7820
7821                 // now move as desired
7822                 ai_turn_towards_vector(&goal_pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0, rvecp);
7823                 accelerate_ship(aip, accel);
7824
7825
7826                 // maybe switch to new mode
7827                 vector vec_to_enemy;
7828                 float dist_to_enemy;
7829                 int moving = (En_objp->phys_info.vel.xyz.z > 0.5f);
7830                 vm_vec_sub(&vec_to_enemy, &En_objp->pos, &Pl_objp->pos);
7831                 dist_to_enemy = vm_vec_mag_quick(&vec_to_enemy);
7832
7833                 switch (aip->submode) {
7834                 case SM_BIG_APPROACH:
7835                         if ( dist_to_enemy < (Pl_objp->radius + En_objp->radius)*1.25f + 200.0f ) {
7836                                 // moving
7837                                 if (moving) {
7838                                         // if within 90 degrees of en forward, go into parallel, otherwise circle
7839                                         if ( vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0 ) {
7840                                                 aip->submode = SM_BIG_PARALLEL;
7841                                         }
7842                                 }
7843
7844                                 // otherwise cirle
7845                                 if ( !maybe_hack_cruiser_chase_abort() ) {
7846                                         aip->submode = SM_BIG_CIRCLE;
7847                                 }
7848                         }
7849                         break;
7850
7851                 case SM_BIG_CIRCLE:
7852                         // moving
7853                         if (moving) {
7854                                 vector temp;
7855                                 float desired_sep, cur_sep;
7856                                 // we're behind the enemy ship
7857                                 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec) > 0) {
7858                                         // and we're turning toward the enemy
7859                                         if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0) {
7860                                                 // get separation
7861                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7862                                                 // and the separation is > 0.9 desired
7863                                                 if (cur_sep > 0.9 * desired_sep) {
7864                                                         aip->submode = SM_BIG_PARALLEL;
7865                                                 }
7866                                         }
7867                                 }
7868                         } else {
7869                                 // still
7870                                 vector temp;
7871                                 float desired_sep, cur_sep;
7872                                 // we're behind the enemy ship
7873                                 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec) > 0) {
7874                                         // and we're turning toward the enemy
7875                                         if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0) {
7876                                                 // get separation
7877                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7878                                                 //and the separation is [0.9 to 1.1] desired
7879                                                 if ( (cur_sep > 0.9f * desired_sep) ) {
7880                                                         aip->submode = SM_BIG_PARALLEL;
7881                                                 }
7882                                         }
7883                                 }
7884                                 // in front of ship
7885                                 else {
7886                                         // and we're turning toward the enemy
7887                                         if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) < 0) {
7888                                                 // get separation
7889                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7890                                                 //and the separation is [0.9 to 1.1] desired
7891                                                 if ( (cur_sep > 0.9f * desired_sep) ) {
7892                                                         aip->submode = SM_BIG_PARALLEL;
7893                                                 }
7894                                         }
7895                                 }
7896                         }
7897                         break;
7898
7899                 case SM_BIG_PARALLEL:
7900                         // we're opposing
7901                         if ( vm_vec_dotprod(&Pl_objp->orient.v.fvec, &En_objp->orient.v.fvec) < 0 ) {
7902                                 // and the other ship is moving
7903                                 if (moving) {
7904                                         // and we no longer overlap
7905                                         if ( dist_to_enemy > (0.75 * (En_objp->radius + Pl_objp->radius)) ) {
7906                                                 aip->submode = SM_BIG_APPROACH;
7907                                         }
7908                                 }
7909                         }
7910                         break;
7911                 }
7912         }
7913 }
7914
7915 // --------------------------------------------------------------------------
7916 // Make object Pl_objp chase object En_objp
7917 void ai_chase()
7918 {
7919         float                   dist_to_enemy, time_to_enemy;
7920         float                   dot_to_enemy, dot_from_enemy, real_dot_to_enemy;
7921         vector          player_pos, enemy_pos, predicted_enemy_pos, real_vec_to_enemy, predicted_vec_to_enemy;
7922         ship_info       *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7923         ship                    *shipp = &Ships[Pl_objp->instance];
7924         ship_weapon     *swp = &shipp->weapons;
7925         ai_info         *aip = &Ai_info[shipp->ai_index];
7926         int                     enemy_sip_flags;
7927
7928         if (aip->mode != AIM_CHASE) {
7929                 Int3();
7930         }
7931
7932         if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
7933                 ai_cruiser_chase();
7934                 return;
7935         }
7936
7937         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER | SIF_ESCAPEPOD))) {
7938                 Warning(LOCATION, "Ship %s is not 'small', but is in chase mode.\nSwitching to AI=none.\n", shipp->ship_name);
7939                 aip->mode = AIM_NONE;
7940                 return;
7941         }
7942
7943         //nprintf(("AI", "%7s ", Submode_text[aip->submode]));
7944
7945         if ( En_objp->type == OBJ_SHIP ) {
7946                 enemy_sip_flags = Ship_info[Ships[En_objp->instance].ship_info_index].flags;
7947         } else {
7948                 enemy_sip_flags = 0;
7949         }
7950
7951         if ( enemy_sip_flags > 0 ) {
7952                 if (enemy_sip_flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
7953                         ai_big_chase();
7954                         return;
7955                 }
7956         }
7957
7958         //      If collided with target_objnum last frame, avoid that ship.
7959         //      This should prevent the embarrassing behavior of ships getting stuck on each other
7960         //      as if they were magnetically attracted. -- MK, 11/13/97.
7961         if ((aip->ai_flags & AIF_TARGET_COLLISION) || (aip->submode == SM_FLY_AWAY)) {
7962                 ai_chase_fly_away(Pl_objp, aip);
7963                 return;
7964         }
7965
7966         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
7967         dist_to_enemy = vm_vec_dist_quick(&player_pos, &enemy_pos);
7968         time_to_enemy = compute_time_to_enemy(dist_to_enemy, Pl_objp, En_objp);
7969         vm_vec_sub(&real_vec_to_enemy, &enemy_pos, &player_pos);
7970
7971         vm_vec_normalize(&real_vec_to_enemy);
7972
7973         real_dot_to_enemy = vm_vec_dot(&real_vec_to_enemy, &Pl_objp->orient.v.fvec);
7974
7975         int is_stealthy_ship = 0;
7976         if ( (enemy_sip_flags > 0) && (enemy_sip_flags & SIF_STEALTH) ) {
7977                 if ( ai_is_stealth_visible(Pl_objp, En_objp) != STEALTH_FULLY_TARGETABLE ) {
7978                         is_stealthy_ship = 1;
7979                 }
7980         }
7981
7982         // Can only acquire lock on a target that isn't hidden from sensors
7983         if ( !(Ships[En_objp->instance].flags & SF_HIDDEN_FROM_SENSORS) && !is_stealthy_ship ) {
7984                 update_aspect_lock_information(aip, &real_vec_to_enemy, dist_to_enemy, En_objp->radius);
7985         } else {
7986                 aip->current_target_is_locked = 0;
7987                 aip->ai_flags &= ~AIF_SEEK_LOCK;
7988         }
7989
7990         //      If seeking lock, try to point directly at ship, else predict position so lasers can hit it.
7991         //      If just acquired target, or target is not in reasonable cone, don't refine believed enemy position.
7992         if ((real_dot_to_enemy < 0.25f) || (aip->target_time < 1.0f) || (aip->ai_flags & AIF_SEEK_LOCK)) {
7993                 predicted_enemy_pos = enemy_pos;
7994         } else {
7995                 //      Set predicted_enemy_pos.
7996                 //      See if attacking a subsystem.
7997                 if (aip->targeted_subsys != NULL) {
7998                         Assert(En_objp->type == OBJ_SHIP);
7999                         ship_info       *esip = &Ship_info[Ships[En_objp->instance].ship_info_index];
8000                         if (get_shield_strength(En_objp)/esip->shields < HULL_DAMAGE_THRESHOLD_PERCENT) {
8001                                 //int   rval;
8002
8003                                 if (aip->targeted_subsys != NULL) {
8004                                         get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
8005                                         predicted_enemy_pos = enemy_pos;
8006                                         predicted_vec_to_enemy = real_vec_to_enemy;
8007                                 } else {
8008                                         set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8009                                         set_target_objnum(aip, -1);
8010                                 }
8011                                 // nprintf(("AI", "Attacking subsystem: rval = %i, pos = %7.3f %7.3f %7.3f\n", rval, predicted_enemy_pos.xyz.x, predicted_enemy_pos.xyz.y, predicted_enemy_pos.xyz.z));
8012
8013                         } else {
8014                                 set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8015                                 // nprintf(("AI", "Attacking subsystem: pos = %7.3f %7.3f %7.3f\n", predicted_enemy_pos.xyz.x, predicted_enemy_pos.xyz.y, predicted_enemy_pos.xyz.z));
8016                         }
8017                 } else {
8018                         set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8019                 }
8020         }
8021
8022         vm_vec_sub(&predicted_vec_to_enemy, &predicted_enemy_pos, &player_pos);
8023
8024         vm_vec_normalize(&predicted_vec_to_enemy);
8025
8026         dot_to_enemy = vm_vec_dot(&Pl_objp->orient.v.fvec, &predicted_vec_to_enemy);
8027         dot_from_enemy= - vm_vec_dot(&En_objp->orient.v.fvec, &real_vec_to_enemy);
8028
8029         //
8030         //      Set turn and acceleration based on submode.
8031         //
8032         switch (aip->submode) {
8033         case SM_CONTINUOUS_TURN:
8034                 ai_chase_ct();
8035                 break;
8036
8037         case SM_STEALTH_FIND:
8038                 ai_stealth_find();
8039                 break;
8040
8041         case SM_STEALTH_SWEEP:
8042                 ai_stealth_sweep();
8043                 break;
8044
8045         case SM_ATTACK:
8046         case SM_SUPER_ATTACK:
8047         case SM_ATTACK_FOREVER:
8048                 if (vm_vec_dist_quick(&Pl_objp->pos, &predicted_enemy_pos) > 100.0f + En_objp->radius * 2.0f) {
8049                         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &predicted_enemy_pos, 10.0f))
8050                                 return;
8051                 }
8052
8053                 ai_chase_attack(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8054                 break;
8055
8056         case SM_EVADE_SQUIGGLE:
8057                 ai_chase_es(aip, sip);
8058                 break;
8059
8060         case SM_EVADE_BRAKE:
8061                 ai_chase_eb(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8062                 break;
8063
8064         case SM_EVADE:
8065                 evade_ship();
8066                 break;
8067
8068         case SM_AVOID:
8069                 avoid_ship();
8070                 break;
8071
8072         case SM_GET_BEHIND:
8073                 get_behind_ship(aip, sip, dist_to_enemy);
8074                 break;
8075
8076         case SM_GET_AWAY:               //      Used to get away from opponent to prevent endless circling.
8077                 ai_chase_ga(aip, sip);
8078                 break;
8079
8080         case SM_EVADE_WEAPON:
8081                 evade_weapon();
8082                 break;
8083
8084         default:
8085                 // Int3();
8086                 aip->last_attack_time = Missiontime;
8087                 aip->submode = SM_ATTACK;
8088                 aip->submode_start_time = Missiontime;
8089         }
8090
8091         //
8092         //      Maybe choose a new submode.
8093         //
8094         if ( (aip->submode != SM_AVOID) && (aip->submode != SM_ATTACK_FOREVER) ) {
8095                 //      If a very long time since attacked, attack no matter what!
8096                 if ( (aip->submode != SM_SUPER_ATTACK) && (aip->submode != SM_GET_AWAY) && !(aip->ai_flags & AIF_STEALTH_PURSIUT) ) {
8097                         if (Missiontime - aip->last_attack_time > i2f(6)) {
8098                                 aip->submode = SM_SUPER_ATTACK;
8099                                 aip->submode_start_time = Missiontime;
8100                                 aip->last_attack_time = Missiontime;
8101                         }
8102                 }
8103
8104                 //      If a collision is expected, pull out!
8105                 //      If enemy is pointing away and moving a bit, don't worry about collision detection.
8106                 if ((dot_from_enemy > 0.5f) || (En_objp->phys_info.speed < 10.0f)) {
8107                         if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 4.0f)) {
8108                                 if ((Missiontime - aip->last_hit_time > F1_0*4) && (dist_to_enemy < Pl_objp->radius*2 + En_objp->radius*2)) {
8109                                         accelerate_ship(aip, -1.0f);
8110                                 } else {
8111                                         aip->submode = SM_AVOID;
8112                                         aip->submode_start_time = Missiontime;
8113                                 }
8114                         }
8115                 }
8116         }
8117
8118         switch (aip->submode) {
8119         case SM_CONTINUOUS_TURN:
8120                 if (Missiontime - aip->submode_start_time > i2f(3)) {
8121                         aip->last_attack_time = Missiontime;
8122                         aip->submode = SM_ATTACK;
8123                         aip->submode_start_time = Missiontime;
8124                 }
8125                 break;
8126
8127         case SM_ATTACK:
8128                 // if taraget is stealth and stealth not visible, then enter stealth find mode
8129                 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8130                         aip->submode = SM_STEALTH_FIND;
8131                         aip->submode_start_time = Missiontime;
8132                         aip->submode_parm0 = SM_SF_AHEAD;
8133                 } 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)) {
8134                         aip->submode = SM_SUPER_ATTACK;
8135                         aip->submode_start_time = Missiontime;
8136                         aip->last_attack_time = Missiontime;
8137                 } else if ((Missiontime - aip->last_hit_target_time > i2f(6)) &&
8138                         (dist_to_enemy < 500.0f) && (dot_to_enemy < 0.2f) &&
8139                         (frand() < (float) Game_skill_level/NUM_SKILL_LEVELS)) {
8140                         aip->submode = SM_GET_AWAY;
8141                         aip->submode_start_time = Missiontime;
8142                         aip->last_hit_target_time = Missiontime;
8143                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP)
8144                         && (dot_to_enemy < dot_from_enemy)
8145                         && (En_objp->phys_info.speed > 15.0f) 
8146                         && (dist_to_enemy < 200.0f) 
8147                         && (dist_to_enemy > 50.0f)
8148                         && (dot_to_enemy < 0.1f)
8149                         && (Missiontime - aip->submode_start_time > i2f(2))) {
8150                         aip->submode = SM_EVADE_BRAKE;
8151                         aip->submode_start_time = Missiontime;
8152                 } else if ((dot_to_enemy > 0.2f) && (dot_from_enemy > -0.2f) && (dot_from_enemy < 0.1f)) {
8153                         aip->submode = SM_GET_BEHIND;
8154                         aip->submode_start_time = Missiontime;
8155                 } 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)) {
8156                         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;
8157                                 aip->submode_start_time = Missiontime;
8158                                 aip->last_hit_target_time = Missiontime;
8159                         } else {
8160                                 aip->submode = SM_EVADE_SQUIGGLE;
8161                                 aip->submode_start_time = Missiontime;
8162                         }
8163                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP) && (Missiontime - aip->submode_start_time > F1_0*2)) {
8164                         if ((dot_to_enemy < 0.8f) && (dot_from_enemy > dot_to_enemy)) {
8165                                 if (frand() > 0.5f) {
8166                                         aip->submode = SM_CONTINUOUS_TURN;
8167                                         aip->submode_parm0 = myrand() & 0x0f;
8168                                         aip->submode_start_time = Missiontime;
8169                                 } else {
8170                                         aip->submode = SM_EVADE;
8171                                         aip->submode_start_time = Missiontime;
8172                                 }
8173                         } else {
8174                                 aip->submode_start_time = Missiontime;
8175                         }
8176                 }
8177
8178                 aip->last_attack_time = Missiontime;
8179
8180                 break;
8181                 
8182         case SM_EVADE_SQUIGGLE:
8183                 if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > 300.0f)) {
8184                         if ((dist_to_enemy < 100.0f) && (dot_to_enemy < 0.0f) && (dot_from_enemy > 0.5f)) {
8185                                 aip->submode = SM_EVADE_BRAKE;
8186                                 aip->submode_start_time = Missiontime;
8187                         } else {
8188                                 aip->last_attack_time = Missiontime;
8189                                 aip->submode = SM_ATTACK;
8190                                 aip->submode_start_time = Missiontime;
8191                         }
8192                 }
8193                 break;
8194         
8195         case SM_EVADE_BRAKE:
8196                 if ((dist_to_enemy < 15.0f) || (En_objp->phys_info.speed < 10.0f)) {
8197                         aip->submode = SM_AVOID;
8198                         aip->submode_start_time = Missiontime;
8199                 } else if ((dot_to_enemy > 0.9f) || ((dot_from_enemy > 0.9f) && (Missiontime - aip->submode_start_time > i2f(1)))) {
8200                         aip->last_attack_time = Missiontime;
8201                         aip->submode = SM_ATTACK;
8202                         aip->submode_start_time = Missiontime;
8203                 } else if (Missiontime - aip->submode_start_time > i2f(4)) {
8204                         aip->last_attack_time = Missiontime;
8205                         aip->submode = SM_ATTACK;
8206                         aip->submode_start_time = Missiontime;
8207                 }
8208                 break;
8209
8210         case SM_EVADE:
8211                 //      Modified by MK on 5/5/97 to keep trying to regain attack mode.  It's what a human would do.
8212                 if ((dot_to_enemy < 0.2f) && (dot_from_enemy < 0.8f) && (dist_to_enemy < 100.0f) && (En_objp->phys_info.speed > 15.0f)) {
8213                         aip->last_attack_time = Missiontime;
8214                         aip->submode = SM_EVADE_BRAKE;
8215                         aip->submode_start_time = Missiontime;
8216                 } else if (((dot_to_enemy > dot_from_enemy - 0.1f)
8217                         && (Missiontime > aip->submode_start_time + i2f(1)))
8218                         || (dist_to_enemy > 150.0f + 2*(Pl_objp->radius + En_objp->radius))) {
8219                         aip->last_attack_time = Missiontime;
8220                         aip->submode = SM_ATTACK;
8221                         aip->submode_start_time = Missiontime;
8222                 } else if (Missiontime - aip->submode_start_time > i2f(2))
8223                         if (dot_from_enemy > 0.8f) {
8224                                 aip->submode = SM_EVADE_SQUIGGLE;
8225                                 aip->submode_start_time = Missiontime;
8226                         }
8227
8228                 break;
8229
8230         case SM_SUPER_ATTACK:
8231                 // if stealth and invisible, enter stealth find mode
8232                 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8233                         aip->submode = SM_STEALTH_FIND;
8234                         aip->submode_start_time = Missiontime;
8235                         aip->submode_parm0 = SM_SF_AHEAD;
8236                 } 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) )) {
8237                         aip->ai_flags &= ~AIF_ATTACK_SLOWLY;    //      Just in case, clear here.
8238
8239                         switch (myrand() % 5) {
8240                         case 0:
8241                                 aip->submode = SM_CONTINUOUS_TURN;
8242                                 aip->submode_start_time = Missiontime;
8243                                 break;
8244                         case 1:
8245                                 aip->submode_start_time = Missiontime;  //      Stay in super attack mode
8246                                 break;
8247                         case 2:
8248                         case 3:
8249                                 if (frand() < (float) 0.5f * (aip->ai_class + Game_skill_level)/(Num_ai_classes + NUM_SKILL_LEVELS)) {
8250                                         aip->submode = SM_GET_AWAY;
8251                                         aip->submode_start_time = Missiontime;
8252                                 } else {
8253                                         aip->submode = SM_EVADE;
8254                                         aip->submode_start_time = Missiontime;
8255                                 }
8256                                 break;
8257                         case 4:
8258                                 if (dot_from_enemy + (NUM_SKILL_LEVELS - Game_skill_level) * 0.1f > dot_to_enemy) {     //      Less likely to GET_AWAY at lower skill levels.
8259                                         aip->submode = SM_EVADE;
8260                                         aip->submode_start_time = Missiontime;
8261                                 } else {
8262                                         aip->submode = SM_GET_AWAY;
8263                                         aip->submode_start_time = Missiontime;
8264                                 }
8265                                 break;
8266                         default:
8267                                 Int3(); //      Impossible!
8268                         }
8269                 }
8270
8271                 aip->last_attack_time = Missiontime;
8272
8273                 break;
8274
8275         case SM_AVOID:
8276                 if ((dot_to_enemy > -0.2f) && (dist_to_enemy / (dot_to_enemy + 0.3f) < 100.0f)) {
8277                         aip->submode_start_time = Missiontime;
8278                 } else if (Missiontime - aip->submode_start_time > i2f(1)/2)
8279                         if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 3.0f)) {
8280                                 aip->submode_start_time = Missiontime;
8281                         } else {
8282                                 aip->submode = SM_GET_BEHIND;
8283                                 aip->submode_start_time = Missiontime;
8284                         }
8285
8286                 break;
8287
8288         case SM_GET_BEHIND:
8289                 if ((dot_from_enemy < -0.7f) || (Missiontime - aip->submode_start_time > i2f(2))) {
8290                         aip->submode = SM_ATTACK;
8291                         aip->submode_start_time = Missiontime;
8292                         aip->last_attack_time = Missiontime;
8293                 }
8294                 break;
8295
8296         case SM_GET_AWAY:
8297                 if (Missiontime - aip->submode_start_time > i2f(2)) {
8298                         float   rand_dist;
8299
8300                         rand_dist = ((Missiontime >> 17) & 0x03) * 100.0f + 200.0f;     //      Some value in 200..500
8301                         if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > rand_dist) || (dot_from_enemy < 0.4f)) {
8302                                 aip->ai_flags |= AIF_ATTACK_SLOWLY;
8303                                 aip->submode = SM_ATTACK;
8304                                 aip->time_enemy_in_range = 2.0f;                //      Cheat.  Presumably if they were running away from you, they were monitoring you!
8305                                 aip->submode_start_time = Missiontime;
8306                                 aip->last_attack_time = Missiontime;
8307                         }
8308                 }
8309                 break;
8310
8311         case SM_EVADE_WEAPON:
8312                 if (aip->danger_weapon_objnum == -1) {
8313                         aip->submode = SM_ATTACK;
8314                         aip->submode_start_time = Missiontime;
8315                         aip->last_attack_time = Missiontime;
8316                 }
8317                 break;
8318
8319         // Either change to SM_ATTACK or AIM_FIND_STEALTH
8320         case SM_STEALTH_FIND:
8321                 // if time > 5 sec change mode to sweep
8322                 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8323                         aip->submode = SM_ATTACK;
8324                         aip->submode_start_time = Missiontime;
8325                         aip->last_attack_time = Missiontime;
8326                         // sweep if I can't find in 5 sec or bail from find
8327                 } else if ( ((Missiontime - aip->submode_start_time) > i2f(5)) || (aip->submode_parm0 == SM_SF_BAIL) ) {
8328                         // begin sweep mode
8329                         aip->submode = SM_STEALTH_SWEEP;
8330                         aip->submode_start_time = Missiontime;
8331                         aip->last_attack_time = Missiontime;
8332                         aip->submode_parm0 = SM_SS_SET_GOAL;
8333                 }
8334                 break;
8335
8336         case SM_STEALTH_SWEEP:
8337                 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8338                         aip->submode = SM_ATTACK;
8339                         aip->submode_start_time = Missiontime;
8340                         aip->last_attack_time = Missiontime;
8341                 } else if ( (timestamp() - aip->stealth_last_visible_stamp) < 5000 ) {
8342                         // go back to find mode
8343                         aip->submode = SM_STEALTH_FIND;
8344                         aip->submode_start_time = Missiontime;
8345                         aip->submode_parm0 = SM_SF_AHEAD;
8346                 } else if ( /*(Missiontime - aip->submode_start_time) > i2f(30) || */(aip->submode_parm0 == SM_SS_DONE) ) {
8347                         // set target objnum = -1
8348                         set_target_objnum(aip, -1);
8349
8350                         // set submode to attack
8351                         aip->submode = SM_ATTACK;
8352                         aip->submode_start_time = Missiontime;
8353                         aip->last_attack_time = Missiontime;
8354                 }
8355                 break;
8356
8357         case SM_ATTACK_FOREVER: //      Engines blown, just attack.
8358                 break;
8359
8360         default:
8361                 //Int3();
8362                 aip->submode = SM_ATTACK;
8363                 aip->last_attack_time = Missiontime;
8364
8365                 aip->submode_start_time = Missiontime;
8366         }
8367
8368         //
8369         //      Maybe fire primary weapon and update time_enemy_in_range
8370         //
8371         //nprintf(("AI", "time_enemy_in_range = %7.3f, dot = %7.3f\n", aip->time_enemy_in_range, dot_to_enemy));
8372
8373         if (aip->mode != AIM_EVADE) {
8374                 if (dot_to_enemy > 0.95f - 0.5f * En_objp->radius/max(1.0f, En_objp->radius + dist_to_enemy)) {
8375                         aip->time_enemy_in_range += flFrametime;
8376                         
8377                         //      Chance of hitting ship is based on dot product of firing ship's forward vector with vector to ship
8378                         //      and also the size of the target relative to distance to target.
8379                         if (dot_to_enemy > max(0.5f, 0.90f + aip->ai_accuracy/10.0f - En_objp->radius/max(1.0f,dist_to_enemy))) {
8380
8381                                 ship *temp_shipp;
8382                                 ship_weapon *tswp;
8383
8384                                 temp_shipp = &Ships[Pl_objp->instance];
8385                                 tswp = &temp_shipp->weapons;
8386                                 if ( tswp->num_primary_banks > 0 ) {
8387                                         float   scale;
8388                                         Assert(tswp->current_primary_bank < tswp->num_primary_banks);
8389                                         weapon_info     *pwip = &Weapon_info[tswp->primary_bank_weapons[tswp->current_primary_bank]];
8390
8391                                         //      Less likely to fire if far away and moving.
8392                                         scale = pwip->max_speed/(En_objp->phys_info.speed + pwip->max_speed);
8393                                         if (scale > 0.6f)
8394                                                 scale = (scale - 0.6f) * 1.5f;
8395                                         else
8396                                                 scale = 0.0f;
8397                                         if (dist_to_enemy < pwip->max_speed * (1.0f + scale)) {
8398                                                 ai_fire_primary_weapon(Pl_objp);
8399                                         }
8400
8401                                         //      Don't fire secondaries at a protected ship.
8402                                         if (!(En_objp->flags & OF_PROTECTED)) {
8403                                                 ai_choose_secondary_weapon(Pl_objp, aip, En_objp);
8404                                                 int current_bank = tswp->current_secondary_bank;
8405                                                 weapon_info     *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8406
8407                                                 if (current_bank > -1) {
8408                                                         if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8409                                                                 if (timestamp_until(swp->next_secondary_fire_stamp[current_bank]) > swip->fire_wait*1000.0f) {
8410                                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (swip->fire_wait*1000.0f));
8411                                                                 }
8412                                                         }
8413
8414                                                         if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
8415                                                                 if (tswp->current_secondary_bank >= 0) {
8416                                                                         weapon_info     *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8417                                                                         float firing_range;
8418                                                                         
8419                                                                         if (swip->wi_flags & WIF_BOMB)
8420                                                                                 firing_range = swip->max_speed * swip->lifetime * 0.75f;
8421                                                                         else
8422                                                                                 firing_range = swip->max_speed * swip->lifetime * (Game_skill_level + 1 + aip->ai_class/2)/NUM_SKILL_LEVELS;
8423
8424                                                                         // reduce firing range in nebula
8425                                                                         extern int Nebula_sec_range;
8426                                                                         if ((The_mission.flags & MISSION_FLAG_FULLNEB) && Nebula_sec_range) {
8427                                                                                 firing_range *= 0.8f;
8428                                                                         }
8429
8430                                                                         //      If firing a spawn weapon, distance doesn't matter.
8431                                                                         int     spawn_fire = 0;
8432
8433                                                                         if (swip->wi_flags & WIF_SPAWN) {
8434                                                                                 int     count;
8435
8436                                                                                 count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(Pl_objp)), &Pl_objp->pos, 1000.0f);
8437
8438                                                                                 if (count > 3)
8439                                                                                         spawn_fire = 1;
8440                                                                                 else if (count >= 1) {
8441                                                                                         float hull_percent = Pl_objp->hull_strength/sip->initial_hull_strength;
8442
8443                                                                                         if (hull_percent < 0.01f)
8444                                                                                                 hull_percent = 0.01f;
8445
8446                                                                                         if (frand() < 0.25f/(30.0f*hull_percent) * count)       //      With timestamp below, this means could fire in 30 seconds if one enemy.
8447                                                                                                 spawn_fire = 1;
8448                                                                                 }
8449                                                                         }
8450
8451                                                                         if (spawn_fire || (dist_to_enemy < firing_range)) {
8452                                                                                 if (ai_fire_secondary_weapon(Pl_objp)) {
8453                                                                                         //      Only if weapon was fired do we specify time until next fire.  If not fired, done in ai_fire_secondary...
8454                                                                                         float t;
8455                                                                                         
8456                                                                                         if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8457                                                                                                 t = swip->fire_wait;
8458                                                                                         } else {
8459                                                                                                 t = set_secondary_fire_delay(aip, temp_shipp, swip);
8460                                                                                         }
8461                                                                                         //nprintf(("AI", "Next secondary to be fired in %7.3f seconds.\n", t));
8462                                                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
8463                                                                                 }
8464                                                                         } else {
8465                                                                                 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
8466                                                                         }
8467                                                                 }
8468                                                         }
8469                                                 }
8470                                         }
8471                                 }
8472                         }
8473                 } else {
8474                         aip->time_enemy_in_range *= (1.0f - flFrametime);
8475                 }
8476         } else
8477                 aip->time_enemy_in_range *= (1.0f - flFrametime);
8478
8479 }
8480
8481 //      Make the object *objp move so that the point *dp on the object moves towards the point *vp
8482 //      Return distance.
8483 void dock_move_towards_point(object *objp, vector *dp, vector *vp, float speed_scale, float other_obj_speed = 0.0f)
8484 {
8485         physics_info    *pi = &objp->phys_info;
8486         float                           dist;                   //      dist to goal
8487         vector                  v2g;                    //      vector to goal
8488         vector                  abs_pnt;                //      location of dock point, ie objp->pos + db
8489
8490         if (dp == NULL)
8491                 abs_pnt = objp->pos;
8492         else
8493                 vm_vec_add(&abs_pnt, &objp->pos, dp);
8494
8495         dist = vm_vec_dist_quick(vp, &abs_pnt);
8496         if (dist > 0.0f) {
8497                 float   speed;
8498
8499                 dist = vm_vec_normalized_dir(&v2g, vp, &abs_pnt);
8500                 speed = fl_sqrt(dist) * speed_scale;
8501                 if (other_obj_speed < MAX_REPAIR_SPEED*0.75f)
8502                         speed += other_obj_speed;
8503                 else
8504                         speed += MAX_REPAIR_SPEED*0.75f;
8505
8506                 vm_vec_copy_scale(&pi->desired_vel, &v2g, speed);
8507         } else
8508                 vm_vec_zero(&pi->desired_vel);
8509 }
8510
8511 //      Set the orientation in the global reference frame for an object to attain
8512 //      to dock with another object.
8513 //      *dom            resultant global matrix
8514 //      *db_dest        pointer to destination docking bay information
8515 //      *db_src pointer to source docking bay information
8516 //      *dorient        pointer to global orientation of docking bay (ie, the dockee object's orient)
8517 //      *sorient        pointer to global orientation of docker
8518 void set_goal_dock_orient(matrix *dom, dock_bay *db_dest, dock_bay *db_src, matrix *dorient, matrix *sorient)
8519 {
8520         vector  fvec, uvec;
8521         matrix  m1, m2, m3;
8522
8523         //      Compute the global orientation of the docker's (dest) docking bay.
8524         fvec = db_dest->norm[0];
8525         vm_vec_negate(&fvec);
8526
8527         vm_vec_normalized_dir(&uvec, &db_dest->pnt[1], &db_dest->pnt[0]);
8528         vm_vector_2_matrix(&m1, &fvec, &uvec, NULL);
8529
8530         vm_matrix_x_matrix(&m3, dorient, &m1);
8531
8532         //      Compute the matrix given by the source docking bay.
8533         //      Pre-multiply the orientation of the source object (sorient) by the transpose
8534         //      of the docking bay's orientation, ie unrotate the source object's matrix.
8535         fvec = db_src->norm[0];
8536         vm_vec_normalized_dir(&uvec, &db_src->pnt[1], &db_src->pnt[0]);
8537         vm_vector_2_matrix(&m2, &fvec, &uvec, NULL);
8538         vm_transpose(&m2);
8539
8540         vm_matrix_x_matrix(dom, &m3, &m2);
8541 }
8542
8543 #define DOCK_BACKUP_RETURN_VAL  99999.9f
8544
8545 //      Make objp dock with dobjp
8546 //      Returns distance to goal, defined as distance between corresponding dock points, plus 10.0f * rotational velocity vector (DOA_DOCK only)
8547 //      DOA_APPROACH    means   approach point aip->path_cur
8548 //      DOA_DOCK                        means dock
8549 //      DOA_UNDOCK_1    means undock, moving to point nearest dock bay
8550 //      DOA_UNDOCK_2    means undock, moving to point nearest dock bay and facing away from ship
8551 //      DOA_DOCK_STAY   means rigidly maintain position in dock bay.
8552 float dock_orient_and_approach(object *objp, object *dobjp, int dock_mode)
8553 {
8554         ship_info       *sip0, *sip1;
8555         polymodel       *pm0, *pm1;
8556         ai_info         *aip;
8557         matrix          dom, nm;
8558         vector          goal_point, docker_point;
8559         float                   fdist = UNINITIALIZED_VALUE;
8560         int                     docker_index, dockee_index;             // index into docking_bays[] array for objects docking
8561                                                                                                                                 // docker is Pl_objp -- dockee is dobjp
8562         aip = &Ai_info[Ships[objp->instance].ai_index];
8563
8564         //      If dockee has moved much, then path will be recreated.
8565         //      Might need to change state if moved too far.
8566         if ((dock_mode != DOA_DOCK_STAY) && (dock_mode != DOA_DOCK)) {
8567                 if (maybe_recreate_path(objp, &Ai_info[Ships[objp->instance].ai_index], 0) > 5.0f) {
8568 /*                      if (dock_mode == DOA_APPROACH) {
8569                                 return DOCK_BACKUP_RETURN_VAL;
8570                         } else if (dock_mode == DOA_DOCK) {
8571                                 return DOCK_BACKUP_RETURN_VAL;          
8572                         }
8573 */              }
8574         }
8575
8576         objp->phys_info.forward_thrust = 0.0f;          //      Kill thrust so we don't have a sputtering thruster.
8577
8578         sip0 = &Ship_info[Ships[objp->instance].ship_info_index];
8579         sip1 = &Ship_info[Ships[dobjp->instance].ship_info_index];
8580         pm0 = model_get( sip0->modelnum );
8581         pm1 = model_get( sip1->modelnum );
8582
8583         docker_index = aip->dock_index;
8584         dockee_index = aip->dockee_index;
8585
8586         Assert( docker_index >= 0 );
8587         Assert( dockee_index >= 0 );
8588
8589         Assert(pm0->docking_bays[docker_index].num_slots == 2);
8590         Assert(pm1->docking_bays[dockee_index].num_slots == 2);
8591
8592         float speed_scale = 1.0f;
8593         if (sip0->flags & SIF_SUPPORT) {
8594                 speed_scale = 3.0f;
8595         }
8596
8597         switch (dock_mode) {
8598         case DOA_APPROACH:
8599                 {
8600                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8601                         return 9999.9f;
8602                 }
8603                 
8604                 //      Compute the desired global orientation matrix for the docker's station.
8605                 //      That is, the normal vector of the docking station must be the same as the
8606                 //      forward vector and the vector between its two points must be the uvec.
8607                 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8608
8609                 //      Compute new orientation matrix and update rotational velocity.
8610                 vector  w_in, w_out, vel_limit, acc_limit;
8611                 float           tdist, mdist, ss1;
8612
8613                 w_in = objp->phys_info.rotvel;
8614                 vel_limit = objp->phys_info.max_rotvel;
8615                 vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8616                 
8617                 if (sip0->flags & SIF_SUPPORT)
8618                         vm_vec_scale(&acc_limit, 2.0f);
8619
8620                 // 1 at end of line prevent overshoot
8621                 vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit, 1);
8622                 objp->phys_info.rotvel = w_out;
8623                 objp->orient = nm;
8624
8625                 //      Translate towards goal and note distance to goal.
8626                 goal_point = Path_points[aip->path_cur].pos;
8627                 mdist = ai_matrix_dist(&objp->orient, &dom);
8628                 tdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8629
8630                 //      If translation is badly lagging rotation, speed up translation.
8631                 if (mdist > 0.1f) {
8632                         ss1 = tdist/(10.0f * mdist);
8633                         if (ss1 > 2.0f)
8634                                 ss1 = 2.0f;
8635                 } else
8636                         ss1 = 2.0f;
8637
8638                 // nprintf(("AI", "speed scale = %7.3f\n", ss1));
8639                 speed_scale *= 1.0f + ss1;
8640
8641                 dock_move_towards_point(objp, NULL, &goal_point, speed_scale, dobjp->phys_info.speed);
8642
8643                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8644
8645                 //      Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8646                 // nprintf(("AI", "matrix dist = %7.3f, threshold = %7.3f\n", mdist, 2*flFrametime));
8647                 fdist += 2.0f * mdist;
8648
8649                 break;
8650         }
8651         case DOA_DOCK:
8652                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8653                         return 9999.9f;
8654                 }
8655         case DOA_DOCK_STAY:
8656                 //      Compute the desired global orientation matrix for the docker's station.
8657                 //      That is, the normal vector of the docking station must be the same as the
8658                 //      forward vector and the vector between its two points must be the uvec.
8659                 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8660
8661                 //      Compute distance between dock bay points.
8662                 vector  db0, db1, db2, db3;
8663
8664                 vm_vec_unrotate(&db0, &pm0->docking_bays[docker_index].pnt[0], &objp->orient);
8665                 vm_vec_add2(&db0, &objp->pos);
8666
8667                 vm_vec_unrotate(&db1, &pm0->docking_bays[docker_index].pnt[1], &objp->orient);
8668                 vm_vec_add2(&db1, &objp->pos);
8669
8670                 vm_vec_unrotate(&db2, &pm1->docking_bays[dockee_index].pnt[0], &dobjp->orient);
8671                 vm_vec_add2(&db2, &dobjp->pos);
8672
8673                 vm_vec_unrotate(&db3, &pm1->docking_bays[dockee_index].pnt[1], &dobjp->orient);
8674                 vm_vec_add2(&db3, &dobjp->pos);
8675
8676                 vm_vec_avg(&goal_point, &db2, &db3);
8677
8678                 vm_vec_avg(&docker_point, &db0, &db1);
8679                 vm_vec_sub2(&docker_point, &objp->pos);
8680
8681                 if (dock_mode == DOA_DOCK) {
8682                         vector  t1, t2;
8683                         vector  w_in, w_out, vel_limit, acc_limit;
8684
8685                         fdist = vm_vec_dist_quick(vm_vec_avg(&t1, &db0, &db1), vm_vec_avg(&t2, &db2, &db3));
8686
8687                         //      Compute new orientation matrix and update rotational velocity.
8688                         w_in = objp->phys_info.rotvel;
8689                         vel_limit = objp->phys_info.max_rotvel;
8690                         vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8691
8692                         if (sip0->flags & SIF_SUPPORT)
8693                                 vm_vec_scale(&acc_limit, 2.0f);
8694
8695                         vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit);
8696                         objp->phys_info.rotvel = w_out;
8697                         objp->orient = nm;
8698
8699                         //      Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8700                         fdist += 10.0f * vm_vec_mag_quick(&w_out);
8701
8702                         dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale, dobjp->phys_info.speed);
8703                 } else {
8704                         Assert(dock_mode == DOA_DOCK_STAY);
8705                         objp->orient = dom;
8706                         vector  temp;
8707                         vm_vec_sub(&temp, &goal_point, &docker_point);
8708                         vm_vec_sub(&objp->pos, &goal_point, &docker_point);
8709                 }
8710
8711                 break;
8712         case DOA_UNDOCK_1: {
8713                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8714                         return 9999.9f;
8715                 }
8716
8717                 //      Undocking.
8718                 //      Move to point on dock path nearest to dock station.
8719                 Assert(aip->path_length >= 2);
8720                 goal_point = Path_points[aip->path_start + aip->path_length-2].pos;
8721
8722                 vm_vec_zero(&docker_point);
8723                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8724
8725                 dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale);
8726
8727                 break;
8728                           }
8729
8730         case DOA_UNDOCK_2: {
8731                 //      Undocking.
8732                 //      Move to point on dock path nearest to dock station and orient away from big ship.
8733                 int             desired_index;
8734
8735                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8736                         return 9999.9f;
8737                 }
8738
8739                 Assert(aip->path_length >= 2);
8740 //              if (aip->path_length >= 3)
8741 //                      desired_index = aip->path_length-3;
8742 //              else
8743                         desired_index = aip->path_length-2;
8744
8745                 goal_point = Path_points[aip->path_start + desired_index].pos;
8746
8747                 dock_move_towards_point(objp, NULL, &goal_point, speed_scale);
8748
8749                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8750                 break;
8751                           }
8752         case DOA_UNDOCK_3: {
8753                 float           dist, goal_dist;
8754                 vector  away_vec;
8755
8756                 goal_dist = objp->radius + dobjp->radius + 25.0f;
8757
8758                 dist = vm_vec_normalized_dir(&away_vec, &objp->pos, &dobjp->pos);
8759                 vm_vec_scale_add(&goal_point, &dobjp->pos, &away_vec, goal_dist);
8760                 if (vm_vec_dist_quick(&goal_point, &dobjp->pos) < vm_vec_dist_quick(&objp->pos, &dobjp->pos))
8761                         fdist = 0.0f;
8762                 else {
8763                         float   dot, accel;
8764                         float turn_time = Ship_info[Ships[objp->instance].ship_info_index].srotation_time;
8765                         ai_turn_towards_vector(&goal_point, objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
8766
8767                         dot = vm_vec_dot(&objp->orient.v.fvec, &away_vec);
8768                         accel = 0.1f;
8769                         if (dot > accel)
8770                                 accel = dot;
8771                         if (dist > goal_dist/2)
8772                                 accel *= 1.2f - 0.5f*goal_dist/dist;
8773
8774                         accelerate_ship(aip, accel);
8775                         fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8776                 }
8777
8778                 break;
8779                                                          }
8780         }
8781
8782 #ifndef NDEBUG
8783         //      For debug purposes, compute global orientation of both dock vectors and show
8784         //      how close they are.
8785         vector  d0, d1;
8786
8787         vm_vec_unrotate(&d0, &pm0->docking_bays[docker_index].norm[0], &objp->orient);
8788         vm_vec_unrotate(&d1, &pm1->docking_bays[dockee_index].norm[0], &dobjp->orient);
8789
8790         //nprintf(("AI", "or/app: dist = %7.3f/%7.3f, dot = %7.3f, global dot = %7.3f\n", 
8791         //      vm_vec_dist_quick(&goal_point, &objp->pos), fdist,
8792         //      vm_vec_dot(&objp->orient.v.fvec, &dom.v.fvec), 
8793         //      vm_vec_dot(&d0, &d1)));
8794 #endif
8795
8796         // -- Note, A lot of callers don't care about fdist, so OK to return ERROR value: Assert(fdist != UNINITIALIZED_VALUE);
8797         return fdist;
8798
8799 }
8800
8801 void debug_find_guard_object()
8802 {
8803         ship                    *shipp = &Ships[Pl_objp->instance];     
8804         object          *objp;
8805
8806         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
8807                 if ((Pl_objp != objp) && (objp->type == OBJ_SHIP)) {
8808                         if (objp->instance != -1) {
8809                                 if (Ships[objp->instance].team == shipp->team)  {
8810                                         // nprintf(("AI", "Setting guard object for %s to %s\n", shipp->ship_name, Ships[objp->instance].ship_name));
8811                                         ai_set_guard_object(Pl_objp, objp);
8812                                 }
8813                         }
8814                 }
8815         }
8816
8817 }
8818
8819 //      Given an object number, return the number of ships attacking it.
8820 int num_ships_attacking(int objnum)
8821 {
8822         object  *objp;
8823         ship_obj        *so;
8824         int             count = 0;
8825
8826         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8827                 objp = &Objects[so->objnum];
8828                 if (objp->instance != -1) {
8829                         ai_info *aip;
8830                         aip = &Ai_info[Ships[objp->instance].ai_index];
8831
8832                         if ((aip->mode == AIM_CHASE) && (aip->target_objnum == objnum))
8833                                 if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team)
8834                                         count++;
8835                 }
8836         }
8837
8838         return count;
8839 }
8840
8841 //      For all objects attacking object #objnum, remove the one that is farthest away.
8842 //      Do this by resuming previous behavior, if any.  If not, set target_objnum to -1.
8843 void remove_farthest_attacker(int objnum)
8844 {
8845         object  *objp, *objp2, *farthest_objp;
8846         ship_obj        *so;
8847         float           farthest_dist;
8848
8849         objp2 = &Objects[objnum];
8850
8851         farthest_dist = 9999999.9f;
8852         farthest_objp = NULL;
8853
8854         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8855                 objp = &Objects[so->objnum];
8856                 if ( !(objp->flags & OF_PLAYER_SHIP)) {
8857                         if (objp->instance != -1) {
8858                                 ai_info *aip2;
8859
8860                                 aip2 = &Ai_info[Ships[objp->instance].ai_index];
8861
8862                                 if ((aip2->mode == AIM_CHASE) && (aip2->target_objnum == objnum)) {
8863                                         if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team) {
8864                                                 float   dist;
8865
8866                                                 dist = vm_vec_dist_quick(&objp->pos, &objp2->pos);
8867                                                 if (dist < farthest_dist) {
8868                                                         farthest_dist = dist;
8869                                                         farthest_objp = objp;
8870                                                 }
8871                                         }
8872                                 }
8873                         }
8874                 }
8875         }
8876
8877         if (farthest_objp != NULL) {
8878                 ai_info *aip;
8879                 Assert(farthest_objp->type == OBJ_SHIP);
8880                 Assert((farthest_objp->instance > -1) && (farthest_objp->instance < MAX_SHIPS));
8881                 Assert(Ships[farthest_objp->instance].ai_index > -1);
8882
8883                 aip = &Ai_info[Ships[farthest_objp->instance].ai_index];
8884
8885                 if (!maybe_resume_previous_mode(Pl_objp, aip)) {
8886                         //      If already ignoring something under player's orders, don't ignore current target.
8887                         if ((aip->ignore_objnum == UNUSED_OBJNUM) || (aip->ai_flags & AIF_TEMPORARY_IGNORE)) {
8888                                 aip->ignore_objnum = aip->target_objnum;
8889                                 aip->ignore_signature = Objects[aip->target_objnum].signature;
8890                                 aip->ai_flags |= AIF_TEMPORARY_IGNORE;
8891                                 aip->ignore_expire_timestamp = timestamp(((myrand() % 10) + 20) * 1000);        //      OK to attack again in 20 to 24 seconds.
8892                         }
8893                         aip->target_objnum = -1;
8894                         ai_do_default_behavior(farthest_objp);
8895                 }
8896         }
8897 }
8898
8899 // Maybe limit the number of attackers on attack_objnum.  For now, only limit attackers
8900 // in attacked_objnum is the player
8901 // input:       attacked_objnum =>              object index for ship we want to limit attacks on
8902 //
8903 //      exit:                   1       =>      num attackers exceeds maximum, abort
8904 //                                      0       =>      removed the farthest attacker
8905 //                                      -1      =>      nothing was done
8906 int ai_maybe_limit_attackers(int attacked_objnum)
8907 {
8908         int rval=-1;
8909
8910         // limit the number of ships attacking the _player_ only
8911 //      if ( attacked_objnum == OBJ_INDEX(Player_obj) ) {
8912         if ( Objects[attacked_objnum].flags & OF_PLAYER_SHIP) {
8913                 int num_attacking;
8914                 num_attacking = num_ships_attacking(attacked_objnum);
8915
8916                 if (num_attacking == Skill_level_max_attackers[Game_skill_level]) {
8917                         remove_farthest_attacker(attacked_objnum);
8918                         rval=0;
8919                 } else if (num_attacking > Skill_level_max_attackers[Game_skill_level]) {
8920                         rval=1;
8921                 }
8922                 //nprintf(("AI", "Num attacking player = %i\n", num_attacking));
8923         }
8924
8925         return rval;
8926 }
8927
8928 //      Object being guarded by object *guard_objp was hit by object *hitter_objp
8929 void guard_object_was_hit(object *guard_objp, object *hitter_objp)
8930 {
8931         int             hitter_objnum;
8932         ai_info *aip;
8933
8934         aip = &Ai_info[Ships[guard_objp->instance].ai_index];
8935
8936         if (guard_objp == hitter_objp) {
8937                 // Int3();      //      Bogus!  Who tried to get me to attack myself!  Trace out and fix!
8938                 return;
8939         }
8940
8941         if (guard_objp->type == OBJ_GHOST || hitter_objp->type == OBJ_GHOST)
8942                 return;
8943
8944         if (aip->ai_flags & AIF_NO_DYNAMIC)     //      Not allowed to pursue dynamic goals.  So, why are we guarding?
8945                 return;
8946
8947         Assert( (hitter_objp->type == OBJ_SHIP) || (hitter_objp->type == OBJ_ASTEROID) || (hitter_objp->type == OBJ_WEAPON) );
8948
8949         hitter_objnum = OBJ_INDEX(hitter_objp);
8950
8951         if ( hitter_objp->type == OBJ_SHIP ) {
8952                 //      If the hitter object is the ignore object, don't attack it.
8953                 if (is_ignore_object(aip, hitter_objp-Objects))
8954                         return;
8955
8956                 //      If hitter is on same team as me, don't attack him.
8957                 if (Ships[guard_objp->instance].team == Ships[hitter_objp->instance].team)
8958                         return;
8959
8960                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
8961                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
8962                         return;
8963                 }
8964
8965                 // dont attack if you can't see him
8966                 if ( awacs_get_level(hitter_objp, &Ships[aip->shipnum], 1) < 1 ) {
8967                         // if he's a stealth and visible, but not targetable, ok to attack.
8968                         if ( is_object_stealth_ship(hitter_objp) ) {
8969                                 if ( ai_is_stealth_visible(guard_objp, hitter_objp) != STEALTH_VISIBLE ) {
8970                                         return;
8971                                 }
8972                         }
8973                 }
8974         }
8975
8976         if (aip->target_objnum == -1) {
8977                 aip->ok_to_target_timestamp = timestamp(0);
8978         }
8979
8980         if ((aip->submode == AIS_GUARD_PATROL) || (aip->submode == AIS_GUARD_STATIC)) {
8981
8982                 if ( hitter_objp->type == OBJ_SHIP ) {
8983                         if (!(Ship_info[Ships[guard_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
8984                                 return;
8985                         }
8986
8987                         // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
8988                         if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
8989                                 return;
8990                         }
8991                 }
8992
8993                 if (aip->target_objnum != hitter_objnum) {
8994                         aip->aspect_locked_time = 0.0f;
8995                 }
8996
8997                 aip->ok_to_target_timestamp = timestamp(0);
8998
8999                 set_target_objnum(aip, hitter_objnum);
9000                 //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));
9001                 aip->previous_mode = AIM_GUARD;
9002                 aip->previous_submode = aip->submode;
9003                 aip->mode = AIM_CHASE;
9004                 aip->submode = SM_ATTACK;
9005                 aip->submode_start_time = Missiontime;
9006                 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9007         } else if (aip->previous_mode == AIM_GUARD) {
9008                 if (aip->target_objnum == -1) {
9009
9010                         if ( hitter_objp->type == OBJ_SHIP ) {
9011                                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9012                                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9013                                         return;
9014                                 }
9015                         }
9016
9017                         set_target_objnum(aip, hitter_objnum);
9018                 //if (aip->target_objnum == -1) nprintf(("AI", "Frame %i: Attacking NONE\n",Framecount)); else nprintf(("AI", "Frame %i: Attacking %s\n", Framecount, Ships[Objects[aip->target_objnum].instance].ship_name));
9019                         aip->mode = AIM_CHASE;
9020                         aip->submode = SM_ATTACK;
9021                         aip->submode_start_time = Missiontime;
9022                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9023                 } else {
9024                         int     num_attacking_cur, num_attacking_new;
9025
9026                         num_attacking_cur = num_ships_attacking(aip->target_objnum);
9027                         if (num_attacking_cur > 1) {
9028                                 num_attacking_new = num_ships_attacking(hitter_objnum);
9029
9030                                 if (num_attacking_new < num_attacking_cur) {
9031
9032                                         if ( hitter_objp->type == OBJ_SHIP ) {
9033                                                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9034                                                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9035                                                         return;
9036                                                 }
9037                                         }
9038                                         set_target_objnum(aip, hitter_objp-Objects);
9039                 //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));
9040                                         aip->mode = AIM_CHASE;
9041                                         aip->submode = SM_ATTACK;
9042                                         aip->submode_start_time = Missiontime;
9043                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9044                                 }
9045                         }
9046                 }
9047         }
9048 }
9049
9050 //      Ship object *hit_objp was hit by ship object *hitter_objp.
9051 //      See if anyone is guarding hit_objp and, if so, do something useful.
9052 void maybe_update_guard_object(object *hit_objp, object *hitter_objp)
9053 {
9054         object  *objp;
9055         ship_obj        *so;
9056
9057         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9058                 objp = &Objects[so->objnum];
9059                 if (objp->instance != -1) {
9060                         ai_info *aip;
9061                         aip = &Ai_info[Ships[objp->instance].ai_index];
9062
9063                         if ((aip->mode == AIM_GUARD) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
9064                                 if (aip->guard_objnum == hit_objp-Objects) {
9065                                         guard_object_was_hit(objp, hitter_objp);
9066                                 } else if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[hit_objp->instance].ai_index].wing)) {
9067                                         guard_object_was_hit(objp, hitter_objp);
9068                                 }
9069                         }
9070                 }
9071         }
9072 }
9073
9074 // Scan missile list looking for bombs homing on guarded_objp
9075 // return 1 if bomb is found (and targeted by guarding_objp), otherwise return 0
9076 int ai_guard_find_nearby_bomb(object *guarding_objp, object *guarded_objp)
9077 {       
9078         missile_obj     *mo;
9079         object          *bomb_objp, *closest_bomb_objp=NULL;
9080         float                   dist, dist_to_guarding_obj,closest_dist_to_guarding_obj=999999.0f;
9081         weapon          *wp;
9082         weapon_info     *wip;
9083
9084         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
9085                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
9086                 bomb_objp = &Objects[mo->objnum];
9087
9088                 wp = &Weapons[bomb_objp->instance];
9089                 wip = &Weapon_info[wp->weapon_info_index];
9090
9091                 if ( !(wip->wi_flags & WIF_BOMB) ) {
9092                         continue;
9093                 }
9094
9095                 if ( wp->homing_object != guarded_objp ) {
9096                         continue;
9097                 }
9098
9099                 dist = vm_vec_dist_quick(&bomb_objp->pos, &guarded_objp->pos);
9100
9101                 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9102                         dist_to_guarding_obj = vm_vec_dist_quick(&bomb_objp->pos, &guarding_objp->pos);
9103                         if ( dist_to_guarding_obj < closest_dist_to_guarding_obj ) {
9104                                 closest_dist_to_guarding_obj = dist_to_guarding_obj;
9105                                 closest_bomb_objp = bomb_objp;
9106                         }
9107                 }
9108         }
9109
9110         if ( closest_bomb_objp ) {
9111                 guard_object_was_hit(guarding_objp, closest_bomb_objp);
9112                 return 1;
9113         }
9114
9115         return 0;
9116 }
9117
9118 //      Scan enemy ships and see if one is near enough to guard object to be pursued.
9119 void ai_guard_find_nearby_ship(object *guarding_objp, object *guarded_objp)
9120 {
9121         ship            *guarding_shipp = &Ships[guarding_objp->instance];
9122         ai_info *guarding_aip = &Ai_info[guarding_shipp->ai_index];
9123         ship_obj        *so;
9124         object  *enemy_objp;
9125         float           dist;
9126
9127         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9128                 enemy_objp = &Objects[so->objnum];
9129
9130                 if (enemy_objp->instance < 0) {
9131                         continue;
9132                 }
9133
9134                 ship    *eshipp = &Ships[enemy_objp->instance];
9135
9136                 //      Don't attack a cargo container or other harmless ships
9137                 if (!(Ship_info[eshipp->ship_info_index].flags & SIF_HARMLESS)) {
9138                         if (guarding_shipp->team != eshipp->team)       {
9139                                 dist = vm_vec_dist_quick(&enemy_objp->pos, &guarded_objp->pos);
9140                                 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9141                                         guard_object_was_hit(guarding_objp, enemy_objp);
9142                                 } else if ((dist < 3000.0f) && (Ai_info[eshipp->ai_index].target_objnum == guarding_aip->guard_objnum)) {
9143                                         //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));
9144                                         guard_object_was_hit(guarding_objp, enemy_objp);
9145                                 }
9146                         }
9147                 }
9148         }
9149 }
9150
9151 // Scan for nearby asteroids.  Favor asteroids which have their collide_objnum set to that of the
9152 // guarded ship.  Also, favor asteroids that are closer to the guarding ship, since it looks cooler
9153 // when a ship blows up an asteroid then goes after the pieces that break off.
9154 void ai_guard_find_nearby_asteroid(object *guarding_objp, object *guarded_objp)
9155 {       
9156         float           dist;
9157
9158         object  *closest_asteroid_objp=NULL, *danger_asteroid_objp=NULL, *asteroid_objp;
9159         float           dist_to_self, closest_danger_asteroid_dist=999999.0f, closest_asteroid_dist=999999.0f;
9160
9161         for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp != END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) {
9162                 if ( asteroid_objp->type == OBJ_ASTEROID ) {
9163                         // Attack asteroid if near guarded ship
9164                         dist = vm_vec_dist_quick(&asteroid_objp->pos, &guarded_objp->pos);
9165                         if ( dist < (MAX_GUARD_DIST + guarded_objp->radius)*2) {
9166                                 dist_to_self = vm_vec_dist_quick(&asteroid_objp->pos, &guarding_objp->pos);
9167                                 if ( OBJ_INDEX(guarded_objp) == asteroid_collide_objnum(asteroid_objp) ) {
9168                                         if( dist_to_self < closest_danger_asteroid_dist ) {
9169                                                 danger_asteroid_objp=asteroid_objp;
9170                                                 closest_danger_asteroid_dist=dist_to_self;
9171                                         }
9172                                 } 
9173                                 if ( dist_to_self < closest_asteroid_dist ) {
9174                                         // only attack if moving slower than own max speed
9175                                         if ( vm_vec_mag_quick(&asteroid_objp->phys_info.vel) < guarding_objp->phys_info.max_vel.xyz.z ) {
9176                                                 closest_asteroid_dist = dist_to_self;
9177                                                 closest_asteroid_objp = asteroid_objp;
9178                                         }
9179                                 }
9180                         }
9181                 }
9182         }
9183
9184         if ( danger_asteroid_objp ) {
9185                 guard_object_was_hit(guarding_objp, danger_asteroid_objp);
9186         } else if ( closest_asteroid_objp ) {
9187                 guard_object_was_hit(guarding_objp, closest_asteroid_objp);
9188         }
9189 }
9190
9191 //      Scan potential harmful objects and see if one is near enough to guard object to be pursued.
9192 void ai_guard_find_nearby_object()
9193 {
9194         ship                    *shipp = &Ships[Pl_objp->instance];
9195         ai_info         *aip = &Ai_info[shipp->ai_index];
9196         object          *guardobjp;
9197         int                     bomb_found=0;
9198
9199         guardobjp = &Objects[aip->guard_objnum];
9200         
9201         // highest priority is a bomb fired on guarded ship
9202         bomb_found = ai_guard_find_nearby_bomb(Pl_objp, guardobjp);
9203
9204         if ( !bomb_found ) {
9205                 // check for ships if there are no bombs fired at guarded ship
9206                 ai_guard_find_nearby_ship(Pl_objp, guardobjp);
9207
9208                 // if not attacking anything, go for asteroid close to guarded ship
9209                 if ( (aip->target_objnum == -1) && asteroid_count() ) {
9210                         ai_guard_find_nearby_asteroid(Pl_objp, guardobjp);
9211                 }
9212         }
9213 }
9214
9215 // gets closest point on extended axis of cylinder, r_vec, and radius of cylinder
9216 // returns z of axis_point in cyl_objp reference frame
9217 float get_cylinder_points(object *other_objp, object *cyl_objp, vector *axis_pt, vector *r_vec, float *radius)
9218 {
9219         Assert(other_objp->type == OBJ_SHIP);
9220         Assert(cyl_objp->type == OBJ_SHIP);
9221
9222         // get radius of cylinder
9223         polymodel *pm = model_get(Ships[cyl_objp->instance].modelnum);
9224         float tempx, tempy;
9225         tempx = max(-pm->mins.xyz.x, pm->maxs.xyz.x);
9226         tempy = max(-pm->mins.xyz.y, pm->maxs.xyz.y);
9227         *radius = max(tempx, tempy);
9228
9229         // get vec from cylinder to other_obj
9230         vector r_sph;
9231         vm_vec_sub(&r_sph, &other_objp->pos, &cyl_objp->pos);
9232
9233         // get point on axis and on cylinder
9234         // extended_cylinder_z is along extended cylinder
9235         // cylinder_z is capped within cylinder
9236         float extended_cylinder_z = vm_vec_dotprod(&r_sph, &cyl_objp->orient.v.fvec);
9237
9238         // get pt on axis of extended cylinder
9239         vm_vec_scale_add(axis_pt, &cyl_objp->pos, &cyl_objp->orient.v.fvec, extended_cylinder_z);
9240
9241         // get r_vec (pos - axis_pt) normalized
9242         vm_vec_normalized_dir(r_vec, &other_objp->pos, axis_pt);
9243
9244         return extended_cylinder_z;
9245 }
9246
9247 // handler for guard behavior when guarding BIG ships
9248 //      When someone has attacked guarded ship, then attack that ship.
9249 // To attack another ship, switch out of guard mode into chase mode.
9250 void ai_big_guard()
9251 {
9252         
9253         ship                    *shipp = &Ships[Pl_objp->instance];
9254         ai_info         *aip = &Ai_info[shipp->ai_index];
9255         object          *guard_objp;
9256
9257         // sanity checks already done in ai_guard()
9258         guard_objp = &Objects[aip->guard_objnum];
9259
9260         switch (aip->submode) {
9261         case AIS_GUARD_STATIC:
9262         case AIS_GUARD_PATROL:
9263                 {
9264                 vector axis_pt, r_vec, theta_vec;
9265                 float radius, extended_z;
9266
9267                 // get random [0 to 1] based on OBJNUM
9268                 float objval = static_randf(Pl_objp-Objects);
9269
9270                 // get position relative to cylinder of guard_objp              
9271                 extended_z = get_cylinder_points(Pl_objp, guard_objp, &axis_pt, &r_vec, &radius);
9272                 vm_vec_crossprod(&theta_vec, &guard_objp->orient.v.fvec, &r_vec);
9273
9274                 // half ships circle each way
9275                 if (objval > 0.5f) {
9276                         vm_vec_negate(&theta_vec);
9277                 }
9278
9279                 float min_guard_dist = radius + Pl_objp->radius + 50.0f;
9280                 float desired_guard_dist = min_guard_dist + 0.5f * ((1.0f + objval) * MAX_GUARD_DIST);
9281                 float max_guard_dist =     min_guard_dist + 1.0f * ((1.0f + objval) * MAX_GUARD_DIST);
9282
9283                 // get z extents
9284                 float min_z, max_z, length;
9285                 polymodel *pm = model_get(Ships[guard_objp->instance].modelnum);
9286                 min_z = pm->mins.xyz.z;
9287                 max_z = pm->maxs.xyz.z;
9288                 length = max_z - min_z;
9289
9290                 // get desired z
9291                 // how often to choose new desired_z
9292                 // 1*(64) sec < 2000, 2*(64) < 2-4000 3*(64) > 4-8000, etc (Missiontime >> 22 is 64 sec intervals)
9293                 int time_choose = int(floor(log(length * 0.001) / log(2)));
9294                 float desired_z = min_z + length * static_randf( Pl_objp-Objects ^ (Missiontime >> (22 + time_choose)) );
9295
9296                 // get r from guard_ship
9297                 float cur_guard_rad = vm_vec_dist(&Pl_objp->pos, &axis_pt);
9298
9299                 // is ship within extents of cylinder of ship it is guarding
9300                 int inside = (extended_z > min_z) && (extended_z < min_z + length);
9301
9302                 vector goal_pt;
9303                 // maybe go into orbit mode
9304                 if (cur_guard_rad < max_guard_dist) {
9305                         if ( cur_guard_rad > min_guard_dist ) {
9306                                 if (inside) {
9307                                         // orbit
9308                                         vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, desired_guard_dist);
9309                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9310                                 } else {
9311                                         // move to where I can orbit
9312                                         if (extended_z < min_z) {
9313                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, min_z);
9314                                         } else {
9315                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, max_z);
9316                                         }
9317                                         vm_vec_scale_add2(&goal_pt, &r_vec, desired_guard_dist);
9318                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9319                                 }
9320                         } else {
9321                                 // too close for orbit mode
9322                                 if (inside) {
9323                                         // inside (fly straight out and return circle)
9324                                         vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, max_guard_dist);
9325                                 } else {
9326                                         // outside (fly to edge and circle)
9327                                         if (extended_z < min_z) {
9328                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, min_z);
9329                                         } else {
9330                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, max_z);
9331                                         }
9332                                         vm_vec_scale_add2(&goal_pt, &r_vec, max_guard_dist);
9333                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9334                                 }
9335                         }
9336
9337                         if (Pl_objp->phys_info.fspeed > 0) {
9338                                 // modify goal_pt to take account moving guard objp
9339                                 float dist = vm_vec_dist_quick(&Pl_objp->pos, &goal_pt);
9340                                 float time = dist / Pl_objp->phys_info.fspeed;
9341                                 vm_vec_scale_add2(&goal_pt, &guard_objp->phys_info.vel, time);
9342
9343                                 // now modify to move to desired z (at a max of 20 m/s)
9344                                 float delta_z = desired_z - extended_z;
9345                                 float v_z = delta_z * 0.2f;
9346                                 if (v_z < -20) {
9347                                         v_z = -20.0f;
9348                                 } else if (v_z > 20) {
9349                                         v_z = 20.0f;
9350                                 }
9351
9352                                 vm_vec_scale_add2(&goal_pt, &guard_objp->orient.v.fvec, v_z*time);
9353                         }
9354
9355                 } else {
9356                         // cast vector to center of guard_ship adjusted by desired_z
9357                         float delta_z = desired_z - extended_z;
9358                         vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, delta_z);
9359                 }
9360
9361                 // try not to bump into things along the way
9362                 if ( (cur_guard_rad > max_guard_dist) || (extended_z < min_z) || (extended_z > max_z) ) {
9363                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9364                                 return;
9365                         }
9366
9367                         if (avoid_player(Pl_objp, &goal_pt)) {
9368                                 return;
9369                         }
9370                 } else {
9371                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9372                                 return;
9373                         }
9374                 }
9375
9376                 // got the point, now let's go there
9377                 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);
9378 //              aip->goal_point = goal_pt;
9379                 accelerate_ship(aip, 1.0f);
9380
9381                 //      Periodically, scan for a nearby ship to attack.
9382                 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9383                         ai_guard_find_nearby_object();
9384                 }
9385                 }
9386                 break;
9387
9388         case AIS_GUARD_ATTACK:
9389                 //      The guarded ship has been attacked.  Do something useful!
9390                 ai_chase();
9391                 break;
9392
9393         default:
9394                 //Int3();       //      Illegal submode for Guard mode.
9395                 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9396                 aip->submode = AIS_GUARD_PATROL;
9397                 break;
9398         }
9399 }
9400
9401 //      Main handler for guard behavior.
9402 //      When someone has attacked guarded ship, then attack that ship.
9403 // To attack another ship, switch out of guard mode into chase mode.
9404 void ai_guard()
9405 {
9406         ship                    *shipp = &Ships[Pl_objp->instance];
9407         ai_info         *aip = &Ai_info[shipp->ai_index];
9408         object          *guard_objp;    
9409         ship                    *gshipp;
9410         float                   dist_to_guardobj, dot_to_guardobj;
9411         vector          vec_to_guardobj;
9412
9413         /*      //      Debug code, find an object to guard.
9414         int finding_guard_objnum = 0;   //      Debug code, to see if body of "if" below gets executed. 
9415         if (aip->guard_objnum == -1) {
9416                 finding_guard_objnum = 1;
9417                 debug_find_guard_object();
9418                 if (aip->guard_objnum == -1)
9419                         return;
9420         }
9421 */
9422         if (aip->guard_objnum == -1) {
9423                 aip->mode = AIM_NONE;
9424                 return;
9425         }
9426
9427         Assert(aip->guard_objnum != -1);
9428
9429         guard_objp = &Objects[aip->guard_objnum];
9430
9431         if (guard_objp == Pl_objp) {
9432                 Int3();         //      This seems illegal.  Why is a ship guarding itself?
9433                 aip->guard_objnum = -1;
9434                 return;
9435         }
9436
9437         // check that I have someone to guard
9438         if (guard_objp->instance == -1) {
9439                 return;
9440         }
9441
9442         //      Not sure whether this should be impossible, or a reasonable cleanup condition.
9443         //      For now (3/31/97), it's getting trapped by an Assert, so clean it up.
9444         if (guard_objp->type != OBJ_SHIP) {
9445                 aip->guard_objnum = -1;
9446                 return;
9447         }
9448
9449         // handler for gurad object with BIG radius
9450         if (guard_objp->radius > BIG_GUARD_RADIUS) {
9451                 ai_big_guard();
9452                 return;
9453         }
9454
9455         gshipp = &Ships[guard_objp->instance];
9456
9457         float                   objval;
9458         vector          goal_point;
9459         vector          rel_vec;
9460         float                   dist_to_goal_point, dot_to_goal_point, accel_scale;
9461         vector          v2g, rvec;
9462
9463         // get random [0 to 1] based on OBJNUM
9464         objval = static_randf(Pl_objp-Objects);
9465
9466         switch (aip->submode) {
9467         case AIS_GUARD_STATIC:
9468         case AIS_GUARD_PATROL:
9469                 //      Stay near ship
9470                 dist_to_guardobj = vm_vec_normalized_dir(&vec_to_guardobj, &guard_objp->pos, &Pl_objp->pos);
9471                 dot_to_guardobj = vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_to_guardobj);
9472
9473                 rel_vec = aip->guard_vec;
9474                 vm_vec_add(&goal_point, &guard_objp->pos, &rel_vec);
9475
9476                 vm_vec_normalized_dir(&v2g, &goal_point, &Pl_objp->pos);
9477                 dist_to_goal_point = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
9478                 dot_to_goal_point = vm_vec_dot(&v2g, &Pl_objp->orient.v.fvec);
9479                 accel_scale = (1.0f + dot_to_goal_point)/2.0f;
9480
9481                 //      If far away, get closer
9482                 if (dist_to_goal_point > MAX_GUARD_DIST + 1.5 * (Pl_objp->radius + guard_objp->radius)) {
9483                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 5.0f)) {
9484                                 return;
9485                         }
9486
9487                         if (avoid_player(Pl_objp, &goal_point)) {
9488                                 return;
9489                         }
9490
9491                         // quite far away, so try to go straight to 
9492                         compute_desired_rvec(&rvec, &goal_point, &Pl_objp->pos);
9493                         ai_turn_towards_vector(&goal_point, Pl_objp, flFrametime, Ship_info[shipp->ship_info_index].srotation_time, NULL, NULL, 0.0f, 0, &rvec);
9494
9495                         accelerate_ship(aip, accel_scale * (0.25f + dist_to_goal_point/700.0f));
9496                 } else {
9497                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 2.0f)) {
9498                                 return;
9499                         }
9500
9501                         // get max of guard_objp (1) normal speed (2) dock speed
9502                         float speed = guard_objp->phys_info.speed;
9503
9504                         if (guard_objp->type == OBJ_SHIP) {
9505                                 ai_info *guard_aip = &Ai_info[Ships[guard_objp->instance].ai_index];
9506
9507                                 if (guard_aip->dock_objnum != -1) {
9508                                         speed = max(speed, Objects[guard_aip->dock_objnum].phys_info.speed);
9509                                 }
9510                         }
9511                         
9512                         //      Deal with guarding a small object.
9513                         //      If going to guard_vec might cause a collision with guarded object, pick a new guard point.
9514                         if (vm_vec_dot(&v2g, &vec_to_guardobj) > 0.8f) {
9515                                 if (dist_to_guardobj < dist_to_goal_point) {
9516                                         ai_set_guard_vec(Pl_objp, guard_objp);  //      OK to return here.
9517                                         return;
9518                                 }
9519                         } 
9520
9521                         if (speed > 10.0f) {
9522                                 //      If goal ship is moving more than a tiny bit, don't orbit it, get near it.
9523                                 if (vm_vec_dist_quick(&goal_point, &Pl_objp->pos) > 40.0f) {
9524                                         if (vm_vec_dot(&Pl_objp->orient.v.fvec, &v2g) < 0.0f) {
9525                                                 //      Just slow down, don't turn.
9526                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point/10.0f);
9527                                         } else {
9528                                                 //      Goal point is in front.
9529
9530                                                 //      If close to goal point, don't change direction, just change speed.
9531                                                 if (dist_to_goal_point > Pl_objp->radius + 10.0f) {
9532                                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9533                                                 }
9534                                                 
9535                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + (dist_to_goal_point-40.0f)/20.0f);
9536                                         }
9537                                 } else {
9538                                         if (dot_to_goal_point > 0.8f) {
9539                                                 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9540                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + dist_to_goal_point*0.1f);
9541                                         } else {
9542                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point*0.1f - 1.0f);
9543                                         }
9544                                 }
9545                         // consider guard object STILL
9546                         } else if (guard_objp->radius < 50.0f) {
9547                                 if (dist_to_goal_point > 15.0f) {
9548                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9549                                         set_accel_for_target_speed(Pl_objp, (dist_to_goal_point-10.0f)/2.0f);
9550                                 } else if (Pl_objp->phys_info.speed < 1.0f) {
9551                                         turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9552                                 }
9553                                 //      It's a big ship
9554                         } else if (dist_to_guardobj > MAX_GUARD_DIST + Pl_objp->radius + guard_objp->radius) {
9555                                 //      Orbiting ship, too far away
9556                                 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.0f + objval/2) * guard_objp->radius);
9557                                 accelerate_ship(aip, (1.0f + dot)/2.0f);
9558                         } else if (dist_to_guardobj < Pl_objp->radius + guard_objp->radius) {
9559                                 //      Orbiting ship, got too close
9560                                 turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9561                                 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9562                                         change_acceleration(aip, 0.25f);
9563                                 else
9564                                         accelerate_ship(aip, 0.5f + objval/4.0f);
9565                         } else {
9566                                 //      Orbiting ship, about the right distance away.
9567                                 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.5f + objval/2.0f)*guard_objp->radius);
9568                                 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9569                                         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));
9570                                 else
9571                                         accelerate_ship(aip, 0.5f * (1.0f + dot) * (0.3f + objval/3.0f));
9572                         }
9573                 }
9574
9575                 //      Periodically, scan for a nearby ship to attack.
9576                 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9577                         ai_guard_find_nearby_object();
9578                 }
9579                 break;
9580
9581         case AIS_GUARD_ATTACK:
9582                 //      The guarded ship has been attacked.  Do something useful!
9583                 ai_chase();
9584
9585                 break;
9586         default:
9587                 //Int3();       //      Illegal submode for Guard mode.
9588                 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9589                 aip->submode = AIS_GUARD_PATROL;
9590                 break;
9591         }
9592
9593 }
9594
9595 // Return the object of the ship that the given object is docked
9596 // with.  Currently, we know a ship is docked when his ai_mode is AIM_DOCK,
9597 // and his submode is AIS_DOCK_3.  I suppose that this is likely to change though.
9598 // Also, the objnum that was is passed in may not be the object that actually
9599 // performed the docking maneuver.  This code will account for that case.
9600 object *ai_find_docked_object( object *docker )
9601 {
9602         ai_info *aip;
9603
9604         // we are trying to find the dockee of docker.  (Note that that these terms
9605         // are totally relative to what is passed in as a parameter.)
9606
9607         // first thing to attempt is to check and see if this object is docked with something.
9608         Assert( docker->type == OBJ_SHIP );             // this had probably better be a ship!!!
9609         aip = &Ai_info[Ships[docker->instance].ai_index];
9610         if ( !(aip->ai_flags & AIF_DOCKED) )            // flag not set if not docked with anything
9611                 return NULL;
9612
9613         if ( aip->dock_objnum == -1 ) {
9614                 Int3();                                                                                 // mwa says this is wrong wrong wrong
9615                 ai_do_objects_undocked_stuff( docker, NULL );
9616                 return NULL;
9617         }
9618
9619         return &Objects[aip->dock_objnum];
9620
9621 }
9622
9623
9624 // define for the points subtracted from score for a rearm started on a player.
9625 #define REPAIR_PENALTY          50
9626
9627
9628 // function to clean up ai flags, variables, and other interesting information
9629 // for a ship that was getting repaired.  The how parameter is useful for multiplayer
9630 // only in that it tells us why the repaired ship is being cleaned up.
9631 void ai_do_objects_repairing_stuff( object *repaired_objp, object *repair_objp, int how )
9632 {
9633         ai_info *aip, *repair_aip;
9634         int             stamp = -1;
9635
9636         Assert( repaired_objp->type == OBJ_SHIP);
9637         aip = &Ai_info[Ships[repaired_objp->instance].ai_index];
9638
9639         // multiplayer
9640         int p_index;
9641         p_index = -1;
9642         if(Game_mode & GM_MULTIPLAYER){
9643                 p_index = multi_find_player_by_object(repaired_objp);           
9644         }               
9645         else {          
9646                 if(repaired_objp == Player_obj){
9647                         p_index = Player_num;
9648                 }
9649         }
9650
9651         switch( how ) {
9652         case REPAIR_INFO_BEGIN:
9653                 aip->ai_flags |= AIF_BEING_REPAIRED;
9654                 aip->ai_flags &= ~AIF_AWAITING_REPAIR;
9655                 stamp = timestamp(-1);
9656
9657                 // if this is a player ship, then subtract the repair penalty from this player's score
9658                 if ( repaired_objp->flags & OF_PLAYER_SHIP ) {
9659                         if ( !(Game_mode & GM_MULTIPLAYER) ) {
9660                                 Player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());                    // subtract the penalty
9661                         } else {
9662                                 /*
9663                                 int pnum;
9664
9665                                 // multiplayer game -- find the player, then subtract the score
9666                                 pnum = multi_find_player_by_object( repaired_objp );
9667                                 if ( pnum != -1 ) {
9668                                         Net_players[pnum].player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());
9669
9670                                         // squad war
9671                                         multi_team_maybe_add_score(-(int)(REPAIR_PENALTY * scoring_get_scale_factor()), Net_players[pnum].p_info.team);
9672                                 } else {
9673                                         nprintf(("Network", "Couldn't find player for ship %s for repair penalty\n", Ships[repaired_objp->instance].ship_name));
9674                                 }
9675                                 */
9676                         }
9677                 }
9678                 break;
9679
9680         case REPAIR_INFO_BROKEN:
9681                 aip->ai_flags &= ~AIF_BEING_REPAIRED;
9682                 aip->ai_flags |= AIF_AWAITING_REPAIR;
9683                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9684                 break;
9685
9686         case REPAIR_INFO_END:
9687                 // when only awaiting repair, and the repair is ended, then set dock_objnum to -1.
9688                 if ( aip->ai_flags & AIF_AWAITING_REPAIR ){
9689                         aip->dock_objnum = -1;
9690                 }
9691                 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9692                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9693                 break;
9694
9695         case REPAIR_INFO_QUEUE:
9696                 aip->ai_flags |= AIF_AWAITING_REPAIR;
9697                 if ( aip == Player_ai ){
9698                         hud_support_view_start();
9699                 }
9700                 stamp = timestamp(-1);
9701                 break;
9702
9703         case REPAIR_INFO_ABORT:
9704         case REPAIR_INFO_KILLED:
9705                 // 5/4/98 -- MWA -- Need to set dock objnum to -1 to let code know this guy who was getting
9706                 // repaired (or queued for repair), isn't really going to be docked with anyone anymore.
9707                 aip->dock_objnum = -1;
9708                 aip->ai_flags &= ~AIF_DOCKED;
9709                 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9710                 if (repair_objp != NULL) {
9711                         repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9712                         repair_aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9713                 }               
9714
9715                 if ( p_index >= 0 ) {
9716                         hud_support_view_abort();
9717
9718                         // send appropriate message to player here
9719                         if ( how == REPAIR_INFO_KILLED ){
9720                                 message_send_builtin_to_player( MESSAGE_SUPPORT_KILLED, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9721                         } else {
9722                                 if ( repair_objp ){
9723                                         message_send_builtin_to_player( MESSAGE_REPAIR_ABORTED, &Ships[repair_objp->instance], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9724                                 }
9725                         }
9726                 }
9727
9728                 // add log entry if this is a player
9729                 if ( repaired_objp->flags & OF_PLAYER_SHIP ){
9730                         mission_log_add_entry(LOG_PLAYER_REARM_ABORT, Ships[repaired_objp->instance].ship_name, NULL);
9731                 }
9732
9733                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9734                 break;
9735
9736         case REPAIR_INFO_COMPLETE:
9737                 // clear the being repaired flag -- and 
9738                 if ( p_index >= 0 ) {
9739                         Assert( repair_objp );
9740                         
9741                         hud_support_view_stop();                        
9742
9743                         message_send_builtin_to_player(MESSAGE_REPAIR_DONE, &Ships[repair_objp->instance], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, p_index, -1);
9744                 }
9745                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9746                 break;
9747
9748         case REPAIR_INFO_ONWAY:
9749                 // need to set the dock_signature so that clients in multiplayer games rearm correctly
9750                 Assert( repair_objp );
9751                 aip->dock_signature = repair_objp->signature; 
9752                 aip->dock_objnum = OBJ_INDEX(repair_objp);
9753                 stamp = timestamp(-1);
9754                 break;
9755
9756         default:
9757                 Int3();                 // bogus type of repair info
9758         }
9759
9760         if (repair_objp){
9761                 Ai_info[Ships[repair_objp->instance].ai_index].warp_out_timestamp = stamp;
9762         }
9763
9764         // repair_objp might be NULL is we are cleaning up this mode because of the support ship
9765         // getting killed.
9766         if ( repair_objp ) {
9767                 aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9768                 switch ( how ) {
9769                 case REPAIR_INFO_ONWAY:
9770                         Assert( repaired_objp != NULL );
9771                         aip->goal_objnum = OBJ_INDEX(repaired_objp);
9772                         aip->ai_flags |= AIF_REPAIRING;
9773                         break;
9774
9775                 case REPAIR_INFO_BROKEN:
9776                         break;
9777
9778                 case REPAIR_INFO_END:
9779                 case REPAIR_INFO_ABORT:
9780                 case REPAIR_INFO_KILLED:
9781                         if ( how == REPAIR_INFO_ABORT )
9782                                 aip->goal_objnum = -1;
9783
9784                         aip->ai_flags &= ~AIF_REPAIRING;
9785                         break;
9786                         
9787                 case REPAIR_INFO_QUEUE:
9788                         ai_add_rearm_goal( repaired_objp, repair_objp );
9789                         break;
9790
9791                 case REPAIR_INFO_BEGIN:
9792                 case REPAIR_INFO_COMPLETE:
9793                         break;
9794
9795                 default:
9796                         Int3();         // bogus type of repair info
9797                 }
9798         }
9799
9800         multi_maybe_send_repair_info( repaired_objp, repair_objp, how );
9801 }
9802
9803 //      Cleanup AI stuff for when a ship was supposed to dock with another, but the ship
9804 //      it was supposed to dock with is no longer valid.
9805 void ai_cleanup_dock_mode(ai_info *aip, ship *shipp)
9806 {
9807         object *objp;
9808
9809         objp = &Objects[shipp->objnum];
9810         aip->mode = AIM_NONE;
9811
9812         if (aip->ai_flags & AIF_REPAIRING) {
9813                 Assert( aip->goal_objnum != -1 );
9814                 ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], &Objects[shipp->objnum], REPAIR_INFO_KILLED );
9815         } else if ( aip->ai_flags & AIF_BEING_REPAIRED ) {
9816                 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9817                 Assert( aip->dock_objnum != -1 );
9818                 ai_do_objects_repairing_stuff( &Objects[shipp->objnum], &Objects[aip->dock_objnum], REPAIR_INFO_KILLED );
9819         } else if ( aip->ai_flags & AIF_AWAITING_REPAIR ) {
9820                 // need to find the support ship that has me as a goal_objnum
9821                 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9822                 // MWA -- 3/38/98  Check to see if this guy is queued for a support ship, or there is already
9823                 // one in the mission
9824                 if ( mission_is_repair_scheduled(objp) ) {
9825                         mission_remove_scheduled_repair( objp );                        // this function will notify multiplayer clients.
9826                 } else {
9827                         if ( aip->dock_objnum != -1 )
9828                                 ai_do_objects_repairing_stuff( objp, &Objects[aip->dock_objnum], REPAIR_INFO_ABORT );
9829                         else
9830                                 ai_do_objects_repairing_stuff( objp, NULL, REPAIR_INFO_ABORT );
9831                 }
9832         }
9833
9834         if ( aip->ai_flags & AIF_DOCKED ) {
9835                 ai_info *other_aip;
9836
9837                 Assert( aip->dock_objnum != -1 );
9838
9839                 // if docked, and the dock_objnum is not undocking, force them to near last stage
9840                 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
9841                 if ( (other_aip->mode == AIM_DOCK) && (other_aip->submode < AIS_UNDOCK_3) )
9842                         other_aip->submode = AIS_UNDOCK_3;
9843                 ai_do_objects_undocked_stuff( objp, &Objects[aip->dock_objnum] );
9844         }
9845 }
9846
9847 /*
9848 //      Make dockee_objp shake a bit due to docking.
9849 void ai_dock_shake(object *docker_objp, object *dockee_objp)
9850 {
9851         vector  tangles;
9852         matrix  rotmat, tmp;
9853         float           scale;
9854         angles  *ap;
9855
9856         scale = 0.25f;          //      Compute this based on mass and speed at time of docking.
9857
9858         vm_vec_rand_vec_quick(&tangles);
9859         vm_vec_scale(&tangles, scale);
9860
9861         ap = (angles *) &tangles;
9862
9863         vm_angles_2_matrix(&rotmat, ap);
9864         vm_matrix_x_matrix( &tmp, &dockee_objp->orient, &rotmat );
9865         dockee_objp->orient = tmp;
9866
9867         vm_orthogonalize_matrix(&dockee_objp->orient);
9868
9869         dock_orient_and_approach(docker_objp, dockee_objp, DOA_DOCK_STAY);
9870
9871 }
9872 */
9873
9874 //      Make Pl_objp point at aip->goal_point.
9875 void ai_still()
9876 {
9877         ship    *shipp;
9878         ai_info *aip;
9879
9880         Assert(Pl_objp->type == OBJ_SHIP);
9881         Assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_OBJECTS));
9882
9883         shipp = &Ships[Pl_objp->instance];
9884         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
9885
9886         aip = &Ai_info[shipp->ai_index];
9887
9888         turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
9889 }
9890
9891 //      Make *Pl_objp stay near another ship.
9892 void ai_stay_near()
9893 {
9894         ai_info *aip;
9895         int             goal_objnum;
9896
9897         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
9898
9899         goal_objnum = aip->goal_objnum;
9900
9901         if ((goal_objnum < 0) || (Objects[goal_objnum].type != OBJ_SHIP) || (Objects[goal_objnum].signature != aip->goal_signature)) {
9902                 aip->mode = AIM_NONE;
9903         } else {
9904                 float           dist, max_dist, scale;
9905                 vector  rand_vec, goal_pos, vec_to_goal;
9906                 object  *goal_objp;
9907
9908                 goal_objp = &Objects[goal_objnum];
9909
9910                 //      Make not all ships pursue same point.
9911                 static_randvec(Pl_objp-Objects, &rand_vec);
9912
9913                 //      Make sure point is in front hemisphere (relative to Pl_objp's position.
9914                 vm_vec_sub(&vec_to_goal, &goal_objp->pos, &Pl_objp->pos);
9915                 if (vm_vec_dot(&rand_vec, &vec_to_goal) > 1.0f) {
9916                         vm_vec_negate(&rand_vec);
9917                 }
9918
9919                 //      Scale the random vector by an amount proportional to the distance from Pl_objp to the true goal.
9920                 dist = vm_vec_dist_quick(&goal_objp->pos, &Pl_objp->pos);
9921                 max_dist = aip->stay_near_distance;
9922                 scale = dist - max_dist/2;
9923                 if (scale < 0.0f)
9924                         scale = 0.0f;
9925
9926                 vm_vec_scale_add(&goal_pos, &goal_objp->pos, &rand_vec, scale);
9927
9928                 if (max_dist < Pl_objp->radius + goal_objp->radius + 25.0f)
9929                         max_dist = Pl_objp->radius + goal_objp->radius + 25.0f;
9930
9931                 if (dist > max_dist) {
9932                         turn_towards_point(Pl_objp, &goal_pos, NULL, 0.0f);
9933                         accelerate_ship(aip, dist / max_dist - 0.8f);
9934                 }
9935         
9936         }
9937
9938 }
9939
9940 //      Warn player if dock path is obstructed.
9941 int maybe_dock_obstructed(object *cur_objp, object *goal_objp, int big_only_flag)
9942 {
9943         vector  *goalpos, *curpos;
9944         float           radius;
9945         ai_info *aip;
9946         int             collide_objnum;
9947
9948         aip = &Ai_info[Ships[cur_objp->instance].ai_index];
9949
9950         Ai_info[Ships[goal_objp->instance].ai_index].ai_flags &= ~AIF_REPAIR_OBSTRUCTED;
9951
9952         if (goal_objp != Player_obj)
9953                 return -1;
9954
9955         curpos = &cur_objp->pos;
9956         radius = cur_objp->radius;
9957         goalpos = &Path_points[aip->path_cur].pos;
9958         collide_objnum = pp_collide_any(curpos, goalpos, radius, cur_objp, goal_objp, big_only_flag);
9959
9960         if (collide_objnum != -1)
9961                 Ai_info[Ships[goal_objp->instance].ai_index].ai_flags |= AIF_REPAIR_OBSTRUCTED;
9962
9963         return collide_objnum;
9964 }
9965
9966
9967 int Dock_path_warning_given = 0;
9968
9969 //      Docking behavior.
9970 //      Approach a ship, follow path to docking platform, approach platform, after awhile,
9971 //      undock.
9972 void ai_dock()
9973 {
9974         ship                    *shipp = &Ships[Pl_objp->instance];
9975         ai_info         *aip = &Ai_info[shipp->ai_index];
9976         object          *goal_objp;
9977         ship_info       *sip = &Ship_info[shipp->ship_info_index];
9978
9979         //      Make sure object we're supposed to dock with still exists.
9980         if ((aip->goal_objnum == -1) || (Objects[aip->goal_objnum].signature != aip->goal_signature)) {
9981                 ai_cleanup_dock_mode(aip, shipp);
9982                 return;
9983         }
9984
9985         goal_objp = &Objects[aip->goal_objnum];
9986
9987         //      For docking submodes (ie, not undocking), follow path.  Once at second last
9988         //      point on path (point just before point on dock platform), orient into position.
9989         // For undocking, first mode pushes docked ship straight back from docking point
9990         // second mode turns ship and moves to point on docking radius
9991         switch (aip->submode) {
9992
9993                 //      This mode means to find the path to the docking point.
9994         case AIS_DOCK_0:
9995                 //aip->path_start = -1;
9996                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
9997                 ai_path();
9998                 if (!Dock_path_warning_given && (aip->path_length < 4)) {
9999                         Warning( LOCATION, "Ship '%s' has only %i points on dock path.  Docking will look strange.  Contact Adam.", shipp->ship_name, aip->path_length );
10000                         Dock_path_warning_given = 1;            //      This is on a mission-wide basis, but it's just a hack for now...
10001                 }
10002
10003                 aip->submode = AIS_DOCK_1;
10004                 aip->path_start = -1;
10005                 aip->submode_start_time = Missiontime;
10006                 break;
10007
10008                 //      This mode means to follow the path until just before the end.
10009         case AIS_DOCK_1: {
10010                 float   dist;
10011                 int     r;
10012
10013                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp, 1)) != -1) {
10014                         int     r1;
10015                         if ((r1 = maybe_avoid_big_ship(Pl_objp, goal_objp, aip, &goal_objp->pos, 7.0f)) != 0) {
10016                                 nprintf(("AI", "Support ship %s avoiding large ship %s\n", Ships[Pl_objp->instance].ship_name, Ships[Objects[r1].instance].ship_name));
10017                                 break;
10018                         } /*else {
10019                                 nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10020                                 accelerate_ship(aip, 0.0f);
10021                                 aip->submode = AIS_DOCK_0;
10022                         } */
10023                 } //else {
10024                 {
10025                         dist = ai_path();
10026                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10027                         //nprintf(("AI", "Dock 1: Frame: %i, goal point = %i, dist = %7.3f\n", Framecount, aip->path_cur-aip->path_start, dist));
10028
10029                         if (aip->path_cur-aip->path_start >= aip->path_length-1) {              //      If got this far, advance no matter what.
10030                                 aip->submode = AIS_DOCK_2;
10031                                 aip->submode_start_time = Missiontime;
10032                                 aip->path_cur--;
10033                                 Assert(aip->path_cur-aip->path_start >= 0);
10034                         } else if (aip->path_cur-aip->path_start >= aip->path_length-2) {
10035                                 if (Pl_objp->phys_info.speed > goal_objp->phys_info.speed + 1.5f) {
10036                                         set_accel_for_target_speed(Pl_objp, goal_objp->phys_info.speed);
10037                                 } else {
10038                                         aip->submode = AIS_DOCK_2;
10039                                         aip->submode_start_time = Missiontime;
10040                                 }
10041                         }
10042                 }
10043                 break;
10044                                           }
10045         //      This mode means to drag oneself right to the second last point on the path.
10046         //      Path code allows it to overshoot.
10047         case AIS_DOCK_2: {
10048                 float           dist;
10049                 int     r;
10050
10051                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10052                         nprintf(("AI", "Dock 2: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10053                         accelerate_ship(aip, 0.0f);
10054                         aip->submode = AIS_DOCK_1;
10055                 } else {
10056                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10057                         dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_APPROACH);
10058                         Assert(dist != UNINITIALIZED_VALUE);
10059
10060                         if (dist == DOCK_BACKUP_RETURN_VAL) {
10061                                 int path_num;
10062                                 aip->submode = AIS_DOCK_1;
10063                                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
10064                                 Assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
10065                                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
10066                                 break;
10067                         }
10068
10069                         //nprintf(("AI", "Dock 2: dist = %7.3f\n", vm_vec_dist_quick(&Pl_objp->pos, &goal_point)));
10070                         float   tolerance;
10071                         if (Objects[aip->goal_objnum].flags & OF_PLAYER_SHIP)
10072                                 tolerance = 6*flFrametime + 1.0f;
10073                         else
10074                                 tolerance = 4*flFrametime + 0.5f;
10075
10076                         if ( dist < tolerance) {
10077                                 aip->submode = AIS_DOCK_3;
10078                                 aip->submode_start_time = Missiontime;
10079                                 aip->path_cur++;
10080                         }
10081                 }
10082                 break;
10083                                                   }
10084
10085         case AIS_DOCK_3:
10086         case AIS_DOCK_3A:
10087                 {
10088                 Assert(aip->goal_objnum != -1);
10089                 int     r;
10090
10091                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10092                         nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10093                         accelerate_ship(aip, 0.0f);
10094                         aip->submode = AIS_DOCK_2;
10095                 } else {
10096
10097                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10098                         float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10099                         Assert(dist != UNINITIALIZED_VALUE);
10100
10101                         if (dist == DOCK_BACKUP_RETURN_VAL) {
10102                                 aip->submode = AIS_DOCK_2;
10103                                 break;
10104                         }
10105
10106                         //nprintf(("AI", "Dock 3: dist = %7.3f\n", dist));
10107
10108                         if (dist < 2*flFrametime * (1.0f + fl_sqrt(goal_objp->phys_info.speed))) {
10109                                 // - Removed by MK on 11/7/97, causes errors for ships docked at mission start: maybe_recreate_path(Pl_objp, aip, 1);
10110                                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10111                                 Assert(dist != UNINITIALIZED_VALUE);
10112
10113                                 physics_ship_init(Pl_objp);
10114
10115                                 ai_do_objects_docked_stuff( Pl_objp, goal_objp );
10116
10117                                 if (aip->submode == AIS_DOCK_3) {
10118                                         snd_play_3d( &Snds[SND_DOCK_ATTACH], &Pl_objp->pos, &View_position );
10119                                         hud_maybe_flash_docking_text(Pl_objp);
10120                                         // ai_dock_shake(Pl_objp, goal_objp);
10121
10122                                         if ((Pl_objp == Player_obj) || (goal_objp == Player_obj))
10123                                                 joy_ff_docked();  // shake player's joystick a little
10124                                 }
10125
10126                                 //      If this ship is repairing another ship...
10127                                 if (aip->ai_flags & AIF_REPAIRING) {
10128                                         aip->submode = AIS_DOCK_4;                      //      Special rearming only dock mode.
10129                                         aip->submode_start_time = Missiontime;
10130                                 } else {
10131                                         aip->submode = AIS_DOCK_4A;
10132                                         aip->submode_start_time = Missiontime;
10133                                 }
10134                         }
10135                 }
10136                 break;
10137                 }
10138
10139                 //      Yes, we just sit here.  We wait for further orders.  No, it's not a bug.
10140         case AIS_DOCK_4A:
10141                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10142                 //nprintf(("AI", "."));
10143                 if (aip->active_goal >= 0) {
10144                         mission_log_add_entry(LOG_SHIP_DOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10145
10146                         if (aip->goals[aip->active_goal].ai_mode == AI_GOAL_DOCK) {
10147                                 ai_mission_goal_complete( aip );                                        // Note, this calls ai_set_default_behavior().
10148                         } 
10149                 } else {        //      Can happen for initially docked ships.
10150                         ai_do_default_behavior( &Objects[Ships[aip->shipnum].objnum] );         // do the default behavior
10151                 }
10152                 
10153                 break;
10154
10155         case AIS_DOCK_4: {
10156                 //      This mode is only for rearming/repairing.
10157                 //      The ship that is performing the rearm enters this mode after it docks.
10158                 Assert((aip->goal_objnum >= -1) && (aip->goal_objnum < MAX_OBJECTS));
10159
10160                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10161                 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10162                 Assert(dist != UNINITIALIZED_VALUE);
10163
10164                 object  *goal_objp = &Objects[aip->goal_objnum];
10165                 Assert(goal_objp->type == OBJ_SHIP);
10166                 ship                    *goal_shipp = &Ships[goal_objp->instance];              
10167                 ai_info         *goal_aip = &Ai_info[goal_shipp->ai_index];
10168
10169                 //nprintf(("AI", "Dock 4: dist = %7.3f\n", dist));
10170
10171                 //      Make sure repair has not broken off.
10172                 if (dist > 5.0f) {      //      Oops, too far away!
10173                         if ( goal_aip->ai_flags & AIF_BEING_REPAIRED )
10174                                 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BROKEN);
10175
10176                         if (dist > Pl_objp->radius*2 + goal_objp->radius*2) {
10177                                 //      Got real far away from goal, so move back a couple modes and try again.
10178                                 aip->submode = AIS_DOCK_2;
10179                                 aip->submode_start_time = Missiontime;
10180                         }
10181                 } else {
10182                         if ( goal_aip->ai_flags & AIF_AWAITING_REPAIR )
10183                                 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BEGIN );
10184                 }
10185
10186                 break;
10187                                                   }
10188
10189         case AIS_UNDOCK_0: {
10190                 int path_num;
10191                 //      First stage of undocking.
10192
10193                 //nprintf(("AI", "Undock 0:\n"));
10194
10195                 aip->submode = AIS_UNDOCK_1;
10196                 aip->submode_start_time = Missiontime;
10197                 if (aip->dock_objnum == -1) {
10198                         aip->submode = AIS_UNDOCK_3;
10199                 } else {
10200
10201                         // set up the path points for the undocking procedure.  dock_path_index member should
10202                         // have gotten set in the docking code.
10203                         Assert( aip->dock_path_index != -1 );
10204                         path_num = ai_return_path_num_from_dockbay(goal_objp, aip->dock_path_index);
10205                         ai_find_path(Pl_objp, goal_objp-Objects, path_num, 0);
10206
10207                         // Play a ship docking detach sound
10208                         snd_play_3d( &Snds[SND_DOCK_DETACH], &Pl_objp->pos, &View_position );
10209                 }
10210                 break;
10211                                                          }
10212         case AIS_UNDOCK_1: {
10213                 //      Using thrusters, exit from dock station to nearest next dock path point.
10214                 float   dist;
10215                 
10216                 //nprintf(("AI", "Undock 1: time in this mode = %7.3f\n", f2fl(Missiontime - aip->submode_start_time)));
10217
10218                 if (Missiontime - aip->submode_start_time < REARM_BREAKOFF_DELAY) {
10219                         break;          //      Waiting for one second to elapse to let detach sound effect play out.
10220                 }
10221                 else {  // AL - added 05/16/97.  Hack to play depart sound.  Will probably take out.
10222                                         // Assumes that the submode_start_time is not used for AIS_UNDOCK_1 anymore
10223                         if ( aip->submode_start_time != 0 )
10224                                 snd_play_3d( &Snds[SND_DOCK_DEPART], &Pl_objp->pos, &View_position );
10225                         aip->submode_start_time = 0;
10226                 }
10227
10228                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_1);
10229                 Assert(dist != UNINITIALIZED_VALUE);
10230
10231                 float dist_to_dock_obj = vm_vec_dist_quick(&Pl_objp->pos, &Objects[aip->goal_objnum].pos);
10232
10233                 //      Move to within 0.1 units of second last point on path before orienting, or just plain far away from docked-to ship.
10234                 //      This allows undock to complete if first ship flies away.
10235                 if ((dist < 2*flFrametime) || (dist_to_dock_obj > 2*Pl_objp->radius)) {
10236                         aip->submode = AIS_UNDOCK_2;
10237                         aip->submode_start_time = Missiontime;
10238                 }
10239                 break;
10240                                                          }
10241         case AIS_UNDOCK_2: {
10242                 float dist;
10243                 ai_info *other_aip;
10244
10245                 // get pointer to docked object's aip to reset flags, etc
10246                 Assert( aip->dock_objnum != -1 );
10247                 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
10248
10249                 //      Second stage of undocking.
10250                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_2);
10251                 Assert(dist != UNINITIALIZED_VALUE);
10252
10253
10254                 //nprintf(("AI", "Undock 2: dist = %7.3f\n", dist));
10255                 
10256                 //      If at goal point, or quite far away from dock object
10257                 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) ) {
10258                         // reset the dock flags.  If rearm/repair, reset rearm repair flags for those ships as well.
10259                         if ( sip->flags & SIF_SUPPORT ) {
10260                                 ai_do_objects_repairing_stuff( &Objects[aip->dock_objnum], Pl_objp, REPAIR_INFO_END );
10261                         }
10262
10263                         // clear out flags for AIF_DOCKED for both objects.
10264                         ai_do_objects_undocked_stuff( Pl_objp, goal_objp );
10265                         physics_ship_init(Pl_objp);
10266                         aip->submode = AIS_UNDOCK_3;                            //      The do-nothing mode, until another order is issued
10267
10268                         //aip->ai_flags &= ~AIF_DOCKED;         //      @MK, 9/18/97
10269                         //other_aip->ai_flags &= ~AIF_DOCKED;
10270                         //aip->dock_objnum = -1;                                        // invalidate who obj is docked with
10271                         //other_aip->dock_objnum = -1;                  // MWA 10/07/97 invalide docked objects dock_objnum value as well
10272
10273                         // don't add undock log entries for support ships.
10274                         if ( !(sip->flags & SIF_SUPPORT) )
10275                                 mission_log_add_entry(LOG_SHIP_UNDOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10276
10277                 }
10278                 break;
10279                 }
10280         case AIS_UNDOCK_3: {
10281                 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_3);
10282                 Assert(dist != UNINITIALIZED_VALUE);
10283
10284                 if (dist < Pl_objp->radius/2 + 5.0f) {
10285                         aip->submode = AIS_UNDOCK_4;
10286                 }
10287
10288                 // possible that this flag hasn't been cleared yet.  When aborting a rearm, this submode might
10289                 // be entered directly.
10290                 if ( (sip->flags & SIF_SUPPORT) && (aip->ai_flags & AIF_REPAIRING) ) {
10291                         ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], Pl_objp, REPAIR_INFO_ABORT );
10292                 }
10293
10294                 break;
10295                                                  }
10296         case AIS_UNDOCK_4: {
10297                 ai_info *other_aip;
10298
10299                 // MWA 10/07/97  I'm slightly confused by the dual use of goal_objnum and dock_objnum.  Seems to me
10300                 // that goal_objnum and dock_objnum are the same through this whole docking/undocking process, although
10301                 // I could be wrong.  dock_objnum was reset in undock_2 submode so try to use goal_objnum here to
10302                 // get other ships ai_info pointer
10303                 Assert( aip->goal_objnum != -1 );
10304                 other_aip = &Ai_info[Ships[Objects[aip->goal_objnum].instance].ai_index];
10305
10306                 aip->mode = AIM_NONE;
10307                 aip->dock_path_index = -1;              // invalidate the docking path index
10308
10309                 // these flags should have been cleared long ago!
10310                 // Get Allender if you hit one of these!!!!!
10311                 // removed by allender on 2/16 since a ship may be docked with some other ship, but still be the
10312                 // goal_objnum of this ship ending it's undocking mode.
10313                 //Assert( !(aip->ai_flags & AIF_DOCKED) );
10314                 //Assert( !(other_aip->ai_flags & AIF_DOCKED) );
10315                 //Assert( !(aip->ai_flags & AIF_REPAIRING) );
10316                 //Assert( !(other_aip->ai_flags & AIF_BEING_REPAIRED) );
10317                 //Assert( !(other_aip->ai_flags & AIF_AWAITING_REPAIR) );
10318
10319                 // only call mission goal complete if this was indeed an undock goal
10320                 if ( aip->active_goal > -1 ) {
10321                         if ( aip->goals[aip->active_goal].ai_mode == AI_GOAL_UNDOCK )
10322                                 ai_mission_goal_complete( aip );                        // this call should reset the AI mode
10323                         //else
10324                         //      aip->active_goal = -1;                                          // this ensures that this ship might get new goal
10325                 }
10326
10327                 break;
10328                                                          }
10329         default:
10330                 Int3(); //      Error, bogus submode
10331         }
10332
10333 }
10334
10335 // TURRET BEGIN
10336
10337 //      Given an object and a turret on that object, return the global position and forward vector
10338 //      of the turret.   The gun normal is the unrotated gun normal, (the center of the FOV cone), not
10339 // the actual gun normal given using the current turret heading.  But it _is_ rotated into the model's orientation
10340 //      in global space.
10341 void ship_get_global_turret_info(object *objp, model_subsystem *tp, vector *gpos, vector *gvec)
10342 {
10343         matrix  m;
10344         vm_copy_transpose_matrix(&m, &objp->orient);
10345 //      vm_vec_rotate(gpos, &tp->turret_avg_firing_point, &m);
10346         vm_vec_rotate(gpos, &tp->pnt, &m);
10347         vm_vec_add2(gpos, &objp->pos);
10348         vm_vec_rotate(gvec, &tp->turret_norm, &m);      
10349 }
10350
10351 // Given an object and a turret on that object, return the actual firing point of the gun
10352 // and its normal.   This uses the current turret angles.  We are keeping track of which
10353 // gun to fire next in the ship specific info for this turret subobject.  Use this info
10354 // to determine which position to fire from next.
10355 //      Stuffs:
10356 //              *gpos: absolute position of gun firing point
10357 //              *gvec: vector fro *gpos to *targetp
10358 void ship_get_global_turret_gun_info(object *objp, ship_subsys *ssp, vector *gpos, vector *gvec, int use_angles, vector *targetp)
10359 {
10360         vector * gun_pos;
10361         model_subsystem *tp = ssp->system_info;
10362
10363         ship_model_start(objp);
10364
10365         gun_pos = &tp->turret_firing_point[ssp->turret_next_fire_pos % tp->turret_num_firing_points];
10366
10367         model_find_world_point(gpos, gun_pos, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10368
10369         if (use_angles)
10370                 model_find_world_dir(gvec, &tp->turret_norm, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10371         else {
10372                 //vector        gun_pos2;
10373                 //vm_vec_add(&gun_pos2, gpos, gun_pos);
10374                 vm_vec_normalized_dir(gvec, targetp, gpos);
10375         }
10376
10377         ship_model_stop(objp);  
10378 }
10379
10380 //      Rotate a turret towards an enemy.
10381 //      Return TRUE if caller should use angles in subsequent rotations.
10382 //      Some obscure model thing only John Slagel knows about.
10383 //      Sets predicted enemy position.
10384 //      If the turret (*ss) has a subsystem targeted, the subsystem is used as the predicted point.
10385 int aifft_rotate_turret(ship *shipp, int parent_objnum, ship_subsys *ss, object *objp, object *lep, vector *predicted_enemy_pos, vector *gvec)
10386 {
10387         if (ss->turret_enemy_objnum != -1)      {
10388                 model_subsystem *tp = ss->system_info;
10389                 vector  gun_pos, gun_vec;
10390                 float           weapon_speed;
10391                 float           weapon_system_strength;
10392
10393                 //      weapon_system_strength scales time enemy in range in 0..1.  So, the lower this is, the worse the aiming will be.
10394                 weapon_system_strength = ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS);
10395
10396                 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gun_pos, &gun_vec);
10397
10398                 weapon_speed = Weapon_info[tp->turret_weapon_type].max_speed;
10399                 float weapon_travel_dist = weapon_speed * Weapon_info[tp->turret_weapon_type].lifetime;
10400
10401                 vector  enemy_point;
10402                 if (ss->targeted_subsys != NULL) {
10403                         if (ss->turret_enemy_objnum != -1) {
10404                                 vm_vec_unrotate(&enemy_point, &ss->targeted_subsys->system_info->pnt, &Objects[ss->turret_enemy_objnum].orient);
10405                                 vm_vec_add2(&enemy_point, &Objects[ss->turret_enemy_objnum].pos);
10406                         }
10407                 } else {
10408                         if ((lep->type == OBJ_SHIP) && (Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
10409                                 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));
10410                         } else {
10411                                 enemy_point = lep->pos;
10412                         }
10413                 }
10414
10415                 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);
10416
10417                 if (weapon_system_strength < 0.7f) {
10418                         vector  rand_vec;
10419
10420                         static_randvec(Missiontime >> 18, &rand_vec);   //      Return same random number for two seconds.
10421                         //      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.
10422                         vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, (1.0f - weapon_system_strength)*1.5f * lep->radius);
10423                 }
10424
10425                 vector  v2e;
10426                 vm_vec_normalized_dir(&v2e, predicted_enemy_pos, &gun_pos);
10427                 if (vm_vec_dot(&v2e, gvec) > tp->turret_fov) {
10428                         int     rval;
10429
10430                         rval = model_rotate_gun(shipp->modelnum, ss->system_info, &Objects[parent_objnum].orient, 
10431                                 &ss->submodel_info_1.angs, &ss->submodel_info_2.angs,
10432                                 &Objects[parent_objnum].pos, predicted_enemy_pos);
10433                 }
10434         }
10435
10436         return 0;
10437 }
10438
10439 //      Determine if subsystem *enemy_subsysp is hittable from objp.
10440 //      If so, return dot product of vector from point abs_gunposp to *enemy_subsysp
10441 float   aifft_compute_turret_dot(object *objp, object *enemy_objp, vector *abs_gunposp, ship_subsys *turret_subsysp, ship_subsys *enemy_subsysp)
10442 {
10443         float   dot_out;
10444         vector  subobj_pos, vector_out;
10445
10446         vm_vec_unrotate(&subobj_pos, &enemy_subsysp->system_info->pnt, &enemy_objp->orient);
10447         vm_vec_add2(&subobj_pos, &enemy_objp->pos);
10448
10449         if (ship_subsystem_in_sight(enemy_objp, enemy_subsysp, abs_gunposp, &subobj_pos, 1, &dot_out, &vector_out)) {
10450                 vector  turret_norm;
10451
10452                 vm_vec_rotate(&turret_norm, &turret_subsysp->system_info->turret_norm, &objp->orient);
10453                 return vm_vec_dot(&turret_norm, &vector_out);
10454         } else
10455                 return -1.0f;
10456
10457 }
10458
10459 #define MAX_AIFFT_TURRETS                       60
10460 ship_subsys *aifft_list[MAX_AIFFT_TURRETS];
10461 float aifft_rank[MAX_AIFFT_TURRETS];
10462 int aifft_list_size = 0;
10463 int aifft_max_checks = 5;
10464 DCF(mf, "")
10465 {
10466         dc_get_arg(ARG_INT);
10467         aifft_max_checks = Dc_arg_int;
10468 }
10469
10470
10471 //      Pick a subsystem to attack on enemy_objp.
10472 //      Only pick one if enemy_objp is a big ship or a capital ship.
10473 //      Returns dot product from turret to subsystem in *dot_out
10474 ship_subsys *aifft_find_turret_subsys(object *objp, ship_subsys *ssp, object *enemy_objp, float *dot_out)
10475 {
10476         ship    *eshipp, *shipp;
10477         ship_info       *esip;
10478         ship_subsys     *best_subsysp = NULL;
10479         float dot;
10480
10481         Assert(enemy_objp->type == OBJ_SHIP);
10482
10483         eshipp = &Ships[enemy_objp->instance];
10484         esip = &Ship_info[eshipp->ship_info_index];
10485
10486         shipp = &Ships[objp->instance];
10487
10488         float   best_dot = 0.0f;
10489         *dot_out = best_dot;
10490
10491         //      Compute absolute gun position.
10492         vector  abs_gun_pos;
10493         vm_vec_unrotate(&abs_gun_pos, &ssp->system_info->pnt, &objp->orient);
10494         vm_vec_add2(&abs_gun_pos, &objp->pos);
10495
10496         //      Only pick a turret to attack on large ships.
10497         if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
10498                 return best_subsysp;
10499
10500         // Make sure big or huge ship *actually* has subsystems  (ie, knossos)
10501         if (esip->n_subsystems == 0) {
10502                 return best_subsysp;
10503         }
10504
10505         // first build up a list subsystems to traverse
10506         ship_subsys     *pss;
10507         aifft_list_size = 0;
10508         for ( pss = GET_FIRST(&eshipp->subsys_list); pss !=END_OF_LIST(&eshipp->subsys_list); pss = GET_NEXT(pss) ) {
10509                 model_subsystem *psub = pss->system_info;
10510
10511                 // if we've reached max turrets bail
10512                 if(aifft_list_size >= MAX_AIFFT_TURRETS){
10513                         break;
10514                 }
10515
10516                 // Don't process destroyed objects
10517                 if ( pss->current_hits <= 0.0f ){
10518                         continue;
10519                 }
10520                 
10521                 switch (psub->type) {
10522                 case SUBSYSTEM_WEAPONS:
10523                         aifft_list[aifft_list_size] = pss;
10524                         aifft_rank[aifft_list_size++] = 1.4f;
10525                         break;
10526
10527                 case SUBSYSTEM_TURRET:
10528                         aifft_list[aifft_list_size] = pss;
10529                         aifft_rank[aifft_list_size++] = 1.2f;
10530                         break;
10531
10532                 case SUBSYSTEM_SENSORS:
10533                 case SUBSYSTEM_ENGINE:
10534                         aifft_list[aifft_list_size] = pss;
10535                         aifft_rank[aifft_list_size++] = 1.0f;
10536                         break;
10537                 }
10538         }
10539
10540         // DKA:  6/28/99 all subsystems can be destroyed.
10541         //Assert(aifft_list_size > 0);
10542         if (aifft_list_size == 0) {
10543                 return best_subsysp;
10544         }
10545
10546         // determine a stride value so we're not checking too many turrets
10547         int stride = aifft_list_size > aifft_max_checks ? aifft_list_size / aifft_max_checks : 1;
10548         if(stride <= 0){
10549                 stride = 1;
10550         }
10551         int offset = (int)frand_range(0.0f, (float)(aifft_list_size % stride));
10552         int idx;
10553         for(idx=offset; idx<aifft_list_size; idx+=stride){
10554                 dot = aifft_compute_turret_dot(objp, enemy_objp, &abs_gun_pos, ssp, aifft_list[idx]);                   
10555
10556                 if (dot* aifft_rank[idx] > best_dot) {
10557                         best_dot = dot*aifft_rank[idx];
10558                         best_subsysp = aifft_list[idx];
10559                 }
10560         }
10561
10562         Assert(best_subsysp != &eshipp->subsys_list);
10563
10564         *dot_out = best_dot;
10565         return best_subsysp;
10566 }
10567
10568 // Set active weapon for turret
10569 void ai_turret_select_default_weapon(ship_subsys *turret)
10570 {
10571         ship_weapon *twp;
10572
10573         twp = &turret->weapons;
10574
10575         // If a primary weapon is available, select it
10576         if ( twp->num_primary_banks > 0 ) {
10577                 turret->system_info->turret_weapon_type = twp->primary_bank_weapons[0];
10578         } else if ( twp->num_secondary_banks > 0 ) {
10579                 turret->system_info->turret_weapon_type = twp->secondary_bank_weapons[0];
10580         }
10581 }
10582
10583 // return !0 if the specified target should scan for a new target, otherwise return 0
10584 int turret_should_pick_new_target(ship_subsys *turret)
10585 {
10586 //      int target_type;
10587
10588         if ( timestamp_elapsed(turret->turret_next_enemy_check_stamp) ) {
10589                 return 1;
10590         }
10591
10592         return 0;
10593
10594 /*
10595         if ( turret->turret_enemy_objnum == -1 ) {
10596                 return 1;
10597         }
10598                 
10599         target_type = Objects[turret->turret_enemy_objnum].type;
10600         if ( (target_type != OBJ_SHIP) && (target_type != OBJ_ASTEROID) ) {
10601                 return 1;
10602         }
10603
10604         return 0;
10605 */
10606 }
10607
10608 // Set the next fire timestamp for a turret, based on weapon type and ai class
10609 void turret_set_next_fire_timestamp(ship_subsys *turret, ai_info *aip)
10610 {
10611         float   wait;
10612         int     weapon_id;
10613
10614         weapon_id = turret->system_info->turret_weapon_type;
10615
10616         wait = Weapon_info[weapon_id].fire_wait * 1000.0f;
10617
10618         // make side even for team vs. team
10619         if ((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) {
10620                 // flak guns need to fire more rapidly
10621                 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10622                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10623                         wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10624                 } else {
10625                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10626                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10627                 }
10628         } else {
10629                 // flak guns need to fire more rapidly
10630                 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10631                         if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10632                                 wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10633                         } else {
10634                                 wait *= Ship_fire_delay_scale_hostile[Game_skill_level] * 0.5f;
10635                         }       
10636                         wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10637
10638                 } else if (Weapon_info[weapon_id].wi_flags & WIF_HUGE) {
10639                         // make huge weapons fire independently of team
10640                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10641                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10642                 } else {
10643                         // give team friendly an advantage
10644                         if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10645                                 wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10646                         } else {
10647                                 wait *= Ship_fire_delay_scale_hostile[Game_skill_level];
10648                         }       
10649                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10650                 }
10651         }
10652
10653         // vary wait time +/- 10%
10654         wait *= frand_range(0.9f, 1.1f);
10655         turret->turret_next_fire_stamp = timestamp((int) wait);
10656 }
10657
10658 // Decide  if a turret should launch an aspect seeking missile
10659 int turret_should_fire_aspect(ship_subsys *turret, float dot, int weapon_class)
10660 {
10661         weapon_info *wip;
10662
10663         wip = &Weapon_info[weapon_class];
10664
10665         if ( (dot > AICODE_TURRET_DUMBFIRE_ANGLE) && (turret->turret_time_enemy_in_range >= min(wip->min_lock_time,AICODE_TURRET_MAX_TIME_IN_RANGE)) ) {
10666                 return 1;
10667         }
10668
10669         return 0;
10670 }
10671
10672 // Update how long current target has been in this turrets range
10673 void turret_update_enemy_in_range(ship_subsys *turret, float seconds)
10674 {
10675         turret->turret_time_enemy_in_range += seconds;
10676
10677         if ( turret->turret_time_enemy_in_range < 0.0f ) {
10678                 turret->turret_time_enemy_in_range = 0.0f;
10679         }
10680
10681         if ( turret->turret_time_enemy_in_range > AICODE_TURRET_MAX_TIME_IN_RANGE ) {
10682                 turret->turret_time_enemy_in_range = AICODE_TURRET_MAX_TIME_IN_RANGE;
10683         }
10684 }
10685
10686
10687
10688 // Fire a weapon from a turret
10689 void turret_fire_weapon(ship_subsys *turret, int parent_objnum, vector *turret_pos, vector *turret_fvec, vector *predicted_pos = NULL)
10690 {
10691         matrix  turret_orient;
10692         int             turret_weapon_class, weapon_objnum;
10693         ai_info *parent_aip;
10694         ship            *parent_ship;
10695         beam_fire_info fire_info;
10696         float flak_range = 0.0f;
10697
10698         parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
10699         parent_ship = &Ships[Objects[parent_objnum].instance];
10700         turret_weapon_class = turret->system_info->turret_weapon_type;
10701
10702         if (check_ok_to_fire(parent_objnum, turret->turret_enemy_objnum, &Weapon_info[turret_weapon_class])) {
10703                 vm_vector_2_matrix(&turret_orient, turret_fvec, NULL, NULL);
10704                 turret->turret_last_fire_direction = *turret_fvec;
10705
10706                 // set next fire timestamp for the turret
10707                 turret_set_next_fire_timestamp(turret, parent_aip);
10708
10709                 // if this weapon is a beam weapon, handle it specially
10710                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM){
10711                         // if this beam isn't free to fire
10712                         if (!(turret->weapons.flags & SW_FLAG_BEAM_FREE)) {
10713                                 Int3(); // should never get this far
10714                                 return;
10715                         }
10716
10717                         // stuff beam firing info
10718                         memset(&fire_info, 0, sizeof(beam_fire_info));
10719                         fire_info.accuracy = 1.0f;
10720                         fire_info.beam_info_index = turret_weapon_class;
10721                         fire_info.beam_info_override = NULL;
10722                         fire_info.shooter = &Objects[parent_objnum];
10723                         fire_info.target = &Objects[turret->turret_enemy_objnum];
10724                         fire_info.target_subsys = NULL;
10725                         fire_info.turret = turret;
10726
10727                         // fire a beam weapon
10728                         beam_fire(&fire_info);
10729                 } else {
10730
10731                         // don't fire swarm, but set up swarm info
10732                         if (Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM) {
10733                                 turret_swarm_set_up_info(parent_objnum, turret, turret_weapon_class);
10734                                 return;
10735                         } else {
10736                                 weapon_objnum = weapon_create( turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10737                                 weapon_set_tracking_info(weapon_objnum, parent_objnum, turret->turret_enemy_objnum, 1, turret->targeted_subsys);                
10738                         }
10739
10740                         //nprintf(("AI", "Turret_time_enemy_in_range = %7.3f\n", ss->turret_time_enemy_in_range));              
10741                         if (weapon_objnum != -1) {
10742                                 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10743                                 // AL 1-6-97: Store pointer to turret subsystem
10744                                 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10745
10746                                 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10747                                         // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10748                                         if ( parent_objnum != OBJ_INDEX(Player_obj) ) {                                         
10749                                                 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], turret_pos, &View_position );                                          
10750                                         }
10751                                 }               
10752
10753                                 // if the gun is a flak gun
10754                                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){                       
10755                                         // show a muzzle flash
10756                                         flak_muzzle_flash(turret_pos, turret_fvec, turret_weapon_class);
10757
10758                                         // pick a firing range so that it detonates properly                    
10759                                         flak_pick_range(&Objects[weapon_objnum], predicted_pos, ship_get_subsystem_strength(parent_ship, SUBSYSTEM_WEAPONS));
10760
10761                                         // determine what that range was
10762                                         flak_range = flak_get_range(&Objects[weapon_objnum]);
10763                                 }
10764
10765                                 // in multiplayer (and the master), then send a turret fired packet.
10766                                 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10767                                         int subsys_index;
10768
10769                                         subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10770                                         Assert( subsys_index != -1 );
10771                                         if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){                       
10772                                                 send_flak_fired_packet( parent_objnum, subsys_index, weapon_objnum, flak_range );
10773                                         } else {
10774                                                 send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10775                                         }
10776                                 }
10777                         }
10778                 }
10779         } else {
10780                 float wait = 1000.0f * frand_range(0.9f, 1.1f);
10781                 turret->turret_next_fire_stamp = timestamp((int) wait);
10782         }
10783 }
10784
10785 void turret_swarm_fire_from_turret(ship_subsys *turret, int parent_objnum, int target_objnum, ship_subsys *target_subsys)
10786 {
10787         int turret_weapon_class, weapon_objnum;
10788         matrix turret_orient;
10789         vector turret_pos, turret_fvec;
10790
10791         // parent not alive, quick out.
10792         if (Objects[parent_objnum].type != OBJ_SHIP) {
10793                 return;
10794         }
10795
10796         //      change firing point
10797         ship_get_global_turret_gun_info(&Objects[parent_objnum], turret, &turret_pos, &turret_fvec, 1, NULL);
10798         turret->turret_next_fire_pos++;
10799
10800         // get class [index into Weapon_info array
10801         turret_weapon_class = turret->system_info->turret_weapon_type;
10802         Assert(Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM);
10803
10804         // make turret_orient from turret_fvec -- turret->turret_last_fire_direction
10805         vm_vector_2_matrix(&turret_orient, &turret_fvec, NULL, NULL);
10806
10807         // create weapon and homing info
10808         weapon_objnum = weapon_create(&turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10809         weapon_set_tracking_info(weapon_objnum, parent_objnum, target_objnum, 1, target_subsys);
10810
10811         // do other cool stuff if weapon is created.
10812         if (weapon_objnum > -1) {
10813                 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10814                 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10815
10816                 // maybe sound
10817                 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10818                         // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10819                         if ( parent_objnum != OBJ_INDEX(Player_obj) ) {
10820                                 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], &turret_pos, &View_position );
10821                         }
10822                 }
10823                 
10824                 // in multiplayer (and the master), then send a turret fired packet.
10825                 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10826                         int subsys_index;
10827
10828                         subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10829                         Assert( subsys_index != -1 );
10830                         send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10831                 }
10832         }
10833 }
10834
10835 int Num_ai_firing = 0;
10836 int Num_find_turret_enemy = 0;
10837 int Num_turrets_fired = 0;
10838 //      Given a turret tp and its parent parent_objnum, fire from the turret at its enemy.
10839 void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
10840 {
10841         float           weapon_firing_range;
10842         vector  v2e;
10843         object  *lep;           //      Last enemy pointer
10844         model_subsystem *tp = ss->system_info;
10845         int             use_angles, turret_weapon_class;
10846         vector  predicted_enemy_pos;
10847         object  *objp;
10848         ai_info *aip;
10849
10850         if (!Ai_firing_enabled) {
10851                 return;
10852         }
10853
10854         if (ss->current_hits < 0.0f) {
10855                 return;
10856         }
10857
10858         if ( ship_subsys_disrupted(ss) ){               // AL 1/19/98: Make sure turret isn't suffering disruption effects
10859                 return;
10860         }
10861
10862         // Check turret free
10863         if (ss->weapons.flags & SW_FLAG_TURRET_LOCK) {
10864                 return;
10865         }
10866
10867         // If beam weapon, check beam free
10868         if ( (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) && !(ss->weapons.flags & SW_FLAG_BEAM_FREE) ) {
10869                 return;
10870         }
10871
10872         Assert( shipp->objnum == parent_objnum );
10873
10874         if ( tp->turret_weapon_type < 0 ){
10875                 return;
10876         }
10877
10878         // Monitor number of calls to ai_fire_from_turret
10879         Num_ai_firing++;
10880
10881         turret_weapon_class = tp->turret_weapon_type;
10882
10883         // AL 09/14/97: ensure ss->turret_enemy_objnum != -1 before setting lep
10884         if ( (ss->turret_enemy_objnum >= 0 && ss->turret_enemy_objnum < MAX_OBJECTS) && (ss->turret_enemy_sig == Objects[ss->turret_enemy_objnum].signature)) {
10885                 lep = &Objects[ss->turret_enemy_objnum];
10886
10887                 // MK -- here is where turret is targeting a bomb.  I simply return for now.  We should force
10888                 // a target change -- or better yet, never pick a weapon when this turret has a "huge" weapon
10889                 // loaded.
10890
10891                 // we only care about targets which are ships.
10892                 //if ( lep->type != OBJ_SHIP )
10893                 //      return;
10894
10895                 //      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.
10896                 if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HUGE ) {
10897                         if ( lep->type != OBJ_SHIP ) {
10898                                 return;
10899                         }
10900                         if ( !(Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
10901                                 return;
10902                         }
10903                 }
10904
10905                 // If targeting protected or beam protected ship, don't fire.  Reset enemy objnum
10906                 if (lep->type == OBJ_SHIP) {
10907                         // Check if we're targeting a protected ship
10908                         if (lep->flags & OF_PROTECTED) {
10909                                 ss->turret_enemy_objnum = -1;
10910                                 ss->turret_time_enemy_in_range = 0.0f;
10911                                 return;
10912                         }
10913
10914                         // Check if we're targeting a beam protected ship with a beam weapon
10915                         if ( (lep->flags & OF_BEAM_PROTECTED) && (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) ) {
10916                                 ss->turret_enemy_objnum = -1;
10917                                 ss->turret_time_enemy_in_range = 0.0f;
10918                                 return;
10919                         }
10920                 }
10921         } else {
10922                 ss->turret_enemy_objnum = -1;
10923                 lep = NULL;
10924         }
10925         
10926         Assert((parent_objnum >= 0) && (parent_objnum < MAX_OBJECTS));
10927         objp = &Objects[parent_objnum];
10928         Assert(objp->type == OBJ_SHIP);
10929         aip = &Ai_info[Ships[objp->instance].ai_index];
10930
10931         // Use the turret info for all guns, not one gun in particular.
10932         vector   gvec, gpos;
10933         ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
10934
10935         // Rotate the turret even if time hasn't elapsed, since it needs to turn to face its target.
10936         use_angles = aifft_rotate_turret(shipp, parent_objnum, ss, objp, lep, &predicted_enemy_pos, &gvec);
10937
10938         if ( !timestamp_elapsed(ss->turret_next_fire_stamp)){
10939                 return;
10940         }
10941
10942         // Don't try to fire beyond weapon_limit_range
10943         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);
10944
10945         // if beam weapon in nebula and target not tagged, decrase firing range
10946         extern int Nebula_sec_range;
10947         if (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) {
10948                 if ( !((shipp->tag_left > 0) || (shipp->level2_tag_left > 0)) ) {
10949                         if (Nebula_sec_range) {
10950                                 weapon_firing_range *= float(BEAM_NEBULA_RANGE_REDUCE_FACTOR);
10951                         }
10952                 }
10953         }
10954
10955         if (ss->turret_enemy_objnum != -1) {
10956                 float dist_to_enemy = vm_vec_normalized_dir(&v2e, &predicted_enemy_pos, &gpos) - lep->radius;
10957                 if (dist_to_enemy > weapon_firing_range) {
10958                         ss->turret_enemy_objnum = -1;           //      Force picking of new enemy.
10959                 }
10960         }
10961
10962         // Turret spawn weapons are a special case.  They fire if there are enough enemies in the 
10963         // immediate area (not necessarily in the turret fov).
10964         if ( Weapon_info[turret_weapon_class].wi_flags & WIF_SPAWN ) {
10965                 int num_ships_nearby;
10966                 num_ships_nearby = num_nearby_fighters(get_enemy_team_mask(parent_objnum), &gpos, 1500.0f);
10967                 if (( num_ships_nearby >= 3 ) || ((num_ships_nearby >= 2) && (frand() < 0.1f))) {
10968                         turret_fire_weapon(ss, parent_objnum, &gpos, &ss->turret_last_fire_direction);
10969                 } else {
10970                         ss->turret_next_fire_stamp = timestamp(1000);   //      Regardless of firing rate, don't check whether should fire for awhile.
10971                 }
10972                 return;
10973         }
10974
10975         //      Maybe pick a new enemy.
10976         if ( turret_should_pick_new_target(ss) ) {
10977                 Num_find_turret_enemy++;
10978                 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);
10979                 Assert(objnum < 0 || is_target_beam_valid(ss, objnum));
10980
10981                 if (objnum != -1) {
10982                         if (ss->turret_enemy_objnum == -1) {
10983                                 ss->turret_enemy_objnum = objnum;
10984                                 ss->turret_enemy_sig = Objects[objnum].signature;
10985                                 // why return?
10986                                 return;
10987                         } else {
10988                                 ss->turret_enemy_objnum = objnum;
10989                                 ss->turret_enemy_sig = Objects[objnum].signature;
10990                         }
10991                 } else {
10992                         ss->turret_enemy_objnum = -1;
10993                 }
10994
10995                 if (ss->turret_enemy_objnum != -1) {
10996                         float   dot = 1.0f;
10997                         lep = &Objects[ss->turret_enemy_objnum];
10998                         if ( lep->type == OBJ_SHIP ) {
10999                                 ss->targeted_subsys = aifft_find_turret_subsys(objp, ss, lep, &dot);                            
11000                         }
11001                         ss->turret_next_enemy_check_stamp = timestamp((int) (max(dot, 0.5f)*2000.0f) + 1000);
11002                 } else {
11003                         ss->turret_next_enemy_check_stamp = timestamp((int) (2000.0f * frand_range(0.9f, 1.1f)));       //      Check every two seconds
11004                 }
11005         }
11006
11007         //      If still don't have an enemy, return.  Or, if enemy is protected, return.
11008         if (ss->turret_enemy_objnum != -1) {
11009                 //      Don't shoot at ship we're going to dock with.
11010                 if (ss->turret_enemy_objnum == aip->dock_objnum) {
11011                         ss->turret_enemy_objnum = -1;
11012                         return;
11013                 }
11014
11015                 if (Objects[ss->turret_enemy_objnum].flags & OF_PROTECTED) {
11016                         //      This can happen if the enemy was selected before it became protected.
11017                         ss->turret_enemy_objnum = -1;
11018                         return;
11019                 }
11020                 lep = &Objects[ss->turret_enemy_objnum];
11021         } else {
11022                 if (timestamp_until(ss->turret_next_fire_stamp) < 500) {
11023                         ss->turret_next_fire_stamp = timestamp(500);
11024                 }
11025                 return;
11026         }
11027
11028         if ( lep == NULL ){
11029                 return;
11030         }
11031
11032         Assert(ss->turret_enemy_objnum != -1);
11033
11034         float dot = vm_vec_dot(&v2e, &gvec);
11035
11036         if (dot > tp->turret_fov ) {
11037                 // Ok, the turret is lined up... now line up a particular gun.
11038                 int ok_to_fire = 0;
11039                 float dist_to_enemy;
11040
11041                 // We're ready to fire... now get down to specifics, like where is the
11042                 // actual gun point and normal, not just the one for whole turret.
11043                 ship_get_global_turret_gun_info(&Objects[parent_objnum], ss, &gpos, &gvec, use_angles, &predicted_enemy_pos);
11044                 ss->turret_next_fire_pos++;
11045
11046                 // Fire in the direction the turret is facing, not right at the target regardless of turret dir.
11047                 vm_vec_sub(&v2e, &predicted_enemy_pos, &gpos);
11048                 dist_to_enemy = vm_vec_normalize(&v2e);
11049                 dot = vm_vec_dot(&v2e, &gvec);
11050
11051                 // 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
11052                 // and make them less lethal
11053                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){
11054                         flak_jitter_aim(&v2e, dist_to_enemy, ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS));
11055                 }
11056
11057                 // Fire if:
11058                 //              dumbfire and nearly pointing at target.
11059                 //              heat seeking and target in a fairly wide cone.
11060                 //              aspect seeking and target is locked.
11061                 turret_weapon_class = tp->turret_weapon_type;
11062
11063                 // if dumbfire (lasers and non-homing missiles)
11064                 if ( !(Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING) ) {
11065                         if ((dist_to_enemy < 75.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11066                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11067                                 ok_to_fire = 1;
11068                         }
11069                 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_HEAT ) {     // if heat seekers
11070                         if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_HEATSEEK_ANGLE )) {
11071                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11072                                 ok_to_fire = 1;
11073                         }
11074                 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_ASPECT ) {   // if aspect seeker
11075                         if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11076                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11077                         }
11078                         if ( turret_should_fire_aspect(ss, dot, turret_weapon_class) ) {
11079                                 ok_to_fire = 1;
11080                         }
11081                 }
11082
11083                 if ( ok_to_fire ) {
11084                         Num_turrets_fired++;
11085                         
11086                         turret_fire_weapon(ss, parent_objnum, &gpos, &v2e, &predicted_enemy_pos);                                               
11087                 } else {
11088                         turret_update_enemy_in_range(ss, -4*Weapon_info[tp->turret_weapon_type].fire_wait);
11089                         ss->turret_next_fire_stamp = timestamp(500);
11090                 }
11091         } else {
11092                 // Lost him!
11093                 ss->turret_enemy_objnum = -1;           //      Reset enemy objnum, find a new one next frame.
11094                 ss->turret_time_enemy_in_range = 0.0f;
11095         }
11096 }
11097
11098 // TURRET END
11099
11100 #ifndef NDEBUG
11101 #define MAX_AI_DEBUG_RENDER_STUFF       100
11102 typedef struct ai_render_stuff {
11103         ship_subsys     *ss;
11104         int                     parent_objnum;
11105 } ai_render_stuff;
11106
11107 ai_render_stuff AI_debug_render_stuff[MAX_AI_DEBUG_RENDER_STUFF];
11108
11109 int     Num_AI_debug_render_stuff = 0;
11110
11111 void ai_debug_render_stuff()
11112 {
11113         vertex  vert1, vert2;
11114         vector  gpos2;
11115         int             i;
11116
11117         for (i=0; i<Num_AI_debug_render_stuff; i++) {
11118                 ship_subsys     *ss;
11119                 int     parent_objnum;
11120                 vector  gpos, gvec;
11121                 model_subsystem *tp;
11122
11123                 ss = AI_debug_render_stuff[i].ss;
11124                 tp = ss->system_info;
11125
11126                 parent_objnum = AI_debug_render_stuff[i].parent_objnum;
11127
11128                 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
11129                 g3_rotate_vertex(&vert1, &gpos);
11130                 vm_vec_scale_add(&gpos2, &gpos, &gvec, 20.0f);
11131                 g3_rotate_vertex(&vert2, &gpos2);
11132                 gr_set_color(0, 0, 255);
11133                 g3_draw_sphere(&vert1, 2.0f);
11134                 gr_set_color(255, 0, 255);
11135                 g3_draw_sphere(&vert2, 2.0f);
11136                 g3_draw_line(&vert1, &vert2);
11137         }
11138
11139         // draw from beta to its goal point
11140 /*      for (i=0; i<6; i++) {
11141                 ai_info *aip = &Ai_info[i];
11142                 gr_set_color(0, 0, 255);
11143                 g3_rotate_vertex(&vert1, &Objects[i].pos);
11144                 g3_rotate_vertex(&vert2, &aip->goal_point);
11145                 g3_draw_line(&vert1, &vert2);
11146         } */
11147         
11148
11149         Num_AI_debug_render_stuff = 0;
11150 }
11151
11152 #endif
11153
11154 #ifndef NDEBUG
11155 int     Msg_count_4996 = 0;
11156 #endif
11157
11158 //      --------------------------------------------------------------------------
11159 // Process subobjects of object objnum.
11160 //      Deal with engines disabled.
11161 void process_subobjects(int objnum)
11162 {
11163         model_subsystem *psub;
11164         ship_subsys     *pss;
11165         object  *objp = &Objects[objnum];
11166         ship            *shipp = &Ships[objp->instance];
11167         ai_info *aip = &Ai_info[shipp->ai_index];
11168         ship_info       *sip = &Ship_info[shipp->ship_info_index];
11169
11170         for ( pss = GET_FIRST(&shipp->subsys_list); pss !=END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
11171                 psub = pss->system_info;
11172
11173                 // Don't process destroyed objects
11174                 if ( pss->current_hits <= 0.0f ) 
11175                         continue;
11176
11177                 switch (psub->type) {
11178                 case SUBSYSTEM_TURRET:
11179                         if ( psub->turret_num_firing_points > 0 )       {
11180                                 ai_fire_from_turret(shipp, pss, objnum);
11181                         } else {
11182 #ifndef NDEBUG
11183                                 if (!Msg_count_4996) {
11184                                         Warning( LOCATION, "Ship '%s' has turrets with no guns!\nProbably a model problem, so get an artist!", shipp->ship_name );
11185                                         Msg_count_4996++;
11186                                 }
11187 #endif
11188                                 }
11189                         break;
11190
11191                 case SUBSYSTEM_ENGINE:
11192                 case SUBSYSTEM_NAVIGATION:
11193                 case SUBSYSTEM_COMMUNICATION:
11194                 case SUBSYSTEM_WEAPONS:
11195                 case SUBSYSTEM_SENSORS:
11196                 case SUBSYSTEM_UNKNOWN:
11197                         break;
11198
11199                 // next set of subsystems may rotation
11200                 case SUBSYSTEM_RADAR:
11201                 case SUBSYSTEM_SOLAR:
11202                 case SUBSYSTEM_GAS_COLLECT:
11203                 case SUBSYSTEM_ACTIVATION:
11204                         break;
11205                 default:
11206                         Error(LOCATION, "Illegal subsystem type.\n");
11207                 }
11208
11209                 // do solar/radar/gas/activator rotation here
11210                 if ( psub->flags & MSS_FLAG_ROTATES )   {
11211                         if (psub->flags & MSS_FLAG_STEPPED_ROTATE       ) {
11212                                 submodel_stepped_rotate(psub, &pss->submodel_info_1);
11213                         } else {
11214                                 submodel_rotate(psub, &pss->submodel_info_1 );
11215                         }
11216                 }
11217
11218         }
11219
11220         //      Deal with a ship with blown out engines.
11221         if (ship_get_subsystem_strength(shipp, SUBSYSTEM_ENGINE) == 0.0f) {
11222                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
11223                         // AL: Only attack forever if not trying to depart to a docking bay.  Need to have this in, since
11224                         //     a ship may get repaired... and it should still try to depart.  Since docking bay departures
11225                         //     are not handled as goals, we don't want to leave the AIM_BAY_DEPART mode.
11226                         if ( aip->mode != AIM_BAY_DEPART ) {
11227                                 ai_attack_object(objp, NULL, 99, NULL);         //      Regardless of current mode, enter attack mode.
11228                                 aip->submode = SM_ATTACK_FOREVER;                               //      Never leave attack submode, don't avoid, evade, etc.
11229                         }
11230                 }
11231         }
11232
11233
11234 }
11235
11236 //      Given an object and the wing it's in, return its index in the wing list.
11237 //      This defines its location in the wing formation.
11238 //      If the object can't be found in the wing, return -1.
11239 //      *objp           object of interest
11240 //      wingnum the wing *objp is in
11241 int get_wing_index(object *objp, int wingnum)
11242 {
11243         wing    *wingp;
11244         int     i;
11245
11246         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11247
11248         wingp = &Wings[wingnum];
11249
11250         for (i=wingp->current_count-1; i>=0; i--)
11251                 if ( objp->instance == wingp->ship_index[i] )
11252                         break;
11253
11254         return i;               //      Note, returns -1 if string not found.
11255 }
11256
11257 //      Given a wing, return a pointer to the object of its leader.
11258 //      Asserts if object not found.
11259 //      Currently, the wing leader is defined as the first object in the wing.
11260 //      wingnum         Wing number in Wings array.
11261 //      If wing leader is disabled, swap it with another ship.
11262 object * get_wing_leader(int wingnum)
11263 {
11264         wing            *wingp;
11265         int             ship_num;
11266
11267         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11268
11269         wingp = &Wings[wingnum];
11270
11271         Assert(wingp->current_count != 0);                      //      Make sure there is a leader
11272
11273         ship_num = wingp->ship_index[0];
11274
11275         //      If this ship is disabled, try another ship in the wing.
11276         int n = 0;
11277         while (ship_get_subsystem_strength(&Ships[ship_num], SUBSYSTEM_ENGINE) == 0.0f) {
11278                 n++;
11279                 if (n >= wingp->current_count)
11280                         break;  
11281                 ship_num = wingp->ship_index[n];
11282         }
11283
11284         if (( n != 0) && (n != wingp->current_count)) {
11285                 int t = wingp->ship_index[0];
11286                 wingp->ship_index[0] = wingp->ship_index[n];
11287                 wingp->ship_index[n] = t;
11288         }
11289
11290         return &Objects[Ships[ship_num].objnum];
11291 }
11292
11293 #define DEFAULT_WING_X_DELTA            1.0f
11294 #define DEFAULT_WING_Y_DELTA            0.25f
11295 #define DEFAULT_WING_Z_DELTA            0.75f
11296 #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))
11297 // next constant is higher that MAX_SHIPS_IN_WINGS to deal with forming on player's wing
11298 #define MAX_FORMATION_ROWS              4
11299
11300 //      Given a position in a wing, return the desired location of the ship relative to the leader
11301 //      *_delta_vec             OUTPUT.  delta vector based on wing_index
11302 //      wing_index              position in wing.
11303 void get_wing_delta(vector *_delta_vec, int wing_index)
11304 {
11305         int     wi0;
11306
11307         Assert(wing_index >= 0);
11308
11309         int     k, row, column;
11310
11311         int bank = wing_index / (MAX_FORMATION_ROWS*(MAX_FORMATION_ROWS+1)/2);
11312         wi0 = wing_index % (MAX_FORMATION_ROWS * (MAX_FORMATION_ROWS+1)/2);
11313
11314         k = 0;
11315         for (row=1; row<MAX_FORMATION_ROWS+1; row++) {
11316                 k += row;
11317                 if (wi0 < k)
11318                         break;
11319         }
11320
11321         row--;
11322         column = wi0 - k + row + 1;
11323
11324         _delta_vec->xyz.x = ((float) column - (float) row/2.0f) * DEFAULT_WING_X_DELTA/DEFAULT_WING_MAG;
11325         _delta_vec->xyz.y = ((float)row + (float)bank*2.25f) * DEFAULT_WING_Y_DELTA/DEFAULT_WING_MAG;
11326         _delta_vec->xyz.z = - ((float)row + 0.5f * (float) bank) * DEFAULT_WING_Z_DELTA/DEFAULT_WING_MAG;
11327 }
11328
11329 //      Compute the largest radius of a ship in a *objp's wing.
11330 float gwlr_1(object *objp, ai_info *aip)
11331 {
11332         int             wingnum = aip->wing;
11333         float           max_radius;
11334         object  *o;
11335         ship_obj        *so;
11336
11337         Assert(wingnum >= 0);
11338
11339         max_radius = objp->radius;
11340
11341         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11342                 o = &Objects[so->objnum];
11343                 if (Ai_info[Ships[o->instance].ai_index].wing == wingnum)
11344                         if (o->radius > max_radius)
11345                                 max_radius = o->radius;
11346         }
11347
11348         return max_radius;
11349 }
11350
11351 //      Compute the largest radius of a ship forming on *objp's wing.
11352 float gwlr_object_1(object *objp, ai_info *aip)
11353 {
11354         float           max_radius;
11355         object  *o;
11356         ship_obj        *so;
11357
11358         max_radius = objp->radius;
11359
11360         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11361                 o = &Objects[so->objnum];
11362                 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == OBJ_INDEX(objp))
11363                         if (o->radius > max_radius)
11364                                 max_radius = o->radius;
11365         }
11366
11367         return max_radius;
11368 }
11369
11370 //      For the wing that *objp is part of, return the largest ship radius in that wing.
11371 float get_wing_largest_radius(object *objp, int formation_object_flag)
11372 {
11373         ship            *shipp;
11374         ai_info *aip;
11375
11376         Assert(objp->type == OBJ_SHIP);
11377         Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
11378         shipp = &Ships[objp->instance];
11379         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11380         aip = &Ai_info[shipp->ai_index];
11381
11382         if (formation_object_flag) {
11383                 return gwlr_object_1(objp, aip);
11384         } else {
11385                 return gwlr_1(objp, aip);
11386         }
11387
11388 }
11389
11390 float Wing_y_scale = 2.0f;
11391 float Wing_scale = 1.0f;
11392 DCF(wing_y_scale, "")
11393 {
11394         dc_get_arg(ARG_FLOAT);
11395         Wing_y_scale = Dc_arg_float;
11396 }
11397
11398 DCF(wing_scale, "")
11399 {
11400         dc_get_arg(ARG_FLOAT);
11401         Wing_scale = Dc_arg_float;
11402 }
11403
11404 // Given a wing leader and a position in the wing formation, return the desired absolute location to fly to.
11405 //      Returns result in *result_pos.
11406 void get_absolute_wing_pos(vector *result_pos, object *leader_objp, int wing_index, int formation_object_flag)
11407 {
11408         vector  wing_delta, rotated_wing_delta;
11409         float           wing_spread_size;
11410
11411         get_wing_delta(&wing_delta, wing_index);                //      Desired location in leader's reference frame
11412
11413         wing_spread_size = max(50.0f, 3.0f * get_wing_largest_radius(leader_objp, formation_object_flag) + 15.0f);
11414
11415         // for player obj (1) move ships up 20% (2) scale formation up 20%
11416         if (leader_objp->flags & OF_PLAYER_SHIP) {
11417                 wing_delta.xyz.y *= Wing_y_scale;
11418                 wing_spread_size *= Wing_scale;
11419         }
11420
11421         vm_vec_scale(&wing_delta, wing_spread_size * (1.0f + leader_objp->phys_info.speed/70.0f));
11422
11423         vm_vec_unrotate(&rotated_wing_delta, &wing_delta, &leader_objp->orient);        //      Rotate into leader's reference.
11424
11425         vm_vec_add(result_pos, &leader_objp->pos, &rotated_wing_delta); //      goal_point is absolute 3-space point.
11426 }
11427
11428 #ifndef NDEBUG
11429 int Debug_render_wing_phantoms;
11430
11431 void render_wing_phantoms(object *objp)
11432 {
11433         int             i;
11434         ship            *shipp;
11435         ai_info *aip;
11436         int             wingnum;
11437         int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11438         vector  goal_point;
11439         
11440         Assert(objp->type == OBJ_SHIP);
11441         Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11442
11443         shipp = &Ships[objp->instance];
11444         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11445
11446         aip = &Ai_info[shipp->ai_index];
11447
11448         wingnum = aip->wing;
11449
11450         if (wingnum == -1)
11451                 return;
11452
11453         wing_index = get_wing_index(objp, wingnum);
11454
11455         //      If this ship is NOT the leader, abort.
11456         if (wing_index != 0)
11457                 return;
11458
11459         for (i=0; i<32; i++)
11460                 if (Debug_render_wing_phantoms & (1 << i)) {
11461                         get_absolute_wing_pos(&goal_point, objp, i, 0);
11462         
11463                         vertex  vert;
11464                         gr_set_color(255, 0, 128);
11465                         g3_rotate_vertex(&vert, &goal_point);
11466                         g3_draw_sphere(&vert, 2.0f);
11467                 }
11468
11469         Debug_render_wing_phantoms = 0;
11470
11471 }
11472
11473 void render_wing_phantoms_all()
11474 {
11475         object  *objp;
11476         ship_obj        *so;
11477
11478         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11479                 ship            *shipp;
11480                 ai_info *aip;
11481                 int             wingnum;
11482                 int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11483
11484                 objp = &Objects[so->objnum];
11485                 
11486                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11487                 shipp = &Ships[objp->instance];
11488                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11489
11490                 aip = &Ai_info[shipp->ai_index];
11491
11492                 wingnum = aip->wing;
11493
11494                 if (wingnum == -1)
11495                         continue;
11496
11497                 wing_index = get_wing_index(objp, wingnum);
11498
11499                 //      If this ship is NOT the leader, abort.
11500                 if (wing_index != 0)
11501                         continue;
11502                 
11503                 render_wing_phantoms(objp);
11504
11505                 return;
11506         }
11507 }
11508
11509 #endif
11510
11511 //      Hook from goals code to AI.
11512 //      Force a wing to fly in formation.
11513 //      Sets AIF_FORMATION bit in ai_flags.
11514 //      wingnum         Wing to force to fly in formation
11515 void ai_fly_in_formation(int wingnum)
11516 {
11517         object  *objp;
11518         ship            *shipp;
11519         ship_obj        *so;
11520
11521         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11522                 objp = &Objects[so->objnum];
11523                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11524
11525                 shipp = &Ships[objp->instance];
11526                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11527
11528                 if (Ai_info[shipp->ai_index].wing == wingnum) {
11529                         Ai_info[shipp->ai_index].ai_flags |= AIF_FORMATION_WING;
11530                         Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_OBJECT;
11531                 }
11532         }
11533 }
11534
11535 //      Hook from goals code to AI.
11536 //      Force a wing to abandon formation flying.
11537 //      Clears AIF_FORMATION bit in ai_flags.
11538 //      wingnum         Wing to force to fly in formation
11539 void ai_disband_formation(int wingnum)
11540 {
11541         object  *objp;
11542         ship            *shipp;
11543         ship_obj        *so;
11544
11545         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11546                 objp = &Objects[so->objnum];
11547                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11548
11549                 shipp = &Ships[objp->instance];
11550                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11551
11552                 if (Ai_info[shipp->ai_index].wing == wingnum) {
11553                         Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_WING;
11554                 }
11555         }
11556 }
11557
11558 float   Leader_chaos = 0.0f;
11559 int Chaos_frame = -1;
11560
11561 //      Return true if objp is flying in an erratic manner
11562 //      Only true if objp is a player
11563 int formation_is_leader_chaotic(object *objp)
11564 {
11565         if (Game_mode & GM_MULTIPLAYER)
11566                 return 0;
11567
11568         if (objp != Player_obj)
11569                 return 0;
11570
11571         if (Framecount != Chaos_frame) {
11572                 float   speed_scale;
11573                 float   fdot, udot;
11574
11575                 speed_scale = 3.0f + objp->phys_info.speed * 0.1f;
11576
11577                 fdot = 5.0f * (1.0f - vm_vec_dot(&objp->orient.v.fvec, &objp->last_orient.v.fvec)) * flFrametime;
11578                 udot = 8.0f * (1.0f - vm_vec_dot(&objp->orient.v.uvec, &objp->last_orient.v.uvec)) * flFrametime;
11579
11580                 Leader_chaos += fdot * speed_scale + udot * speed_scale;
11581
11582                 Leader_chaos *= (1.0f - flFrametime*0.2f);
11583
11584                 if (Leader_chaos < 0.0f)
11585                         Leader_chaos = 0.0f;
11586                 else if (Leader_chaos > 1.7f)
11587                         Leader_chaos = 1.7f;
11588
11589                 //nprintf(("AI", "Frame %i: chaos = %7.4f\n", Framecount, Leader_chaos));
11590
11591                 Chaos_frame = Framecount;
11592         }
11593
11594         return (Leader_chaos > 1.0f);
11595 }
11596
11597 // Fly in formation.
11598 //      Make Pl_objp assume its proper place in formation.
11599 //      If the leader of the wing is doing something stupid, like fighting a battle,
11600 //      then the poor sap wingmates will be in for a "world of hurt"
11601 //      Return TRUE if we need to process this object's normal mode
11602 int ai_formation()
11603 {
11604         object  *leader_objp;
11605         ship            *shipp;
11606         ai_info *aip, *laip;
11607         int             wingnum;
11608         int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11609         int             player_wing;    // index of the players wingnum
11610         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;
11611         float           dot_to_goal, dist_to_goal, leader_speed;
11612
11613         Assert(Pl_objp->type == OBJ_SHIP);
11614         Assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_SHIPS));
11615
11616         shipp = &Ships[Pl_objp->instance];
11617
11618         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11619
11620         aip = &Ai_info[shipp->ai_index];
11621
11622         Assert((aip->ai_flags & AIF_FORMATION) != AIF_FORMATION);       //      Make sure not both types of formation flying in effect.
11623
11624         //      Determine which kind of formation flying.
11625         //      If tracking an object, not in waypoint mode:
11626         if (aip->ai_flags & AIF_FORMATION_OBJECT) {
11627                 if ((aip->goal_objnum < 0) || (aip->goal_objnum >= MAX_OBJECTS)) {
11628                         aip->ai_flags &= ~AIF_FORMATION_OBJECT;
11629                         return 1;
11630                 }
11631                 
11632                 wing_index = ai_formation_object_get_slotnum(aip->goal_objnum, Pl_objp);
11633                 leader_objp = &Objects[aip->goal_objnum];
11634         } else {        //      Formation flying in waypoint mode.
11635                 Assert(aip->ai_flags & AIF_FORMATION_WING);
11636                 if (aip->mode != AIM_WAYPOINTS) {
11637                         aip->ai_flags &= ~AIF_FORMATION_WING;
11638                         return 1;
11639                 }
11640
11641                 wingnum = aip->wing;
11642
11643                 if (wingnum == -1)
11644                         return 1;
11645
11646                 // disable formation flying for any ship in the players wing
11647                 player_wing = Ships[Player_obj->instance].wingnum;
11648                 if ( (player_wing != -1) && (wingnum == player_wing) )
11649                         return 1;
11650
11651                 wing_index = get_wing_index(Pl_objp, wingnum);
11652
11653                 leader_objp = get_wing_leader(wingnum);
11654
11655         }
11656
11657         //      If docked with a ship in this wing, only the more massive one actually flies in formation.
11658         if (aip->dock_objnum != -1) {
11659                 object  *other_objp = &Objects[aip->dock_objnum];
11660                 ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
11661
11662                 if (aip->wing == other_aip->wing) {
11663                         if (Pl_objp->phys_info.mass < other_objp->phys_info.mass)
11664                                 return 0;
11665                         else if (Pl_objp->phys_info.mass == other_objp->phys_info.mass) {
11666                                 if (Pl_objp->signature < other_objp->signature)
11667                                         return 0;
11668                         }
11669                 }
11670         }
11671
11672         Assert(leader_objp != NULL);
11673         laip = &Ai_info[Ships[leader_objp->instance].ai_index];
11674
11675         //      Make sure we're really in this wing.
11676         if (wing_index == -1)
11677                 return 1;
11678
11679         //      If this ship is the leader, abort, as he doesn't have to follow anyone.
11680         if (wing_index == 0) {
11681                 // nprintf(("AI", "Hmm, wing leader %s in ai_formation for no good reason.\n", shipp->ship_name));
11682                 return 1;
11683         }
11684
11685         if (aip->mode == AIM_WAYPOINTS) {
11686                 aip->wp_list = laip->wp_list;
11687                 if (laip->wp_index < Waypoint_lists[laip->wp_list].count)
11688                         aip->wp_index = laip->wp_index;
11689                 else
11690                         aip->wp_index = Waypoint_lists[laip->wp_list].count - 1;
11691                 aip->wp_flags = laip->wp_flags;
11692                 aip->wp_dir = laip->wp_dir;
11693         }
11694
11695         #ifndef NDEBUG
11696         Debug_render_wing_phantoms |= (1 << wing_index);
11697         #endif
11698
11699         leader_speed = leader_objp->phys_info.speed;
11700         vector leader_vec = leader_objp->phys_info.vel;
11701
11702         get_absolute_wing_pos(&goal_point, leader_objp, wing_index, aip->ai_flags & AIF_FORMATION_OBJECT);
11703         vm_vec_scale_add(&future_goal_point_5, &goal_point, &leader_vec, 10.0f);
11704         vm_vec_scale_add(&future_goal_point_2, &goal_point, &leader_vec, 5.0f);
11705         vm_vec_scale_add(&future_goal_point_x, &goal_point, &leader_objp->orient.v.fvec, 10.0f);        //      used when very close to destination
11706         vm_vec_scale_add(&future_goal_point_1000x, &goal_point, &leader_objp->orient.v.fvec, 1000.0f);  //      used when very close to destination
11707
11708         //      Now, get information telling this object how to turn and accelerate to get to its
11709         //      desired location.
11710         vm_vec_sub(&vec_to_goal, &goal_point, &Pl_objp->pos);
11711         if ( vm_vec_mag_quick(&vec_to_goal) < AICODE_SMALL_MAGNITUDE )
11712                 vec_to_goal.xyz.x += 0.1f;
11713
11714         vm_vec_copy_normalize(&dir_to_goal, &vec_to_goal);
11715         //dot_to_goal = vm_vec_dot(&dir_to_goal, &leader_objp->orient.v.fvec);
11716         dot_to_goal = vm_vec_dot(&dir_to_goal, &Pl_objp->orient.v.fvec);
11717         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &goal_point);
11718         float   dist_to_goal_2 = vm_vec_dist_quick(&Pl_objp->pos, &future_goal_point_2);
11719
11720         // 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));
11721
11722         int     chaotic_leader = 0;
11723
11724         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.
11725
11726         if (dist_to_goal > 500.0f) {
11727                 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11728                 accelerate_ship(aip, 1.0f);
11729         } else if (dist_to_goal > 200.0f) {
11730                 if (dot_to_goal > -0.5f) {
11731                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11732                         float range_speed = shipp->current_max_speed - leader_speed;
11733                         if (range_speed > 0.0f)
11734                                 set_accel_for_target_speed(Pl_objp, leader_speed + range_speed * (dist_to_goal+100.0f)/500.0f);
11735                         else
11736                                 set_accel_for_target_speed(Pl_objp, shipp->current_max_speed);
11737                 } else {
11738                         turn_towards_point(Pl_objp, &future_goal_point_5, NULL, 0.0f);
11739                         if (leader_speed > 10.0f)
11740                                 set_accel_for_target_speed(Pl_objp, leader_speed *(1.0f + dot_to_goal));
11741                         else
11742                                 set_accel_for_target_speed(Pl_objp, 10.0f);
11743                 }
11744         } else {
11745                 vector  v2f2;
11746                 float   dot_to_f2;
11747                 float   dist_to_f2;
11748
11749                 dist_to_f2 = vm_vec_normalized_dir(&v2f2, &future_goal_point_2, &Pl_objp->pos);
11750                 dot_to_f2 = vm_vec_dot(&v2f2, &Pl_objp->orient.v.fvec);
11751
11752                 //      Leader flying like a maniac.  Don't try hard to form on wing.
11753                 if (chaotic_leader) {
11754                         turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11755                         set_accel_for_target_speed(Pl_objp, min(leader_speed*0.8f, 20.0f));
11756                 } else if (dist_to_goal > 75.0f) {
11757                         turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11758                         float   delta_speed;
11759                         float range_speed = shipp->current_max_speed - leader_speed;
11760                         if (range_speed > 0.0f)
11761                                 delta_speed = dist_to_goal_2/500.0f * range_speed;
11762                         else
11763                                 delta_speed = shipp->current_max_speed - leader_speed;
11764                         if (dot_to_goal < 0.0f) {
11765                                 delta_speed = -delta_speed;
11766                                 if (-delta_speed > leader_speed/2)
11767                                         delta_speed = -leader_speed/2;
11768                         }
11769
11770                         if (leader_speed < 5.0f)
11771                                 if (delta_speed < 5.0f)
11772                                         delta_speed = 5.0f;
11773
11774                         float scale = dot_to_f2;
11775                         if (scale < 0.1f)
11776                                 scale = 0.0f;
11777                         else
11778                                 scale *= scale;
11779
11780                         set_accel_for_target_speed(Pl_objp, scale * (leader_speed + delta_speed));
11781                 } else {
11782                         //nprintf(("AI", "Dot = %7.3f\n", dot_to_goal));
11783
11784                         if (leader_speed < 5.0f) {
11785                                 //      Leader very slow.  If not close to goal point, get very close.  Note, keep trying to get close unless
11786                                 //      moving very slowly, else momentum can carry far away from goal.
11787
11788                                 if ((dist_to_goal > 10.0f) || ((Pl_objp->phys_info.speed > leader_speed + 2.5f) && (dot_to_goal > 0.5f))) {
11789                                         //nprintf(("MK", "(1) "));
11790                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11791                                         set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/10.0f);
11792                                 } else {
11793                                         if (Pl_objp->phys_info.speed < 0.5f) {
11794                                                 //nprintf(("MK", "(2) "));
11795                                                 turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11796                                         } else {
11797                                                 //nprintf(("MK", "(3) "));
11798                                         }
11799                                         set_accel_for_target_speed(Pl_objp, leader_speed);
11800                                 }
11801                                 //nprintf(("MK", "dist: %7.3f, dot: %6.3f, speed: %7.3f\n", dist_to_goal, dot_to_goal, Pl_objp->phys_info.speed));
11802                         } else if (dist_to_goal > 10.0f) {
11803                                 float   dv;
11804
11805                                 future_goal_point_2;
11806
11807                                 turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11808
11809                                 if (dist_to_goal > 25.0f) {
11810                                         if (dot_to_goal < 0.3f)
11811                                                 dv = -0.1f;
11812                                         else
11813                                                 dv = dot_to_goal - 0.2f;
11814
11815                                         set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/5.0f * dv);
11816                                 } else {
11817                                         set_accel_for_target_speed(Pl_objp, leader_speed + 1.5f * dot_to_goal - 1.0f);
11818                                 }
11819                         } else {
11820                                 if (Pl_objp->phys_info.speed < 0.1f)
11821                                         turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11822                                 else
11823                                         turn_towards_point(Pl_objp, &future_goal_point_x, NULL, 0.0f);
11824                                 set_accel_for_target_speed(Pl_objp, 0.0f);
11825                         }
11826                 }
11827
11828         }
11829
11830         //      See how different this ship's bank is relative to wing leader
11831         float   up_dot = vm_vec_dot(&leader_objp->orient.v.uvec, &Pl_objp->orient.v.uvec);
11832         if (up_dot < 0.996f) {
11833                 vector  w_out;
11834                 matrix  new_orient;
11835                 vector  angular_accel;
11836
11837                 vm_vec_copy_scale(&angular_accel, &Pl_objp->phys_info.max_rotvel, 0.2f);
11838                 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);
11839
11840         //      nprintf(("AI", "Frame %d Bashing formation orient.  Dot was %6.3f, becomes %6.3f\n", Framecount, up_dot, vm_vec_dot(&leader_objp->orient.v.uvec, &new_orient.v.uvec)));
11841                 Pl_objp->orient = new_orient;
11842                 Pl_objp->phys_info.rotvel = w_out;
11843         //      Pl_objp->phys_info.desired_rotvel = w_out;
11844         } else {
11845                 Pl_objp->phys_info.rotvel.xyz.z = 0.0f;
11846         }
11847
11848         return 0;
11849 }
11850
11851 //      Return index of object repairing object objnum.
11852 int find_repairing_objnum(int objnum)
11853 {
11854         object          *objp;
11855         ship                    *shipp;
11856         ship_info       *sip;
11857         ship_obj                *so;
11858
11859         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11860                 objp = &Objects[so->objnum];
11861
11862                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11863
11864                 shipp = &Ships[objp->instance];
11865                 sip = &Ship_info[shipp->ship_info_index];
11866
11867                 if (sip->flags & SIF_SUPPORT) {
11868                         ai_info *aip;
11869
11870                         aip = &Ai_info[shipp->ai_index];
11871
11872                         if (aip->goal_objnum == objnum) {
11873                                 return objp-Objects;
11874                         }
11875                 }
11876         }
11877
11878         return -1;
11879 }
11880
11881 //      If object *objp is being repaired, deal with it!
11882 void ai_do_repair_frame(object *objp, ai_info *aip, float frametime)
11883 {
11884         if (Ships[objp->instance].team == TEAM_TRAITOR) {
11885                 ai_abort_rearm_request(objp);
11886                 return;
11887         }
11888
11889         if (aip->ai_flags & (AIF_BEING_REPAIRED | AIF_AWAITING_REPAIR)) {
11890                 int     dock_objnum;
11891                 ai_info *repair_aip;
11892
11893                 dock_objnum = aip->dock_objnum; // find_repairing_objnum(objp-Objects);
11894                 //Assert(dock_objnum != -1);
11895                 if (dock_objnum == -1)
11896                         return;
11897                 if (Objects[dock_objnum].signature != aip->dock_signature) {
11898                         Int3();         //      Curious -- object numbers match, but signatures do not.
11899                                                         //      Must mean original repair ship died and was replaced by current ship.
11900                         return;
11901                 }
11902         
11903                 repair_aip = &Ai_info[Ships[Objects[dock_objnum].instance].ai_index];
11904                 //Assert(repair_aip->mode == AIM_DOCK);
11905
11906                 if (aip->ai_flags & AIF_BEING_REPAIRED) {
11907                         // Assert(repair_aip->submode == AIS_DOCK_4);
11908
11909                         //      Wait awhile into the mode to synchronize with sound effect.
11910                         if (Missiontime - repair_aip->submode_start_time > REARM_SOUND_DELAY) {
11911                                 int repaired;
11912
11913                                 repaired = ship_do_rearm_frame( objp, frametime );              // hook to do missile rearming
11914
11915                                 //      See if fully repaired.  If so, cause process to stop.
11916                                 if ( repaired && (repair_aip->submode == AIS_DOCK_4)) {
11917
11918                                         repair_aip->submode = AIS_UNDOCK_0;
11919                                         repair_aip->submode_start_time = Missiontime;
11920
11921                                         // if repairing player object -- tell him done with repair
11922                                         if ( !MULTIPLAYER_CLIENT ){
11923                                                 ai_do_objects_repairing_stuff( objp, &Objects[dock_objnum], REPAIR_INFO_COMPLETE );
11924                                         }
11925                                 }
11926                         }
11927                 } else if (aip->ai_flags & AIF_AWAITING_REPAIR) {
11928                         //      If this ship has been awaiting repair for 90+ seconds, abort.
11929                         if ( !MULTIPLAYER_CLIENT ) {
11930                                 if ((Game_mode & GM_MULTIPLAYER) || (objp != Player_obj)) {
11931                                         if ((repair_aip->goal_objnum == OBJ_INDEX(objp)) && (timestamp_elapsed(aip->abort_rearm_timestamp))) {
11932                                                 ai_abort_rearm_request(objp);
11933                                                 aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);
11934                                         }
11935                                 }
11936                         }
11937                 }
11938         } else {
11939                 // AL 11-24-97: If this is the player ship, ensure the repair sound isn't playing.  We need to
11940                 //              do this check, since this is a looping sound, and may continue on if rearm/repair
11941                 //              finishes abnormally once sound begins looping.
11942                 if ( objp == Player_obj ) {
11943                         player_stop_repair_sound();
11944                 }
11945         }
11946 }
11947
11948 //      Shell around dock_orient_and_approach to detect whether dock process should be aborted.
11949 //      obj1 is the ship performing the repair.
11950 //      obj2 is the ship being repaired.
11951 void call_doa(object *obj1, object *obj2, ship_info *sip1)
11952 {
11953         if (sip1->flags & SIF_SUPPORT) {
11954                 if (obj2->phys_info.speed > MAX_REPAIR_SPEED) {
11955
11956                         // call the ai_abort rearm request code
11957                         ai_abort_rearm_request( obj2 );
11958                 } else
11959                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11960         } else {
11961                 if (Ship_info[Ships[obj1->instance].ship_info_index].flags & SIF_CARGO)
11962                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11963                 else if (Ship_info[Ships[obj2->instance].ship_info_index].flags & SIF_CARGO)
11964                         dock_orient_and_approach(obj2, obj1, DOA_DOCK_STAY);
11965                 else {
11966                         //mprintf(("Warning: Not sure, but making %s [%s] move to stay docked with %s [%s]\n",
11967                         //      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));
11968                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11969
11970                 }
11971         }
11972
11973 }
11974
11975 //      Maybe launch a countermeasure.
11976 //      Also, detect a supposed homing missile that no longer exists.
11977 void ai_maybe_launch_cmeasure(object *objp, ai_info *aip)
11978 {
11979         float                   dist;
11980         ship_info       *sip;
11981         ship                    *shipp;
11982
11983         shipp = &Ships[objp->instance];
11984         sip = &Ship_info[shipp->ship_info_index];
11985
11986         if (!(sip->flags & (SIF_SMALL_SHIP | SIF_TRANSPORT)))
11987                 return;
11988
11989         if (!shipp->cmeasure_count)
11990                 return;
11991
11992         if ( !timestamp_elapsed(shipp->cmeasure_fire_stamp) )
11993                 return;
11994
11995         //      If not on player's team and Skill_level + ai_class is low, never fire a countermeasure.  The ship is too dumb.
11996         if (shipp->team != Player_ship->team) {
11997                 if (Game_skill_level + aip->ai_class < 4){
11998                         return;
11999                 }
12000         }
12001
12002         if ((aip->nearest_locked_object != -1) && (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)) {
12003                 object  *weapon_objp;
12004                 weapon  *weaponp;
12005                 weapon_info     *wip;
12006
12007                 weapon_objp = &Objects[aip->nearest_locked_object];
12008                 weaponp = &Weapons[weapon_objp->instance];
12009                 wip = &Weapon_info[weaponp->weapon_info_index];
12010
12011                 if ((dist = vm_vec_dist_quick(&objp->pos, &weapon_objp->pos)) < weapon_objp->phys_info.speed*2.0f) {
12012         
12013                         aip->nearest_locked_distance = dist;
12014                         //      Verify that this object is really homing on us.
12015                         object  *weapon_objp;
12016
12017                         weapon_objp = &Objects[aip->nearest_locked_object];
12018
12019                         float   fire_chance;
12020
12021                         //      For ships on player's team, have constant, average chance to fire.
12022                         //      For enemies, increasing chance with higher skill level.
12023                         if (shipp->team == Player_ship->team)
12024                                 fire_chance = Cmeasure_fire_chance[NUM_SKILL_LEVELS/2];
12025                         else
12026                                 fire_chance = Cmeasure_fire_chance[Game_skill_level];
12027
12028                         //      Decrease chance to fire at lower ai class.
12029                         fire_chance *= (float) aip->ai_class/Num_ai_classes;
12030
12031                         float r = frand();
12032                         if (fire_chance < r) {
12033                                 //nprintf(("AI", "Not firing countermeasure due to skill level: %7.3f < %7.3f\n", fire_chance, r));
12034                                 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.
12035                                 return;
12036                         }
12037
12038                         if (weapon_objp->type == OBJ_WEAPON) {
12039                                 if (weapon_objp->instance >= 0) {
12040                                         //nprintf(("AI", "Firing countermeasure at time t=%7.3f\n", f2fl(Missiontime)));
12041                                         ship_launch_countermeasure(objp);
12042                                         shipp->cmeasure_fire_stamp = timestamp(2*CMEASURE_WAIT);
12043                                         return;
12044                                 }
12045                         }
12046         
12047                 }
12048         }
12049
12050         return;
12051 }
12052
12053 //      --------------------------------------------------------------------------
12054 void ai_preprocess_ignore_objnum(object *objp, ai_info *aip)
12055 {
12056 //      if (aip->ignore_objnum == UNUSED_OBJNUM)
12057 //              return;
12058
12059         if (aip->ai_flags & AIF_TEMPORARY_IGNORE) {
12060                 if (timestamp_elapsed(aip->ignore_expire_timestamp)) {
12061                         aip->ignore_objnum = UNUSED_OBJNUM;
12062                 }
12063         }
12064
12065         if (is_ignore_object(aip, aip->goal_objnum)) {
12066                 aip->goal_objnum = -1;
12067                 // AL 12-11-97: If in STRAFE mode, we need to ensure that target_objnum is also
12068                 //              set to -1
12069                 if ( aip->mode == AIM_STRAFE ) {
12070                         aip->target_objnum = -1;
12071                 }
12072         }
12073
12074         if (is_ignore_object(aip, aip->target_objnum))
12075                 aip->target_objnum = -1;
12076 }
12077
12078 /*
12079 void ai_safety_circle_spot()
12080 {
12081         vector  goal_point;
12082         ship_info       *sip;
12083
12084         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12085
12086         goal_point = Ai_info[Ships[Pl_objp->instance].ai_index].goal_point;
12087         turn_towards_tangent(Pl_objp, &goal_point, 50.0f);
12088
12089         set_accel_for_target_speed(Pl_objp, sip->max_speed/4.0f);
12090
12091 //      float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
12092 //      nprintf(("AI", "Ship %s circling %7.3f %7.3f %7.3f.  Distance = %7.3f\n", Ships[Pl_objp->instance].ship_name, goal_point.xyz.x, goal_point.xyz.y, goal_point.xyz.z, dist));
12093
12094 }
12095 */
12096
12097 #define CHASE_CIRCLE_DIST               100.0f
12098
12099 void ai_chase_circle(object *objp)
12100 {
12101         float           dist_to_goal;
12102         float           target_speed;
12103         vector  goal_point;
12104         ship_info       *sip;
12105         ai_info         *aip;
12106
12107         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12108
12109         target_speed = sip->max_speed/4.0f;
12110         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12111
12112         Assert(vm_vec_mag(&aip->goal_point) >= 0.0f);           //      Supposedly detects bogus vector
12113
12114         goal_point = aip->goal_point;
12115
12116         if (aip->ignore_objnum == UNUSED_OBJNUM) {
12117                 dist_to_goal = vm_vec_dist_quick(&aip->goal_point, &objp->pos);
12118
12119                 if (dist_to_goal > 2*CHASE_CIRCLE_DIST) {
12120                         vector  vec_to_goal;
12121                         //      Too far from circle goal, create a new goal point.
12122                         vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
12123                         vm_vec_scale_add(&aip->goal_point, &objp->pos, &vec_to_goal, CHASE_CIRCLE_DIST);
12124                 }
12125
12126                 goal_point = aip->goal_point;
12127         } else if (is_ignore_object(aip, aip->ignore_objnum)) {
12128                 object  *ignore_objp = &Objects[aip->ignore_objnum];
12129
12130                 vector  tvec1;
12131                 float           dist;
12132
12133                 dist = vm_vec_normalized_dir(&tvec1, &Pl_objp->pos, &ignore_objp->pos);
12134
12135                 if (dist < ignore_objp->radius*2 + 1500.0f) {
12136                         vm_vec_scale_add(&goal_point, &Pl_objp->pos, &tvec1, ignore_objp->radius*2 + 1400.0f);
12137                         if (dist < ignore_objp->radius*2 + 1300.0f)
12138                                 target_speed = sip->max_speed * (1.25f - dist/(ignore_objp->radius*2 + 1500.0f));
12139                 }
12140         }
12141
12142         Assert(vm_vec_mag(&aip->goal_point) >= 0.0f);           //      Supposedly detects bogus vector
12143
12144         turn_towards_tangent(Pl_objp, &goal_point, 10*objp->radius + 200.0f);
12145
12146         set_accel_for_target_speed(Pl_objp, target_speed);
12147
12148 }
12149
12150 #define SHIELD_BALANCE_RATE     0.2f            //      0.1f -> takes 10 seconds to equalize shield.
12151
12152 //      Transfer shield energy to most recently hit section from others.
12153 void ai_transfer_shield(object *objp, int quadrant_num)
12154 {
12155         int     i;
12156         float   transfer_amount;
12157         float   transfer_delta;
12158         ship_info       *sip;
12159         float   max_quadrant_strength;
12160
12161         sip = &Ship_info[Ships[objp->instance].ship_info_index];
12162         max_quadrant_strength = sip->shields/MAX_SHIELD_SECTIONS;
12163
12164         transfer_amount = 0.0f;
12165         transfer_delta = (SHIELD_BALANCE_RATE/2) * max_quadrant_strength;
12166
12167         if (objp->shields[quadrant_num] + (MAX_SHIELD_SECTIONS-1)*transfer_delta > max_quadrant_strength)
12168                 transfer_delta = (max_quadrant_strength - objp->shields[quadrant_num])/(MAX_SHIELD_SECTIONS-1);
12169
12170         for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12171                 if (i != quadrant_num) {
12172                         if (objp->shields[i] >= transfer_delta) {
12173                                 objp->shields[i] -= transfer_delta;
12174                                 transfer_amount += transfer_delta;
12175                         } else {
12176                                 transfer_amount += objp->shields[i];
12177                                 objp->shields[i] = 0.0f;
12178                         }
12179                 }
12180
12181         objp->shields[quadrant_num] += transfer_amount;
12182 }
12183
12184 void ai_balance_shield(object *objp)
12185 {
12186         int     i;
12187         float   shield_strength_avg;
12188         float   delta;
12189
12190
12191         shield_strength_avg = get_shield_strength(objp)/MAX_SHIELD_SECTIONS;
12192
12193         delta = SHIELD_BALANCE_RATE * shield_strength_avg;
12194
12195         for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12196                 if (objp->shields[i] < shield_strength_avg) {
12197                         add_shield_strength(objp, delta);
12198                         if (objp->shields[i] > shield_strength_avg)
12199                                 objp->shields[i] = shield_strength_avg;
12200                 } else {
12201                         add_shield_strength(objp, -delta);
12202                         if (objp->shields[i] < shield_strength_avg)
12203                                 objp->shields[i] = shield_strength_avg;
12204                 }
12205 }
12206
12207 //      Manage the shield for this ship.
12208 //      Try to max out the side that was most recently hit.
12209 void ai_manage_shield(object *objp, ai_info *aip)
12210 {
12211         ship_info *sip;
12212
12213         sip = &Ship_info[Ships[objp->instance].ship_info_index];
12214
12215         if (timestamp_elapsed(aip->shield_manage_timestamp)) {
12216                 float           delay;
12217
12218                 //      Scale time until next manage shield based on Skill_level.
12219                 //      Ships on player's team are treated as if Skill_level is average.
12220                 if (Ships[objp->instance].team != Player_ship->team){
12221                         delay = Shield_manage_delays[Game_skill_level];
12222                 } else {
12223                         delay = Shield_manage_delays[NUM_SKILL_LEVELS/2];
12224                 }
12225
12226                 //      Scale between 1x and 3x based on ai_class
12227                 delay = delay + delay * (float) (3*(Num_ai_classes - aip->ai_class - 1) / (Num_ai_classes - 1));
12228                 aip->shield_manage_timestamp = timestamp((int) (delay * 1000.0f));
12229
12230                 if (sip->flags & SIF_SMALL_SHIP) {
12231                         if (Missiontime - aip->last_hit_time < F1_0*10)
12232                                 ai_transfer_shield(objp, aip->last_hit_quadrant);
12233                         else
12234                                 ai_balance_shield(objp);
12235                 }
12236
12237                 // 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]));
12238         }
12239 }
12240
12241 //      See if object *objp should evade an incoming missile.
12242 //      *aip is the ai_info pointer within *objp.
12243 void ai_maybe_evade_locked_missile(object *objp, ai_info *aip)
12244 {
12245         ship                    *shipp;
12246         ship_info       *sip;
12247
12248         shipp = &Ships[objp->instance];
12249         sip = &Ship_info[shipp->ship_info_index];
12250
12251         //      Only small ships evade an incoming missile.  Why would a capital ship try to swerve?
12252         if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12253                 return;
12254         }
12255
12256         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
12257                 return;
12258         }
12259
12260         if (aip->nearest_locked_object != -1) {
12261                 object  *missile_objp;
12262
12263                 missile_objp = &Objects[aip->nearest_locked_object];
12264
12265                 if (Weapons[missile_objp->instance].homing_object != objp) {
12266                         //nprintf(("AI", "\nMissile lost home!"));
12267                         aip->nearest_locked_object = -1;
12268                         return;
12269                 }
12270
12271                 if ((missile_objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[missile_objp->instance].weapon_info_index].wi_flags & WIF_HOMING)) {
12272                         float dist = vm_vec_dist_quick(&missile_objp->pos, &objp->pos);
12273                         float dist2 = 4.0f  * vm_vec_mag_quick(&missile_objp->phys_info.vel);                   
12274                         if (dist < dist2) {
12275                                 switch (aip->mode) {
12276                                 //      If in AIM_STRAFE mode, don't evade if parent of weapon is targeted ship.
12277                                 case AIM_STRAFE:
12278                                         if ((missile_objp->parent != -1) && (missile_objp->parent == aip->target_objnum)) {
12279                                                 ;
12280                                         } else {
12281                                                 ;               //      Alan -- If you want to handle incoming weapons from someone other than the ship
12282                                                                 //      the strafing ship is attacking, do it here.
12283                                         }
12284                                         break;
12285                                 case AIM_CHASE:
12286                                         //      Don't always go into evade weapon mode.  Usually, a countermeasure gets launched.
12287                                         // If low on countermeasures, more likely to try to evade.  If 8+, never evade due to low cmeasures.
12288                                         if (((((Missiontime >> 18) ^ OBJ_INDEX(objp)) & 3) == 0) || 
12289                                                 (objp->phys_info.speed < 40.0f) ||
12290                                                 (frand() < 1.0f - (float) shipp->cmeasure_count/8.0f)) {
12291                                                 if (aip->submode != SM_ATTACK_FOREVER) {        //      SM_ATTACK_FOREVER means engines blown.
12292                                                         aip->submode = SM_EVADE_WEAPON;
12293                                                         aip->submode_start_time = Missiontime;
12294                                                 }
12295                                         }
12296                                         break;
12297                                 case AIM_DOCK:  //      Ships in dock mode can evade iif they are not currently repairing or docked.
12298                                         if (aip->ai_flags & (AIF_REPAIRING | AIF_DOCKED))
12299                                                 break;
12300                                 case AIM_GUARD:
12301                                         //      If in guard mode and far away from guard object, don't pursue guy that hit me.
12302                                         if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12303                                                 if (vm_vec_dist_quick(&objp->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12304                                                         return;
12305                                                 }
12306                                         }
12307                                 case AIM_EVADE:
12308                                 case AIM_GET_BEHIND:
12309                                 case AIM_STAY_NEAR:
12310                                 case AIM_STILL:
12311                                 case AIM_AVOID:
12312                                 case AIM_WAYPOINTS:
12313                                 case AIM_NONE:
12314                                 case AIM_BIGSHIP:
12315                                 case AIM_PATH:
12316                                 case AIM_BE_REARMED:
12317                                 case AIM_SAFETY:
12318                                 case AIM_BAY_EMERGE:
12319                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12320                                         aip->previous_mode = aip->mode;
12321                                         aip->previous_submode = aip->submode;
12322                                         aip->mode = AIM_EVADE_WEAPON;
12323                                         aip->submode = -1;
12324                                         aip->submode_start_time = Missiontime;
12325                                         aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Max time to evade.
12326                                         //nprintf(("AI", "%s Evade weapon in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
12327                                         break;
12328                                 case AIM_EVADE_WEAPON:          //      Note: We don't want to change mode on another evasion, or previous_mode will get bashed.
12329                                 case AIM_PLAY_DEAD:
12330                                 case AIM_BAY_DEPART:
12331                                 case AIM_SENTRYGUN:
12332                                         break;
12333                                 case AIM_WARP_OUT:
12334                                         break;
12335                                 default:
12336                                         Int3();                 //      Hey, what mode is it?
12337                                         break;
12338                                 }
12339                         }
12340                 } else {
12341                         aip->nearest_locked_object = -1;
12342                 }
12343         }
12344 }
12345
12346 //      Maybe evade a dumbfire weapon that was fired when Pl_objp was targeted.
12347 //      Have an 80% chance of evading in a second
12348 void maybe_evade_dumbfire_weapon(ai_info *aip)
12349 {
12350         //      Only small ships evade an incoming missile.  Why would a capital ship try to swerve?
12351         if (!(Ship_info[Ships[Pl_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12352                 return;
12353         }
12354
12355         //      Make sure in a mode in which we evade dumbfire weapons.
12356         switch (aip->mode) {
12357         case AIM_CHASE:
12358                 if (aip->submode == SM_ATTACK_FOREVER) {
12359                         return;
12360                 }
12361         case AIM_GUARD:
12362                 //      If in guard mode and far away from guard object, don't pursue guy that hit me.
12363                 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12364                         if (vm_vec_dist_quick(&Objects[Ships[aip->shipnum].objnum].pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12365                                 return;
12366                         }
12367                 }
12368         case AIM_STILL:
12369         case AIM_STAY_NEAR:
12370         case AIM_EVADE:
12371         case AIM_GET_BEHIND:
12372         case AIM_AVOID:
12373         case AIM_PATH:
12374         case AIM_NONE:
12375         case AIM_WAYPOINTS:
12376         case AIM_SAFETY:
12377                 break;
12378         case AIM_STRAFE:
12379         case AIM_BIGSHIP:
12380         case AIM_DOCK:
12381         case AIM_PLAY_DEAD:
12382         case AIM_EVADE_WEAPON:
12383         case AIM_BAY_EMERGE:
12384         case AIM_BAY_DEPART:
12385         case AIM_SENTRYGUN:
12386         case AIM_WARP_OUT:
12387                 return;
12388         default:
12389                 Int3(); //      Bogus mode!
12390                 return;
12391         }
12392
12393         if (is_instructor(&Objects[Ships[aip->shipnum].objnum]))
12394                 return; //      Instructor doesn't evade.
12395
12396         float t = ai_endangered_by_weapon(aip);
12397         if ((t > 0.0f) && (t < 1.0f)) {
12398         // Check if this weapon is from a large ship Pl_objp is attacking... if so, enter strafe mode
12399                 if ( ai_big_maybe_enter_strafe_mode(Pl_objp, aip->danger_weapon_objnum) ) {
12400                         return;
12401                 }
12402
12403                 switch (aip->mode) {
12404                 case AIM_CHASE:
12405                         switch (aip->submode) {
12406                         case SM_EVADE:
12407                         case SM_ATTACK_FOREVER:
12408                         case SM_AVOID:
12409                         case SM_GET_AWAY:
12410                         case SM_EVADE_WEAPON:
12411                                 break;
12412                         default:
12413                                 if (ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
12414                                         //mprintf(("Ship %s entered super mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12415                                         aip->submode = SM_SUPER_ATTACK;
12416                                         aip->submode_start_time = Missiontime;
12417                                         aip->last_attack_time = Missiontime;
12418                                 } else {
12419                                         //mprintf(("Ship %s entered dumbfire evade mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12420                                         aip->submode = SM_EVADE_WEAPON;
12421                                         aip->submode_start_time = Missiontime;
12422                                 }
12423                                 break;
12424                         }
12425                         break;
12426                 case AIM_GUARD:
12427                 case AIM_STILL:
12428                 case AIM_STAY_NEAR:
12429                 case AIM_EVADE:
12430                 case AIM_GET_BEHIND:
12431                 case AIM_AVOID:
12432                 case AIM_PATH:
12433                 case AIM_NONE:
12434                 case AIM_WAYPOINTS:
12435                 case AIM_SAFETY:
12436                         if (!(aip->ai_flags & (AIF_NO_DYNAMIC | AIF_KAMIKAZE)) && (Ship_info[Ships[aip->shipnum].ship_info_index].flags & SIF_SMALL_SHIP)) {
12437                                 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12438                                 aip->previous_mode = aip->mode;
12439                                 aip->previous_submode = aip->submode;
12440                                 aip->mode = AIM_EVADE_WEAPON;
12441                                 aip->submode = -1;
12442                                 aip->submode_start_time = Missiontime;
12443                                 aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Evade for up to five seconds.
12444                         }
12445                         break;
12446                 case AIM_STRAFE:
12447                 case AIM_BIGSHIP:
12448                 case AIM_DOCK:
12449                 case AIM_PLAY_DEAD:
12450                 case AIM_EVADE_WEAPON:
12451                 case AIM_BAY_EMERGE:
12452                 case AIM_BAY_DEPART:
12453                 case AIM_SENTRYGUN:
12454                         break;
12455                 default:
12456                         Int3(); //      Bogus mode!
12457                 }
12458         }
12459 }
12460
12461 // determine what path to use when emerging from a fighter bay
12462 // input:       pl_objp =>      pointer to object for ship that is arriving
12463 //                              pos             =>      output parameter, it is the starting world pos for path choosen
12464 //                              v.fvec          =>      output parameter, this is the forward vector that ship has when arriving
12465 //
12466 // exit:                -1              =>      path could not be located
12467 //                               0              => success
12468 int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, vector *pos, vector *fvec)
12469 {
12470         int                     path_index, sb_path_index;
12471         ship                    *parent_sp = NULL;
12472         polymodel       *pm;
12473         ai_info         *aip;
12474         ship_bay                *sb;
12475         pnode                   *pnp;
12476         vector          *next_point;
12477
12478         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12479
12480         if ( parent_objnum == -1 ) {
12481                 Int3();
12482                 return -1;
12483         }
12484
12485         parent_sp = &Ships[Objects[parent_objnum].instance];
12486
12487         Assert(parent_sp != NULL);
12488         pm = model_get( parent_sp->modelnum );
12489         sb = pm->ship_bay;
12490
12491         if ( sb == NULL ) 
12492                 return -1;
12493
12494         if ( sb->num_paths <= 0 ) 
12495                 return -1;
12496
12497         // try to find a bay path that is not taken
12498         path_index = -1;
12499         sb_path_index = Ai_last_arrive_path++;
12500
12501         if ( sb_path_index >= sb->num_paths ) {
12502                 sb_path_index=0;
12503                 Ai_last_arrive_path=0;
12504         }
12505
12506         path_index = sb->paths[sb_path_index];
12507         if ( path_index == -1 ) 
12508                 return -1;
12509
12510         // create the path for pl_objp to follow
12511         create_model_exit_path(pl_objp, &Objects[parent_objnum], path_index, pm->paths[path_index].nverts);
12512         
12513         // Set this flag, so we don't bother recreating the path... we won't need to update the path
12514         // that has just been created.
12515 //      aip->ai_flags |= AIF_USE_STATIC_PATH;
12516
12517         // now return to the caller what the starting world pos and starting fvec for the ship will be
12518         Assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
12519         pnp = &Path_points[aip->path_start];
12520         *pos = pnp->pos;
12521
12522         // calc the forward vector using the starting two points of the path
12523         pnp = &Path_points[aip->path_start+1];
12524         next_point = &pnp->pos;
12525         vm_vec_normalized_dir(fvec, next_point, pos);
12526
12527         // record the parent objnum, since we'll need it once we're done with following the path
12528         aip->goal_objnum = parent_objnum;
12529         aip->goal_signature = Objects[parent_objnum].signature;
12530         aip->mode = AIM_BAY_EMERGE;
12531         aip->submode_start_time = Missiontime;
12532
12533         // set up starting vel
12534         vector vel;
12535         float speed;
12536         speed = Ship_info[Ships[pl_objp->instance].ship_info_index].max_speed;
12537         vel = *fvec;
12538         vm_vec_scale( &vel, speed );
12539         pl_objp->phys_info.vel = vel;
12540         pl_objp->phys_info.desired_vel = vel;
12541         pl_objp->phys_info.prev_ramp_vel.xyz.x = 0.0f;
12542         pl_objp->phys_info.prev_ramp_vel.xyz.y = 0.0f;
12543         pl_objp->phys_info.prev_ramp_vel.xyz.z = speed;
12544         pl_objp->phys_info.forward_thrust = 0.0f;               // How much the forward thruster is applied.  0-1.
12545
12546         return 0;       
12547 }
12548
12549 // clean up path data used for emerging from a fighter bay
12550 void ai_emerge_bay_path_cleanup(ai_info *aip)
12551 {
12552         aip->path_start = -1;
12553         aip->path_cur = -1;
12554         aip->path_length = 0;
12555         aip->mode = AIM_NONE;
12556 }
12557
12558 // handler for AIM_BAY_EMERGE
12559 void ai_bay_emerge()
12560 {
12561         ai_info *aip;
12562         int             parent_died=0;
12563
12564         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12565
12566         // if no path to follow, leave this mode
12567         if ( aip->path_start < 0 ) {
12568                 aip->mode = AIM_NONE;
12569                 return;
12570         }
12571
12572         // ensure parent ship is still alive
12573         if ( aip->goal_objnum < 0 ) {
12574                 parent_died=1;
12575         } 
12576         if ( !parent_died ) {
12577                 if ( Objects[aip->goal_objnum].signature != aip->goal_signature ) {
12578                         parent_died=1;
12579                 }
12580         }
12581
12582         if ( !parent_died ) {
12583                 Assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
12584                 if ( Ships[Objects[aip->goal_objnum].instance].flags & SF_DYING ) {
12585                         parent_died = 1;
12586                 }
12587         }
12588
12589         if ( parent_died ) {
12590                 ai_emerge_bay_path_cleanup(aip);
12591                 return;
12592         }
12593
12594         // follow the path to the final point
12595         ai_path();
12596
12597         // New test: must have been in AI_EMERGE mode for at least 10 seconds, and be a minimum distance from the start point
12598         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)) {
12599                 // erase path
12600                 ai_emerge_bay_path_cleanup(aip);
12601         }
12602
12603         // 2-25-99: Need this check to fix an assert for supercap ships... maybe we'll only do this check for supercaps 
12604         if (aip->path_cur > (aip->path_start+aip->path_length-1)) {
12605                 ai_emerge_bay_path_cleanup(aip);
12606         }       
12607 }
12608
12609 // Select the closest depart path
12610 //
12611 //      input:  aip     =>              ai info pointer to ship seeking to depart
12612 //                              pm              =>              pointer to polymodel for the ship contining the ship bay/depart paths
12613 //
12614 // exit:                >=0     =>              ship bay path index for depart path (ie index into sb->paths[])
12615 //                              -1              =>              no path could be found
12616 //
12617 // NOTE: this function should only be used for calculating closest depart paths for ai mode
12618 //                      AI_BAY_DEPART.  It tries to find the closest path that isn't already in use
12619 int ai_find_closest_depart_path(ai_info *aip, polymodel *pm)
12620 {
12621         int                     i, j, best_path, best_free_path;
12622         float                   dist, min_dist, min_free_dist;
12623         vector          *source;
12624         model_path      *mp;
12625         ship_bay                *sb;
12626
12627         sb = pm->ship_bay;
12628
12629         best_free_path = best_path = -1;
12630         min_free_dist = min_dist = 1e20f;
12631         Assert(aip->shipnum >= 0);
12632         source = &Objects[Ships[aip->shipnum].objnum].pos;
12633
12634         for ( i = 0; i < sb->num_paths; i++ ) {
12635
12636
12637                 mp = &pm->paths[sb->paths[i]];
12638                 for ( j = 0; j < mp->nverts; j++ ) {
12639                         dist = vm_vec_dist_squared(source, &mp->verts[j].pos);
12640
12641                         if ( dist < min_dist ) {
12642                                 min_dist = dist;
12643                                 best_path = i;
12644                         }
12645
12646                         // If this is a free path
12647                         if ( !(sb->depart_flags & (1<<i)) ) {
12648                                 if ( dist < min_free_dist ) {
12649                                         min_free_dist = dist;
12650                                         best_free_path = i;
12651                                 }
12652                         }
12653                 }
12654         }
12655
12656         if ( best_free_path >= 0 ) {
12657                 return best_free_path;          
12658         }
12659
12660         return best_path;
12661 }
12662
12663 // determine what path to use when trying to depart to a fighter bay
12664 // NOTE: this should be called when AIM_BAY_DEPART mode is set
12665 //
12666 // input:       pl_objp =>      pointer to object for ship that is departing
12667 //
12668 // exit:                -1      =>      could not find depart path
12669 //                              0       => found depart path
12670 int ai_acquire_depart_path(object *pl_objp, int parent_objnum)
12671 {
12672         int                     objnum, path_index;
12673         polymodel       *pm;
12674         ai_info         *aip;
12675         ship                    *sp;
12676         ship_bay                *sb;
12677
12678         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12679
12680         if ( parent_objnum == -1 ) {
12681                 ship_obj        *so;
12682
12683                 // for now just locate a captial ship on the same team:
12684                 so = GET_FIRST(&Ship_obj_list);
12685                 objnum = -1;
12686                 while(so != END_OF_LIST(&Ship_obj_list)){
12687                         sp = &Ships[Objects[so->objnum].instance];
12688                         if ( (Ship_info[sp->ship_info_index].flags & (SIF_HUGE_SHIP)) && (sp->team == Ships[pl_objp->instance].team) ) {
12689                                 objnum = so->objnum;
12690                                 break;
12691                         }
12692                         so = GET_NEXT(so);
12693                 } 
12694         } else {
12695                 objnum = parent_objnum;
12696         }
12697
12698         aip->path_start = -1;
12699
12700         if ( objnum == -1 )
12701                 return -1;
12702
12703         pm = model_get( Ships[Objects[objnum].instance].modelnum );
12704         sb = pm->ship_bay;
12705
12706         if ( sb == NULL ) 
12707                 return -1;
12708         if ( sb->num_paths <= 0 ) 
12709                 return -1;
12710
12711 /*
12712         
12713         path_index = -1;
12714         for ( i = 0; i < sb->num_paths; i++ ) {
12715                 if ( !(sb->depart_flags & (1<<i)) ) {
12716                         sb->depart_flags |= (1<<i);
12717                         path_index = sb->paths[i];
12718                         aip->submode_parm0 = i;                 // use mode-specific parameter to record ship bay path index
12719                         break;
12720                 }
12721         }
12722 */
12723         
12724         // take the closest path we can find
12725         int ship_bay_path;
12726         ship_bay_path = ai_find_closest_depart_path(aip, pm);
12727         path_index = sb->paths[ship_bay_path];
12728         aip->submode_parm0 = ship_bay_path;
12729         sb->depart_flags |= (1<<ship_bay_path);
12730
12731         if ( path_index == -1 ) {
12732                 return -1;
12733         }
12734
12735         Assert(pm->n_paths > path_index);
12736         ai_find_path(pl_objp, objnum, path_index, 0);
12737
12738         // Set this flag, so we don't bother recreating the path... we won't need to update the path
12739         // that has just been created.
12740         aip->ai_flags &= ~AIF_USE_STATIC_PATH;
12741
12742         aip->goal_objnum = objnum;
12743         aip->goal_signature = Objects[objnum].signature;
12744         aip->mode = AIM_BAY_DEPART;
12745
12746         Ships[pl_objp->instance].flags |= SF_DEPART_DOCKBAY;
12747         return 0;
12748 }
12749
12750 // handler for AIM_BAY_DEPART
12751 void ai_bay_depart()
12752 {
12753         ai_info *aip;
12754
12755         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12756
12757         // if no path to follow, leave this mode
12758         if ( aip->path_start < 0 ) {
12759                 aip->mode = AIM_NONE;
12760                 return;
12761         }
12762
12763         // check if parent ship still exists, if not abort depart 
12764         if ( aip->goal_signature != Objects[aip->goal_objnum].signature ) {
12765                 aip->mode = AIM_NONE;
12766                 return;
12767         }
12768
12769         // follow the path to the final point
12770         ai_path();
12771
12772         // if the final point is reached, let default AI take over
12773         if ( aip->path_cur >= (aip->path_start+aip->path_length) ) {
12774                 polymodel       *pm;
12775                 ship_bay                *sb;
12776
12777                 pm = model_get( Ships[Objects[aip->goal_objnum].instance].modelnum );
12778                 sb = pm->ship_bay;
12779                 if ( sb != NULL ) {
12780                         sb->depart_flags &= ~(1<<aip->submode_parm0);
12781                 }
12782
12783                 // make ship disappear
12784                 Pl_objp->flags |= OF_SHOULD_BE_DEAD;
12785                 ship_departed( Pl_objp->instance );
12786
12787                 // clean up path stuff
12788                 aip->path_start = -1;
12789                 aip->path_cur = -1;
12790                 aip->path_length = 0;
12791                 aip->mode = AIM_NONE;
12792         }
12793 }
12794
12795 // Handler for AIM_SENTRYGUN.  This AI mode is for sentry guns only (ie floating turrets).
12796 void ai_sentrygun()
12797 {
12798         // Nothing to do here.  Turret firing is handled via process_subobjects().
12799         // If you want the sentry guns to do anything beyond firing their turrets at enemies, add it here!
12800 }
12801
12802 //      --------------------------------------------------------------------------
12803 //      Execute behavior given by aip->mode.
12804 void ai_execute_behavior(ai_info *aip)
12805 {
12806         switch (aip->mode) {
12807         case AIM_CHASE:
12808                 if (En_objp) {
12809                         ai_chase();
12810                 } else if (aip->submode == SM_EVADE_WEAPON) {
12811                         evade_weapon();
12812                         // maybe reset submode
12813                         if (aip->danger_weapon_objnum == -1) {
12814                                 aip->submode = SM_ATTACK;
12815                                 aip->submode_start_time = Missiontime;
12816                                 aip->last_attack_time = Missiontime;
12817                         }
12818                 } else {
12819                         //      Don't circle if this is the instructor.
12820                         ship    *shipp = &Ships[aip->shipnum];
12821                         ship_info       *sip = &Ship_info[shipp->ship_info_index];
12822
12823                         if (strnicmp(shipp->ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME))) {
12824                                 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
12825                                         aip->mode = AIM_NONE;
12826                                 } else {
12827                                         ai_chase_circle(Pl_objp);
12828                                 }
12829                         }
12830                 }
12831                 break;
12832         case AIM_EVADE:
12833                 if (En_objp) {
12834                         ai_evade();
12835                 } else {
12836                         vector  tvec;
12837                         vm_vec_scale_add(&tvec, &Pl_objp->pos, &Pl_objp->orient.v.rvec, 100.0f);
12838                         turn_towards_point(Pl_objp, &tvec, NULL, 0.0f);
12839                         accelerate_ship(aip, 0.5f);
12840                 }
12841                 break;
12842         case AIM_STILL:
12843                 ai_still();
12844                 break;
12845         case AIM_STAY_NEAR:
12846                 ai_stay_near();
12847                 break;
12848         case AIM_GUARD:
12849                 ai_guard();
12850                 break;
12851         case AIM_WAYPOINTS:
12852                 ai_waypoints();
12853                 break;
12854         case AIM_DOCK:
12855                 ai_dock();
12856                 break;
12857         case AIM_NONE:
12858                 // ai_formation();
12859                 break;
12860         case AIM_BIGSHIP:
12861                 ai_big_ship(Pl_objp);
12862                 break;
12863         case AIM_PATH: {
12864                 int path_num;
12865                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], 0);
12866                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
12867                 ai_path();
12868                 break;
12869         }
12870         case AIM_SAFETY:
12871                 ai_safety();
12872                 break;
12873         case AIM_EVADE_WEAPON:
12874                 evade_weapon();
12875                 break;
12876         case AIM_STRAFE:
12877                 if (En_objp) {
12878                         Assert(En_objp->type == OBJ_SHIP);
12879                         ai_big_strafe();        // strafe a big ship
12880                 } else {
12881                         aip->mode = AIM_NONE;
12882                 }
12883                 break;
12884         case AIM_BAY_EMERGE:
12885                 ai_bay_emerge();
12886                 break;
12887         case AIM_BAY_DEPART:
12888                 ai_bay_depart();
12889                 break;
12890         case AIM_SENTRYGUN:
12891                 ai_sentrygun();
12892                 break;
12893         case AIM_WARP_OUT:
12894                 break;          //      Note, handled directly from ai_frame().
12895         default:
12896                 Int3();         //      This should never happen -- MK, 5/12/97 
12897                 break;
12898         }
12899
12900         if ( !(ship_get_SIF(aip->shipnum) & SIF_NOT_FLYABLE) ) {
12901                 maybe_evade_dumbfire_weapon(aip);
12902         }
12903 }
12904
12905 //      Auxiliary function for maybe_request_support.
12906 //      Return 1 if subsystem "type" is worthy of repair, else return 0.
12907 //      Since subsystems cannot be repaired if they are at 0 strength, don't return 1 if subsystem is dead.
12908 int mrs_subsystem(ship *shipp, int type)
12909 {
12910         float   t;
12911
12912         t = ship_get_subsystem_strength(shipp, type);
12913
12914         if (t > 0.0f) {
12915                 return (int) ((1.0f - t) * 3);
12916         } else {
12917                 return 3;
12918         }
12919 }
12920
12921 //      Return number of ships on *objp's team that are currently rearming.
12922 int num_allies_rearming(object *objp)
12923 {
12924         ship_obj        *so;
12925         int             team;
12926         int             count = 0;
12927
12928         team = Ships[objp->instance].team;
12929
12930         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
12931                 object  *A;
12932                 
12933                 Assert (so->objnum != -1);
12934                 A = &Objects[so->objnum];
12935
12936                 if (Ships[A->instance].team == team) {
12937                         if (Ai_info[Ships[A->instance].ai_index].ai_flags & (AIF_REPAIRING | AIF_AWAITING_REPAIR)) {
12938                                 count++;
12939                         }
12940                 }
12941         }
12942
12943         return count;
12944 }
12945
12946
12947 //      Maybe ship *objp should request support (rearm/repair).
12948 //      If it does, return TRUE, else return FALSE.
12949 int maybe_request_support(object *objp)
12950 {
12951         ship_info       *sip;
12952         ship                    *shipp;
12953         ai_info         *aip;
12954         int                     desire;
12955
12956         Assert(objp->type == OBJ_SHIP);
12957         shipp = &Ships[objp->instance];
12958         aip = &Ai_info[shipp->ai_index];
12959         sip = &Ship_info[shipp->ship_info_index];
12960
12961         if (!timestamp_elapsed(aip->next_rearm_request_timestamp))
12962                 return 0;
12963
12964         //      Only fighters and bombers request support.
12965         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER)))
12966                 return 0;
12967
12968         //      A ship that is currently awaiting does not need support!
12969         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
12970                 return 0;
12971
12972         if (!is_support_allowed(objp))
12973                 return 0;
12974
12975         //if (shipp->team != TEAM_FRIENDLY)
12976         //      return 0;
12977
12978         //      Compute a desire value.
12979         //      Desire of 0 means no reason to request support.
12980         //      1 is slight, 2 more, etc.  Maximum is around 20.  Anything larger than 3 is pretty strong.
12981         desire = 0;
12982
12983         //      Set desire based on hull strength.
12984         //      No: We no longer repair hull, so this would cause repeated repair requests.
12985         //desire += 6 - (int) ((objp->hull_strength/sip->initial_hull_strength) * 6.0f);
12986
12987         //      Set desire based on key subsystems.
12988         desire += 2*mrs_subsystem(shipp, SUBSYSTEM_ENGINE);     //      Note, disabled engine forces repair request, regardless of nearby enemies.
12989         desire += mrs_subsystem(shipp, SUBSYSTEM_COMMUNICATION);
12990         desire += mrs_subsystem(shipp, SUBSYSTEM_WEAPONS);
12991         desire += mrs_subsystem(shipp, SUBSYSTEM_SENSORS);
12992
12993         //      Set desire based on percentage of secondary weapons.
12994         ship_weapon *swp = &shipp->weapons;
12995
12996         for ( int i = 0; i < swp->num_secondary_banks; i++ ) {
12997                 if (swp->secondary_bank_start_ammo[i] > 0) {
12998 //                      float r = (float) swp->secondary_bank_ammo[i]*Weapon_info[swp->secondary_bank_weapons[i]].cargo_size/swp->secondary_bank_capacity[i];
12999                         float r = (float) swp->secondary_bank_ammo[i]/swp->secondary_bank_start_ammo[i];
13000                         desire += (int) ((1.0f - r) * 3.0f);
13001                 }
13002         }
13003
13004         //      If no reason to repair, don't bother to see if it's safe to repair.
13005         if (desire == 0){
13006                 return 0;
13007         }
13008
13009         //      Compute danger threshold.
13010         //      Balance this with desire and maybe request support.
13011         if (ai_good_time_to_rearm( objp )) {
13012                 ai_issue_rearm_request(objp);
13013                 return 1;
13014         } else if (num_allies_rearming(objp) < 2) {
13015                 if (desire >= 8) {      //      guarantees disabled will cause repair request
13016                         ai_issue_rearm_request(objp);
13017                 } else if (desire >= 3) {               //      >= 3 means having a single subsystem fully blown will cause repair.
13018                         int     count;
13019                         int objnum = find_nearby_hostile(OBJ_INDEX(objp), get_enemy_team_mask(OBJ_INDEX(objp)), 2000.0f, &count);
13020
13021                         if ((objnum == -1) || (count < 2) || (vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos) > 3000.0f*count/desire)) {
13022                                 ai_issue_rearm_request(objp);
13023                                 return 1;
13024                         } else {
13025                                 //nprintf(("AI", "Would like to rearm, but enemy only %7.3f units away.\n", vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos)));
13026                         }
13027                 }
13028         }
13029
13030         return 0;
13031
13032 }
13033
13034 void ai_set_mode_warp_out(object *objp, ai_info *aip)
13035 {
13036         ai_abort_rearm_request(objp);
13037         if (aip->mode != AIM_WARP_OUT) {
13038                 aip->mode = AIM_WARP_OUT;
13039                 aip->submode = AIS_WARP_1;
13040         }
13041 }
13042
13043 //      Maybe warp ship out.
13044 //      Shivan and HoL fighter/bomber warp out if their weapons subsystems have been destroyed.
13045 void ai_maybe_warp_out(object *objp)
13046 {
13047         ship    *shipp;
13048
13049         // don't do anything if in a training mission.
13050         if ( The_mission.game_type & MISSION_TYPE_TRAINING )
13051                 return;
13052
13053         Assert(objp->type == OBJ_SHIP);
13054
13055         shipp = &Ships[objp->instance];
13056         ai_info *aip = &Ai_info[shipp->ai_index];
13057
13058         if (aip->mode == AIM_WARP_OUT)
13059                 return;
13060
13061         //      If a support ship with no goals and low hull, warp out.  Be sure that there are no pending goals
13062         // in the support ships ai_goal array.  Just process this ships goals.
13063         ship_info       *sip = &Ship_info[shipp->ship_info_index];
13064         if (sip->flags & SIF_SUPPORT) {
13065                 if ( timestamp_elapsed(aip->warp_out_timestamp) ) {
13066                         ai_process_mission_orders( OBJ_INDEX(objp), aip );
13067                         if ( (aip->dock_objnum == -1) && (objp->hull_strength/sip->initial_hull_strength < 0.25f) ) {
13068                                 ai_set_mode_warp_out(objp, aip);
13069                         }
13070                 }
13071         }
13072
13073         //      Friendly don't warp out, they'll eventually request support.
13074         if (shipp->team == TEAM_FRIENDLY)
13075                 return;
13076
13077         if (!(shipp->flags & SF_DEPARTING)) {
13078                 ship_info       *sip;
13079
13080                 sip = &Ship_info[shipp->ship_info_index];
13081                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
13082                         if (aip->warp_out_timestamp == 0) {
13083                                 //if (ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS) == 0.0f) {
13084                                 //      aip->warp_out_timestamp = timestamp(((myrand() % 10) + 10) * 1000);
13085                                 //}
13086                         } else if (timestamp_elapsed(aip->warp_out_timestamp)) {
13087                                 ai_set_mode_warp_out(objp, aip);
13088                         }
13089                 }
13090         }
13091 }
13092
13093 //      Warp this ship out.
13094 void ai_warp_out(object *objp)
13095 {
13096         // if dying, don't warp out.
13097         if (Ships[objp->instance].flags & SF_DYING) {
13098                 return;
13099         }
13100
13101         ai_info *aip;
13102
13103         aip = &Ai_info[Ships[objp->instance].ai_index];
13104
13105         switch (aip->submode) {
13106         case AIS_WARP_1:
13107                 aip->force_warp_time = timestamp(10*1000);      //      Try to avoid a collision for up to ten seconds.
13108                 aip->submode = AIS_WARP_2;
13109                 break;
13110         case AIS_WARP_2:                        //      Make sure won't collide with any object.
13111                 if (timestamp_elapsed(aip->force_warp_time) || !collide_predict_large_ship(objp, objp->radius*2.0f + 100.0f)) {
13112                         aip->submode = AIS_WARP_3;
13113
13114                         // maybe recalculate collision pairs.
13115                         if (ship_get_warp_speed(objp) > ship_get_max_speed(&Ships[objp->instance])) {
13116                                 // recalculate collision pairs
13117                                 OBJ_RECALC_PAIRS(objp); 
13118                         }
13119
13120                         aip->force_warp_time = timestamp(4*1000);               //      Try to attain target speed for up to 4 seconds.
13121                 } else {
13122                         vector  goal_point;
13123                         vm_vec_scale_add(&goal_point, &objp->pos, &objp->orient.v.uvec, 100.0f);
13124                         turn_towards_point(objp, &goal_point, NULL, 0.0f);
13125                         accelerate_ship(aip, 0.0f);
13126                 }
13127                 break;
13128         case AIS_WARP_3:
13129                 //      Rampup desired_vel in here from current to desired velocity and set PF_USE_VEL. (not sure this is the right flag)
13130                 //      desired velocity is computed in shipfx_calculate_warp_time().  See shipfx#572 for sample code.
13131                 float   speed, goal_speed;
13132                 float shipfx_calculate_warp_speed(object*);
13133                 goal_speed = shipfx_calculate_warp_speed(objp);
13134
13135                 // HUGE ships go immediately to AIS_WARP_4
13136                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_HUGE_SHIP) {
13137                         aip->submode = AIS_WARP_4;
13138                         break;
13139                 }
13140                 //compute_warpout_stuff(objp, &goal_speed, &warp_time, &warp_pos);
13141                 //goal_speed = 80.0f;
13142                 //set_accel_for_target_speed(objp, 40.0f);
13143                 // 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
13144                 speed = goal_speed * flFrametime + objp->phys_info.speed * (1.0f - flFrametime);
13145                 vm_vec_copy_scale(&objp->phys_info.vel, &objp->orient.v.fvec, speed);
13146                 objp->phys_info.desired_vel = objp->phys_info.vel;
13147                 // nprintf(("AI", "Frame %i, speed = %7.3f, goal = %7.3f\n", Framecount, vm_vec_mag_quick(&objp->phys_info.vel), goal_speed));
13148                 if (timestamp_elapsed(aip->force_warp_time) || (fl_abs(objp->phys_info.speed - goal_speed) < 2.0f))
13149                         aip->submode = AIS_WARP_4;
13150                 break;
13151         case AIS_WARP_4: {
13152                 shipfx_warpout_start(objp);
13153                 aip->submode = AIS_WARP_5;
13154                 break;
13155         }
13156         case AIS_WARP_5:
13157                 break;
13158         default:
13159                 Int3();         //      Illegal submode for warping out.
13160         }
13161 }
13162
13163 //      Return object index of weapon that could produce a shockwave that should be known about to *objp.
13164 //      Return nearest one.
13165 int ai_find_shockwave_weapon(object *objp, ai_info *aip)
13166 {
13167         missile_obj     *mo;
13168         float   nearest_dist = 999999.9f;
13169         int     nearest_index = -1;
13170
13171         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
13172                 object          *A;
13173                 weapon          *wp;
13174                 weapon_info     *wip;
13175         
13176                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
13177                 A = &Objects[mo->objnum];
13178
13179                 Assert(A->type == OBJ_WEAPON);
13180                 Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
13181                 wp = &Weapons[A->instance];
13182                 wip = &Weapon_info[wp->weapon_info_index];
13183                 Assert( wip->subtype == WP_MISSILE );
13184
13185                 if (wip->shockwave_speed > 0.0f) {
13186                         float   dist;
13187
13188                         dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13189                         if (dist < nearest_dist) {
13190                                 nearest_dist = dist;
13191                                 nearest_index = mo->objnum;
13192                         }
13193                 }
13194         }
13195
13196         return nearest_index;
13197
13198 }
13199
13200 #define EVADE_SHOCKWAVE_DAMAGE_THRESHOLD                100.0f
13201
13202 //      Tell all ships to avoid a big ship that is blowing up.
13203 //      Only avoid if shockwave is fairly large.
13204 //      OK to tell everyone to avoid.  If they're too far away, that gets cleaned up in the frame interval.
13205 void ai_announce_ship_dying(object *dying_objp)
13206 {
13207         float damage = ship_get_exp_damage(dying_objp);
13208         if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {
13209                 ship_obj        *so;
13210
13211                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13212                         if (Ship_info[Ships[Objects[so->objnum].instance].ship_info_index].flags & (SIF_SMALL_SHIP | SIF_FREIGHTER)) {
13213                                 ai_info *aip;
13214
13215                                 aip = &Ai_info[Ships[Objects[so->objnum].instance].ai_index];
13216
13217                                 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
13218                                         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_SHIP;
13219                                 }
13220                         }
13221                 }
13222         }
13223 }
13224
13225
13226 //      Return object index of weapon that could produce a shockwave that should be known about to *objp.
13227 //      Return nearest one.
13228 int ai_find_shockwave_ship(object *objp, ai_info *aip)
13229 {
13230         ship_obj        *so;
13231         float   nearest_dist = 999999.9f;
13232         int     nearest_index = -1;
13233
13234         for ( so = GET_NEXT(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13235                 object          *A;
13236                 ship                    *shipp;
13237         
13238                 Assert(so->objnum >= 0 && so->objnum < MAX_OBJECTS);
13239                 A = &Objects[so->objnum];
13240
13241                 Assert(A->type == OBJ_SHIP);
13242                 Assert((A->instance >= 0) && (A->instance < MAX_SHIPS));
13243                 shipp = &Ships[A->instance];
13244                 //      Only look at objects in the process of dying.
13245                 if (shipp->flags & SF_DYING) {
13246                         float damage = ship_get_exp_damage(objp);
13247
13248                         if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {               //      Only evade quite large blasts
13249                                 float   dist;
13250
13251                                 dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13252                                 if (dist < nearest_dist) {
13253                                         nearest_dist = dist;
13254                                         nearest_index = so->objnum;
13255                                 }
13256                         }
13257                 }
13258         }
13259
13260         return nearest_index;
13261
13262 }
13263
13264 int aas_1(object *objp, ai_info *aip, vector *safe_pos)
13265 {
13266         // MAKE SURE safe_pos DOES NOT TAKE US TOWARD THE A SHIP WE'RE ATTACKING.
13267         if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_WEAPON) {
13268                 //      If we don't currently know of a weapon to avoid, try to find one.
13269                 //      If we can't find one, then clear the bit so we don't keep coming here.
13270                 if (aip->shockwave_object == -1) {
13271                         int shockwave_weapon = ai_find_shockwave_weapon(objp, aip);
13272                         if (shockwave_weapon == -1) {
13273                                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13274                                 return 0;
13275                         } else {
13276                                 aip->shockwave_object = shockwave_weapon;
13277                         }
13278                 }
13279
13280                 //      OK, we have reason to believe we should avoid aip->shockwave_object.
13281                 Assert(aip->shockwave_object > -1);
13282                 object  *weapon_objp = &Objects[aip->shockwave_object];
13283                 if (weapon_objp->type != OBJ_WEAPON) {
13284                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13285                         aip->shockwave_object = -1;
13286                         return 0;
13287                 }
13288
13289                 weapon  *weaponp = &Weapons[weapon_objp->instance];
13290                 weapon_info     *wip = &Weapon_info[weaponp->weapon_info_index];
13291                 object *target_ship_obj = NULL;
13292
13293                 if (wip->shockwave_speed == 0.0f) {
13294                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13295                         aip->shockwave_object = -1;
13296                         return 0;
13297                 }
13298
13299                 float   danger_dist;
13300                 vector  expected_pos;           //      Position at which we expect the weapon to detonate.
13301                 int             pos_set = 0;
13302
13303                 danger_dist = wip->outer_radius;
13304                 //      Set predicted position of detonation.
13305                 //      If an aspect locked missile, assume it will detonate at the homing position.
13306                 //      If not, which is not possible in a default FreeSpace weapon, then predict it will detonate at some
13307                 //      time in the future, this time based on max lifetime and life left.
13308                 if (wip->wi_flags & WIF_HOMING_ASPECT) {
13309                         expected_pos = weaponp->homing_pos;
13310                         if (weaponp->homing_object && weaponp->homing_object->type == OBJ_SHIP) {
13311                                 target_ship_obj = weaponp->homing_object;
13312                         }
13313                         pos_set = 1;
13314                         if (IS_VEC_NULL(&weaponp->homing_pos)) {
13315                                 pos_set = 0;
13316                                 if (weaponp->target_num != -1) {
13317                                         if (Objects[weaponp->target_num].type == OBJ_SHIP) {
13318                                                 target_ship_obj = &Objects[weaponp->target_num];
13319                                                 expected_pos = target_ship_obj->pos;
13320                                                 pos_set = 1;
13321                                         }
13322                                 }
13323                         }
13324                 }
13325
13326                 if (!pos_set) {
13327                         float   time_scale;
13328
13329                         if (wip->lifetime - weaponp->lifeleft > 5.0f) {
13330                                 time_scale = 1.0f;
13331                         } else {
13332                                 time_scale = weaponp->lifeleft/2.0f;
13333                         }
13334
13335                         vm_vec_scale_add(&expected_pos, &weapon_objp->pos, &weapon_objp->orient.v.fvec, time_scale);
13336                 }
13337
13338                 //      See if too far away to care about shockwave.
13339                 if (vm_vec_dist_quick(&objp->pos, &expected_pos) > danger_dist*2.0f) {
13340                         //aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13341                         return 0;
13342                 } else {
13343                         // try to find a safe position
13344                         vector vec_from_exp;
13345                         float dir = 1.0f;
13346                         vm_vec_sub(&vec_from_exp, &objp->pos, &expected_pos);
13347                         float dot = vm_vec_dotprod(&vec_from_exp, &weapon_objp->orient.v.fvec);
13348                         if (dot > -30) {
13349                                 // if we're already on the other side of the explosion, don't try to fly behind it
13350                                 dir = -1.0f;
13351                         }
13352
13353                         //      Fly towards a point behind the weapon.
13354                         vm_vec_scale_add(safe_pos, &weapon_objp->pos, &weapon_objp->orient.v.fvec, -50000.0f*dir);
13355
13356                         // verify safe_pos will not make us collide with our target objnum, else try 2 other vecs
13357                         // don't bang your head, else go
13358 //                      int go_safe = FALSE;
13359                         int go_safe = TRUE;
13360 /*                      if (target_ship_obj) {
13361                                 if (pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius)) {
13362                                         // try up to 2 other random directions
13363                                         vector dir_vec, rand_vec;
13364                                         int idx;
13365                                         for (idx=0; idx<2; idx++) {
13366                                                 vm_vec_rand_vec_quick(&rand_vec);
13367                                                 vm_vec_scale_add(&dir_vec, &weapon_objp->orient.v.fvec, &rand_vec, 0.5f);
13368                                                 vm_vec_scale_add(safe_pos, &weapon_objp->pos, &dir_vec, -50000.0f*dir);
13369                                                 if ( !pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius) ) {
13370                                                         go_safe = TRUE;
13371                                                         break;
13372                                                 }
13373                                         }
13374                                 } else { // direct path is safe
13375                                         go_safe = TRUE;
13376                                 }
13377                         } else { // no target_obj_ship
13378                                 go_safe = TRUE;
13379                         } */
13380
13381                         if (go_safe) {
13382                                 return 1;
13383                         } else {
13384                                 // can't figure out a good way to go
13385                                 return 0;
13386                         }
13387                 }
13388         } else if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_SHIP) {
13389                 if (aip->shockwave_object == -1) {
13390                         int shockwave_ship = ai_find_shockwave_ship(objp, aip);
13391                         if (shockwave_ship == -1) {
13392                                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13393                                 return 0;
13394                         } else {
13395                                 aip->shockwave_object = shockwave_ship;
13396                         }
13397                 }
13398
13399                 Assert(aip->shockwave_object > -1);
13400                 object  *ship_objp = &Objects[aip->shockwave_object];
13401                 if (ship_objp == objp) {
13402                         aip->shockwave_object = -1;
13403                         return 0;
13404                 }
13405
13406                 if (ship_objp->type != OBJ_SHIP) {
13407                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13408                         return 0;
13409                 }
13410
13411                 //      Optimize note! Don't really have to normalize.  We only need a point away from the blowing-up ship.
13412                 vector safe_vec;
13413
13414                 vm_vec_normalized_dir(&safe_vec, &objp->pos, &ship_objp->pos);
13415                 vm_vec_scale_add(safe_pos, &ship_objp->pos, &safe_vec, 50000.0f);       //      Fly away from the ship.
13416
13417                 float outer_rad = ship_get_exp_outer_rad(ship_objp);
13418
13419                 if (vm_vec_dist_quick(&objp->pos, &ship_objp->pos) > outer_rad*1.5f) {
13420                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13421                         return 0;
13422                 }
13423
13424                 return 1;
13425
13426         } else {
13427                 Int3(); //      Illegal -- supposedly avoiding a shockwave, but neither ship nor weapon.  What is it!?
13428         }
13429
13430         return 0;
13431 }
13432
13433 /*
13434 int rct_done = 0;
13435
13436 void rand_chance_test()
13437 {
13438         int     i;
13439         float   frametime;
13440
13441         if (rct_done)
13442                 return;
13443
13444         rct_done = 1;
13445
13446         for (frametime=0.02f; frametime<0.25f; frametime *= 1.25f) {
13447                 float   chance;
13448
13449                 nprintf(("AI", "%6.4f: ", frametime));
13450                 for (chance=0.25f; chance<2.5f; chance += 0.25f) {
13451                         int count = 0;
13452
13453                         for (i=0; i<100.0f/frametime; i++) {
13454                                 if (rand_chance(frametime, chance))
13455                                         count++;
13456                         }
13457                         nprintf(("AI", "%3i ", count));
13458                 }
13459                 nprintf(("AI", "\n"));
13460         }
13461 }
13462 */
13463
13464 //      --------------------------------------------------------------------------
13465 //      Make object *objp avoid the nearest dangerous shockwave-producing weapon.
13466 //      If it looks like there is no valid shockwave-producing weapon then clear the AIF_AVOID_SHOCKWAVE_WEAPON bit in ai_flags and return.
13467 //      Return 1 if avoiding a shockwave, else return 0.
13468 int ai_avoid_shockwave(object *objp, ai_info *aip)
13469 {
13470         vector  safe_pos;
13471
13472         //rand_chance_test();
13473         // BIG|HUGE do not respond to shockwaves
13474         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
13475                 // don't come here again
13476                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE;
13477                 return 0;
13478         }
13479
13480         //      Don't all react right away.
13481         if (!(aip->ai_flags & AIF_AVOID_SHOCKWAVE_STARTED))
13482                 if (!rand_chance(flFrametime, (float) aip->ai_class/4.0f + 0.25f))      //      Chance to avoid in 1 second is 0.25 + ai_class/4
13483                         return 0;
13484
13485         if (!aas_1(objp, aip, &safe_pos)) {
13486                 aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13487                 return 0;
13488         }
13489
13490         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13491
13492         //      OK, evade the shockwave!
13493         turn_towards_point(objp, &safe_pos, NULL, 0.0f);
13494         vector  vec_to_safe_pos;
13495         float           dot_to_goal;
13496
13497         vm_vec_normalized_dir(&vec_to_safe_pos, &safe_pos, &objp->pos);
13498
13499         dot_to_goal = vm_vec_dot(&objp->orient.v.fvec, &vec_to_safe_pos);
13500         if (dot_to_goal < -0.5f)
13501                 accelerate_ship(aip, 0.3f);
13502         else {
13503                 accelerate_ship(aip, 1.0f + dot_to_goal);
13504                 if (dot_to_goal > 0.2f) {
13505                         if (!(objp->phys_info.flags & PF_AFTERBURNER_ON )) {
13506                                 afterburners_start(objp);
13507                                 aip->afterburner_stop_time = Missiontime + 2*F1_0;
13508                         }
13509                 }
13510         }
13511
13512         return 1;
13513 }
13514
13515 //      Awaiting repair.  Be useful.
13516 //      Probably fly towards incoming repair ship.
13517 //      Return true if this ship is close to being repaired, else return false.
13518 int ai_await_repair_frame(object *objp, ai_info *aip)
13519 {
13520         if (!(aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)))
13521                 return 0;
13522
13523         if (aip->dock_objnum == -1)
13524                 return 0;
13525
13526         ship    *shipp;
13527         ship_info       *sip;
13528
13529         shipp = &Ships[Objects[aip->dock_objnum].instance];
13530         sip = &Ship_info[shipp->ship_info_index];
13531
13532         aip->ai_flags &= ~AIF_FORMATION_OBJECT; //      Prevents endless rotation.
13533
13534         if (!(sip->flags & SIF_SUPPORT))
13535                 return 0;
13536
13537         vector  goal_point;
13538         object  *repair_objp;
13539
13540         repair_objp = &Objects[aip->dock_objnum];
13541
13542         if (Ships[repair_objp->instance].team == TEAM_TRAITOR) {
13543                 ai_abort_rearm_request(repair_objp);
13544                 return 0;
13545         }
13546
13547         vm_vec_scale_add(&goal_point, &repair_objp->pos, &repair_objp->orient.v.uvec, -50.0f);  //      Fly towards point below repair ship.
13548
13549         vector  vtr;
13550         float dist = vm_vec_normalized_dir(&vtr, &goal_point, &objp->pos);
13551         float dot = vm_vec_dot(&vtr, &objp->orient.v.fvec);
13552
13553         if (dist > 200.0f) {
13554                 //nprintf(("AI", "%s flying towards %s for repair, dist = %7.3f\n", Ships[objp->instance].ship_name, &Ships[repair_objp->instance].ship_name, dist));
13555                 accelerate_ship(aip, (0.9f + dot) * dist/1500.0f);
13556                 turn_towards_point(objp, &goal_point, NULL, 0.0f);
13557         } else {
13558                 accelerate_ship(aip, 0.0f);
13559                 //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));
13560         }
13561
13562         return 1;
13563 }
13564
13565 //      Maybe cause this ship to self-destruct.
13566 //      Currently, any small ship (SIF_SMALL_SHIP) that has been disabled will self-destruct after awhile.
13567 //      Maybe should only do this if they are preventing their wing from re-entering.
13568 void ai_maybe_self_destruct(object *objp, ai_info *aip)
13569 {
13570         //      Friendly ships can be repaired, so no self-destruct.
13571         //      In multiplayer, just don't self-destruct.  I figured there would be a problem. -- MK, 3/19/98.
13572         if ((Ships[objp->instance].team == TEAM_FRIENDLY) || (Game_mode & GM_MULTIPLAYER))
13573                 return;
13574
13575         //      Small ships in a wing blow themselves up after awhile if engine or weapons system has been destroyed.
13576         //      Reason: Don't want them to prevent a re-emergence of the wing.
13577         //      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
13578         //      mission would be broken.
13579         if ((Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP) && (Ships[objp->instance].wingnum != -1)) {
13580                 if ((ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) ||
13581                         (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_WEAPONS) <= 0.0f)) {
13582                         if (aip->self_destruct_timestamp < 0)
13583                                 aip->self_destruct_timestamp = timestamp(90 * 1000);    //      seconds until self-destruct
13584                 } else {
13585                         aip->self_destruct_timestamp = -1;
13586                 }
13587
13588                 if (aip->self_destruct_timestamp < 0) {
13589                         return;
13590                 }
13591
13592                 if (timestamp_elapsed(aip->self_destruct_timestamp)) {
13593                         ship_apply_local_damage( objp, objp, &objp->pos, objp->hull_strength*flFrametime + 1.0f, MISS_SHIELDS);
13594                 }
13595         }
13596 }
13597
13598 // Determine if pl_objp needs a new target, called from ai_frame()
13599 int ai_need_new_target(object *pl_objp, int target_objnum)
13600 {
13601         object *objp;
13602
13603         if ( target_objnum < 0 ) {
13604                 return 1;
13605         }
13606
13607         objp = &Objects[target_objnum];
13608
13609         if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID) && (objp->type != OBJ_WEAPON) ) {
13610                 return 1;
13611         }
13612
13613         if ( objp->type == OBJ_SHIP ) {
13614                 if ( Ships[objp->instance].flags & SF_DYING ) {
13615                         return 1;
13616                 } else if (Ships[objp->instance].team == Ships[pl_objp->instance].team)
13617                         return 1;
13618         }
13619
13620         return 0;
13621 }
13622
13623 //      If *objp is recovering from a collision with a big ship, handle it.
13624 //      Return true if recovering.
13625 int maybe_big_ship_collide_recover_frame(object *objp, ai_info *aip)
13626 {
13627         float   dot, dist;
13628         vector  v2g;
13629         
13630         if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1) {
13631                 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);
13632                 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_1, &objp->pos);
13633                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
13634                 accelerate_ship(aip, dot);
13635
13636                 //      If close to desired point, or 15+ seconds since entered this mode, continue to next mode.
13637                 if ((timestamp_until(aip->big_recover_timestamp) < -15*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13638                         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_1;
13639                         aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13640                 }
13641
13642                 return 1;
13643
13644         } else if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_2) {
13645                 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);
13646                 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_2, &objp->pos);
13647                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
13648                 accelerate_ship(aip, dot);
13649
13650                 //      If close to desired point, or 30+ seconds since started avoiding collision, done avoiding.
13651                 if ((timestamp_until(aip->big_recover_timestamp) < -30*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13652                         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13653                         aip->ai_flags &= ~AIF_TARGET_COLLISION;
13654                 }
13655
13656                 return 1;
13657         }
13658
13659         if (aip->ai_flags & AIF_TARGET_COLLISION) {
13660                 aip->ai_flags &= ~AIF_TARGET_COLLISION;
13661         }
13662         return 0;
13663 }
13664
13665 void validate_mode_submode(ai_info *aip)
13666 {
13667         switch (aip->mode) {
13668         case AIM_CHASE:
13669                 // check valid submode
13670                 switch (aip->submode) {
13671                 case SM_CONTINUOUS_TURN:
13672                 case SM_ATTACK:
13673                 case SM_EVADE_SQUIGGLE:
13674                 case SM_EVADE_BRAKE:    
13675                 case SM_EVADE:          
13676                 case SM_SUPER_ATTACK:
13677                 case SM_AVOID:  
13678                 case SM_GET_BEHIND:
13679                 case SM_GET_AWAY:               
13680                 case SM_EVADE_WEAPON:
13681                 case SM_FLY_AWAY:       
13682                 case SM_ATTACK_FOREVER:
13683                         break;
13684                 default:
13685                         Int3();
13686                 }
13687                 break;
13688
13689         case AIM_STRAFE:
13690                 // check valid submode
13691                 switch(aip->submode) {
13692                 case AIS_STRAFE_ATTACK:
13693                 case AIS_STRAFE_AVOID:
13694                 case AIS_STRAFE_RETREAT1:
13695                 case AIS_STRAFE_RETREAT2:
13696                 case AIS_STRAFE_POSITION:
13697                         break;
13698                 default:
13699                         Int3();
13700                 }
13701                 break;
13702         }
13703 }
13704
13705 //      --------------------------------------------------------------------------
13706 // Process AI object "objnum".
13707 void ai_frame(int objnum)
13708 {
13709         ship            *shipp = &Ships[Objects[objnum].instance];
13710         ai_info *aip = &Ai_info[shipp->ai_index];
13711         int             target_objnum;
13712
13713 //      validate_mode_submode(aip);
13714
13715         Assert((aip->mode != AIM_WAYPOINTS) || (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC));
13716
13717         // Set globals defining the current object and its enemy object.
13718         Pl_objp = &Objects[objnum];
13719
13720         if (aip->mode == AIM_WARP_OUT) {
13721                 ai_warp_out(Pl_objp);
13722                 return;
13723         }
13724
13725 /*      //      HACK! TEST! REMOVE ME!
13726         if (Ship_info[shipp->ship_info_index].flags & SIF_BIG_SHIP)
13727                 if (shipp->team == Player_ship->team)
13728                         aip->mode = AIM_CHASE;
13729 */
13730
13731 //      if (!strnicmp(Ships[Pl_objp->instance].ship_name, "cancer", 6))
13732 //              nprintf(("AI", "Ship %s: mode = %s, submode = %i\n", Ships[Pl_objp->instance].ship_name, Mode_text[aip->mode], aip->submode));
13733
13734         ai_maybe_self_destruct(Pl_objp, aip);
13735
13736 //      if ( timestamp_elapsed(aip->goal_check_time) ) {
13737                 ai_process_mission_orders( objnum, aip );
13738 //              aip->goal_check_time = timestamp_rand(1000,2000);
13739 //      }
13740
13741         //      Avoid a shockwave, if necessary.  If a shockwave and rearming, stop rearming.
13742         if (aip->ai_flags & AIF_AVOID_SHOCKWAVE) {
13743                 if (ai_avoid_shockwave(Pl_objp, aip)) {
13744                         aip->ai_flags &= ~(AIF_BIG_SHIP_COLLIDE_RECOVER_1 | AIF_BIG_SHIP_COLLIDE_RECOVER_2);
13745                         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
13746                                 ai_abort_rearm_request(Pl_objp);
13747                         return;
13748                 }
13749         } else {
13750                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_STARTED;
13751         }
13752
13753         // moved call to ai_do_repair frame here from below because of the subsequent if statment returning
13754         // if the ship is getting repaired
13755         //      If waiting to be repaired, just stop and sit.
13756         ai_do_repair_frame(Pl_objp, aip, flFrametime);
13757         if ((aip->ai_flags & AIF_AWAITING_REPAIR) || (aip->ai_flags & AIF_BEING_REPAIRED)) {
13758                 if (ai_await_repair_frame(Pl_objp, aip))
13759                         return;
13760         }
13761
13762         if (aip->mode == AIM_PLAY_DEAD)
13763                 return;
13764
13765         //      If recovering from a collision with a big ship, don't continue.
13766         if (maybe_big_ship_collide_recover_frame(Pl_objp, aip))
13767                 return;
13768
13769         ai_preprocess_ignore_objnum(Pl_objp, aip);
13770         target_objnum = set_target_objnum(aip, aip->target_objnum);
13771
13772         // nprintf(("AI", "Frame %i: Coords = %7.3f %7.3f %7.3f\n", AI_FrameCount, Pl_objp->pos.xyz.x, Pl_objp->pos.xyz.y, Pl_objp->pos.xyz.z));
13773
13774         Assert(objnum != target_objnum);
13775
13776         ai_manage_shield(Pl_objp, aip);
13777         
13778         if ( maybe_request_support(Pl_objp) ) {
13779                 if ( Ships[Pl_objp->instance].flags & SF_FROM_PLAYER_WING ) {
13780                         ship_maybe_tell_about_rearm(shipp);
13781                 }
13782         }
13783
13784         ai_maybe_warp_out(Pl_objp);
13785
13786 /*
13787         //      If this ship is attacking an object's subsystems and someone else destroyed
13788         //      the subsystem, it could continue attacking the ship.  Need to invalidate the objnum.
13789         if (target_objnum >= 0)
13790                 if (Objects[target_objnum].flags & OF_PROTECTED) {
13791                         // if (aip->targeted_subsys != NULL)
13792                         //      ; //nprintf(("AI", "subsys hits = %7.3f\n", aip->targeted_subsys->current_hits));
13793
13794                         if ((aip->targeted_subsys == NULL) || (aip->targeted_subsys->current_hits <= 0.0f)) {
13795                                 target_objnum = -1;
13796                                 aip->target_objnum = -1;
13797                         }
13798                 }
13799 */
13800
13801
13802         //      Find an enemy if don't already have one.
13803         En_objp = NULL;
13804         if ( ai_need_new_target(Pl_objp, target_objnum) ) {
13805                 if ((aip->mode != AIM_EVADE_WEAPON) && (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
13806                         aip->resume_goal_time = -1;
13807                         aip->active_goal = AI_GOAL_NONE;
13808                 } else if (aip->resume_goal_time == -1) {
13809                         // AL 12-9-97: Don't allow cargo and navbuoys to set their aip->target_objnum
13810                         if ( !(Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS) ) {
13811                                 target_objnum = find_enemy(objnum, MAX_ENEMY_DISTANCE, Skill_level_max_attackers[Game_skill_level]);            //      Attack up to 25K units away.
13812                                 if (target_objnum != -1) {
13813                                         if (aip->target_objnum != target_objnum)
13814                                                 aip->aspect_locked_time = 0.0f;
13815                                         set_target_objnum(aip, target_objnum);
13816                                         En_objp = &Objects[target_objnum];
13817                                 }
13818                         }
13819                 }
13820         } else if (target_objnum >= 0) {
13821                 En_objp = &Objects[target_objnum];
13822         }
13823
13824         // set base stealth info each frame
13825         aip->ai_flags &= ~AIF_STEALTH_PURSIUT;
13826         if (En_objp && En_objp->type == OBJ_SHIP) {
13827                 if (Ship_info[Ships[En_objp->instance].ship_info_index].flags & SIF_STEALTH) {
13828                         int stealth_state = ai_is_stealth_visible(Pl_objp, En_objp);
13829                         float dist = vm_vec_dist_quick(&En_objp->pos, &Pl_objp->pos);
13830
13831                         if (stealth_state != STEALTH_FULLY_TARGETABLE) {
13832                                 aip->ai_flags |= AIF_STEALTH_PURSIUT;
13833                         }
13834
13835                         if ( (stealth_state == STEALTH_FULLY_TARGETABLE) || (stealth_state == STEALTH_VISIBLE) ) {
13836                                 aip->stealth_last_visible_stamp = timestamp();
13837                                 aip->stealth_last_cheat_visible_stamp = aip->stealth_last_visible_stamp;
13838                                 aip->stealth_last_pos = En_objp->pos;
13839                                 aip->stealth_velocity = En_objp->phys_info.vel;
13840                         } else if (dist < 100) {
13841                                 // get cheat timestamp
13842                                 aip->stealth_last_cheat_visible_stamp = timestamp();
13843
13844                                 // set approximate pos and vel, with increasing error as time from last_visible_stamp increases
13845                                 update_ai_stealth_info_with_error(aip/*, 0*/);
13846                         }
13847                 }
13848         }
13849
13850         /*      if ((Pl_objp != NULL) && (En_objp != NULL)) {
13851                 slide_face_ship();
13852                 return;
13853         }
13854 */
13855         // AL 12-10-97: ensure that cargo and navbuoys aip->target_objnum is always -1.
13856         if ( Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS ) {
13857                 aip->target_objnum = -1;
13858         }
13859
13860         if ((En_objp != NULL) && (En_objp->pos.xyz.x == Pl_objp->pos.xyz.x) && (En_objp->pos.xyz.y == Pl_objp->pos.xyz.y) && (En_objp->pos.xyz.z == Pl_objp->pos.xyz.z)) {
13861                 mprintf(("Warning: Object and its enemy have same position.  Object #%i\n", Pl_objp-Objects));
13862                 En_objp = NULL;
13863         }
13864
13865         if (aip->mode == AIM_CHASE) {
13866                 if (En_objp == NULL) {
13867                         aip->active_goal = -1;
13868                 }
13869         }
13870
13871         //      If there is a goal to resume and enough time has elapsed, resume the goal.
13872         if ((aip->resume_goal_time > 0) && (aip->resume_goal_time < Missiontime)) {
13873                 aip->active_goal = AI_GOAL_NONE;
13874                 aip->resume_goal_time = -1;
13875                 target_objnum = find_enemy(objnum, 2000.0f, Skill_level_max_attackers[Game_skill_level]);
13876                 if (target_objnum != -1) {
13877                         if (aip->target_objnum != target_objnum) {
13878                                 aip->aspect_locked_time = 0.0f;
13879                         }
13880                         set_target_objnum(aip, target_objnum);
13881                 }
13882         }
13883
13884         // check if targeted subsystem has been destroyed, if so, move onto another subsystem
13885         // if trying to disable or disarm the target
13886         if ((En_objp != NULL) && ( aip->targeted_subsys != NULL )) {
13887                 Assert(En_objp->type == OBJ_SHIP);
13888                 if ( aip->targeted_subsys->current_hits <= 0.0f ) {
13889                         int subsys_type;
13890
13891                         if ( aip->goals[0].ai_mode == AI_GOAL_DISABLE_SHIP ) {
13892                                 subsys_type = SUBSYSTEM_ENGINE;
13893                         } else if ( aip->goals[0].ai_mode == AI_GOAL_DISARM_SHIP ) {
13894                                 subsys_type = SUBSYSTEM_TURRET;
13895                         } else {
13896                                 subsys_type = -1;
13897                         }
13898
13899                         if ( subsys_type != -1 ) {
13900                                 ship_subsys *new_subsys;
13901                                 new_subsys = ship_return_next_subsys(&Ships[En_objp->instance], subsys_type, &Pl_objp->pos);
13902                                 if ( new_subsys != NULL ) {
13903                                         set_targeted_subsys(aip, new_subsys, aip->target_objnum);
13904                                 } else {
13905                                         // AL 12-16-97: no more subsystems to attack... reset targeting info
13906                                         aip->target_objnum = -1;
13907                                         set_targeted_subsys(aip, NULL, -1);
13908                                 }
13909                         } else {
13910                                 // targeted subsys is destroyed, so stop attacking it
13911                                 set_targeted_subsys(aip, NULL, -1);
13912                         }
13913                 }
13914         }
13915
13916         ai_maybe_launch_cmeasure(Pl_objp, aip);
13917         ai_maybe_evade_locked_missile(Pl_objp, aip);
13918
13919         aip->target_time += flFrametime;
13920
13921         int in_formation = 0;
13922         if (aip->ai_flags & AIF_FORMATION) {
13923                 in_formation = !ai_formation();
13924         }
13925
13926         if ( !in_formation ) {
13927                 ai_execute_behavior(aip);
13928         }
13929
13930         process_subobjects(objnum);
13931         maybe_resume_previous_mode(Pl_objp, aip);
13932         
13933         if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON ) {
13934                 if (Missiontime > aip->afterburner_stop_time) {
13935                         //nprintf(("AI", "Frame %i, turning off afterburner.\n", AI_FrameCount));
13936                         afterburners_stop(Pl_objp);
13937                 }
13938         }
13939 //      validate_mode_submode(aip);
13940 }
13941
13942 int Waypoints_created = 0;
13943
13944 //      Find the ship with the name *name in the Ship_info array.
13945 int find_ship_name(char *name)
13946 {
13947         int     i;
13948
13949         for (i=0; i<Num_ship_types; i++)
13950                 if (!strcmp(Ship_info[i].name, name))
13951                         return i;
13952
13953         return -1;
13954 }
13955
13956 void create_waypoints()
13957 {
13958         int     i, j, z;
13959
13960         // Waypoints_created = 1;
13961
13962         if (Waypoints_created)
13963                 return;
13964
13965         for (j=0; j<Num_waypoint_lists; j++)
13966                 for (i=0; i<Waypoint_lists[j].count; i++) {
13967                         z = obj_create(OBJ_WAYPOINT, 0, j * 65536 + i, NULL,
13968                                 &Waypoint_lists[j].waypoints[i], 0.0f, OF_RENDERS);
13969                 }
13970
13971         Waypoints_created = 1;
13972 }
13973
13974 int Last_ai_obj = -1;
13975
13976 void ai_process( object * obj, int ai_index, float frametime )
13977 {
13978 //      if (Ships[obj->instance].flags & SF_DYING)
13979 //              nprintf(("AI", "Frame: %i Ship %s is dying!\n", Framecount, Ships[obj->instance].ship_name));
13980
13981         if (obj->flags & OF_SHOULD_BE_DEAD)
13982                 return;
13983
13984         // 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.
13985         if ((Ships[obj->instance].flags & SF_DYING ) && !(Ship_info[Ships[obj->instance].ship_info_index].flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP))){
13986                 return;
13987         }
13988
13989         int rfc = 1;            //      Assume will be Reading Flying Controls.
13990
13991         Assert( obj->type == OBJ_SHIP );
13992         Assert( ai_index >= 0 );
13993
13994         init_ship_info();
13995
13996         create_waypoints();
13997
13998         AI_frametime = frametime;
13999         if (obj-Objects <= Last_ai_obj) {
14000                 AI_FrameCount++;
14001         }
14002
14003         memset( &AI_ci, 0, sizeof(AI_ci) );
14004
14005         ai_frame(obj-Objects);
14006
14007         AI_ci.pitch = 0.0f;
14008         AI_ci.bank = 0.0f;
14009         AI_ci.heading = 0.0f;
14010
14011         // the ships maximum velocity now depends on the energy flowing to engines
14012         obj->phys_info.max_vel.xyz.z = Ships[obj->instance].current_max_speed;
14013         ai_info *aip = &Ai_info[Ships[obj->instance].ai_index];
14014
14015         //      In certain circumstances, the AI says don't fly in the normal way.
14016         //      One circumstance is in docking and undocking, when the ship is moving
14017         //      under thruster control.
14018         switch (aip->mode) {
14019         case AIM_DOCK:
14020                 if ((aip->submode >= AIS_DOCK_2) && (aip->submode != AIS_UNDOCK_3))
14021                         rfc = 0;
14022                 break;
14023         case AIM_WARP_OUT:
14024                 if (aip->submode >= AIS_WARP_3)
14025                         rfc = 0;
14026                 break;
14027 //      case AIM_NONE:
14028 //              if (aip->submode == AIS_NONE_FORMATION)
14029 //                      rfc = 0;
14030 //              break;
14031         default:
14032                 break;
14033         }
14034
14035         if (rfc == 1) {
14036                 vector copy_desired_rotvel = obj->phys_info.rotvel;
14037                 physics_read_flying_controls( &obj->orient, &obj->phys_info, &AI_ci, frametime);
14038                 // if obj is in formation and not flight leader, don't update rotvel
14039                 if (aip->ai_flags & AIF_FORMATION) {
14040                         if (&Objects[aip->goal_objnum] != obj) {
14041                                 obj->phys_info.desired_rotvel = copy_desired_rotvel;
14042                                 obj->phys_info.rotvel = copy_desired_rotvel;
14043                         }
14044                 }
14045         }
14046
14047         Last_ai_obj = obj-Objects;
14048 }
14049
14050 //      Initialize ai_info struct of object objnum.
14051 void init_ai_object(int objnum)
14052 {
14053         int     ship_index, ai_index;
14054         ai_info *aip;
14055         int ship_type;
14056         object  *objp;
14057         vector  near_vec;                       //      A vector nearby and mainly in front of this object.
14058
14059         objp = &Objects[objnum];
14060         ship_index = objp->instance;
14061         ai_index = Ships[ship_index].ai_index;
14062         Assert((ai_index >= 0) && (ai_index < MAX_AI_INFO));
14063
14064         aip = &Ai_info[ai_index];
14065
14066         ship_type = Ships[ship_index].ship_info_index;
14067
14068         vm_vec_scale_add(&near_vec, &objp->pos, &objp->orient.v.fvec, 100.0f);
14069         vm_vec_scale_add2(&near_vec, &objp->orient.v.rvec, 10.0f);
14070
14071         // Things that shouldn't have to get initialized, but initialize them just in case!
14072         aip->ai_flags = 0;
14073         aip->previous_mode = AIM_NONE;
14074         aip->mode_time = -1;
14075         aip->target_objnum = -1;
14076         aip->target_signature = -1;
14077         aip->previous_target_objnum = -1;
14078         aip->target_time = 0.0f;
14079         aip->enemy_wing = -1;
14080         aip->attacker_objnum = -1;
14081         aip->goal_objnum = -1;
14082         aip->goal_signature = -1;
14083         aip->guard_objnum = -1;
14084         aip->guard_signature = -1;
14085         aip->guard_wingnum = -1;
14086         aip->dock_signature = -1;
14087         aip->submode = 0;
14088         aip->previous_submode = 0;
14089         aip->best_dot_to_enemy = -1.0f;
14090         aip->best_dot_from_enemy = -1.0f;
14091         aip->best_dot_to_time = 0;
14092         aip->best_dot_from_time = 0;
14093         aip->submode_start_time = 0;
14094         aip->submode_parm0 = 0;
14095         aip->active_goal = -1;
14096         aip->goal_check_time = timestamp(0);
14097         aip->last_predicted_enemy_pos = near_vec;
14098         aip->prev_goal_point = near_vec;
14099         aip->goal_point = near_vec;
14100         aip->time_enemy_in_range = 0.0f;
14101         aip->last_attack_time = 0;
14102         aip->last_hit_time = 0;
14103         aip->last_hit_quadrant = 0;
14104         aip->hitter_objnum = -1;
14105         aip->hitter_signature = -1;
14106         aip->resume_goal_time = -1;
14107         aip->prev_accel = 0.0f;
14108         aip->prev_dot_to_goal = 0.0f;
14109
14110         aip->ignore_objnum = UNUSED_OBJNUM;
14111         aip->ignore_signature = -1;
14112
14113         // aip->mode = AIM_NONE;
14114
14115         // End of Things that shouldn't have to get initialized, but initialize them just in case!
14116
14117         aip->ai_courage = Ai_classes[Ship_info[ship_type].ai_class].ai_courage[Game_skill_level];
14118         aip->ai_patience = Ai_classes[Ship_info[ship_type].ai_class].ai_patience[Game_skill_level];
14119         aip->ai_evasion = Ai_classes[Ship_info[ship_type].ai_class].ai_evasion[Game_skill_level];
14120         aip->ai_accuracy = Ai_classes[Ship_info[ship_type].ai_class].ai_accuracy[Game_skill_level];
14121
14122         if (Num_waypoint_lists > 0) {
14123                 aip->wp_index = -1;
14124                 aip->wp_list = -1;
14125         } else {
14126                 aip->wp_index = -1;
14127                 aip->wp_list = -1;
14128         }
14129
14130         aip->attacker_objnum = -1;
14131         aip->goal_signature = -1;
14132
14133         Objects[objnum].phys_info.prev_fvec = Objects[objnum].orient.v.fvec;
14134
14135         aip->last_predicted_enemy_pos.xyz.x = 0.0f;     //      Says this value needs to be recomputed!
14136         aip->time_enemy_in_range = 0.0f;
14137
14138         aip->resume_goal_time = -1;                                     //      Say there is no goal to resume.
14139
14140         aip->active_goal = -1;
14141         aip->path_start = -1;
14142         aip->path_goal_dist = -1;
14143         aip->path_length = 0;
14144         aip->path_subsystem_next_check = 1;
14145         aip->dock_path_index = -1;
14146         aip->dock_index = -1;
14147         aip->dock_objnum = -1;
14148
14149         aip->danger_weapon_objnum = -1;
14150         aip->danger_weapon_signature = -1;
14151
14152         aip->lead_scale = 0.0f;
14153         aip->last_hit_target_time = Missiontime;
14154
14155         aip->nearest_locked_object = -1;
14156         aip->nearest_locked_distance = 99999.0f;
14157
14158         aip->targeted_subsys = NULL;
14159         aip->last_subsys_target = NULL;
14160         aip->targeted_subsys_parent = -1;
14161
14162         // The next two fields are used to time the rearming to allow useful sound effects for missile rearming
14163         aip->rearm_first_missile = TRUE;                //      flag to indicate that next missile to load is the first missile
14164         aip->rearm_release_delay = 0;                   //      timestamp to delay the separation of docked ships after rearm
14165
14166         aip->next_predict_pos_time = 0;
14167
14168         aip->afterburner_stop_time = 0;
14169         aip->last_objsig_hit = -1;                              // object signature of the ship most recently hit by aip
14170
14171         aip->path_next_create_time = timestamp(1);
14172         aip->path_create_pos = Objects[objnum].pos;
14173         aip->path_create_orient = Objects[objnum].orient;
14174
14175         aip->ignore_expire_timestamp = timestamp(1);
14176         aip->warp_out_timestamp = 0;
14177         aip->next_rearm_request_timestamp = timestamp(1);
14178         aip->primary_select_timestamp = timestamp(1);
14179         aip->secondary_select_timestamp = timestamp(1);
14180         aip->scan_for_enemy_timestamp = timestamp(1);
14181
14182         aip->choose_enemy_timestamp = timestamp(3*(NUM_SKILL_LEVELS-Game_skill_level) * ((rand_alt() % 500) + 500));
14183
14184         aip->shockwave_object = -1;
14185         aip->shield_manage_timestamp = timestamp(1);
14186         aip->self_destruct_timestamp = -1;      //      This is a flag that we have not yet set this.
14187         aip->ok_to_target_timestamp = timestamp(1);
14188         aip->pick_big_attack_point_timestamp = timestamp(1);
14189         vm_vec_zero(&aip->big_attack_point);
14190
14191         aip->avoid_check_timestamp = timestamp(1);
14192
14193         aip->abort_rearm_timestamp = -1;
14194
14195         // artillery stuff
14196         aip->artillery_objnum = -1;
14197         aip->artillery_sig = -1;        
14198
14199         // waypoint speed cap
14200         aip->waypoint_speed_cap = -1;
14201
14202         // set lethality to enemy team
14203         aip->lethality = 0.0f;
14204 }
14205
14206 void init_ai_objects()
14207 {
14208         int     i;
14209
14210         for (i=0; i<num_objects; i++){
14211                 if (Objects[i].type == OBJ_SHIP){
14212                         init_ai_object(i);
14213                 }
14214         }
14215 }
14216
14217 void init_ai_system()
14218 {
14219         // MWA -- removed next line of code on 11/12/97.  When a ship is created
14220         // it calls init_ai_object() on it's objnum.  Doing this init at the point where
14221         // this function gets called messes things up.
14222         //init_ai_objects();
14223
14224         Ppfp = Path_points;
14225         Waypoints_created = 0;
14226
14227         Dock_path_warning_given = 0;
14228
14229 /*      for (int i=0; i<MAX_IGNORE_OBJECTS; i++) {
14230                 Ignore_objects[i].objnum = -1;
14231                 Ignore_objects[i].signature = -1;
14232         }
14233 */
14234
14235 }
14236
14237 void ai_set_default_behavior(object *obj, int classnum)
14238 {
14239         ai_info *aip;
14240
14241         Assert(obj != NULL);
14242         Assert(obj->instance != -1);
14243         Assert(Ships[obj->instance].ai_index != -1);
14244
14245         aip = &Ai_info[Ships[obj->instance].ai_index];
14246
14247         aip->behavior = classnum;
14248
14249 }
14250
14251 void ai_do_default_behavior(object *obj)
14252 {
14253         ai_info *aip;
14254         int             ship_flags;
14255
14256         Assert(obj != NULL);
14257         Assert(obj->instance != -1);
14258         Assert(Ships[obj->instance].ai_index != -1);
14259
14260         aip = &Ai_info[Ships[obj->instance].ai_index];
14261
14262         ship_flags = Ship_info[Ships[obj->instance].ship_info_index].flags;
14263         if (!is_instructor(obj) && (ship_flags & (SIF_FIGHTER | SIF_BOMBER))) {
14264                 int enemy_objnum = find_enemy(OBJ_INDEX(obj), 1000.0f, Skill_level_max_attackers[Game_skill_level]);
14265                 set_target_objnum(aip, enemy_objnum);
14266                 aip->mode = AIM_CHASE;
14267                 aip->submode = SM_ATTACK;
14268         } else if (ship_flags & (SIF_SUPPORT)) {
14269                 aip->mode = AIM_SAFETY;
14270                 aip->submode = AISS_1;
14271                 aip->ai_flags &= ~(AIF_REPAIRING);
14272         } else if ( ship_flags & SIF_SENTRYGUN ) {
14273                 aip->mode = AIM_SENTRYGUN;
14274         } else {
14275                 aip->mode = AIM_NONE;
14276         }
14277         
14278         aip->submode_start_time = Missiontime;
14279         aip->active_goal = AI_GOAL_NONE;
14280 }
14281
14282 #define FRIENDLY_DAMAGE_THRESHOLD       50.0f           //      Display a message at this threshold.  Note, this gets scaled by Skill_level
14283
14284 // send the given message from objp.  called from the maybe_process_friendly_hit
14285 // code below when a message must get send to the player when he fires on friendlies
14286 void process_friendly_hit_message( int message, object *objp )
14287 {
14288         int index;
14289
14290         // no traitor in multiplayer
14291         if(Game_mode & GM_MULTIPLAYER){
14292                 return;
14293         }
14294
14295         // don't send this message if a player ship was hit.
14296         if ( objp->flags & OF_PLAYER_SHIP ){
14297                 return;
14298         }
14299
14300         // check if objp is a cargo contianer -- if so, then find a new ship to send the message
14301         index = objp->instance;
14302         if ( !(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER|SIF_BOMBER)) ){
14303                 index = -1;
14304         }
14305
14306         // if the message is "oops" (the don't hit me message), always make come from Terran command
14307         if ( message == MESSAGE_OOPS ){
14308                 index = -1;
14309         }
14310
14311         if ( index >= 0){
14312                 message_send_builtin_to_player( message, &Ships[index], MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14313         } else {
14314                 message_send_builtin_to_player( message, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14315         }
14316 }
14317
14318 extern  void ship_set_subsystem_strength( ship *shipp, int type, float strength );
14319
14320 //      Object *objp_weapon, fired by *objp_hitter, hit object *objp_ship.
14321 void maybe_process_friendly_hit(object *objp_hitter, object *objp_hit, object *objp_weapon)
14322 {
14323         // no turning traitor in multiplayer
14324         if ( Game_mode & GM_MULTIPLAYER ) {
14325                 return;
14326         }
14327
14328         // ditto if mission says no traitors allowed
14329         if (The_mission.flags & MISSION_FLAG_NO_TRAITOR) {
14330                 return;
14331         }
14332
14333         if ((objp_hitter == Player_obj) && (Player_ship->team == TEAM_FRIENDLY)) {
14334
14335                 // AL 12-4-97: It is possible the Player is a OBJ_GHOST at this point.  If so, bail out.
14336                 if ( objp_hitter->type != OBJ_SHIP ) {
14337                         return;
14338                 }
14339
14340                 Assert(objp_hitter->type == OBJ_SHIP);
14341                 Assert(objp_hit->type == OBJ_SHIP);
14342                 Assert(objp_weapon->type == OBJ_WEAPON);
14343
14344                 ship    *shipp_hitter = &Ships[objp_hitter->instance];
14345                 ship    *shipp_hit = &Ships[objp_hit->instance];
14346
14347                 if (shipp_hitter->team != shipp_hit->team) {
14348                         return;
14349                 }
14350
14351                 // get the player
14352                 player *pp = &Players[Player_num];
14353
14354                 // wacky stuff here
14355                 if (pp->friendly_hits != 0) {
14356                         float   time_since_last_hit = f2fl(Missiontime - pp->friendly_last_hit_time);
14357                         if ((time_since_last_hit >= 0.0f) && (time_since_last_hit < 10000.0f)) {
14358                                 if (time_since_last_hit > 60.0f) {
14359                                         pp->friendly_hits = 0;
14360                                         pp->friendly_damage = 0.0f;
14361                                 } else if (time_since_last_hit > 2.0f) {
14362                                         pp->friendly_hits -= (int) time_since_last_hit/2;
14363                                         pp->friendly_damage -= time_since_last_hit;
14364                                 }
14365
14366                                 if (pp->friendly_damage < 0.0f) {
14367                                         pp->friendly_damage = 0.0f;
14368                                 }
14369
14370                                 if (pp->friendly_hits < 0) {
14371                                         pp->friendly_hits = 0;
14372                                 }
14373                         }
14374                 }
14375
14376                 float   damage;         //      Damage done by weapon.  Gets scaled down based on size of ship.
14377
14378                 damage = Weapon_info[Weapons[objp_weapon->instance].weapon_info_index].damage;
14379                 
14380                 // wacky stuff here
14381                 ship_info *sip = &Ship_info[Ships[objp_hit->instance].ship_info_index];
14382                 if (sip->initial_hull_strength > 1000.0f) {
14383                         float factor = sip->initial_hull_strength / 1000.0f;
14384                         factor = min(100.0f, factor);
14385                         damage /= factor;
14386                 }
14387
14388                 //      Don't penalize much at all for hitting cargo
14389                 if (sip->flags & (SIF_CARGO | SIF_SENTRYGUN)) {
14390                         damage /= 10.0f;
14391                 }
14392
14393                 //      Hit ship, but not targeting it, so it's not so heinous, maybe an accident.
14394                 if (Ai_info[shipp_hitter->ai_index].target_objnum != OBJ_INDEX(objp_hit)) {
14395                         damage /= 5.0f;
14396                 }
14397
14398                 pp->friendly_last_hit_time = Missiontime;
14399                 pp->friendly_hits++;
14400
14401                 // cap damage and number of hits done this frame
14402                 float accredited_damage = min(MAX_BURST_DAMAGE, pp->damage_this_burst + damage) - pp->damage_this_burst;
14403                 pp->friendly_damage += accredited_damage;
14404                 pp->damage_this_burst += accredited_damage;
14405
14406                 // Done with adjustments to damage.  Evaluate based on current friendly_damage
14407                 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 ));
14408                 
14409                 if (is_instructor(objp_hit)) {
14410                         // it's not nice to hit your instructor
14411                         if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD) {
14412                                 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_ATTACK, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14413                                 pp->last_warning_message_time = Missiontime;
14414                                 ship_set_subsystem_strength( Player_ship, SUBSYSTEM_WEAPONS, 0.0f);
14415
14416                                 training_fail();
14417
14418                                 //      Instructor warp out.
14419                                 ai_set_mode_warp_out(objp_hit, &Ai_info[Ships[objp_hit->instance].ai_index]);
14420                                 gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START_FORCED );     //      Force player to warp out.
14421
14422                                 //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) );
14423                                 //ship_apply_global_damage( objp_hitter, objp_hit, NULL, 1.0f );
14424                         } else if (Missiontime - pp->last_warning_message_time > F1_0*4) {
14425                                 // warning every 4 sec
14426                                 // use NULL as the message sender here since it is the Terran Command persona
14427                                 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_HIT, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14428                                 pp->last_warning_message_time = Missiontime;
14429                         }
14430
14431                 // not nice to hit your friends
14432                 } else if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f)) {
14433                         process_friendly_hit_message( MESSAGE_HAMMER_SWINE, objp_hit );
14434                         mission_goal_fail_all();
14435                         ai_abort_rearm_request( Player_obj );
14436
14437                         Player_ship->team = TEAM_TRAITOR;
14438
14439                 } else if ((damage > frand()) && (Missiontime - pp->last_warning_message_time > F1_0*4) && (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD)) {
14440                         // no closer than 4 sec intervals
14441                         //      Note: (damage > frand()) added on 12/9/97 by MK.  Since damage is now scaled down for big ships, we could get too
14442                         //      many warnings.  Kind of tedious.  frand() returns a value in 0..1, so this won't affect legit hits.
14443                         process_friendly_hit_message( MESSAGE_OOPS, objp_hit );
14444                         pp->last_warning_message_time = Missiontime;
14445                 }
14446         }
14447 }
14448
14449 //      Maybe make ship with ai_info *aip attack hitter_objnum as a dynamic goal
14450 void maybe_set_dynamic_chase(ai_info *aip, int hitter_objnum)
14451 {
14452         Assert(Ship_info[Ships[aip->shipnum].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER));
14453
14454         // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
14455         if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
14456                 return;
14457         }
14458
14459         // only set as target if can be targeted.
14460         if (awacs_get_level(&Objects[hitter_objnum], &Ships[aip->shipnum], 1) < 1) {
14461                 return;
14462         }
14463
14464         if (aip->target_objnum != hitter_objnum)
14465                 aip->aspect_locked_time = 0.0f;
14466         set_target_objnum(aip, hitter_objnum);
14467         aip->resume_goal_time = Missiontime + i2f(20);  //      Only chase up to 20 seconds.
14468         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14469
14470         set_targeted_subsys(aip, NULL, -1);             //      Say not attacking any particular subsystem.
14471
14472         aip->previous_submode = aip->mode;
14473         aip->mode = AIM_CHASE;
14474         aip->submode = SM_ATTACK;
14475 }
14476
14477
14478 //      Return true if *objp has armed an aspect seeking bomb.
14479 //      This function written so a ship with an important bomb to fire will willingly take hits in the face to fire its bomb.
14480 int firing_aspect_seeking_bomb(object *objp)
14481 {
14482         ship    *shipp;
14483         int     bank_index;
14484         ship_weapon     *swp;
14485
14486         shipp = &Ships[objp->instance];
14487
14488         swp = &shipp->weapons;
14489
14490         bank_index = swp->current_secondary_bank;
14491
14492         if (bank_index != -1)
14493                 if (swp->secondary_bank_ammo[bank_index] > 0) {
14494                         if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_BOMB) {
14495                                 if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_HOMING_ASPECT) {
14496                                         return 1;
14497                                 }
14498                         }
14499                 }
14500
14501         return 0;
14502 }
14503
14504 //      *objp collided with big ship *big_objp at global point *collide_pos
14505 //      Make it fly away from the collision point.
14506 // collision_normal is NULL, when a collision is imminent and we just want to bug out.
14507 void big_ship_collide_recover_start(object *objp, object *big_objp, vector *collide_pos, vector *collision_normal)
14508 {
14509         ai_info *aip;
14510
14511         Assert(objp->type == OBJ_SHIP);
14512
14513         aip = &Ai_info[Ships[objp->instance].ai_index];
14514
14515         if (!timestamp_elapsed(aip->big_recover_timestamp) && (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1))
14516                 return;
14517
14518         //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)));
14519         if (collision_normal) {
14520                 aip->big_recover_timestamp = timestamp(2000);
14521                 aip->big_collision_normal = *collision_normal;
14522         //      nprintf(("AI", " normal\n"));
14523         } else {
14524                 aip->big_recover_timestamp = timestamp(500);
14525         //      nprintf(("AI", " no normal\n"));
14526         }
14527
14528
14529         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
14530         aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_1;
14531
14532 //      vector  out_vec;
14533 //      vm_vec_normalized_dir(&out_vec, &objp->pos, collide_pos);
14534
14535         // big_recover_pos_1 is 100 m out along normal
14536         vector direction;
14537         if (collision_normal) {
14538                 direction = *collision_normal;
14539         } else {
14540                 vm_vec_copy_scale(&direction, &objp->orient.v.fvec, -1.0f);
14541         }
14542         vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &direction, 100.0f);
14543
14544         // go out 200 m from box closest box point
14545         get_world_closest_box_point_with_delta(&aip->big_recover_pos_2, big_objp, &aip->big_recover_pos_1, NULL, 300.0f);
14546
14547         accelerate_ship(aip, 0.0f);
14548 /*
14549         if (vm_vec_dot(collision_normal, &objp->orient.v.fvec) > 0.5f) {
14550 //              vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14551 //              vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.v.uvec, big_objp->radius/2.0f);
14552 //              vm_vec_scale_add(&aip->big_recover_pos_2, &objp->pos, &out_vec, big_objp->radius*2.0f);
14553                 accelerate_ship(aip, 2.0f);
14554         } else {
14555 //              vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14556 //              vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.v.uvec, big_objp->radius/2.0f);
14557                 accelerate_ship(aip, 0.0f);
14558         } */
14559 }
14560
14561 float max_lethality = 0.0f;
14562
14563 void ai_update_lethality(object *ship_obj, object *other_obj, float damage)
14564 {
14565         Assert(ship_obj->type == OBJ_SHIP);
14566         Assert(other_obj->type == OBJ_WEAPON || other_obj->type == OBJ_SHOCKWAVE);
14567         int dont_count = FALSE;
14568
14569         int parent = other_obj->parent;
14570         if (Objects[parent].type == OBJ_SHIP) {
14571                 if (Objects[parent].signature == other_obj->parent_sig) {
14572
14573                         // check damage done to enemy team
14574                         if (Ships[ship_obj->instance].team != Ships[Objects[parent].instance].team) {
14575
14576                                 // other is weapon
14577                                 if (other_obj->type == OBJ_WEAPON) {
14578                                         weapon *wp = &Weapons[other_obj->instance];
14579                                         weapon_info *wif = &Weapon_info[wp->weapon_info_index];
14580
14581                                         // if parent is BIG|HUGE, don't count beam
14582                                         if (Ship_info[Ships[Objects[parent].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
14583                                                 if (wif->wi_flags & WIF_BEAM) {
14584                                                         dont_count = TRUE;
14585                                                 }
14586                                         }
14587                                 }
14588
14589                                 if (!dont_count) {
14590                                         float lethality = 0.025f * damage;      // 2 cyclops (@2000) put you at 100 lethality
14591
14592                                         // increase lethality weapon's parent ship
14593                                         ai_info *aip = &Ai_info[Ships[Objects[parent].instance].ai_index];
14594                                         aip->lethality += lethality;
14595                                         aip->lethality = min(110.0f, aip->lethality);
14596                                         // if you hit, don;t be less than 0
14597                                         aip->lethality = max(0.0f, aip->lethality);
14598
14599 //                                      if (aip->lethality > max_lethality) {
14600 //                                              max_lethality = aip->lethality;
14601 //                                              mprintf(("new lethalilty high: %.1f\n", max_lethality));
14602 //                                      }
14603
14604                                         // if parent is player, show his lethality
14605 //                                      if (Objects[parent].flags & OF_PLAYER_SHIP) {
14606 //                                              mprintf(("Player lethality: %.1f\n", aip->lethality));
14607 //                                      }
14608                                 }
14609                         }
14610                 }
14611         }
14612 }
14613
14614
14615 //      Object *objp_ship was hit by either weapon *objp_weapon or collided into by ship hit_objp at point *hitpos.
14616 void ai_ship_hit(object *objp_ship, object *hit_objp, vector *hitpos, int shield_quadrant, vector *hit_normal)
14617 {
14618         int             hitter_objnum = -2;
14619         object  *objp_hitter = NULL;
14620         ship            *shipp;
14621         ai_info *aip, *hitter_aip;
14622
14623         shipp = &Ships[objp_ship->instance];
14624         aip = &Ai_info[shipp->ai_index];
14625
14626         if (objp_ship->flags & OF_PLAYER_SHIP)
14627                 return;
14628
14629         if ((aip->mode == AIM_WARP_OUT) || (aip->mode == AIM_PLAY_DEAD))
14630                 return;
14631
14632         if (hit_objp->type == OBJ_SHIP) {
14633                 //      If the object that this ship collided with is a big ship
14634                 if (Ship_info[Ships[hit_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
14635                         //      And the current object is _not_ a big ship
14636                         if (!(Ship_info[Ships[objp_ship->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
14637                                 //      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.
14638                                 big_ship_collide_recover_start(objp_ship, hit_objp, hitpos, hit_normal);
14639                         }
14640                 }
14641         }
14642
14643         if (hit_objp->type == OBJ_WEAPON) {
14644                 //      Make sure the object that fired this weapon is still alive.  If not, abort.
14645                 // Assert(hit_objp->parent >= 0);
14646                 if(hit_objp->parent < 0){
14647                         return;
14648                 }
14649                 if ( hit_objp->parent_sig != Objects[hit_objp->parent].signature ){
14650                         return;
14651                 }
14652
14653                 //      Hit by a protected ship, don't attack it.
14654                 if (Objects[hit_objp->parent].flags & OF_PROTECTED) {
14655                         if ((Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) && (aip->target_objnum == -1)) {
14656                                 if (aip->mode == AIM_CHASE) {
14657                                         if (aip->submode != SM_EVADE_WEAPON) {
14658                                                 aip->mode = AIM_CHASE;
14659                                                 aip->submode = SM_EVADE_WEAPON;
14660                                                 aip->submode_start_time = Missiontime;
14661                                         }
14662                                 } else if (aip->mode != AIM_EVADE_WEAPON) {
14663                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14664                                         aip->previous_mode = aip->mode;
14665                                         aip->previous_submode = aip->submode;
14666                                         aip->mode = AIM_EVADE_WEAPON;
14667                                         aip->submode = -1;
14668                                         aip->submode_start_time = Missiontime;
14669                                         aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Evade for up to five seconds.
14670                                 }
14671
14672                         }
14673                         return;
14674                 }
14675
14676                 hitter_objnum = hit_objp->parent;
14677                 Assert((hitter_objnum >= 0) && (hitter_objnum < MAX_OBJECTS));
14678                 objp_hitter = &Objects[hitter_objnum];
14679                 maybe_process_friendly_hit(objp_hitter, objp_ship, hit_objp);           //      Deal with player's friendly fire.
14680
14681                 if ( (shipp->team & TEAM_FRIENDLY) && !(Game_mode & GM_MULTIPLAYER) ) {
14682                         ship_maybe_ask_for_help(shipp);
14683                 }
14684         } else if (hit_objp->type == OBJ_SHIP) {
14685                 if (shipp->team == Ships[hit_objp->instance].team)              //      Don't have AI react to collisions between teammates.
14686                         return;
14687                 objp_hitter = hit_objp;
14688                 hitter_objnum = hit_objp-Objects;
14689         } else {
14690                 Int3(); //      Hmm, what kind of object hit this if not weapon or ship?  Get MikeK.
14691                 return;
14692         }
14693
14694         //      Collided into a protected ship, don't attack it.
14695         if (hit_objp->flags & OF_PROTECTED)
14696                 return;
14697
14698         Assert(objp_hitter != NULL);
14699         hitter_aip = &Ai_info[Ships[objp_hitter->instance].ai_index];
14700         hitter_aip->last_hit_target_time = Missiontime;
14701         
14702         // store the object signature of objp_ship into ai_info, since we want to track the last ship hit by 'hitter_objnum'
14703         hitter_aip->last_objsig_hit = objp_ship->signature; 
14704
14705         aip->last_hit_time = Missiontime;
14706
14707         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
14708                 return;
14709
14710         //      If this ship is awaiting repair, abort!
14711         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)) {
14712                 ship_info       *sip = &Ship_info[shipp->ship_info_index];
14713
14714                 if (objp_ship->hull_strength/sip->initial_hull_strength < 0.3f) {
14715                         //      No, only abort if hull below a certain level.
14716                         aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP/2);  //      Might request again after 15 seconds.
14717                         if ( !(objp_ship->flags & OF_PLAYER_SHIP) )                                             // mwa -- don't abort rearm for a player
14718                                 ai_abort_rearm_request(objp_ship);
14719                 }
14720         }
14721
14722         //      If firing a bomb, ignore enemy fire so we can gain lock drop the bomb.
14723         //      Only ignore fire if aspect_locked_time > 0.5f, as this means we're in range.
14724         if (firing_aspect_seeking_bomb(objp_ship)) {
14725                 if ((aip->ai_flags & AIF_SEEK_LOCK) && (aip->aspect_locked_time > 0.1f))
14726                         return;
14727         }
14728
14729         //      If in AIM_STRAFE mode and got hit by target, maybe attack turret if appropriate
14730         if (aip->mode == AIM_STRAFE) {
14731                 Assert(hitter_objnum != -2);
14732                 if (aip->target_objnum == hitter_objnum) {
14733                         if ( hit_objp->type == OBJ_WEAPON ) {
14734                                 ai_big_strafe_maybe_attack_turret(objp_ship, hit_objp);
14735                         }
14736                         return;
14737                 }
14738                 else {
14739                                 // AL 11-10-97:
14740                         ;       // do nothing here, we'll attack this hitter if it is a fighter or bomber (this is handled
14741                                 // in code later in this function
14742                 }
14743         }
14744
14745         if (objp_ship == Player_obj)
14746                 return;         //      We don't do AI for the player.
14747
14748         maybe_update_guard_object(objp_ship, objp_hitter);
14749
14750         //      Big ships don't go any further.
14751         if (!(Ship_info[shipp->ship_info_index].flags & SIF_SMALL_SHIP))
14752                 return;
14753
14754         //      If the hitter object is the ignore object, don't attack it.
14755         ship_info       *sip = &Ship_info[shipp->ship_info_index];
14756         if ((is_ignore_object(aip, objp_hitter-Objects)) && (sip->flags & (SIF_BOMBER | SIF_FIGHTER))) {
14757                 if (aip->mode == AIM_NONE) {
14758                         aip->mode = AIM_CHASE;  //      This will cause the ship to move, if not attack.
14759                         aip->submode = SM_EVADE;
14760                 }
14761                 return;
14762         }
14763
14764         //      Maybe abort based on mode.
14765         switch (aip->mode) {
14766         case AIM_CHASE:
14767                 if (aip->submode == SM_ATTACK_FOREVER)
14768                         return;
14769
14770                 if ( hit_objp->type == OBJ_WEAPON ) {
14771                         if ( ai_big_maybe_enter_strafe_mode(objp_ship, OBJ_INDEX(hit_objp), 1) )
14772                                 return;
14773                 }
14774
14775         case AIM_GUARD:
14776                 //      If in guard mode and far away from guard object, don't pursue guy that hit me.
14777                         if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
14778                                 if (vm_vec_dist_quick(&objp_ship->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
14779                                         return;
14780                                 }
14781                         }
14782         case AIM_STILL:
14783         case AIM_STAY_NEAR:
14784                 // Note: Dealt with above, at very top.  case AIM_PLAY_DEAD:
14785         case AIM_STRAFE:
14786                 break;
14787         case AIM_EVADE_WEAPON:
14788         case AIM_EVADE:
14789         case AIM_GET_BEHIND:
14790         case AIM_AVOID:
14791         case AIM_DOCK:
14792         case AIM_BIGSHIP:
14793         case AIM_PATH:
14794         case AIM_NONE:
14795         case AIM_BAY_DEPART:
14796         case AIM_SENTRYGUN:
14797                 return;
14798         case AIM_BAY_EMERGE:
14799                 // If just leaving the docking bay, don't react to enemy fire... just keep flying away from docking bay
14800                 if ( (Missiontime - aip->submode_start_time) < 5*F1_0 ) {
14801                         return;
14802                 }
14803                 break;
14804         case AIM_WAYPOINTS:
14805                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER))
14806                         break;
14807                 else
14808                         return;
14809                 break;
14810         case AIM_SAFETY:
14811                 if ((aip->submode != AISS_1) || (Missiontime - aip->submode_start_time > i2f(1))) {
14812                         aip->submode = AISS_1;
14813                         aip->submode_start_time = Missiontime;
14814                 }
14815                 return;
14816                 break;
14817         case AIM_WARP_OUT:
14818                 return;
14819                 break;
14820         default:
14821                 Int3(); //      Bogus mode!
14822         }
14823
14824         if (timestamp_elapsed(aip->ok_to_target_timestamp))
14825                 aip->ai_flags &= ~AIF_FORMATION;                        //      If flying in formation, bug out!
14826
14827         aip->hitter_objnum = hitter_objnum;
14828         aip->hitter_signature = Objects[hitter_objnum].signature;
14829
14830         //      If the hitter is not on the same team as the hittee, do some stuff.
14831         if (shipp->team != Ships[objp_hitter->instance].team) {
14832                 //nprintf(("AI", "Object %i attacking %i, who just hit him!\n", objp_ship-Objects, hitter_objnum));
14833
14834                 if ((hitter_objnum != aip->target_objnum) && (sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
14835                         maybe_set_dynamic_chase(aip, hitter_objnum);
14836                         maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14837                 } else {
14838                         if ((aip->mode == AIM_CHASE) && ((objp_ship->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp_ship)/sip->shields > 0.8f))) {
14839                                 switch (aip->submode) {
14840                                 case SM_ATTACK:
14841                                 case SM_SUPER_ATTACK:
14842                                 case SM_GET_AWAY:
14843                                         break;
14844                                 default:
14845                                         if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
14846                                                 maybe_set_dynamic_chase(aip, hitter_objnum);
14847                                         }
14848                                         maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14849                                         break;
14850                                 }
14851                         } else if (aip->mode == AIM_CHASE) {
14852                                 switch (aip->submode) {
14853                                 case SM_ATTACK:
14854                                         aip->submode = SM_EVADE;
14855                                         aip->submode_start_time = Missiontime;
14856                                         break;
14857                                 case SM_SUPER_ATTACK:
14858                                         if (Missiontime - aip->submode_start_time > i2f(1)) {
14859                                                 aip->submode = SM_EVADE;
14860                                                 aip->submode_start_time = Missiontime;
14861                                         }
14862                                         break;
14863                                 case SM_EVADE_BRAKE:
14864                                         break;
14865                                 case SM_EVADE_SQUIGGLE:
14866                                         aip->submode = SM_EVADE;
14867                                         aip->submode_start_time = Missiontime;
14868                                         break;
14869                                 default:
14870                                         if (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) {
14871                                                 maybe_set_dynamic_chase(aip, hitter_objnum);
14872                                                 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14873                                         }
14874
14875                                         break;
14876                                 }
14877                         } else {
14878                                 // AL 3-15-98: Prevent escape pods from entering chase mode
14879                                 if ( (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) ) {
14880                                         maybe_set_dynamic_chase(aip, hitter_objnum);
14881                                 }
14882                                 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14883                         }
14884                 }
14885         }
14886 }
14887
14888 //      Ship shipnum has been destroyed.
14889 //      Cleanup.
14890 // the parameter 'method' is used to tell is this ship was destroyed or it departed normally.
14891 // This function will get called in either case, and there are things that should be done if
14892 // the ship actually gets destroyed which shouldn't get done if it departed.
14893 void ai_ship_destroy(int shipnum, int method)
14894 {
14895         int             objnum;
14896         object  *other_objp;
14897         ship            *shipp;
14898         ship_obj        *so;
14899         ai_info *dead_aip;
14900
14901         Assert((shipnum >= 0) && (shipnum < MAX_SHIPS));
14902         objnum = Ships[shipnum].objnum;
14903         dead_aip = &Ai_info[Ships[shipnum].ai_index];
14904
14905         // if I was getting repaired, or awaiting repair, then cleanup the repair mode.  When awaiting repair, the dock_objnum
14906         // is -1.  When the support ship is on the way, the dock_objnum >= 0 (points to support ship).
14907         if ( dead_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14908                 if ( dead_aip->dock_objnum >= 0 )
14909                         ai_do_objects_repairing_stuff( &Objects[objnum], &Objects[dead_aip->dock_objnum], REPAIR_INFO_END);
14910                 else
14911                         ai_do_objects_repairing_stuff( &Objects[objnum], NULL, REPAIR_INFO_END );
14912         }
14913
14914         //      For all objects that had this ship as a target, wipe it out, forcing find of a new enemy.
14915         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
14916                 other_objp = &Objects[so->objnum];
14917                 Assert(other_objp->instance != -1);
14918
14919                 shipp = &Ships[other_objp->instance];
14920                 Assert(shipp->ai_index != -1);
14921
14922                 ai_info *aip = &Ai_info[shipp->ai_index];
14923
14924                 // MWA 2/11/98
14925                 // code commented out below is taken care of in ai_cleanup_dock_mode when gets called when the
14926                 // support ship starts it's death roll.
14927
14928                 //      If the destroyed ship was on its way to repair the current ship
14929                 if (aip->dock_objnum == objnum) {
14930
14931                         // clean up the flags for any kind of docking mode.  If aip was part of a goal of dock/undock
14932                         // then it will get cleaned up by the goal code.
14933                         ai_do_objects_undocked_stuff( other_objp, NULL );
14934
14935                         if ( aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14936                                 int abort_reason;
14937                                 if ( method == SEF_DEPARTED ) {
14938                                         abort_reason = REPAIR_INFO_ABORT;
14939                                 } else {
14940                                         abort_reason = REPAIR_INFO_KILLED;
14941                                 }
14942                                 ai_do_objects_repairing_stuff( other_objp, NULL, abort_reason );
14943                         }
14944                 }
14945
14946                 if (aip->target_objnum == objnum) {
14947                         set_target_objnum(aip, -1);
14948                         //      If this ship had a dynamic goal of chasing the dead ship, clear the dynamic goal.
14949                         if (aip->resume_goal_time != -1)
14950                                 aip->active_goal = AI_GOAL_NONE;
14951                 }
14952
14953                 if (aip->goal_objnum == objnum) {
14954                         aip->goal_objnum = -1;
14955                         aip->goal_signature = -1;
14956                 }
14957
14958                 if (aip->guard_objnum == objnum) {
14959                         aip->guard_objnum = -1;
14960                         aip->guard_signature = -1;
14961                 }
14962
14963                 if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[Objects[objnum].instance].ai_index].wing)) {
14964                         if (aip->guard_wingnum != aip->wing)
14965                                 ai_set_guard_wing(other_objp, aip->guard_wingnum);
14966                 }
14967
14968                 if (aip->hitter_objnum == objnum)
14969                         aip->hitter_objnum = -1;
14970
14971         }
14972
14973 }
14974
14975 /*
14976 //      Interface function to goals code.
14977 //      Make object *objp fly to point *vp and warp out.
14978 void ai_warp_out(object *objp, vector *vp)
14979 {
14980         ai_info *aip;
14981
14982         aip = &Ai_info[Ships[objp->instance].ai_index];
14983
14984         if (aip->mode != AIM_WARP_OUT) {
14985                 ai_set_mode_warp_out(objp, aip);
14986         }
14987         float   dist;
14988         float   dot;
14989         vector  v2v;
14990         ai_info *aip;
14991
14992         dist = vm_vec_normalized_dir(&v2v, vp, &objp->pos);
14993
14994         if (dist < objp->radius + 5.0f) {
14995
14996                 // Start the warp out effect 
14997                 shipfx_warpout_start(objp);
14998
14999         } else {
15000                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2v);
15001
15002                 aip = &Ai_info[Ships[objp->instance].ai_index];
15003
15004                 if (dist > 500.0f)
15005                         accelerate_ship(aip, 1.0f);
15006                 else
15007                         accelerate_ship(aip, (3*dot + 1.0f)/4.0f);
15008
15009                 turn_towards_point(objp, vp, NULL, 0.0f);
15010         }
15011 }
15012 */
15013
15014
15015 //      Do stuff at start of deathroll.
15016 void ai_deathroll_start(object *ship_obj)
15017 {
15018         ai_info *aip;
15019         ship            *shipp, *other_ship;
15020
15021         shipp = &Ships[ship_obj->instance];
15022         aip = &Ai_info[shipp->ai_index];
15023
15024         // mark object we are docked with so we can do damage and separate during deathroll
15025         // keep dock_objnum_when_dead from being changed if already set (only allow to be set when -1)
15026         if (Ships[ship_obj->instance].dock_objnum_when_dead == -1) {
15027                 Ships[ship_obj->instance].dock_objnum_when_dead = aip->dock_objnum;
15028                 // set other_ship dock_objnum_when_dead, if other_ship exits.
15029                 if (Ships[ship_obj->instance].dock_objnum_when_dead != -1) {
15030                         other_ship = &Ships[Objects[aip->dock_objnum].instance];
15031                         other_ship->dock_objnum_when_dead = shipp->objnum;
15032                 }
15033         }
15034
15035         ai_cleanup_dock_mode(aip, shipp);
15036
15037         aip->mode = AIM_NONE;
15038 }
15039
15040 //      Object *requester_objp tells rearm ship to abort rearm.
15041 //      Returns true if it succeeded, else false.
15042 //      To succeed means you were previously rearming.
15043 int ai_abort_rearm_request(object *requester_objp)
15044 {
15045         ship            *requester_shipp;
15046         ai_info *requester_aip;
15047
15048         Assert(requester_objp->type == OBJ_SHIP);
15049         if(requester_objp->type != OBJ_SHIP){
15050                 return 0;
15051         }
15052         Assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));      
15053         if((requester_objp->instance < 0) || (requester_objp->instance >= MAX_SHIPS)){
15054                 return 0;
15055         }
15056         requester_shipp = &Ships[requester_objp->instance];
15057         Assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));          
15058         if((requester_shipp->ai_index < 0) || (requester_shipp->ai_index >= MAX_AI_INFO)){
15059                 return 0;
15060         }       
15061         requester_aip = &Ai_info[requester_shipp->ai_index];
15062         
15063         if (requester_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)){
15064
15065                 // dock_objnum is always valid once a rearm repair has been requested.  It points to the
15066                 // ship that is coming to repair me.
15067                 if (requester_aip->dock_objnum != -1) {
15068                         object  *repair_objp;
15069                         ai_info *repair_aip;
15070
15071                         repair_objp = &Objects[requester_aip->dock_objnum];
15072                         repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
15073
15074                         //      Make sure signatures match.  This prevents nasty bugs in which an object
15075                         //      that was repairing another is destroyed and is replaced by another ship
15076                         //      before this code comes around.
15077                         if (repair_objp->signature == requester_aip->dock_signature) {
15078
15079                                 Assert( repair_objp->type == OBJ_SHIP );
15080
15081                                 // if support ship is in the process of undocking, don't do anything.
15082                                 if ( repair_aip->submode < AIS_UNDOCK_0 ) {
15083                                         ai_do_objects_repairing_stuff( requester_objp, repair_objp, REPAIR_INFO_ABORT );
15084
15085                                         if ( repair_aip->submode == AIS_DOCK_4 )
15086                                                 repair_aip->submode = AIS_UNDOCK_0;
15087                                         else
15088                                                 repair_aip->submode = AIS_UNDOCK_3;
15089
15090                                         repair_aip->submode_start_time = Missiontime;
15091                                 } else {
15092                                         nprintf(("AI", "Not aborting rearm since already undocking\n"));
15093                                 }
15094                         }
15095                 } else {
15096                         // setting these flags is the safe things to do.  There may not be a corresponding repair
15097                         // ship for this guys since a repair ship may be currently repairing someone else.
15098                         ai_do_objects_repairing_stuff( requester_objp, NULL, REPAIR_INFO_ABORT );
15099
15100                         // try and remove this guy from an arriving support ship.
15101                         mission_remove_scheduled_repair(requester_objp);
15102                 }
15103
15104                 return 1;
15105         } else if ( requester_aip->ai_flags & AIF_REPAIRING ) {
15106                 // a support ship can request to abort when he is told to do something else (like warp out).
15107                 // see if this support ships goal_objnum is valid.  If so, then issue this ai_abort comment
15108                 // for the ship that he is enroute to repair
15109                 if ( requester_aip->goal_objnum != -1 ) {
15110                         int val;
15111
15112                         val = ai_abort_rearm_request( &Objects[requester_aip->goal_objnum] );
15113                         return val;
15114                 }
15115         }
15116
15117         return 0;
15118 }
15119
15120 // function which gets called from ai-issue_rearm_request and from code in missionparse.cpp
15121 // to actually issue the rearm goal (support_obj to rearm requester_obj);
15122 void ai_add_rearm_goal( object *requester_objp, object *support_objp )
15123 {
15124         ship *support_shipp, *requester_shipp;
15125         ai_info *support_aip, *requester_aip;
15126
15127         support_shipp = &Ships[support_objp->instance];
15128         requester_shipp = &Ships[requester_objp->instance];
15129         requester_aip = &Ai_info[requester_shipp->ai_index];
15130
15131         Assert( support_shipp->ai_index != -1 );
15132         support_aip = &Ai_info[support_shipp->ai_index];
15133
15134         // if the requester is a player object, issue the order as the squadmate messaging code does.  Doing so
15135         // ensures that the player get a higher priority!
15136         requester_aip->ai_flags |= AIF_AWAITING_REPAIR; //      Tell that I'm awaiting repair.
15137         if ( requester_objp->flags & OF_PLAYER_SHIP )
15138                 ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_REARM_REPAIR, -1, requester_shipp->ship_name, support_aip );
15139         else
15140                 ai_add_goal_ship_internal( support_aip, AI_GOAL_REARM_REPAIR, requester_shipp->ship_name, -1, -1 );
15141
15142 }
15143
15144 //      Object *requester_objp requests rearming.
15145 //      Returns objnum of ship coming to repair requester on success
15146 //      Success means you found someone to rearm you and you weren't previously rearming.
15147 int ai_issue_rearm_request(object *requester_objp)
15148 {
15149         object  *objp;
15150         ship            *requester_shipp;
15151         ai_info *requester_aip;
15152
15153         Assert(requester_objp->type == OBJ_SHIP);
15154         Assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));
15155         requester_shipp = &Ships[requester_objp->instance];
15156         Assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));
15157         requester_aip = &Ai_info[requester_shipp->ai_index];
15158         
15159         //      Make sure not already awaiting repair.
15160         if (requester_aip->ai_flags & AIF_AWAITING_REPAIR) {
15161                 nprintf(("AI", "Ship %s already awaiting rearm by ship %s.\n", requester_shipp->ship_name, &Ships[Objects[requester_aip->dock_objnum].instance].ship_name));    
15162                 return -1;
15163         }
15164
15165         if ( !is_support_allowed(requester_objp) )
15166                 return -1;
15167
15168         //nprintf(("AI", "Ship %s requesting rearming.\n", requester_shipp->ship_name));
15169         requester_aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);  //      Might request again after this much time.
15170
15171         // call ship_find_repair_ship to get a support ship.  If none is found, then we will warp one in.  This
15172         // function will return the next available ship which can repair requester
15173         objp = ship_find_repair_ship( requester_objp );
15174         ai_do_objects_repairing_stuff( requester_objp, objp, REPAIR_INFO_QUEUE );
15175         if ( objp ) {
15176
15177                 // MWA 5/14/98 -- moved next item into the ai_do_objects_repairing_stuff function so that clients
15178                 // would properly update their hud support view
15179                 //ai_add_rearm_goal( requester_objp, objp );
15180                 return OBJ_INDEX(objp);
15181
15182         } else {
15183                 // call to warp in repair ship!!!!  for now, warp in any number of ships needed.  Should cap it to
15184                 // some reasonable max (or let support ships warp out).  We should assume here that ship_find_repair_ship()
15185                 // would have returned a valid object if there are too many support ships already in the mission
15186                 mission_warp_in_support_ship( requester_objp );
15187
15188                 return -1;
15189         }
15190
15191 }
15192
15193 // make objp rearm and repair goal_objp
15194 void ai_rearm_repair( object *objp, object  *goal_objp, int priority, int docker_index, int dockee_index )
15195 {
15196         ai_info *aip, *goal_aip;
15197
15198         aip = &Ai_info[Ships[objp->instance].ai_index];
15199         aip->goal_objnum = goal_objp-Objects;
15200
15201         // nprintf(("AI", "Ship %s preparing to rearm ship %s.\n", shipp->ship_name, requester_shipp->ship_name));
15202
15203         ai_dock_with_object(objp, goal_objp, priority, AIDO_DOCK, docker_index, dockee_index);
15204         aip->ai_flags |= AIF_REPAIRING;                                         //      Tell that repair guy is busy trying to repair someone.
15205
15206         goal_aip = &Ai_info[Ships[goal_objp->instance].ai_index];
15207         goal_aip->dock_objnum = objp-Objects;           //      Tell which object is coming to repair.
15208         goal_aip->dock_signature = objp->signature;
15209
15210         ai_do_objects_repairing_stuff( goal_objp, objp, REPAIR_INFO_ONWAY );
15211
15212         goal_aip->abort_rearm_timestamp = timestamp(NEXT_REARM_TIMESTAMP*3/2);
15213 }
15214
15215 // Given a dockee object and the index of the dockbay for that object (ie the dockbay index
15216 // into polymodel->dockbays[] for the model associated with the object), return the index
15217 // of a path_num associated with than dockbay (this is an index into polymodel->paths[])
15218 int ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index)
15219 {
15220         if ( dockbay_index < 0 || dockee_objp == NULL ) {
15221                 Int3();         // should never happen
15222                 return -1;
15223         }
15224
15225         if ( dockee_objp->type == OBJ_SHIP ) {
15226                 int                     path_num;
15227                 polymodel       *pm;
15228
15229                 pm = model_get( Ships[dockee_objp->instance].modelnum );
15230
15231                 // sanity checks
15232                 Assert(pm->n_docks > dockbay_index);
15233                 Assert(pm->docking_bays[dockbay_index].num_spline_paths > 0);
15234                 Assert(pm->docking_bays[dockbay_index].splines != NULL);
15235                 if(pm->n_docks <= dockbay_index){
15236                         return -1;
15237                 }
15238                 if(pm->docking_bays[dockbay_index].num_spline_paths <= 0){
15239                         return -1;
15240                 }
15241                 if(pm->docking_bays[dockbay_index].splines == NULL){
15242                         return -1;
15243                 }
15244
15245                 // We only need to return one path for the dockbay, so return the first
15246                 path_num = pm->docking_bays[dockbay_index].splines[0];
15247                 return path_num;
15248         } else {
15249                 return -1;
15250         }
15251 }
15252
15253 //      Actually go ahead and fire the synaptics.
15254 void cheat_fire_synaptic(object *objp, ship *shipp, ai_info *aip)
15255 {
15256         ship_weapon     *swp;
15257         swp = &shipp->weapons;
15258         int     current_bank = swp->current_secondary_bank;
15259
15260         ai_select_secondary_weapon(objp, swp, WIF_SPAWN, 0);
15261         if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
15262                 if (ship_fire_secondary(objp)) {
15263                         nprintf(("AI", "ship %s cheat fired synaptic!\n", shipp->ship_name));
15264                         swp->next_secondary_fire_stamp[current_bank] = timestamp(2500);
15265                 }
15266         }
15267 }
15268
15269 //      For the subspace mission (sm3-09a)
15270 //              for delta wing
15271 //                      if they're sufficiently far into the mission
15272 //                              if they're near one or more enemies
15273 //                                      every so often
15274 //                                              fire a synaptic if they have one.
15275 void maybe_cheat_fire_synaptic(object *objp, ai_info *aip)
15276 {
15277         //      Only do in subspace missions.
15278         if ( The_mission.flags & MISSION_FLAG_SUBSPACE )        {
15279                 ship    *shipp;
15280                 int     num, time;
15281
15282                 shipp = &Ships[objp->instance];
15283
15284                 if (!(strnicmp(shipp->ship_name, NOX("delta"), 5))) {
15285                         num = shipp->ship_name[6] - '1';
15286
15287                         if ((num >= 0) && (num <= 3)) {
15288                                 time = Missiontime >> 16;       //      Convert to seconds.
15289
15290                                 time -= 2*60;   //      Subtract off two minutes.
15291
15292                                 if (time > 0) {
15293                                         int modulus = 17 + num*3;
15294
15295                                         if ((time % modulus) < 2) {
15296                                                 int count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(objp)), &objp->pos, 1500.0f);
15297
15298                                                 if (count > 0) {
15299                                                         cheat_fire_synaptic(objp, shipp, aip);
15300                                                 }
15301                                         }
15302                                 }
15303                         }
15304                 }
15305         }
15306
15307 }
15308