]> icculus.org git repositories - taylor/freespace2.git/blob - src/ship/aicode.cpp
const-char warning fixes
[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.11  2006/04/26 19:45:22  taylor
19  * fix a FS1 docked speed issue, should keep it in line with the original now
20  *
21  * Revision 1.10  2005/03/29 02:18:47  taylor
22  * Various 64-bit platform fixes
23  * Fix compiler errors with MAKE_FS1 and fix gr_set_bitmap() too
24  * Make sure that turrets can fire at asteroids for FS1 (needed for a couple missions)
25  * Streaming audio support (big thanks to Pierre Willenbrock!!)
26  * Removed dependance on strings.tbl for FS1 since we don't actually need it now
27  *
28  * Revision 1.9  2004/09/20 01:31:44  theoddone33
29  * GCC 3.4 fixes.
30  *
31  * Revision 1.8  2003/08/03 16:10:30  taylor
32  * cleanup; compile warning fixes
33  *
34  * Revision 1.7  2003/05/25 02:30:43  taylor
35  * Freespace 1 support
36  *
37  * Revision 1.6  2002/07/13 19:47:02  theoddone33
38  * Fix some more warnings
39  *
40  * Change demo building, edit Makefile if you want the demo.
41  *
42  * Revision 1.5  2002/06/17 06:33:10  relnev
43  * ryan's struct patch for gcc 2.95
44  *
45  * Revision 1.4  2002/06/09 04:41:26  relnev
46  * added copyright header
47  *
48  * Revision 1.3  2002/06/01 07:12:34  relnev
49  * a few NDEBUG updates.
50  *
51  * removed a few warnings.
52  *
53  * Revision 1.2  2002/05/03 13:34:33  theoddone33
54  * More stuff compiles
55  *
56  * Revision 1.1.1.1  2002/05/03 03:28:10  root
57  * Initial import.
58  *
59  * 
60  * 107   9/15/99 4:42a Mikek
61  * Make any big ship attacking Colossus, or Colossus attacking any large
62  * ship not use big cruiser movement code.
63  * 
64  * 106   9/15/99 3:28a Jimb
65  * Make all big ships in sm3-08 not do cruiser chase code when attacking
66  * Colossus.  Added so Beast doesn't swerve away from Colossus.
67  * 
68  * 105   9/14/99 4:18p Andsager
69  * hack for mission sm3-08 to abort cruiser_chase as sathanas is about to
70  * begin circling colossus.
71  * 
72  * 104   9/08/99 10:44p Andsager
73  * Make HUGE ships not die when warping out, after warp effect started.
74  * 
75  * 103   9/03/99 11:40p Mikek
76  * Comment out an annoying nprintf().
77  * 
78  * 102   9/01/99 11:26p Dave
79  * Fixed release build warnings.
80  * 
81  * 101   9/01/99 9:12p Mikek
82  * Make it a boatload harder to become a traitor from hitting a large
83  * ship.
84  * 
85  * 100   9/01/99 4:01p Andsager
86  * Make sure BIG|HUGE ships do not respond to shockwaves
87  * 
88  * 99    9/01/99 10:09a Dave
89  * Pirate bob.
90  * 
91  * 98    8/31/99 4:24p Andsager
92  * Reduce collisions when attacking big ships.
93  * 
94  * 97    8/31/99 7:33a Mikek
95  * Improvements in formation flying, less silly behavior, especially when
96  * leader is moving very slowly.
97  * 
98  * 96    8/31/99 5:48a Mikek
99  * Making ships not overshoot so much in formation flying.  Intermediate
100  * checkin.
101  * 
102  * 95    8/30/99 12:03a Mikek
103  * Make guard behavior much less annoying.  Guarders don't get quite so
104  * close and they try to avoid striking the target they are guarding.
105  * 
106  * 94    8/29/99 4:18p Andsager
107  * New "burst" limit for friendly damage.  Also credit more damage done
108  * against large friendly ships.
109  * 
110  * 93    8/28/99 7:29p Dave
111  * Fixed wingmen persona messaging. Make sure locked turrets don't count
112  * towards the # attacking a player.
113  * 
114  * 92    8/26/99 10:46p Andsager
115  * Apply shockwave damage to lethality.
116  * 
117  * 91    8/26/99 8:52p Dave
118  * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
119  * 
120  * 90    8/26/99 5:14p Andsager
121  * 
122  * 89    8/24/99 8:55p Dave
123  * Make sure nondimming pixels work properly in tech menu.
124  * 
125  * 88    8/23/99 6:21p Jefff
126  * added "no traitor" option to missions (and fred)
127  * 
128  * 87    8/20/99 3:36p Andsager
129  * Make sure we don;t miss stealth sweep points.
130  * 
131  * 86    8/16/99 8:21a Andsager
132  * fix link error
133  * 
134  * 85    8/16/99 8:19a Andsager
135  * Add project_point_onto_bbox() to fvi and include in aicode
136  * 
137  * 84    8/15/99 1:30p Dave
138  * Removed some bounding box code because of link errors. Assuming needed
139  * function just needs to get checked in by DaveA.
140  * 
141  * 83    8/15/99 11:59a Andsager
142  * For targing big/huge ships, find nearest distance to bbox, not center.
143  * 
144  * 82    8/13/99 2:20p Andsager
145  * Add speed modification to chances turret will find stealth ship
146  * 
147  * 81    8/13/99 10:49a Andsager
148  * Knossos and HUGE ship warp out.  HUGE ship warp in.  Stealth search
149  * modes dont collide big ships.
150  * 
151  * 80    8/10/99 5:02p Andsager
152  * Fix bug where AI gets stuck in SM_EVADE_WEAPON with no target.
153  * 
154  * 79    8/10/99 11:58a Andsager
155  * Allow turrets to sometimes see stealth.
156  * 
157  * 78    7/31/99 2:57p Dave
158  * Scaled flak aim and jitter by weapon subsystem strength.
159  * 
160  * 77    7/27/99 10:33p Andsager
161  * improve ai for attacking stealth.  reduced jitter in aim.  reduced
162  * error in position when avoiding.  skill level support for attacking
163  * stealth.  Made target error same for team vs. team.
164  * 
165  * 76    7/27/99 10:49a Andsager
166  * Make turret fire rate independent of team for HUGE turrets, and also
167  * for mult team vs. team.
168  * 
169  * 75    7/26/99 12:14p Andsager
170  * Apply cap to how much slower a transport flies with cargo.  Remove
171  * limit on waypoint speed for training.  Enemy ai get stealth exact pos
172  * when stealth fires
173  * 
174  * 74    7/20/99 1:49p Dave
175  * Peter Drake build. Fixed some release build warnings.
176  * 
177  * 73    7/19/99 2:13p Dave
178  * Added some new strings for Heiko.
179  * 
180  * 72    7/19/99 12:02p Andsager
181  * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
182  * only blow up subsystem if its strength is > 0
183  * 
184  * 71    7/15/99 9:20a Andsager
185  * FS2_DEMO initial checkin
186  * 
187  * 70    7/14/99 1:44p Andsager
188  * modify ai_guard for BIG ships to circle around the long axis
189  * 
190  * 69    7/09/99 5:54p Dave
191  * Seperated cruiser types into individual types. Added tons of new
192  * briefing icons. Campaign screen.
193  * 
194  * 68    7/08/99 4:32p Andsager
195  * fix bug with turret-tagged-only
196  * 
197  * 67    7/08/99 12:06p Andsager
198  * Add turret-tagged-only and turret-tagged-clear sexp.
199  * 
200  * 66    7/02/99 3:49p Andsager
201  * Remove debug code.  Allow targeting of stealth from any weapon it
202  * fires.
203  * 
204  * 65    7/02/99 2:01p Andsager
205  * Fix bug where big ship tries to evade dumpfire weapon.
206  * 
207  * 64    7/02/99 10:58a Andsager
208  * Put in big ship - big ship attack mode.  Modify stealth sweep ai.
209  * 
210  * 63    6/30/99 5:53p Dave
211  * Put in new anti-camper code.
212  * 
213  * 62    6/28/99 3:22p Anoop
214  * Fix turret optimization, where ship may not have any valid subsystems
215  * (all blown off).
216  * 
217  * 61    6/25/99 5:56p Andsager
218  * First real pass on stealth ai.
219  * 
220  * 60    6/25/99 3:08p Dave
221  * Multiple flyby sounds.
222  * 
223  * 59    6/25/99 1:12p Danw
224  * DKA:  Make sure big ship has subsystems before trying to target them.
225  * 
226  * 58    6/25/99 10:56a Johnson
227  * Fixed dumb ai code.
228  * 
229  * 57    6/24/99 5:15p Dave
230  * Make sure stride is always at least one for checking turret subsystem
231  * targets.
232  * 
233  * 56    6/24/99 4:59p Dave
234  * Significant speedups to turret firing.
235  * 
236  * 55    6/23/99 5:51p Andsager
237  * Add waypoint-cap-speed.  Checkin stealth ai - inactive.
238  * 
239  * 54    6/16/99 10:21a Dave
240  * Added send-message-list sexpression.
241  * 
242  * 53    6/15/99 9:25a Andsager
243  * Make guard and dynamic chase (who hit you) work with stealth
244  * 
245  * 52    6/14/99 3:21p Andsager
246  * Allow collisions between ship and its debris.  Fix up collision pairs
247  * when large ship is warping out.
248  * 
249  * 51    6/14/99 10:45a Dave
250  * Made beam weapons specify accuracy by skill level in the weapons.tbl
251  * 
252  * 50    6/03/99 8:11a Andsager
253  * 
254  * 49    6/02/99 5:41p Andsager
255  * Reduce range of secondary weapons not fired from turrets in nebula.
256  * Reduce range of beams fired from turrrets in nebula
257  * 
258  * 48    6/02/99 3:23p Andsager
259  * Make AI aware of team visibility.  Allow player targeting with team
260  * visibility info.  Make stealth ships not targetable by AI in nebula
261  * unless tagged.
262  * 
263  * 47    6/02/99 12:52p Andsager
264  * Added team-wide ship visibility.  Implemented for player.
265  * 
266  * 46    6/01/99 8:35p Dave
267  * Finished lockarm weapons. Added proper supercap weapons/damage. Added
268  * awacs-set-radius sexpression.
269  * 
270  * 45    5/28/99 5:35p Andsager
271  * Make ai nebula aware
272  * 
273  * 44    5/24/99 9:55a Dave
274  * Fixed stream weapon ai firing problem. ick.
275  * 
276  * 43    5/20/99 7:00p Dave
277  * Added alternate type names for ships. Changed swarm missile table
278  * entries.
279  * 
280  * 42    5/18/99 1:30p Dave
281  * Added muzzle flash table stuff.
282  * 
283  * 41    5/12/99 2:55p Andsager
284  * Implemented level 2 tag as priority in turret object selection
285  * 
286  * 40    5/12/99 10:42a Andsager
287  * Fix turret bug allowing HUGE turrets to fire at fighters
288  * 
289  * 39    5/06/99 11:46a Andsager
290  * Bug fixes.  Don't get into illegal strafe submode.  Don't choose turret
291  * enemy objnum for beam protected.
292  * 
293  * 38    5/03/99 10:50p Andsager
294  * Make Asteroid_obj_list.  Change get_nearest_turret_objnum() to use
295  * Asteroid_obj_list, Ship_obj_list and Missile_obj_list vs.
296  * obj_used_list.
297  * 
298  * 37    4/29/99 2:29p Dave
299  * Made flak work much better in multiplayer.
300  * 
301  * 36    4/28/99 11:36p Dave
302  * Tweaked up subspace missile strike a bit,
303  * 
304  * 35    4/28/99 3:11p Andsager
305  * Stagger turret weapon fire times.  Make turrets smarter when target is
306  * protected or beam protected.  Add weaopn range to weapon info struct.
307  * 
308  * 34    4/26/99 10:58a Andsager
309  * Add OF_BEAM_PROTECTED flag to keep object from being targeted for zing.
310  * 
311  * 33    4/23/99 12:12p Andsager
312  * Modify wing positions when player is wing leader to prevent some
313  * collisions.
314  * 
315  * 32    4/23/99 12:01p Johnson
316  * Added SIF_HUGE_SHIP
317  * 
318  * 31    4/22/99 11:06p Dave
319  * Final pass at beam weapons. Solidified a lot of stuff. All that remains
320  * now is to tweak and fix bugs as they come up. No new beam weapon
321  * features.
322  * 
323  * 30    4/20/99 6:39p Dave
324  * Almost done with artillery targeting. Added support for downloading
325  * images on the PXO screen.
326  * 
327  * 29    4/20/99 3:40p Andsager
328  * Changes to big ship ai.  Uses bounding box as limit where to fly to
329  * when flying away.
330  * 
331  * 28    4/16/99 5:54p Dave
332  * Support for on/off style "stream" weapons. Real early support for
333  * target-painting lasers.
334  * 
335  * 27    4/02/99 9:55a Dave
336  * Added a few more options in the weapons.tbl for beam weapons. Attempt
337  * at putting "pain" packets into multiplayer.
338  * 
339  * 26    3/28/99 5:58p Dave
340  * Added early demo code. Make objects move. Nice and framerate
341  * independant, but not much else. Don't use yet unless you're me :)
342  * 
343  * 25    3/19/99 9:51a Dave
344  * Checkin to repair massive source safe crash. Also added support for
345  * pof-style nebulae, and some new weapons code.
346  * 
347  * 24    3/08/99 7:03p Dave
348  * First run of new object update system. Looks very promising.
349  * 
350  * 23    3/05/99 3:55p Anoop
351  * Handle some asserts properly.
352  * 
353  * 22    3/04/99 6:09p Dave
354  * Added in sexpressions for firing beams and checking for if a ship is
355  * tagged.
356  * 
357  * 21    3/02/99 9:25p Dave
358  * Added a bunch of model rendering debug code. Started work on fixing
359  * beam weapon wacky firing.
360  * 
361  * 20    2/25/99 2:32p Anoop
362  * (Alan). Fixed ai path following code for AI_BAY_EMERGE. Put in sanity
363  * check so that when the last point on the path is reached, it finishes.
364  * 
365  * 19    2/19/99 2:11p Anoop
366  * Put in some nice handling code for wacky support ship problems (like no
367  * docking paths)
368  * 
369  * 18    2/17/99 2:11p Dave
370  * First full run of squad war. All freespace and tracker side stuff
371  * works.
372  * 
373  * 17    2/11/99 5:22p Andsager
374  * Fixed bugs, generalized block Sexp_variables
375  * 
376  * 16    1/29/99 5:07p Dave
377  * Fixed multiplayer stuff. Put in multiplayer support for rapid fire
378  * missiles.
379  * 
380  * 15    1/29/99 2:25p Andsager
381  * Added turret_swarm_missiles
382  * 
383  * 14    1/27/99 9:56a Dave
384  * Temporary checkin of beam weapons for Dan to make cool sounds.
385  * 
386  * 13    1/24/99 11:37p Dave
387  * First full rev of beam weapons. Very customizable. Removed some bogus
388  * Int3()'s in low level net code.
389  * 
390  * 12    1/21/99 10:44a Dave
391  * More beam weapon stuff. Put in warmdown time.
392  * 
393  * 11    1/12/99 5:45p Dave
394  * Moved weapon pipeline in multiplayer to almost exclusively client side.
395  * Very good results. Bandwidth goes down, playability goes up for crappy
396  * connections. Fixed object update problem for ship subsystems.
397  * 
398  * 10    1/08/99 2:08p Dave
399  * Fixed software rendering for pofview. Super early support for AWACS and
400  * beam weapons.
401  * 
402  * 9     12/23/98 2:53p Andsager
403  * Added ship activation and gas collection subsystems, removed bridge
404  * 
405  * 8     11/12/98 12:13a Dave
406  * Tidied code up for multiplayer test. Put in network support for flak
407  * guns.
408  * 
409  * 7     11/05/98 5:55p Dave
410  * Big pass at reducing #includes
411  * 
412  * 6     10/26/98 9:42a Dave
413  * Early flak gun support.
414  * 
415  * 5     10/23/98 3:51p Dave
416  * Full support for tstrings.tbl and foreign languages. All that remains
417  * is to make it active in Fred.
418  * 
419  * 4     10/20/98 1:39p Andsager
420  * Make so sparks follow animated ship submodels.  Modify
421  * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
422  * submodel_num.  Add submodel_num to multiplayer hit packet.
423  * 
424  * 3     10/13/98 9:29a Dave
425  * Started neatening up freespace.h. Many variables renamed and
426  * reorganized. Added AlphaColors.[h,cpp]
427  * 
428  * 2     10/07/98 10:53a Dave
429  * Initial checkin.
430  * 
431  * 1     10/07/98 10:51a Dave
432  * 
433  * 
434  * $NoKeywords: $
435  */
436
437 // This module contains the actual AI code that does interesting stuff
438 // to objects.   The code in Ai.cpp is just for bookeeping, allocating
439 // ai slots and linking them to ships.
440
441 #include "pstypes.h"
442 #include "fix.h"
443 #include "linklist.h"
444 #include "object.h"
445 #include "physics.h"
446 #include "vecmat.h"
447 #include "ship.h"
448 #include "model.h"
449 #include "2d.h"
450 #include "3d.h"
451 #include "ai.h"
452 #include "floating.h"
453 #include "player.h"
454 #include "freespace.h"
455 #include "weapon.h"
456 #include "missiongoals.h"
457 #include "missionlog.h"
458 #include "timer.h"
459 #include "sound.h"
460 #include "aigoals.h"
461 #include "gamesnd.h"
462 #include "hudmessage.h"
463 #include "missionmessage.h"
464 #include "cmeasure.h"
465 #include "staticrand.h"
466 #include "multimsgs.h"
467 #include "afterburner.h"
468 #include "hudets.h"
469 #include "shipfx.h"
470 #include "shiphit.h"
471 #include "aibig.h"
472 #include "multiutil.h"
473 #include "hud.h"
474 #include "objcollide.h"
475 #include "asteroid.h"
476 #include "hudlock.h"
477 #include "missiontraining.h"
478 #include "gamesequence.h"
479 #include "joy_ff.h"
480 #include "localize.h"
481 #include "flak.h"
482 #include "beam.h"
483 #include "multi.h"
484 #include "swarm.h"
485 #include "multi_team.h"
486 #include "awacs.h"
487 #include "fvi.h"
488
489 #ifndef PLAT_UNIX
490 #pragma optimize("", off)
491 #pragma auto_inline(off)
492 #endif
493
494 #define UNINITIALIZED_VALUE     -99999.9f
495
496 #define INSTRUCTOR_SHIP_NAME NOX("instructor")
497
498 #define AICODE_SMALL_MAGNITUDE  0.001f          // cosider a vector NULL if mag is less than this
499
500 #define NEXT_REARM_TIMESTAMP (60*1000)                  //      Ships will re-request rearm, typically, after this long.
501
502 #define BEAM_NEBULA_RANGE_REDUCE_FACTOR         0.8
503
504 // AIM_CHASE submode defines
505 // SM_STEALTH_FIND
506 #define SM_SF_AHEAD             0
507 #define SM_SF_BEHIND    1
508 #define SM_SF_BAIL              2
509
510 // SM_STEALTH_SWEEP
511 #define SM_SS_SET_GOAL  -1
512 #define SM_SS_BOX0              0
513 #define SM_SS_LR                        1
514 #define SM_SS_UL                        2
515 #define SM_SS_BOX1              3
516 #define SM_SS_UR                        4
517 #define SM_SS_LL                        5
518 #define SM_SS_BOX2              6
519 #define SM_SS_DONE              7
520
521 //XSTR:OFF
522
523 const char *Mode_text[MAX_AI_BEHAVIORS] = {
524         "CHASE",
525         "EVADE",
526         "GET_BEHIND",
527         "CHASE_LONG",
528         "SQUIGGLE",
529         "GUARD",
530         "AVOID",
531         "WAYPOINTS",
532         "DOCK",
533         "NONE",
534         "BIGSHIP",
535         "PATH",
536         "BE_REARMED",
537         "SAFETY",
538         "EV_WEAPON",
539         "STRAFE",
540         "PLAY_DEAD",
541         "BAY_EMERGE",
542         "BAY_DEPART",
543         "SENTRYGUN",
544         "WARP_OUT",
545 };
546
547 //      Submode text is only valid for CHASE mode.
548 const char *Submode_text[] = {
549 "undefined",
550 "CONT_TURN",
551 "ATTACK   ",
552 "E_SQUIG  ",
553 "E_BRAKE  ",
554 "EVADE    ",
555 "SUP_ATTAK",
556 "AVOID    ",
557 "BEHIND   ",
558 "GET_AWAY ",
559 "E_WEAPON ",
560 "FLY_AWAY ",
561 "ATK_4EVER",
562 "STLTH_FND",
563 "STLTH_SWP",
564 "BIG_APPR",
565 "BIG_CIRC",
566 "BIG_PARL"
567 };
568
569 const char *Strafe_submode_text[5] = {
570 "ATTACK",
571 "AVOID",
572 "RETREAT1",
573 "RETREAT2",
574 "POSITION"
575 };
576 //XSTR:ON
577
578 /*
579 //      Used for global ignore of objects.  If an object appears in the Ignore_objects array,
580 //      no one will attack it.
581 #define MAX_IGNORE_OBJECTS      16
582 typedef struct {
583         int     objnum;
584         int     signature;
585 } ignore_object;
586
587 ignore_object   Ignore_objects[MAX_IGNORE_OBJECTS];
588 */
589
590 typedef struct eval_enemy_obj_struct {
591         int                     turret_parent_objnum;                   // parent of turret
592         float                   weapon_travel_dist;                             // max targeting range of turret weapon
593         int                     enemy_team_mask;
594         int                     weapon_system_ok;                                       // is the weapon subsystem of turret ship ok
595         int                     big_only_flag;                                          // turret fires only at big and huge ships
596         vector          *tpos;
597         vector          *tvec;
598         ship_subsys *turret_subsys;
599         int                     current_enemy;
600
601
602         float                   nearest_attacker_dist;                  // nearest ship 
603         int                     nearest_attacker_objnum;
604
605         float                   nearest_homing_bomb_dist;               // nearest homing bomb
606         int                     nearest_homing_bomb_objnum;
607
608         float                   nearest_bomb_dist;                              // nearest non-homing bomb
609         int                     nearest_bomb_objnum;
610
611         float                   nearest_dist;                                           // nearest ship attacking this turret
612         int                     nearest_objnum;
613 }       eval_enemy_obj_struct;
614
615
616 control_info    AI_ci;
617
618 object *Pl_objp;
619 object *En_objp;
620
621 waypoint_list Waypoint_lists[MAX_WAYPOINT_LISTS];
622
623 // How close a turret has to be point at its target before it
624 // can fire.  If the dot of the gun normal and the vector from gun
625 // to target is greater than this, the turret fires.  The smaller
626 // the sloppier the shooting.
627 #define AICODE_TURRET_DUMBFIRE_ANGLE            (0.8f)  
628 #define AICODE_TURRET_HEATSEEK_ANGLE            (0.7f)  
629 #define AICODE_TURRET_MAX_TIME_IN_RANGE (5.0f)
630
631 #define REARM_SOUND_DELAY               (3*F1_0)                //      Amount of time to delay rearm/repair after mode start
632 #define REARM_BREAKOFF_DELAY    (3*F1_0)                //      Amount of time to wait after fully rearmed to breakoff.
633
634 #define MIN_DIST_TO_WAYPOINT_GOAL       5.0f
635 #define MAX_GUARD_DIST                                  250.0f
636 #define BIG_GUARD_RADIUS                                500.0f
637
638 #define MAX_EVADE_TIME                  (15 * 1000)     //      Max time to evade a weapon.
639
640 // defines for repair ship stuff.
641 #define MAX_REPAIR_SPEED                        25.0f
642 #define MAX_UNDOCK_ABORT_SPEED  2.0f
643
644 // defines for EMP effect stuff
645 #define MAX_EMP_INACCURACY              50.0f
646
647 // defines for stealth
648 #define MAX_STEALTH_INACCURACY  50.0f           // at max view dist
649 #define STEALTH_MAX_VIEW_DIST   400             // dist at which 1) stealth no longer visible 2) firing inaccuracy is greatest
650 #define STEALTH_VIEW_CONE_DOT   0.707           // (half angle of 45 degrees)
651
652
653 ai_class        Ai_classes[MAX_AI_CLASSES];
654 int     Ai_firing_enabled = 1;
655 int     Num_ai_classes;
656
657 int     AI_FrameCount = 0;
658 int     Ship_info_inited = 0;
659 int     AI_watch_object = 0; // Debugging, object to spew debug info for.
660 int     Num_waypoint_lists = 0;
661 int     Mission_all_attack = 0;                                 //      !0 means all teams attack all teams.
662
663 const char *Skill_level_names(int level, int translate)
664 {
665         const char *str = NULL;
666
667         #if NUM_SKILL_LEVELS != 5
668         #error Number of skill levels is wrong!
669         #endif
670
671         if(translate){
672                 switch( level ) {
673                 case 0:
674                         str = XSTR("Very Easy", 469);
675                         break;
676                 case 1:
677                         str = XSTR("Easy", 470);
678                         break;
679                 case 2:
680                         str = XSTR("Medium", 471);
681                         break;
682                 case 3:
683                         str = XSTR("Hard", 472);
684                         break;
685                 case 4:
686                         str = XSTR("Insane", 473);
687                         break;
688                 default:        
689                         Int3();
690                 }
691         } else {
692                 switch( level ) {
693                 case 0:
694                         str = NOX("Very Easy");
695                         break;
696                 case 1:
697                         str = NOX("Easy");
698                         break;
699                 case 2:
700                         str = NOX("Medium");
701                         break;
702                 case 3:
703                         str = NOX("Hard");
704                         break;
705                 case 4:
706                         str = NOX("Insane");
707                         break;
708                 default:        
709                         Int3();
710                 }
711         }
712
713         return str;
714 }
715
716 #define DELAY_TARGET_TIME       (12*1000)               //      time in milliseconds until a ship can target a new enemy after an order.
717
718 //      Make enemy ships turn more slowly at lower skill levels.
719 float   Turn_time_skill_level_scale[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.6f, 1.3f, 1.0f};
720
721 //      Maximum number of simultaneous homing weapons on player based on skill level.
722 int     Max_allowed_player_homers[NUM_SKILL_LEVELS] = {2, 3, 4, 7, 99};
723
724 //      Number of ships that can attack another ship at a given skill level.
725 int     Skill_level_max_attackers[NUM_SKILL_LEVELS] = {2, 3, 4, 5, 99};
726
727 //      How long until next predict position.
728 fix Skill_level_delay[NUM_SKILL_LEVELS] = {2*F1_0, 3*F1_0/2, 4*F1_0/3, F1_0/2, 0};
729
730 //      AI ships link primary weapons if energy levels greater than the following amounts:
731 float   Link_energy_levels_always[NUM_SKILL_LEVELS] = {100.0f, 80.0f, 60.0f, 40.0f, 20.0f};     //      always link
732 float   Link_energy_levels_maybe[NUM_SKILL_LEVELS] = {90.0f, 60.0f, 40.0f, 20.0f, 10.0f};       //      link if hull strength low
733
734 //      Seconds to add to time it takes to get enemy in range.  Only for player's enemies.
735 float   In_range_time[NUM_SKILL_LEVELS] = {2.0f, 1.4f, 0.75f, 0.0f, -1.0f};
736
737 //      No matter what, a random unit vector gets scaled by this amount in firing at an enemy.
738 //      Note that for shorter in-range times, these values get scaled, so a value of 0.5f is meaningful.
739 float   Aiming_error[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.3f, 0.7f, 0.2f};
740
741 //      Chance a countermeasure will be fired based on skill level.
742 float Cmeasure_fire_chance[NUM_SKILL_LEVELS] = {0.2f, 0.3f, 0.5f, 0.9f, 1.1f};  //      Note, this gets scaled by ai_class
743
744 float Shield_manage_delays[NUM_SKILL_LEVELS] = {5.0f, 4.0f, 2.5f, 1.2f, 0.1f};
745
746 // accuracy we feed into the beam weapons based upon skill system
747 // float Beam_accuracy[NUM_SKILL_LEVELS] = {2.0f, 1.5f, 1.0f, 0.7f, 0.4f};
748
749 extern float Ship_fire_delay_scale_hostile[NUM_SKILL_LEVELS];
750 extern float Ship_fire_delay_scale_friendly[NUM_SKILL_LEVELS];
751
752 pnode           Path_points[MAX_PATH_POINTS];
753 pnode           *Ppfp;                  //      Free pointer in path points.
754
755 float   AI_frametime;
756
757 char *Ai_class_names[MAX_AI_CLASSES];
758
759 // global for rearm status for teams
760 int Ai_friendly_rearm_timestamp, Ai_hostile_rearm_timestamp, Ai_neutral_rearm_timestamp, Ai_traitor_rearm_timestamp, Ai_unknown_rearm_timestamp;
761
762 // globals for dealing with when to fire huge secondary weapons
763 #define MAX_HUGE_SECONDARY_INFO 10
764
765 typedef struct {
766         int team;
767         int weapon_index;
768         int max_fire_count;
769         char    *shipname;
770 } huge_fire_info;
771
772 huge_fire_info Ai_huge_fire_info[MAX_HUGE_SECONDARY_INFO];
773
774 int Ai_last_arrive_path;        // index of ship_bay path used by last arrival from a fighter bay
775
776 // forward declarations
777 int     ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index);
778 void    create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count=1);
779 void    copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt=-1);
780
781 // ai_set_rearm_status takes a team (friendly, hostile, neutral) and a time.  This function
782 // sets the timestamp used to tell is it is a good time for this team to rearm.  Once the timestamp
783 // is no longer valid, then rearming is not a "good time"
784 // not safe.  Called from sexpression code.
785 void ai_set_rearm_status( int team, int time )
786 {
787         Assert( time >= 0 );
788
789         switch (team) {
790         case TEAM_FRIENDLY:
791                 Ai_friendly_rearm_timestamp = timestamp( time * 1000 );
792                 break;
793         case TEAM_HOSTILE:
794                 Ai_hostile_rearm_timestamp = timestamp( time * 1000 );
795                 break;
796         case TEAM_NEUTRAL:
797                 Ai_neutral_rearm_timestamp = timestamp( time * 1000 );
798                 break;
799         case TEAM_TRAITOR:
800                 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
801                 break;
802         case TEAM_UNKNOWN:
803                 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
804                 break;
805         default:
806                 Int3();
807                 break;
808         }
809 }
810
811 // int ai_good_time_to_rearm returns true(1) or false(0) if it is "safe" for the given
812 // object to rearm.  "safe" is currently defined by the mission designer using the good/bad
813 // time to rearm sexpressions.  This status is currently team based.  This function could
814 // be easily expended to further the definition of "safe"
815 int ai_good_time_to_rearm( object *objp )
816 {
817         int team, status;
818
819         Assert(objp->type == OBJ_SHIP);
820         team = Ships[objp->instance].team;
821         status = 0;
822
823         switch(team) {
824         case TEAM_FRIENDLY:
825                 status = timestamp_valid(Ai_friendly_rearm_timestamp);
826                 break;
827         case TEAM_HOSTILE:
828                 status = timestamp_valid(Ai_hostile_rearm_timestamp);
829                 break;
830         case TEAM_NEUTRAL:
831                 status = timestamp_valid(Ai_neutral_rearm_timestamp);
832                 break;
833         case TEAM_TRAITOR:
834                 status = timestamp_valid(Ai_traitor_rearm_timestamp);
835                 break;
836         case TEAM_UNKNOWN:
837                 status = timestamp_valid(Ai_unknown_rearm_timestamp);
838                 break;
839         default:
840                 Int3();
841                 break;
842         }
843
844         return status;
845 }
846
847 // functions to deal with letting the ai know about good times to fire powerful secondary
848 // weapons.
849
850 // this function is entry point from sexpression code to set internal data for use by ai code.
851 void ai_good_secondary_time( int team, int weapon_index, int max_fire_count, char *shipname )
852 {
853         int i, index;
854
855         // find an open slot to put this data
856         for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
857                 if ( Ai_huge_fire_info[i].weapon_index == -1 )
858                         break;
859         }
860
861         Assert( i < MAX_HUGE_SECONDARY_INFO );                  // we've run out of room
862
863         Ai_huge_fire_info[i].weapon_index = weapon_index;
864         Ai_huge_fire_info[i].team = team;
865         Ai_huge_fire_info[i].max_fire_count = max_fire_count;
866
867         Ai_huge_fire_info[i].shipname = ai_get_goal_ship_name( shipname, &index );
868 }
869
870 // function called internally to the ai code to tell whether or not weapon_num can be fired
871 // from firer_objp at target_objp.  This function will resolve the team for the firer.
872 // returns:
873 //              -1  -- when conditions don't allow firer to fire weapon_num on target_objp
874 //              >=0 -- when conditions allow firer to fire.  Return value is max number of weapon_nums
875 //           which can be fired on target_objp
876 int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
877 {
878         int i, firer_team, target_signature;
879         ship *firer_ship;
880         huge_fire_info *hfi = NULL;
881
882         Assert( firer_objp->type == OBJ_SHIP );
883         firer_ship = &Ships[firer_objp->instance];
884         firer_team = firer_ship->team;
885
886         // get target object's signature and try to find it in the list.
887         target_signature = target_objp->signature;
888         for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
889                 int ship_index, signature;
890
891                 hfi = &Ai_huge_fire_info[i];
892                 if ( hfi->weapon_index == -1 )
893                         continue;
894
895                 ship_index = ship_name_lookup( hfi->shipname );
896                 if ( ship_index == -1 )
897                         continue;
898
899                 signature = Objects[Ships[ship_index].objnum].signature;
900
901                 // sigatures, weapon_index, and team must match
902                 if ( (signature == target_signature) && (hfi->weapon_index == weapon_num) && (hfi->team == firer_team) )
903                         break;
904         }
905
906         // return -1 if not found
907         if ( i == MAX_HUGE_SECONDARY_INFO )
908                 return -1;
909
910         // otherwise, we can return the max number of weapons we can fire against target_objps
911
912         return hfi->max_fire_count;
913 }
914
915 // function to clear out secondary firing infomration between levels
916 void ai_init_secondary_info()
917 {
918         int i;
919
920         // clear out the data for dealing with when ai ships can fire huge secondary weapons
921         for (i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
922                 Ai_huge_fire_info[i].weapon_index = -1;
923                 Ai_huge_fire_info[i].team = -1;
924                 Ai_huge_fire_info[i].max_fire_count = -1;
925                 Ai_huge_fire_info[i].shipname = NULL;
926         }
927 }
928
929
930 //      Garbage collect the Path_points buffer.
931 //      Scans all objects, looking for used Path_points records.
932 //      Compresses Path_points buffer, updating aip->path_start and aip->path_cur indices.
933 //      Updates Ppfp to point to first free record.
934 //      This function is fairly fast.  Its worst-case running time is proportional to
935 //      3*MAX_PATH_POINTS + MAX_OBJECTS
936 //      Things to do to optimize this function:
937 //              1. if (t != 0) xlt++; can be replaced by xlt += t; assuming t can only be 0 or 1.
938 //              2. When pp_xlate is getting stuffed the first time, note highest index and use that 
939 //                      instead of MAX_PATH_POINTS in following two for loops.
940 void garbage_collect_path_points()
941 {
942         int     i;
943         int     pp_xlate[MAX_PATH_POINTS];
944         object  *A;
945         ship_obj        *so;
946
947         //      Scan all objects and create Path_points xlate table.
948         for (i=0; i<MAX_PATH_POINTS; i++)
949                 pp_xlate[i] = 0;
950
951         //      in pp_xlate, mark all used Path_point records
952         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
953                 A = &Objects[so->objnum];
954                 ship    *shipp = &Ships[A->instance];
955                 if (shipp->ai_index != -1) {
956                         ai_info *aip = &Ai_info[shipp->ai_index];
957
958                         if ((aip->path_length > 0) && (aip->path_start > -1)) {
959
960                                 for (int i=aip->path_start; i<aip->path_start + aip->path_length; i++) {
961                                         Assert(pp_xlate[i] == 0);       //      If this is not 0, then two paths use this point!
962                                         pp_xlate[i] = 1;
963                                 }
964                         }
965                 }
966         }
967
968         //      Now, stuff xlate index in pp_xlate.  This is the number to translate any path_start
969         //      or path_cur index to.
970         int     xlt = 0;
971         for (i=0; i<MAX_PATH_POINTS; i++) {
972                 int     t = pp_xlate[i];
973
974                 pp_xlate[i] = xlt;
975                 if (t != 0)
976                         xlt++;
977         }
978         
979         //      Update global Path_points free pointer.
980         Ppfp = &Path_points[xlt];
981
982         //      Now, using pp_xlate, fixup all aip->path_cur and aip->path_start indices
983         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
984                 A = &Objects[so->objnum];
985                 ship    *shipp = &Ships[A->instance];
986                 if (shipp->ai_index != -1) {
987                         ai_info *aip = &Ai_info[shipp->ai_index];
988
989                         if ((aip->path_length > 0) && (aip->path_start > -1)) {
990                                 Assert(aip->path_start < MAX_PATH_POINTS);
991                                 aip->path_start = pp_xlate[aip->path_start];
992
993                                 Assert((aip->path_cur >= 0) && (aip->path_cur < MAX_PATH_POINTS));
994                                 aip->path_cur = pp_xlate[aip->path_cur];
995                         }
996                 }
997         }
998
999         //      Now, compress the buffer.
1000         for (i=0; i<MAX_PATH_POINTS; i++)
1001                 if (i != pp_xlate[i])
1002                         Path_points[pp_xlate[i]] = Path_points[i];
1003
1004 }
1005
1006 //      Hash two values together, return result.
1007 //      Hash function: curval shifted right circular by one, newval xored in.
1008 int hash(unsigned int curval, int newval)
1009 {
1010         int     addval = curval & 1;
1011
1012         curval >>= 1;
1013         if (addval)
1014                 curval |= 0x80000000;
1015         curval ^= newval;
1016
1017         return curval;
1018 }
1019
1020 //      Hash some information in an object together.
1021 //      On 2/20/97, the information is position and orientation.
1022 int create_object_hash(object *objp)
1023 {
1024         int     *ip;
1025         unsigned int    hashval = 0;
1026         int     i;
1027
1028         ip = (int *) &objp->orient;
1029
1030         for (i=0; i<9; i++) {
1031                 hashval = hash(hashval, *ip);
1032                 ip++;
1033         }
1034
1035         ip = (int *) &objp->pos;
1036
1037         for (i=0; i<3; i++) {
1038                 hashval = hash(hashval, *ip);
1039                 ip++;
1040         }
1041
1042         return hashval;
1043 }
1044
1045 //      Stuff a list of NUM_SKILL_LEVELS floats at *plist.
1046 void parse_float_list(float *plist)
1047 {
1048         int     i;
1049
1050         for (i=0; i<NUM_SKILL_LEVELS; i++) {
1051                 stuff_float(&plist[i]);
1052         }
1053 }
1054
1055 void parse_ai_class()
1056 {
1057         ai_class        *aicp = &Ai_classes[Num_ai_classes];
1058
1059         required_string("$Name:");
1060         stuff_string(aicp->name, F_NAME, NULL);
1061
1062         Ai_class_names[Num_ai_classes] = aicp->name;
1063
1064         required_string("$accuracy:");
1065         parse_float_list(aicp->ai_accuracy);
1066
1067         required_string("$evasion:");
1068         parse_float_list(aicp->ai_evasion);
1069
1070         required_string("$courage:");
1071         parse_float_list(aicp->ai_courage);
1072
1073         required_string("$patience:");
1074         parse_float_list(aicp->ai_patience);
1075 }
1076
1077 void parse_aitbl()
1078 {
1079         // open localization
1080         lcl_ext_open();
1081
1082         read_file_text("ai.tbl");
1083
1084         reset_parse();
1085
1086         Num_ai_classes = 0;
1087
1088         required_string("#AI Classes");
1089
1090         while (required_string_either("#End", "$Name:")) {
1091                 Assert( Num_ai_classes < MAX_AI_CLASSES);
1092
1093                 parse_ai_class();
1094
1095                 Num_ai_classes++;
1096         }
1097
1098         // close localization
1099         lcl_ext_close();
1100 }
1101
1102 LOCAL int ai_inited = 0;
1103
1104 //========================= BOOK-KEEPING FUNCTIONS =======================
1105
1106 // Called once at game start-up
1107 void ai_init()
1108 {
1109         if ( !ai_inited )       {
1110                 // Do the first time initialization stuff here
1111                 int     rval;
1112
1113                 if ((rval = setjmp(parse_abort)) != 0) {
1114                         Error(LOCATION, "Error parsing 'ai.tbl'\r\nError code = %i.\r\n", rval);
1115                 } else {                        
1116                         parse_aitbl();                  
1117                 }
1118
1119                 ai_inited = 1;
1120         }
1121
1122         init_semirand();
1123         
1124         ai_level_init();
1125 }
1126
1127 // this inits the ai.  You should be able to call this between
1128 // levels to reset everything.
1129 void ai_level_init()
1130 {
1131         int i;
1132  
1133         // Do the stuff to reset all ai stuff here
1134         for (i=0; i<MAX_AI_INFO ; i++) {
1135                 Ai_info[i].shipnum = -1;
1136         }
1137         Ai_goal_signature = 0;
1138         Ai_friendly_rearm_timestamp = timestamp(-1);
1139         Ai_hostile_rearm_timestamp = timestamp(-1);
1140         Ai_neutral_rearm_timestamp = timestamp(-1);
1141         Ai_traitor_rearm_timestamp = timestamp(-1);
1142
1143         // clear out the stuff needed for AI firing powerful secondary weapons
1144         ai_init_secondary_info();
1145
1146         Ai_last_arrive_path=0;
1147 }
1148
1149 // BEGIN STEALTH
1150 // -----------------------------------------------------------------------------
1151 // Check if object is a stealth ship
1152 int is_object_stealth_ship(object* objp)
1153 {
1154         if (objp->type == OBJ_SHIP) {
1155                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_STEALTH) {
1156                         return 1;
1157                 }
1158         }
1159
1160         // not stealth ship
1161         return 0;
1162 }
1163
1164 // -----------------------------------------------------------------------------
1165 // Init necessary ai info for new stealth target
1166 void init_ai_stealth_info(ai_info *aip, object *stealth_objp)
1167 {
1168         Assert(is_object_stealth_ship(stealth_objp));
1169
1170         // set necessary ai info for new stealth target
1171         aip->stealth_last_pos = stealth_objp->pos;
1172         aip->stealth_velocity = stealth_objp->phys_info.vel;
1173         aip->stealth_last_visible_stamp = timestamp();
1174 }
1175
1176 // -----------------------------------------------------------------------------
1177 // Check whether Pl_objp can see a stealth ship object
1178 #define STEALTH_INVISIBLE                       0
1179 #define STEALTH_VISIBLE                         1
1180 #define STEALTH_FULLY_TARGETABLE        2
1181
1182 float get_skill_stealth_dist_scaler()
1183 {
1184         // return dist scaler based on skill level
1185         switch (Game_skill_level) {
1186         case 0: // very easy
1187                 return 0.65f;
1188
1189         case 1: // easy
1190                 return 0.9f;
1191
1192         case 2: // medium
1193                 return 1.0f;
1194
1195         case 3: // hard
1196                 return 1.1f;
1197
1198         case 4: // insane
1199                 return 1.3f;
1200
1201         default:
1202                 Int3();
1203         }
1204
1205         return 1.0f;
1206 }
1207
1208 float get_skill_stealth_dot_scaler()
1209 {
1210         // return multiplier on dot based on skill level
1211         switch (Game_skill_level) {
1212         case 0: // very easy
1213                 return 1.3f;
1214
1215         case 1: // easy
1216                 return 1.1f;
1217
1218         case 2: // medium
1219                 return 1.0f;
1220
1221         case 3: // hard
1222                 return 0.9f;
1223
1224         case 4: // insane
1225                 return 0.7f;
1226
1227         default:
1228                 Int3();
1229         }
1230
1231         return 1.0f;
1232 }
1233
1234 int ai_is_stealth_visible(object *viewer_objp, object *stealth_objp)
1235 {
1236         ship *shipp;
1237         vector vec_to_stealth;
1238         float dot_to_stealth, dist_to_stealth, max_stealth_dist;
1239
1240         Assert(stealth_objp->type == OBJ_SHIP);
1241         shipp = &Ships[stealth_objp->instance];
1242         Assert(viewer_objp->type == OBJ_SHIP);
1243
1244         // check if stealth ship
1245         Assert(Ship_info[shipp->ship_info_index].flags & SIF_STEALTH);
1246
1247         // check if in neb and below awac level for visible
1248         if ( !ship_is_visible_by_team(stealth_objp->instance, Ships[viewer_objp->instance].team) ) {
1249                 vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &viewer_objp->pos);
1250                 dist_to_stealth = vm_vec_mag_quick(&vec_to_stealth);
1251                 dot_to_stealth = vm_vec_dotprod(&viewer_objp->orient.v.fvec, &vec_to_stealth) / dist_to_stealth;
1252
1253                 // get max dist at which stealth is visible
1254                 max_stealth_dist = get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST;
1255
1256                 // now check if within view frustrum
1257                 float needed_dot_to_stealth;
1258                 if (dist_to_stealth < 100) {
1259                         needed_dot_to_stealth = 0.0f;
1260                 } else {
1261                         needed_dot_to_stealth = get_skill_stealth_dot_scaler() * float(STEALTH_VIEW_CONE_DOT) * (dist_to_stealth / max_stealth_dist);
1262                 }
1263                 if (dot_to_stealth > needed_dot_to_stealth) {
1264                         if (dist_to_stealth < max_stealth_dist) {
1265                                 return STEALTH_VISIBLE;
1266                         }
1267                 }
1268
1269                 // not within frustrum
1270                 return STEALTH_INVISIBLE;
1271         }
1272
1273         // visible by awacs level
1274         return STEALTH_FULLY_TARGETABLE;
1275 }
1276
1277 // END STEALTH
1278
1279 //      Compute dot product of direction vector and forward vector.
1280 //      Direction vector is vector from one object to other object.
1281 //      Forward vector is the forward vector of the ship.
1282 //      If from_dot == NULL, don't fill it in.
1283 float compute_dots(object *objp, object *other_objp, float *to_dot, float *from_dot)
1284 {
1285         vector  v2o;
1286         float           dist;
1287
1288         dist = vm_vec_normalized_dir(&v2o, &other_objp->pos, &objp->pos);
1289
1290         *to_dot = vm_vec_dot(&objp->orient.v.fvec, &v2o);
1291
1292         if (from_dot != NULL)
1293                 *from_dot = - vm_vec_dot(&other_objp->orient.v.fvec, &v2o);
1294
1295         return dist;
1296 }
1297
1298 // -----------------------------------------------------------------------------
1299 // update estimated stealth info
1300 // this is a "cheat" update
1301 // error increases with time not seen, true distance away, dot to enemey
1302 // this is done only if we can not see the stealth target
1303 // need to infer its position either by weapon fire pos or last know pos
1304 void update_ai_stealth_info_with_error(ai_info *aip/*, int no_error*/)
1305 {
1306         object *ship;
1307         object *stealth_objp;
1308         /*
1309         float error_time_mult, error_dist_mult, error_dot_mult, error_mult;
1310         float pos_error, vel_error;
1311         vector error_vec, vec_to_stealth;
1312         float dist_to_stealth, dot_to_stealth;
1313         float delta_time, delta_capped;
1314         */
1315
1316         // make sure I am targeting a stealth ship
1317         Assert( is_object_stealth_ship(&Objects[aip->target_objnum]) );
1318         stealth_objp = &Objects[aip->target_objnum];
1319
1320         // my_ship
1321         ship = &Objects[Ships[aip->shipnum].objnum];
1322
1323         // if update is due to weapon fire, get exact stealth position
1324 //      if (no_error) {
1325         aip->stealth_last_pos = stealth_objp->pos;
1326         aip->stealth_velocity = stealth_objp->phys_info.vel;
1327         aip->stealth_last_visible_stamp = timestamp();
1328 //              return;
1329 //      }
1330 /*
1331         // get time since last seen
1332         delta_time = 0.001f * (timestamp() - aip->stealth_last_visible_stamp);
1333
1334         // we don't want our "cheat" guess to more off than what we would get from extrapolating from last visible
1335         // only update if stealth info is "old"
1336         if ( (delta_time) < 0.5 ) {
1337                 return;
1338         }
1339
1340         // find vec_to_stealth and dist
1341         vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &ship->pos);
1342         dist_to_stealth = vm_vec_normalize_quick(&vec_to_stealth);
1343         dot_to_stealth = vm_vec_dotprod(&vec_to_stealth, &ship->orient.v.fvec);
1344
1345         // put cap on time
1346         delta_capped = delta_time;
1347         if (delta_time > 5.0) {
1348                 delta_capped = 5.0f;
1349         }
1350
1351         // erorr_time_mult (for 0-5) -> (1-6)
1352         error_time_mult = (1.0f + delta_capped);
1353
1354         // error_dot_mult (-1 to 1) -> (1-3)
1355         error_dot_mult = (2 - dot_to_stealth);
1356
1357         // error_dist_mult (0-1000+) -> (1-4)
1358         error_dist_mult = dist_to_stealth * 4.0f * 0.001f;
1359         if (error_dist_mult < 1) {
1360                 error_dist_mult = 1.0f;
1361         } else if (error_dist_mult > 4) {
1362                 error_dist_mult = 4.0f;
1363         }
1364
1365         // multiply error out
1366         error_mult = error_time_mult * error_dot_mult * error_dist_mult;
1367
1368         float base_pos_error = 10;
1369         float base_vel_error = 2;
1370
1371         // find the position and velocity error magnitude;
1372         pos_error = base_pos_error * error_mult;
1373         vel_error = base_vel_error * error_mult;
1374
1375         // get an error that changes slowly over time
1376         static_randvec( ((int)aip ^ (Missiontime >> 18)) & 7, &error_vec);
1377         vm_vec_zero(&error_vec);
1378
1379         // update pos and vel with error
1380         vm_vec_scale_add(&aip->stealth_velocity, &stealth_objp->phys_info.vel, &error_vec, vel_error);
1381
1382         // revise last "known" position to arrive at last pos with given error
1383         vm_vec_scale_add(&aip->stealth_last_pos, &stealth_objp->pos, &error_vec, pos_error);
1384         vm_vec_scale_add2(&aip->stealth_last_pos, &aip->stealth_velocity, -(0.001f * delta_time));
1385         */
1386 }
1387
1388 //      Update danger_weapon_objnum and signature in ai_info to say this weapon is to be avoided.
1389 void ai_update_danger_weapon(int attacked_objnum, int weapon_objnum)
1390 {
1391         object  *objp, *weapon_objp;
1392         ai_info *aip;
1393         float           old_dist, new_dist;
1394         float           old_dot, new_dot;
1395         object  *old_weapon_objp = NULL;
1396
1397         if ((attacked_objnum == -1) || (weapon_objnum == -1)) {
1398                 return;
1399         }
1400
1401         objp = &Objects[attacked_objnum];
1402
1403         // AL 2-24-98: If this isn't a ship, we don't need to worry about updating weapon_objnum (ie it would be
1404         //                                      an asteroid or bomb).
1405         if ( objp->type != OBJ_SHIP ) {
1406                 return;
1407         }
1408
1409         weapon_objp = &Objects[weapon_objnum];
1410
1411         aip = &Ai_info[Ships[objp->instance].ai_index];
1412
1413         // if my taraget is a stealth ship and is not visible
1414         if (aip->target_objnum >= 0) {
1415                 if ( is_object_stealth_ship(&Objects[aip->target_objnum]) ) {
1416                         if ( ai_is_stealth_visible(objp, &Objects[aip->target_objnum]) == STEALTH_INVISIBLE ) {
1417                                 // and the weapon is coming from that stealth ship
1418                                 if (weapon_objp->parent == aip->target_objnum) {
1419                                         // update my position estimate for stealth ship
1420                                         update_ai_stealth_info_with_error(aip/*, 1*/);
1421                                 }
1422                         }
1423                 }
1424         }
1425
1426         if (aip->danger_weapon_objnum != -1) {
1427                 old_weapon_objp = &Objects[aip->danger_weapon_objnum];
1428                 if ((old_weapon_objp->type == OBJ_WEAPON) && (old_weapon_objp->signature == aip->danger_weapon_signature)) {
1429                         ;
1430                 } else {
1431                         aip->danger_weapon_objnum = -1;
1432                 }
1433         }
1434
1435         new_dist = compute_dots(weapon_objp, objp, &new_dot, NULL);
1436
1437         if (aip->danger_weapon_objnum == -1) {
1438                 if (new_dist < 1500.0f) {
1439                         if (new_dot > 0.5f) {
1440                                 aip->danger_weapon_objnum = weapon_objnum;
1441                                 aip->danger_weapon_signature = weapon_objp->signature;
1442                         }
1443                 }
1444         } else {
1445                 Assert(old_weapon_objp != NULL);
1446                 old_dist = compute_dots(old_weapon_objp, objp, &old_dot, NULL);
1447         
1448                 if (old_dot < 0.5f) {
1449                         aip->danger_weapon_objnum = -1;
1450                         old_dist = 9999.9f;
1451                 }
1452
1453                 if ((new_dot > 0.5f) && (new_dot > old_dot-0.01f)) {
1454                         if (new_dist < old_dist) {
1455                                 aip->danger_weapon_objnum = weapon_objnum;
1456                                 aip->danger_weapon_signature = weapon_objp->signature;
1457                         }
1458                 }
1459         }
1460 }
1461
1462 //      If rvec != NULL, use it to match bank by calling vm_matrix_interpolate.
1463 //      (rvec defaults to NULL)
1464 void ai_turn_towards_vector(vector *dest, object *objp, 
1465                                                                          float frametime, float turn_time, vector *slide_vec, vector *rel_pos, float bank_override, int flags, vector *rvec)
1466 {
1467         //matrix        goal_orient;
1468         matrix  curr_orient;
1469         vector  vel_in, vel_out, desired_fvec, src;
1470         float           delta_time;
1471         physics_info    *pip;
1472         vector  vel_limit, acc_limit;
1473         float           delta_bank;
1474
1475         //      Don't allow a ship to turn if it has no engine strength.
1476         // AL 3-12-98: objp may not always be a ship!
1477         if ( objp->type == OBJ_SHIP ) {
1478                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f)
1479                         return;
1480         }
1481                         
1482         //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));
1483         pip = &objp->phys_info;
1484
1485         vel_in = pip->rotvel;
1486         curr_orient = objp->orient;
1487         delta_time = flFrametime;
1488
1489         Assert(turn_time > 0.0f);
1490         
1491         //      Scale turn_time based on skill level and team.
1492         if (!(flags & AITTV_FAST)){
1493                 if (objp->type == OBJ_SHIP){
1494                         if (Ships[objp->instance].team != Ships[Player_obj->instance].team){
1495                                 turn_time *= Turn_time_skill_level_scale[Game_skill_level];
1496                         }
1497                 }
1498         }
1499
1500         //      Set max turn rate.
1501         vel_limit.xyz.x = 2*PI/turn_time;
1502         vel_limit.xyz.y = 2*PI/turn_time;
1503         vel_limit.xyz.z = 2*PI/turn_time;
1504
1505         //      Set rate at which ship can accelerate to its rotational velocity.
1506         //      For now, weapons just go much faster.
1507         acc_limit = vel_limit;
1508         if (objp->type == OBJ_WEAPON)
1509                 vm_vec_scale(&acc_limit, 8.0f);
1510
1511         src = objp->pos;
1512
1513         if (rel_pos != NULL) {
1514                 vector  gun_point;
1515                 vm_vec_unrotate(&gun_point, rel_pos, &objp->orient);
1516                 vm_vec_add2(&src, &gun_point);
1517         }
1518
1519         vm_vec_normalized_dir(&desired_fvec, dest, &src);
1520
1521         //      Since ship isn't necessarily moving in the direction it's pointing, sometimes it's better
1522         //      to be moving towards goal rather than just pointing.  So, if slide_vec is !NULL, try to
1523         //      make ship move towards goal, not point at goal.
1524         if (slide_vec != NULL) {
1525                 vm_vec_add2(&desired_fvec, slide_vec);
1526                 vm_vec_normalize(&desired_fvec);
1527         }
1528
1529         //      Should be more general case here.  Currently, anything that is not a weapon will bank when it turns.
1530         if (objp->type == OBJ_WEAPON)
1531                 delta_bank = 0.0f;
1532         else if ((bank_override) && (Ships[objp->instance].team & opposing_team_mask(Player_ship->team))) {     //      Theoretically, this will only happen for Shivans.
1533                 delta_bank = bank_override;
1534                 //nprintf(("AI", "%i: %7.3f\n", Framecount, bank_override));
1535         } else {
1536                 delta_bank = vm_vec_dot(&curr_orient.v.rvec, &objp->last_orient.v.rvec);
1537                 delta_bank = 100.0f * (1.0f - delta_bank);
1538                 if (vm_vec_dot(&objp->last_orient.v.fvec, &objp->orient.v.rvec) < 0.0f)
1539                         delta_bank = -delta_bank;
1540
1541                 //nprintf(("AI", "%s: Frame %i: delta bank = %7.3f\n", Ships[objp->instance].ship_name, Framecount, delta_bank));
1542         }
1543
1544         //      Dave Andsager: The non-indented lines here are debug code to help you track down the problem in the physics
1545         //      that is causing ships to inexplicably rotate very far.  If you hit the Int3(), set the next statement to be
1546         //      the one marked "HERE".  (Do this clicking the cursor there, then right clicking.  Choose the right option.)
1547         //      This will allow you to rerun vm_forward_interpolate() with the values that caused the error.
1548         //      Note, you'll need to enable the Int3() about ten lines below.
1549 #ifndef NDEBUG
1550 vector tvec = objp->orient.v.fvec;
1551 vector  vel_in_copy;
1552 matrix  objp_orient_copy;
1553
1554 vel_in_copy = vel_in;
1555 objp_orient_copy = objp->orient;
1556
1557 vel_in = vel_in_copy;   //      HERE
1558 objp->orient = objp_orient_copy;
1559 #endif
1560         if (rvec != NULL) {
1561                 matrix  out_orient, goal_orient;
1562
1563                 vm_vector_2_matrix(&goal_orient, &desired_fvec, NULL, rvec);
1564                 vm_matrix_interpolate(&goal_orient, &curr_orient, &vel_in, delta_time, &out_orient, &vel_out, &vel_limit, &acc_limit);
1565                 objp->orient = out_orient;
1566         } else {
1567                 vm_forward_interpolate(&desired_fvec, &curr_orient, &vel_in, delta_time, delta_bank, &objp->orient, &vel_out, &vel_limit, &acc_limit);
1568         }
1569 #ifndef NDEBUG
1570 if (!((objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE))) {
1571         if (delta_time < 0.25f && vm_vec_dot(&objp->orient.v.fvec, &tvec) < 0.1f)
1572                 Int3(); //      Get Andsager.  A ship has turned too far in one frame.
1573 }
1574 #endif
1575
1576         pip->rotvel = vel_out;
1577 }
1578
1579 void init_ship_info()
1580 {
1581         int     i;
1582
1583         if (Ship_info_inited)
1584                 return;
1585
1586         for (i=0; i<MAX_SHIP_TYPES; i++) {
1587                 Ship_info[i].min_speed = - Ship_info[i].max_rear_vel;
1588                 Ship_info[i].max_accel = Ship_info[i].max_vel.xyz.z;
1589         }
1590
1591         Ship_info_inited = 1;
1592
1593 }
1594
1595 //      Set aip->target_objnum to objnum
1596 //      Update aip->previous_target_objnum.
1597 //      If new target (objnum) is different than old target, reset target_time.
1598 int set_target_objnum(ai_info *aip, int objnum)
1599 {
1600 /*
1601         char    old_name[32], new_name[32];
1602
1603         if (!timestamp_elapsed(aip->ok_to_target_timestamp))
1604                 return aip->target_objnum;
1605
1606         if (Player_ship && (Ships[aip->shipnum].team == Player_ship->team)) {
1607                 if (aip->target_objnum == -1)
1608                         strcpy(old_name, "none");
1609                 else
1610                         strcpy(old_name, Ships[Objects[aip->target_objnum].instance].ship_name);
1611
1612                 if (objnum == -1)
1613                         strcpy(new_name, "none");
1614                 else
1615                         strcpy(new_name, Ships[Objects[objnum].instance].ship_name);
1616
1617                 nprintf(("AI", "Ship %s changing target from %s to %s\n", Ships[aip->shipnum].ship_name, old_name, new_name));
1618         }
1619 */
1620
1621         // AL 2-25-97: Ensure that a protected ship isn't being set as a target (for non-players only)
1622         /*
1623         if ( objnum >= 0 ) {
1624                 if ( !(Objects[Ships[aip->shipnum].objnum].flags & OF_PLAYER_SHIP) ) {
1625                         if ( Objects[objnum].flags & OF_PROTECTED ) {
1626                                 // AL 2-26-97: removing Int3() until issue with setting OF_PROTECTED in ai_set_attack_subsystem()
1627                                 //Int3();                                                               // this should not happen
1628                                 return aip->target_objnum;              // don't change targets
1629                         }
1630                 }
1631         }
1632         */
1633
1634         if ((aip != Player_ai) && (!timestamp_elapsed(aip->ok_to_target_timestamp))) {
1635                 return aip->target_objnum;
1636         }
1637
1638         if (aip->target_objnum == objnum) {
1639                 aip->previous_target_objnum = aip->target_objnum;
1640         } else {
1641                 aip->previous_target_objnum = aip->target_objnum;
1642
1643                 // ignore this assert if a multiplayer observer
1644                 if((Game_mode & GM_MULTIPLAYER) && (aip == Player_ai) && (Player_obj->type == OBJ_OBSERVER)){
1645                 } else {
1646                         Assert(objnum != Ships[aip->shipnum].objnum);   //      make sure not targeting self
1647                 }
1648
1649                 // if stealth target, init ai_info for stealth
1650                 if ( (objnum > 0) && is_object_stealth_ship(&Objects[objnum]) ) {
1651                         init_ai_stealth_info(aip, &Objects[objnum]);
1652                 }
1653
1654                 aip->target_objnum = objnum;
1655                 aip->target_time = 0.0f;
1656                 aip->target_signature = Objects[objnum].signature;
1657                 // clear targeted subsystem
1658                 set_targeted_subsys(aip, NULL, -1);
1659         }
1660         
1661         return aip->target_objnum;
1662 }
1663
1664 int ai_select_primary_weapon(object *objp, object *other_objp, int flags);
1665
1666 //      Make new_subsys the targeted subsystem of ship *aip.
1667 ship_subsys *set_targeted_subsys(ai_info *aip, ship_subsys *new_subsys, int parent_objnum)
1668 {
1669         Assert(aip != NULL);
1670
1671         aip->last_subsys_target = aip->targeted_subsys;
1672         aip->targeted_subsys = new_subsys;
1673         aip->targeted_subsys_parent = parent_objnum;
1674
1675         if ( new_subsys ) {
1676                 // Make new_subsys target
1677                 if (new_subsys->system_info->type == SUBSYSTEM_ENGINE) {
1678                         if ( aip != Player_ai ) {
1679                                 ai_select_primary_weapon(&Objects[Ships[aip->shipnum].objnum], &Objects[parent_objnum], WIF_PUNCTURE);
1680                                 ship_primary_changed(&Ships[aip->shipnum]);     // AL: maybe send multiplayer information when AI ship changes primaries
1681                         }
1682                 }
1683
1684                 if ( aip == Player_ai ) {
1685                         hud_lock_reset(0.5f);
1686                 }
1687
1688         } else {
1689                 // Cleanup any subsys path information if it exists
1690                 ai_big_subsys_path_cleanup(aip);
1691         }
1692         
1693         return aip->targeted_subsys;
1694 }                                                                                         
1695
1696 // called to init the data for single ai object.  At this point,
1697 // the ship and the object and the ai_info are are correctly
1698 // linked together. Ai_info[ai_index].shipnum is the only valid field 
1699 // in ai_info.
1700 //      This is called right when the object is parsed, so you can't assume much
1701 //      has been initialized.  For example, wings, waypoints, goals are probably
1702 //      not yet loaded. --MK, 10/8/96
1703 void ai_object_init(object * obj, int ai_index)
1704 {
1705         ai_info *aip;
1706         Assert(ai_index >= 0 && ai_index < MAX_AI_INFO);
1707
1708         aip = &Ai_info[ai_index];
1709
1710         aip->type = 0;          //      0 means not in use.
1711         aip->wing = -1;         //      Member of what wing? -1 means none.
1712         aip->ai_class = Ship_info[Ships[obj->instance].ship_info_index].ai_class;
1713         aip->behavior = AIM_NONE;
1714 }
1715
1716 //      If *aip is docked, set max acceleration to A->mass/(A->mass + B->mass) where A is *aip and B is dock object
1717 void adjust_accel_for_docking(ai_info *aip)
1718 {
1719         if (aip->dock_objnum != -1) {
1720                 object  *obj2p = &Objects[aip->dock_objnum];
1721                 object  *obj1p;
1722
1723                 obj1p = &Objects[Ships[aip->shipnum].objnum];
1724
1725                 if (obj2p->signature == aip->dock_signature) {
1726                         float   ratio;
1727
1728                         ratio = obj1p->phys_info.mass / (obj1p->phys_info.mass + obj2p->phys_info.mass);
1729
1730                         // put cap on how much ship can slow down
1731 #ifdef MAKE_FS1
1732                         // FS1 can go slower, perhaps down to 0, but I'll cap it at .25 just in case
1733                         if (ratio < 0.25f) {
1734                                 ratio = 0.25f;
1735                         }
1736 #else
1737                         if (ratio < 0.8) {
1738                                 ratio = 0.8f;
1739                         }
1740 #endif
1741
1742                         if (AI_ci.forward > ratio) {
1743                                 AI_ci.forward = ratio;
1744                         }
1745                 }
1746         }
1747 }
1748
1749 // -------------------------------------------------------------------
1750 void accelerate_ship(ai_info *aip, float accel)
1751 {
1752         aip->prev_accel = accel;
1753         AI_ci.forward = accel;
1754         adjust_accel_for_docking(aip);
1755 }
1756
1757 //      --------------------------------------------------------------------------
1758 void change_acceleration(ai_info *aip, float delta_accel)
1759 {
1760         float   new_accel;
1761
1762         if (delta_accel < 0.0f) {
1763                 if (aip->prev_accel > 0.0f)
1764                         aip->prev_accel = 0.0f;
1765         } else if (aip->prev_accel < 0.0f)
1766                 aip->prev_accel = 0.0f;
1767
1768         new_accel = aip->prev_accel + delta_accel * flFrametime;
1769
1770         if (new_accel > 1.0f)
1771                 new_accel = 1.0f;
1772         else if (new_accel < -1.0f)
1773                 new_accel = -1.0f;
1774         
1775         aip->prev_accel = new_accel;
1776
1777         AI_ci.forward = new_accel;
1778         adjust_accel_for_docking(aip);
1779 }
1780
1781 void set_accel_for_target_speed(object *objp, float tspeed)
1782 {
1783         float   max_speed;
1784         ai_info *aip;
1785
1786         aip = &Ai_info[Ships[objp->instance].ai_index];
1787
1788         max_speed = Ships[objp->instance].current_max_speed;
1789
1790         AI_ci.forward = tspeed/max_speed;
1791         aip->prev_accel = AI_ci.forward;
1792
1793         adjust_accel_for_docking(aip);
1794 }
1795
1796 //      Stuff perim_point with a point on the perimeter of the sphere defined by object *objp
1797 //      on the vector from the center of *objp through the point *vp.
1798 void project_point_to_perimeter(vector *perim_point, vector *pos, float radius, vector *vp)
1799 {
1800         vector  v1;
1801         float           mag;
1802
1803         vm_vec_sub(&v1, vp, pos);
1804         mag = vm_vec_mag(&v1);
1805
1806         if (mag == 0.0f) {
1807                 Warning(LOCATION, "projectable point is at center of sphere.");
1808                 (void) vm_vec_make(&v1, 0.0f, radius, 0.0f);
1809         } else {
1810                 vm_vec_normalize(&v1);
1811                 vm_vec_scale(&v1, 1.1f * radius + 10.0f);
1812         }
1813
1814         vm_vec_add2(&v1, pos);
1815         *perim_point = v1;
1816 }
1817
1818 //      Stuff tan1 with tangent point on sphere.  tan1 is point nearer to *p1
1819 //      *p0 is point through which tangents pass.
1820 //      *centerp is center of sphere.
1821 //      *p1 is another point in space to define the plane in which tan1, tan2 reside.
1822 //      radius is the radius of the sphere.
1823 //      Note, this is a very approximate function just for AI.
1824 //      Note also: On 12/26/96, p1 is used to define the plane perpendicular to that which
1825 //      contains the tangent point.
1826 void get_tangent_point(vector *tan1, vector *p0, vector *centerp, vector *p1, float radius)
1827 {
1828         vector  dest_vec, v2c, perp_vec, temp_vec, v2;
1829         float           dist, ratio;
1830
1831         //      Detect condition of point inside sphere.
1832         if (vm_vec_dist(p0, centerp) < radius)
1833                 project_point_to_perimeter(tan1, centerp, radius, p0);
1834         else {
1835                 vm_vec_normalized_dir(&v2c, centerp, p0);
1836
1837                 //      Compute perpendicular vector using p0, centerp, p1
1838                 vm_vec_normal(&temp_vec, p0, centerp, p1);
1839                 vm_vec_sub(&v2, centerp, p0);
1840                 vm_vec_cross(&perp_vec, &temp_vec, &v2);
1841
1842                 vm_vec_normalize(&perp_vec);
1843
1844                 dist = vm_vec_dist_quick(p0, centerp);
1845                 ratio = dist / radius;
1846
1847                 if (ratio < 2.0f)
1848                         vm_vec_scale_add(&dest_vec, &perp_vec, &v2c, ratio-1.0f);
1849                 else
1850                         vm_vec_scale_add(&dest_vec, &v2c, &perp_vec, (1.0f + 1.0f/ratio));
1851
1852                 vm_vec_scale_add(tan1, p0, &dest_vec, dist + radius);
1853         }
1854 }
1855
1856 //      --------------------------------------------------------------------------
1857 //      Given an object and a point, turn towards the point, resulting in
1858 // approach behavior.
1859 void turn_towards_point(object *objp, vector *point, vector *slide_vec, float bank_override)
1860 {
1861         ai_info *aip;
1862         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1863         
1864         // check if in formation and if not leader, don't change rotvel.xyz.z (bank to match leader elsewhere)
1865         if (aip->ai_flags & AIF_FORMATION) {
1866                 if (&Objects[aip->goal_objnum] != objp) {
1867                         float rotvel_z = objp->phys_info.rotvel.xyz.z;
1868                         ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1869                         objp->phys_info.rotvel.xyz.z = rotvel_z;
1870                 }
1871         } else {
1872                 // normal turn
1873                 ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1874         }
1875 }
1876
1877 //      --------------------------------------------------------------------------
1878 //      Given an object and a point, turn away from the point, resulting in avoidance behavior.
1879 //      Note: Turn away at full speed, not scaled down by skill level.
1880 void turn_away_from_point(object *objp, vector *point, float bank_override)
1881 {
1882         vector  opposite_point;
1883
1884         vm_vec_sub(&opposite_point, &objp->pos, point);
1885         vm_vec_add2(&opposite_point, &objp->pos);
1886
1887         ai_turn_towards_vector(&opposite_point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, NULL, NULL, bank_override, AITTV_FAST);
1888 }
1889
1890
1891 //      --------------------------------------------------------------------------
1892 //      Given an object and a point, turn tangent to the point, resulting in
1893 // a circling behavior.
1894 //      Make object *objp turn around the point *point with a radius of radius.
1895 //      Note that this isn't the same as following a circle of radius radius with
1896 //      center *point, but it should be adequate.
1897 //      Note that if you want to circle an object without hitting it, you should use
1898 //      about twice that object's radius for radius, else you'll certainly bump into it.
1899 //      Return dot product to goal point.
1900 float turn_towards_tangent(object *objp, vector *point, float radius)
1901 {
1902         vector  vec_to_point;
1903         vector  goal_point;
1904         vector  perp_point;                             //      point radius away from *point on vector to objp->pos
1905         vector  up_vec, perp_vec;
1906
1907         vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1908         vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.v.fvec);
1909         vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1910
1911         vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1912         if (vm_vec_dot(&objp->orient.v.fvec, &perp_vec) > 0.0f) {
1913                 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, radius);
1914         } else {
1915                 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, -radius);
1916         }
1917
1918 //      Ai_info[Ships[objp->instance].ai_index].goal_point = goal_point;
1919         turn_towards_point(objp, &goal_point, NULL, 0.0f);
1920
1921         vector  v2g;
1922
1923         vm_vec_normalized_dir(&v2g, &goal_point, &objp->pos);
1924         return vm_vec_dot(&objp->orient.v.fvec, &v2g);
1925 }
1926
1927 float turn_toward_tangent_with_axis(object *objp, object *center_objp, float radius)
1928 {
1929         vector r_vec, theta_vec;
1930         vector center_vec, vec_on_cylinder, sph_r_vec;
1931         float center_obj_z;
1932
1933         // find closest z of center objp
1934         vm_vec_sub(&sph_r_vec, &objp->pos, &center_objp->pos);
1935         center_obj_z = vm_vec_dotprod(&sph_r_vec, &center_objp->orient.v.fvec);
1936
1937         // find pt on axis with closest z
1938         vm_vec_scale_add(&center_vec, &center_objp->pos, &center_objp->orient.v.fvec, center_obj_z);
1939
1940         // get r_vec
1941         vm_vec_sub(&r_vec, &objp->pos, &center_vec);
1942 //      float r_mag = vm_vec_normalize_quick(&r_vec);
1943 //      mprintf(("cur_r: %.1f, desired_r: %.1f\n", r_mag, radius));
1944         Assert( (vm_vec_dotprod(&r_vec, &center_objp->orient.v.fvec) < 0.0001));
1945
1946         // get theta vec - perp to r_vec and z_vec
1947         vm_vec_crossprod(&theta_vec, &center_objp->orient.v.fvec, &r_vec);
1948
1949 #ifndef NDEBUG
1950         float mag = vm_vec_normalize(&theta_vec);
1951         Assert(mag > 0.9999 && mag < 1.0001);
1952 #endif
1953
1954         vector temp;
1955         vm_vec_crossprod(&temp, &r_vec, &theta_vec);
1956
1957 #ifndef NDEBUG
1958         float dot = vm_vec_dotprod(&temp, &center_objp->orient.v.fvec);
1959         Assert( dot >0.9999 && dot < 1.0001);
1960 #endif
1961
1962         // find pt on clylinder with closest z
1963         vm_vec_scale_add(&vec_on_cylinder, &center_vec, &r_vec, radius);
1964
1965         vector goal_pt, v2g;
1966         vm_vec_scale_add(&goal_pt, &vec_on_cylinder, &theta_vec, radius);
1967
1968 //      Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pt;
1969         turn_towards_point(objp, &goal_pt, NULL, 0.0f);
1970
1971         vm_vec_normalized_dir(&v2g, &goal_pt, &objp->pos);
1972         return vm_vec_dot(&objp->orient.v.fvec, &v2g);
1973 }
1974
1975 //      Returns a point radius units away from *point that *objp should turn towards to orbit *point
1976 void get_tangent_point(vector *goal_point, object *objp, vector *point, float radius)
1977 {
1978         vector  vec_to_point;
1979         vector  perp_point;                             //      point radius away from *point on vector to objp->pos
1980         vector  up_vec, perp_vec;
1981
1982         vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1983         vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.v.fvec);
1984         vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1985         vm_vec_normalize(&perp_vec);
1986
1987         vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1988
1989         if (vm_vec_dot(&objp->orient.v.fvec, &perp_vec) > 0.0f) {
1990                 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, radius);
1991         } else {
1992                 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, -radius);
1993         }
1994 }
1995
1996 int     Player_attacking_enabled = 1;
1997
1998 // -----------------------------------------------------------------------------
1999 // Determine whether an object is targetable within a nebula
2000 int object_is_targetable(object *target, ship *viewer)
2001 {
2002         int stealth_ship = 0;
2003
2004         // if target is ship, check if visible by team
2005         if (target->type == OBJ_SHIP) {
2006                 stealth_ship = (Ship_info[Ships[target->instance].ship_info_index].flags & SIF_STEALTH);
2007                 if ( ship_is_visible_by_team(target->instance, viewer->team) == 1) {
2008                         return 1;
2009                 }
2010         }
2011
2012         // for AI partially targetable works as fully targetable, except for stealth ship
2013         if (stealth_ship) {
2014                 // if not team targetable, check if within frustrum
2015                 if ( ai_is_stealth_visible(&Objects[viewer->objnum], target) == STEALTH_VISIBLE ) {
2016                         return 1;
2017                 } else {
2018                         return 0;
2019                 }
2020         }
2021
2022         // if not fully targetable by team, check awacs level with viewer
2023         // allow targeting even if only only partially targetable to player
2024         float radar_return = awacs_get_level(target, viewer);
2025         if ( radar_return > 0.4 ) {
2026                 return 1;
2027         } else {
2028                 return 0;
2029         }
2030 }
2031
2032 //      Return number of enemies attacking object objnum
2033 //
2034 // AL 10.26.97: Also include turrets on large ships when couting enemies attacking
2035 int num_enemies_attacking(int objnum)
2036 {
2037         object          *objp;
2038         ship                    *sp;
2039         ship_subsys     *ssp;
2040         ship_obj                *so;
2041         int                     count;
2042
2043         count = 0;
2044
2045         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2046                 objp = &Objects[so->objnum];
2047                 Assert(objp->instance != -1);
2048                 sp = &Ships[objp->instance];
2049
2050                 if (Ai_info[sp->ai_index].target_objnum == objnum)
2051                         count++;
2052
2053                 // consider turrets that may be attacking objnum (but only turrets on SIF_BIG_SHIP ships)
2054                 if ( Ship_info[sp->ship_info_index].flags & SIF_BIG_SHIP ) {
2055
2056                         // loop through all the subsystems, check if turret has objnum as a target
2057                         ssp = GET_FIRST(&sp->subsys_list);
2058                         while ( ssp != END_OF_LIST( &sp->subsys_list ) ) {
2059
2060                                 if ( ssp->system_info->type == SUBSYSTEM_TURRET ) {
2061                                         if ( (ssp->turret_enemy_objnum == objnum) && (ssp->current_hits > 0) ) {
2062                                                 count++;
2063                                         }
2064                                 }
2065                                 ssp = GET_NEXT( ssp );
2066                         } // end while
2067                 }
2068         }
2069
2070         return count;
2071 }
2072
2073 //      Get the team to fire on given an object.
2074 int get_enemy_team_mask(int objnum)
2075 {
2076         int     my_team, enemy_team_mask;
2077
2078         my_team = Ships[Objects[objnum].instance].team;
2079
2080         if (Mission_all_attack) {
2081                 //      All teams attack all teams.
2082                 switch (my_team) {
2083                 case TEAM_FRIENDLY:
2084                         enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2085                         break;
2086                 case TEAM_HOSTILE:
2087                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2088                         break;
2089                 case TEAM_NEUTRAL:
2090                         enemy_team_mask = TEAM_FRIENDLY | TEAM_HOSTILE | TEAM_TRAITOR;
2091                         break;
2092                 case TEAM_UNKNOWN:
2093                         enemy_team_mask = TEAM_HOSTILE;
2094                         break;
2095                 case TEAM_TRAITOR:
2096                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2097                         break;
2098                 default:
2099                         enemy_team_mask = TEAM_HOSTILE;
2100                         Int3();                 //      Illegal value for team!
2101                         break;
2102                 }
2103         } else {
2104                 switch (my_team) {
2105                 case TEAM_FRIENDLY:
2106                         enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2107                         break;
2108                 case TEAM_HOSTILE:
2109                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2110                         break;
2111                 case TEAM_NEUTRAL:
2112                         enemy_team_mask = TEAM_FRIENDLY | TEAM_TRAITOR;
2113                         break;
2114                 case TEAM_UNKNOWN:
2115                         enemy_team_mask = TEAM_HOSTILE;
2116                         break;
2117                 case TEAM_TRAITOR:
2118                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2119                         break;
2120                 default:
2121                         enemy_team_mask = TEAM_HOSTILE;
2122                         Int3();                 //      Illegal value for team!
2123                         break;
2124                 }
2125         }
2126
2127         return enemy_team_mask;
2128 }
2129
2130 //      Scan all the ships in *objp's wing.
2131 //      Return the lowest maximum speed of a ship in the wing.
2132 //      Current maximum speed (based on energy settings) is shipp->current_max_speed
2133 float get_wing_lowest_max_speed(object *objp)
2134 {
2135         ship            *shipp;
2136         ai_info *aip;
2137         float           lowest_max_speed;
2138         int             wingnum;
2139         object  *o;
2140         ship_obj        *so;
2141
2142         Assert(objp->type == OBJ_SHIP);
2143         Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
2144         shipp = &Ships[objp->instance];
2145         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
2146         aip = &Ai_info[shipp->ai_index];
2147
2148         wingnum = aip->wing;
2149
2150         lowest_max_speed = shipp->current_max_speed;
2151
2152         if ( wingnum == -1 )
2153                 return lowest_max_speed;
2154
2155         Assert(wingnum >= 0);
2156
2157         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2158                 o = &Objects[so->objnum];
2159                 ship    *oshipp = &Ships[o->instance];
2160                 ai_info *oaip = &Ai_info[oshipp->ai_index];
2161
2162                 if ((oaip->mode == AIM_WAYPOINTS) && (oaip->wing == wingnum)) {
2163                         //      Note: If a ship in the wing has a super low max speed, probably its engines are disabled.  So, fly along and
2164                         //      ignore the poor guy.
2165                         float   cur_max = oshipp->current_max_speed;
2166
2167                         if (oaip->ai_flags & AIF_DOCKED) {
2168                                 if (oaip->dock_objnum > -1)
2169                                         if (Objects[oaip->dock_objnum].type == OBJ_SHIP) 
2170                                                 cur_max *= o->phys_info.mass/(o->phys_info.mass + Objects[oaip->dock_objnum].phys_info.mass);
2171                         }
2172                                                         
2173                         if ((oshipp->current_max_speed > 5.0f) && (cur_max < lowest_max_speed)) {
2174                                 lowest_max_speed = cur_max;
2175                         }
2176                 }
2177         }
2178
2179         return lowest_max_speed;
2180 }
2181
2182 /*
2183 //      Tell everyone to ignore object objnum.
2184 void set_global_ignore_object(int objnum)
2185 {
2186         int     i;
2187
2188         Assert(Objects[objnum].type == OBJ_SHIP);
2189
2190         nprintf(("AI", "Telling everyone to ignore object %s\n", Ships[Objects[objnum].instance].ship_name));
2191
2192         for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2193                 if (Ignore_objects[i].objnum == -1) {
2194                         Ignore_objects[i].objnum = objnum;
2195                         Ignore_objects[i].signature = Objects[objnum].signature;
2196                         break;
2197                 }
2198         }
2199
2200         if (i == MAX_IGNORE_OBJECTS) {
2201                 //      Couldn't find a free slot, but maybe one of these objects has died.
2202                 for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2203                         int     o = Ignore_objects[i].objnum;
2204                         if (Objects[o].type != OBJ_SHIP)
2205                                 break;          //      Not a ship, so use this slot.
2206                         if (Objects[o].signature != Ignore_objects[i].signature)
2207                                 break;          //      Signatures don't match, so use this slot.
2208                 }
2209
2210                 if (i != MAX_IGNORE_OBJECTS) {
2211                         Ignore_objects[i].objnum = objnum;
2212                         Ignore_objects[i].signature = Objects[objnum].signature;
2213                 } else {
2214                         nprintf(("Warning", "Ignore_objects buffer full.  Stealing a slot to ignore object #%i\n"));
2215                         Int3();
2216
2217                         int     r;
2218
2219                         r = objnum % MAX_IGNORE_OBJECTS;
2220
2221                         Ignore_objects[r].objnum = objnum;
2222                         Ignore_objects[r].signature = Objects[objnum].signature;
2223                 }
2224         }
2225 }
2226
2227 */
2228
2229 //      Determine if object objnum is supposed to be ignored by object with ai_info *aip.
2230 //      Return:
2231 //              TRUE    if objnum is aip->ignore_objnum (and signatures match)
2232 //                              or objnum is in ignore wing
2233 //              FALSE   otherwise
2234 int is_ignore_object(ai_info *aip, int objnum)
2235 {
2236
2237 /*      //      First, scan all objects in global array of objects to be ignored.
2238         for (int i=0; i<MAX_IGNORE_OBJECTS; i++)
2239                 if (Ignore_objects[i].objnum != -1)
2240                         if (objnum == Ignore_objects[i].objnum)
2241                                 if (Objects[Ignore_objects[i].objnum].signature == Ignore_objects[i].signature)
2242                                         return 1;
2243 */
2244
2245         //      Didn't find in global list.  Now check 
2246         if (aip->ignore_objnum == UNUSED_OBJNUM)
2247                 return 0;                                                                       //      Not ignoring anything.
2248         else if (aip->ignore_objnum >= 0) {             //      This means it's ignoring an object, not a wing.
2249                 if (aip->ignore_objnum == objnum) {
2250                         if (Objects[aip->ignore_objnum].signature == aip->ignore_signature) {
2251                                 return 1;
2252                         } else {
2253                                 aip->ignore_objnum = UNUSED_OBJNUM;
2254                                 return 0;
2255                         }
2256                 } else {
2257                         return 0;
2258                 }
2259         } else {                                                                                        //      Ignoring a wing.
2260                 Int3(); // Should never happen.  I thought I removed this behavior! -- MK, 5/17/98
2261                 return 0;
2262 /*              int     ignore_wingnum = -(aip->ignore_objnum + 1);
2263
2264                 Assert(ignore_wingnum < MAX_WINGS);
2265                 Assert(aip->shipnum >= 0);
2266                 return (Ships[Objects[objnum].instance].wingnum == ignore_wingnum);
2267 */      }
2268 }
2269
2270 // -----------------------------------------------------------------------------
2271
2272 // given a ship with bounding box and a point, find the closest point on the bbox
2273 int get_nearest_bbox_point(object *ship_obj, vector *start, vector *box_pt)
2274 {
2275         vector temp, rf_start;
2276         polymodel *pm;
2277         pm = model_get(Ship_info[Ships[ship_obj->instance].ship_info_index].modelnum);
2278
2279         // get start in ship rf
2280         vm_vec_sub(&temp, start, &ship_obj->pos);
2281         vm_vec_rotate(&rf_start, &temp, &ship_obj->orient);
2282
2283         // find box_pt
2284         int inside = project_point_onto_bbox(&pm->mins, &pm->maxs, &rf_start, &temp);
2285
2286         // get box_pt in world rf
2287         vm_vec_unrotate(box_pt, &temp, &ship_obj->orient);
2288         vm_vec_add2(box_pt, &ship_obj->pos);
2289
2290         return inside;
2291 }
2292
2293
2294 typedef struct eval_nearest_objnum {
2295         int     objnum;
2296         object *trial_objp;
2297         int     enemy_team_mask;
2298         int     enemy_wing;
2299         float   range;
2300         int     max_attackers;
2301         int     nearest_objnum;
2302         float   nearest_dist;
2303         int     check_danger_weapon_objnum;
2304 } eval_nearest_objnum;
2305
2306
2307 void evaluate_object_as_nearest_objnum(eval_nearest_objnum *eno)
2308 {
2309         ai_info *aip;
2310         ship_subsys     *attacking_subsystem;
2311
2312         aip = &Ai_info[Ships[Objects[eno->objnum].instance].ai_index];
2313
2314         attacking_subsystem = aip->targeted_subsys;
2315
2316         if ((attacking_subsystem != NULL) || !(eno->trial_objp->flags & OF_PROTECTED)) {
2317                 if ( OBJ_INDEX(eno->trial_objp) != eno->objnum ) {
2318 #ifndef NDEBUG
2319                         if (!Player_attacking_enabled && (eno->trial_objp == Player_obj))
2320                                 return;
2321 #endif
2322                         //      If only supposed to attack ship in a specific wing, don't attack other ships.
2323                         if ((eno->enemy_wing != -1) && (Ships[eno->trial_objp->instance].wingnum != eno->enemy_wing))
2324                                 return;
2325
2326                         //      Don't keep firing at a ship that is in its death throes.
2327                         if (Ships[eno->trial_objp->instance].flags & SF_DYING)
2328                                 return;
2329
2330                         if (is_ignore_object(aip, ((eno->trial_objp)-Objects)))
2331                                 return;
2332
2333                         if (eno->trial_objp->flags & OF_PROTECTED)
2334                                 return;
2335
2336                         if (Ships[eno->trial_objp->instance].flags & SF_ARRIVING)
2337                                 return;
2338
2339                         ship_info *sip = &Ship_info[Ships[eno->trial_objp->instance].ship_info_index];
2340
2341                         if (sip->flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2342                                 return;
2343
2344                         if (Ships[eno->trial_objp->instance].team & eno->enemy_team_mask) {
2345                                 float   dist;
2346                                 int     num_attacking;
2347
2348                                 // Allow targeting of stealth in nebula by his firing at me
2349                                 // This is done for a specific ship, not generally.
2350                                 if ( !eno->check_danger_weapon_objnum ) {
2351                                         // check if can be targeted if inside nebula
2352                                         if ( !object_is_targetable(eno->trial_objp, &Ships[Objects[eno->objnum].instance]) ) {
2353                                                 // check if stealth ship is visible, but not "targetable"
2354                                                 if ( !((sip->flags & SIF_STEALTH) && ai_is_stealth_visible(&Objects[eno->objnum], eno->trial_objp)) ) {
2355                                                         return;
2356                                                 }
2357                                         }
2358                                 }
2359
2360                                 // if objnum is BIG or HUGE, find distance to bbox
2361                                 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
2362                                         vector box_pt;
2363                                         // check if inside bbox
2364                                         int inside = get_nearest_bbox_point(eno->trial_objp, &Objects[eno->objnum].pos, &box_pt);
2365                                         if (inside) {
2366                                                 dist = 10.0f;
2367                                                 // on the box
2368                                         } else {
2369                                                 dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &box_pt);
2370                                         }
2371                                 } else {
2372                                         dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &eno->trial_objp->pos);
2373                                 }
2374                                 
2375                                 //      Make it more likely that fighters (or bombers) will be picked as an enemy by scaling up distance for other types.
2376                                 if ((Ship_info[Ships[eno->trial_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))) {
2377                                         dist = dist * 0.5f;
2378                                 }
2379
2380                                 num_attacking = num_enemies_attacking(eno->trial_objp-Objects);
2381                                 if ((sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) || (num_attacking < eno->max_attackers)) {
2382                                         if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))){
2383                                                 dist *= (float) (num_attacking+2)/2.0f;                         //      prevents lots of ships from attacking same target
2384                                         }
2385
2386                                         if (eno->trial_objp->flags & OF_PLAYER_SHIP){
2387                                                 dist *= 1.0f + (NUM_SKILL_LEVELS - Game_skill_level - 1)/NUM_SKILL_LEVELS;      //      Favor attacking non-players based on skill level.
2388                                         }
2389
2390                                         if (dist < eno->nearest_dist) {
2391                                                 eno->nearest_dist = dist;
2392                                                 eno->nearest_objnum = eno->trial_objp-Objects;
2393                                         }
2394                                 }
2395                         }
2396                 }
2397         }
2398
2399 }
2400
2401
2402 //      Given an object and an enemy team, return the index of the nearest enemy object.
2403 //      Unless aip->targeted_subsys != NULL, don't allow to attack objects
2404 //      with OF_PROTECTED bit set.
2405 //      Ship must be within range "range".
2406 //      Don't attack a ship that already has at least max_attackers attacking it.
2407 int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers)
2408 {
2409         object  *danger_weapon_objp;
2410         ai_info *aip;
2411         ship_obj        *so;
2412
2413         // initialize eno struct
2414         eval_nearest_objnum eno;
2415         eno.enemy_team_mask = enemy_team_mask;
2416         eno.enemy_wing = enemy_wing;
2417         eno.max_attackers = max_attackers;
2418         eno.objnum = objnum;
2419         eno.range = range;
2420         eno.nearest_dist = range;
2421         eno.nearest_objnum = -1;
2422         eno.check_danger_weapon_objnum = 0;
2423
2424         // go through the list of all ships and evaluate as potential targets
2425         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2426                 eno.trial_objp = &Objects[so->objnum];
2427                 evaluate_object_as_nearest_objnum(&eno);
2428
2429         }
2430
2431         // check if danger_weapon_objnum has will show a stealth ship
2432         aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2433         if (aip->danger_weapon_objnum >= 0) {
2434                 danger_weapon_objp = &Objects[aip->danger_weapon_objnum];
2435                 // validate weapon
2436                 if (danger_weapon_objp->signature == aip->danger_weapon_signature) {
2437                         Assert(danger_weapon_objp->type == OBJ_WEAPON);
2438                         // check if parent is a ship
2439                         if (danger_weapon_objp->parent >= 0) {
2440                                 if ( is_object_stealth_ship(&Objects[danger_weapon_objp->parent]) ) {
2441                                         // check if stealthy
2442                                         if ( ai_is_stealth_visible(&Objects[objnum], &Objects[danger_weapon_objp->parent]) != STEALTH_FULLY_TARGETABLE ) {
2443                                                 // check if weapon is laser
2444                                                 if (Weapon_info[Weapons[danger_weapon_objp->instance].weapon_info_index].subtype == WP_LASER) {
2445                                                         // check stealth ship by its laser fire
2446                                                         eno.check_danger_weapon_objnum = 1;
2447                                                         eno.trial_objp = &Objects[danger_weapon_objp->parent];
2448                                                         evaluate_object_as_nearest_objnum(&eno);
2449                                                 }
2450                                         }
2451                                 }
2452                         }
2453                 }
2454         }
2455
2456         //      If only looking for target in certain wing and couldn't find anything in
2457         //      that wing, look for any object.
2458         if ((eno.nearest_objnum == -1) && (enemy_wing != -1)) {
2459                 return get_nearest_objnum(objnum, enemy_team_mask, -1, range, max_attackers);
2460         }
2461
2462         return eno.nearest_objnum;
2463 }
2464
2465 //      Given an object and an enemy team, return the index of the nearest enemy object.
2466 //      Unlike find_enemy or find_nearest_objnum, this doesn't care about things like the protected flag or number
2467 //      of enemies attacking.
2468 //      It is used to find the nearest enemy to determine things like whether to rearm.
2469 int find_nearby_hostile(int objnum, int enemy_team_mask, float range, int *count)
2470 {
2471         int             nearest_objnum;
2472         float           nearest_dist;
2473         object  *objp;
2474         ai_info *aip;
2475         ship_obj        *so;
2476
2477         nearest_objnum = -1;
2478         nearest_dist = range;
2479
2480         aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2481
2482         *count = 0;
2483
2484         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2485                 objp = &Objects[so->objnum];
2486
2487                 if ( OBJ_INDEX(objp) != objnum ) {
2488                         if (Ships[objp->instance].flags & SF_DYING)
2489                                 continue;
2490
2491                         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2492                                 continue;
2493
2494                         if (Ships[objp->instance].team & enemy_team_mask) {
2495                                 float   dist;
2496
2497                                 dist = vm_vec_dist_quick(&Objects[objnum].pos, &objp->pos) - objp->radius*0.75f;
2498                                 
2499                                 if (dist < range) {
2500                                         (*count)++;
2501
2502                                         if (dist < nearest_dist) {
2503                                                 nearest_dist = dist;
2504                                                 nearest_objnum = objp-Objects;
2505                                         }
2506                                 }
2507                         }
2508                 }
2509         }
2510
2511         return nearest_objnum;
2512 }
2513
2514 // return !0 if objp can be considered for a turret target, 0 otherwise
2515 // input:       objp                            =>      object that turret is considering as an enemy
2516 //                              turret_parent   =>      object index for ship that turret sits on
2517 int valid_turret_enemy(object *objp, object *turret_parent)
2518 {
2519         if ( objp == turret_parent ) {
2520                 return 0;
2521         }
2522
2523         if ( objp->type == OBJ_ASTEROID ) {
2524                 return 1;
2525         }
2526
2527         if ( (objp->type == OBJ_SHIP) ) {
2528                 ship *shipp;
2529                 shipp = &Ships[objp->instance];
2530
2531                 // don't fire at ships with protected bit set!!!
2532                 if ( objp->flags & OF_PROTECTED ) {
2533                         return 0;
2534                 }
2535
2536                 if ( !(Ship_info[shipp->ship_info_index].flags & SIF_DO_COLLISION_CHECK)) {
2537                         return 0;
2538                 }
2539
2540                 if (shipp->flags & SF_ARRIVING) {
2541                         return 0;
2542                 }
2543
2544                 return 1;
2545         }
2546
2547         if ( objp->type == OBJ_WEAPON ) {
2548                 if ( Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_BOMB ) {
2549                         if ( obj_team(turret_parent) != Weapons[objp->instance].team ) {
2550                                 return 1;
2551                         }
2552                 }
2553         }
2554
2555         return 0;
2556 }
2557
2558 // return 1 if objp is in fov of the specified turret, tp.  Otherwise return 0.
2559 //      dist = distance from turret to center point of object
2560 int object_in_turret_fov(object *objp, model_subsystem *tp, vector *tvec, vector *tpos, float dist)
2561 {
2562         vector  v2e;
2563         float           dot;
2564         vm_vec_normalized_dir(&v2e, &objp->pos, tpos);
2565         dot = vm_vec_dot(&v2e, tvec);
2566
2567         dot += objp->radius / (dist + objp->radius);
2568
2569         if ( dot >= tp->turret_fov ) {
2570                 return 1;
2571         }
2572
2573         return 0;
2574 }
2575
2576 // return 1 if bomb_objp is headed towards ship_objp
2577 int bomb_headed_towards_ship(object *bomb_objp, object *ship_objp)
2578 {
2579         float           dot;
2580         vector  bomb_to_ship_vector;
2581
2582         vm_vec_normalized_dir(&bomb_to_ship_vector, &ship_objp->pos, &bomb_objp->pos);
2583         dot = vm_vec_dot(&bomb_objp->orient.v.fvec, &bomb_to_ship_vector);
2584
2585         if ( dot > 0 ) {
2586                 return 1;
2587         }
2588
2589         return 0;
2590 }
2591
2592 // nubmer of live turrets with target_objnum 
2593 int num_turrets_attacking(object *turret_parent, int target_objnum) 
2594 {
2595         ship_subsys *ss;
2596         ship *shipp;
2597         int count = 0;
2598         shipp = &Ships[turret_parent->instance];
2599
2600         Assert(turret_parent->type == OBJ_SHIP);
2601         Assert(Objects[target_objnum].type == OBJ_SHIP);
2602
2603         for (ss=GET_FIRST(&shipp->subsys_list); ss!=END_OF_LIST(&shipp->subsys_list); ss=GET_NEXT(ss)) {
2604                 // check if subsys is alive
2605                 if (ss->current_hits <= 0.0f) {
2606                         continue;
2607                 }
2608
2609                 // check if it's a turret
2610                 if (ss->system_info->type != SUBSYSTEM_TURRET) {
2611                         continue;
2612                 }
2613
2614                 // if the turret is locked
2615                 if(ss->weapons.flags & SW_FLAG_TURRET_LOCK){
2616                         continue;
2617                 }               
2618
2619                 // check if turret is targeting target_objnum
2620                 if (ss->turret_enemy_objnum == target_objnum) {
2621                         count++;
2622                 }
2623         }
2624
2625         return count;
2626 }
2627
2628 float Lethality_range_const = 2.0f;
2629 DCF(lethality_range, "N for modifying range: 1 / (1+N) at 100")
2630 {
2631         dc_get_arg(ARG_FLOAT);
2632         Lethality_range_const = Dc_arg_float;
2633 }
2634
2635 float Player_lethality_bump[NUM_SKILL_LEVELS] = {
2636         // 0.0f, 5.0f, 10.0f, 25.0f, 40.0f
2637         0.0f, 0.0f, 0.0f, 0.0f, 0.0f
2638 };
2639
2640 // evaluate obj as posssible target for turret
2641 void evaluate_obj_as_target(object *objp, eval_enemy_obj_struct *eeo)
2642 {
2643         object  *turret_parent_obj = &Objects[eeo->turret_parent_objnum];
2644         ship            *shipp;
2645         model_subsystem *tp = eeo->turret_subsys->system_info;
2646         float dist;
2647
2648         // Don't look for bombs when weapon system is not ok
2649         if (objp->type == OBJ_WEAPON && !eeo->weapon_system_ok) {
2650                 return;
2651         }
2652
2653         if ( !valid_turret_enemy(objp, turret_parent_obj) ) {
2654                 return;
2655         }
2656
2657 #ifndef NDEBUG
2658         if (!Player_attacking_enabled && (objp == Player_obj)) {
2659                 return;
2660         }
2661 #endif
2662
2663         if ( objp->type == OBJ_SHIP ) {
2664                 shipp = &Ships[objp->instance];
2665
2666                 // check on enemy team
2667                 if ( !(shipp->team & eeo->enemy_team_mask) ) {
2668                         return;
2669                 }
2670
2671                 // check if protected
2672                 if (objp->flags & OF_PROTECTED) {
2673                         return;
2674                 }
2675
2676                 // check if beam protected
2677                 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2678                         if (objp->flags & OF_BEAM_PROTECTED) {
2679                                 return;
2680                         }
2681                 }
2682
2683                 if (eeo->big_only_flag) {
2684                         if (!(Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2685                                 return;
2686                         }
2687                 }
2688
2689                 // check if     turret flagged to only target tagged ships
2690                 if ( (eeo->turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && !ship_is_tagged(objp) ) {
2691                         return;
2692                 }
2693
2694                 // check if valid target in nebula
2695                 if ( !object_is_targetable(objp, &Ships[Objects[eeo->turret_parent_objnum].instance]) ) {
2696                         // BYPASS ocassionally for stealth
2697                         int try_anyway = FALSE;
2698                         if ( is_object_stealth_ship(objp) ) {
2699                                 float turret_stealth_find_chance = 0.5f;
2700                                 float speed_mod = -0.1f + vm_vec_mag_quick(&objp->phys_info.vel) / 70.0f;
2701                                 if (frand() > (turret_stealth_find_chance + speed_mod)) {
2702                                         try_anyway = TRUE;
2703                                 }
2704                         }
2705
2706                         if (!try_anyway) {
2707                                 return;
2708                         }
2709                 }
2710
2711         } else {
2712                 shipp = NULL;
2713         }
2714
2715         // modify dist for BIG|HUGE, getting closest point on bbox, if not inside
2716         dist = vm_vec_dist_quick(eeo->tpos, &objp->pos) - objp->radius;
2717         if (dist < 0.0f) {
2718                 dist = 0.0f;
2719         }
2720
2721         // check if object is a bomb attacking the turret parent
2722         // check if bomb is homing on the turret parent ship
2723         if (objp->type == OBJ_WEAPON) {
2724                 if ( Weapons[objp->instance].homing_object == &Objects[eeo->turret_parent_objnum] ) {
2725                         if ( dist < eeo->nearest_homing_bomb_dist ) {
2726                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2727                                         eeo->nearest_homing_bomb_dist = dist;
2728                                         eeo->nearest_homing_bomb_objnum = OBJ_INDEX(objp);
2729                                 }
2730                         }
2731                 // if not homing, check if bomb is flying towards ship
2732                 } else if ( bomb_headed_towards_ship(objp, &Objects[eeo->turret_parent_objnum]) ) {
2733                         if ( dist < eeo->nearest_bomb_dist ) {
2734                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2735                                         eeo->nearest_bomb_dist = dist;
2736                                         eeo->nearest_bomb_objnum = OBJ_INDEX(objp);
2737                                 }
2738                         }
2739                 }
2740         } // end weapon section
2741
2742         // maybe recalculate dist for big or huge ship
2743 //      if (shipp && (Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2744 //              fvi_ray_boundingbox(min, max, start, direction, hit);
2745 //              dist = vm_vec_dist_quick(hit, tvec);
2746 //      }
2747
2748         // check for nearest attcker
2749         if ( (shipp) && (dist < eeo->weapon_travel_dist) ) {
2750                 ai_info *aip = &Ai_info[shipp->ai_index];
2751
2752                 // modify distance based on number of turrets from my ship attacking enemy (add 10% per turret)
2753                 // dist *= (num_enemies_attacking(OBJ_INDEX(objp))+2)/2;        //      prevents lots of ships from attacking same target
2754                 int num_att_turrets = num_turrets_attacking(turret_parent_obj, OBJ_INDEX(objp));
2755                 dist *= (1.0f + 0.1f*num_att_turrets);
2756
2757                 // return if we're over the cap
2758                 int max_turrets = 3 + Game_skill_level * Game_skill_level;
2759                 if (num_att_turrets > max_turrets) {
2760                         return;
2761                 }
2762
2763                 // modify distance based on lethality of objp to my ship
2764                 float active_lethality = aip->lethality;
2765                 if (objp->flags & OF_PLAYER_SHIP) {
2766                         active_lethality += Player_lethality_bump[Game_skill_level];
2767                 }
2768
2769                 dist /= (1.0f + 0.01f*Lethality_range_const*active_lethality);
2770
2771                 // Make level 2 tagged ships more likely to be targeted
2772                 if (shipp->level2_tag_left > 0.0f) {
2773                         dist *= 0.3f;
2774                 }
2775
2776                 // check if objp is targeting the turret's ship, or if objp has just hit the turret's ship
2777                 if ( aip->target_objnum == eeo->turret_parent_objnum || aip->last_objsig_hit == Objects[eeo->turret_parent_objnum].signature ) {
2778                         // A turret will always target a ship that is attacking itself... self-preservation!
2779                         if ( aip->targeted_subsys == eeo->turret_subsys ) {
2780                                 dist *= 0.5f;   // highest priority
2781                         }
2782                 }
2783
2784                 // maybe update nearest attacker
2785                 if ( dist < eeo->nearest_attacker_dist ) {
2786                         if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2787                                 // 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));
2788                                 eeo->nearest_attacker_dist = dist;
2789                                 eeo->nearest_attacker_objnum = OBJ_INDEX(objp);
2790                         }
2791                 }
2792         } // end ship section
2793
2794 #ifdef MAKE_FS1
2795         // check if object is an asteroid attacking the turret parent - taylor
2796         if (objp->type == OBJ_ASTEROID) {
2797                 if ( eeo->turret_parent_objnum == asteroid_collide_objnum(objp) ) {
2798                         // give priority to the closest asteroid *impact* (ms intervals)
2799                         dist *= 0.9f + (0.01f * asteroid_time_to_impact(objp));
2800
2801                         if (dist < eeo->nearest_dist ) {
2802                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2803                                         eeo->nearest_dist = dist;
2804                                         eeo->nearest_objnum = OBJ_INDEX(objp);
2805                                 }
2806                         }
2807                 }
2808         } // end asteroid selection
2809 #endif
2810 }
2811
2812 // return 0 only if objnum is beam protected and turret is beam turret
2813 int is_target_beam_valid(ship_subsys *turret_subsys, int objnum)
2814 {
2815         // check if turret has beam weapon
2816         model_subsystem *tp = turret_subsys->system_info;
2817
2818         if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2819                 if (Objects[objnum].flags & OF_BEAM_PROTECTED) {
2820                         return 0;
2821                 }
2822
2823                 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_HUGE) {
2824                         if (Objects[objnum].type == OBJ_SHIP && !(Ship_info[Ships[Objects[objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
2825                                 return 0;
2826                         }
2827                 }
2828         }
2829
2830         return 1;
2831 }
2832
2833
2834 //      Given an object and an enemy team, return the index of the nearest enemy object.
2835 //
2836 // input:
2837 //                              turret_parent_objnum    => parent objnum for the turret
2838 //                              turret_subsys                   => pointer to system_info for the turret subsystem
2839 //                              enemy_team_mask         => OR'ed TEAM_ flags for the enemy of the turret parent ship
2840 //                              tpos                                            => position of turret (world coords)
2841 //                              tvec                                            => forward vector of turret (world coords)
2842 //                              current_enemy                   =>      objnum of current turret target
2843 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)
2844 {
2845         float                                   weapon_travel_dist;
2846         int                                     weapon_system_ok;
2847         object                          *objp;
2848         model_subsystem *tp;
2849         eval_enemy_obj_struct eeo;
2850
2851         // list of stuff to go thru
2852         ship_obj                *so;
2853         missile_obj *mo;
2854
2855         tp = turret_subsys->system_info;
2856         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);
2857
2858         // Set flag based on strength of weapons subsystem.  If weapons subsystem is destroyed, don't let turrets fire at bombs
2859         weapon_system_ok = 0;
2860         if ( ship_get_subsystem_strength( &Ships[Objects[turret_parent_objnum].instance], SUBSYSTEM_WEAPONS ) > 0 ) {
2861                 weapon_system_ok = 1;
2862         }
2863
2864         // Initialize eeo struct.
2865         eeo.turret_parent_objnum = turret_parent_objnum;
2866         eeo.weapon_system_ok = weapon_system_ok;
2867         eeo.weapon_travel_dist = weapon_travel_dist;
2868         eeo.big_only_flag = big_only_flag;
2869         eeo.enemy_team_mask = enemy_team_mask;
2870         eeo.current_enemy = current_enemy;
2871         eeo.tpos = tpos;
2872         eeo.tvec = tvec;
2873         eeo.turret_subsys = turret_subsys;
2874
2875         eeo.nearest_attacker_dist = 99999.0f;
2876         eeo.nearest_attacker_objnum = -1;
2877
2878         eeo.nearest_homing_bomb_dist = 99999.0f;
2879         eeo.nearest_homing_bomb_objnum = -1;
2880
2881         eeo.nearest_bomb_dist = 99999.0f;
2882         eeo.nearest_bomb_objnum = -1;
2883
2884         eeo.nearest_dist = 99999.0f;
2885         eeo.nearest_objnum = -1;
2886
2887
2888         // Missile_obj_list
2889         for( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
2890                 objp = &Objects[mo->objnum];
2891                 evaluate_obj_as_target(objp, &eeo);
2892         }
2893         // highest priority
2894         if ( eeo.nearest_homing_bomb_objnum != -1 ) {                                   // highest priority is an incoming homing bomb
2895                 return eeo.nearest_homing_bomb_objnum;
2896         } else if ( eeo.nearest_bomb_objnum != -1 ) {                                   // next highest priority is an incoming dumbfire bomb
2897                 return eeo.nearest_bomb_objnum;
2898         }
2899
2900
2901         // Ship_used_list
2902         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2903                 objp = &Objects[so->objnum];
2904                 evaluate_obj_as_target(objp, &eeo);
2905         }
2906
2907         Assert(eeo.nearest_attacker_objnum < 0 || is_target_beam_valid(turret_subsys, eeo.nearest_attacker_objnum));
2908                 // next highest priority is attacking ship
2909         if ( eeo.nearest_attacker_objnum != -1 ) {                      // next highest priority is an attacking ship
2910                 return eeo.nearest_attacker_objnum;
2911          }
2912
2913
2914 #if !(defined(FS2_DEMO) || defined(FS1_DEMO))
2915                 asteroid_obj *ao;
2916         // Asteroid_obj_list
2917         for( ao = GET_FIRST(&Asteroid_obj_list); ao != END_OF_LIST(&Asteroid_obj_list); ao = GET_NEXT(ao) ) {
2918                 objp = &Objects[ao->objnum];
2919                 evaluate_obj_as_target(objp, &eeo);
2920         }
2921 #endif
2922
2923         return eeo.nearest_objnum;                                                                              // lowest priority is the closest enemy objnum
2924 }
2925
2926 //      Return timestamp until a ship can find an enemy.
2927 //      Yes, no parameters.  Based solely on skill level.
2928 int get_enemy_timestamp()
2929 {
2930         return (NUM_SKILL_LEVELS - Game_skill_level) * ( (myrand() % 500) + 500);
2931 }
2932
2933 // -------------------------------------------------------------------
2934 //      Return objnum if enemy found, else return -1;
2935 //      Don't attack a ship that already has at least max_attackers attacking it.
2936 int find_enemy(int objnum, float range, int max_attackers)
2937 {
2938         int     enemy_team_mask;
2939
2940         enemy_team_mask = get_enemy_team_mask(objnum);
2941
2942         //      if target_objnum != -1, use that as goal.
2943         ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2944         if (timestamp_elapsed(aip->choose_enemy_timestamp)) {
2945                 aip->choose_enemy_timestamp = timestamp(get_enemy_timestamp());
2946                 if (aip->target_objnum != -1) {
2947                         int     target_objnum = aip->target_objnum;
2948
2949                         // DKA don't undo object as target in nebula missions.
2950                         // This could cause attack on ship on fringe on nebula to stop if attackee moves our of nebula range.  (BAD)
2951                         if ( (Objects[target_objnum].signature == aip->target_signature) ) {
2952                                 if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
2953                                         if (!(Objects[target_objnum].flags & OF_PROTECTED)) {
2954                                                 // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
2955                                                 return target_objnum;
2956                                         }
2957                                 }
2958                         } else {
2959                                 aip->target_objnum = -1;
2960                                 aip->target_signature = -1;
2961                         }
2962                 }
2963                 return get_nearest_objnum(objnum, enemy_team_mask, aip->enemy_wing, range, max_attackers);
2964         } else {
2965                 aip->target_objnum = -1;
2966                 aip->target_signature = -1;
2967                 return -1;
2968         }
2969
2970 }
2971
2972 int Use_parent_target = 0;
2973 DCF_BOOL(use_parent_target, Use_parent_target)
2974
2975 // -------------------------------------------------------------------
2976 //      Return objnum if enemy found, else return -1;
2977 //
2978 // input:
2979 //                              turret_subsys   => pointer to turret subsystem
2980 //                              objnum                  => parent objnum for the turret
2981 //                              tpos                            => position of turret (world coords)
2982 //                              tvec                            => forward vector of turret (world coords)
2983 //                              current_enemy   =>      objnum of current turret target
2984 int find_turret_enemy(ship_subsys *turret_subsys, int objnum, vector *tpos, vector *tvec, int current_enemy, float fov, int big_only_flag = 0)
2985 {
2986         int                                     enemy_team_mask, enemy_objnum;
2987         model_subsystem *tp;
2988         ship_info                       *sip;
2989
2990         tp = turret_subsys->system_info;
2991         enemy_team_mask = get_enemy_team_mask(objnum);
2992
2993         //      If a small ship and target_objnum != -1, use that as goal.
2994         ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2995         sip = &Ship_info[Ships[Objects[objnum].instance].ship_info_index];
2996
2997         if ((sip->flags & SIF_SMALL_SHIP) && (aip->target_objnum != -1)) {
2998                 int target_objnum = aip->target_objnum;
2999
3000                 if (Objects[target_objnum].signature == aip->target_signature) {
3001                         if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
3002                                 if ( !(Objects[target_objnum].flags & OF_PROTECTED) ) {         // check this flag as well.
3003                                         // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
3004                                         return target_objnum;
3005                                 }
3006                         }
3007                 } else {
3008                         aip->target_objnum = -1;
3009                         aip->target_signature = -1;
3010                 }
3011         // Not small or small with target objnum
3012         } else {
3013                 // maybe use aip->target_objnum as next target
3014                 if ((frand() < 0.8f) && (aip->target_objnum != -1) && Use_parent_target) {
3015
3016                         //check if aip->target_objnum is valid target
3017                         int target_flags = Objects[aip->target_objnum].flags;
3018                         if ( target_flags & OF_PROTECTED ) {
3019                                 // AL 2-27-98: why is a protected ship being targeted?
3020                                 set_target_objnum(aip, -1);
3021                                 return -1;
3022                         }
3023
3024                         // maybe use ship target_objnum if valid for turret
3025                         // check for beam weapon and beam protected
3026                         if ( !((target_flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) ) {
3027                                 if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
3028                                         // check for huge weapon and huge ship
3029                                         if ( !big_only_flag || (Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
3030                                                 // check for tagged only and tagged ship
3031                                                 if ( (turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && ship_is_tagged(&Objects[aip->target_objnum]) ) {
3032                                                         // select new target if aip->target_objnum is out of field of view
3033                                                         vector v2e;
3034                                                         float dot, dist;
3035                                                         dist = vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, tpos);
3036                                                         dot = vm_vec_dot(&v2e, tvec);
3037                                                         //      MODIFY FOR ATTACKING BIG SHIP
3038                                                         // dot += (0.5f * Objects[aip->target_objnum].radius / dist);
3039                                                         if (dot > fov) {
3040                                                                 return aip->target_objnum;
3041                                                         }
3042                                                 }
3043                                         }
3044                                 }
3045                         }
3046                 }
3047         }
3048
3049         enemy_objnum = get_nearest_turret_objnum(objnum, turret_subsys, enemy_team_mask, tpos, tvec, current_enemy, big_only_flag);
3050         if ( enemy_objnum >= 0 ) {
3051                 Assert( !((Objects[enemy_objnum].flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) );
3052                 if ( Objects[enemy_objnum].flags & OF_PROTECTED ) {
3053                         Int3();
3054                         enemy_objnum = aip->target_objnum;
3055                 }
3056         }
3057
3058         return enemy_objnum;
3059 }
3060
3061 //      If issued an order to a ship that's awaiting repair, abort that process.
3062 //      However, do not abort process for an object that is currently being repaired -- let it finish.
3063 void ai_set_goal_maybe_abort_dock(object *objp, ai_info *aip)
3064 {
3065         if (aip->ai_flags & AIF_AWAITING_REPAIR) {
3066                 object  *repair_obj;
3067
3068                 if (aip->dock_objnum == -1) {
3069                         repair_obj = NULL;
3070                 } else {
3071                         repair_obj = &Objects[aip->dock_objnum];
3072                 }
3073                 ai_do_objects_repairing_stuff( objp, repair_obj, REPAIR_INFO_ABORT );
3074         }
3075         aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);    //      Might request again after 30 seconds.
3076 }
3077
3078 void force_avoid_player_check(object *objp, ai_info *aip)
3079 {
3080         if (Ships[objp->instance].team == Player_ship->team){
3081                 aip->avoid_check_timestamp = timestamp(0);              //      Force a check for collision next frame.
3082         }
3083 }
3084
3085 //      --------------------------------------------------------------------------
3086 //      Set *attacked as object to attack for object *attacker
3087 //      If attacked == NULL, then attack any enemy object.
3088 //      Attack point *rel_pos on object.  This is for supporting attacking subsystems.
3089 void ai_attack_object(object *attacker, object *attacked, int priority, ship_subsys *ssp)
3090 {
3091         ai_info *aip;
3092
3093         Assert(attacker != NULL);
3094         Assert(attacker->instance != -1);
3095         Assert(Ships[attacker->instance].ai_index != -1);
3096
3097         aip = &Ai_info[Ships[attacker->instance].ai_index];
3098         force_avoid_player_check(attacker, aip);
3099
3100         aip->ok_to_target_timestamp = timestamp(0);             //      Guarantee we can target.
3101
3102 //      if (!strnicmp(Ships[attacker->instance].ship_name, NOX("Kami"), 4)) {
3103 //              aip->ai_flags |= AIF_KAMIKAZE;
3104 //              aip->ai_flags |= AIF_NO_DYNAMIC;
3105 //      }
3106
3107         if (attacker == attacked) {
3108                 Int3();         //      Bogus!  Who tried to get me to attack myself!  Trace out and fix!
3109                 return;
3110         }
3111
3112         //      Only set to chase if a fighter or bomber, otherwise just return.
3113         if (!(Ship_info[Ships[attacker->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
3114 //              nprintf(("AI","Note: AI ship %s refusing to set AI mode to AIM_CHASE\n", Ships[attacker->instance].ship_name));
3115 //              return;
3116                 nprintf(("AI", "AI ship %s is large ship ordered to attack %s\n", Ships[attacker->instance].ship_name, Ships[attacked->instance].ship_name));
3117         }
3118
3119         //      This is how "engage enemy" gets processed
3120         if (attacked == NULL) {
3121                 aip->choose_enemy_timestamp = timestamp(0);
3122                 // nebula safe
3123                 set_target_objnum(aip, find_enemy(attacker-Objects, 99999.9f, 4));
3124         } else {
3125                 // check if we can see atacked in nebula
3126                 if (aip->target_objnum != attacked - Objects) {
3127                         aip->aspect_locked_time = 0.0f;
3128                 }
3129                 set_target_objnum(aip, attacked - Objects);
3130         }
3131
3132         ai_set_goal_maybe_abort_dock(attacker, aip);
3133         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);     //      No dynamic targeting for 7 seconds.
3134
3135         if (is_ignore_object(aip, aip->target_objnum)) {
3136                 aip->ignore_objnum = UNUSED_OBJNUM;
3137         }
3138
3139         aip->mode = AIM_CHASE;
3140         aip->submode = SM_ATTACK;       // AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
3141                                                                                 //                                       for AIM_CHASE... it may have been not set correctly here
3142         if (ssp == NULL) {
3143                 set_targeted_subsys(aip, NULL, -1);
3144                 if (aip->target_objnum != -1) {
3145                         //nprintf(("AI", "Unprotecting ship %s\n", Ships[Objects[aip->target_objnum].instance].ship_name));
3146                         Objects[aip->target_objnum].flags &= ~OF_PROTECTED;     //      If ship had been protected, unprotect it.
3147                 }
3148         } else {
3149                 Int3(); //      Not supported yet!
3150         }
3151 }
3152
3153 //      --------------------------------------------------------------------------
3154 //      Set *attacked as object to attack for object *attacker
3155 //      Attack point *rel_pos on object.  This is for supporting attacking subsystems.
3156 void ai_attack_wing(object *attacker, int wingnum, int priority)
3157 {
3158         ai_info *aip;
3159
3160         Assert(attacker != NULL);
3161         Assert(attacker->instance != -1);
3162         Assert(Ships[attacker->instance].ai_index != -1);
3163
3164         aip = &Ai_info[Ships[attacker->instance].ai_index];
3165
3166         aip->enemy_wing = wingnum;
3167         aip->mode = AIM_CHASE;
3168         aip->submode = SM_ATTACK;       // AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
3169                                                                                 //                                       for AIM_CHASE... it may have been not set correctly here
3170
3171         aip->ok_to_target_timestamp = timestamp(0);             //      Guarantee we can target.
3172
3173         int count = Wings[wingnum].current_count;
3174         if (count > 0) {
3175                 int     index;
3176
3177                 index = (int) (frand() * count);
3178
3179                 if (index >= count)
3180                         index = 0;
3181
3182                 set_target_objnum(aip, Ships[Wings[wingnum].ship_index[index]].objnum);
3183
3184                 ai_set_goal_maybe_abort_dock(attacker, aip);
3185                 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);     //      No dynamic targeting for 7 seconds.
3186         }
3187 }
3188
3189 //      --------------------------------------------------------------------------
3190 //      Set *evaded as object for *evader to evade.
3191 void ai_evade_object(object *evader, object *evaded, int priority)
3192 {
3193         ai_info *aip;
3194
3195         Assert(evader != NULL);
3196         Assert(evaded != NULL);
3197         Assert(evader->instance != -1);
3198         Assert(Ships[evader->instance].ai_index != -1);
3199
3200         if (evaded == evader) {
3201                 Int3(); //      Bogus!  Who tried to get me to evade myself!  Trace out and fix!
3202                 return;
3203         }
3204
3205         aip = &Ai_info[Ships[evader->instance].ai_index];
3206
3207         set_target_objnum(aip, evaded - Objects);
3208         aip->mode = AIM_EVADE;
3209
3210 }
3211
3212 //      Ignore some object without changing mode.
3213 void ai_ignore_object(object *ignorer, object *ignored, int priority)
3214 {
3215         ai_info *aip;
3216
3217         Assert(ignorer != NULL);
3218         Assert(ignored != NULL);
3219         Assert(ignorer->instance != -1);
3220         Assert(Ships[ignorer->instance].ai_index != -1);
3221         Assert(ignorer != ignored);
3222
3223         aip = &Ai_info[Ships[ignorer->instance].ai_index];
3224
3225         //      MK, 5/17/98, removing ignoring of wings.
3226         //      It's too confusing.  It often causes mysterious behavior in which fighters unexpectedly refuse to attack anything.
3227 /*      if (Ships[ignored->instance].wingnum > -1) {
3228                 int wingnum, i;
3229
3230                 wingnum = Ships[ignored->instance].wingnum;
3231                 aip->ignore_objnum = -(wingnum+1);
3232                 // set protected bit for each ship in a wing
3233                 //      MK, 4/23/98: Only set for fighters if they are the original "ignored" object
3234                 for (i = 0; i < Wings[wingnum].current_count; i++ ) {
3235                         object  *objp;
3236
3237                         objp = &Objects[Ships[Wings[wingnum].ship_index[i]].objnum];
3238                         if (objp != ignored) {
3239                                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))
3240                                         continue;
3241                         }
3242
3243                         Objects[Ships[Wings[wingnum].ship_index[i]].objnum].flags |= OF_PROTECTED;
3244                 }
3245
3246         } else {
3247         */ {
3248                 aip->ignore_objnum = ignored - Objects;
3249                 aip->ignore_signature = ignored->signature;
3250                 aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3251                 ignored->flags |= OF_PROTECTED;                                 // set protected bit of ignored ship.
3252         }
3253
3254 }
3255
3256 //      Ignore some object without changing mode.
3257 void ai_ignore_wing(object *ignorer, int wingnum, int priority)
3258 {
3259         ai_info *aip;
3260
3261         Assert(ignorer != NULL);
3262         Assert(ignorer->instance != -1);
3263         Assert(Ships[ignorer->instance].ai_index != -1);
3264         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
3265
3266         aip = &Ai_info[Ships[ignorer->instance].ai_index];
3267
3268         aip->ignore_objnum = -(wingnum +1);
3269         aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3270 }
3271
3272
3273 //      Add a path point in the global buffer Path_points.
3274 //      modify_index = index in Path_points at which to store path point.
3275 //      If modify_index == -1, then create a new point.
3276 //      If a new point is created (ie, modify_index == -1), then Ppfp is updated.
3277 void add_path_point(vector *pos, int path_num, int path_index, int modify_index)
3278 {
3279         pnode   *pnp;
3280
3281         if (modify_index == -1) {
3282                 Assert(Ppfp-Path_points < MAX_PATH_POINTS-1);
3283                 pnp = Ppfp;
3284                 Ppfp++;
3285         } else {
3286                 Assert((modify_index >= 0) && (modify_index < MAX_PATH_POINTS-1));
3287                 pnp = &Path_points[modify_index];
3288         }
3289
3290         pnp->pos = *pos;
3291         pnp->path_num = path_num;
3292         pnp->path_index = path_index;
3293 }
3294
3295 //      Given two points on a sphere, the center of the sphere and the radius, return a
3296 //      point on the vector through the midpoint of the chord on the sphere.
3297 void bisect_chord(vector *p0, vector *p1, vector *centerp, float radius)
3298 {
3299         vector  tvec;
3300         vector  new_pnt;
3301
3302         vm_vec_add(&tvec, p0, p1);
3303         vm_vec_sub2(&tvec, centerp);
3304         vm_vec_sub2(&tvec, centerp);
3305         if (vm_vec_mag_quick(&tvec) < 0.1f) {
3306                 vm_vec_sub(&tvec, p0, p1);
3307                 if (fl_abs(tvec.xyz.x) <= fl_abs(tvec.xyz.z)){
3308                         tvec.xyz.x = -tvec.xyz.z;
3309                 } else {
3310                         tvec.xyz.y = -tvec.xyz.x;
3311                 }
3312         }
3313
3314         vm_vec_normalize(&tvec);
3315         vm_vec_scale(&tvec, radius);
3316         vm_vec_add(&new_pnt, centerp, &tvec);
3317
3318         add_path_point(&new_pnt, -1, -1, -1);
3319 }
3320                         
3321 //      Create a path from the current position to a goal position.
3322 //      The current position is in the current object and the goal position is
3323 //      in the goal object.
3324 //      It is ok to intersect the current object, but not the goal object.
3325 //      This function is useful for creating a path to an initial point near a large
3326 //      object.
3327 //
3328 // input:       subsys_path:    optional param (default 0), indicates this is a path to a subsystem
3329 void create_path_to_point(vector *curpos, vector *goalpos, object *curobjp, object *goalobjp, int subsys_path)
3330 {
3331         //      If can't cast vector to goalpos, then create an intermediate point.
3332         if (pp_collide(curpos, goalpos, goalobjp, curobjp->radius)) {
3333                 vector  tan1;
3334                 float           radius;
3335
3336                 // If this is a path to a subsystem, use SUBSYS_PATH_DIST as the radius for the object you are
3337                 // trying to avoid.  This is needed since subsystem paths extend out to SUBSYS_PATH_DIST, and we
3338                 // want ships to reach their path destination without flying to points that sit on the radius of
3339                 // a small ship
3340                 radius = goalobjp->radius;
3341                 if (subsys_path) {
3342                         if ( SUBSYS_PATH_DIST > goalobjp->radius ) {
3343                                 radius = SUBSYS_PATH_DIST;
3344                         }
3345                 }
3346
3347                 //      The intermediate point is at the intersection of:
3348                 //              tangent to *goalobjp sphere at point *goalpos
3349                 //              tangent to *goalobjp sphere through point *curpos in plane defined by *curpos, *goalpos, goalobjp->pos
3350                 //      Note, there are two tangents through *curpos, unless *curpos is on the
3351                 //      sphere.  The tangent that causes the nearer intersection (to *goalpos) is chosen.
3352                 get_tangent_point(&tan1, curpos, &goalobjp->pos, goalpos, radius);
3353
3354                 //      If we can't reach tan1 from curpos, insert a new point.
3355                 if (pp_collide(&tan1, curpos, goalobjp, curobjp->radius))
3356                         bisect_chord(curpos, &tan1, &goalobjp->pos, radius);
3357
3358                 add_path_point(&tan1, -1, -1, -1);
3359
3360                 //      If we can't reach goalpos from tan1, insert a new point.
3361                 if (pp_collide(goalpos, &tan1, goalobjp, curobjp->radius))
3362                         bisect_chord(goalpos, &tan1, &goalobjp->pos, radius);
3363         }
3364
3365 }
3366
3367 //      Given an object and a model path, globalize the points on the model
3368 //      and copy into the global path list.
3369 //      If pnp != NULL, then modify, in place, the path points.  This is used to create new
3370 //      globalized points when the base object has moved.
3371 // input:       randomize_pnt   => optional parameter (default value -1), add random vector in sphere to this path point
3372 void copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt)
3373 {
3374         matrix  m;
3375         int             i;
3376         vector  v1;
3377         int             pp_index;               //      index in Path_points at which to store point, if this is a modify-in-place (pnp ! NULL)
3378         int             start_index, finish_index;
3379         
3380         // nprintf(("AI", "Creating path for object %s in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
3381         
3382         //      Initialize pp_index.
3383         //      If pnp == NULL, that means we're creating new points.  If not NULL, then modify in place.
3384         if (pnp == NULL)
3385                 pp_index = -1;                  //      This tells add_path_point to create a new point.
3386         else
3387                 pp_index = 0;                   //      pp_index will get assigned to index in Path_points to reuse.
3388
3389         vm_copy_transpose_matrix(&m, &objp->orient);
3390
3391         if (dir == 1) {
3392                 start_index = 0;
3393                 finish_index = min(count, mp->nverts);
3394         } else {
3395                 Assert(dir == -1);      //      direction must be up by 1 or down by 1 and it's neither!
3396                 start_index = mp->nverts-1;
3397                 finish_index = max(-1, mp->nverts-1-count);
3398         }
3399
3400         int offset = 0;
3401         for (i=start_index; i != finish_index; i += dir) {
3402                 //      Globalize the point.
3403                 vm_vec_rotate(&v1, &mp->verts[i].pos, &m);
3404                 vm_vec_add2(&v1, &objp->pos);
3405
3406                 if ( randomize_pnt == i ) {
3407                         vector v_rand;
3408                         static_randvec(OBJ_INDEX(objp), &v_rand);
3409                         vm_vec_scale(&v_rand, 30.0f);
3410                         vm_vec_add2(&v1, &v_rand);
3411                 }
3412
3413                 if (pp_index != -1)
3414                         pp_index = pnp-Path_points + offset;
3415
3416                 add_path_point(&v1, path_num, i, pp_index);
3417                 offset++;
3418         }
3419 }
3420
3421
3422 //      For pl_objp, create a path along path path_num into mobjp.
3423 //      The tricky part of this problem is creating the entry to the first point on the
3424 //      predefined path.  The points on this entry path are based on the location of Pl_objp
3425 //      relative to the start of the path.
3426 //
3427 // input:
3428 //                              subsys_path:    optional param (default 0), indicating this is a path to a subsystem
3429 void create_model_path(object *pl_objp, object *mobjp, int path_num, int subsys_path)
3430 {       
3431         ship                    *shipp = &Ships[pl_objp->instance];
3432         ai_info         *aip = &Ai_info[shipp->ai_index];
3433
3434         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3435         polymodel       *pm = model_get(osip->modelnum);
3436         int                     num_points;
3437         model_path      *mp;
3438         pnode                   *ppfp_start = Ppfp;
3439         matrix          m;
3440         vector          gp0;
3441
3442         Assert(path_num >= 0);
3443
3444         //      Do garbage collection if necessary.
3445         if (Ppfp-Path_points + 64 > MAX_PATH_POINTS) {
3446                 garbage_collect_path_points();
3447                 ppfp_start = Ppfp;
3448         }
3449
3450         aip->path_start = Ppfp - Path_points;
3451         Assert(path_num < pm->n_paths);
3452         
3453         mp = &pm->paths[path_num];
3454         num_points = mp->nverts;
3455
3456         Assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
3457
3458         vm_copy_transpose_matrix(&m, &mobjp->orient);
3459         vm_vec_rotate(&gp0, &mp->verts[0].pos, &m);
3460         vm_vec_add2(&gp0, &mobjp->pos);
3461
3462         if (pp_collide(&pl_objp->pos, &gp0, mobjp, pl_objp->radius)) {
3463                 vector  perim_point1;
3464                 vector  perim_point2;
3465
3466                 perim_point2 = pl_objp->pos;
3467                 
3468                 //      If object that wants to dock is inside bounding sphere of object it wants to dock with, make it fly out.
3469                 //      Assume it can fly "straight" out to the bounding sphere.
3470                 if (vm_vec_dist_quick(&pl_objp->pos, &mobjp->pos) < mobjp->radius) {
3471                         project_point_to_perimeter(&perim_point2, &mobjp->pos, mobjp->radius, &pl_objp->pos);
3472                         add_path_point(&perim_point2, path_num, -1, -1);
3473                 }
3474
3475                 //      If last point on pre-defined path is inside bounding sphere, create a new point on the surface of the sphere.
3476                 if (vm_vec_dist_quick(&mobjp->pos, &gp0) < mobjp->radius) {
3477                         project_point_to_perimeter(&perim_point1, &mobjp->pos, mobjp->radius, &gp0);
3478                         create_path_to_point(&perim_point2, &perim_point1, pl_objp, mobjp, subsys_path);
3479                         add_path_point(&perim_point1, path_num, -1, -1);
3480                 } else {                //      The predefined path extends outside the sphere.  Create path to that point.
3481                         create_path_to_point(&perim_point2, &gp0, pl_objp, mobjp, subsys_path);
3482                 }
3483         }
3484
3485         // AL 12-31-97: If following a subsystem path, add random vector to second last path point
3486         if ( subsys_path ) {
3487                 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL, mp->nverts-2);
3488         } else {
3489                 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL);
3490         }
3491
3492         aip->path_cur = aip->path_start;
3493         aip->path_dir = PD_FORWARD;
3494         aip->path_objnum = mobjp-Objects;
3495         aip->mp_index = path_num;
3496         aip->path_length = Ppfp - ppfp_start;
3497         aip->path_next_check_time = timestamp(1);
3498
3499         aip->path_goal_obj_hash = create_object_hash(&Objects[aip->path_objnum]);
3500
3501         aip->path_next_create_time = timestamp(1000);   //      OK to try to create one second later
3502         aip->path_create_pos = pl_objp->pos;
3503         aip->path_create_orient = pl_objp->orient;
3504
3505         aip->ai_flags &= ~AIF_USE_EXIT_PATH;                    // ensure this flag is cleared
3506 }
3507
3508 //      For pl_objp, create a path along path path_num into mobjp.
3509 //      The tricky part of this problem is creating the entry to the first point on the
3510 //      predefined path.  The points on this entry path are based on the location of pl_objp
3511 //      relative to the start of the path.
3512 void create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count)
3513 {       
3514         ship                    *shipp = &Ships[pl_objp->instance];
3515         ai_info         *aip = &Ai_info[shipp->ai_index];
3516
3517         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3518         polymodel       *pm = model_get(osip->modelnum);
3519         int                     num_points;
3520         model_path      *mp;
3521         pnode                   *ppfp_start = Ppfp;
3522
3523         aip->path_start = Ppfp - Path_points;
3524         Assert(path_num < pm->n_paths);
3525         
3526         mp = &pm->paths[path_num];
3527         num_points = mp->nverts;
3528
3529         Assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
3530
3531         copy_xlate_model_path_points(mobjp, mp, -1, count, path_num, NULL);
3532
3533         aip->path_cur = aip->path_start;
3534         aip->path_dir = PD_FORWARD;
3535         aip->path_objnum = mobjp-Objects;
3536         aip->mp_index = path_num;
3537         aip->path_length = Ppfp - ppfp_start;
3538         aip->path_next_check_time = timestamp(1);
3539
3540         aip->ai_flags |= AIF_USE_EXIT_PATH;             // mark as exit path, referenced in maybe
3541 }
3542
3543 //      Return true if the vector from curpos to goalpos intersects with any ship other than the ignore objects.
3544 //      Calls pp_collide
3545 int pp_collide_any(vector *curpos, vector *goalpos, float radius, object *ignore_objp1, object *ignore_objp2, int big_only_flag)
3546 {
3547         ship_obj        *so;    
3548
3549         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
3550                 object *objp = &Objects[so->objnum];
3551
3552                 if (big_only_flag) {
3553                         if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
3554                                 continue;
3555                 }
3556
3557                 if ((objp != ignore_objp1) && (objp != ignore_objp2)) {
3558                         if (pp_collide(curpos, goalpos, objp, radius))
3559                                 return OBJ_INDEX(objp);
3560                 }
3561         }
3562
3563         return -1;
3564 }
3565
3566 //      Used to create docking paths and other pre-defined paths through ships.
3567 //      Creates a path in absolute space.
3568 //      Create a path into the object objnum.
3569 //
3570 // input:
3571 //      pl_objp:                        object that will use the path
3572 //      objnum:                 Object to find path to.
3573 //      path_num:               model path index to use
3574 //      exit_flag:              true means this is an exit path in the model
3575 // subsys_path: optional param (default 0) that indicates this is a path to a subsystem
3576 //      Exit:
3577 //      ai_info struct in Pl_objp gets stuffed with information to enable Pl_objp to fly the path.
3578 void ai_find_path(object *pl_objp, int objnum, int path_num, int exit_flag, int subsys_path)
3579 {
3580         ai_info *aip = &Ai_info[Ships[pl_objp->instance].ai_index];
3581
3582         Assert(path_num >= 0);
3583
3584         //      This is test code, find an object with paths.
3585         if (objnum != -1) {
3586                 object  *objp = &Objects[objnum];
3587
3588                 if (objp->type == OBJ_SHIP) {
3589                         polymodel *pm;
3590
3591                         ship    *shipp = &Ships[objp->instance];
3592                         pm = model_get( shipp->modelnum );
3593                         Assert(pm->n_paths > path_num);
3594                         aip->goal_objnum = objp-Objects;
3595                         aip->goal_signature = objp->signature;
3596                         if (exit_flag)
3597                                 create_model_exit_path(pl_objp, objp, path_num);
3598                         else
3599                                 create_model_path(pl_objp, objp, path_num, subsys_path);
3600                         return;
3601                 }
3602
3603         }
3604 }
3605
3606 extern int vector_object_collision(vector *start_pos, vector *end_pos, object *objp, float radius_scale);
3607
3608 //      Maybe make *objp avoid a player object.
3609 //      For now, 4/6/98, only check Player_obj.
3610 //      If player collision would occur, set AIF_AVOIDING_SMALL_SHIP bit in ai_flags.
3611 //      Set aip->avoid_goal_point
3612 int maybe_avoid_player(object *objp, vector *goal_pos)
3613 {
3614         ai_info *aip;
3615         vector  cur_pos, new_goal_pos;
3616         object  *player_objp;
3617         vector  n_vec_to_goal, n_vec_to_player;
3618
3619         aip = &Ai_info[Ships[objp->instance].ai_index];
3620
3621         if (!timestamp_elapsed(aip->avoid_check_timestamp))
3622                 return 0;
3623
3624         player_objp = Player_obj;
3625
3626         float   speed_time;
3627
3628         //      How far two ships could be apart and still collide within one second.
3629         speed_time = player_objp->phys_info.speed + objp->phys_info.speed;
3630
3631         float   obj_obj_dist;
3632
3633         obj_obj_dist = vm_vec_dist_quick(&player_objp->pos, &objp->pos);
3634
3635         if (obj_obj_dist > speed_time*2.0f)
3636                 return 0;
3637
3638         cur_pos = objp->pos;
3639
3640         new_goal_pos = *goal_pos;
3641
3642         float dist = vm_vec_normalized_dir(&n_vec_to_goal, goal_pos, &objp->pos);
3643         vm_vec_normalized_dir(&n_vec_to_player, &player_objp->pos, &objp->pos);
3644
3645         if (dist > speed_time*2.0f) {
3646                 vm_vec_scale_add(&new_goal_pos, &objp->pos, &n_vec_to_goal, 200.0f);
3647         }
3648
3649         if (vector_object_collision(&objp->pos, &new_goal_pos, player_objp, 1.5f)) {
3650                 aip->ai_flags |= AIF_AVOIDING_SMALL_SHIP;
3651
3652                 vector  avoid_vec;
3653
3654                 vm_vec_sub(&avoid_vec, &n_vec_to_goal, &n_vec_to_player);
3655                 if (vm_vec_mag_quick(&avoid_vec) < 0.01f) {
3656                         vm_vec_copy_scale(&avoid_vec, &objp->orient.v.rvec, frand()-0.5f);
3657                         vm_vec_scale_add2(&avoid_vec, &objp->orient.v.uvec, frand()-0.5f);
3658                         vm_vec_normalize(&avoid_vec);
3659                 } else {
3660                         vector  tvec1;
3661                         vm_vec_normalize(&avoid_vec);
3662                         vm_vec_crossprod(&tvec1, &n_vec_to_goal, &avoid_vec);
3663                         vm_vec_crossprod(&avoid_vec, &tvec1, &n_vec_to_player);
3664                 }
3665
3666                 //      Now, avoid_vec is a vector perpendicular to the vector to the player and the direction *objp
3667                 //      should fly in to avoid the player while still approaching its goal.
3668                 vm_vec_scale_add(&aip->avoid_goal_point, &player_objp->pos, &avoid_vec, 400.0f);
3669
3670                 aip->avoid_check_timestamp = timestamp(1000);
3671
3672                 return 1;
3673         } else {
3674                 aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
3675                 aip->avoid_check_timestamp = timestamp((int) (obj_obj_dist/200.0f) + 500);
3676
3677                 return 0;
3678         }
3679 }
3680
3681 //      Make object *still_objp enter AIM_STILL mode.
3682 //      Make it point at view_pos.
3683 void ai_stay_still(object *still_objp, vector *view_pos)
3684 {
3685         ship    *shipp;
3686         ai_info *aip;
3687
3688         Assert(still_objp->type == OBJ_SHIP);
3689         Assert((still_objp->instance >= 0) && (still_objp->instance < MAX_OBJECTS));
3690
3691         shipp = &Ships[still_objp->instance];
3692         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
3693
3694         aip = &Ai_info[shipp->ai_index];
3695
3696         aip->mode = AIM_STILL;
3697
3698         //      If view_pos not NULL, point at that point.  Else, point at a point directly in front of ship.  Ie, don't turn.
3699         if (view_pos != NULL)
3700                 aip->goal_point = *view_pos;
3701         else
3702                 vm_vec_scale_add(&aip->goal_point, &still_objp->pos, &still_objp->orient.v.fvec, 100.0f);
3703 }
3704
3705 // code which is called from ai_dock_with_object and ai_dock to set flags and apprioriate variable
3706 // when two objects have completed docking.  used because we can dock object initially at misison load
3707 // time (meaning that ai_dock() might never get called).  docker has docked with dockee (i.e. docker
3708 // would be a freighter and dockee would be a cargo).
3709 void ai_do_objects_docked_stuff(object *docker, object *dockee)
3710 {
3711         ai_info *aip, *other_aip;
3712
3713         aip = &Ai_info[Ships[docker->instance].ai_index];
3714         other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3715
3716         // set the flags and dock_objnum for both objects
3717         aip->ai_flags |= AIF_DOCKED;
3718         aip->dock_objnum = OBJ_INDEX(dockee);
3719         other_aip->ai_flags |= AIF_DOCKED;
3720         other_aip->dock_objnum = OBJ_INDEX(docker);
3721         aip->dock_signature = dockee->signature;
3722         other_aip->dock_signature = docker->signature;
3723
3724         // add multiplayer hook here to deal with docked objects.  We need to only send information
3725         // about the object that is docking.  Both flags will get updated.
3726         if ( MULTIPLAYER_MASTER )
3727                 send_ai_info_update_packet( docker, AI_UPDATE_DOCK );
3728
3729 }
3730
3731 // code which is called when objects become undocked. Equivalent of above function.
3732 // dockee might not be valid since this code can get called to cleanup after a ship
3733 // has blown up!
3734 void ai_do_objects_undocked_stuff( object *docker, object *dockee )
3735 {
3736         ai_info *aip, *other_aip;
3737
3738         // add multiplayer hook here to deal with undocked objects.  Do it before we
3739         // do anything else.  We don't need to send info for both objects, since we can find
3740         // it be dock_objnum
3741         if ( MULTIPLAYER_MASTER )
3742                 send_ai_info_update_packet( docker, AI_UPDATE_UNDOCK );
3743
3744         aip = &Ai_info[Ships[docker->instance].ai_index];
3745
3746         // set the flags and dock_objnum for both objects
3747         aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3748         aip->dock_objnum = -1;
3749         
3750         if ( dockee != NULL ) {
3751                 other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3752                 other_aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3753                 other_aip->dock_objnum = -1;
3754         }
3755
3756 }
3757
3758
3759 //      --------------------------------------------------------------------------
3760 //      Interface from goals code to AI.
3761 //      Cause *docker to dock with *dockee.
3762 //      priority is priority of goal from goals code.
3763 //      dock_type is:
3764 //              AIDO_DOCK               set goal of docking
3765 //              AIDO_DOCK_NOW   immediately dock, used for ships that need to be docked at mission start
3766 //              AIDO_UNDOCK             set goal of undocking
3767 void ai_dock_with_object(object *docker, object *dockee, int priority, int dock_type, int docker_index, int dockee_index)
3768 {
3769         ai_info         *aip;
3770         polymodel       *pm;
3771         ai_info         *dockee_aip;
3772
3773         Assert(docker != NULL);
3774         Assert(dockee != NULL);
3775         Assert(docker->instance != -1);
3776         Assert(Ships[docker->instance].ai_index != -1);
3777         Assert(Ships[dockee->instance].ai_index != -1);
3778         Assert( docker_index != -1 );
3779         Assert( dockee_index != -1 );
3780
3781         aip = &Ai_info[Ships[docker->instance].ai_index];
3782
3783         if ((aip->ai_flags & AIF_DOCKED) && (dock_type == AIDO_DOCK)) {
3784                 object  *dockee2;
3785                 int             docker_index2, dockee_index2;
3786
3787                 Assert(aip->dock_objnum > -1);
3788                 dockee2 = &Objects[aip->dock_objnum];
3789                 docker_index2 = aip->dock_index;
3790                 dockee_index2 = aip->dockee_index;
3791                 // MWA -- 2/9/98.  use the goal code to undock the ships since goals might need to get removed
3792                 // and that code will do it properly.  I'd actually be surprised if we got into this code anymore
3793                 // since the outer layer goal code should deal with this issue....but who knows...
3794                 ai_add_goal_ship_internal( aip, AI_GOAL_UNDOCK, NULL, -1, -1, 0 );
3795
3796                 // old code below
3797                 //ai_dock_with_object(docker, dockee2, priority, AIDO_UNDOCK, docker_index2, dockee_index2);
3798                 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));
3799                 nprintf(("AI", "...so ship %s will now undock.\n", Ships[docker->instance].ship_name));
3800                 return;
3801         }
3802
3803         dockee_aip = &Ai_info[Ships[dockee->instance].ai_index];
3804
3805         aip->goal_objnum = dockee - Objects;
3806         aip->goal_signature = dockee->signature;
3807
3808         aip->mode = AIM_DOCK;
3809
3810         switch (dock_type) {
3811         case AIDO_DOCK:
3812                 aip->submode = AIS_DOCK_0;
3813                 break;
3814         case AIDO_DOCK_NOW:
3815                 aip->submode = AIS_DOCK_3A;
3816                 break;
3817         case AIDO_UNDOCK:
3818                 aip->submode = AIS_UNDOCK_0;
3819                 break;
3820         default:
3821                 Int3();         //      Bogus dock_type.
3822         }
3823
3824         aip->submode_start_time = Missiontime;
3825         aip->dock_index = docker_index;
3826         aip->dockee_index = dockee_index;
3827
3828         dockee_aip->dock_index = dockee_index;
3829         dockee_aip->dockee_index = docker_index;
3830
3831         // get the path number to the docking point on the dockee.  Each docking point contains a list
3832         // of paths that the point can be reached by.  Pick the first path in the path list for now.
3833         // We only want to do this stuff if we are docking!!!  Be sure to set the path index
3834         if ((dock_type == AIDO_DOCK) || (dock_type == AIDO_DOCK_NOW)) {
3835                 pm = model_get( Ships[dockee->instance].modelnum );
3836                 Assert( pm->docking_bays[dockee_index].num_spline_paths > 0 );
3837
3838                 // only set the dock path index if we are docking.  undocking will assume that dock_path_index
3839                 // already set from some other docking command
3840                 aip->dock_path_index = dockee_index;
3841                 dockee_aip->dock_path_index = docker_index;
3842         }
3843
3844         if (dock_type != AIDO_DOCK_NOW) {
3845                 int path_num;
3846                 //      Note: Second parameter is dock path index.  This should be specified as an
3847                 //      _input_ to this function and passed through.  The path index should be already
3848                 // set for the undock function
3849                 path_num = ai_return_path_num_from_dockbay(dockee, dockee_index);
3850                 ai_find_path(docker, dockee-Objects, path_num, 0);
3851 //              ai_find_path(dockee-Objects, dockee_index, 0);
3852         } else {
3853                 dock_orient_and_approach(docker, dockee, DOA_DOCK_STAY);
3854                 //aip->dock_objnum = OBJ_INDEX(dockee);
3855                 ai_do_objects_docked_stuff( docker, dockee );
3856         }
3857
3858 }
3859
3860 //      Cause a ship to fly its waypoints.
3861 //      flags tells:
3862 //              WPF_REPEAT      Set -> repeat waypoints.
3863 void ai_start_waypoints(object *objp, int waypoint_list_index, int wp_flags)
3864 {
3865         ai_info *aip;
3866
3867         Assert(waypoint_list_index < Num_waypoint_lists);
3868
3869         //nprintf(("AI", "Frame %i: Ship %s instructed to fly waypoint list #%i\n", AI_FrameCount, Ships[objp->instance].ship_name, waypoint_list_index));
3870         aip = &Ai_info[Ships[objp->instance].ai_index];
3871
3872         if ( (aip->mode == AIM_WAYPOINTS) && (aip->wp_index == waypoint_list_index) )
3873                 return;
3874
3875         aip->ai_flags |= AIF_FORMATION_WING;
3876         aip->ai_flags &= ~AIF_FORMATION_OBJECT;
3877         aip->wp_list = waypoint_list_index;
3878         aip->wp_index = 0;
3879         aip->wp_flags = wp_flags;
3880         aip->mode = AIM_WAYPOINTS;
3881
3882         Assert(aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC);
3883 }
3884
3885 //      Make *objp stay within dist units of *other_objp
3886 void ai_do_stay_near(object *objp, object *other_objp, float dist)
3887 {
3888         ai_info *aip;
3889
3890         Assert(objp != other_objp);             //      Bogus!  Told to stay near self.
3891         Assert(objp->type == OBJ_SHIP);
3892         Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
3893
3894         aip = &Ai_info[Ships[objp->instance].ai_index];
3895
3896         aip->mode = AIM_STAY_NEAR;
3897         aip->submode = -1;
3898         aip->stay_near_distance = dist;
3899         aip->goal_objnum = other_objp-Objects;
3900         aip->goal_signature = other_objp->signature;
3901
3902 }
3903
3904 //      Make object *objp form on wing of object *goal_objp
3905 void ai_form_on_wing(object *objp, object *goal_objp)
3906 {
3907         ai_info *aip;
3908         ship                    *shipp;
3909         ship_info       *sip;
3910
3911         // objp == goal_objp sometimes in multiplayer when someone leaves a game -- make a simple
3912         // out for this case.
3913         if ( Game_mode & GM_MULTIPLAYER ) {
3914                 if ( objp == goal_objp ) {
3915                         return;
3916                 }
3917         }
3918
3919         Assert(objp != goal_objp);              //      Bogus!  Told to form on own's wing!
3920
3921         shipp = &Ships[objp->instance];
3922         sip = &Ship_info[shipp->ship_info_index];
3923
3924         //      Only fighters or bombers allowed to form on wing.
3925         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
3926                 nprintf(("AI", "Warning: Ship %s tried to form on player's wing, but not fighter or bomber.\n", shipp->ship_name));
3927                 return;
3928         }
3929
3930         aip = &Ai_info[Ships[objp->instance].ai_index];
3931
3932         aip->ai_flags &= ~AIF_FORMATION_WING;
3933         aip->ai_flags |= AIF_FORMATION_OBJECT;
3934
3935         aip->goal_objnum = goal_objp-Objects;
3936         ai_set_goal_maybe_abort_dock(objp, aip);
3937         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME*4);           //      Super extra long time until can target another ship.
3938
3939 }
3940
3941 //      Given an object and an object on whose wing to form, return slot to use.
3942 //      Optimize:
3943 //              This function is called per object in formation per frame.  Should store slot in ai_info struct.
3944 int ai_formation_object_get_slotnum(int objnum, object *objp)
3945 {
3946         int     slotnum = 1;                    //      Note: Slot #0 means leader, which isn't someone who was told to form-on-wing.
3947         object *o;
3948
3949         for ( o = GET_FIRST(&obj_used_list); o != END_OF_LIST(&obj_used_list); o = GET_NEXT(o) ) {
3950                 if (objp == o)
3951                         break;
3952                 else if (o->type == OBJ_SHIP)
3953                         if (Ai_info[Ships[o->instance].ai_index].ai_flags & AIF_FORMATION_OBJECT)
3954                                 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == objnum)
3955                                         slotnum++;
3956         }
3957
3958         Assert(o != END_OF_LIST(&obj_used_list));       //      Didn't find objp in list of used ships.  Impossible!
3959
3960         return slotnum;
3961 }
3962
3963 #define BIGNUM  100000.0f
3964
3965 int Debug_k = 0;
3966
3967 //      Given an attacker's position and a target's position and velocity, compute the time of
3968 //      intersection of a weapon fired by the attacker with speed weapon_speed.
3969 //      Return this value.  Return value of 0.0f means no collision is possible.
3970 float compute_collision_time(vector *targpos, vector *targvel, vector *attackpos, float weapon_speed)
3971 {
3972         vector  vec_to_target;
3973         float           pos_dot_vel;
3974         float           vel_sqr;
3975         float           discrim;
3976
3977         vm_vec_sub(&vec_to_target, targpos, attackpos);
3978         pos_dot_vel = vm_vec_dot(&vec_to_target, targvel);
3979         vel_sqr = vm_vec_dot(targvel, targvel) - weapon_speed*weapon_speed;
3980         discrim = pos_dot_vel*pos_dot_vel - vel_sqr*vm_vec_dot(&vec_to_target, &vec_to_target);
3981
3982         if (discrim > 0.0f) {
3983                 float   t1, t2, t_solve;
3984
3985                 t1 = (-pos_dot_vel + fl_sqrt(discrim)) / vel_sqr;
3986                 t2 = (-pos_dot_vel - fl_sqrt(discrim)) / vel_sqr;
3987
3988                 t_solve = BIGNUM;
3989
3990                 if (t1 > 0.0f)
3991                         t_solve = t1;
3992                 if ((t2 > 0.0f) && (t2 < t_solve))
3993                         t_solve = t2;
3994
3995                 if (t_solve < BIGNUM-1.0f) {
3996                         return t_solve + Debug_k * flFrametime;
3997                 }
3998         }
3999
4000         return 0.0f;
4001 }
4002
4003
4004 //      --------------------------------------------------------------------------
4005 //      If far away, use player's speed.
4006 //      If in between, lerp between player and laser speed
4007 //      If close, use laser speed.
4008 // Want to know how much time it will take to get to the enemy.
4009 // This function doesn't account for the fact that by the time the player
4010 // (or his laser) gets to the current enemy position, the enemy will have moved.
4011 // This is dealt with in polish_predicted_enemy_pos.
4012 float compute_time_to_enemy(float dist_to_enemy, object *pobjp, object *eobjp)
4013 {
4014         float   time_to_enemy;
4015         float   pl_speed = pobjp->phys_info.speed;
4016         float   max_laser_distance, max_laser_speed;
4017         int     bank_num, weapon_num;
4018         ship    *shipp = &Ships[pobjp->instance];
4019
4020         bank_num = shipp->weapons.current_primary_bank;
4021         weapon_num = shipp->weapons.primary_bank_weapons[bank_num];
4022         max_laser_speed = Weapon_info[weapon_num].max_speed;
4023         max_laser_distance = max_laser_speed * Weapon_info[weapon_num].lifetime;
4024
4025         //      If pretty far away, use player's speed to predict position, else
4026         //      use laser's speed because when close, we care more about hitting
4027         //      with a laser than about causing ship:ship rendezvous.
4028         if (dist_to_enemy > 1.5 * max_laser_distance) {
4029                 if (pl_speed > 0.0f)
4030                         time_to_enemy = dist_to_enemy/pl_speed;
4031                 else
4032                         time_to_enemy = 1.0f;
4033         } else if (dist_to_enemy > 1.1*max_laser_distance) {
4034                 if (pl_speed > 0.1f) {
4035                         float   scale;
4036
4037                         scale = (float) ((dist_to_enemy - max_laser_distance) / max_laser_distance);
4038                 
4039                         time_to_enemy = (float) (dist_to_enemy/(pl_speed * scale + max_laser_speed * (1.0f - scale)));
4040                 } else
4041                         time_to_enemy = 2.0f;
4042         } else
4043                 time_to_enemy = (float) (dist_to_enemy/max_laser_speed);
4044
4045         // return time_to_enemy * (1.0f + Ai_info[Ships[pobjp->instance].ai_index].lead_scale);
4046         return time_to_enemy + flFrametime;
4047 }
4048
4049 //      Stuff *dot and *tts.
4050 //      *dot is always computed.  If dot is less than zero, the magnitude is
4051 //      incorrect, not having been divided by distance.
4052 //      If *dot is > 0.0f, then tts is computed.  This is the time it will take object
4053 //      *objp to get to *pos, assuming it moves right at it.
4054 void fds_aux(float *dot, float *tts, vector *pos, float dtime, object *objp)
4055 {
4056         vector  v2s;
4057
4058         vm_vec_sub(&v2s, pos, &objp->pos);
4059         *dot = vm_vec_dot(&v2s, &objp->orient.v.fvec);
4060
4061         if (*dot > 0.0f) {
4062                 float   dist;
4063
4064                 dist = vm_vec_dist(&objp->pos, pos);
4065
4066                 if (dist > 0.1f)
4067                         *dot /= dist;
4068                 else
4069                         *dot = 1.0f;
4070
4071                 if (objp->phys_info.speed > 0.1f)
4072                         *tts = dist / objp->phys_info.speed;
4073                 else
4074                         *tts = dist * 100.0f;
4075         }
4076 }
4077
4078 /*
4079 //      Return index of weapon that could hit object *sobjp within dtime seconds.
4080 //      Actual time until impact returned in *atime.
4081 int find_danger_weapon(object *sobjp, float dtime, float *atime, float dot_threshhold)
4082 {
4083         object  *objp, *best_objp = NULL;
4084         float           best_tts = 1000.0f;
4085
4086         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
4087                 if ((objp->type == OBJ_WEAPON) && (sobjp-Objects != objp->parent)) {
4088                         float           dot, tts;
4089                         // vector       psp;            //      Predicted ship position.
4090
4091                         //      Get dot and time to current ship position.
4092                         fds_aux(&dot, &tts, &sobjp->pos, dtime, objp);
4093
4094                         //      If dot and tts are in plausible range, do more expensive stuff.
4095                         if (dot > 0.98f) {
4096 //                              float   dot_from_sobjp;
4097                                 vector  v2e;
4098
4099                                 vm_vec_normalized_dir(&v2e, &objp->pos, &sobjp->pos);
4100 //                              dot_from_sobjp = vm_vec_dot(&sobjp->orient.v.fvec, &v2e);
4101 //                              if (dot_from_sobjp >= dot_threshhold)
4102                                         if (tts < dtime) {
4103                                                 if (tts < best_tts) {
4104                                                         best_tts = tts;
4105                                                         best_objp = objp;
4106                                                 }
4107                                         }
4108                         }
4109                 }
4110         }
4111
4112         *atime = best_tts;
4113
4114         if (best_objp != NULL)
4115                 return best_objp-Objects;
4116         else
4117                 return -1;
4118 }
4119 */
4120
4121 //      --------------------------------------------------------------------------
4122 void ai_set_positions(object *pl_objp, object *en_objp, ai_info *aip, vector *player_pos, vector *enemy_pos)
4123 {
4124         *player_pos = pl_objp->pos;
4125
4126         if (aip->next_predict_pos_time > Missiontime) {
4127                 *enemy_pos = aip->last_predicted_enemy_pos;
4128         } else {
4129                 *enemy_pos = en_objp->pos;
4130
4131                 aip->next_predict_pos_time = Missiontime + Skill_level_delay[Game_skill_level];
4132                 aip->last_predicted_enemy_pos = *enemy_pos;
4133         }
4134
4135
4136 }
4137
4138 //      --------------------------------------------------------------------------
4139 int find_nearest_waypoint(object *objp)
4140 {
4141         int     i;
4142         float   dist, min_dist, dot;
4143         int     min_ind;
4144         ship    *shipp;
4145         int     wp_listnum;
4146         waypoint_list   *wpl;
4147
4148         shipp = &Ships[objp->instance];
4149         wp_listnum = Ai_info[Ships[objp->instance].ai_index].wp_list;
4150         Assert(wp_listnum > 0);
4151         wpl = &Waypoint_lists[wp_listnum];
4152
4153         min_dist = 999999.0f;
4154         min_ind = -1;
4155
4156         for (i=0; i<wpl->count; i++) {
4157                 dist = vm_vec_dist_quick(&objp->pos, &wpl->waypoints[i]);
4158                 dot = vm_vec_dot_to_point(&objp->orient.v.fvec, &objp->pos, &wpl->waypoints[i]);
4159                 dist = (float) (dist * (1.25 - dot));
4160                 if (dist < min_dist) {
4161                         min_dist = dist;
4162                         min_ind = i;
4163                 }
4164         }
4165
4166         Assert(min_ind != -1);
4167
4168         return min_ind;
4169 }
4170
4171 //      Given an ai_info struct, by reading current goal and path information,
4172 //      extract base path information and return in pmp and pmpv.
4173 //      Return true if found, else return false.
4174 //      false means the current point is not on the original path.
4175 int get_base_path_info(int path_cur, int goal_objnum, model_path **pmp, mp_vert **pmpv)
4176 {
4177         pnode                   *pn = &Path_points[path_cur];
4178         ship_info       *sip = &Ship_info[Ships[Objects[goal_objnum].instance].ship_info_index];
4179         polymodel       *pm = model_get(sip->modelnum);
4180         //static        int     debug_last_index = -1;  // no longer used
4181         *pmpv = NULL;
4182         *pmp = NULL;
4183
4184         if (pn->path_num != -1) {
4185                 *pmp = &pm->paths[pn->path_num];
4186                 if (pn->path_index != -1)
4187                         *pmpv = &(*pmp)->verts[pn->path_index];
4188                 else
4189                         return 0;
4190         } else
4191                 return 0;
4192
4193 /*      if (debug_last_index != *pmpv-(*pmp)->verts) {
4194                 debug_last_index = *pmpv-(*pmp)->verts;
4195                 nprintf(("AI", "Point %i has %i turrets: ", *pmpv-(*pmp)->verts, (*pmpv)->nturrets));
4196                 for (int i=0; i<(*pmpv)->nturrets; i++) {
4197                         nprintf(("AI", "%i ", (*pmpv)->turret_ids[i]));
4198                 }
4199                 nprintf(("AI", "\n"));
4200         }
4201 */
4202         return 1;
4203 }
4204
4205 //      Modify, in place, the points in a global model path.
4206 //      Only modify those points that are defined in the model path.  Don't modify the
4207 //      leadin points, such as those that are necessary to get the model on the path.
4208 void modify_model_path_points(object *objp)
4209 {       
4210         ai_info         *aip = &Ai_info[Ships[objp->instance].ai_index];
4211         object          *mobjp = &Objects[aip->path_objnum];
4212         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
4213         polymodel       *pm = model_get(osip->modelnum);
4214         pnode                   *pnp;
4215         int                     path_num, dir;
4216
4217         Assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
4218
4219         pnp = &Path_points[aip->path_start];
4220         while ((pnp->path_index == -1) && (pnp-Path_points - aip->path_start < aip->path_length))
4221                 pnp++;
4222
4223         path_num = pnp->path_num;
4224         Assert((path_num >= 0) && (path_num < pm->n_paths));
4225         
4226         Assert(pnp->path_index != -1);  //      If this is -1, that means we never found the model path points
4227
4228         dir = 1;
4229         if ( aip->ai_flags & AIF_USE_EXIT_PATH ) {
4230                 dir = -1;
4231         }
4232
4233         copy_xlate_model_path_points(mobjp, &pm->paths[path_num], dir, pm->paths[path_num].nverts, path_num, pnp);
4234 }
4235
4236 //      Return an indication of the distance between two matrices.
4237 //      This is the sum of the distances of their dot products from 1.0f.
4238 float ai_matrix_dist(matrix *mat1, matrix *mat2)
4239 {
4240         float   t;
4241
4242         t =  1.0f - vm_vec_dot(&mat1->v.fvec, &mat2->v.fvec);
4243         t += 1.0f - vm_vec_dot(&mat1->v.uvec, &mat2->v.uvec);
4244         t += 1.0f - vm_vec_dot(&mat1->v.rvec, &mat2->v.rvec);
4245
4246         return t;
4247 }
4248
4249
4250 //      Paths are created in absolute space, so a moving object needs to have model paths within it recreated.
4251 //      This uses the hash functions which means the slightest movement will cause a recreate, though the timestamp
4252 //      prevents this from happening too often.
4253 //      force_recreate_flag TRUE means to recreate regardless of timestamp.
4254 //      Returns TRUE if path recreated.
4255 float maybe_recreate_path(object *objp, ai_info *aip, int force_recreate_flag)
4256 {
4257         int     hashval;
4258
4259         Assert(&Ai_info[Ships[objp->instance].ai_index] == aip);
4260
4261         if ((aip->mode == AIM_BAY_EMERGE) || (aip->mode == AIM_BAY_DEPART))
4262                 if ((OBJ_INDEX(objp) % 4) == (Framecount % 4))
4263                         force_recreate_flag = 1;
4264
4265         //      If no path, that means we don't need one.
4266         if (aip->path_start == -1)
4267                 return 0.0f;
4268
4269         // AL 11-12-97: If AIF_USE_STATIC_PATH is set, don't try to recreate.  This is needed when ships
4270         //                                  emerge from fighter bays.  We don't need to recreate the path.. and in case the 
4271         //              parent ship dies, we still want to be able to continue on the path
4272         if ( aip->ai_flags & AIF_USE_STATIC_PATH ) 
4273                 return 0.0f;
4274
4275         if (force_recreate_flag || timestamp_elapsed(aip->path_next_create_time)) {
4276                 object  *path_objp;
4277
4278                 path_objp = &Objects[aip->path_objnum];
4279
4280                 if ((hashval = create_object_hash(path_objp)) != aip->path_goal_obj_hash) {
4281                         float dist;
4282                         
4283                         dist = vm_vec_dist_quick(&path_objp->pos, &aip->path_create_pos);
4284                         dist += ai_matrix_dist(&path_objp->orient, &aip->path_create_orient) * 25.0f;
4285
4286                         if (force_recreate_flag || (dist > 2.0f)) {
4287                                 aip->path_next_create_time = timestamp(1000);   //      Update again in as little as 1000 milliseconds, ie 1 second.
4288                                 aip->path_goal_obj_hash = hashval;
4289                                 modify_model_path_points(objp);
4290
4291                                 aip->path_create_pos = path_objp->pos;
4292                                 aip->path_create_orient = path_objp->orient;
4293                                 
4294                                 return dist;
4295                         }
4296                 }
4297         }
4298
4299         return 0.0f;
4300 }
4301
4302 //      Set acceleration for ai_dock().
4303 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)
4304 {
4305         float prev_dot_to_goal = aip->prev_dot_to_goal;
4306         
4307         aip->prev_dot_to_goal = dot;
4308
4309         if (objp->phys_info.speed < 0.0f) {
4310                 accelerate_ship(aip, 1.0f/32.0f);
4311         } else if ((prev_dot_to_goal-dot) > 0.01) {
4312                 if (prev_dot_to_goal > dot + 0.05f) {
4313                         accelerate_ship(aip, 0.0f);
4314                 } else {
4315                         change_acceleration(aip, -1.0f);        //      -1.0f means subtract off flFrametime from acceleration value in 0.0..1.0
4316                 }
4317         } else {
4318                 if ((aip->mode == AIM_DOCK) && (dist_to_next < 150.0f) && (aip->path_start + aip->path_length - 2 == aip->path_cur)) {
4319                         set_accel_for_target_speed(objp, sip->max_speed * max(dist_to_next/500.0f, 1.0f));
4320                         //mprintf(("dist = %7.3f, speed = %7.3f\n", dist_to_next, objp->phys_info.speed));
4321                 } else if ((dot_to_next >= dot * .9) || (dist_to_next > 100.0f)) {
4322                         if (dist_to_goal > 200.0f)
4323                                 set_accel_for_target_speed(objp, sip->max_speed * (dot + 1.0f) / 2.0f);
4324                         else {
4325                                 float   xdot;
4326
4327                                 xdot = (dot_to_next + dot)/2.0f;
4328                                 if (xdot < 0.0f)
4329                                         xdot = 0.0f;
4330
4331                                 // AL: if following a path not in dock mode, move full speed
4332                                 if (( aip->mode != AIM_DOCK ) && (dot > 0.9f)) {
4333                                         set_accel_for_target_speed(objp, sip->max_speed*dot*dot*dot);
4334                                 } else {
4335                                         if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4336                                                 //nprintf(("AI", "Target speed = %7.3f\n", dist_to_goal/8.0f));
4337                                                 set_accel_for_target_speed(objp, dist_to_goal/8.0f + 2.0f);
4338                                         } else {
4339                                                 set_accel_for_target_speed(objp, sip->max_speed * (2*xdot + 0.25f)/4.0f);
4340                                         }
4341                                 }
4342                         }
4343                 } else {
4344                         float   xdot;
4345
4346                         xdot = max(dot_to_next, 0.1f);
4347                         if ( aip->mode != AIM_DOCK ) {
4348                                 set_accel_for_target_speed(objp, sip->max_speed);
4349                         } else {
4350                                 float   speed;
4351                                 if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4352                                         speed = dist_to_goal/8.0f + 2.0f;
4353                                 } else if (dist_to_goal < 4*objp->radius + 50.0f) {
4354                                         speed = dist_to_goal/4.0f + 4.0f;
4355                                 } else {
4356                                         speed = sip->max_speed * (3*xdot + 1.0f)/4.0f;
4357                                 }
4358                                 if (aip->mode == AIM_DOCK) {
4359                                         speed = speed * 2.0f + 1.0f;
4360                                         if (aip->goal_objnum != -1) {
4361                                                 speed += Objects[aip->goal_objnum].phys_info.speed;
4362                                         }
4363                                 }
4364
4365                                 set_accel_for_target_speed(objp, speed);
4366                         }
4367                 }
4368         }
4369 }
4370
4371 //      --------------------------------------------------------------------------
4372 //      Follow a path associated with a large object, such as a capital ship.
4373 //      The points defined on the path are in the object's reference frame.
4374 //      The object of interest is goal_objnum.
4375 //      The paths are defined in the model.  The path of interest is wp_list.
4376 //      The next goal point in the path is wp_index.
4377 //      wp_flags contain special information specific to the path.
4378
4379 // The path vertices are defined by model_path structs:
4380 //              typedef struct model_path {
4381 //                      char            name[MAX_NAME_LEN];                                     // name of the subsystem.  Probably displayed on HUD
4382 //                      int             nverts;
4383 //                      vector  *verts;
4384 //              } model_path;
4385
4386 //      The polymodel struct for the object contains the following:
4387 //              int                     n_paths;
4388 //              model_path      *paths;
4389
4390 //      Returns distance to goal point.
4391 float ai_path()
4392 {
4393         polymodel       *pm;
4394         int             num_paths, num_points;
4395         float           dot, dist_to_goal, dist_to_next, speed, dot_to_next;
4396         ship            *shipp = &Ships[Pl_objp->instance];
4397         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4398         ai_info *aip;
4399         vector  nvel_vec;
4400         float           mag, prev_dot_to_goal;
4401         vector  temp_vec, *slop_vec;
4402         object  *gobjp;
4403         ship            *gshipp;
4404         vector  *cvp, *nvp, next_vec, gcvp, gnvp;               //      current and next vertices in global coordinates.
4405
4406         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4407
4408         Assert(aip->goal_objnum != -1);
4409         Assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
4410
4411         gobjp = &Objects[aip->goal_objnum];
4412         gshipp = &Ships[gobjp->instance];
4413
4414         pm = model_get( gshipp->modelnum );
4415         num_paths = pm->n_paths;
4416         Assert(num_paths > 0);
4417
4418         if (aip->path_start == -1) {
4419                 int path_num;
4420                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
4421                 Assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
4422                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
4423         }
4424
4425         // nprintf(("AI", "Frame: %i, Path index = %i/%i\n", AI_FrameCount, aip->path_cur-aip->path_start, aip->path_length));
4426
4427         maybe_recreate_path(Pl_objp, aip, 0);
4428
4429         num_points = aip->path_length;
4430
4431         //      Set cvp and nvp as pointers to current and next vertices of interest on path.
4432         cvp = &Path_points[aip->path_cur].pos;
4433         if ((aip->path_cur + aip->path_dir - aip->path_start < num_points) || (aip->path_cur + aip->path_dir < aip->path_start))
4434                 nvp = &Path_points[aip->path_cur + aip->path_dir].pos;
4435         else {
4436                 //      If this is 0, then path length must be 1 which means we have no direction!
4437                 Assert((aip->path_cur - aip->path_dir >= aip->path_start) && (aip->path_cur - aip->path_dir - aip->path_start < num_points));
4438                 //      Cleanup for above Assert() which we hit too near release. -- MK, 5/24/98.
4439                 if (aip->path_cur - aip->path_dir - aip->path_start >= num_points) {
4440                         if (aip->path_dir == 1)
4441                                 aip->path_cur = aip->path_start;
4442                         else
4443                                 aip->path_cur = aip->path_start + num_points - 1;
4444                 }
4445
4446                 vector  delvec;
4447                 vm_vec_sub(&delvec, cvp, &Path_points[aip->path_cur - aip->path_dir].pos);
4448                 vm_vec_normalize(&delvec);
4449                 vm_vec_scale_add(&next_vec, cvp, &delvec, 10.0f);
4450                 nvp = &next_vec;
4451         }
4452
4453         //      Interrupt if can't get to current goal point.  Debug only.
4454 /*      if (pp_collide(&Pl_objp->pos, cvp, gobjp, Pl_objp->radius)) {
4455                 Int3();
4456         }
4457 */
4458         //      See if can reach next point (as opposed to current point)
4459         //      However, don't do this if docking and next point is last point.
4460         //      That is, we don't want to pursue the last point under control of the
4461         //      path code.  In docking, this is a special hack.
4462         if ((aip->mode != AIM_DOCK) || ((aip->path_cur-aip->path_start) < num_points - 2)) {
4463                 if ((aip->path_cur + aip->path_dir > aip->path_start) && (aip->path_cur + aip->path_dir < aip->path_start + num_points-2)) {
4464                         if ( timestamp_elapsed(aip->path_next_check_time)) {
4465                                 aip->path_next_check_time = timestamp( 3000 );
4466                                 if (!pp_collide(&Pl_objp->pos, nvp, gobjp, 1.1f * Pl_objp->radius)) {
4467                                         cvp = nvp;
4468                                         aip->path_cur += aip->path_dir;
4469                                         nvp = &Path_points[aip->path_cur].pos;
4470                                         //nprintf(("AI", "Reach: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4471                                 }
4472                         }
4473                 }
4474         }
4475
4476         gcvp = *cvp;
4477         gnvp = *nvp;
4478
4479         speed = Pl_objp->phys_info.speed;
4480
4481         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &gcvp);
4482         dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, &gnvp);
4483         //      Can't use fvec, need to use velocity vector because we aren't necessarily
4484         //      moving in the direction we're facing.
4485
4486 //      if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4487         if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4488                 mag = 0.0f;
4489                 vm_vec_zero(&nvel_vec);
4490         } else
4491                 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4492
4493         //      If moving not-very-slowly and sliding, then try to slide at goal, rather than
4494         //      point at goal.
4495         slop_vec = NULL;
4496         if (mag < 1.0f)
4497                 nvel_vec = Pl_objp->orient.v.fvec;
4498         else if (mag > 5.0f) {
4499                 float   nv_dot;
4500                 nv_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4501                 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4502                         slop_vec = &temp_vec;
4503                         vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.v.fvec);
4504                 }
4505         }
4506
4507         if (dist_to_goal > 0.1f)
4508                 ai_turn_towards_vector(&gcvp, Pl_objp, flFrametime, sip->srotation_time, slop_vec, NULL, 0.0f, 0);
4509
4510         //      Code to control speed is MUCH less forgiving in path following than in waypoint
4511         //      following.  Must be very close to path or might hit objects.
4512         prev_dot_to_goal = aip->prev_dot_to_goal;
4513         dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gcvp);
4514         dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gnvp);
4515
4516         set_accel_for_docking(Pl_objp, aip, dot, dot_to_next, dist_to_next, dist_to_goal, sip);
4517         aip->prev_dot_to_goal = dot;
4518
4519 //mprintf(("Goal index = %i, dist = %7.3f, dot = %7.3f\n", wp_index, dist_to_goal, dot));
4520
4521         //      If moving at a non-tiny velocity, detect attaining path point by its being close to
4522         //      line between previous and current object location.
4523         if ((dist_to_goal < MIN_DIST_TO_WAYPOINT_GOAL) || (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f)) {
4524                 vector  nearest_point;
4525                 float           r, min_dist_to_goal;
4526
4527                 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, &gcvp);
4528
4529                 //      Set min_dist_to_goal = how close must be to waypoint to pick next one.
4530                 //      If docking and this is the second last waypoint, must be very close.
4531                 if ((aip->mode == AIM_DOCK) && (aip->path_cur >= aip->path_length-2))
4532                         min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL;
4533                 else
4534                         min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius;
4535
4536                 if ( (vm_vec_dist_quick(&Pl_objp->pos, &gcvp) < min_dist_to_goal) ||
4537                         ((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, &gcvp) < (MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius))) {
4538                         aip->path_cur += aip->path_dir;
4539                         //nprintf(("AI", " Near: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4540                         if (((aip->path_cur - aip->path_start) > (num_points+1)) || (aip->path_cur < aip->path_start)) {
4541                                 Assert(aip->mode != AIM_DOCK);          //      If docking, should never get this far, getting to last point handled outside ai_path()
4542                                 aip->path_dir = -aip->path_dir;
4543 //                              aip->path_cur += aip->path_dir;
4544                         }
4545                 }
4546         }
4547
4548         return dist_to_goal;
4549 }
4550
4551 void update_min_max(float val, float *min, float *max)
4552 {
4553         if (val < *min)
4554                 *min = val;
4555         else if (val > *max)
4556                 *max = val;
4557 }
4558
4559 //      Stuff bounding box of all enemy objects within "range" units of object *my_objp.
4560 //      Stuff ni min_vec and max_vec.
4561 //      Return value: Number of enemy objects in bounding box.
4562 int get_enemy_team_range(object *my_objp, float range, int enemy_team_mask, vector *min_vec, vector *max_vec)
4563 {
4564         object  *objp;
4565         ship_obj        *so;
4566         int             count = 0;
4567
4568         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4569                 objp = &Objects[so->objnum];
4570                 if (Ships[objp->instance].team & enemy_team_mask) {
4571                         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))
4572                                 if (vm_vec_dist_quick(&my_objp->pos, &objp->pos) < range) {
4573                                         if (count == 0) {
4574                                                 *min_vec = objp->pos;
4575                                                 *max_vec = objp->pos;
4576                                                 count++;
4577                                         } else {
4578                                                 update_min_max(objp->pos.xyz.x, &min_vec->xyz.x, &max_vec->xyz.x);
4579                                                 update_min_max(objp->pos.xyz.y, &min_vec->xyz.y, &max_vec->xyz.y);
4580                                                 update_min_max(objp->pos.xyz.z, &min_vec->xyz.z, &max_vec->xyz.z);
4581                                         }
4582                                 }
4583
4584                 }
4585         }
4586
4587         return count;
4588 }
4589
4590 //      Pick a relatively safe spot for objp to fly to.
4591 //      Problem:
4592 //              Finds a spot away from any enemy within a bounding box.
4593 //              Doesn't verify that "safe spot" is not near some other enemy.
4594 void ai_safety_pick_spot(object *objp)
4595 {
4596         int             objnum;
4597         int             enemy_team_mask;
4598         vector  min_vec, max_vec;
4599         vector  vec_to_center, center;
4600         vector  goal_pos;
4601
4602         objnum = OBJ_INDEX(objp);
4603
4604         enemy_team_mask = get_enemy_team_mask(objnum);
4605
4606         if (get_enemy_team_range(objp, 1000.0f, enemy_team_mask, &min_vec, &max_vec)) {
4607                 vm_vec_avg(&center, &min_vec, &max_vec);
4608                 vm_vec_normalized_dir(&vec_to_center, &center, &objp->pos);
4609
4610                 vm_vec_scale_add(&goal_pos, &center, &vec_to_center, 2000.0f);
4611         } else
4612                 vm_vec_scale_add(&goal_pos, &objp->pos, &objp->orient.v.fvec, 100.0f);
4613
4614         Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pos;
4615 }
4616
4617 //      Fly to desired safe point.
4618 // Returns distance to that point.
4619 float ai_safety_goto_spot(object *objp)
4620 {
4621         float   dot, dist;
4622         ai_info *aip;
4623         vector  vec_to_goal;
4624         ship_info       *sip;
4625         float   dot_val;
4626
4627         sip = &Ship_info[Ships[objp->instance].ship_info_index];
4628
4629         aip = &Ai_info[Ships[objp->instance].ai_index];
4630         dist = vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
4631         dot = vm_vec_dot(&vec_to_goal, &objp->orient.v.fvec);
4632
4633         dot_val = (1.1f + dot) / 2.0f;
4634         if (dist > 200.0f) {
4635                 set_accel_for_target_speed(objp, sip->max_speed * dot_val);
4636         } else
4637                 set_accel_for_target_speed(objp, sip->max_speed * dot_val * (dist/200.0f + 0.2f));
4638
4639         return dist;
4640 }
4641
4642 void ai_safety_circle_spot(object *objp)
4643 {
4644         vector  goal_point;
4645         ship_info       *sip;
4646         float           dot;
4647
4648         sip = &Ship_info[Ships[objp->instance].ship_info_index];
4649
4650         goal_point = Ai_info[Ships[objp->instance].ai_index].goal_point;
4651         dot = turn_towards_tangent(objp, &goal_point, 250.0f);  //      Increased from 50 to 250 to make circling not look so wacky.
4652
4653         set_accel_for_target_speed(objp, 0.5f * (1.0f + dot) * sip->max_speed/4.0f);
4654
4655 //      float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
4656 //      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));
4657
4658 }
4659
4660 //      --------------------------------------------------------------------------
4661 void ai_safety()
4662 {
4663         ai_info *aip;
4664
4665         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4666
4667         switch (aip->submode) {
4668         case AISS_1:
4669                 ai_safety_pick_spot(Pl_objp);
4670                 aip->submode = AISS_2;
4671                 aip->submode_start_time = Missiontime;
4672                 break;
4673         case AISS_1a:   //      Pick a safe point because we just got whacked!
4674                 Int3();
4675                 break;
4676         case AISS_2:
4677                 if (ai_safety_goto_spot(Pl_objp) < 25.0f) {
4678                         aip->submode = AISS_3;
4679                         aip->submode_start_time = Missiontime;
4680                 }
4681                 break;
4682         case AISS_3:
4683                 ai_safety_circle_spot(Pl_objp);
4684                 break;
4685         default:
4686                 Int3();         //      Illegal submode for ai_safety();
4687                 break;
4688         }
4689 }
4690
4691 //      --------------------------------------------------------------------------
4692 //      make Pl_objp fly waypoints.
4693 void ai_waypoints()
4694 {
4695         int             wp_index;
4696         vector  *wp_cur, *wp_next;
4697         float           dot, dist_to_goal, dist_to_next, speed, dot_to_next;
4698         ship            *shipp = &Ships[Pl_objp->instance];
4699         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4700         waypoint_list   *wpl;
4701         ai_info *aip;
4702         vector  nvel_vec;
4703         float           mag;
4704         float           prev_dot_to_goal;
4705         vector  temp_vec;
4706         vector  *slop_vec;
4707
4708         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4709
4710         wp_index = aip->wp_index;
4711
4712         if (wp_index == -1) {
4713                 ai_start_waypoints(Pl_objp, 0, WPF_REPEAT);
4714                 wp_index = aip->wp_index;
4715                 aip->wp_dir = 1;
4716         }
4717
4718         wpl = &Waypoint_lists[Ai_info[Ships[Pl_objp->instance].ai_index].wp_list];
4719
4720         Assert(wpl->count);     // What? Is this zero? Probably wp_index never got initialized!
4721
4722         wp_cur = &wpl->waypoints[wp_index];
4723         wp_next = &wpl->waypoints[(wp_index+1) % wpl->count];
4724         speed = Pl_objp->phys_info.speed;
4725
4726         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, wp_cur);
4727         dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, wp_next);
4728
4729         //      Can't use fvec, need to use velocity vector because we aren't necessarily
4730         //      moving in the direction we're facing.
4731         // AL 23-3-98: Account for very small velocities by checking result of vm_vec_mag().
4732         //                                      If we don't vm_vec_copy_normalize() will think it is normalizing a null vector.
4733 //      if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4734         if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4735                 mag = 0.0f;
4736                 vm_vec_zero(&nvel_vec);
4737         } else {
4738                 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4739         }
4740
4741         //      If moving not-very-slowly and sliding, then try to slide at goal, rather than
4742         //      point at goal.
4743         slop_vec = NULL;
4744         if (mag < 1.0f) {
4745                 nvel_vec = Pl_objp->orient.v.fvec;
4746         } else if (mag > 5.0f) {
4747                 float   nv_dot;
4748                 nv_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4749                 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4750                         slop_vec = &temp_vec;
4751                         vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.v.fvec);
4752                 }
4753         }
4754
4755         //      If a wing leader, take turns more slowly, based on size of wing.
4756         int     scale;
4757
4758         if (Ai_info[Ships[Pl_objp->instance].ai_index].wing >= 0) {
4759                 scale = Wings[Ai_info[Ships[Pl_objp->instance].ai_index].wing].current_count;
4760                 scale = (int) ((scale+1)/2);
4761         } else {
4762                 scale = 1;
4763         }
4764
4765         if (dist_to_goal > 0.1f) {
4766                 ai_turn_towards_vector(wp_cur, Pl_objp, flFrametime, sip->srotation_time*3.0f*scale, slop_vec, NULL, 0.0f, 0);
4767         }
4768
4769         prev_dot_to_goal = aip->prev_dot_to_goal;
4770         dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_cur);
4771         dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_next);
4772         aip->prev_dot_to_goal = dot;
4773
4774         //      If there is no next point on the path, don't care about dot to next.
4775         if (wp_index + 1 >= wpl->count) {
4776                 dot_to_next = dot;
4777         }
4778
4779         // nprintf(("AI", "Wp #%i, dot = %6.3f, next dot = %6.3f, dist = %7.2f\n", wp_index, dot, dot_to_next, dist_to_goal));
4780
4781         if (Pl_objp->phys_info.speed < 0.0f) {
4782                 accelerate_ship(aip, 1.0f/32);
4783         } else if (prev_dot_to_goal > dot+0.01f) {
4784                 //      We are further from pointing at our goal this frame than last frame, so slow down.
4785                 set_accel_for_target_speed(Pl_objp, Pl_objp->phys_info.speed * 0.95f);
4786         } else if (dist_to_goal < 100.0f) {
4787                 float slew_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4788                 if (fl_abs(slew_dot) < 0.9f) {
4789                         accelerate_ship(aip, 0.0f);
4790                 } else if (dot < 0.88f + 0.1f*(100.0f - dist_to_goal)/100.0f) {
4791                         accelerate_ship(aip, 0.0f);
4792                 } else {
4793                         accelerate_ship(aip, 0.5f * dot * dot);
4794                 }
4795         } else {
4796                 float   dot1;
4797                 if (dist_to_goal < 250.0f) {
4798                         dot1 = dot*dot*dot;                             //      Very important to be pointing towards goal when nearby.  Note, cubing preserves sign.
4799                 } else {
4800                         if (dot > 0.0f) {
4801                                 dot1 = dot*dot;
4802                         } else {
4803                                 dot1 = dot;
4804                         }
4805                 }
4806
4807                 if (dist_to_goal > 100.0f + Pl_objp->radius * 2) {
4808                         if (dot < 0.2f) {
4809                                 dot1 = 0.2f;
4810                         }
4811                 }
4812
4813                 if (sip->flags & SIF_SMALL_SHIP) {
4814                         set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/5.0f);
4815                 } else {
4816                         set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/10.0f);
4817                 }
4818         }
4819
4820         //      Make sure not travelling too fast for someone to keep up.
4821         float   max_allowed_speed = 9999.9f;
4822
4823         if (shipp->wingnum != -1) {
4824                 max_allowed_speed = 0.9f * get_wing_lowest_max_speed(Pl_objp);
4825         }
4826
4827         // check if waypoint speed cap is set and adjust max speed
4828         if (aip->waypoint_speed_cap > 0) {
4829                 max_allowed_speed = (float) aip->waypoint_speed_cap;
4830         }
4831
4832         if (aip->prev_accel * shipp->current_max_speed > max_allowed_speed) {
4833                 accelerate_ship(aip, max_allowed_speed / shipp->current_max_speed);
4834         }
4835
4836         if (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f) {
4837                 vector  nearest_point;
4838                 float           r;
4839
4840                 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, wp_cur);
4841
4842                 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))) ||
4843                         ((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, wp_cur) < (MIN_DIST_TO_WAYPOINT_GOAL + fl_sqrt(Pl_objp->radius)))) {
4844                         wp_index++;
4845                         if (wp_index >= wpl->count)
4846                                 if (aip->wp_flags & WPF_REPEAT) {
4847                                         wp_index = 0;
4848                                 } else {
4849                                         int treat_as_ship;
4850
4851                                         // when not repeating waypoints -- mark the goal as done and put and entry into the mission log
4852                                         // we must be careful when dealing with wings.  A ship in a wing might be completing
4853                                         // a waypoint for for the entire wing, or it might be completing a goal for itself.  If
4854                                         // for itself and in a wing, treat the completion as we would a ship
4855                                         treat_as_ship = 1;
4856                                         if ( Ships[Pl_objp->instance].wingnum != -1 ) {
4857                                                 int type;
4858
4859                                                 // I don't think that you can fly waypoints as dynamic goals!!!
4860                                                 // -- This is legal, just stupid. -- Assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4861                                                 
4862                                                 //      Clean up from above Assert, just in case we ship without fixing it.  (Encountered by JimB on 2/9/98)
4863                                                 if ( (aip->active_goal == AI_GOAL_NONE) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC) ) {
4864                                                         aip->mode = AIM_NONE;
4865                                                         Int3(); //      Look at the ship, find out of it's supposed to be flying waypoints. -- MK.
4866                                                 }
4867
4868                                                 type = aip->goals[aip->active_goal].type;
4869                                                 if ( (type == AIG_TYPE_EVENT_WING) || (type == AIG_TYPE_PLAYER_WING) ) {
4870                                                         treat_as_ship = 0;
4871                                                 } else {
4872                                                         treat_as_ship = 1;
4873                                                 }
4874                                         }
4875
4876                                         // if the ship is not in a wing, remove the goal and continue on
4877                                         if ( treat_as_ship ) {
4878                                                 ai_mission_goal_complete( aip );                                        // this call should reset the AI mode
4879                                                 mission_log_add_entry(LOG_WAYPOINTS_DONE, Ships[Pl_objp->instance].ship_name, wpl->name, -1 );
4880                                         } else {
4881                                                 // this ship is in a wing.  We must mark the goal as being completed for all ships
4882                                                 // in the wing.  We will also mark an entry in the log that the wing completed the goal
4883                                                 // not the individual ship.
4884                                                 ai_mission_wing_goal_complete( Ships[Pl_objp->instance].wingnum, &(aip->goals[aip->active_goal]) );
4885                                                 mission_log_add_entry( LOG_WAYPOINTS_DONE, Wings[Ships[Pl_objp->instance].wingnum].name, wpl->name, -1 );
4886                                         }
4887                                         //wp_index = wpl->count-1;
4888                                 }
4889
4890                         aip->wp_index = wp_index;
4891                 }
4892         }
4893 }
4894
4895 //      Make Pl_objp avoid En_objp
4896 //      Not like evading.  This is for avoiding a collision!
4897 //      Note, use sliding if available.
4898 void avoid_ship()
4899 {
4900         //      To avoid an object, turn towards right or left vector until facing away from object.
4901         //      To choose right vs. left, pick one that is further from center of avoid object.
4902         //      Keep turning away from until pointing away from ship.
4903         //      Stay in avoid mode until at least 3 enemy ship radii away.
4904
4905         //      Speed setting:
4906         //      If inside sphere, zero speed and turn towards outside.
4907         //      If outside sphere, inside 2x sphere, set speed percent of max to:
4908         //              max(away_dot, (dist-rad)/rad)
4909         //      where away_dot is dot(Pl_objp->v.fvec, vec_En_objp_to_Pl_objp)
4910
4911         vector  vec_to_enemy;
4912         float           away_dot;
4913         float           dist;
4914         ship            *shipp = &Ships[Pl_objp->instance];
4915         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4916         ai_info *aip = &Ai_info[shipp->ai_index];
4917         vector  player_pos, enemy_pos;
4918
4919         // if we're avoiding a stealth ship, then we know where he is, update with no error
4920         if ( is_object_stealth_ship(En_objp) ) {
4921                 update_ai_stealth_info_with_error(aip/*, 1*/);
4922         }
4923
4924         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
4925         vm_vec_sub(&vec_to_enemy, &enemy_pos, &Pl_objp->pos);
4926
4927         dist = vm_vec_normalize(&vec_to_enemy);
4928         away_dot = -vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_to_enemy);
4929         
4930         if ((sip->max_vel.xyz.x > 0.0f) || (sip->max_vel.xyz.y > 0.0f)) {
4931                 if (vm_vec_dot(&Pl_objp->orient.v.rvec, &vec_to_enemy) > 0.0f) {
4932                         AI_ci.sideways = -1.0f;
4933                 } else {
4934                         AI_ci.sideways = 1.0f;
4935                 }
4936                 if (vm_vec_dot(&Pl_objp->orient.v.uvec, &vec_to_enemy) > 0.0f) {
4937                         AI_ci.vertical = -1.0f;
4938                 } else {
4939                         AI_ci.vertical = 1.0f;
4940                 }
4941         }               
4942
4943         //nprintf(("AI", "Frame %i: Sliding: %s %s\n", Framecount, AI_ci.sideways < 0 ? "left" : "right", AI_ci.vertical < 0 ? "down" : "up" ));
4944         // nprintf(("AI", "away_dot = %6.3f, dist = %7.2f, dist/radsum = %6.3f\n", away_dot, dist, dist/(Pl_objp->radius + En_objp->radius)));
4945
4946         //      If in front of enemy, turn away from it.
4947         //      If behind enemy, try to get fully behind it.
4948         if (away_dot < 0.0f) {
4949                 turn_away_from_point(Pl_objp, &enemy_pos, Pl_objp->phys_info.speed);
4950         } else {
4951                 vector  goal_pos;
4952
4953                 vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.fvec, -100.0f);
4954                 turn_towards_point(Pl_objp, &goal_pos, NULL, Pl_objp->phys_info.speed);
4955         }
4956
4957         //      Set speed.
4958         float   radsum = Pl_objp->radius + En_objp->radius;
4959
4960         if (dist < radsum)
4961                 accelerate_ship(aip, max(away_dot, 0.2f));
4962         else if (dist < 2*radsum)
4963                 accelerate_ship(aip, max(away_dot, (dist - radsum) / radsum)+0.2f);
4964         else
4965                 accelerate_ship(aip, 1.0f);
4966
4967 }
4968
4969 //      Maybe it's time to resume the previous AI mode in aip->previous_mode.
4970 //      Each type of previous_mode has its own criteria on when to resume.
4971 //      Return true if previous mode was resumed.
4972 int maybe_resume_previous_mode(object *objp, ai_info *aip)
4973 {
4974         //      Only (maybe) resume previous goal if current goal is dynamic.
4975         if (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC)
4976                 return 0;
4977
4978         if (aip->mode == AIM_EVADE_WEAPON) {
4979                 if (timestamp_elapsed(aip->mode_time) || (((aip->nearest_locked_object == -1) || (Objects[aip->nearest_locked_object].type != OBJ_WEAPON)) && (aip->danger_weapon_objnum == -1))) {
4980                         Assert(aip->previous_mode != AIM_EVADE_WEAPON);
4981                         aip->mode = aip->previous_mode;
4982                         aip->submode = aip->previous_submode;
4983                         aip->submode_start_time = Missiontime;
4984                         aip->active_goal = AI_GOAL_NONE;
4985                         aip->mode_time = -1;                    //      Means do forever.
4986                         return 1;
4987                 }
4988         } else if ( aip->previous_mode == AIM_GUARD) {
4989                 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
4990                         object  *guard_objp;
4991                         float   dist;
4992
4993                         guard_objp = &Objects[aip->guard_objnum];
4994                         dist = vm_vec_dist_quick(&guard_objp->pos, &objp->pos);
4995
4996                         //      If guarding ship is far away from guardee and enemy is far away from guardee,
4997                         //      then stop chasing and resume guarding.
4998                         if (dist > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4999                                 if ((En_objp != NULL) && (En_objp->type == OBJ_SHIP)) {
5000                                         if (vm_vec_dist_quick(&guard_objp->pos, &En_objp->pos) > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
5001                                                 Assert(aip->previous_mode == AIM_GUARD);
5002                                                 aip->mode = aip->previous_mode;
5003                                                 aip->submode = AIS_GUARD_PATROL;
5004                                                 aip->active_goal = AI_GOAL_NONE;
5005                                                 return 1;
5006                                         }
5007                                 }
5008                         }
5009                 }
5010         }
5011
5012         return 0;
5013
5014 }
5015
5016 //      Call this function if you want something to happen on average every N quarters of a second.
5017 //      The truth value returned by this function will be the same for any given quarter second interval.
5018 //      The value "num" is only passed in to get asynchronous behavior for different objects.
5019 //      modulus == 1 will always return true.
5020 //      modulus == 2 will return true half the time.
5021 //      modulus == 16 will return true for one quarter second interval every four seconds.
5022 int static_rand_timed(int num, int modulus)
5023 {
5024         if (modulus < 2)
5025                 return 1;
5026         else {
5027                 int     t;
5028
5029                 t = Missiontime >> 18;          //      Get time in quarters of a second
5030                 t += num;
5031
5032                 return !(t % modulus);
5033         }
5034 }
5035
5036 //      Maybe fire afterburner based on AI class
5037 int ai_maybe_fire_afterburner(object *objp, ai_info *aip)
5038 {
5039         if (aip->ai_class == 0)
5040                 return 0;               //      Lowest level never aburners away
5041         else  {
5042                 //      Maybe don't afterburner because of a potential collision with the player.
5043                 //      If not multiplayer, near player and player in front, probably don't afterburner.
5044                 if (!(Game_mode & GM_MULTIPLAYER)) {
5045                         if (Ships[objp->instance].team == Player_ship->team) {
5046                                 float   dist;
5047
5048                                 dist = vm_vec_dist_quick(&objp->pos, &Player_obj->pos) - Player_obj->radius - objp->radius;
5049                                 if (dist < 150.0f) {
5050                                         vector  v2p;
5051                                         float           dot;
5052
5053                                         vm_vec_normalized_dir(&v2p, &Player_obj->pos, &objp->pos);
5054                                         dot = vm_vec_dot(&v2p, &objp->orient.v.fvec);
5055
5056                                         if (dot > 0.0f) {
5057                                                 if (dot * dist > 50.0f)
5058                                                         return 0;
5059                                         }
5060                                 }
5061                         }
5062                 }
5063
5064                 if (aip->ai_class >= Num_ai_classes-2)
5065                         return 1;               //      Highest two levels always aburner away.
5066                 else {
5067                         return static_rand_timed(objp-Objects, Num_ai_classes - aip->ai_class);
5068                 }
5069         }
5070 }
5071
5072 //      Maybe engage afterburner after being hit by an object.
5073 void maybe_afterburner_after_ship_hit(object *objp, ai_info *aip, object *en_objp)
5074 {
5075         //      Only do if facing a little away.
5076         if (en_objp != NULL) {
5077                 vector  v2e;
5078
5079                 vm_vec_normalized_dir(&v2e, &en_objp->pos, &objp->pos);
5080                 if (vm_vec_dot(&v2e, &objp->orient.v.fvec) > -0.5f)
5081                         return;
5082         }
5083
5084         if (!( objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5085                 if (ai_maybe_fire_afterburner(objp, aip)) {
5086                         afterburners_start(objp);
5087                         aip->afterburner_stop_time = Missiontime + F1_0/2;
5088                 }
5089         }
5090 }
5091
5092 //      Return true if object *objp is an instructor.
5093 //      Is an instructor if name begins INSTRUCTOR_SHIP_NAME else not.
5094 int is_instructor(object *objp)
5095 {
5096         return !strnicmp(Ships[objp->instance].ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME));
5097 }
5098
5099 //      Evade the weapon aip->danger_weapon_objnum
5100 //      If it's not valid, do a quick out.
5101 //      Evade by accelerating hard.
5102 //      If necessary, turn hard left or hard right.
5103 void evade_weapon()
5104 {
5105         object  *weapon_objp = NULL;
5106         object  *unlocked_weapon_objp = NULL, *locked_weapon_objp = NULL;
5107         vector  weapon_pos, player_pos, goal_point;
5108         vector  vec_from_enemy;
5109         float           dot_from_enemy, dot_to_enemy;
5110         float           dist;
5111         ship            *shipp = &Ships[Pl_objp->instance];
5112         ai_info *aip = &Ai_info[shipp->ai_index];
5113
5114         if (is_instructor(Pl_objp))
5115                 return;
5116
5117         //      Make sure we're actually being attacked.
5118         //      Favor locked objects.
5119         if (aip->nearest_locked_object != -1) {
5120                 if (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)
5121                         locked_weapon_objp = &Objects[aip->nearest_locked_object];
5122         }
5123         
5124         if (aip->danger_weapon_objnum != -1)
5125                 if (Objects[aip->danger_weapon_objnum].signature == aip->danger_weapon_signature)
5126                         unlocked_weapon_objp = &Objects[aip->danger_weapon_objnum];
5127                 else
5128                         aip->danger_weapon_objnum = -1;         //      Signatures don't match, so no longer endangered.
5129
5130         if (locked_weapon_objp != NULL) {
5131                 if (unlocked_weapon_objp != NULL) {
5132                         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))
5133                                 weapon_objp = locked_weapon_objp;
5134                         else
5135                                 weapon_objp = unlocked_weapon_objp;
5136                 } else
5137                         weapon_objp = locked_weapon_objp;
5138         } else if (unlocked_weapon_objp != NULL)
5139                 weapon_objp = unlocked_weapon_objp;
5140         else {
5141                 if (aip->mode == AIM_EVADE_WEAPON)
5142                         maybe_resume_previous_mode(Pl_objp, aip);
5143                 return;
5144         }
5145
5146         Assert(weapon_objp != NULL);
5147
5148         if (weapon_objp->type != OBJ_WEAPON) {
5149                 if (aip->mode == AIM_EVADE_WEAPON)
5150                         maybe_resume_previous_mode(Pl_objp, aip);
5151                 return;
5152         }
5153         
5154         weapon_pos = weapon_objp->pos;
5155         player_pos = Pl_objp->pos;
5156
5157         //      Make speed based on skill level, varying at highest skill level, which is harder to hit.
5158         accelerate_ship(aip, 1.0f);
5159
5160         dist = vm_vec_normalized_dir(&vec_from_enemy, &player_pos, &weapon_pos);
5161
5162         dot_to_enemy = -vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_from_enemy);
5163         dot_from_enemy = vm_vec_dot(&weapon_objp->orient.v.fvec, &vec_from_enemy);
5164         //nprintf(("AI", "dot from enemy = %7.3f\n", dot_from_enemy));
5165
5166         //      If shot is incoming...
5167         if (dot_from_enemy < 0.3f) {
5168                 if (weapon_objp == unlocked_weapon_objp)
5169                         aip->danger_weapon_objnum = -1;
5170                 return;
5171         } else if (dot_from_enemy > 0.7f) {
5172                 if (dist < 200.0f) {
5173                         if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5174                                 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
5175                                         //nprintf(("AI", "Frame %i, turning on afterburner.\n", AI_FrameCount));
5176                                         afterburners_start(Pl_objp);
5177                                         aip->afterburner_stop_time = Missiontime + F1_0/2;
5178                                 }
5179                         }
5180                 }
5181
5182                 //      If we're sort of pointing towards it...
5183                 if ((dot_to_enemy < -0.5f) || (dot_to_enemy > 0.5f)) {
5184                         float   rdot;
5185
5186                         //      Turn hard left or right, depending on which gets out of way quicker.
5187                         rdot = vm_vec_dot(&Pl_objp->orient.v.rvec, &vec_from_enemy);
5188
5189                         if ((rdot < -0.5f) || (rdot > 0.5f))
5190                                 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.v.rvec, -200.0f);
5191                         else
5192                                 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.v.rvec, 200.0f);
5193
5194                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
5195                 }
5196         }
5197
5198 }
5199
5200 //      Use sliding and backwards moving to face enemy.
5201 //      (Coded 2/20/98.  Works fine, but it's hard to see how to integrate it into the AI system.
5202 //       Typically ships are moving so fast that a little sliding isn't enough to gain an advantage.
5203 //       It's currently used to avoid collisions and could be used to evade weapon fire, but the latter
5204 //       would be frustrating, I think.
5205 //       This function is currently not called.)
5206 void slide_face_ship()
5207 {
5208         ship_info       *sip;
5209
5210         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
5211
5212         //      If can't slide, return.
5213         if ((sip->max_vel.xyz.x == 0.0f) && (sip->max_vel.xyz.y == 0.0f))
5214                 return;
5215
5216         vector  goal_pos;
5217         float           dot_from_enemy, dot_to_enemy;
5218         vector  vec_from_enemy, vec_to_goal;
5219         float           dist;
5220         float           up, right;
5221         ai_info         *aip;
5222
5223         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
5224
5225         dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
5226
5227         ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
5228
5229         dot_from_enemy = vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.fvec);
5230         dot_to_enemy = -vm_vec_dot(&vec_from_enemy, &Pl_objp->orient.v.fvec);
5231
5232         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.rvec) > 0.0f)
5233                 right = 1.0f;
5234         else
5235                 right = -1.0f;
5236
5237         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.uvec) > 0.0f)
5238                 up = 1.0f;
5239         else
5240                 up = -1.0f;
5241
5242         vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.rvec, right * 200.0f);
5243         vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.uvec, up * 200.0f);
5244
5245         vm_vec_normalized_dir(&vec_to_goal, &goal_pos, &Pl_objp->pos);
5246
5247         if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.v.rvec) > 0.0f)
5248                 AI_ci.sideways = 1.0f;
5249         else
5250                 AI_ci.sideways = -1.0f;
5251
5252         if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.v.uvec) > 0.0f)
5253                 AI_ci.vertical = 1.0f;
5254         else
5255                 AI_ci.vertical = -1.0f;
5256
5257         if (dist < 200.0f) {
5258                 if (dot_from_enemy < 0.7f)
5259                         accelerate_ship(aip, -1.0f);
5260                 else
5261                         accelerate_ship(aip, dot_from_enemy + 0.5f);
5262         } else {
5263                 if (dot_from_enemy < 0.7f) {
5264                         accelerate_ship(aip, 0.2f);
5265                 } else {
5266                         accelerate_ship(aip, 1.0f);
5267                 }
5268         }
5269 }
5270
5271 //      General code for handling one ship evading another.
5272 //      Problem: This code is also used for avoiding an impending collision.
5273 //      In such a case, it is not good to go to max speed, which is often good
5274 //      for a certain kind of evasion.
5275 void evade_ship()
5276 {
5277         vector  player_pos, enemy_pos, goal_point;
5278         vector  vec_from_enemy;
5279         float           dot_from_enemy;
5280         float           dist;
5281         ship            *shipp = &Ships[Pl_objp->instance];
5282         ship_info       *sip = &Ship_info[shipp->ship_info_index];
5283         ai_info *aip = &Ai_info[shipp->ai_index];
5284         float           bank_override = 0.0f;
5285
5286         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
5287
5288         //      Make speed based on skill level, varying at highest skill level, which is harder to hit.
5289         if (Game_skill_level == NUM_SKILL_LEVELS-1) {
5290                 int     rand_int;
5291                 float   accel_val;
5292
5293                 rand_int = static_rand(Pl_objp-Objects);
5294                 accel_val = (float) (((Missiontime^rand_int) >> 14) & 0x0f)/32.0f + 0.5f;
5295                 accelerate_ship(aip, accel_val);
5296                 //nprintf(("AI", "Accel value = %7.3f\n", accel_val));
5297         } else
5298                 accelerate_ship(aip, (float) (Game_skill_level+2) / (NUM_SKILL_LEVELS+1));
5299
5300         if ((Missiontime - aip->submode_start_time > F1_0/2) && (sip->afterburner_fuel_capacity > 0.0f)) {
5301                 float percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
5302                 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
5303                         afterburners_start(Pl_objp);
5304                         aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
5305                 }
5306         }
5307
5308         vm_vec_sub(&vec_from_enemy, &player_pos, &enemy_pos);
5309
5310         dist = vm_vec_normalize(&vec_from_enemy);
5311         dot_from_enemy = vm_vec_dot(&En_objp->orient.v.fvec, &vec_from_enemy);
5312
5313         if (dist > 250.0f) {
5314                 vector  gp1, gp2;
5315                 //      If far away from enemy, circle, going to nearer of point far off left or right wing
5316                 vm_vec_scale_add(&gp1, &enemy_pos, &En_objp->orient.v.rvec, 250.0f);
5317                 vm_vec_scale_add(&gp2, &enemy_pos, &En_objp->orient.v.rvec, -250.0f);
5318                 if (vm_vec_dist_quick(&gp1, &Pl_objp->pos) < vm_vec_dist_quick(&gp2, &Pl_objp->pos))
5319                         goal_point = gp1;
5320                 else
5321                         goal_point = gp2;
5322         } else if (dot_from_enemy < 0.1f) {
5323                 //      If already close to behind, goal is to get completely behind.
5324                 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.fvec, -1000.0f);
5325         } else if (dot_from_enemy > 0.9f) {
5326                 //      If enemy pointing almost right at self, and self pointing close to enemy, turn away from
5327                 vector  vec_to_enemy;
5328                 float           dot_to_enemy;
5329
5330                 vm_vec_sub(&vec_to_enemy, &enemy_pos, &player_pos);
5331
5332                 vm_vec_normalize(&vec_to_enemy);
5333                 dot_to_enemy = vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_to_enemy);
5334                 if (dot_to_enemy > 0.75f) {
5335                         //      Used to go to En_objp's right vector, but due to banking while turning, that
5336                         //      caused flying in an odd spiral.
5337                         vm_vec_scale_add(&goal_point, &enemy_pos, &Pl_objp->orient.v.rvec, 1000.0f);
5338                         if (dist < 100.0f)
5339                                 bank_override = Pl_objp->phys_info.speed; 
5340                 } else {
5341                         bank_override = Pl_objp->phys_info.speed;                       //      In enemy's sights, not pointing at him, twirl away.
5342                         // nprintf(("Mike", " Do sumpin' else."));
5343                         goto evade_ship_l1;
5344                 }
5345         } else {
5346 evade_ship_l1: ;
5347                 if (aip->ai_evasion > myrand()*100.0f/32767.0f) {
5348                         int     temp;
5349                         float   scale;
5350                         float   psrandval;      //      some value close to zero to choose whether to turn right or left.
5351
5352                         psrandval = (float) (((Missiontime >> 14) & 0x0f) - 8); //      Value between -8 and 7
5353                         psrandval = psrandval/16.0f;                                                    //      Value between -1/2 and 1/2 (approx)
5354
5355                         //      If not close to behind, turn towards his right or left vector, whichever won't cross his path.
5356                         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.rvec) > psrandval) {
5357                                 scale = 1000.0f;
5358                         } else {
5359                                 scale = -1000.0f;
5360                         }
5361
5362                         vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.rvec, scale);
5363
5364                         temp = ((Missiontime >> 16) & 0x07);
5365                         temp = ((temp * (temp+1)) % 16)/2 - 4;
5366                         if ((psrandval == 0) && (temp == 0))
5367                                 temp = 3;
5368
5369                         scale = 200.0f * temp;
5370
5371                         vm_vec_scale_add2(&goal_point, &En_objp->orient.v.uvec, scale);
5372                 } else {
5373                         //      No evasion this frame, but continue with previous turn.
5374                         //      Reason: If you don't, you lose rotational momentum.  Turning every other frame,
5375                         //      and not in between results in a very slow turn because of loss of momentum.
5376                         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))
5377                                 goal_point = aip->prev_goal_point;
5378                         else
5379                                 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.rvec, 100.0f);
5380                 }
5381         }
5382
5383         // nprintf(("Mike", "Goal point = %7.1f %7.1f %7.1f\n", goal_point.xyz.x, goal_point.xyz.y, goal_point.xyz.z));
5384         turn_towards_point(Pl_objp, &goal_point, NULL, bank_override);
5385
5386         aip->prev_goal_point = goal_point;
5387 }
5388
5389 //      --------------------------------------------------------------------------
5390 //      Fly in a manner making it difficult for opponent to attack.
5391 void ai_evade()
5392 {
5393         evade_ship();
5394 }
5395
5396 /*
5397 // -------------------------------------------------------------------
5398 //      Refine predicted enemy position because enemy will move while we move
5399 //      towards predicted enemy position.
5400 //      last_delta_vec is stuffed with size of polishing in last step.  This small amount
5401 //      can be used to perturb the predicted position to make firing not be exact.
5402 //      This function will almost always undershoot actual position, assuming both ships
5403 //      are moving at constant speed.  But with even one polishing step, the error should
5404 //      be under 1%. The number of polishing steps is specified in the parameter num_polish_steps.
5405 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)
5406 {
5407         int     iteration;
5408         vector  player_pos = pobjp->pos;
5409         vector  enemy_pos = *predicted_enemy_pos;
5410         physics_info    *en_physp = &eobjp->phys_info;
5411         float           time_to_enemy;
5412         vector  last_predicted_enemy_pos = *predicted_enemy_pos;
5413         
5414         vm_vec_zero(last_delta_vec);
5415
5416         for (iteration=0; iteration < num_polish_steps; iteration++) {
5417                 dist_to_enemy = vm_vec_dist_quick(predicted_enemy_pos, &player_pos);
5418                 time_to_enemy = compute_time_to_enemy(dist_to_enemy, pobjp, eobjp);
5419                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, time_to_enemy);
5420                 vm_vec_sub(last_delta_vec, predicted_enemy_pos, &last_predicted_enemy_pos);
5421                 last_predicted_enemy_pos= *predicted_enemy_pos;
5422         }
5423 }
5424 */
5425
5426 /*
5427 Relevant variables are:
5428         best_dot_to_enemy               best dot product to enemy in last BEST_DOT_TIME seconds
5429         best_dot_to_time                time at which best dot occurred
5430         best_dot_from_enemy     best dot product for enemy to player in last BEST_DOT_TIME seconds
5431         best_dot_from_time      time at which best dot occurred
5432         submode_start_time      time at which we entered the current submode
5433         previous_submode                previous submode, get it?
5434 Legal submodes are:
5435         CONTINUOUS_TURN vector_id {0..3 = right, -right, up, -up}
5436         ATTACK
5437         EVADE_SQUIGGLE
5438         EVADE_BRAKE
5439 */
5440
5441 float   G_collision_time;
5442 vector  G_predicted_pos, G_fire_pos;
5443
5444 /*
5445 void show_firing_diag()
5446 {
5447         float           dot;
5448         vector  v2t;
5449         vector  pos1, pos2;
5450         float           dist;
5451
5452         if (G_collision_time == 0.0f)
5453                 return;
5454
5455         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",
5456                 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));
5457         vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5458         dot = vm_vec_dot(&v2t, &Pl_objp->orient.v.fvec);
5459         mprintf(("Dot of v.fvec and vector to predicted position = %10.7f (%7.3f degrees)\n", dot, acos(dot)*180.0f/3.141592654f));
5460
5461         vm_vec_scale_add(&pos1, &En_objp->pos, &En_objp->phys_info.vel, G_collision_time);
5462         vm_vec_scale_add(&pos2, &G_fire_pos, &Pl_objp->orient.v.fvec, G_collision_time*300.0f);
5463         dist = vm_vec_dist(&pos1, &pos2);
5464
5465         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));
5466 }
5467 */
5468
5469 //      If:
5470 //              flags & WIF_PUNCTURE
5471 //      Then Select a Puncture weapon.
5472 //      Else
5473 //              Select Any ol' weapon.
5474 //      Returns primary_bank index.
5475 int ai_select_primary_weapon(object *objp, object *other_objp, int flags)
5476 {
5477         ship    *shipp = &Ships[objp->instance];
5478         ship_weapon *swp = &shipp->weapons;
5479         ship_info *sip;
5480
5481         //Assert( other_objp != NULL );
5482         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5483
5484         sip = &Ship_info[shipp->ship_info_index];
5485
5486         if (flags & WIF_PUNCTURE) {
5487                 if (swp->current_primary_bank >= 0) {
5488                         int     bank_index;
5489
5490                         bank_index = swp->current_primary_bank;
5491
5492                         if (Weapon_info[swp->primary_bank_weapons[bank_index]].wi_flags & WIF_PUNCTURE) {
5493                                 //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[bank_index]].name));
5494                                 return swp->current_primary_bank;
5495                         }
5496                 }
5497                 for (int i=0; i<swp->num_primary_banks; i++) {
5498                         int     weapon_info_index;
5499
5500                         weapon_info_index = swp->primary_bank_weapons[i];
5501
5502                         if (weapon_info_index > -1){
5503                                 if (Weapon_info[weapon_info_index].wi_flags & WIF_PUNCTURE) {
5504                                         swp->current_primary_bank = i;
5505                                         //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5506                                         return i;
5507                                 }
5508                         }
5509                 }
5510                 
5511                 // AL 26-3-98: If we couldn't find a puncture weapon, pick first available weapon if one isn't active
5512                 if ( swp->current_primary_bank < 0 ) {
5513                         if ( swp->num_primary_banks > 0 ) {
5514                                 swp->current_primary_bank = 0;
5515                         }
5516                 }
5517
5518         } else {                //      Don't need to be using a puncture weapon.
5519                 if (swp->current_primary_bank >= 0) {
5520                         if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)){
5521                                 return swp->current_primary_bank;
5522                         }
5523                 }
5524                 for (int i=0; i<swp->num_primary_banks; i++) {
5525                         if (swp->primary_bank_weapons[i] > -1) {
5526                                 if (!(Weapon_info[swp->primary_bank_weapons[i]].wi_flags & WIF_PUNCTURE)) {
5527                                         swp->current_primary_bank = i;
5528                                         nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5529                                         return i;
5530                                 }
5531                         }
5532                 }
5533                 //      Wasn't able to find a non-puncture weapon.  Stick with what we have.
5534         }
5535
5536         Assert( swp->current_primary_bank != -1 );              // get Alan or Allender
5537
5538         return swp->current_primary_bank;
5539 }
5540
5541 //      --------------------------------------------------------------------------
5542 //      Maybe link primary weapons.
5543 void set_primary_weapon_linkage(object *objp)
5544 {
5545         ship            *shipp;
5546         ai_info *aip;
5547
5548         shipp = &Ships[objp->instance];
5549         aip     = &Ai_info[shipp->ai_index];
5550
5551         shipp->flags &= ~SF_PRIMARY_LINKED;
5552
5553         if (Num_weapons > (int) (MAX_WEAPONS * 0.75f)) {
5554                 if (shipp->flags & SF_PRIMARY_LINKED)
5555                         nprintf(("AI", "Frame %i, ship %s: Unlinking primaries.\n", Framecount, shipp->ship_name));
5556                 shipp->flags &= ~SF_PRIMARY_LINKED;
5557                 return;         //      If low on slots, don't link.
5558         }
5559
5560         shipp->flags &= ~SF_PRIMARY_LINKED;
5561
5562         // AL: ensure target is a ship!
5563         if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
5564                 // If trying to destroy a big ship (i.e., not disable/disarm), always unleash all weapons
5565                 if ( ship_get_SIF(&Ships[Objects[aip->target_objnum].instance]) & SIF_BIG_SHIP) {
5566                         if ( aip->targeted_subsys == NULL ) {
5567                                 shipp->flags |= SF_PRIMARY_LINKED;
5568                                 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5569                                 return;
5570                         }
5571                 }
5572         }
5573
5574         // AL 2-11-98: If ship has a disarm or disable goal, don't link unless both weapons are
5575         //                                      puncture weapons
5576         if ( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) ) {
5577                 if ( aip->goals[aip->active_goal].ai_mode & (AI_GOAL_DISABLE_SHIP|AI_GOAL_DISARM_SHIP) ) {
5578                         ship_weapon     *swp;
5579                         swp = &shipp->weapons;
5580                         // only continue if both primaries are puncture weapons
5581                         if ( swp->num_primary_banks == 2 ) {
5582                                 if ( !(Weapon_info[swp->primary_bank_weapons[0]].wi_flags & WIF_PUNCTURE) ) 
5583                                         return;
5584                                 if ( !(Weapon_info[swp->primary_bank_weapons[1]].wi_flags & WIF_PUNCTURE) ) 
5585                                         return;
5586                         }
5587                 }
5588         }
5589
5590         //      Don't want all ships always linking weapons at start, so asynchronize.
5591         if (Missiontime < i2f(30))
5592                 return;
5593         else if (Missiontime < i2f(120)) {
5594                 int r = static_rand((Missiontime >> 17) ^ OBJ_INDEX(objp));
5595                 if ( (r&3) != 0)
5596                         return;
5597         }
5598
5599         if (shipp->weapon_energy > Link_energy_levels_always[Game_skill_level]) {
5600                 shipp->flags |= SF_PRIMARY_LINKED;
5601         } else if (shipp->weapon_energy > Link_energy_levels_maybe[Game_skill_level]) {
5602                 if (objp->hull_strength < Ship_info[shipp->ship_info_index].initial_hull_strength/3.0f)
5603                         shipp->flags |= SF_PRIMARY_LINKED;
5604         }
5605 }
5606
5607 //      --------------------------------------------------------------------------
5608 //      Fire the current primary weapon.
5609 //      *objp is the object to fire from.
5610 void ai_fire_primary_weapon(object *objp)
5611 {
5612         ship                    *shipp = &Ships[objp->instance];
5613         ship_weapon     *swp = &shipp->weapons;
5614         ship_info       *sip;
5615         ai_info         *aip;
5616         object          *enemy_objp;
5617
5618         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5619         sip = &Ship_info[shipp->ship_info_index];
5620
5621         aip = &Ai_info[shipp->ai_index];
5622
5623         //      If low on slots, fire a little less often.
5624         if (Num_weapons > (int) (0.9f * MAX_WEAPONS)) {
5625                 if (frand() > 0.5f) {
5626                         nprintf(("AI", "Frame %i, %s not fire.\n", Framecount, shipp->ship_name));
5627                         return;
5628                 }
5629         }
5630
5631         if (!Ai_firing_enabled){
5632                 return;
5633         }
5634
5635         if (aip->target_objnum != -1){
5636                 enemy_objp = &Objects[aip->target_objnum];
5637         } else {
5638                 enemy_objp = NULL;
5639         }
5640
5641         if ( (swp->current_primary_bank < 0) || (swp->current_primary_bank >= swp->num_primary_banks) || timestamp_elapsed(aip->primary_select_timestamp)) {
5642                 int     flags = 0;
5643                 // AL 2-11-98: If attacking any subsystem (not just engines), use disrupter weapon
5644 //              if ((aip->targeted_subsys != NULL) && (aip->targeted_subsys->system_info->type == SUBSYSTEM_ENGINE)) {
5645                 if ( aip->targeted_subsys != NULL ) {
5646                         flags = WIF_PUNCTURE;
5647                 }
5648                 ai_select_primary_weapon(objp, enemy_objp, flags);
5649                 ship_primary_changed(shipp);    // AL: maybe send multiplayer information when AI ship changes primaries
5650                 aip->primary_select_timestamp = timestamp(5 * 1000);    //      Maybe change primary weapon five seconds from now.
5651         }
5652
5653         //      If pointing nearly at predicted collision point of target, bash orientation to be perfectly pointing.
5654         float   dot;
5655         vector  v2t;
5656
5657 //      if (!IS_VEC_NULL(&G_predicted_pos)) {
5658         if (!( vm_vec_mag_quick(&G_predicted_pos) < AICODE_SMALL_MAGNITUDE )) {
5659                 if ( !vm_vec_cmp(&G_predicted_pos, &G_fire_pos) ) {
5660                         nprintf(("Warning", "Avoid NULL vector assert.. why are G_predicted_pos and G_fire_pos the same?\n"));
5661                 } else {
5662                         vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5663                         dot = vm_vec_dot(&v2t, &objp->orient.v.fvec);
5664                         if (dot > .998629534f){ //      if within 3.0 degrees of desired heading, bash
5665                                 vm_vector_2_matrix(&objp->orient, &v2t, &objp->orient.v.uvec, NULL);
5666                         }
5667                 }
5668         }
5669
5670         //      Make sure not firing at a protected ship unless firing at a live subsystem.
5671         //      Note: This happens every time the ship tries to fire, perhaps every frame.
5672         //      Should be wrapped in a timestamp, same one that enables it to fire, but that is complicated
5673         //      by multiple banks it can fire from.
5674         if (aip->target_objnum != -1) {
5675                 object  *tobjp = &Objects[aip->target_objnum];
5676                 if (tobjp->flags & OF_PROTECTED) {
5677                         if (aip->targeted_subsys != NULL) {
5678                                 int     type;
5679
5680                                 type = aip->targeted_subsys->system_info->type;
5681                                 if (ship_get_subsystem_strength(&Ships[tobjp->instance], type) == 0.0f) {
5682                                         aip->target_objnum = -1;
5683                                         return;
5684                                 }
5685                         } else {
5686                                 aip->target_objnum = -1;
5687                                 return;
5688                         }
5689                 }
5690         }
5691
5692         //      If enemy is protected, not firing a puncture weapon and enemy's hull is low, don't fire.
5693         if ((enemy_objp != NULL) && (enemy_objp->flags & OF_PROTECTED)) {
5694                 // AL: 3-6-98: Check if current_primary_bank is valid
5695                 if ((enemy_objp->hull_strength < 750.0f) && 
5696                         ((aip->targeted_subsys == NULL) || (enemy_objp->hull_strength < aip->targeted_subsys->current_hits + 50.0f)) &&
5697                         (swp->current_primary_bank >= 0) ) {
5698                         if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)) {
5699                                 //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));
5700                                 swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(1000);
5701                                 return;
5702                         }
5703
5704                         /*
5705                         int     num_attacking;
5706                         num_attacking = num_enemies_attacking(enemy_objp-Objects);
5707                         if (enemy_objp->hull_strength / num_attacking < 200.0f) {
5708                                 if (frand() < 0.75f) {
5709                                         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));
5710                                         swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(500);
5711                                         return;
5712                                 }
5713                         }
5714                         */
5715                 }
5716         }
5717
5718         set_primary_weapon_linkage(objp);
5719         
5720         // I think this will properly solve the problem
5721         // fire non-streaming weapons
5722         ship_fire_primary(objp, 0);
5723         
5724         // fire streaming weapons
5725         shipp->flags |= SF_TRIGGER_DOWN;
5726         ship_fire_primary(objp, 1);
5727         shipp->flags &= ~SF_TRIGGER_DOWN;
5728 }
5729
5730 //      --------------------------------------------------------------------------
5731 //      Return number of nearby enemy fighters.
5732 //      threshold is the distance within which a ship is considered near.
5733 //
5734 // input:       enemy_team_mask =>      teams that are considered as an enemy
5735 //                              pos                                     =>      world position to measure ship distances from
5736 //                              threshold                       =>      max distance from pos to be considered "near"
5737 //
5738 // exit:                number of ships within threshold units of pos
5739 int num_nearby_fighters(int enemy_team_mask, vector *pos, float threshold)
5740 {
5741         ship_obj        *so;
5742         object  *ship_objp;
5743         int             count = 0;
5744
5745         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5746
5747                 ship_objp = &Objects[so->objnum];
5748
5749                 if (Ships[ship_objp->instance].team & enemy_team_mask) {
5750                         if (Ship_info[Ships[ship_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) {
5751                                 if (vm_vec_dist_quick(pos, &ship_objp->pos) < threshold)
5752                                         count++;
5753                         }
5754                 }
5755         }
5756
5757         return count;
5758 }
5759
5760 //      --------------------------------------------------------------------------
5761 //      Select secondary weapon to fire.
5762 //      Currently, 1/16/98:
5763 //              If 0 secondary weapons available, return -1
5764 //              If 1 available, use it.
5765 //              If 2 or more, if the current weapon is one of them, stick with it, otherwise choose a random one.
5766 //      priority1 and priority2 are Weapon_info[] bitmasks such as WIF_HOMING_ASPECT.  If any weapon has any bit in priority1
5767 //      set, that weapon will be selected.  If not, apply to priority2.  If neither, return -1, meaning no weapon selected.
5768 //      Note, priorityX have default values of -1, meaning if not set, they will match any weapon.
5769 //      Return value:
5770 //              bank index
5771 //      Should do this:
5772 //              Favor aspect seekers when attacking small ships faraway.
5773 //              Favor rapid fire dumbfire when attacking a large ship.
5774 //              Ignore heat seekers because we're not sure how they'll work.
5775 void ai_select_secondary_weapon(object *objp, ship_weapon *swp, int priority1 = -1, int priority2 = -1)
5776 {
5777         int     num_weapon_types;
5778         int     weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
5779         int     i;
5780         int     ignore_mask;
5781         int     initial_bank;
5782
5783         initial_bank = swp->current_secondary_bank;
5784
5785         //      Ignore bombs unless one of the priorities asks for them to be selected.
5786         if (WIF_HUGE & (priority1 | priority2))
5787                 ignore_mask = 0;
5788         else
5789                 ignore_mask = WIF_HUGE;
5790
5791         if (!(WIF_BOMBER_PLUS & (priority1 | priority2)))
5792                 ignore_mask |= WIF_BOMBER_PLUS;
5793
5794 #ifndef NDEBUG
5795         for (i=0; i<MAX_WEAPON_TYPES; i++) {
5796                 weapon_id_list[i] = -1;
5797                 weapon_bank_list[i] = -1;
5798         }
5799 #endif
5800
5801         //      Stuff weapon_bank_list with bank index of available weapons.
5802         num_weapon_types = get_available_secondary_weapons(objp, weapon_id_list, weapon_bank_list);
5803
5804         int     priority2_index = -1;
5805
5806         for (i=0; i<num_weapon_types; i++) {
5807                 int     wi_flags;
5808
5809                 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5810                 if (!(wi_flags & ignore_mask)) {                                        //      Maybe bombs are illegal.
5811                         if (wi_flags & priority1) {
5812                                 swp->current_secondary_bank = weapon_bank_list[i];                              //      Found first priority, return it.
5813                                 break;
5814                         } else if (wi_flags & priority2)
5815                                 priority2_index = weapon_bank_list[i];  //      Found second priority, but might still find first priority.
5816                 }
5817         }
5818
5819         //      If didn't find anything above, then pick any secondary weapon.
5820         if (i == num_weapon_types) {
5821                 swp->current_secondary_bank = priority2_index;  //      Assume we won't find anything.
5822                 if (priority2_index == -1) {
5823                         for (i=0; i<num_weapon_types; i++) {
5824                                 int     wi_flags;
5825
5826                                 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5827                                 if (!(wi_flags & ignore_mask)) {                                        //      Maybe bombs are illegal.
5828                                         if (swp->secondary_bank_ammo[i] > 0) {
5829                                                 swp->current_secondary_bank = i;
5830                                                 break;
5831                                         }
5832                                 }
5833                         }
5834                 }
5835         }
5836
5837         //      If switched banks, force reacquisition of aspect lock.
5838         if (swp->current_secondary_bank != initial_bank) {
5839                 ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
5840                 
5841                 aip->aspect_locked_time = 0.0f;
5842                 aip->current_target_is_locked = 0;
5843         }
5844
5845
5846         ship_secondary_changed(&Ships[objp->instance]); // AL: let multiplayer know if secondary bank has changed
5847         // nprintf(("AI", "Ship %s selected weapon %s\n", Ships[objp->instance].ship_name, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
5848 }
5849
5850 //      Return number of objects homing on object *target_objp
5851 int compute_num_homing_objects(object *target_objp)
5852 {
5853         object  *objp;
5854         int             count = 0;
5855
5856         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
5857                 if (objp->type == OBJ_WEAPON) {
5858                         if (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_HOMING) {
5859                                 if (Weapons[objp->instance].homing_object == target_objp) {
5860                                         count++;
5861                                 }
5862                         }
5863                 }
5864         }
5865
5866         return count;
5867 }
5868
5869 //      Object *firing_objp just fired weapon weapon_index (index in Weapon_info).
5870 //      If it's a shockwave weapon, tell your team about it!
5871 void ai_maybe_announce_shockwave_weapon(object *firing_objp, int weapon_index)
5872 {
5873         if ((firing_objp->type == OBJ_SHIP) && (Weapon_info[weapon_index].shockwave_speed > 0.0f)) {
5874                 ship_obj        *so;
5875                 int             firing_ship_team;
5876
5877                 firing_ship_team = Ships[firing_objp->instance].team;
5878
5879                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5880                         object  *A = &Objects[so->objnum];
5881                         Assert(A->type == OBJ_SHIP);
5882
5883                         if (Ships[A->instance].team == firing_ship_team) {
5884                                 ai_info *aip = &Ai_info[Ships[A->instance].ai_index];
5885                                 // AL 1-5-98: only avoid shockwave if not docked or repairing
5886                                 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
5887                                         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_WEAPON;
5888                                 }
5889                         }
5890                 }
5891         }
5892 }
5893
5894 //      Return total payload of all incoming missiles.
5895 float compute_incoming_payload(object *target_objp)
5896 {
5897         missile_obj     *mo;
5898         float                   payload = 0.0f;
5899
5900         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
5901                 object  *objp;
5902
5903                 objp = &Objects[mo->objnum];
5904                 Assert(objp->type == OBJ_WEAPON);
5905                 if (Weapons[objp->instance].homing_object == target_objp) {
5906                         payload += Weapon_info[Weapons[objp->instance].weapon_info_index].damage;
5907                 }
5908         }
5909
5910         return payload;
5911 }
5912
5913 //      --------------------------------------------------------------------------
5914 //      Return true if OK for *aip to fire its current weapon at its current target.
5915 //      Only reason this function returns false is:
5916 //              weapon is a homer
5917 //              targeted at player
5918 //                      OR:     player has too many homers targeted at him
5919 //                                      Missiontime in that dead zone in which can't fire at this player
5920 //      Note: If player is attacking a ship, that ship is allowed to fire at player.  Otherwise, we get in a situation in which
5921 //      player is attacking a large ship, but that large ship is not defending itself with missiles.
5922 int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip)
5923 {
5924         int     num_homers = 0;
5925         object  *tobjp = &Objects[target_objnum];
5926
5927         if (target_objnum > -1) {
5928                 // AL 3-4-98: Ensure objp target is a ship first 
5929                 if ( tobjp->type == OBJ_SHIP ) {
5930
5931                         // should not get this far. check if ship is protected from beam and weapon is type beam
5932                         if ( (wip->wi_flags & WIF_BEAM) && (tobjp->flags & OF_BEAM_PROTECTED) ) {
5933                                 Int3();
5934                                 return 0;
5935                         }
5936                         if (Ship_info[Ships[tobjp->instance].ship_info_index].flags & SIF_SMALL_SHIP) {
5937                                 num_homers = compute_num_homing_objects(&Objects[target_objnum]);
5938                         }
5939                 }
5940
5941                 //      If player, maybe fire based on Skill_level and number of incoming weapons.
5942                 //      If non-player, maybe fire based on payload of incoming weapons.
5943                 if (wip->wi_flags & WIF_HOMING) {
5944                         if ((target_objnum > -1) && (tobjp->flags & OF_PLAYER_SHIP)) {
5945                                 if (Ai_info[Ships[tobjp->instance].ai_index].target_objnum != objnum) {
5946                                         //      Don't allow AI ships to fire at player for fixed periods of time based on skill level.
5947                                         //      With 5 skill levels, at Very Easy, they fire in 1/7 of every 10 second interval.
5948                                         //      At Easy, 2/7...at Expert, 5/7
5949                                         int t = ((Missiontime /(65536*10)) ^ target_objnum ^ 0x01) % (NUM_SKILL_LEVELS+2);
5950                                         if (t > Game_skill_level) {
5951                                                 //nprintf(("AI", "Not OK to fire homer at time thing %i\n", t));
5952                                                 return 0;
5953                                         }
5954                                 }
5955                                 //nprintf(("AI", " IS OK to fire homer at time thing %i ***\n", t));
5956                                 int     swarmers = 0;
5957                                 if (wip->wi_flags & WIF_SWARM)
5958                                         swarmers = 2;   //      Note, always want to be able to fire swarmers if no currently incident homers.
5959                                 if (Max_allowed_player_homers[Game_skill_level] < num_homers + swarmers) {
5960                                         return 0;
5961                                 }
5962                         } else if (num_homers > 3) {
5963                                 float   incoming_payload;
5964
5965                                 incoming_payload = compute_incoming_payload(&Objects[target_objnum]);
5966
5967                                 if (incoming_payload > tobjp->hull_strength) {
5968                                         return 0;
5969                                 }
5970                         }
5971                 }
5972         }
5973
5974         return 1;
5975 }
5976
5977 //      --------------------------------------------------------------------------
5978 //      Fire a secondary weapon.
5979 //      Maybe choose to fire a different one.
5980 //      priority1 and priority2 are optional parameters with defaults = -1
5981 int ai_fire_secondary_weapon(object *objp, int priority1, int priority2)
5982 {
5983         ship_weapon *swp;
5984         ship    *shipp;
5985         ship_info *sip;
5986         int             current_bank;
5987         int             rval = 0;
5988
5989 #ifndef NDEBUG
5990         if (!Ai_firing_enabled)
5991                 return rval;
5992 #endif
5993
5994         Assert( objp != NULL );
5995         Assert(objp->type == OBJ_SHIP);
5996         shipp = &Ships[objp->instance];
5997         swp = &shipp->weapons;
5998
5999         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
6000         sip = &Ship_info[shipp->ship_info_index];
6001
6002         //      Select secondary weapon.
6003         current_bank = swp->current_secondary_bank; //ai_select_secondary_weapon(objp, swp, priority1, priority2);
6004
6005         //nprintf(("AI", "Frame %i: Current bank = %i, ammo remaining = %i\n", Framecount, current_bank, swp->secondary_bank_ammo[current_bank]));
6006         if (current_bank == -1) {
6007                 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
6008                 return rval;
6009         }
6010
6011         Assert(current_bank < shipp->weapons.num_secondary_banks);
6012
6013         weapon_info     *wip = &Weapon_info[shipp->weapons.secondary_bank_weapons[current_bank]];
6014
6015         if ((wip->wi_flags & WIF_HOMING_ASPECT) && (!Ai_info[shipp->ai_index].current_target_is_locked)) {
6016                 //nprintf(("AI", "Not firing secondary weapon because not aspect locked.\n"));
6017                 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
6018         } else if ((wip->wi_flags & WIF_BOMB) || (vm_vec_dist_quick(&objp->pos, &En_objp->pos) > 50.0f)) {
6019                 //      This might look dumb, firing a bomb even if closer than 50 meters, but the reason is, if you're carrying
6020                 //      bombs, delivering them is probably more important than surviving.
6021                 ai_info *aip;
6022
6023                 aip = &Ai_info[shipp->ai_index];
6024                 
6025                 //      Note, maybe don't fire if firing at player and any homers yet fired.
6026                 //      Decreasing chance to fire the more homers are incoming on player.
6027                 if (check_ok_to_fire(OBJ_INDEX(objp), aip->target_objnum, wip)) {
6028                         if (ship_fire_secondary(objp)) {
6029                                 rval = 1;
6030                                 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
6031                                 //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));
6032                         }
6033
6034                 } else {
6035                         swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
6036                 }
6037         }
6038
6039         return rval;
6040 }
6041
6042 //      Return true if it looks like obj1, if continuing to move along current vector, will
6043 //      collide with obj2.
6044 int might_collide_with_ship(object *obj1, object *obj2, float dot_to_enemy, float dist_to_enemy, float duration)
6045 {
6046         if (obj1->phys_info.speed * duration + 2*(obj1->radius + obj2->radius) > dist_to_enemy)
6047                 if (dot_to_enemy > 0.8f - 2*(obj1->radius + obj2->radius)/dist_to_enemy)
6048                         return objects_will_collide(obj1, obj2, duration, 2.0f);
6049
6050 //              BABY - 
6051 //              CONDITION 1, dist_to_enemy < o1_rad + o2_rad + (obj1.speed + obj2.speed) * time + 50
6052         
6053         return 0;
6054
6055 }
6056
6057 //      --------------------------------------------------------------------------
6058 //      Return true if ship *objp firing a laser believes it will hit a teammate.
6059 int might_hit_teammate(object *firing_objp)
6060 {
6061         int             team;
6062         object  *objp;
6063         ship_obj        *so;
6064
6065         team = Ships[firing_objp->instance].team;
6066
6067         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6068                 objp = &Objects[so->objnum];
6069                 if (Ships[objp->instance].team == team) {
6070                         float           dist, dot;
6071                         vector  vec_to_objp;
6072
6073                         vm_vec_sub(&vec_to_objp, &firing_objp->pos, &objp->pos);
6074                         dist = vm_vec_mag_quick(&vec_to_objp);
6075                         dot = vm_vec_dot(&firing_objp->orient.v.fvec, &vec_to_objp)/dist;
6076                         if (might_collide_with_ship(firing_objp, objp, dot, dist, 2.0f))
6077                                 return 1;
6078                 }
6079         }
6080
6081         return 0;
6082
6083 }
6084
6085 //int   Team_not_fire_count=0, Team_hit_count = 0;
6086
6087 void render_all_ship_bay_paths(object *objp)
6088 {
6089         int             i,j,color;
6090         ship            *sp = &Ships[objp->instance];
6091         polymodel       *pm;
6092         model_path      *mp;
6093
6094         pm = model_get(sp->modelnum);
6095         vector  global_path_point;
6096         vertex  v, prev_vertex;
6097
6098         if ( pm->ship_bay == NULL )
6099                 return;
6100
6101         for ( i = 0; i < pm->ship_bay->num_paths; i++ ) {
6102                 mp = &pm->paths[pm->ship_bay->paths[i]];
6103
6104                 for ( j = 0; j < mp->nverts; j++ ) {
6105                         vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6106                         vm_vec_add2(&global_path_point, &objp->pos);
6107                         g3_rotate_vertex(&v, &global_path_point);
6108                         color = 255 - j*50;
6109                         if ( color < 50 ) 
6110                                 color = 100;
6111                         gr_set_color(0, color, 0);
6112
6113                         if ( j == mp->nverts-1 ) {
6114                                 gr_set_color(255, 0, 0);
6115                         }
6116
6117                         g3_draw_sphere( &v, 1.5f);
6118
6119                         if ( j > 0 )
6120                                 g3_draw_line(&v, &prev_vertex);
6121
6122                         prev_vertex = v;
6123         
6124                 }
6125         }
6126 }
6127
6128 // debug function to show all path points associated with an object
6129 void render_all_subsys_paths(object *objp)
6130 {
6131         int             i,j,color;
6132         ship            *sp = &Ships[objp->instance];
6133         polymodel       *pm;
6134         model_path      *mp;
6135
6136         pm = model_get(sp->modelnum);
6137         vector  global_path_point;
6138         vertex  v, prev_vertex;
6139
6140         if ( pm->ship_bay == NULL )
6141                 return;
6142
6143         for ( i = 0; i < pm->n_paths; i++ ) {
6144                 mp = &pm->paths[i];
6145                 for ( j = 0; j < mp->nverts; j++ ) {
6146                         vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6147                         vm_vec_add2(&global_path_point, &objp->pos);
6148                         g3_rotate_vertex(&v, &global_path_point);
6149                         color = 255 - j*50;
6150                         if ( color < 50 ) 
6151                                 color = 100;
6152                         gr_set_color(0, color, 0);
6153
6154                         if ( j == mp->nverts-1 ) {
6155                                 gr_set_color(255, 0, 0);
6156                         }
6157
6158                         g3_draw_sphere( &v, 1.5f);
6159
6160                         if ( j > 0 )
6161                                 g3_draw_line(&v, &prev_vertex);
6162
6163                         prev_vertex = v;
6164                 }
6165         }
6166 }
6167
6168 void render_path_points(object *objp)
6169 {
6170         ship            *shipp = &Ships[objp->instance];
6171         ai_info *aip = &Ai_info[shipp->ai_index];
6172         object  *dobjp;
6173         polymodel       *pm;
6174
6175         render_all_subsys_paths(objp);
6176         render_all_ship_bay_paths(objp);
6177
6178         if (aip->goal_objnum < 0)
6179                 return;
6180
6181         dobjp = &Objects[aip->goal_objnum];
6182         pm = model_get(Ships[dobjp->instance].modelnum);
6183         vector  dock_point, global_dock_point;
6184         vertex  v;
6185
6186         ship_model_start(&Objects[aip->goal_objnum]);
6187         if (pm->n_docks) {
6188                 dock_point = pm->docking_bays[0].pnt[0];
6189                 model_find_world_point(&global_dock_point, &dock_point, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6190                 g3_rotate_vertex(&v, &global_dock_point);
6191                 gr_set_color(255, 255, 255);
6192                 g3_draw_sphere( &v, 1.5f);
6193         }
6194
6195         if (aip->path_start != -1) {
6196                 vertex          prev_vertex;
6197                 pnode                   *pp = &Path_points[aip->path_start];
6198                 int                     num_points = aip->path_length;
6199                 int                     i;
6200
6201                 for (i=0; i<num_points; i++) {
6202                         vertex  v0;
6203
6204                         g3_rotate_vertex( &v0, &pp->pos );
6205
6206                         gr_set_color(0, 128, 96);
6207                         if (i != 0)
6208                                 g3_draw_line(&v0, &prev_vertex);
6209
6210                         if (pp-Path_points == aip->path_cur)
6211                                 gr_set_color(255,255,0);
6212                         
6213                         g3_draw_sphere( &v0, 4.5f);
6214
6215                         //      Connect all the turrets that can fire upon this point to this point.
6216 /*                      if (0) { //pp->path_index != -1) {
6217                                 model_path      *pmp;
6218                                 mp_vert         *pmpv;
6219
6220                                 get_base_path_info(pp->path_index, aip->goal_objnum, &pmp, &pmpv);
6221
6222                                 if (pmpv->nturrets) {
6223                                         for (int j = 0; j<pmpv->nturrets; j++) {
6224                                                 vertex  v1;
6225                                                 vector  turret_pos;
6226                                                 ship_subsys     *ssp;
6227
6228                                                 ssp = ship_get_indexed_subsys(&Ships[Objects[aip->goal_objnum].instance], pmpv->turret_ids[j]);
6229
6230 model_find_world_point(&turret_pos, &ssp->system_info->pnt, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6231         
6232                                                 g3_rotate_vertex(&v1, &turret_pos);
6233                                                 gr_set_color(255, 255, 0);
6234                                                 g3_draw_line(&v0, &v1);
6235                                                 g3_draw_sphere( &v1, 1.5f);
6236                                         }
6237                                 }
6238                         } */
6239
6240                         prev_vertex = v0;
6241
6242                         pp++;
6243                 }
6244         }
6245
6246         ship_model_stop(&Objects[aip->goal_objnum]);
6247 }
6248
6249 // Return the distance that the current AI weapon will travel
6250 float ai_get_weapon_dist(ship_weapon *swp)
6251 {
6252         int     bank_num, weapon_num;
6253
6254         bank_num = swp->current_primary_bank;
6255         weapon_num = swp->primary_bank_weapons[bank_num];
6256
6257         //      If weapon_num is illegal, return a reasonable value.  A valid weapon
6258         //      will get selected when this ship tries to fire.
6259         if (weapon_num == -1) {
6260                 // Int3();
6261                 return 1000.0f;
6262         }
6263
6264         return Weapon_info[weapon_num].max_speed * Weapon_info[weapon_num].lifetime;
6265 }
6266
6267 float ai_get_weapon_speed(ship_weapon *swp)
6268 {
6269         int     bank_num, weapon_num;
6270
6271         bank_num = swp->current_primary_bank;
6272         if (bank_num < 0)
6273                 return 100.0f;
6274
6275         weapon_num = swp->primary_bank_weapons[bank_num];
6276
6277         if (weapon_num == -1) {
6278                 //Int3();
6279                 return 100.0f;
6280         }
6281
6282         return Weapon_info[weapon_num].max_speed;
6283 }
6284
6285 //      Compute the predicted position of a ship to be fired upon from a turret.
6286 //      This is based on position of firing gun, enemy object, weapon speed and skill level constraints.
6287 //      Return value in *predicted_enemy_pos.
6288 //      Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6289 //      *pobjp          object firing the weapon
6290 //      *eobjp          object being fired upon
6291 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)
6292 {
6293         ship    *shipp = &Ships[pobjp->instance];
6294         float   range_time;
6295
6296         //weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6297
6298         if (weapon_speed < 1.0f)
6299                 weapon_speed = 1.0f;
6300
6301         range_time = 2.0f;
6302
6303         //      Make it take longer for enemies to get player's allies in range based on skill level.
6304         if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team)
6305                 range_time += In_range_time[Game_skill_level];
6306
6307         //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6308
6309         if (time_enemy_in_range < range_time) {
6310                 float   dist;
6311
6312                 dist = vm_vec_dist_quick(&pobjp->pos, enemy_pos);
6313                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, time_enemy_in_range * dist/weapon_speed);
6314         } else {
6315                 float   collision_time, scale;
6316                 vector  rand_vec;
6317                 ai_info *aip = &Ai_info[shipp->ai_index];
6318
6319                 collision_time = compute_collision_time(enemy_pos, enemy_vel, gun_pos, weapon_speed);
6320
6321                 if (collision_time == 0.0f){
6322                         collision_time = 100.0f;
6323                 }
6324
6325                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, collision_time);
6326                 if (time_enemy_in_range > 2*range_time){
6327                         scale = (1.0f - aip->ai_accuracy) * 4.0f;
6328                 } else {
6329                         scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - time_enemy_in_range/(2*range_time)));
6330                 }               
6331
6332                 static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6333
6334                 vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6335                 G_collision_time = collision_time;
6336                 G_fire_pos = *gun_pos;
6337         }
6338
6339         G_predicted_pos = *predicted_enemy_pos;
6340 }
6341
6342 //      Compute the predicted position of a ship to be fired upon.
6343 //      This is based on current position of firing object, enemy object, relative position of gun on firing object,
6344 //      weapon speed and skill level constraints.
6345 //      Return value in *predicted_enemy_pos.
6346 //      Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6347 void set_predicted_enemy_pos(vector *predicted_enemy_pos, object *pobjp, object *eobjp, ai_info *aip)
6348 {
6349         float   weapon_speed, range_time;
6350         ship    *shipp = &Ships[pobjp->instance];
6351
6352         weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6353         weapon_speed = max(weapon_speed, 1.0f);         // set not less than 1
6354
6355         range_time = 2.0f;
6356
6357         //      Make it take longer for enemies to get player's allies in range based on skill level.
6358         // but don't bias team v. team missions
6359         if ( !((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) ) {
6360                 if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team) {
6361                         range_time += In_range_time[Game_skill_level];
6362                 }
6363         }
6364         //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6365
6366         if (aip->time_enemy_in_range < range_time) {
6367                 float   dist;
6368
6369                 dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6370                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, aip->time_enemy_in_range * dist/weapon_speed);
6371         } else {
6372                 float   collision_time;
6373                 vector  gun_pos, pnt;
6374                 polymodel *po = model_get( Ship_info[shipp->ship_info_index].modelnum );
6375
6376                 //      Compute position of gun in absolute space and use that as fire position.
6377                 if(po->gun_banks != NULL){
6378                         pnt = po->gun_banks[0].pnt[0];
6379                 } else {
6380                         pnt = Objects[shipp->objnum].pos;
6381                 }
6382                 vm_vec_unrotate(&gun_pos, &pnt, &pobjp->orient);
6383                 vm_vec_add2(&gun_pos, &pobjp->pos);
6384
6385                 collision_time = compute_collision_time(&eobjp->pos, &eobjp->phys_info.vel, &gun_pos, weapon_speed);
6386
6387                 if (collision_time == 0.0f) {
6388                         collision_time = 100.0f;
6389                 }
6390
6391                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, collision_time);
6392
6393                 // set globals
6394                 G_collision_time = collision_time;
6395                 G_fire_pos = gun_pos;
6396         }
6397
6398         // Now add error terms (1) regular aim (2) EMP (3) stealth
6399         float scale = 0.0f;
6400         vector rand_vec;
6401
6402         // regular skill level error in aim
6403         if (aip->time_enemy_in_range > 2*range_time) {
6404                 scale = (1.0f - aip->ai_accuracy) * 4.0f;
6405         } else {
6406                 scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - aip->time_enemy_in_range/(2*range_time)));
6407         }
6408
6409         // if this ship is under the effect of an EMP blast, throw his aim off a bit
6410         if (shipp->emp_intensity > 0.0f) {
6411                 // never go lower than 1/2 of the EMP effect max, otherwise things aren't noticeable
6412                 scale += (MAX_EMP_INACCURACY * (shipp->emp_intensity < 0.5f ? 0.5f : shipp->emp_intensity));
6413                 mprintf(("AI miss scale factor (EMP) %f\n",scale));
6414         }
6415
6416         // if stealthy ship, throw his aim off, more when farther away and when dot is small
6417         if ( aip->ai_flags & AIF_STEALTH_PURSIUT ) {
6418                 float dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6419                 vector temp;
6420                 vm_vec_sub(&temp, &eobjp->pos, &pobjp->pos);
6421                 vm_vec_normalize_quick(&temp);
6422                 float dot = vm_vec_dotprod(&temp, &pobjp->orient.v.fvec);
6423                 float st_err = 3.0f * (1.4f - dot) * (1.0f + dist / (get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST)) * (1 - aip->ai_accuracy);
6424                 scale += st_err;
6425                 // mprintf(("error term: %.1f, total %.1f, dot %.3f\n", st_err, scale, dot));
6426         }
6427
6428         // get a random vector that changes slowly over time (1x / sec)
6429         static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6430
6431         vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6432
6433         // set global
6434         G_predicted_pos = *predicted_enemy_pos;
6435 }
6436
6437 //      Handler of submode for Chase.  Go into a continuous turn for awhile.
6438 void ai_chase_ct()
6439 {
6440         vector          tvec;
6441         ship_info       *sip;
6442         ai_info         *aip;
6443
6444         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6445         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6446         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6447         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6448
6449         //      Make a continuous turn towards any combination of possibly negated
6450         // up and right vectors.
6451         tvec = Pl_objp->pos;
6452
6453         if (aip->submode_parm0 & 0x01)
6454                 vm_vec_add2(&tvec, &Pl_objp->orient.v.rvec);
6455         if (aip->submode_parm0 & 0x02)
6456                 vm_vec_sub2(&tvec, &Pl_objp->orient.v.rvec);
6457         if (aip->submode_parm0 & 0x04)
6458                 vm_vec_add2(&tvec, &Pl_objp->orient.v.uvec);
6459         if (aip->submode_parm0 & 0x08)
6460                 vm_vec_sub2(&tvec, &Pl_objp->orient.v.uvec);
6461
6462         //      Detect degenerate cases that cause tvec to be same as player pos.
6463         if (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
6464                 aip->submode_parm0 &= 0x05;
6465                 if (aip->submode_parm0 == 0)
6466                         aip->submode_parm0 = 1;
6467                 vm_vec_add2(&tvec, &Pl_objp->orient.v.rvec);
6468         }
6469
6470         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6471         accelerate_ship(aip, 1.0f);
6472 }
6473
6474 //      ATTACK submode handler for chase mode.
6475 void ai_chase_eb(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
6476 {
6477         vector  _pep;
6478         float           dot_to_enemy, dot_from_enemy;
6479
6480         compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
6481
6482         //      If we're trying to slow down to get behind, then point to turn towards is different.
6483         _pep = *predicted_enemy_pos;
6484         if ((dot_to_enemy > dot_from_enemy + 0.1f) || (dot_to_enemy > 0.9f))
6485                 vm_vec_scale_add(&_pep, &Pl_objp->pos, &En_objp->orient.v.fvec, 100.0f);
6486
6487         ai_turn_towards_vector(&_pep, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6488
6489         accelerate_ship(aip, 0.0f);
6490 }
6491
6492 //      Return time until weapon_objp might hit ship_objp.
6493 //      Assumes ship_objp is not moving.
6494 //      Returns negative time if not going to hit.
6495 //      This is a very approximate function, but is pretty fast.
6496 float ai_endangered_time(object *ship_objp, object *weapon_objp)
6497 {
6498         float           to_dot, from_dot, dist;
6499
6500         dist = compute_dots(ship_objp, weapon_objp, &to_dot, &from_dot);
6501
6502         //      Note, this is bogus.  It assumes only the weapon is moving.
6503         //      Only proceed if weapon sort of pointing at object and object pointing towards or away from weapon
6504         //      (Ie, if object moving at right angle to weapon, just continue for now...)
6505         if (weapon_objp->phys_info.speed < 1.0f)
6506                 return dist + 1.0f;
6507         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))
6508                 return dist / weapon_objp->phys_info.speed;
6509         else
6510                 return -1.0f;
6511 }
6512
6513 //      Return time until danger weapon could hit this ai object.
6514 //      Return negative time if not endangered.
6515 float ai_endangered_by_weapon(ai_info *aip)
6516 {
6517         object  *weapon_objp;
6518
6519         if (aip->danger_weapon_objnum == -1) {
6520                 return -1.0f;
6521         }
6522
6523         weapon_objp = &Objects[aip->danger_weapon_objnum];
6524
6525         if (weapon_objp->signature != aip->danger_weapon_signature) {
6526                 aip->danger_weapon_objnum = -1;
6527                 return -1.0f;
6528         }
6529
6530         return ai_endangered_time(&Objects[Ships[aip->shipnum].objnum], weapon_objp);
6531 }
6532
6533 //      Return true if this ship is near full strength.
6534 int ai_near_full_strength(object *objp, ship_info *sip)
6535 {
6536         return (objp->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp)/sip->shields > 0.8f);
6537 }
6538                                 
6539 //      Set acceleration while in attack mode.
6540 void attack_set_accel(ai_info *aip, float dist_to_enemy, float dot_to_enemy, float dot_from_enemy)
6541 {
6542         float   speed_ratio;
6543
6544         if (En_objp->phys_info.speed > 1.0f)
6545                 speed_ratio = Pl_objp->phys_info.speed/En_objp->phys_info.speed;
6546         else
6547                 speed_ratio = 5.0f;
6548
6549         //      Sometimes, told to attack slowly.  Allows to get in more hits.
6550         if (aip->ai_flags & AIF_ATTACK_SLOWLY) {
6551                 if ((dist_to_enemy > 200.0f) && (dist_to_enemy < 800.0f)) {
6552                         if ((dot_from_enemy < 0.9f) || ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
6553                                 //nprintf(("AI", " slowly "));
6554                                 accelerate_ship(aip, max(1.0f - (dist_to_enemy-200.0f)/600.0f, 0.1f));
6555                                 return;
6556                         }
6557                 } else
6558                         aip->ai_flags &= ~AIF_ATTACK_SLOWLY;
6559         }
6560
6561         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) {
6562                 //nprintf(("AI", "1"));
6563                 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6564                         if (dist_to_enemy > 800.0f) {
6565                                 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6566                                         float percent_left;
6567                                         ship    *shipp;
6568                                         ship_info *sip;
6569
6570                                         shipp = &Ships[Pl_objp->instance];
6571                                         sip = &Ship_info[shipp->ship_info_index];
6572
6573                                         if (sip->afterburner_fuel_capacity > 0.0f) {
6574                                                 percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
6575                                                 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
6576                                                         afterburners_start(Pl_objp);
6577                                                         aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
6578                                                 }
6579                                         }
6580                                 }
6581                         }
6582                 }
6583
6584                 accelerate_ship(aip, 1.0f);
6585         } else if ((Missiontime - aip->last_hit_time > F1_0*7)
6586                 && (En_objp->phys_info.speed < 10.0f) 
6587                 && (dist_to_enemy > 25.0f) 
6588                 && (dot_to_enemy > 0.8f)
6589                 && (dot_from_enemy < 0.8f)) {
6590                 accelerate_ship(aip, 0.0f);             //      No one attacking us, so don't need to move.
6591         } else if ((dot_from_enemy < 0.25f) && (dot_to_enemy > 0.5f)) {
6592                 set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed);
6593         } else if (Pl_objp->phys_info.speed < 15.0f) {
6594                 accelerate_ship(aip, 1.0f);
6595         } else if (Pl_objp->phys_info.speed > En_objp->phys_info.speed - 1.0f) {
6596                 if (dot_from_enemy > 0.75f)
6597                         accelerate_ship(aip, 1.0f);
6598                 else
6599                         set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed*0.75f + 3.0f);
6600         } else {
6601                 change_acceleration(aip, 0.5f);
6602         }
6603 }
6604
6605 //      Pl_objp (aip) tries to get behind En_objp.
6606 //      New on 2/21/98: If this ship can move backwards and slide, maybe do that to get behind.
6607 void get_behind_ship(ai_info *aip, ship_info *sip, float dist_to_enemy)
6608 {
6609         vector  new_pos;
6610         float           dot;
6611         vector  vec_from_enemy;
6612         float           dist;
6613
6614         dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
6615
6616         vm_vec_scale_add(&new_pos, &En_objp->pos, &En_objp->orient.v.fvec, -100.0f);            //      Pick point 100 units behind.
6617         ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6618
6619         dot = vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.fvec);
6620
6621         if (dot > 0.25f) {
6622                 accelerate_ship(aip, 1.0f);
6623         } else {
6624                 accelerate_ship(aip, (dot + 1.0f)/2.0f);
6625         }
6626 }
6627
6628 int avoid_player(object *objp, vector *goal_pos)
6629 {
6630         maybe_avoid_player(Pl_objp, goal_pos);
6631         ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
6632
6633         if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6634                 ship_info *sip = &Ship_info[Ships[objp->instance].ship_info_index];
6635
6636                 if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6637                         ai_turn_towards_vector(&aip->avoid_goal_point, objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6638                         accelerate_ship(aip, 0.5f);
6639                         return 1;
6640                 }
6641         }
6642
6643         return 0;
6644 }
6645
6646 //      Determine if a cylinder of width radius from p0 to p1 will collide with big_objp.
6647 //      If so, stuff *collision_point.
6648 int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6649 {
6650         mc_info mc;
6651
6652         mc.model_num = Ships[big_objp->instance].modelnum;              // Fill in the model to check
6653         mc.orient = &big_objp->orient;                  // The object's orient
6654         mc.pos = &big_objp->pos;                                        // The object's position
6655         mc.p0 = p0;                                                                             // Point 1 of ray to check
6656         mc.p1 = p1;
6657         mc.flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE | MC_SUBMODEL;                                  // flags
6658
6659         mc.radius = radius;
6660
6661         // Only check the 2nd lowest hull object
6662         polymodel *pm = model_get(Ships[big_objp->instance].modelnum);
6663         mc.submodel_num = pm->detail[0]; //pm->submodel->num_details-2];
6664         model_collide(&mc);
6665
6666         if (mc.num_hits)
6667                 *collision_point = mc.hit_point_world;
6668
6669         return mc.num_hits;
6670 }
6671
6672 //      Return true/false if *objp will collide with *big_objp
6673 //      Stuff distance in *distance to collision point if *objp will collide with *big_objp within delta_time seconds.
6674 //      Global collision point stuffed in *collision_point
6675 int will_collide_with_big_ship(object *objp, vector *goal_point, object *big_objp, vector *collision_point, float delta_time)
6676 {
6677         float           radius;
6678         vector  end_pos;
6679
6680         radius = big_objp->radius + delta_time * objp->phys_info.speed;
6681
6682         if (vm_vec_dist_quick(&big_objp->pos, &objp->pos) > radius) {
6683                 return 0;
6684         }
6685
6686         if (goal_point == NULL) {
6687                 vm_vec_scale_add(&end_pos, &objp->pos, &objp->phys_info.vel, delta_time);                                       // Point 2 of ray to check
6688         } else {
6689                 end_pos = *goal_point;
6690         }
6691
6692         return will_collide_pp(&objp->pos, &end_pos, objp->radius, big_objp, collision_point);
6693 }
6694
6695 //      Return true if *objp is expected to collide with a large ship.
6696 //      Stuff global collision point in *collision_point.
6697 //      If *goal_point is not NULL, use that as the point towards which *objp will be flying.  Don't use *objp velocity
6698 //      *ignore_objp will typically be the target this ship is pursuing, either to attack or guard.  We don't want to avoid it.
6699 int will_collide_with_big_ship_all(object *objp, object *ignore_objp, vector *goal_point, vector *collision_point, float *distance, float delta_time)
6700 {
6701         ship_obj        *so;
6702         object  *big_objp;
6703         int             collision_obj_index = -1;
6704         float           min_dist = 999999.9f;
6705         float           collision_time = -1.0f;
6706
6707         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6708                 float   time = 0.0f;
6709                 big_objp = &Objects[so->objnum];
6710
6711                 if (big_objp == ignore_objp)
6712                         continue;
6713
6714                 if (Ship_info[Ships[big_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
6715                         vector  cur_collision_point;
6716                         float           cur_dist;
6717
6718                         if (will_collide_with_big_ship(objp, goal_point, big_objp, &cur_collision_point, delta_time)) {
6719
6720                                 cur_dist = vm_vec_dist(&cur_collision_point, &objp->pos);
6721
6722                                 if (cur_dist < min_dist) {
6723                                         min_dist = cur_dist;
6724                                         *collision_point = cur_collision_point;
6725                                         collision_time = time;
6726                                         collision_obj_index = OBJ_INDEX(big_objp);
6727                                 }
6728                         }
6729                 }
6730         }
6731
6732         *distance = min_dist;
6733         return collision_obj_index;
6734
6735 }
6736
6737 typedef struct {
6738         float           dist;
6739         int             collide;
6740         vector  pos;
6741 } sgoal;
6742
6743 //int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6744 //      Pick a point for *objp to fly towards to avoid a collision with *big_objp at *collision_point
6745 //      Return result in *avoid_pos
6746 void mabs_pick_goal_point(object *objp, object *big_objp, vector *collision_point, vector *avoid_pos)
6747 {
6748         matrix  mat1;
6749         sgoal           goals[4];
6750         vector  v2b;
6751
6752         vm_vec_normalized_dir(&v2b, collision_point, &objp->pos);
6753         vm_vector_2_matrix(&mat1, &v2b, NULL, NULL);
6754
6755         int     found = 0;
6756
6757         //      Try various scales, in 0.5f, 0.75f, 1.0f, 1.25f.
6758         //      First try 0.5f to see if we can find a point that near the center of the target ship, which presumably
6759         //      means less of a turn.
6760         //      Try going as far as 1.25f * radius.
6761         float   s;
6762         for (s=0.5f; s<1.3f; s += 0.25f) {
6763                 int     i;
6764                 for (i=0; i<4; i++) {
6765                         vector p = big_objp->pos;
6766                         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
6767                         float kr = big_objp->radius*s + objp->radius * ((OBJ_INDEX(objp) % 4) ^ 2)/4;
6768                         if (i&1)
6769                                 ku = -ku;
6770                         if (i&2)
6771                                 kr = -kr;
6772                         vm_vec_scale_add2(&p, &mat1.v.uvec, ku);
6773                         vm_vec_scale_add2(&p, &mat1.v.rvec, kr);
6774                         goals[i].pos = p;
6775                         goals[i].dist = vm_vec_dist_quick(&objp->pos, &p);
6776                         goals[i].collide = will_collide_pp(&objp->pos, &p, objp->radius, big_objp, collision_point);
6777                         if (!goals[i].collide)
6778                                 found = 1;
6779                 }
6780
6781                 //      If we found a point that doesn't collide, find the nearest one and make that the *avoid_pos.
6782                 if (found) {
6783                         float   min_dist = 9999999.9f;
6784                         int     min_index = -1;
6785
6786                         for (i=0; i<4; i++) {
6787                                 if (!goals[i].collide && (goals[i].dist < min_dist)) {
6788                                         min_dist = goals[i].dist;
6789                                         min_index = i;
6790                                 }
6791                         }
6792
6793                         Assert(i != -1);
6794                         if (i != -1) {
6795                                 *avoid_pos = goals[min_index].pos;
6796                                 return;
6797                         }
6798                 }
6799         }
6800
6801         //      Drat.  We tried and tried and could not find a point that did not cause a collision.
6802         //      Get this dump pilot far away from the problem ship.
6803         vector  away_vec;
6804         vm_vec_normalized_dir(&away_vec, &objp->pos, collision_point);
6805         vm_vec_scale_add(avoid_pos, &objp->pos, &away_vec, big_objp->radius*1.5f);
6806
6807 }
6808
6809 //      Return true if a large ship is being ignored.
6810 int maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vector *goal_point, float delta_time)
6811 {
6812         if (timestamp_elapsed(aip->avoid_check_timestamp)) {
6813                 float           distance;
6814                 vector  collision_point;
6815                 int             ship_num;
6816                 if ((ship_num = will_collide_with_big_ship_all(Pl_objp, ignore_objp, goal_point, &collision_point, &distance, delta_time)) != -1) {
6817                         aip->ai_flags |= AIF_AVOIDING_BIG_SHIP;
6818                         mabs_pick_goal_point(objp, &Objects[ship_num], &collision_point, &aip->avoid_goal_point);
6819                         float dist = vm_vec_dist_quick(&aip->avoid_goal_point, &objp->pos);
6820                         aip->avoid_check_timestamp = timestamp(2000 + min(1000, (int) (dist * 2.0f)));  //      Delay until check again is based on distance to avoid point.
6821                         aip->avoid_ship_num = ship_num;
6822                 } else {
6823                         aip->ai_flags &= ~AIF_AVOIDING_BIG_SHIP;
6824                         aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
6825                         aip->avoid_ship_num = -1;
6826                         aip->avoid_check_timestamp = timestamp(1500);
6827                 }
6828         }
6829         
6830         if (aip->ai_flags & AIF_AVOIDING_BIG_SHIP) {
6831                 ship_info *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6832
6833                 vector  v2g;
6834
6835                 ai_turn_towards_vector(&aip->avoid_goal_point, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6836                 vm_vec_normalized_dir(&v2g, &aip->avoid_goal_point, &Pl_objp->pos);
6837                 float dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
6838                 float d2 = (1.0f + dot) * (1.0f + dot);
6839                 accelerate_ship(aip, d2/4.0f);
6840                 return 1;
6841         }
6842
6843         return 0;
6844 }
6845
6846 //      Set desired right vector for ships flying towards another ship.
6847 //      Since this is governed only by vector to target, it causes ships to align bank and look less chaotic.
6848 void compute_desired_rvec(vector *rvec, vector *goal_pos, vector *cur_pos)
6849 {
6850         vector  v2e;
6851
6852         vm_vec_normalized_dir(&v2e, goal_pos, cur_pos);
6853         rvec->xyz.x = v2e.xyz.z;
6854         rvec->xyz.y = 0.0f;
6855         rvec->xyz.z = -v2e.xyz.x;
6856         if (vm_vec_mag_squared(rvec) < 0.001f)
6857                 rvec->xyz.y = 1.0f;
6858 }
6859
6860 // Handler for stealth find submode of Chase.
6861 void ai_stealth_find()
6862 {
6863         ai_info         *aip;
6864         ship_info       *sip;
6865
6866         vector new_pos, vec_to_enemy;
6867         float dist_to_enemy, dot_to_enemy, dot_from_enemy;
6868
6869         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6870         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6871         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6872         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6873
6874         // get time since last seen
6875         int delta_time = (timestamp() - aip->stealth_last_visible_stamp);
6876
6877         // if delta_time is really big, i'm real confused, start sweep
6878         if (delta_time > 10000) {
6879                 aip->submode_parm0 = SM_SF_BAIL;
6880         }
6881
6882         // guestimate new position
6883         vm_vec_scale_add(&new_pos, &aip->stealth_last_pos, &aip->stealth_velocity, (delta_time * 0.001f));
6884
6885         // if I think he's behind me, go to the goal point
6886         if ( aip->submode_parm0 == SM_SF_BEHIND ) {
6887                 new_pos = aip->goal_point;
6888         }
6889
6890         // check for collision with big ships
6891         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &new_pos, 10.0f)) {
6892                 // reset ai submode to chase
6893                 return;
6894         }
6895
6896         // if dist is near max and dot is close to 1, accel, afterburn
6897         vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6898         dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6899         dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.v.fvec);
6900
6901         // 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
6902         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) ) {
6903                 // do turn around)
6904                 vm_vec_scale_add(&aip->goal_point, &Pl_objp->pos, &Pl_objp->orient.v.fvec, -300.0f);
6905                 aip->submode_parm0 = SM_SF_BEHIND;
6906                 vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6907                 dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6908                 dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.v.fvec);
6909         }
6910
6911         if ( (dist_to_enemy > get_skill_stealth_dist_scaler()*STEALTH_MAX_VIEW_DIST) && (dot_to_enemy > 0.94f) ) {              // 20 degree half angle
6912                 // accelerate ship
6913                 accelerate_ship(aip, 1.0f);
6914
6915                 // engage afterburner
6916                 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6917                         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6918                                 afterburners_start(Pl_objp);
6919                                 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
6920                         }
6921                 }
6922
6923                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6924                 return;
6925         }
6926
6927         //      If enemy more than 500 meters away, all ships flying there will tend to match bank.
6928         //      They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
6929         //      to interpolate a matrix rather than just a vector.
6930         if (dist_to_enemy > 500.0f) {
6931                 vector  rvec;
6932                 compute_desired_rvec(&rvec, &new_pos, &Pl_objp->pos);
6933                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0, &rvec);
6934         } else {
6935                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6936         }
6937
6938         dot_from_enemy = -vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec);
6939
6940         attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
6941 }
6942
6943 // -----------------------------------------------------------------------------
6944 // try to find stealth ship by sweeping an area
6945 void ai_stealth_sweep()
6946 {
6947         ai_info         *aip;
6948         ship_info       *sip;
6949
6950         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6951         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6952         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6953         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6954
6955         vector goal_pt;
6956         vector forward, right, up;
6957         int lost_time;
6958
6959         // time since stealth last seen
6960         lost_time = (timestamp() - aip->stealth_last_visible_stamp);
6961
6962         // determine which pt to fly to in sweep by keeping track of parm0
6963         if (aip->submode_parm0 == SM_SS_SET_GOAL) {
6964
6965                 // don't make goal pt more than 2k from current pos
6966                 vm_vec_scale_add(&goal_pt, &aip->stealth_last_pos, &aip->stealth_velocity, (0.001f * lost_time));
6967
6968                 // make box size based on speed of stealth and expected time to intercept (keep box in range 200-500)
6969                 float box_size = vm_vec_mag_quick(&aip->stealth_velocity) * (0.001f * lost_time);
6970                 box_size = min(200.0f, box_size);
6971                 box_size = max(500.0f, box_size);
6972                 aip->stealth_sweep_box_size = box_size;
6973
6974                 aip->goal_point = goal_pt;
6975                 aip->submode_parm0 = SM_SS_BOX0;
6976         }
6977
6978         // GET UP, RIGHT, FORWARD FOR BOX based on stealth ship's velocity
6979         // if velocity changes in stealth mode, then ship is *seen*, and falls out of sweep mode
6980         // if stealth has no velocity make a velocity
6981         if ( vm_vec_mag_quick(&aip->stealth_velocity) < 1 ) {
6982                 vm_vec_rand_vec_quick(&aip->stealth_velocity);
6983         }
6984
6985         // get "right" vector for box
6986         vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_y_vector);
6987
6988         if ( vm_vec_mag_quick(&right) < 0.01 ) {
6989                 vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_z_vector);
6990         }
6991
6992         vm_vec_normalize_quick(&right);
6993
6994         // get forward for box
6995         vm_vec_copy_normalize_quick(&forward, &aip->stealth_velocity);
6996
6997         // get "up" for box
6998         vm_vec_crossprod(&up, &forward, &right);
6999         
7000         // lost far away ahead (do box)
7001         switch(aip->submode_parm0) {
7002         case SM_SS_BOX0:
7003                 goal_pt = aip->goal_point;
7004                 break;
7005
7006         // pt1 -U +R
7007         case SM_SS_LR:
7008                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
7009                 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
7010                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
7011                 break;
7012
7013         // pt2 +U -R
7014         case SM_SS_UL:
7015                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
7016                 vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
7017                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
7018                 break;
7019
7020         // pt3 back
7021         case SM_SS_BOX1:
7022                 goal_pt = aip->goal_point;
7023                 break;
7024
7025         // pt4 +U +R
7026         case SM_SS_UR:
7027                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
7028                 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
7029                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
7030                 break;
7031
7032         // pt5 -U -R
7033         case SM_SS_LL:
7034                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
7035                 vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
7036                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
7037                 break;
7038
7039         // pt6 back
7040         case SM_SS_BOX2:
7041                 goal_pt = aip->goal_point;
7042                 break;
7043
7044         default:
7045                 Int3();
7046
7047         }
7048
7049         // when close to goal_pt, update next goal pt
7050         float dist_to_goal = vm_vec_dist(&goal_pt, &Pl_objp->pos);
7051         if (dist_to_goal < 15) {
7052                 aip->submode_parm0++;
7053         }
7054
7055         // check for collision with big ship
7056         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &goal_pt, 10.0f)) {
7057                 // skip to the next pt on box
7058                 aip->submode_parm0++;
7059                 return;
7060         }
7061
7062         ai_turn_towards_vector(&goal_pt, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
7063
7064         float dot = 1.0f;
7065         if (dist_to_goal < 100) {
7066                 vector vec_to_goal;
7067                 vm_vec_normalized_dir(&vec_to_goal, &goal_pt, &Pl_objp->pos);
7068                 dot = vm_vec_dotprod(&vec_to_goal, &Pl_objp->orient.v.fvec);
7069         }
7070
7071         accelerate_ship(aip, 0.8f*dot);
7072 }
7073
7074 //      ATTACK submode handler for chase mode.
7075 void ai_chase_attack(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
7076 {
7077         int             start_bank;
7078         float           dot_to_enemy, dot_from_enemy; //, time_to_hit;
7079         float           bank_override = 0.0f;
7080
7081         if (avoid_player(Pl_objp, predicted_enemy_pos))
7082                 return;
7083
7084         compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
7085
7086         polymodel *po = model_get( sip->modelnum );
7087
7088         vector  *rel_pos;
7089         float           scale;
7090         vector  randvec;
7091         vector  new_pos;
7092
7093         start_bank = Ships[aip->shipnum].weapons.current_primary_bank;
7094         if (po->n_guns && start_bank != -1 ) {
7095                 rel_pos = &po->gun_banks[start_bank].pnt[0];
7096         } else
7097                 rel_pos = NULL;
7098
7099         //      If ship moving slowly relative to its size, then don't attack its center point.
7100         //      How far from center we attack is based on speed, size and distance to enemy
7101         if (En_objp->radius > En_objp->phys_info.speed) {
7102                 static_randvec(Pl_objp-Objects, &randvec);
7103                 scale = dist_to_enemy/(dist_to_enemy + En_objp->radius) * En_objp->radius;
7104                 scale *= 0.5f * En_objp->radius/(En_objp->phys_info.speed + En_objp->radius);   // scale downward by 1/2 to 1/4
7105                 vm_vec_scale_add(&new_pos, predicted_enemy_pos, &randvec, scale);
7106         } else
7107                 new_pos = *predicted_enemy_pos;
7108
7109         if (dist_to_enemy < 250.0f) {
7110                 if (dot_from_enemy > 0.7f) {
7111                         bank_override = Pl_objp->phys_info.speed;
7112                 }
7113         }
7114
7115         //      If enemy more than 500 meters away, all ships flying there will tend to match bank.
7116         //      They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
7117         //      to interpolate a matrix rather than just a vector.
7118         if (dist_to_enemy > 500.0f) {
7119                 vector  rvec;
7120                 compute_desired_rvec(&rvec, predicted_enemy_pos, &Pl_objp->pos);
7121                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0, &rvec);
7122         } else {
7123                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0);
7124         }
7125
7126         attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
7127 }
7128
7129 //      EVADE_SQUIGGLE submode handler for chase mode.
7130 //      Changed by MK on 5/5/97.
7131 //      Used to evade towards a point off the right or up vector.
7132 //      Now, evade straight away to try to get far away.
7133 //      The squiggling should protect against laser fire.
7134 void ai_chase_es(ai_info *aip, ship_info *sip)
7135 {
7136         vector  tvec;
7137         fix             timeslice;
7138         fix             scale;
7139         float           bank_override = 0.0f;
7140
7141         tvec = Pl_objp->pos;
7142
7143         timeslice = (Missiontime >> 16) & 0x0f;
7144         scale = ((Missiontime >> 16) & 0x0f) << 14;
7145
7146         if (timeslice & 0x01)
7147                 vm_vec_scale_add2(&tvec, &Pl_objp->orient.v.rvec, f2fl(scale ^ 0x10000));
7148         if (timeslice & 0x02)
7149                 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.v.rvec, f2fl(scale));
7150         if (timeslice & 0x04)
7151                 vm_vec_scale_add2(&tvec, &Pl_objp->orient.v.uvec, f2fl(scale ^ 0x10000));
7152         if (timeslice & 0x08)
7153                 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.v.uvec, f2fl(scale));
7154
7155         while (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
7156                 tvec.xyz.x += frand();
7157                 tvec.xyz.y += frand();
7158         }
7159
7160         bank_override = Pl_objp->phys_info.speed;
7161
7162         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7163         accelerate_ship(aip, 1.0f);
7164 }
7165
7166 //      Trying to get away from opponent.
7167 void ai_chase_ga(ai_info *aip, ship_info *sip)
7168 {
7169         //      If not near end of this submode, evade squiggly.  If near end, just fly straight for a bit
7170         vector  tvec;
7171         float           bank_override;
7172         vector  vec_from_enemy;
7173
7174         if (En_objp != NULL) {
7175                 vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
7176         } else
7177                 vec_from_enemy = Pl_objp->orient.v.fvec;
7178
7179         static_randvec(Missiontime >> 15, &tvec);
7180         vm_vec_scale(&tvec, 100.0f);
7181         vm_vec_scale_add2(&tvec, &vec_from_enemy, 300.0f);
7182         vm_vec_add2(&tvec, &Pl_objp->pos);
7183
7184         bank_override = Pl_objp->phys_info.speed;
7185
7186         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7187
7188         accelerate_ship(aip, 2.0f);
7189
7190         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
7191                 if (!(Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
7192                         float percent_left = 100.0f * Ships[Pl_objp->instance].afterburner_fuel / sip->afterburner_fuel_capacity;
7193                         if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
7194                                 afterburners_start(Pl_objp);
7195                                 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7196                         }
7197                         afterburners_start(Pl_objp);
7198                         aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7199                 }
7200         }
7201
7202 }
7203
7204 //      Make object *objp attack subsystem with ID = subnum.
7205 //      Return true if found a subsystem to attack, else return false.
7206 //      Note, can fail if subsystem exists, but has no hits.
7207 int ai_set_attack_subsystem(object *objp, int subnum)
7208 {
7209         ship                    *shipp, *attacker_shipp;
7210         ai_info         *aip;
7211         ship_subsys     *ssp;
7212         object          *attacked_objp;
7213
7214         Assert(objp->type == OBJ_SHIP);
7215         Assert(objp->instance >= 0);
7216
7217         attacker_shipp = &Ships[objp->instance];
7218         Assert(attacker_shipp->ai_index >= 0);
7219
7220         aip = &Ai_info[attacker_shipp->ai_index];
7221
7222         // MWA -- 2/27/98.  Due to AL's changes, target_objnum is now not always valid (at least sometimes
7223         // in terms of goals).  So, bail if we don't have a valid target.
7224         if ( aip->target_objnum == -1 )
7225                 return 0;
7226
7227         attacked_objp = &Objects[aip->target_objnum];
7228         shipp = &Ships[attacked_objp->instance];                //  need to get our target's ship pointer!!!
7229
7230         ssp = ship_get_indexed_subsys(shipp, subnum, &objp->pos);
7231         if (ssp == NULL)
7232                 return 0;
7233
7234         set_targeted_subsys(aip, ssp, aip->target_objnum);
7235         
7236         if (aip->ignore_objnum == aip->target_objnum)
7237                 aip->ignore_objnum = UNUSED_OBJNUM;
7238
7239         // -- Done at caller in ai_process_mission_orders -- attacked_objp->flags |= OF_PROTECTED;
7240
7241         ai_set_goal_maybe_abort_dock(objp, aip);
7242         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7243
7244         return 1;
7245 }
7246
7247 void ai_set_guard_vec(object *objp, object *guard_objp)
7248 {
7249         ai_info *aip;
7250         float   radius;
7251
7252         aip = &Ai_info[Ships[objp->instance].ai_index];
7253
7254         //      Handle case of bogus call in which ship is told to guard self.
7255         Assert(objp != guard_objp);
7256         if (objp == guard_objp) {
7257                 vm_vec_rand_vec_quick(&aip->guard_vec);
7258                 vm_vec_scale(&aip->guard_vec, 100.0f);
7259                 return;
7260         }
7261
7262         // check if guard_objp is BIG
7263         radius = 5.0f * (objp->radius + guard_objp->radius) + 50.0f;
7264         if (radius > 300.0f) {
7265                 radius = guard_objp->radius * 1.25f;
7266         }
7267
7268         vm_vec_sub(&aip->guard_vec, &objp->pos, &guard_objp->pos);
7269
7270         if (vm_vec_mag(&aip->guard_vec) > 3.0f*radius) {
7271                 //      Far away, don't just use vector to object, causes clustering of guard ships.
7272                 vector  tvec, rvec;
7273                 float   mag;
7274                 mag = vm_vec_copy_normalize(&tvec, &aip->guard_vec);
7275                 vm_vec_rand_vec_quick(&rvec);                   
7276                 vm_vec_scale_add2(&tvec, &rvec, 0.5f);
7277                 vm_vec_copy_scale(&aip->guard_vec, &tvec, mag);
7278         }
7279
7280         vm_vec_normalize_quick(&aip->guard_vec);
7281         vm_vec_scale(&aip->guard_vec, radius);
7282 }
7283
7284 //      Make object *objp guard object *other_objp.
7285 //      To be called from the goals code.
7286 void ai_set_guard_wing(object *objp, int wingnum)
7287 {
7288         ship            *shipp;
7289         ai_info *aip;
7290         int             leader_objnum, leader_shipnum;
7291
7292         Assert(wingnum >= 0);
7293
7294         Assert(objp->type == OBJ_SHIP);
7295         Assert(objp->instance >= 0);
7296
7297         // shouldn't set the ai mode for the player
7298         if ( objp == Player_obj ) {
7299                 return;
7300         }
7301
7302         shipp = &Ships[objp->instance];
7303
7304         Assert(shipp->ai_index >= 0);
7305
7306         aip = &Ai_info[shipp->ai_index];
7307         force_avoid_player_check(objp, aip);
7308
7309         ai_set_goal_maybe_abort_dock(objp, aip);
7310         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7311
7312         //      This function is called whenever a guarded ship is destroyed, so this code
7313         //      prevents a ship from trying to guard a non-existent wing.
7314         if (Wings[wingnum].current_count < 1) {
7315                 aip->guard_objnum = -1;
7316                 aip->guard_wingnum = -1;
7317                 aip->mode = AIM_NONE;
7318         } else {
7319                 leader_shipnum = Wings[wingnum].ship_index[0];
7320                 leader_objnum = Ships[leader_shipnum].objnum;
7321
7322                 Assert((leader_objnum >= 0) && (leader_objnum < MAX_OBJECTS));
7323                 //Assert(leader_objnum != objp-Objects);        //      Don't allow ships to guard themselves.
7324                 if (leader_objnum == OBJ_INDEX(objp)) {
7325                         //Int3();       //      Seems illegal, but let's clean up.  Get MikeK.
7326                         return;
7327                 }
7328
7329                 aip->guard_wingnum = wingnum;
7330                 aip->guard_objnum = leader_objnum;
7331                 aip->guard_signature = Objects[leader_objnum].signature;
7332                 aip->mode = AIM_GUARD;
7333                 aip->submode = AIS_GUARD_STATIC;
7334
7335                 ai_set_guard_vec(objp, &Objects[leader_objnum]);
7336         }
7337 }
7338
7339 //      Make object *objp guard object *other_objp.
7340 //      To be called from the goals code.
7341 void ai_set_evade_object(object *objp, object *other_objp)
7342 {
7343         ship            *shipp;
7344         ai_info *aip;
7345         int             other_objnum;
7346
7347         Assert(objp->type == OBJ_SHIP);
7348         Assert(objp->instance >= 0);
7349
7350         shipp = &Ships[objp->instance];
7351
7352         Assert(shipp->ai_index >= 0);
7353
7354         aip = &Ai_info[shipp->ai_index];
7355
7356         other_objnum = OBJ_INDEX(other_objp);
7357         Assert(other_objnum >= 0);
7358
7359         Assert(other_objnum != Ships[aip->shipnum].objnum);     //      make sure not targeting self
7360         aip->target_objnum = other_objnum;
7361
7362         aip->mode = AIM_EVADE;
7363 }
7364
7365 //      Make objp guard other_objp
7366 //      If other_objp is a member of a wing, objp will guard that whole wing
7367 //      UNLESS objp is also a member of the wing!
7368 void ai_set_guard_object(object *objp, object *other_objp)
7369 {
7370         ship            *shipp;
7371         ai_info *aip;
7372         int             other_objnum;
7373
7374         Assert(objp->type == OBJ_SHIP);
7375         Assert(objp->instance >= 0);
7376         Assert(objp != other_objp);
7377
7378         shipp = &Ships[objp->instance];
7379
7380         Assert(shipp->ai_index >= 0);
7381
7382         aip = &Ai_info[shipp->ai_index];
7383         aip->avoid_check_timestamp = timestamp(1);
7384
7385         //      If ship to guard is in a wing, guard that whole wing.
7386         ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
7387         if ((other_aip->wing != -1) && (other_aip->wing != aip->wing)) {
7388                 ai_set_guard_wing(objp, Ai_info[Ships[other_objp->instance].ai_index].wing);
7389         } else {
7390
7391                 other_objnum = other_objp-Objects;
7392
7393                 aip->guard_objnum = other_objnum;
7394                 aip->guard_signature = other_objp->signature;
7395                 aip->guard_wingnum = -1;
7396
7397                 aip->mode = AIM_GUARD;
7398                 aip->submode = AIS_GUARD_STATIC;
7399
7400                 Assert(other_objnum >= 0);      //      Hmm, bogus object and we need its position for guard_vec.
7401
7402                 // vm_vec_sub(&aip->guard_vec, &objp->pos, &Objects[other_objnum].pos);
7403                 ai_set_guard_vec(objp, &Objects[other_objnum]);
7404
7405                 ai_set_goal_maybe_abort_dock(objp, aip);
7406                 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7407         }
7408 }
7409
7410 //      Update the aspect_locked_time field based on whether enemy is in view cone.
7411 //      Also set/clear AIF_SEEK_LOCK.
7412 void update_aspect_lock_information(ai_info *aip, vector *vec_to_enemy, float dist_to_enemy, float enemy_radius)
7413 {
7414         float   dot_to_enemy;
7415         int     num_weapon_types;
7416         int     weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
7417         ship    *shipp;
7418         ship_weapon     *swp;
7419         weapon_info     *wip;
7420
7421         shipp = &Ships[aip->shipnum];
7422         swp = &shipp->weapons;
7423
7424         // AL 3-7-98: This probably should never happen, but check to ensure that current_secondary_bank is valid
7425         if ( (swp->current_secondary_bank < 0) || (swp->current_secondary_bank > swp->num_secondary_banks) ) {
7426                 return;
7427         }
7428
7429         num_weapon_types = get_available_secondary_weapons(Pl_objp, weapon_id_list, weapon_bank_list);
7430
7431         wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
7432
7433         if (num_weapon_types && (wip->wi_flags & WIF_HOMING_ASPECT)) {
7434                 if (dist_to_enemy > 300.0f - min(enemy_radius, 100.0f))
7435                         aip->ai_flags |= AIF_SEEK_LOCK;
7436                 else
7437                         aip->ai_flags &= ~AIF_SEEK_LOCK;
7438
7439                 //      Update locking information for aspect seeking missiles.
7440                 aip->current_target_is_locked = 0;
7441                 dot_to_enemy = vm_vec_dot(vec_to_enemy, &Pl_objp->orient.v.fvec);
7442
7443                 float   needed_dot = 0.9f - 0.5f * enemy_radius/(dist_to_enemy + enemy_radius); //      Replaced MIN_TRACKABLE_DOT with 0.9f
7444                 if (dot_to_enemy > needed_dot) {
7445                         aip->aspect_locked_time += flFrametime;
7446                         // nprintf(("AI", "+ Lock time = %7.3f\n", aip->aspect_locked_time));
7447                         if (aip->aspect_locked_time >= wip->min_lock_time) {
7448                                 aip->aspect_locked_time = wip->min_lock_time;
7449                                 aip->current_target_is_locked = 1;
7450                         }
7451                 } else {
7452                         aip->aspect_locked_time -= flFrametime*2;
7453                         // nprintf(("AI", "- Lock time = %7.3f\n", aip->aspect_locked_time));
7454                         if (aip->aspect_locked_time < 0.0f)
7455                                 aip->aspect_locked_time = 0.0f;
7456                 }
7457                 //nprintf(("AI", "dot = %7.3f, time = %7.3f\n", dot_to_enemy, aip->aspect_locked_time));
7458         
7459         } else {
7460                 aip->current_target_is_locked = 0;
7461                 aip->aspect_locked_time = 0.0f; // Used to be this, why?: wip->min_lock_time;
7462                 aip->ai_flags &= ~AIF_SEEK_LOCK;
7463         }
7464
7465 }
7466
7467 //      We're in chase mode and we've recently collided with our target.
7468 //      Fly away from it!
7469 void ai_chase_fly_away(object *objp, ai_info *aip)
7470 {
7471         int     abort_flag = 0;
7472
7473         if (aip->ai_flags & AIF_TARGET_COLLISION) {
7474                 aip->ai_flags &= ~AIF_TARGET_COLLISION; //      Don't process this hit again next frame.
7475                 aip->submode = SM_FLY_AWAY;                                     //      Focus on avoiding target
7476                 aip->submode_start_time = Missiontime;
7477         }
7478
7479         if ((aip->target_objnum == -1) || (Objects[aip->target_objnum].signature != aip->target_signature)) {
7480                 abort_flag = 1;
7481         }
7482
7483         if (abort_flag || (Missiontime > aip->submode_start_time + F1_0)) {
7484                 aip->last_attack_time = Missiontime;
7485                 aip->submode = SM_ATTACK;
7486                 aip->submode_start_time = Missiontime;
7487         } else {
7488                 vector  v2e;
7489                 float           dot;
7490
7491                 vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, &objp->pos);
7492
7493                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2e);
7494                 if (dot < 0.0f)
7495                         accelerate_ship(aip, 1.0f);
7496                 else
7497                         accelerate_ship(aip, 1.0f - dot);
7498                 turn_away_from_point(objp, &Objects[aip->target_objnum].pos, 0.0f);
7499         }
7500 }
7501
7502 //      Return bank index of favored secondary weapon.
7503 //      Return -1 if nothing favored.
7504 //      "favored" means SEXPs have specified the weapon as being good to fire at en_objp.
7505 int has_preferred_secondary(object *objp, object *en_objp, ship_weapon *swp)
7506 {
7507 // int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
7508         int     i;
7509
7510         for (i=0; i<swp->num_secondary_banks; i++) {
7511                 if (swp->secondary_bank_capacity[i] > 0) {
7512                         if (swp->secondary_bank_ammo[i] > 0) {
7513                                 if (is_preferred_weapon(swp->secondary_bank_weapons[i], objp, en_objp) != -1){
7514                                         return i;
7515                                 }
7516                         }
7517                 }
7518         }
7519
7520         return -1;
7521 }
7522
7523 //      Choose which secondary weapon to fire.
7524 //      Note, this is not like ai_select_secondary_weapon().  "choose" means make a choice.
7525 //      "select" means execute an order.  Get it?
7526 //      This function calls ai_select_secondary_weapon() with the characteristics it should search for.
7527 void ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp)
7528 {
7529         float                   subsystem_strength = 0.0f;
7530         int                     is_big_ship, priority1, priority2;
7531         ship_weapon     *swp;
7532         ship_info       *esip;
7533
7534         if ( en_objp->type == OBJ_SHIP ) {
7535                 esip = &Ship_info[Ships[en_objp->instance].ship_info_index];
7536         } else {
7537                 esip = NULL;
7538         }
7539
7540         swp = &Ships[objp->instance].weapons;
7541
7542         // AL 3-5-98: do a quick out if the ship has no secondaries
7543         if ( swp->num_secondary_banks <= 0 ) {
7544                 swp->current_secondary_bank = -1;
7545                 return;
7546         }
7547
7548         int preferred_secondary = has_preferred_secondary(objp, en_objp, swp);
7549
7550         if (preferred_secondary != -1) {
7551                 if (swp->current_secondary_bank != preferred_secondary) {
7552                         aip->current_target_is_locked = 0;
7553                         aip->aspect_locked_time = 0.0f;
7554                         swp->current_secondary_bank = preferred_secondary;
7555                 }
7556                 //nprintf(("AI", "Favored secondary = %s\n", Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7557                 aip->ai_flags |= AIF_UNLOAD_SECONDARIES;
7558         } else {
7559                 aip->ai_flags &= ~AIF_UNLOAD_SECONDARIES;
7560                 if (aip->targeted_subsys) {
7561                         subsystem_strength = aip->targeted_subsys->current_hits;
7562                 }
7563
7564                 if ( esip ) {
7565                         is_big_ship = esip->flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP);
7566                 } else {
7567                         is_big_ship=0;
7568                 }
7569
7570                 if (is_big_ship) {
7571                         priority1 = WIF_HUGE;
7572                         priority2 = WIF_HOMING;
7573                 } else if ( (esip != NULL) && (esip->flags & SIF_BOMBER) ) {
7574                         priority1 = WIF_BOMBER_PLUS;
7575                         priority2 = WIF_HOMING;
7576                 } else if (subsystem_strength > 100.0f) {
7577                         priority1 = WIF_PUNCTURE;
7578                         priority2 = WIF_HOMING;
7579                 } else {
7580                         priority1 = WIF_HOMING;
7581                         priority2 = 0;
7582                 }
7583                 
7584                 ai_select_secondary_weapon(objp, swp, priority1, priority2);
7585         }
7586
7587         // nprintf(("AI", "Frame %i: Chose secondary %s\n", Framecount, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7588 }
7589
7590 //      Return time, in seconds, at which this ship can next fire its current secondary weapon.
7591 float set_secondary_fire_delay(ai_info *aip, ship *shipp, weapon_info *swip)
7592 {
7593         float t = swip->fire_wait;              //      Base delay for this weapon.
7594         if (shipp->team == Player_ship->team) {
7595                 //      On player's team, _lower_ skill level = faster firing
7596                 t = t * (Game_skill_level+2) / (NUM_SKILL_LEVELS);
7597         } else {                //      Not on player's team, higher skill level = faster firing
7598                 t = t * (NUM_SKILL_LEVELS - Game_skill_level+2) / (NUM_SKILL_LEVELS);
7599         }
7600
7601         t += (Num_ai_classes - aip->ai_class + 1) * 0.5f;
7602         t *= frand_range(0.8f, 1.2f);
7603
7604         //      For the missiles that fire fairly quickly, occasionally add an additional substantial delay.
7605         if (t < 5.0f)
7606                 if (frand() < 0.5f)
7607                         t = t * 2.0f + 2.0f;
7608
7609         return t;
7610 }
7611
7612
7613 void ai_chase_big_approach_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7614 {
7615         float dist_to_goal;
7616
7617         // head straight toward him and maybe circle later
7618         vm_vec_avg(goal_pos, &attack_objp->pos, &target_objp->pos);
7619
7620         // get distance to goal
7621         dist_to_goal = vm_vec_dist(goal_pos, &attack_objp->pos);
7622         
7623         // set accel
7624         if (dist_to_goal > 400.0f) {
7625                 *accel = 1.0f;
7626         } else {
7627                 *accel = dist_to_goal/400.0f;
7628         }
7629 }
7630
7631 void ai_chase_big_circle_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7632 {
7633         get_tangent_point(goal_pos, attack_objp, &target_objp->pos, attack_objp->radius + target_objp->radius + 100.0f);
7634
7635         *accel = 1.0f;
7636 }
7637
7638 // get the current and desired horizontal separations between target
7639 void ai_chase_big_get_separations(object *attack_objp, object *target_objp, vector *horz_vec_to_target, float *desired_separation, float *cur_separation)
7640 {
7641         float temp, r_target, r_attacker, h_attacker, h_target;
7642         float perp_dist;
7643         vector vec_to_target;
7644         polymodel *pm;
7645
7646         // get parameters of ships (as cylinders - radius and height)
7647         // get radius of attacker (for rotations about forward)
7648         pm = model_get(Ships[attack_objp->instance].modelnum);
7649         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7650         r_attacker = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7651         r_attacker = max(temp, r_attacker);
7652         h_attacker = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7653
7654         // get radius of target (for rotations about forward)
7655         pm = model_get(Ships[attack_objp->instance].modelnum);
7656         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7657         r_target = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7658         r_target = max(temp, r_target);
7659         h_target = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7660
7661         // find separation between cylinders [if parallel]
7662         vm_vec_sub(&vec_to_target, &attack_objp->pos, &target_objp->pos);
7663
7664         // find the distance between centers along forward direction of ships
7665         perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.v.fvec);
7666
7667         // subtract off perp component to get "horizontal" separation vector between cylinders [ASSUMING parallel]
7668         vm_vec_scale_add(horz_vec_to_target, &vec_to_target, &target_objp->orient.v.fvec, -perp_dist);
7669         *cur_separation = vm_vec_mag_quick(horz_vec_to_target);
7670
7671         // choose "optimal" separation of 1000 + r_target + r_attacker
7672         *desired_separation = 1000 + r_target + r_attacker;
7673 }
7674
7675 void ai_chase_big_parallel_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7676 {
7677         int opposing;
7678         float temp, r_target, r_attacker, h_attacker, h_target;
7679         float separation, optimal_separation;
7680         vector  horz_vec_to_target;
7681         polymodel *pm;
7682
7683         // get parameters of ships (as cylinders - radius and height)
7684         // get radius of attacker (for rotations about forward)
7685         pm = model_get(Ships[attack_objp->instance].modelnum);
7686         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7687         r_attacker = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7688         r_attacker = max(temp, r_attacker);
7689         h_attacker = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7690
7691         // get radius of target (for rotations about forward)
7692         pm = model_get(Ships[attack_objp->instance].modelnum);
7693         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7694         r_target = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7695         r_target = max(temp, r_target);
7696         h_target = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7697
7698         // are we opposing (only when other ship is not moving)
7699         opposing = ( vm_vec_dotprod(&attack_objp->orient.v.fvec, &target_objp->orient.v.fvec) < 0 );
7700
7701         ai_chase_big_get_separations(attack_objp, target_objp, &horz_vec_to_target, &optimal_separation, &separation);
7702
7703         // choose dist (2000) so that we don't bash
7704         float dist = 2000;
7705         if (opposing) {
7706                 dist = - dist;
7707         }
7708
7709         // set the goal pos as dist forward from target along target forward
7710         vm_vec_scale_add(goal_pos, &target_objp->pos, &target_objp->orient.v.fvec, dist);
7711         // then add horizontal separation
7712         vm_vec_scale_add2(goal_pos, &horz_vec_to_target, optimal_separation/separation);
7713
7714         // find the distance between centers along forward direction of ships
7715         vector vec_to_target;
7716         vm_vec_sub(&vec_to_target, &target_objp->pos, &attack_objp->pos);
7717         float perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.v.fvec);
7718
7719         float match_accel = target_objp->phys_info.vel.xyz.z / Ship_info[Ships[attack_objp->instance].ship_info_index].max_vel.xyz.z;
7720         float length_scale = attack_objp->radius;
7721
7722         // if we're heading toward enemy ship, we want to keep going if we're ahead
7723         if (opposing) {
7724                 perp_dist = -perp_dist;
7725         }
7726
7727         if (perp_dist > 0) {
7728                 // falling behind, so speed up
7729                 *accel = match_accel + (1.0f - match_accel) / length_scale * (perp_dist);
7730         } else {
7731                 // up in front, so slow down
7732                 *accel = match_accel  - match_accel / length_scale * -perp_dist;
7733                 *accel = max(0.0f, *accel);
7734         }
7735
7736 }
7737
7738
7739 //      Return *goal_pos for one cruiser to attack another (big ship).
7740 //      Choose point fairly nearby that is not occupied by another cruiser.
7741 void ai_cruiser_chase_set_goal_pos(vector *goal_pos, object *pl_objp, object *en_objp)
7742 {
7743         ai_info *aip;
7744
7745         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
7746         float accel;
7747
7748         switch (aip->submode) {
7749         case SM_BIG_APPROACH:
7750                 // do approach stuff;
7751                 ai_chase_big_approach_set_goal(goal_pos, pl_objp, en_objp, &accel);
7752                 break;
7753
7754         case SM_BIG_CIRCLE:
7755                 // do circle stuff
7756                 ai_chase_big_circle_set_goal(goal_pos, pl_objp, en_objp, &accel);
7757                 break;
7758
7759         case SM_BIG_PARALLEL:
7760                 // do parallel stuff
7761                 ai_chase_big_parallel_set_goal(goal_pos, pl_objp, en_objp, &accel);
7762                 break;
7763         }
7764 }
7765
7766 int maybe_hack_cruiser_chase_abort()
7767 {
7768         ship                    *shipp = &Ships[Pl_objp->instance];     
7769         ship                    *eshipp = &Ships[En_objp->instance];
7770         ai_info         *aip = &Ai_info[shipp->ai_index];
7771
7772         // mission sm3-08, sathanos chasing collosus
7773         if ( stricmp(Mission_filename, "sm3-08.fs2") == 0 ) {
7774                 if (( stricmp(eshipp->ship_name, "colossus") == 0 ) || ( stricmp(shipp->ship_name, "colossus") == 0 )) {
7775                         // Changed so all big ships attacking the Colossus will not do the chase code.
7776                         // Did this so Beast wouldn't swerve away from Colossus. -- MK, 9/14/99
7777                         //if ( stricmp(shipp->ship_name, "Sathanas") == 0 ) {
7778                                 // do cool hack stuff here
7779                                 ai_clear_ship_goals( aip );
7780                                 aip->mode = AIM_NONE;
7781                                 return 1;
7782                         //}
7783                 }
7784         }
7785
7786         return 0;
7787 }
7788
7789 //      Make a big ship pursue another big ship.
7790 //      (Note, called "ai_cruiser_chase" because we already have ai_chase_big() which means fighter chases big ship.
7791 void ai_cruiser_chase()
7792 {
7793         ship_info       *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7794         ship                    *shipp = &Ships[Pl_objp->instance];     
7795         ai_info         *aip = &Ai_info[shipp->ai_index];
7796
7797         if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7798                 Int3(); //      Hmm, not a very big ship, how did we get in this function?
7799                 aip->mode = AIM_NONE;
7800                 return;
7801         }
7802
7803         if (En_objp->type != OBJ_SHIP) {
7804                 Int3();
7805                 return;
7806         }
7807
7808         if (En_objp->instance < 0) {
7809                 Int3();
7810                 return;
7811         }
7812
7813         ship                    *eshipp;
7814         ship_info       *esip;
7815
7816         eshipp = &Ships[En_objp->instance];
7817         esip = &Ship_info[eshipp->ship_info_index];
7818
7819         if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7820                 // Int3();      //      Hmm, we're big and we're pursuing something other than a big ship?
7821                 aip->mode = AIM_NONE;
7822                 return;
7823         }
7824
7825         vector  goal_pos;
7826         float turn_time = Ship_info[Ships[Pl_objp->instance].ship_info_index].srotation_time;
7827
7828         // kamikaze - ram and explode
7829         if (aip->ai_flags & AIF_KAMIKAZE) {
7830                 ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
7831                 accelerate_ship(aip, 1.0f);
7832         } 
7833         
7834         // really track down and chase
7835         else {
7836                 // check valid submode
7837                 Assert( (aip->submode == SM_ATTACK) || (aip->submode == SM_BIG_APPROACH) || (aip->submode == SM_BIG_CIRCLE) || (aip->submode == SM_BIG_PARALLEL) );
7838
7839                 // just entering, approach enemy ship
7840                 if (aip->submode == SM_ATTACK) {
7841                         aip->submode = SM_BIG_APPROACH;
7842                 }
7843
7844                 // desired accel
7845                 float accel = 0.0f;
7846                 vector *rvecp = NULL;
7847
7848                 switch (aip->submode) {
7849                 case SM_BIG_APPROACH:
7850                         // do approach stuff;
7851                         ai_chase_big_approach_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7852                         // maybe set rvec
7853                         break;
7854
7855                 case SM_BIG_CIRCLE:
7856                         // do circle stuff
7857                         ai_chase_big_circle_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7858                         // maybe set rvec
7859                         break;
7860
7861                 case SM_BIG_PARALLEL:
7862                         // do parallel stuff
7863                         ai_chase_big_parallel_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7864                         //maybe set rvec
7865                         break;
7866                 }
7867
7868
7869                 // now move as desired
7870                 ai_turn_towards_vector(&goal_pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0, rvecp);
7871                 accelerate_ship(aip, accel);
7872
7873
7874                 // maybe switch to new mode
7875                 vector vec_to_enemy;
7876                 float dist_to_enemy;
7877                 int moving = (En_objp->phys_info.vel.xyz.z > 0.5f);
7878                 vm_vec_sub(&vec_to_enemy, &En_objp->pos, &Pl_objp->pos);
7879                 dist_to_enemy = vm_vec_mag_quick(&vec_to_enemy);
7880
7881                 switch (aip->submode) {
7882                 case SM_BIG_APPROACH:
7883                         if ( dist_to_enemy < (Pl_objp->radius + En_objp->radius)*1.25f + 200.0f ) {
7884                                 // moving
7885                                 if (moving) {
7886                                         // if within 90 degrees of en forward, go into parallel, otherwise circle
7887                                         if ( vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0 ) {
7888                                                 aip->submode = SM_BIG_PARALLEL;
7889                                         }
7890                                 }
7891
7892                                 // otherwise cirle
7893                                 if ( !maybe_hack_cruiser_chase_abort() ) {
7894                                         aip->submode = SM_BIG_CIRCLE;
7895                                 }
7896                         }
7897                         break;
7898
7899                 case SM_BIG_CIRCLE:
7900                         // moving
7901                         if (moving) {
7902                                 vector temp;
7903                                 float desired_sep, cur_sep;
7904                                 // we're behind the enemy ship
7905                                 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec) > 0) {
7906                                         // and we're turning toward the enemy
7907                                         if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0) {
7908                                                 // get separation
7909                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7910                                                 // and the separation is > 0.9 desired
7911                                                 if (cur_sep > 0.9 * desired_sep) {
7912                                                         aip->submode = SM_BIG_PARALLEL;
7913                                                 }
7914                                         }
7915                                 }
7916                         } else {
7917                                 // still
7918                                 vector temp;
7919                                 float desired_sep, cur_sep;
7920                                 // we're behind the enemy ship
7921                                 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec) > 0) {
7922                                         // and we're turning toward the enemy
7923                                         if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0) {
7924                                                 // get separation
7925                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7926                                                 //and the separation is [0.9 to 1.1] desired
7927                                                 if ( (cur_sep > 0.9f * desired_sep) ) {
7928                                                         aip->submode = SM_BIG_PARALLEL;
7929                                                 }
7930                                         }
7931                                 }
7932                                 // in front of ship
7933                                 else {
7934                                         // and we're turning toward the enemy
7935                                         if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) < 0) {
7936                                                 // get separation
7937                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7938                                                 //and the separation is [0.9 to 1.1] desired
7939                                                 if ( (cur_sep > 0.9f * desired_sep) ) {
7940                                                         aip->submode = SM_BIG_PARALLEL;
7941                                                 }
7942                                         }
7943                                 }
7944                         }
7945                         break;
7946
7947                 case SM_BIG_PARALLEL:
7948                         // we're opposing
7949                         if ( vm_vec_dotprod(&Pl_objp->orient.v.fvec, &En_objp->orient.v.fvec) < 0 ) {
7950                                 // and the other ship is moving
7951                                 if (moving) {
7952                                         // and we no longer overlap
7953                                         if ( dist_to_enemy > (0.75 * (En_objp->radius + Pl_objp->radius)) ) {
7954                                                 aip->submode = SM_BIG_APPROACH;
7955                                         }
7956                                 }
7957                         }
7958                         break;
7959                 }
7960         }
7961 }
7962
7963 // --------------------------------------------------------------------------
7964 // Make object Pl_objp chase object En_objp
7965 void ai_chase()
7966 {
7967         float                   dist_to_enemy, time_to_enemy;
7968         float                   dot_to_enemy, dot_from_enemy, real_dot_to_enemy;
7969         vector          player_pos, enemy_pos, predicted_enemy_pos, real_vec_to_enemy, predicted_vec_to_enemy;
7970         ship_info       *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7971         ship                    *shipp = &Ships[Pl_objp->instance];
7972         ship_weapon     *swp = &shipp->weapons;
7973         ai_info         *aip = &Ai_info[shipp->ai_index];
7974         int                     enemy_sip_flags;
7975
7976         if (aip->mode != AIM_CHASE) {
7977                 Int3();
7978         }
7979
7980         if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
7981                 ai_cruiser_chase();
7982                 return;
7983         }
7984
7985         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER | SIF_ESCAPEPOD))) {
7986                 Warning(LOCATION, "Ship %s is not 'small', but is in chase mode.\nSwitching to AI=none.\n", shipp->ship_name);
7987                 aip->mode = AIM_NONE;
7988                 return;
7989         }
7990
7991         //nprintf(("AI", "%7s ", Submode_text[aip->submode]));
7992
7993         if ( En_objp->type == OBJ_SHIP ) {
7994                 enemy_sip_flags = Ship_info[Ships[En_objp->instance].ship_info_index].flags;
7995         } else {
7996                 enemy_sip_flags = 0;
7997         }
7998
7999         if ( enemy_sip_flags > 0 ) {
8000                 if (enemy_sip_flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
8001                         ai_big_chase();
8002                         return;
8003                 }
8004         }
8005
8006         //      If collided with target_objnum last frame, avoid that ship.
8007         //      This should prevent the embarrassing behavior of ships getting stuck on each other
8008         //      as if they were magnetically attracted. -- MK, 11/13/97.
8009         if ((aip->ai_flags & AIF_TARGET_COLLISION) || (aip->submode == SM_FLY_AWAY)) {
8010                 ai_chase_fly_away(Pl_objp, aip);
8011                 return;
8012         }
8013
8014         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
8015         dist_to_enemy = vm_vec_dist_quick(&player_pos, &enemy_pos);
8016         time_to_enemy = compute_time_to_enemy(dist_to_enemy, Pl_objp, En_objp);
8017         vm_vec_sub(&real_vec_to_enemy, &enemy_pos, &player_pos);
8018
8019         vm_vec_normalize(&real_vec_to_enemy);
8020
8021         real_dot_to_enemy = vm_vec_dot(&real_vec_to_enemy, &Pl_objp->orient.v.fvec);
8022
8023         int is_stealthy_ship = 0;
8024         if ( (enemy_sip_flags > 0) && (enemy_sip_flags & SIF_STEALTH) ) {
8025                 if ( ai_is_stealth_visible(Pl_objp, En_objp) != STEALTH_FULLY_TARGETABLE ) {
8026                         is_stealthy_ship = 1;
8027                 }
8028         }
8029
8030         // Can only acquire lock on a target that isn't hidden from sensors
8031         if ( !(Ships[En_objp->instance].flags & SF_HIDDEN_FROM_SENSORS) && !is_stealthy_ship ) {
8032                 update_aspect_lock_information(aip, &real_vec_to_enemy, dist_to_enemy, En_objp->radius);
8033         } else {
8034                 aip->current_target_is_locked = 0;
8035                 aip->ai_flags &= ~AIF_SEEK_LOCK;
8036         }
8037
8038         //      If seeking lock, try to point directly at ship, else predict position so lasers can hit it.
8039         //      If just acquired target, or target is not in reasonable cone, don't refine believed enemy position.
8040         if ((real_dot_to_enemy < 0.25f) || (aip->target_time < 1.0f) || (aip->ai_flags & AIF_SEEK_LOCK)) {
8041                 predicted_enemy_pos = enemy_pos;
8042         } else {
8043                 //      Set predicted_enemy_pos.
8044                 //      See if attacking a subsystem.
8045                 if (aip->targeted_subsys != NULL) {
8046                         Assert(En_objp->type == OBJ_SHIP);
8047                         ship_info       *esip = &Ship_info[Ships[En_objp->instance].ship_info_index];
8048                         if (get_shield_strength(En_objp)/esip->shields < HULL_DAMAGE_THRESHOLD_PERCENT) {
8049                                 //int   rval;
8050
8051                                 if (aip->targeted_subsys != NULL) {
8052                                         get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
8053                                         predicted_enemy_pos = enemy_pos;
8054                                         predicted_vec_to_enemy = real_vec_to_enemy;
8055                                 } else {
8056                                         set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8057                                         set_target_objnum(aip, -1);
8058                                 }
8059                                 // 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));
8060
8061                         } else {
8062                                 set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8063                                 // 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));
8064                         }
8065                 } else {
8066                         set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8067                 }
8068         }
8069
8070         vm_vec_sub(&predicted_vec_to_enemy, &predicted_enemy_pos, &player_pos);
8071
8072         vm_vec_normalize(&predicted_vec_to_enemy);
8073
8074         dot_to_enemy = vm_vec_dot(&Pl_objp->orient.v.fvec, &predicted_vec_to_enemy);
8075         dot_from_enemy= - vm_vec_dot(&En_objp->orient.v.fvec, &real_vec_to_enemy);
8076
8077         //
8078         //      Set turn and acceleration based on submode.
8079         //
8080         switch (aip->submode) {
8081         case SM_CONTINUOUS_TURN:
8082                 ai_chase_ct();
8083                 break;
8084
8085         case SM_STEALTH_FIND:
8086                 ai_stealth_find();
8087                 break;
8088
8089         case SM_STEALTH_SWEEP:
8090                 ai_stealth_sweep();
8091                 break;
8092
8093         case SM_ATTACK:
8094         case SM_SUPER_ATTACK:
8095         case SM_ATTACK_FOREVER:
8096                 if (vm_vec_dist_quick(&Pl_objp->pos, &predicted_enemy_pos) > 100.0f + En_objp->radius * 2.0f) {
8097                         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &predicted_enemy_pos, 10.0f))
8098                                 return;
8099                 }
8100
8101                 ai_chase_attack(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8102                 break;
8103
8104         case SM_EVADE_SQUIGGLE:
8105                 ai_chase_es(aip, sip);
8106                 break;
8107
8108         case SM_EVADE_BRAKE:
8109                 ai_chase_eb(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8110                 break;
8111
8112         case SM_EVADE:
8113                 evade_ship();
8114                 break;
8115
8116         case SM_AVOID:
8117                 avoid_ship();
8118                 break;
8119
8120         case SM_GET_BEHIND:
8121                 get_behind_ship(aip, sip, dist_to_enemy);
8122                 break;
8123
8124         case SM_GET_AWAY:               //      Used to get away from opponent to prevent endless circling.
8125                 ai_chase_ga(aip, sip);
8126                 break;
8127
8128         case SM_EVADE_WEAPON:
8129                 evade_weapon();
8130                 break;
8131
8132         default:
8133                 // Int3();
8134                 aip->last_attack_time = Missiontime;
8135                 aip->submode = SM_ATTACK;
8136                 aip->submode_start_time = Missiontime;
8137         }
8138
8139         //
8140         //      Maybe choose a new submode.
8141         //
8142         if ( (aip->submode != SM_AVOID) && (aip->submode != SM_ATTACK_FOREVER) ) {
8143                 //      If a very long time since attacked, attack no matter what!
8144                 if ( (aip->submode != SM_SUPER_ATTACK) && (aip->submode != SM_GET_AWAY) && !(aip->ai_flags & AIF_STEALTH_PURSIUT) ) {
8145                         if (Missiontime - aip->last_attack_time > i2f(6)) {
8146                                 aip->submode = SM_SUPER_ATTACK;
8147                                 aip->submode_start_time = Missiontime;
8148                                 aip->last_attack_time = Missiontime;
8149                         }
8150                 }
8151
8152                 //      If a collision is expected, pull out!
8153                 //      If enemy is pointing away and moving a bit, don't worry about collision detection.
8154                 if ((dot_from_enemy > 0.5f) || (En_objp->phys_info.speed < 10.0f)) {
8155                         if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 4.0f)) {
8156                                 if ((Missiontime - aip->last_hit_time > F1_0*4) && (dist_to_enemy < Pl_objp->radius*2 + En_objp->radius*2)) {
8157                                         accelerate_ship(aip, -1.0f);
8158                                 } else {
8159                                         aip->submode = SM_AVOID;
8160                                         aip->submode_start_time = Missiontime;
8161                                 }
8162                         }
8163                 }
8164         }
8165
8166         switch (aip->submode) {
8167         case SM_CONTINUOUS_TURN:
8168                 if (Missiontime - aip->submode_start_time > i2f(3)) {
8169                         aip->last_attack_time = Missiontime;
8170                         aip->submode = SM_ATTACK;
8171                         aip->submode_start_time = Missiontime;
8172                 }
8173                 break;
8174
8175         case SM_ATTACK:
8176                 // if taraget is stealth and stealth not visible, then enter stealth find mode
8177                 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8178                         aip->submode = SM_STEALTH_FIND;
8179                         aip->submode_start_time = Missiontime;
8180                         aip->submode_parm0 = SM_SF_AHEAD;
8181                 } 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)) {
8182                         aip->submode = SM_SUPER_ATTACK;
8183                         aip->submode_start_time = Missiontime;
8184                         aip->last_attack_time = Missiontime;
8185                 } else if ((Missiontime - aip->last_hit_target_time > i2f(6)) &&
8186                         (dist_to_enemy < 500.0f) && (dot_to_enemy < 0.2f) &&
8187                         (frand() < (float) Game_skill_level/NUM_SKILL_LEVELS)) {
8188                         aip->submode = SM_GET_AWAY;
8189                         aip->submode_start_time = Missiontime;
8190                         aip->last_hit_target_time = Missiontime;
8191                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP)
8192                         && (dot_to_enemy < dot_from_enemy)
8193                         && (En_objp->phys_info.speed > 15.0f) 
8194                         && (dist_to_enemy < 200.0f) 
8195                         && (dist_to_enemy > 50.0f)
8196                         && (dot_to_enemy < 0.1f)
8197                         && (Missiontime - aip->submode_start_time > i2f(2))) {
8198                         aip->submode = SM_EVADE_BRAKE;
8199                         aip->submode_start_time = Missiontime;
8200                 } else if ((dot_to_enemy > 0.2f) && (dot_from_enemy > -0.2f) && (dot_from_enemy < 0.1f)) {
8201                         aip->submode = SM_GET_BEHIND;
8202                         aip->submode_start_time = Missiontime;
8203                 } 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)) {
8204                         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;
8205                                 aip->submode_start_time = Missiontime;
8206                                 aip->last_hit_target_time = Missiontime;
8207                         } else {
8208                                 aip->submode = SM_EVADE_SQUIGGLE;
8209                                 aip->submode_start_time = Missiontime;
8210                         }
8211                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP) && (Missiontime - aip->submode_start_time > F1_0*2)) {
8212                         if ((dot_to_enemy < 0.8f) && (dot_from_enemy > dot_to_enemy)) {
8213                                 if (frand() > 0.5f) {
8214                                         aip->submode = SM_CONTINUOUS_TURN;
8215                                         aip->submode_parm0 = myrand() & 0x0f;
8216                                         aip->submode_start_time = Missiontime;
8217                                 } else {
8218                                         aip->submode = SM_EVADE;
8219                                         aip->submode_start_time = Missiontime;
8220                                 }
8221                         } else {
8222                                 aip->submode_start_time = Missiontime;
8223                         }
8224                 }
8225
8226                 aip->last_attack_time = Missiontime;
8227
8228                 break;
8229                 
8230         case SM_EVADE_SQUIGGLE:
8231                 if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > 300.0f)) {
8232                         if ((dist_to_enemy < 100.0f) && (dot_to_enemy < 0.0f) && (dot_from_enemy > 0.5f)) {
8233                                 aip->submode = SM_EVADE_BRAKE;
8234                                 aip->submode_start_time = Missiontime;
8235                         } else {
8236                                 aip->last_attack_time = Missiontime;
8237                                 aip->submode = SM_ATTACK;
8238                                 aip->submode_start_time = Missiontime;
8239                         }
8240                 }
8241                 break;
8242         
8243         case SM_EVADE_BRAKE:
8244                 if ((dist_to_enemy < 15.0f) || (En_objp->phys_info.speed < 10.0f)) {
8245                         aip->submode = SM_AVOID;
8246                         aip->submode_start_time = Missiontime;
8247                 } else if ((dot_to_enemy > 0.9f) || ((dot_from_enemy > 0.9f) && (Missiontime - aip->submode_start_time > i2f(1)))) {
8248                         aip->last_attack_time = Missiontime;
8249                         aip->submode = SM_ATTACK;
8250                         aip->submode_start_time = Missiontime;
8251                 } else if (Missiontime - aip->submode_start_time > i2f(4)) {
8252                         aip->last_attack_time = Missiontime;
8253                         aip->submode = SM_ATTACK;
8254                         aip->submode_start_time = Missiontime;
8255                 }
8256                 break;
8257
8258         case SM_EVADE:
8259                 //      Modified by MK on 5/5/97 to keep trying to regain attack mode.  It's what a human would do.
8260                 if ((dot_to_enemy < 0.2f) && (dot_from_enemy < 0.8f) && (dist_to_enemy < 100.0f) && (En_objp->phys_info.speed > 15.0f)) {
8261                         aip->last_attack_time = Missiontime;
8262                         aip->submode = SM_EVADE_BRAKE;
8263                         aip->submode_start_time = Missiontime;
8264                 } else if (((dot_to_enemy > dot_from_enemy - 0.1f)
8265                         && (Missiontime > aip->submode_start_time + i2f(1)))
8266                         || (dist_to_enemy > 150.0f + 2*(Pl_objp->radius + En_objp->radius))) {
8267                         aip->last_attack_time = Missiontime;
8268                         aip->submode = SM_ATTACK;
8269                         aip->submode_start_time = Missiontime;
8270                 } else if (Missiontime - aip->submode_start_time > i2f(2))
8271                         if (dot_from_enemy > 0.8f) {
8272                                 aip->submode = SM_EVADE_SQUIGGLE;
8273                                 aip->submode_start_time = Missiontime;
8274                         }
8275
8276                 break;
8277
8278         case SM_SUPER_ATTACK:
8279                 // if stealth and invisible, enter stealth find mode
8280                 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8281                         aip->submode = SM_STEALTH_FIND;
8282                         aip->submode_start_time = Missiontime;
8283                         aip->submode_parm0 = SM_SF_AHEAD;
8284                 } 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) )) {
8285                         aip->ai_flags &= ~AIF_ATTACK_SLOWLY;    //      Just in case, clear here.
8286
8287                         switch (myrand() % 5) {
8288                         case 0:
8289                                 aip->submode = SM_CONTINUOUS_TURN;
8290                                 aip->submode_start_time = Missiontime;
8291                                 break;
8292                         case 1:
8293                                 aip->submode_start_time = Missiontime;  //      Stay in super attack mode
8294                                 break;
8295                         case 2:
8296                         case 3:
8297                                 if (frand() < (float) 0.5f * (aip->ai_class + Game_skill_level)/(Num_ai_classes + NUM_SKILL_LEVELS)) {
8298                                         aip->submode = SM_GET_AWAY;
8299                                         aip->submode_start_time = Missiontime;
8300                                 } else {
8301                                         aip->submode = SM_EVADE;
8302                                         aip->submode_start_time = Missiontime;
8303                                 }
8304                                 break;
8305                         case 4:
8306                                 if (dot_from_enemy + (NUM_SKILL_LEVELS - Game_skill_level) * 0.1f > dot_to_enemy) {     //      Less likely to GET_AWAY at lower skill levels.
8307                                         aip->submode = SM_EVADE;
8308                                         aip->submode_start_time = Missiontime;
8309                                 } else {
8310                                         aip->submode = SM_GET_AWAY;
8311                                         aip->submode_start_time = Missiontime;
8312                                 }
8313                                 break;
8314                         default:
8315                                 Int3(); //      Impossible!
8316                         }
8317                 }
8318
8319                 aip->last_attack_time = Missiontime;
8320
8321                 break;
8322
8323         case SM_AVOID:
8324                 if ((dot_to_enemy > -0.2f) && (dist_to_enemy / (dot_to_enemy + 0.3f) < 100.0f)) {
8325                         aip->submode_start_time = Missiontime;
8326                 } else if (Missiontime - aip->submode_start_time > i2f(1)/2)
8327                         if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 3.0f)) {
8328                                 aip->submode_start_time = Missiontime;
8329                         } else {
8330                                 aip->submode = SM_GET_BEHIND;
8331                                 aip->submode_start_time = Missiontime;
8332                         }
8333
8334                 break;
8335
8336         case SM_GET_BEHIND:
8337                 if ((dot_from_enemy < -0.7f) || (Missiontime - aip->submode_start_time > i2f(2))) {
8338                         aip->submode = SM_ATTACK;
8339                         aip->submode_start_time = Missiontime;
8340                         aip->last_attack_time = Missiontime;
8341                 }
8342                 break;
8343
8344         case SM_GET_AWAY:
8345                 if (Missiontime - aip->submode_start_time > i2f(2)) {
8346                         float   rand_dist;
8347
8348                         rand_dist = ((Missiontime >> 17) & 0x03) * 100.0f + 200.0f;     //      Some value in 200..500
8349                         if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > rand_dist) || (dot_from_enemy < 0.4f)) {
8350                                 aip->ai_flags |= AIF_ATTACK_SLOWLY;
8351                                 aip->submode = SM_ATTACK;
8352                                 aip->time_enemy_in_range = 2.0f;                //      Cheat.  Presumably if they were running away from you, they were monitoring you!
8353                                 aip->submode_start_time = Missiontime;
8354                                 aip->last_attack_time = Missiontime;
8355                         }
8356                 }
8357                 break;
8358
8359         case SM_EVADE_WEAPON:
8360                 if (aip->danger_weapon_objnum == -1) {
8361                         aip->submode = SM_ATTACK;
8362                         aip->submode_start_time = Missiontime;
8363                         aip->last_attack_time = Missiontime;
8364                 }
8365                 break;
8366
8367         // Either change to SM_ATTACK or AIM_FIND_STEALTH
8368         case SM_STEALTH_FIND:
8369                 // if time > 5 sec change mode to sweep
8370                 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8371                         aip->submode = SM_ATTACK;
8372                         aip->submode_start_time = Missiontime;
8373                         aip->last_attack_time = Missiontime;
8374                         // sweep if I can't find in 5 sec or bail from find
8375                 } else if ( ((Missiontime - aip->submode_start_time) > i2f(5)) || (aip->submode_parm0 == SM_SF_BAIL) ) {
8376                         // begin sweep mode
8377                         aip->submode = SM_STEALTH_SWEEP;
8378                         aip->submode_start_time = Missiontime;
8379                         aip->last_attack_time = Missiontime;
8380                         aip->submode_parm0 = SM_SS_SET_GOAL;
8381                 }
8382                 break;
8383
8384         case SM_STEALTH_SWEEP:
8385                 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8386                         aip->submode = SM_ATTACK;
8387                         aip->submode_start_time = Missiontime;
8388                         aip->last_attack_time = Missiontime;
8389                 } else if ( (timestamp() - aip->stealth_last_visible_stamp) < 5000 ) {
8390                         // go back to find mode
8391                         aip->submode = SM_STEALTH_FIND;
8392                         aip->submode_start_time = Missiontime;
8393                         aip->submode_parm0 = SM_SF_AHEAD;
8394                 } else if ( /*(Missiontime - aip->submode_start_time) > i2f(30) || */(aip->submode_parm0 == SM_SS_DONE) ) {
8395                         // set target objnum = -1
8396                         set_target_objnum(aip, -1);
8397
8398                         // set submode to attack
8399                         aip->submode = SM_ATTACK;
8400                         aip->submode_start_time = Missiontime;
8401                         aip->last_attack_time = Missiontime;
8402                 }
8403                 break;
8404
8405         case SM_ATTACK_FOREVER: //      Engines blown, just attack.
8406                 break;
8407
8408         default:
8409                 //Int3();
8410                 aip->submode = SM_ATTACK;
8411                 aip->last_attack_time = Missiontime;
8412
8413                 aip->submode_start_time = Missiontime;
8414         }
8415
8416         //
8417         //      Maybe fire primary weapon and update time_enemy_in_range
8418         //
8419         //nprintf(("AI", "time_enemy_in_range = %7.3f, dot = %7.3f\n", aip->time_enemy_in_range, dot_to_enemy));
8420
8421         if (aip->mode != AIM_EVADE) {
8422                 if (dot_to_enemy > 0.95f - 0.5f * En_objp->radius/max(1.0f, En_objp->radius + dist_to_enemy)) {
8423                         aip->time_enemy_in_range += flFrametime;
8424                         
8425                         //      Chance of hitting ship is based on dot product of firing ship's forward vector with vector to ship
8426                         //      and also the size of the target relative to distance to target.
8427                         if (dot_to_enemy > max(0.5f, 0.90f + aip->ai_accuracy/10.0f - En_objp->radius/max(1.0f,dist_to_enemy))) {
8428
8429                                 ship *temp_shipp;
8430                                 ship_weapon *tswp;
8431
8432                                 temp_shipp = &Ships[Pl_objp->instance];
8433                                 tswp = &temp_shipp->weapons;
8434                                 if ( tswp->num_primary_banks > 0 ) {
8435                                         float   scale;
8436                                         Assert(tswp->current_primary_bank < tswp->num_primary_banks);
8437                                         weapon_info     *pwip = &Weapon_info[tswp->primary_bank_weapons[tswp->current_primary_bank]];
8438
8439                                         //      Less likely to fire if far away and moving.
8440                                         scale = pwip->max_speed/(En_objp->phys_info.speed + pwip->max_speed);
8441                                         if (scale > 0.6f)
8442                                                 scale = (scale - 0.6f) * 1.5f;
8443                                         else
8444                                                 scale = 0.0f;
8445                                         if (dist_to_enemy < pwip->max_speed * (1.0f + scale)) {
8446                                                 ai_fire_primary_weapon(Pl_objp);
8447                                         }
8448
8449                                         //      Don't fire secondaries at a protected ship.
8450                                         if (!(En_objp->flags & OF_PROTECTED)) {
8451                                                 ai_choose_secondary_weapon(Pl_objp, aip, En_objp);
8452                                                 int current_bank = tswp->current_secondary_bank;
8453                                                 weapon_info     *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8454
8455                                                 if (current_bank > -1) {
8456                                                         if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8457                                                                 if (timestamp_until(swp->next_secondary_fire_stamp[current_bank]) > swip->fire_wait*1000.0f) {
8458                                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (swip->fire_wait*1000.0f));
8459                                                                 }
8460                                                         }
8461
8462                                                         if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
8463                                                                 if (tswp->current_secondary_bank >= 0) {
8464                                                                         weapon_info     *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8465                                                                         float firing_range;
8466                                                                         
8467                                                                         if (swip->wi_flags & WIF_BOMB)
8468                                                                                 firing_range = swip->max_speed * swip->lifetime * 0.75f;
8469                                                                         else
8470                                                                                 firing_range = swip->max_speed * swip->lifetime * (Game_skill_level + 1 + aip->ai_class/2)/NUM_SKILL_LEVELS;
8471
8472                                                                         // reduce firing range in nebula
8473                                                                         extern int Nebula_sec_range;
8474                                                                         if ((The_mission.flags & MISSION_FLAG_FULLNEB) && Nebula_sec_range) {
8475                                                                                 firing_range *= 0.8f;
8476                                                                         }
8477
8478                                                                         //      If firing a spawn weapon, distance doesn't matter.
8479                                                                         int     spawn_fire = 0;
8480
8481                                                                         if (swip->wi_flags & WIF_SPAWN) {
8482                                                                                 int     count;
8483
8484                                                                                 count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(Pl_objp)), &Pl_objp->pos, 1000.0f);
8485
8486                                                                                 if (count > 3)
8487                                                                                         spawn_fire = 1;
8488                                                                                 else if (count >= 1) {
8489                                                                                         float hull_percent = Pl_objp->hull_strength/sip->initial_hull_strength;
8490
8491                                                                                         if (hull_percent < 0.01f)
8492                                                                                                 hull_percent = 0.01f;
8493
8494                                                                                         if (frand() < 0.25f/(30.0f*hull_percent) * count)       //      With timestamp below, this means could fire in 30 seconds if one enemy.
8495                                                                                                 spawn_fire = 1;
8496                                                                                 }
8497                                                                         }
8498
8499                                                                         if (spawn_fire || (dist_to_enemy < firing_range)) {
8500                                                                                 if (ai_fire_secondary_weapon(Pl_objp)) {
8501                                                                                         //      Only if weapon was fired do we specify time until next fire.  If not fired, done in ai_fire_secondary...
8502                                                                                         float t;
8503                                                                                         
8504                                                                                         if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8505                                                                                                 t = swip->fire_wait;
8506                                                                                         } else {
8507                                                                                                 t = set_secondary_fire_delay(aip, temp_shipp, swip);
8508                                                                                         }
8509                                                                                         //nprintf(("AI", "Next secondary to be fired in %7.3f seconds.\n", t));
8510                                                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
8511                                                                                 }
8512                                                                         } else {
8513                                                                                 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
8514                                                                         }
8515                                                                 }
8516                                                         }
8517                                                 }
8518                                         }
8519                                 }
8520                         }
8521                 } else {
8522                         aip->time_enemy_in_range *= (1.0f - flFrametime);
8523                 }
8524         } else
8525                 aip->time_enemy_in_range *= (1.0f - flFrametime);
8526
8527 }
8528
8529 //      Make the object *objp move so that the point *dp on the object moves towards the point *vp
8530 //      Return distance.
8531 void dock_move_towards_point(object *objp, vector *dp, vector *vp, float speed_scale, float other_obj_speed = 0.0f)
8532 {
8533         physics_info    *pi = &objp->phys_info;
8534         float                           dist;                   //      dist to goal
8535         vector                  v2g;                    //      vector to goal
8536         vector                  abs_pnt;                //      location of dock point, ie objp->pos + db
8537
8538         if (dp == NULL)
8539                 abs_pnt = objp->pos;
8540         else
8541                 vm_vec_add(&abs_pnt, &objp->pos, dp);
8542
8543         dist = vm_vec_dist_quick(vp, &abs_pnt);
8544         if (dist > 0.0f) {
8545                 float   speed;
8546
8547                 dist = vm_vec_normalized_dir(&v2g, vp, &abs_pnt);
8548                 speed = fl_sqrt(dist) * speed_scale;
8549                 if (other_obj_speed < MAX_REPAIR_SPEED*0.75f)
8550                         speed += other_obj_speed;
8551                 else
8552                         speed += MAX_REPAIR_SPEED*0.75f;
8553
8554                 vm_vec_copy_scale(&pi->desired_vel, &v2g, speed);
8555         } else
8556                 vm_vec_zero(&pi->desired_vel);
8557 }
8558
8559 //      Set the orientation in the global reference frame for an object to attain
8560 //      to dock with another object.
8561 //      *dom            resultant global matrix
8562 //      *db_dest        pointer to destination docking bay information
8563 //      *db_src pointer to source docking bay information
8564 //      *dorient        pointer to global orientation of docking bay (ie, the dockee object's orient)
8565 //      *sorient        pointer to global orientation of docker
8566 void set_goal_dock_orient(matrix *dom, dock_bay *db_dest, dock_bay *db_src, matrix *dorient, matrix *sorient)
8567 {
8568         vector  fvec, uvec;
8569         matrix  m1, m2, m3;
8570
8571         //      Compute the global orientation of the docker's (dest) docking bay.
8572         fvec = db_dest->norm[0];
8573         vm_vec_negate(&fvec);
8574
8575         vm_vec_normalized_dir(&uvec, &db_dest->pnt[1], &db_dest->pnt[0]);
8576         vm_vector_2_matrix(&m1, &fvec, &uvec, NULL);
8577
8578         vm_matrix_x_matrix(&m3, dorient, &m1);
8579
8580         //      Compute the matrix given by the source docking bay.
8581         //      Pre-multiply the orientation of the source object (sorient) by the transpose
8582         //      of the docking bay's orientation, ie unrotate the source object's matrix.
8583         fvec = db_src->norm[0];
8584         vm_vec_normalized_dir(&uvec, &db_src->pnt[1], &db_src->pnt[0]);
8585         vm_vector_2_matrix(&m2, &fvec, &uvec, NULL);
8586         vm_transpose(&m2);
8587
8588         vm_matrix_x_matrix(dom, &m3, &m2);
8589 }
8590
8591 #define DOCK_BACKUP_RETURN_VAL  99999.9f
8592
8593 //      Make objp dock with dobjp
8594 //      Returns distance to goal, defined as distance between corresponding dock points, plus 10.0f * rotational velocity vector (DOA_DOCK only)
8595 //      DOA_APPROACH    means   approach point aip->path_cur
8596 //      DOA_DOCK                        means dock
8597 //      DOA_UNDOCK_1    means undock, moving to point nearest dock bay
8598 //      DOA_UNDOCK_2    means undock, moving to point nearest dock bay and facing away from ship
8599 //      DOA_DOCK_STAY   means rigidly maintain position in dock bay.
8600 float dock_orient_and_approach(object *objp, object *dobjp, int dock_mode)
8601 {
8602         ship_info       *sip0, *sip1;
8603         polymodel       *pm0, *pm1;
8604         ai_info         *aip;
8605         matrix          dom, nm;
8606         vector          goal_point, docker_point;
8607         float                   fdist = UNINITIALIZED_VALUE;
8608         int                     docker_index, dockee_index;             // index into docking_bays[] array for objects docking
8609                                                                                                                                 // docker is Pl_objp -- dockee is dobjp
8610         aip = &Ai_info[Ships[objp->instance].ai_index];
8611
8612         //      If dockee has moved much, then path will be recreated.
8613         //      Might need to change state if moved too far.
8614         if ((dock_mode != DOA_DOCK_STAY) && (dock_mode != DOA_DOCK)) {
8615                 if (maybe_recreate_path(objp, &Ai_info[Ships[objp->instance].ai_index], 0) > 5.0f) {
8616 /*                      if (dock_mode == DOA_APPROACH) {
8617                                 return DOCK_BACKUP_RETURN_VAL;
8618                         } else if (dock_mode == DOA_DOCK) {
8619                                 return DOCK_BACKUP_RETURN_VAL;          
8620                         }
8621 */              }
8622         }
8623
8624         objp->phys_info.forward_thrust = 0.0f;          //      Kill thrust so we don't have a sputtering thruster.
8625
8626         sip0 = &Ship_info[Ships[objp->instance].ship_info_index];
8627         sip1 = &Ship_info[Ships[dobjp->instance].ship_info_index];
8628         pm0 = model_get( sip0->modelnum );
8629         pm1 = model_get( sip1->modelnum );
8630
8631         docker_index = aip->dock_index;
8632         dockee_index = aip->dockee_index;
8633
8634         Assert( docker_index >= 0 );
8635         Assert( dockee_index >= 0 );
8636
8637         Assert(pm0->docking_bays[docker_index].num_slots == 2);
8638         Assert(pm1->docking_bays[dockee_index].num_slots == 2);
8639
8640         float speed_scale = 1.0f;
8641         if (sip0->flags & SIF_SUPPORT) {
8642                 speed_scale = 3.0f;
8643         }
8644
8645         switch (dock_mode) {
8646         case DOA_APPROACH:
8647                 {
8648                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8649                         return 9999.9f;
8650                 }
8651                 
8652                 //      Compute the desired global orientation matrix for the docker's station.
8653                 //      That is, the normal vector of the docking station must be the same as the
8654                 //      forward vector and the vector between its two points must be the uvec.
8655                 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8656
8657                 //      Compute new orientation matrix and update rotational velocity.
8658                 vector  w_in, w_out, vel_limit, acc_limit;
8659                 float           tdist, mdist, ss1;
8660
8661                 w_in = objp->phys_info.rotvel;
8662                 vel_limit = objp->phys_info.max_rotvel;
8663                 vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8664                 
8665                 if (sip0->flags & SIF_SUPPORT)
8666                         vm_vec_scale(&acc_limit, 2.0f);
8667
8668                 // 1 at end of line prevent overshoot
8669                 vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit, 1);
8670                 objp->phys_info.rotvel = w_out;
8671                 objp->orient = nm;
8672
8673                 //      Translate towards goal and note distance to goal.
8674                 goal_point = Path_points[aip->path_cur].pos;
8675                 mdist = ai_matrix_dist(&objp->orient, &dom);
8676                 tdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8677
8678                 //      If translation is badly lagging rotation, speed up translation.
8679                 if (mdist > 0.1f) {
8680                         ss1 = tdist/(10.0f * mdist);
8681                         if (ss1 > 2.0f)
8682                                 ss1 = 2.0f;
8683                 } else
8684                         ss1 = 2.0f;
8685
8686                 // nprintf(("AI", "speed scale = %7.3f\n", ss1));
8687                 speed_scale *= 1.0f + ss1;
8688
8689                 dock_move_towards_point(objp, NULL, &goal_point, speed_scale, dobjp->phys_info.speed);
8690
8691                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8692
8693                 //      Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8694                 // nprintf(("AI", "matrix dist = %7.3f, threshold = %7.3f\n", mdist, 2*flFrametime));
8695                 fdist += 2.0f * mdist;
8696
8697                 break;
8698         }
8699         case DOA_DOCK:
8700                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8701                         return 9999.9f;
8702                 }
8703         case DOA_DOCK_STAY:
8704                 //      Compute the desired global orientation matrix for the docker's station.
8705                 //      That is, the normal vector of the docking station must be the same as the
8706                 //      forward vector and the vector between its two points must be the uvec.
8707                 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8708
8709                 //      Compute distance between dock bay points.
8710                 vector  db0, db1, db2, db3;
8711
8712                 vm_vec_unrotate(&db0, &pm0->docking_bays[docker_index].pnt[0], &objp->orient);
8713                 vm_vec_add2(&db0, &objp->pos);
8714
8715                 vm_vec_unrotate(&db1, &pm0->docking_bays[docker_index].pnt[1], &objp->orient);
8716                 vm_vec_add2(&db1, &objp->pos);
8717
8718                 vm_vec_unrotate(&db2, &pm1->docking_bays[dockee_index].pnt[0], &dobjp->orient);
8719                 vm_vec_add2(&db2, &dobjp->pos);
8720
8721                 vm_vec_unrotate(&db3, &pm1->docking_bays[dockee_index].pnt[1], &dobjp->orient);
8722                 vm_vec_add2(&db3, &dobjp->pos);
8723
8724                 vm_vec_avg(&goal_point, &db2, &db3);
8725
8726                 vm_vec_avg(&docker_point, &db0, &db1);
8727                 vm_vec_sub2(&docker_point, &objp->pos);
8728
8729                 if (dock_mode == DOA_DOCK) {
8730                         vector  t1, t2;
8731                         vector  w_in, w_out, vel_limit, acc_limit;
8732
8733                         fdist = vm_vec_dist_quick(vm_vec_avg(&t1, &db0, &db1), vm_vec_avg(&t2, &db2, &db3));
8734
8735                         //      Compute new orientation matrix and update rotational velocity.
8736                         w_in = objp->phys_info.rotvel;
8737                         vel_limit = objp->phys_info.max_rotvel;
8738                         vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8739
8740                         if (sip0->flags & SIF_SUPPORT)
8741                                 vm_vec_scale(&acc_limit, 2.0f);
8742
8743                         vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit);
8744                         objp->phys_info.rotvel = w_out;
8745                         objp->orient = nm;
8746
8747                         //      Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8748                         fdist += 10.0f * vm_vec_mag_quick(&w_out);
8749
8750                         dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale, dobjp->phys_info.speed);
8751                 } else {
8752                         Assert(dock_mode == DOA_DOCK_STAY);
8753                         objp->orient = dom;
8754                         vector  temp;
8755                         vm_vec_sub(&temp, &goal_point, &docker_point);
8756                         vm_vec_sub(&objp->pos, &goal_point, &docker_point);
8757                 }
8758
8759                 break;
8760         case DOA_UNDOCK_1: {
8761                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8762                         return 9999.9f;
8763                 }
8764
8765                 //      Undocking.
8766                 //      Move to point on dock path nearest to dock station.
8767                 Assert(aip->path_length >= 2);
8768                 goal_point = Path_points[aip->path_start + aip->path_length-2].pos;
8769
8770                 vm_vec_zero(&docker_point);
8771                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8772
8773                 dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale);
8774
8775                 break;
8776                           }
8777
8778         case DOA_UNDOCK_2: {
8779                 //      Undocking.
8780                 //      Move to point on dock path nearest to dock station and orient away from big ship.
8781                 int             desired_index;
8782
8783                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8784                         return 9999.9f;
8785                 }
8786
8787                 Assert(aip->path_length >= 2);
8788 //              if (aip->path_length >= 3)
8789 //                      desired_index = aip->path_length-3;
8790 //              else
8791                         desired_index = aip->path_length-2;
8792
8793                 goal_point = Path_points[aip->path_start + desired_index].pos;
8794
8795                 dock_move_towards_point(objp, NULL, &goal_point, speed_scale);
8796
8797                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8798                 break;
8799                           }
8800         case DOA_UNDOCK_3: {
8801                 float           dist, goal_dist;
8802                 vector  away_vec;
8803
8804                 goal_dist = objp->radius + dobjp->radius + 25.0f;
8805
8806                 dist = vm_vec_normalized_dir(&away_vec, &objp->pos, &dobjp->pos);
8807                 vm_vec_scale_add(&goal_point, &dobjp->pos, &away_vec, goal_dist);
8808                 if (vm_vec_dist_quick(&goal_point, &dobjp->pos) < vm_vec_dist_quick(&objp->pos, &dobjp->pos))
8809                         fdist = 0.0f;
8810                 else {
8811                         float   dot, accel;
8812                         float turn_time = Ship_info[Ships[objp->instance].ship_info_index].srotation_time;
8813                         ai_turn_towards_vector(&goal_point, objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
8814
8815                         dot = vm_vec_dot(&objp->orient.v.fvec, &away_vec);
8816                         accel = 0.1f;
8817                         if (dot > accel)
8818                                 accel = dot;
8819                         if (dist > goal_dist/2)
8820                                 accel *= 1.2f - 0.5f*goal_dist/dist;
8821
8822                         accelerate_ship(aip, accel);
8823                         fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8824                 }
8825
8826                 break;
8827                                                          }
8828         }
8829
8830 #ifndef NDEBUG
8831         //      For debug purposes, compute global orientation of both dock vectors and show
8832         //      how close they are.
8833         vector  d0, d1;
8834
8835         vm_vec_unrotate(&d0, &pm0->docking_bays[docker_index].norm[0], &objp->orient);
8836         vm_vec_unrotate(&d1, &pm1->docking_bays[dockee_index].norm[0], &dobjp->orient);
8837
8838         //nprintf(("AI", "or/app: dist = %7.3f/%7.3f, dot = %7.3f, global dot = %7.3f\n", 
8839         //      vm_vec_dist_quick(&goal_point, &objp->pos), fdist,
8840         //      vm_vec_dot(&objp->orient.v.fvec, &dom.v.fvec), 
8841         //      vm_vec_dot(&d0, &d1)));
8842 #endif
8843
8844         // -- Note, A lot of callers don't care about fdist, so OK to return ERROR value: Assert(fdist != UNINITIALIZED_VALUE);
8845         return fdist;
8846
8847 }
8848
8849 void debug_find_guard_object()
8850 {
8851         ship                    *shipp = &Ships[Pl_objp->instance];     
8852         object          *objp;
8853
8854         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
8855                 if ((Pl_objp != objp) && (objp->type == OBJ_SHIP)) {
8856                         if (objp->instance != -1) {
8857                                 if (Ships[objp->instance].team == shipp->team)  {
8858                                         // nprintf(("AI", "Setting guard object for %s to %s\n", shipp->ship_name, Ships[objp->instance].ship_name));
8859                                         ai_set_guard_object(Pl_objp, objp);
8860                                 }
8861                         }
8862                 }
8863         }
8864
8865 }
8866
8867 //      Given an object number, return the number of ships attacking it.
8868 int num_ships_attacking(int objnum)
8869 {
8870         object  *objp;
8871         ship_obj        *so;
8872         int             count = 0;
8873
8874         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8875                 objp = &Objects[so->objnum];
8876                 if (objp->instance != -1) {
8877                         ai_info *aip;
8878                         aip = &Ai_info[Ships[objp->instance].ai_index];
8879
8880                         if ((aip->mode == AIM_CHASE) && (aip->target_objnum == objnum))
8881                                 if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team)
8882                                         count++;
8883                 }
8884         }
8885
8886         return count;
8887 }
8888
8889 //      For all objects attacking object #objnum, remove the one that is farthest away.
8890 //      Do this by resuming previous behavior, if any.  If not, set target_objnum to -1.
8891 void remove_farthest_attacker(int objnum)
8892 {
8893         object  *objp, *objp2, *farthest_objp;
8894         ship_obj        *so;
8895         float           farthest_dist;
8896
8897         objp2 = &Objects[objnum];
8898
8899         farthest_dist = 9999999.9f;
8900         farthest_objp = NULL;
8901
8902         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8903                 objp = &Objects[so->objnum];
8904                 if ( !(objp->flags & OF_PLAYER_SHIP)) {
8905                         if (objp->instance != -1) {
8906                                 ai_info *aip2;
8907
8908                                 aip2 = &Ai_info[Ships[objp->instance].ai_index];
8909
8910                                 if ((aip2->mode == AIM_CHASE) && (aip2->target_objnum == objnum)) {
8911                                         if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team) {
8912                                                 float   dist;
8913
8914                                                 dist = vm_vec_dist_quick(&objp->pos, &objp2->pos);
8915                                                 if (dist < farthest_dist) {
8916                                                         farthest_dist = dist;
8917                                                         farthest_objp = objp;
8918                                                 }
8919                                         }
8920                                 }
8921                         }
8922                 }
8923         }
8924
8925         if (farthest_objp != NULL) {
8926                 ai_info *aip;
8927                 Assert(farthest_objp->type == OBJ_SHIP);
8928                 Assert((farthest_objp->instance > -1) && (farthest_objp->instance < MAX_SHIPS));
8929                 Assert(Ships[farthest_objp->instance].ai_index > -1);
8930
8931                 aip = &Ai_info[Ships[farthest_objp->instance].ai_index];
8932
8933                 if (!maybe_resume_previous_mode(Pl_objp, aip)) {
8934                         //      If already ignoring something under player's orders, don't ignore current target.
8935                         if ((aip->ignore_objnum == UNUSED_OBJNUM) || (aip->ai_flags & AIF_TEMPORARY_IGNORE)) {
8936                                 aip->ignore_objnum = aip->target_objnum;
8937                                 aip->ignore_signature = Objects[aip->target_objnum].signature;
8938                                 aip->ai_flags |= AIF_TEMPORARY_IGNORE;
8939                                 aip->ignore_expire_timestamp = timestamp(((myrand() % 10) + 20) * 1000);        //      OK to attack again in 20 to 24 seconds.
8940                         }
8941                         aip->target_objnum = -1;
8942                         ai_do_default_behavior(farthest_objp);
8943                 }
8944         }
8945 }
8946
8947 // Maybe limit the number of attackers on attack_objnum.  For now, only limit attackers
8948 // in attacked_objnum is the player
8949 // input:       attacked_objnum =>              object index for ship we want to limit attacks on
8950 //
8951 //      exit:                   1       =>      num attackers exceeds maximum, abort
8952 //                                      0       =>      removed the farthest attacker
8953 //                                      -1      =>      nothing was done
8954 int ai_maybe_limit_attackers(int attacked_objnum)
8955 {
8956         int rval=-1;
8957
8958         // limit the number of ships attacking the _player_ only
8959 //      if ( attacked_objnum == OBJ_INDEX(Player_obj) ) {
8960         if ( Objects[attacked_objnum].flags & OF_PLAYER_SHIP) {
8961                 int num_attacking;
8962                 num_attacking = num_ships_attacking(attacked_objnum);
8963
8964                 if (num_attacking == Skill_level_max_attackers[Game_skill_level]) {
8965                         remove_farthest_attacker(attacked_objnum);
8966                         rval=0;
8967                 } else if (num_attacking > Skill_level_max_attackers[Game_skill_level]) {
8968                         rval=1;
8969                 }
8970                 //nprintf(("AI", "Num attacking player = %i\n", num_attacking));
8971         }
8972
8973         return rval;
8974 }
8975
8976 //      Object being guarded by object *guard_objp was hit by object *hitter_objp
8977 void guard_object_was_hit(object *guard_objp, object *hitter_objp)
8978 {
8979         int             hitter_objnum;
8980         ai_info *aip;
8981
8982         aip = &Ai_info[Ships[guard_objp->instance].ai_index];
8983
8984         if (guard_objp == hitter_objp) {
8985                 // Int3();      //      Bogus!  Who tried to get me to attack myself!  Trace out and fix!
8986                 return;
8987         }
8988
8989         if (guard_objp->type == OBJ_GHOST || hitter_objp->type == OBJ_GHOST)
8990                 return;
8991
8992         if (aip->ai_flags & AIF_NO_DYNAMIC)     //      Not allowed to pursue dynamic goals.  So, why are we guarding?
8993                 return;
8994
8995         Assert( (hitter_objp->type == OBJ_SHIP) || (hitter_objp->type == OBJ_ASTEROID) || (hitter_objp->type == OBJ_WEAPON) );
8996
8997         hitter_objnum = OBJ_INDEX(hitter_objp);
8998
8999         if ( hitter_objp->type == OBJ_SHIP ) {
9000                 //      If the hitter object is the ignore object, don't attack it.
9001                 if (is_ignore_object(aip, hitter_objp-Objects))
9002                         return;
9003
9004                 //      If hitter is on same team as me, don't attack him.
9005                 if (Ships[guard_objp->instance].team == Ships[hitter_objp->instance].team)
9006                         return;
9007
9008                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9009                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9010                         return;
9011                 }
9012
9013                 // dont attack if you can't see him
9014                 if ( awacs_get_level(hitter_objp, &Ships[aip->shipnum], 1) < 1 ) {
9015                         // if he's a stealth and visible, but not targetable, ok to attack.
9016                         if ( is_object_stealth_ship(hitter_objp) ) {
9017                                 if ( ai_is_stealth_visible(guard_objp, hitter_objp) != STEALTH_VISIBLE ) {
9018                                         return;
9019                                 }
9020                         }
9021                 }
9022         }
9023
9024         if (aip->target_objnum == -1) {
9025                 aip->ok_to_target_timestamp = timestamp(0);
9026         }
9027
9028         if ((aip->submode == AIS_GUARD_PATROL) || (aip->submode == AIS_GUARD_STATIC)) {
9029
9030                 if ( hitter_objp->type == OBJ_SHIP ) {
9031                         if (!(Ship_info[Ships[guard_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
9032                                 return;
9033                         }
9034
9035                         // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9036                         if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9037                                 return;
9038                         }
9039                 }
9040
9041                 if (aip->target_objnum != hitter_objnum) {
9042                         aip->aspect_locked_time = 0.0f;
9043                 }
9044
9045                 aip->ok_to_target_timestamp = timestamp(0);
9046
9047                 set_target_objnum(aip, hitter_objnum);
9048                 //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));
9049                 aip->previous_mode = AIM_GUARD;
9050                 aip->previous_submode = aip->submode;
9051                 aip->mode = AIM_CHASE;
9052                 aip->submode = SM_ATTACK;
9053                 aip->submode_start_time = Missiontime;
9054                 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9055         } else if (aip->previous_mode == AIM_GUARD) {
9056                 if (aip->target_objnum == -1) {
9057
9058                         if ( hitter_objp->type == OBJ_SHIP ) {
9059                                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9060                                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9061                                         return;
9062                                 }
9063                         }
9064
9065                         set_target_objnum(aip, hitter_objnum);
9066                 //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));
9067                         aip->mode = AIM_CHASE;
9068                         aip->submode = SM_ATTACK;
9069                         aip->submode_start_time = Missiontime;
9070                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9071                 } else {
9072                         int     num_attacking_cur, num_attacking_new;
9073
9074                         num_attacking_cur = num_ships_attacking(aip->target_objnum);
9075                         if (num_attacking_cur > 1) {
9076                                 num_attacking_new = num_ships_attacking(hitter_objnum);
9077
9078                                 if (num_attacking_new < num_attacking_cur) {
9079
9080                                         if ( hitter_objp->type == OBJ_SHIP ) {
9081                                                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9082                                                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9083                                                         return;
9084                                                 }
9085                                         }
9086                                         set_target_objnum(aip, hitter_objp-Objects);
9087                 //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));
9088                                         aip->mode = AIM_CHASE;
9089                                         aip->submode = SM_ATTACK;
9090                                         aip->submode_start_time = Missiontime;
9091                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9092                                 }
9093                         }
9094                 }
9095         }
9096 }
9097
9098 //      Ship object *hit_objp was hit by ship object *hitter_objp.
9099 //      See if anyone is guarding hit_objp and, if so, do something useful.
9100 void maybe_update_guard_object(object *hit_objp, object *hitter_objp)
9101 {
9102         object  *objp;
9103         ship_obj        *so;
9104
9105         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9106                 objp = &Objects[so->objnum];
9107                 if (objp->instance != -1) {
9108                         ai_info *aip;
9109                         aip = &Ai_info[Ships[objp->instance].ai_index];
9110
9111                         if ((aip->mode == AIM_GUARD) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
9112                                 if (aip->guard_objnum == hit_objp-Objects) {
9113                                         guard_object_was_hit(objp, hitter_objp);
9114                                 } else if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[hit_objp->instance].ai_index].wing)) {
9115                                         guard_object_was_hit(objp, hitter_objp);
9116                                 }
9117                         }
9118                 }
9119         }
9120 }
9121
9122 // Scan missile list looking for bombs homing on guarded_objp
9123 // return 1 if bomb is found (and targeted by guarding_objp), otherwise return 0
9124 int ai_guard_find_nearby_bomb(object *guarding_objp, object *guarded_objp)
9125 {       
9126         missile_obj     *mo;
9127         object          *bomb_objp, *closest_bomb_objp=NULL;
9128         float                   dist, dist_to_guarding_obj,closest_dist_to_guarding_obj=999999.0f;
9129         weapon          *wp;
9130         weapon_info     *wip;
9131
9132         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
9133                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
9134                 bomb_objp = &Objects[mo->objnum];
9135
9136                 wp = &Weapons[bomb_objp->instance];
9137                 wip = &Weapon_info[wp->weapon_info_index];
9138
9139                 if ( !(wip->wi_flags & WIF_BOMB) ) {
9140                         continue;
9141                 }
9142
9143                 if ( wp->homing_object != guarded_objp ) {
9144                         continue;
9145                 }
9146
9147                 dist = vm_vec_dist_quick(&bomb_objp->pos, &guarded_objp->pos);
9148
9149                 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9150                         dist_to_guarding_obj = vm_vec_dist_quick(&bomb_objp->pos, &guarding_objp->pos);
9151                         if ( dist_to_guarding_obj < closest_dist_to_guarding_obj ) {
9152                                 closest_dist_to_guarding_obj = dist_to_guarding_obj;
9153                                 closest_bomb_objp = bomb_objp;
9154                         }
9155                 }
9156         }
9157
9158         if ( closest_bomb_objp ) {
9159                 guard_object_was_hit(guarding_objp, closest_bomb_objp);
9160                 return 1;
9161         }
9162
9163         return 0;
9164 }
9165
9166 //      Scan enemy ships and see if one is near enough to guard object to be pursued.
9167 void ai_guard_find_nearby_ship(object *guarding_objp, object *guarded_objp)
9168 {
9169         ship            *guarding_shipp = &Ships[guarding_objp->instance];
9170         ai_info *guarding_aip = &Ai_info[guarding_shipp->ai_index];
9171         ship_obj        *so;
9172         object  *enemy_objp;
9173         float           dist;
9174
9175         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9176                 enemy_objp = &Objects[so->objnum];
9177
9178                 if (enemy_objp->instance < 0) {
9179                         continue;
9180                 }
9181
9182                 ship    *eshipp = &Ships[enemy_objp->instance];
9183
9184                 //      Don't attack a cargo container or other harmless ships
9185                 if (!(Ship_info[eshipp->ship_info_index].flags & SIF_HARMLESS)) {
9186                         if (guarding_shipp->team != eshipp->team)       {
9187                                 dist = vm_vec_dist_quick(&enemy_objp->pos, &guarded_objp->pos);
9188                                 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9189                                         guard_object_was_hit(guarding_objp, enemy_objp);
9190                                 } else if ((dist < 3000.0f) && (Ai_info[eshipp->ai_index].target_objnum == guarding_aip->guard_objnum)) {
9191                                         //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));
9192                                         guard_object_was_hit(guarding_objp, enemy_objp);
9193                                 }
9194                         }
9195                 }
9196         }
9197 }
9198
9199 // Scan for nearby asteroids.  Favor asteroids which have their collide_objnum set to that of the
9200 // guarded ship.  Also, favor asteroids that are closer to the guarding ship, since it looks cooler
9201 // when a ship blows up an asteroid then goes after the pieces that break off.
9202 void ai_guard_find_nearby_asteroid(object *guarding_objp, object *guarded_objp)
9203 {       
9204         float           dist;
9205
9206         object  *closest_asteroid_objp=NULL, *danger_asteroid_objp=NULL, *asteroid_objp;
9207         float           dist_to_self, closest_danger_asteroid_dist=999999.0f, closest_asteroid_dist=999999.0f;
9208
9209         for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp != END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) {
9210                 if ( asteroid_objp->type == OBJ_ASTEROID ) {
9211                         // Attack asteroid if near guarded ship
9212                         dist = vm_vec_dist_quick(&asteroid_objp->pos, &guarded_objp->pos);
9213                         if ( dist < (MAX_GUARD_DIST + guarded_objp->radius)*2) {
9214                                 dist_to_self = vm_vec_dist_quick(&asteroid_objp->pos, &guarding_objp->pos);
9215                                 if ( OBJ_INDEX(guarded_objp) == asteroid_collide_objnum(asteroid_objp) ) {
9216                                         if( dist_to_self < closest_danger_asteroid_dist ) {
9217                                                 danger_asteroid_objp=asteroid_objp;
9218                                                 closest_danger_asteroid_dist=dist_to_self;
9219                                         }
9220                                 } 
9221                                 if ( dist_to_self < closest_asteroid_dist ) {
9222                                         // only attack if moving slower than own max speed
9223                                         if ( vm_vec_mag_quick(&asteroid_objp->phys_info.vel) < guarding_objp->phys_info.max_vel.xyz.z ) {
9224                                                 closest_asteroid_dist = dist_to_self;
9225                                                 closest_asteroid_objp = asteroid_objp;
9226                                         }
9227                                 }
9228                         }
9229                 }
9230         }
9231
9232         if ( danger_asteroid_objp ) {
9233                 guard_object_was_hit(guarding_objp, danger_asteroid_objp);
9234         } else if ( closest_asteroid_objp ) {
9235                 guard_object_was_hit(guarding_objp, closest_asteroid_objp);
9236         }
9237 }
9238
9239 //      Scan potential harmful objects and see if one is near enough to guard object to be pursued.
9240 void ai_guard_find_nearby_object()
9241 {
9242         ship                    *shipp = &Ships[Pl_objp->instance];
9243         ai_info         *aip = &Ai_info[shipp->ai_index];
9244         object          *guardobjp;
9245         int                     bomb_found=0;
9246
9247         guardobjp = &Objects[aip->guard_objnum];
9248         
9249         // highest priority is a bomb fired on guarded ship
9250         bomb_found = ai_guard_find_nearby_bomb(Pl_objp, guardobjp);
9251
9252         if ( !bomb_found ) {
9253                 // check for ships if there are no bombs fired at guarded ship
9254                 ai_guard_find_nearby_ship(Pl_objp, guardobjp);
9255
9256                 // if not attacking anything, go for asteroid close to guarded ship
9257                 if ( (aip->target_objnum == -1) && asteroid_count() ) {
9258                         ai_guard_find_nearby_asteroid(Pl_objp, guardobjp);
9259                 }
9260         }
9261 }
9262
9263 // gets closest point on extended axis of cylinder, r_vec, and radius of cylinder
9264 // returns z of axis_point in cyl_objp reference frame
9265 float get_cylinder_points(object *other_objp, object *cyl_objp, vector *axis_pt, vector *r_vec, float *radius)
9266 {
9267         Assert(other_objp->type == OBJ_SHIP);
9268         Assert(cyl_objp->type == OBJ_SHIP);
9269
9270         // get radius of cylinder
9271         polymodel *pm = model_get(Ships[cyl_objp->instance].modelnum);
9272         float tempx, tempy;
9273         tempx = max(-pm->mins.xyz.x, pm->maxs.xyz.x);
9274         tempy = max(-pm->mins.xyz.y, pm->maxs.xyz.y);
9275         *radius = max(tempx, tempy);
9276
9277         // get vec from cylinder to other_obj
9278         vector r_sph;
9279         vm_vec_sub(&r_sph, &other_objp->pos, &cyl_objp->pos);
9280
9281         // get point on axis and on cylinder
9282         // extended_cylinder_z is along extended cylinder
9283         // cylinder_z is capped within cylinder
9284         float extended_cylinder_z = vm_vec_dotprod(&r_sph, &cyl_objp->orient.v.fvec);
9285
9286         // get pt on axis of extended cylinder
9287         vm_vec_scale_add(axis_pt, &cyl_objp->pos, &cyl_objp->orient.v.fvec, extended_cylinder_z);
9288
9289         // get r_vec (pos - axis_pt) normalized
9290         vm_vec_normalized_dir(r_vec, &other_objp->pos, axis_pt);
9291
9292         return extended_cylinder_z;
9293 }
9294
9295 // handler for guard behavior when guarding BIG ships
9296 //      When someone has attacked guarded ship, then attack that ship.
9297 // To attack another ship, switch out of guard mode into chase mode.
9298 void ai_big_guard()
9299 {
9300         
9301         ship                    *shipp = &Ships[Pl_objp->instance];
9302         ai_info         *aip = &Ai_info[shipp->ai_index];
9303         object          *guard_objp;
9304
9305         // sanity checks already done in ai_guard()
9306         guard_objp = &Objects[aip->guard_objnum];
9307
9308         switch (aip->submode) {
9309         case AIS_GUARD_STATIC:
9310         case AIS_GUARD_PATROL:
9311                 {
9312                 vector axis_pt, r_vec, theta_vec;
9313                 float radius, extended_z;
9314
9315                 // get random [0 to 1] based on OBJNUM
9316                 float objval = static_randf(Pl_objp-Objects);
9317
9318                 // get position relative to cylinder of guard_objp              
9319                 extended_z = get_cylinder_points(Pl_objp, guard_objp, &axis_pt, &r_vec, &radius);
9320                 vm_vec_crossprod(&theta_vec, &guard_objp->orient.v.fvec, &r_vec);
9321
9322                 // half ships circle each way
9323                 if (objval > 0.5f) {
9324                         vm_vec_negate(&theta_vec);
9325                 }
9326
9327                 float min_guard_dist = radius + Pl_objp->radius + 50.0f;
9328                 float desired_guard_dist = min_guard_dist + 0.5f * ((1.0f + objval) * MAX_GUARD_DIST);
9329                 float max_guard_dist =     min_guard_dist + 1.0f * ((1.0f + objval) * MAX_GUARD_DIST);
9330
9331                 // get z extents
9332                 float min_z, max_z, length;
9333                 polymodel *pm = model_get(Ships[guard_objp->instance].modelnum);
9334                 min_z = pm->mins.xyz.z;
9335                 max_z = pm->maxs.xyz.z;
9336                 length = max_z - min_z;
9337
9338                 // get desired z
9339                 // how often to choose new desired_z
9340                 // 1*(64) sec < 2000, 2*(64) < 2-4000 3*(64) > 4-8000, etc (Missiontime >> 22 is 64 sec intervals)
9341                 int time_choose = int(floor(log(length * 0.001) / log(2)));
9342                 float desired_z = min_z + length * static_randf( Pl_objp-Objects ^ (Missiontime >> (22 + time_choose)) );
9343
9344                 // get r from guard_ship
9345                 float cur_guard_rad = vm_vec_dist(&Pl_objp->pos, &axis_pt);
9346
9347                 // is ship within extents of cylinder of ship it is guarding
9348                 int inside = (extended_z > min_z) && (extended_z < min_z + length);
9349
9350                 vector goal_pt;
9351                 // maybe go into orbit mode
9352                 if (cur_guard_rad < max_guard_dist) {
9353                         if ( cur_guard_rad > min_guard_dist ) {
9354                                 if (inside) {
9355                                         // orbit
9356                                         vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, desired_guard_dist);
9357                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9358                                 } else {
9359                                         // move to where I can orbit
9360                                         if (extended_z < min_z) {
9361                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, min_z);
9362                                         } else {
9363                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, max_z);
9364                                         }
9365                                         vm_vec_scale_add2(&goal_pt, &r_vec, desired_guard_dist);
9366                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9367                                 }
9368                         } else {
9369                                 // too close for orbit mode
9370                                 if (inside) {
9371                                         // inside (fly straight out and return circle)
9372                                         vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, max_guard_dist);
9373                                 } else {
9374                                         // outside (fly to edge and circle)
9375                                         if (extended_z < min_z) {
9376                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, min_z);
9377                                         } else {
9378                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, max_z);
9379                                         }
9380                                         vm_vec_scale_add2(&goal_pt, &r_vec, max_guard_dist);
9381                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9382                                 }
9383                         }
9384
9385                         if (Pl_objp->phys_info.fspeed > 0) {
9386                                 // modify goal_pt to take account moving guard objp
9387                                 float dist = vm_vec_dist_quick(&Pl_objp->pos, &goal_pt);
9388                                 float time = dist / Pl_objp->phys_info.fspeed;
9389                                 vm_vec_scale_add2(&goal_pt, &guard_objp->phys_info.vel, time);
9390
9391                                 // now modify to move to desired z (at a max of 20 m/s)
9392                                 float delta_z = desired_z - extended_z;
9393                                 float v_z = delta_z * 0.2f;
9394                                 if (v_z < -20) {
9395                                         v_z = -20.0f;
9396                                 } else if (v_z > 20) {
9397                                         v_z = 20.0f;
9398                                 }
9399
9400                                 vm_vec_scale_add2(&goal_pt, &guard_objp->orient.v.fvec, v_z*time);
9401                         }
9402
9403                 } else {
9404                         // cast vector to center of guard_ship adjusted by desired_z
9405                         float delta_z = desired_z - extended_z;
9406                         vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, delta_z);
9407                 }
9408
9409                 // try not to bump into things along the way
9410                 if ( (cur_guard_rad > max_guard_dist) || (extended_z < min_z) || (extended_z > max_z) ) {
9411                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9412                                 return;
9413                         }
9414
9415                         if (avoid_player(Pl_objp, &goal_pt)) {
9416                                 return;
9417                         }
9418                 } else {
9419                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9420                                 return;
9421                         }
9422                 }
9423
9424                 // got the point, now let's go there
9425                 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);
9426 //              aip->goal_point = goal_pt;
9427                 accelerate_ship(aip, 1.0f);
9428
9429                 //      Periodically, scan for a nearby ship to attack.
9430                 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9431                         ai_guard_find_nearby_object();
9432                 }
9433                 }
9434                 break;
9435
9436         case AIS_GUARD_ATTACK:
9437                 //      The guarded ship has been attacked.  Do something useful!
9438                 ai_chase();
9439                 break;
9440
9441         default:
9442                 //Int3();       //      Illegal submode for Guard mode.
9443                 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9444                 aip->submode = AIS_GUARD_PATROL;
9445                 break;
9446         }
9447 }
9448
9449 //      Main handler for guard behavior.
9450 //      When someone has attacked guarded ship, then attack that ship.
9451 // To attack another ship, switch out of guard mode into chase mode.
9452 void ai_guard()
9453 {
9454         ship                    *shipp = &Ships[Pl_objp->instance];
9455         ai_info         *aip = &Ai_info[shipp->ai_index];
9456         object          *guard_objp;    
9457         ship                    *gshipp;
9458         float                   dist_to_guardobj, dot_to_guardobj;
9459         vector          vec_to_guardobj;
9460
9461         /*      //      Debug code, find an object to guard.
9462         int finding_guard_objnum = 0;   //      Debug code, to see if body of "if" below gets executed. 
9463         if (aip->guard_objnum == -1) {
9464                 finding_guard_objnum = 1;
9465                 debug_find_guard_object();
9466                 if (aip->guard_objnum == -1)
9467                         return;
9468         }
9469 */
9470         if (aip->guard_objnum == -1) {
9471                 aip->mode = AIM_NONE;
9472                 return;
9473         }
9474
9475         Assert(aip->guard_objnum != -1);
9476
9477         guard_objp = &Objects[aip->guard_objnum];
9478
9479         if (guard_objp == Pl_objp) {
9480                 Int3();         //      This seems illegal.  Why is a ship guarding itself?
9481                 aip->guard_objnum = -1;
9482                 return;
9483         }
9484
9485         // check that I have someone to guard
9486         if (guard_objp->instance == -1) {
9487                 return;
9488         }
9489
9490         //      Not sure whether this should be impossible, or a reasonable cleanup condition.
9491         //      For now (3/31/97), it's getting trapped by an Assert, so clean it up.
9492         if (guard_objp->type != OBJ_SHIP) {
9493                 aip->guard_objnum = -1;
9494                 return;
9495         }
9496
9497         // handler for gurad object with BIG radius
9498         if (guard_objp->radius > BIG_GUARD_RADIUS) {
9499                 ai_big_guard();
9500                 return;
9501         }
9502
9503         gshipp = &Ships[guard_objp->instance];
9504
9505         float                   objval;
9506         vector          goal_point;
9507         vector          rel_vec;
9508         float                   dist_to_goal_point, dot_to_goal_point, accel_scale;
9509         vector          v2g, rvec;
9510
9511         // get random [0 to 1] based on OBJNUM
9512         objval = static_randf(Pl_objp-Objects);
9513
9514         switch (aip->submode) {
9515         case AIS_GUARD_STATIC:
9516         case AIS_GUARD_PATROL:
9517                 //      Stay near ship
9518                 dist_to_guardobj = vm_vec_normalized_dir(&vec_to_guardobj, &guard_objp->pos, &Pl_objp->pos);
9519                 dot_to_guardobj = vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_to_guardobj);
9520
9521                 rel_vec = aip->guard_vec;
9522                 vm_vec_add(&goal_point, &guard_objp->pos, &rel_vec);
9523
9524                 vm_vec_normalized_dir(&v2g, &goal_point, &Pl_objp->pos);
9525                 dist_to_goal_point = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
9526                 dot_to_goal_point = vm_vec_dot(&v2g, &Pl_objp->orient.v.fvec);
9527                 accel_scale = (1.0f + dot_to_goal_point)/2.0f;
9528
9529                 //      If far away, get closer
9530                 if (dist_to_goal_point > MAX_GUARD_DIST + 1.5 * (Pl_objp->radius + guard_objp->radius)) {
9531                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 5.0f)) {
9532                                 return;
9533                         }
9534
9535                         if (avoid_player(Pl_objp, &goal_point)) {
9536                                 return;
9537                         }
9538
9539                         // quite far away, so try to go straight to 
9540                         compute_desired_rvec(&rvec, &goal_point, &Pl_objp->pos);
9541                         ai_turn_towards_vector(&goal_point, Pl_objp, flFrametime, Ship_info[shipp->ship_info_index].srotation_time, NULL, NULL, 0.0f, 0, &rvec);
9542
9543                         accelerate_ship(aip, accel_scale * (0.25f + dist_to_goal_point/700.0f));
9544                 } else {
9545                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 2.0f)) {
9546                                 return;
9547                         }
9548
9549                         // get max of guard_objp (1) normal speed (2) dock speed
9550                         float speed = guard_objp->phys_info.speed;
9551
9552                         if (guard_objp->type == OBJ_SHIP) {
9553                                 ai_info *guard_aip = &Ai_info[Ships[guard_objp->instance].ai_index];
9554
9555                                 if (guard_aip->dock_objnum != -1) {
9556                                         speed = max(speed, Objects[guard_aip->dock_objnum].phys_info.speed);
9557                                 }
9558                         }
9559                         
9560                         //      Deal with guarding a small object.
9561                         //      If going to guard_vec might cause a collision with guarded object, pick a new guard point.
9562                         if (vm_vec_dot(&v2g, &vec_to_guardobj) > 0.8f) {
9563                                 if (dist_to_guardobj < dist_to_goal_point) {
9564                                         ai_set_guard_vec(Pl_objp, guard_objp);  //      OK to return here.
9565                                         return;
9566                                 }
9567                         } 
9568
9569                         if (speed > 10.0f) {
9570                                 //      If goal ship is moving more than a tiny bit, don't orbit it, get near it.
9571                                 if (vm_vec_dist_quick(&goal_point, &Pl_objp->pos) > 40.0f) {
9572                                         if (vm_vec_dot(&Pl_objp->orient.v.fvec, &v2g) < 0.0f) {
9573                                                 //      Just slow down, don't turn.
9574                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point/10.0f);
9575                                         } else {
9576                                                 //      Goal point is in front.
9577
9578                                                 //      If close to goal point, don't change direction, just change speed.
9579                                                 if (dist_to_goal_point > Pl_objp->radius + 10.0f) {
9580                                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9581                                                 }
9582                                                 
9583                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + (dist_to_goal_point-40.0f)/20.0f);
9584                                         }
9585                                 } else {
9586                                         if (dot_to_goal_point > 0.8f) {
9587                                                 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9588                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + dist_to_goal_point*0.1f);
9589                                         } else {
9590                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point*0.1f - 1.0f);
9591                                         }
9592                                 }
9593                         // consider guard object STILL
9594                         } else if (guard_objp->radius < 50.0f) {
9595                                 if (dist_to_goal_point > 15.0f) {
9596                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9597                                         set_accel_for_target_speed(Pl_objp, (dist_to_goal_point-10.0f)/2.0f);
9598                                 } else if (Pl_objp->phys_info.speed < 1.0f) {
9599                                         turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9600                                 }
9601                                 //      It's a big ship
9602                         } else if (dist_to_guardobj > MAX_GUARD_DIST + Pl_objp->radius + guard_objp->radius) {
9603                                 //      Orbiting ship, too far away
9604                                 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.0f + objval/2) * guard_objp->radius);
9605                                 accelerate_ship(aip, (1.0f + dot)/2.0f);
9606                         } else if (dist_to_guardobj < Pl_objp->radius + guard_objp->radius) {
9607                                 //      Orbiting ship, got too close
9608                                 turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9609                                 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9610                                         change_acceleration(aip, 0.25f);
9611                                 else
9612                                         accelerate_ship(aip, 0.5f + objval/4.0f);
9613                         } else {
9614                                 //      Orbiting ship, about the right distance away.
9615                                 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.5f + objval/2.0f)*guard_objp->radius);
9616                                 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9617                                         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));
9618                                 else
9619                                         accelerate_ship(aip, 0.5f * (1.0f + dot) * (0.3f + objval/3.0f));
9620                         }
9621                 }
9622
9623                 //      Periodically, scan for a nearby ship to attack.
9624                 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9625                         ai_guard_find_nearby_object();
9626                 }
9627                 break;
9628
9629         case AIS_GUARD_ATTACK:
9630                 //      The guarded ship has been attacked.  Do something useful!
9631                 ai_chase();
9632
9633                 break;
9634         default:
9635                 //Int3();       //      Illegal submode for Guard mode.
9636                 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9637                 aip->submode = AIS_GUARD_PATROL;
9638                 break;
9639         }
9640
9641 }
9642
9643 // Return the object of the ship that the given object is docked
9644 // with.  Currently, we know a ship is docked when his ai_mode is AIM_DOCK,
9645 // and his submode is AIS_DOCK_3.  I suppose that this is likely to change though.
9646 // Also, the objnum that was is passed in may not be the object that actually
9647 // performed the docking maneuver.  This code will account for that case.
9648 object *ai_find_docked_object( object *docker )
9649 {
9650         ai_info *aip;
9651
9652         // we are trying to find the dockee of docker.  (Note that that these terms
9653         // are totally relative to what is passed in as a parameter.)
9654
9655         // first thing to attempt is to check and see if this object is docked with something.
9656         Assert( docker->type == OBJ_SHIP );             // this had probably better be a ship!!!
9657         aip = &Ai_info[Ships[docker->instance].ai_index];
9658         if ( !(aip->ai_flags & AIF_DOCKED) )            // flag not set if not docked with anything
9659                 return NULL;
9660
9661         if ( aip->dock_objnum == -1 ) {
9662                 Int3();                                                                                 // mwa says this is wrong wrong wrong
9663                 ai_do_objects_undocked_stuff( docker, NULL );
9664                 return NULL;
9665         }
9666
9667         return &Objects[aip->dock_objnum];
9668
9669 }
9670
9671
9672 // define for the points subtracted from score for a rearm started on a player.
9673 #define REPAIR_PENALTY          50
9674
9675
9676 // function to clean up ai flags, variables, and other interesting information
9677 // for a ship that was getting repaired.  The how parameter is useful for multiplayer
9678 // only in that it tells us why the repaired ship is being cleaned up.
9679 void ai_do_objects_repairing_stuff( object *repaired_objp, object *repair_objp, int how )
9680 {
9681         ai_info *aip, *repair_aip;
9682         int             stamp = -1;
9683
9684         Assert( repaired_objp->type == OBJ_SHIP);
9685         aip = &Ai_info[Ships[repaired_objp->instance].ai_index];
9686
9687         // multiplayer
9688         int p_index;
9689         p_index = -1;
9690         if(Game_mode & GM_MULTIPLAYER){
9691                 p_index = multi_find_player_by_object(repaired_objp);           
9692         }               
9693         else {          
9694                 if(repaired_objp == Player_obj){
9695                         p_index = Player_num;
9696                 }
9697         }
9698
9699         switch( how ) {
9700         case REPAIR_INFO_BEGIN:
9701                 aip->ai_flags |= AIF_BEING_REPAIRED;
9702                 aip->ai_flags &= ~AIF_AWAITING_REPAIR;
9703                 stamp = timestamp(-1);
9704
9705                 // if this is a player ship, then subtract the repair penalty from this player's score
9706                 if ( repaired_objp->flags & OF_PLAYER_SHIP ) {
9707                         if ( !(Game_mode & GM_MULTIPLAYER) ) {
9708                                 Player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());                    // subtract the penalty
9709                         } else {
9710                                 /*
9711                                 int pnum;
9712
9713                                 // multiplayer game -- find the player, then subtract the score
9714                                 pnum = multi_find_player_by_object( repaired_objp );
9715                                 if ( pnum != -1 ) {
9716                                         Net_players[pnum].player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());
9717
9718                                         // squad war
9719                                         multi_team_maybe_add_score(-(int)(REPAIR_PENALTY * scoring_get_scale_factor()), Net_players[pnum].p_info.team);
9720                                 } else {
9721                                         nprintf(("Network", "Couldn't find player for ship %s for repair penalty\n", Ships[repaired_objp->instance].ship_name));
9722                                 }
9723                                 */
9724                         }
9725                 }
9726                 break;
9727
9728         case REPAIR_INFO_BROKEN:
9729                 aip->ai_flags &= ~AIF_BEING_REPAIRED;
9730                 aip->ai_flags |= AIF_AWAITING_REPAIR;
9731                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9732                 break;
9733
9734         case REPAIR_INFO_END:
9735                 // when only awaiting repair, and the repair is ended, then set dock_objnum to -1.
9736                 if ( aip->ai_flags & AIF_AWAITING_REPAIR ){
9737                         aip->dock_objnum = -1;
9738                 }
9739                 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9740                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9741                 break;
9742
9743         case REPAIR_INFO_QUEUE:
9744                 aip->ai_flags |= AIF_AWAITING_REPAIR;
9745                 if ( aip == Player_ai ){
9746                         hud_support_view_start();
9747                 }
9748                 stamp = timestamp(-1);
9749                 break;
9750
9751         case REPAIR_INFO_ABORT:
9752         case REPAIR_INFO_KILLED:
9753                 // 5/4/98 -- MWA -- Need to set dock objnum to -1 to let code know this guy who was getting
9754                 // repaired (or queued for repair), isn't really going to be docked with anyone anymore.
9755                 aip->dock_objnum = -1;
9756                 aip->ai_flags &= ~AIF_DOCKED;
9757                 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9758                 if (repair_objp != NULL) {
9759                         repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9760                         repair_aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9761                 }               
9762
9763                 if ( p_index >= 0 ) {
9764                         hud_support_view_abort();
9765
9766                         // send appropriate message to player here
9767                         if ( how == REPAIR_INFO_KILLED ){
9768                                 message_send_builtin_to_player( MESSAGE_SUPPORT_KILLED, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9769                         } else {
9770                                 if ( repair_objp ){
9771                                         message_send_builtin_to_player( MESSAGE_REPAIR_ABORTED, &Ships[repair_objp->instance], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9772                                 }
9773                         }
9774                 }
9775
9776                 // add log entry if this is a player
9777                 if ( repaired_objp->flags & OF_PLAYER_SHIP ){
9778                         mission_log_add_entry(LOG_PLAYER_REARM_ABORT, Ships[repaired_objp->instance].ship_name, NULL);
9779                 }
9780
9781                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9782                 break;
9783
9784         case REPAIR_INFO_COMPLETE:
9785                 // clear the being repaired flag -- and 
9786                 if ( p_index >= 0 ) {
9787                         Assert( repair_objp );
9788                         
9789                         hud_support_view_stop();                        
9790
9791                         message_send_builtin_to_player(MESSAGE_REPAIR_DONE, &Ships[repair_objp->instance], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, p_index, -1);
9792                 }
9793                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9794                 break;
9795
9796         case REPAIR_INFO_ONWAY:
9797                 // need to set the dock_signature so that clients in multiplayer games rearm correctly
9798                 Assert( repair_objp );
9799                 aip->dock_signature = repair_objp->signature; 
9800                 aip->dock_objnum = OBJ_INDEX(repair_objp);
9801                 stamp = timestamp(-1);
9802                 break;
9803
9804         default:
9805                 Int3();                 // bogus type of repair info
9806         }
9807
9808         if (repair_objp){
9809                 Ai_info[Ships[repair_objp->instance].ai_index].warp_out_timestamp = stamp;
9810         }
9811
9812         // repair_objp might be NULL is we are cleaning up this mode because of the support ship
9813         // getting killed.
9814         if ( repair_objp ) {
9815                 aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9816                 switch ( how ) {
9817                 case REPAIR_INFO_ONWAY:
9818                         Assert( repaired_objp != NULL );
9819                         aip->goal_objnum = OBJ_INDEX(repaired_objp);
9820                         aip->ai_flags |= AIF_REPAIRING;
9821                         break;
9822
9823                 case REPAIR_INFO_BROKEN:
9824                         break;
9825
9826                 case REPAIR_INFO_END:
9827                 case REPAIR_INFO_ABORT:
9828                 case REPAIR_INFO_KILLED:
9829                         if ( how == REPAIR_INFO_ABORT )
9830                                 aip->goal_objnum = -1;
9831
9832                         aip->ai_flags &= ~AIF_REPAIRING;
9833                         break;
9834                         
9835                 case REPAIR_INFO_QUEUE:
9836                         ai_add_rearm_goal( repaired_objp, repair_objp );
9837                         break;
9838
9839                 case REPAIR_INFO_BEGIN:
9840                 case REPAIR_INFO_COMPLETE:
9841                         break;
9842
9843                 default:
9844                         Int3();         // bogus type of repair info
9845                 }
9846         }
9847
9848         multi_maybe_send_repair_info( repaired_objp, repair_objp, how );
9849 }
9850
9851 //      Cleanup AI stuff for when a ship was supposed to dock with another, but the ship
9852 //      it was supposed to dock with is no longer valid.
9853 void ai_cleanup_dock_mode(ai_info *aip, ship *shipp)
9854 {
9855         object *objp;
9856
9857         objp = &Objects[shipp->objnum];
9858         aip->mode = AIM_NONE;
9859
9860         if (aip->ai_flags & AIF_REPAIRING) {
9861                 Assert( aip->goal_objnum != -1 );
9862                 ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], &Objects[shipp->objnum], REPAIR_INFO_KILLED );
9863         } else if ( aip->ai_flags & AIF_BEING_REPAIRED ) {
9864                 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9865                 Assert( aip->dock_objnum != -1 );
9866                 ai_do_objects_repairing_stuff( &Objects[shipp->objnum], &Objects[aip->dock_objnum], REPAIR_INFO_KILLED );
9867         } else if ( aip->ai_flags & AIF_AWAITING_REPAIR ) {
9868                 // need to find the support ship that has me as a goal_objnum
9869                 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9870                 // MWA -- 3/38/98  Check to see if this guy is queued for a support ship, or there is already
9871                 // one in the mission
9872                 if ( mission_is_repair_scheduled(objp) ) {
9873                         mission_remove_scheduled_repair( objp );                        // this function will notify multiplayer clients.
9874                 } else {
9875                         if ( aip->dock_objnum != -1 )
9876                                 ai_do_objects_repairing_stuff( objp, &Objects[aip->dock_objnum], REPAIR_INFO_ABORT );
9877                         else
9878                                 ai_do_objects_repairing_stuff( objp, NULL, REPAIR_INFO_ABORT );
9879                 }
9880         }
9881
9882         if ( aip->ai_flags & AIF_DOCKED ) {
9883                 ai_info *other_aip;
9884
9885                 Assert( aip->dock_objnum != -1 );
9886
9887                 // if docked, and the dock_objnum is not undocking, force them to near last stage
9888                 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
9889                 if ( (other_aip->mode == AIM_DOCK) && (other_aip->submode < AIS_UNDOCK_3) )
9890                         other_aip->submode = AIS_UNDOCK_3;
9891                 ai_do_objects_undocked_stuff( objp, &Objects[aip->dock_objnum] );
9892         }
9893 }
9894
9895 /*
9896 //      Make dockee_objp shake a bit due to docking.
9897 void ai_dock_shake(object *docker_objp, object *dockee_objp)
9898 {
9899         vector  tangles;
9900         matrix  rotmat, tmp;
9901         float           scale;
9902         angles  *ap;
9903
9904         scale = 0.25f;          //      Compute this based on mass and speed at time of docking.
9905
9906         vm_vec_rand_vec_quick(&tangles);
9907         vm_vec_scale(&tangles, scale);
9908
9909         ap = (angles *) &tangles;
9910
9911         vm_angles_2_matrix(&rotmat, ap);
9912         vm_matrix_x_matrix( &tmp, &dockee_objp->orient, &rotmat );
9913         dockee_objp->orient = tmp;
9914
9915         vm_orthogonalize_matrix(&dockee_objp->orient);
9916
9917         dock_orient_and_approach(docker_objp, dockee_objp, DOA_DOCK_STAY);
9918
9919 }
9920 */
9921
9922 //      Make Pl_objp point at aip->goal_point.
9923 void ai_still()
9924 {
9925         ship    *shipp;
9926         ai_info *aip;
9927
9928         Assert(Pl_objp->type == OBJ_SHIP);
9929         Assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_OBJECTS));
9930
9931         shipp = &Ships[Pl_objp->instance];
9932         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
9933
9934         aip = &Ai_info[shipp->ai_index];
9935
9936         turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
9937 }
9938
9939 //      Make *Pl_objp stay near another ship.
9940 void ai_stay_near()
9941 {
9942         ai_info *aip;
9943         int             goal_objnum;
9944
9945         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
9946
9947         goal_objnum = aip->goal_objnum;
9948
9949         if ((goal_objnum < 0) || (Objects[goal_objnum].type != OBJ_SHIP) || (Objects[goal_objnum].signature != aip->goal_signature)) {
9950                 aip->mode = AIM_NONE;
9951         } else {
9952                 float           dist, max_dist, scale;
9953                 vector  rand_vec, goal_pos, vec_to_goal;
9954                 object  *goal_objp;
9955
9956                 goal_objp = &Objects[goal_objnum];
9957
9958                 //      Make not all ships pursue same point.
9959                 static_randvec(Pl_objp-Objects, &rand_vec);
9960
9961                 //      Make sure point is in front hemisphere (relative to Pl_objp's position.
9962                 vm_vec_sub(&vec_to_goal, &goal_objp->pos, &Pl_objp->pos);
9963                 if (vm_vec_dot(&rand_vec, &vec_to_goal) > 1.0f) {
9964                         vm_vec_negate(&rand_vec);
9965                 }
9966
9967                 //      Scale the random vector by an amount proportional to the distance from Pl_objp to the true goal.
9968                 dist = vm_vec_dist_quick(&goal_objp->pos, &Pl_objp->pos);
9969                 max_dist = aip->stay_near_distance;
9970                 scale = dist - max_dist/2;
9971                 if (scale < 0.0f)
9972                         scale = 0.0f;
9973
9974                 vm_vec_scale_add(&goal_pos, &goal_objp->pos, &rand_vec, scale);
9975
9976                 if (max_dist < Pl_objp->radius + goal_objp->radius + 25.0f)
9977                         max_dist = Pl_objp->radius + goal_objp->radius + 25.0f;
9978
9979                 if (dist > max_dist) {
9980                         turn_towards_point(Pl_objp, &goal_pos, NULL, 0.0f);
9981                         accelerate_ship(aip, dist / max_dist - 0.8f);
9982                 }
9983         
9984         }
9985
9986 }
9987
9988 //      Warn player if dock path is obstructed.
9989 int maybe_dock_obstructed(object *cur_objp, object *goal_objp, int big_only_flag)
9990 {
9991         vector  *goalpos, *curpos;
9992         float           radius;
9993         ai_info *aip;
9994         int             collide_objnum;
9995
9996         aip = &Ai_info[Ships[cur_objp->instance].ai_index];
9997
9998         Ai_info[Ships[goal_objp->instance].ai_index].ai_flags &= ~AIF_REPAIR_OBSTRUCTED;
9999
10000         if (goal_objp != Player_obj)
10001                 return -1;
10002
10003         curpos = &cur_objp->pos;
10004         radius = cur_objp->radius;
10005         goalpos = &Path_points[aip->path_cur].pos;
10006         collide_objnum = pp_collide_any(curpos, goalpos, radius, cur_objp, goal_objp, big_only_flag);
10007
10008         if (collide_objnum != -1)
10009                 Ai_info[Ships[goal_objp->instance].ai_index].ai_flags |= AIF_REPAIR_OBSTRUCTED;
10010
10011         return collide_objnum;
10012 }
10013
10014
10015 int Dock_path_warning_given = 0;
10016
10017 //      Docking behavior.
10018 //      Approach a ship, follow path to docking platform, approach platform, after awhile,
10019 //      undock.
10020 void ai_dock()
10021 {
10022         ship                    *shipp = &Ships[Pl_objp->instance];
10023         ai_info         *aip = &Ai_info[shipp->ai_index];
10024         object          *goal_objp;
10025         ship_info       *sip = &Ship_info[shipp->ship_info_index];
10026
10027         //      Make sure object we're supposed to dock with still exists.
10028         if ((aip->goal_objnum == -1) || (Objects[aip->goal_objnum].signature != aip->goal_signature)) {
10029                 ai_cleanup_dock_mode(aip, shipp);
10030                 return;
10031         }
10032
10033         goal_objp = &Objects[aip->goal_objnum];
10034
10035         //      For docking submodes (ie, not undocking), follow path.  Once at second last
10036         //      point on path (point just before point on dock platform), orient into position.
10037         // For undocking, first mode pushes docked ship straight back from docking point
10038         // second mode turns ship and moves to point on docking radius
10039         switch (aip->submode) {
10040
10041                 //      This mode means to find the path to the docking point.
10042         case AIS_DOCK_0:
10043                 //aip->path_start = -1;
10044                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10045                 ai_path();
10046                 if (!Dock_path_warning_given && (aip->path_length < 4)) {
10047                         Warning( LOCATION, "Ship '%s' has only %i points on dock path.  Docking will look strange.  Contact Adam.", shipp->ship_name, aip->path_length );
10048                         Dock_path_warning_given = 1;            //      This is on a mission-wide basis, but it's just a hack for now...
10049                 }
10050
10051                 aip->submode = AIS_DOCK_1;
10052                 aip->path_start = -1;
10053                 aip->submode_start_time = Missiontime;
10054                 break;
10055
10056                 //      This mode means to follow the path until just before the end.
10057         case AIS_DOCK_1: {
10058                 float   dist;
10059                 int     r;
10060
10061                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp, 1)) != -1) {
10062                         int     r1;
10063                         if ((r1 = maybe_avoid_big_ship(Pl_objp, goal_objp, aip, &goal_objp->pos, 7.0f)) != 0) {
10064                                 nprintf(("AI", "Support ship %s avoiding large ship %s\n", Ships[Pl_objp->instance].ship_name, Ships[Objects[r1].instance].ship_name));
10065                                 break;
10066                         } /*else {
10067                                 nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10068                                 accelerate_ship(aip, 0.0f);
10069                                 aip->submode = AIS_DOCK_0;
10070                         } */
10071                 } //else {
10072                 {
10073                         dist = ai_path();
10074                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10075                         //nprintf(("AI", "Dock 1: Frame: %i, goal point = %i, dist = %7.3f\n", Framecount, aip->path_cur-aip->path_start, dist));
10076
10077                         if (aip->path_cur-aip->path_start >= aip->path_length-1) {              //      If got this far, advance no matter what.
10078                                 aip->submode = AIS_DOCK_2;
10079                                 aip->submode_start_time = Missiontime;
10080                                 aip->path_cur--;
10081                                 Assert(aip->path_cur-aip->path_start >= 0);
10082                         } else if (aip->path_cur-aip->path_start >= aip->path_length-2) {
10083                                 if (Pl_objp->phys_info.speed > goal_objp->phys_info.speed + 1.5f) {
10084                                         set_accel_for_target_speed(Pl_objp, goal_objp->phys_info.speed);
10085                                 } else {
10086                                         aip->submode = AIS_DOCK_2;
10087                                         aip->submode_start_time = Missiontime;
10088                                 }
10089                         }
10090                 }
10091                 break;
10092                                           }
10093         //      This mode means to drag oneself right to the second last point on the path.
10094         //      Path code allows it to overshoot.
10095         case AIS_DOCK_2: {
10096                 float           dist;
10097                 int     r;
10098
10099                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10100                         nprintf(("AI", "Dock 2: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10101                         accelerate_ship(aip, 0.0f);
10102                         aip->submode = AIS_DOCK_1;
10103                 } else {
10104                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10105                         dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_APPROACH);
10106                         Assert(dist != UNINITIALIZED_VALUE);
10107
10108                         if (dist == DOCK_BACKUP_RETURN_VAL) {
10109                                 int path_num;
10110                                 aip->submode = AIS_DOCK_1;
10111                                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
10112                                 Assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
10113                                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
10114                                 break;
10115                         }
10116
10117                         //nprintf(("AI", "Dock 2: dist = %7.3f\n", vm_vec_dist_quick(&Pl_objp->pos, &goal_point)));
10118                         float   tolerance;
10119                         if (Objects[aip->goal_objnum].flags & OF_PLAYER_SHIP)
10120                                 tolerance = 6*flFrametime + 1.0f;
10121                         else
10122                                 tolerance = 4*flFrametime + 0.5f;
10123
10124                         if ( dist < tolerance) {
10125                                 aip->submode = AIS_DOCK_3;
10126                                 aip->submode_start_time = Missiontime;
10127                                 aip->path_cur++;
10128                         }
10129                 }
10130                 break;
10131                                                   }
10132
10133         case AIS_DOCK_3:
10134         case AIS_DOCK_3A:
10135                 {
10136                 Assert(aip->goal_objnum != -1);
10137                 int     r;
10138
10139                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10140                         nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10141                         accelerate_ship(aip, 0.0f);
10142                         aip->submode = AIS_DOCK_2;
10143                 } else {
10144
10145                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10146                         float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10147                         Assert(dist != UNINITIALIZED_VALUE);
10148
10149                         if (dist == DOCK_BACKUP_RETURN_VAL) {
10150                                 aip->submode = AIS_DOCK_2;
10151                                 break;
10152                         }
10153
10154                         //nprintf(("AI", "Dock 3: dist = %7.3f\n", dist));
10155
10156                         if (dist < 2*flFrametime * (1.0f + fl_sqrt(goal_objp->phys_info.speed))) {
10157                                 // - Removed by MK on 11/7/97, causes errors for ships docked at mission start: maybe_recreate_path(Pl_objp, aip, 1);
10158                                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10159                                 Assert(dist != UNINITIALIZED_VALUE);
10160
10161                                 physics_ship_init(Pl_objp);
10162
10163                                 ai_do_objects_docked_stuff( Pl_objp, goal_objp );
10164
10165                                 if (aip->submode == AIS_DOCK_3) {
10166                                         snd_play_3d( &Snds[SND_DOCK_ATTACH], &Pl_objp->pos, &View_position );
10167                                         hud_maybe_flash_docking_text(Pl_objp);
10168                                         // ai_dock_shake(Pl_objp, goal_objp);
10169
10170                                         if ((Pl_objp == Player_obj) || (goal_objp == Player_obj))
10171                                                 joy_ff_docked();  // shake player's joystick a little
10172                                 }
10173
10174                                 //      If this ship is repairing another ship...
10175                                 if (aip->ai_flags & AIF_REPAIRING) {
10176                                         aip->submode = AIS_DOCK_4;                      //      Special rearming only dock mode.
10177                                         aip->submode_start_time = Missiontime;
10178                                 } else {
10179                                         aip->submode = AIS_DOCK_4A;
10180                                         aip->submode_start_time = Missiontime;
10181                                 }
10182                         }
10183                 }
10184                 break;
10185                 }
10186
10187                 //      Yes, we just sit here.  We wait for further orders.  No, it's not a bug.
10188         case AIS_DOCK_4A:
10189                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10190                 //nprintf(("AI", "."));
10191                 if (aip->active_goal >= 0) {
10192                         mission_log_add_entry(LOG_SHIP_DOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10193
10194                         if (aip->goals[aip->active_goal].ai_mode == AI_GOAL_DOCK) {
10195                                 ai_mission_goal_complete( aip );                                        // Note, this calls ai_set_default_behavior().
10196                         } 
10197                 } else {        //      Can happen for initially docked ships.
10198                         ai_do_default_behavior( &Objects[Ships[aip->shipnum].objnum] );         // do the default behavior
10199                 }
10200                 
10201                 break;
10202
10203         case AIS_DOCK_4: {
10204                 //      This mode is only for rearming/repairing.
10205                 //      The ship that is performing the rearm enters this mode after it docks.
10206                 Assert((aip->goal_objnum >= -1) && (aip->goal_objnum < MAX_OBJECTS));
10207
10208                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10209                 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10210                 Assert(dist != UNINITIALIZED_VALUE);
10211
10212                 object  *goal_objp = &Objects[aip->goal_objnum];
10213                 Assert(goal_objp->type == OBJ_SHIP);
10214                 ship                    *goal_shipp = &Ships[goal_objp->instance];              
10215                 ai_info         *goal_aip = &Ai_info[goal_shipp->ai_index];
10216
10217                 //nprintf(("AI", "Dock 4: dist = %7.3f\n", dist));
10218
10219                 //      Make sure repair has not broken off.
10220                 if (dist > 5.0f) {      //      Oops, too far away!
10221                         if ( goal_aip->ai_flags & AIF_BEING_REPAIRED )
10222                                 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BROKEN);
10223
10224                         if (dist > Pl_objp->radius*2 + goal_objp->radius*2) {
10225                                 //      Got real far away from goal, so move back a couple modes and try again.
10226                                 aip->submode = AIS_DOCK_2;
10227                                 aip->submode_start_time = Missiontime;
10228                         }
10229                 } else {
10230                         if ( goal_aip->ai_flags & AIF_AWAITING_REPAIR )
10231                                 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BEGIN );
10232                 }
10233
10234                 break;
10235                                                   }
10236
10237         case AIS_UNDOCK_0: {
10238                 int path_num;
10239                 //      First stage of undocking.
10240
10241                 //nprintf(("AI", "Undock 0:\n"));
10242
10243                 aip->submode = AIS_UNDOCK_1;
10244                 aip->submode_start_time = Missiontime;
10245                 if (aip->dock_objnum == -1) {
10246                         aip->submode = AIS_UNDOCK_3;
10247                 } else {
10248
10249                         // set up the path points for the undocking procedure.  dock_path_index member should
10250                         // have gotten set in the docking code.
10251                         Assert( aip->dock_path_index != -1 );
10252                         path_num = ai_return_path_num_from_dockbay(goal_objp, aip->dock_path_index);
10253                         ai_find_path(Pl_objp, goal_objp-Objects, path_num, 0);
10254
10255                         // Play a ship docking detach sound
10256                         snd_play_3d( &Snds[SND_DOCK_DETACH], &Pl_objp->pos, &View_position );
10257                 }
10258                 break;
10259                                                          }
10260         case AIS_UNDOCK_1: {
10261                 //      Using thrusters, exit from dock station to nearest next dock path point.
10262                 float   dist;
10263                 
10264                 //nprintf(("AI", "Undock 1: time in this mode = %7.3f\n", f2fl(Missiontime - aip->submode_start_time)));
10265
10266                 if (Missiontime - aip->submode_start_time < REARM_BREAKOFF_DELAY) {
10267                         break;          //      Waiting for one second to elapse to let detach sound effect play out.
10268                 }
10269                 else {  // AL - added 05/16/97.  Hack to play depart sound.  Will probably take out.
10270                                         // Assumes that the submode_start_time is not used for AIS_UNDOCK_1 anymore
10271                         if ( aip->submode_start_time != 0 )
10272                                 snd_play_3d( &Snds[SND_DOCK_DEPART], &Pl_objp->pos, &View_position );
10273                         aip->submode_start_time = 0;
10274                 }
10275
10276                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_1);
10277                 Assert(dist != UNINITIALIZED_VALUE);
10278
10279                 float dist_to_dock_obj = vm_vec_dist_quick(&Pl_objp->pos, &Objects[aip->goal_objnum].pos);
10280
10281                 //      Move to within 0.1 units of second last point on path before orienting, or just plain far away from docked-to ship.
10282                 //      This allows undock to complete if first ship flies away.
10283                 if ((dist < 2*flFrametime) || (dist_to_dock_obj > 2*Pl_objp->radius)) {
10284                         aip->submode = AIS_UNDOCK_2;
10285                         aip->submode_start_time = Missiontime;
10286                 }
10287                 break;
10288                                                          }
10289         case AIS_UNDOCK_2: {
10290                 float dist;
10291                 ai_info *other_aip;
10292
10293                 // get pointer to docked object's aip to reset flags, etc
10294                 Assert( aip->dock_objnum != -1 );
10295                 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
10296
10297                 //      Second stage of undocking.
10298                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_2);
10299                 Assert(dist != UNINITIALIZED_VALUE);
10300
10301
10302                 //nprintf(("AI", "Undock 2: dist = %7.3f\n", dist));
10303                 
10304                 //      If at goal point, or quite far away from dock object
10305                 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) ) {
10306                         // reset the dock flags.  If rearm/repair, reset rearm repair flags for those ships as well.
10307                         if ( sip->flags & SIF_SUPPORT ) {
10308                                 ai_do_objects_repairing_stuff( &Objects[aip->dock_objnum], Pl_objp, REPAIR_INFO_END );
10309                         }
10310
10311                         // clear out flags for AIF_DOCKED for both objects.
10312                         ai_do_objects_undocked_stuff( Pl_objp, goal_objp );
10313                         physics_ship_init(Pl_objp);
10314                         aip->submode = AIS_UNDOCK_3;                            //      The do-nothing mode, until another order is issued
10315
10316                         //aip->ai_flags &= ~AIF_DOCKED;         //      @MK, 9/18/97
10317                         //other_aip->ai_flags &= ~AIF_DOCKED;
10318                         //aip->dock_objnum = -1;                                        // invalidate who obj is docked with
10319                         //other_aip->dock_objnum = -1;                  // MWA 10/07/97 invalide docked objects dock_objnum value as well
10320
10321                         // don't add undock log entries for support ships.
10322                         if ( !(sip->flags & SIF_SUPPORT) )
10323                                 mission_log_add_entry(LOG_SHIP_UNDOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10324
10325                 }
10326                 break;
10327                 }
10328         case AIS_UNDOCK_3: {
10329                 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_3);
10330                 Assert(dist != UNINITIALIZED_VALUE);
10331
10332                 if (dist < Pl_objp->radius/2 + 5.0f) {
10333                         aip->submode = AIS_UNDOCK_4;
10334                 }
10335
10336                 // possible that this flag hasn't been cleared yet.  When aborting a rearm, this submode might
10337                 // be entered directly.
10338                 if ( (sip->flags & SIF_SUPPORT) && (aip->ai_flags & AIF_REPAIRING) ) {
10339                         ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], Pl_objp, REPAIR_INFO_ABORT );
10340                 }
10341
10342                 break;
10343                                                  }
10344         case AIS_UNDOCK_4: {
10345                 ai_info *other_aip;
10346
10347                 // MWA 10/07/97  I'm slightly confused by the dual use of goal_objnum and dock_objnum.  Seems to me
10348                 // that goal_objnum and dock_objnum are the same through this whole docking/undocking process, although
10349                 // I could be wrong.  dock_objnum was reset in undock_2 submode so try to use goal_objnum here to
10350                 // get other ships ai_info pointer
10351                 Assert( aip->goal_objnum != -1 );
10352                 other_aip = &Ai_info[Ships[Objects[aip->goal_objnum].instance].ai_index];
10353
10354                 aip->mode = AIM_NONE;
10355                 aip->dock_path_index = -1;              // invalidate the docking path index
10356
10357                 // these flags should have been cleared long ago!
10358                 // Get Allender if you hit one of these!!!!!
10359                 // removed by allender on 2/16 since a ship may be docked with some other ship, but still be the
10360                 // goal_objnum of this ship ending it's undocking mode.
10361                 //Assert( !(aip->ai_flags & AIF_DOCKED) );
10362                 //Assert( !(other_aip->ai_flags & AIF_DOCKED) );
10363                 //Assert( !(aip->ai_flags & AIF_REPAIRING) );
10364                 //Assert( !(other_aip->ai_flags & AIF_BEING_REPAIRED) );
10365                 //Assert( !(other_aip->ai_flags & AIF_AWAITING_REPAIR) );
10366
10367                 // only call mission goal complete if this was indeed an undock goal
10368                 if ( aip->active_goal > -1 ) {
10369                         if ( aip->goals[aip->active_goal].ai_mode == AI_GOAL_UNDOCK )
10370                                 ai_mission_goal_complete( aip );                        // this call should reset the AI mode
10371                         //else
10372                         //      aip->active_goal = -1;                                          // this ensures that this ship might get new goal
10373                 }
10374
10375                 break;
10376                                                          }
10377         default:
10378                 Int3(); //      Error, bogus submode
10379         }
10380
10381 }
10382
10383 // TURRET BEGIN
10384
10385 //      Given an object and a turret on that object, return the global position and forward vector
10386 //      of the turret.   The gun normal is the unrotated gun normal, (the center of the FOV cone), not
10387 // the actual gun normal given using the current turret heading.  But it _is_ rotated into the model's orientation
10388 //      in global space.
10389 void ship_get_global_turret_info(object *objp, model_subsystem *tp, vector *gpos, vector *gvec)
10390 {
10391         matrix  m;
10392         vm_copy_transpose_matrix(&m, &objp->orient);
10393 //      vm_vec_rotate(gpos, &tp->turret_avg_firing_point, &m);
10394         vm_vec_rotate(gpos, &tp->pnt, &m);
10395         vm_vec_add2(gpos, &objp->pos);
10396         vm_vec_rotate(gvec, &tp->turret_norm, &m);      
10397 }
10398
10399 // Given an object and a turret on that object, return the actual firing point of the gun
10400 // and its normal.   This uses the current turret angles.  We are keeping track of which
10401 // gun to fire next in the ship specific info for this turret subobject.  Use this info
10402 // to determine which position to fire from next.
10403 //      Stuffs:
10404 //              *gpos: absolute position of gun firing point
10405 //              *gvec: vector fro *gpos to *targetp
10406 void ship_get_global_turret_gun_info(object *objp, ship_subsys *ssp, vector *gpos, vector *gvec, int use_angles, vector *targetp)
10407 {
10408         vector * gun_pos;
10409         model_subsystem *tp = ssp->system_info;
10410
10411         ship_model_start(objp);
10412
10413         gun_pos = &tp->turret_firing_point[ssp->turret_next_fire_pos % tp->turret_num_firing_points];
10414
10415         model_find_world_point(gpos, gun_pos, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10416
10417         if (use_angles)
10418                 model_find_world_dir(gvec, &tp->turret_norm, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10419         else {
10420                 //vector        gun_pos2;
10421                 //vm_vec_add(&gun_pos2, gpos, gun_pos);
10422                 vm_vec_normalized_dir(gvec, targetp, gpos);
10423         }
10424
10425         ship_model_stop(objp);  
10426 }
10427
10428 //      Rotate a turret towards an enemy.
10429 //      Return TRUE if caller should use angles in subsequent rotations.
10430 //      Some obscure model thing only John Slagel knows about.
10431 //      Sets predicted enemy position.
10432 //      If the turret (*ss) has a subsystem targeted, the subsystem is used as the predicted point.
10433 int aifft_rotate_turret(ship *shipp, int parent_objnum, ship_subsys *ss, object *objp, object *lep, vector *predicted_enemy_pos, vector *gvec)
10434 {
10435         if (ss->turret_enemy_objnum != -1)      {
10436                 model_subsystem *tp = ss->system_info;
10437                 vector  gun_pos, gun_vec;
10438                 float           weapon_speed;
10439                 float           weapon_system_strength;
10440
10441                 //      weapon_system_strength scales time enemy in range in 0..1.  So, the lower this is, the worse the aiming will be.
10442                 weapon_system_strength = ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS);
10443
10444                 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gun_pos, &gun_vec);
10445
10446                 weapon_speed = Weapon_info[tp->turret_weapon_type].max_speed;
10447                 float weapon_travel_dist = weapon_speed * Weapon_info[tp->turret_weapon_type].lifetime;
10448
10449                 vector  enemy_point;
10450                 if (ss->targeted_subsys != NULL) {
10451                         if (ss->turret_enemy_objnum != -1) {
10452                                 vm_vec_unrotate(&enemy_point, &ss->targeted_subsys->system_info->pnt, &Objects[ss->turret_enemy_objnum].orient);
10453                                 vm_vec_add2(&enemy_point, &Objects[ss->turret_enemy_objnum].pos);
10454                         }
10455                 } else {
10456                         if ((lep->type == OBJ_SHIP) && (Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
10457                                 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));
10458                         } else {
10459                                 enemy_point = lep->pos;
10460                         }
10461                 }
10462
10463                 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);
10464
10465                 if (weapon_system_strength < 0.7f) {
10466                         vector  rand_vec;
10467
10468                         static_randvec(Missiontime >> 18, &rand_vec);   //      Return same random number for two seconds.
10469                         //      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.
10470                         vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, (1.0f - weapon_system_strength)*1.5f * lep->radius);
10471                 }
10472
10473                 vector  v2e;
10474                 vm_vec_normalized_dir(&v2e, predicted_enemy_pos, &gun_pos);
10475                 if (vm_vec_dot(&v2e, gvec) > tp->turret_fov) {
10476                         int     rval;
10477
10478                         rval = model_rotate_gun(shipp->modelnum, ss->system_info, &Objects[parent_objnum].orient, 
10479                                 &ss->submodel_info_1.angs, &ss->submodel_info_2.angs,
10480                                 &Objects[parent_objnum].pos, predicted_enemy_pos);
10481                 }
10482         }
10483
10484         return 0;
10485 }
10486
10487 //      Determine if subsystem *enemy_subsysp is hittable from objp.
10488 //      If so, return dot product of vector from point abs_gunposp to *enemy_subsysp
10489 float   aifft_compute_turret_dot(object *objp, object *enemy_objp, vector *abs_gunposp, ship_subsys *turret_subsysp, ship_subsys *enemy_subsysp)
10490 {
10491         float   dot_out;
10492         vector  subobj_pos, vector_out;
10493
10494         vm_vec_unrotate(&subobj_pos, &enemy_subsysp->system_info->pnt, &enemy_objp->orient);
10495         vm_vec_add2(&subobj_pos, &enemy_objp->pos);
10496
10497         if (ship_subsystem_in_sight(enemy_objp, enemy_subsysp, abs_gunposp, &subobj_pos, 1, &dot_out, &vector_out)) {
10498                 vector  turret_norm;
10499
10500                 vm_vec_rotate(&turret_norm, &turret_subsysp->system_info->turret_norm, &objp->orient);
10501                 return vm_vec_dot(&turret_norm, &vector_out);
10502         } else
10503                 return -1.0f;
10504
10505 }
10506
10507 #define MAX_AIFFT_TURRETS                       60
10508 ship_subsys *aifft_list[MAX_AIFFT_TURRETS];
10509 float aifft_rank[MAX_AIFFT_TURRETS];
10510 int aifft_list_size = 0;
10511 int aifft_max_checks = 5;
10512 DCF(mf, "")
10513 {
10514         dc_get_arg(ARG_INT);
10515         aifft_max_checks = Dc_arg_int;
10516 }
10517
10518
10519 //      Pick a subsystem to attack on enemy_objp.
10520 //      Only pick one if enemy_objp is a big ship or a capital ship.
10521 //      Returns dot product from turret to subsystem in *dot_out
10522 ship_subsys *aifft_find_turret_subsys(object *objp, ship_subsys *ssp, object *enemy_objp, float *dot_out)
10523 {
10524         ship    *eshipp, *shipp;
10525         ship_info       *esip;
10526         ship_subsys     *best_subsysp = NULL;
10527         float dot;
10528
10529         Assert(enemy_objp->type == OBJ_SHIP);
10530
10531         eshipp = &Ships[enemy_objp->instance];
10532         esip = &Ship_info[eshipp->ship_info_index];
10533
10534         shipp = &Ships[objp->instance];
10535
10536         float   best_dot = 0.0f;
10537         *dot_out = best_dot;
10538
10539         //      Compute absolute gun position.
10540         vector  abs_gun_pos;
10541         vm_vec_unrotate(&abs_gun_pos, &ssp->system_info->pnt, &objp->orient);
10542         vm_vec_add2(&abs_gun_pos, &objp->pos);
10543
10544         //      Only pick a turret to attack on large ships.
10545         if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
10546                 return best_subsysp;
10547
10548         // Make sure big or huge ship *actually* has subsystems  (ie, knossos)
10549         if (esip->n_subsystems == 0) {
10550                 return best_subsysp;
10551         }
10552
10553         // first build up a list subsystems to traverse
10554         ship_subsys     *pss;
10555         aifft_list_size = 0;
10556         for ( pss = GET_FIRST(&eshipp->subsys_list); pss !=END_OF_LIST(&eshipp->subsys_list); pss = GET_NEXT(pss) ) {
10557                 model_subsystem *psub = pss->system_info;
10558
10559                 // if we've reached max turrets bail
10560                 if(aifft_list_size >= MAX_AIFFT_TURRETS){
10561                         break;
10562                 }
10563
10564                 // Don't process destroyed objects
10565                 if ( pss->current_hits <= 0.0f ){
10566                         continue;
10567                 }
10568                 
10569                 switch (psub->type) {
10570                 case SUBSYSTEM_WEAPONS:
10571                         aifft_list[aifft_list_size] = pss;
10572                         aifft_rank[aifft_list_size++] = 1.4f;
10573                         break;
10574
10575                 case SUBSYSTEM_TURRET:
10576                         aifft_list[aifft_list_size] = pss;
10577                         aifft_rank[aifft_list_size++] = 1.2f;
10578                         break;
10579
10580                 case SUBSYSTEM_SENSORS:
10581                 case SUBSYSTEM_ENGINE:
10582                         aifft_list[aifft_list_size] = pss;
10583                         aifft_rank[aifft_list_size++] = 1.0f;
10584                         break;
10585                 }
10586         }
10587
10588         // DKA:  6/28/99 all subsystems can be destroyed.
10589         //Assert(aifft_list_size > 0);
10590         if (aifft_list_size == 0) {
10591                 return best_subsysp;
10592         }
10593
10594         // determine a stride value so we're not checking too many turrets
10595         int stride = aifft_list_size > aifft_max_checks ? aifft_list_size / aifft_max_checks : 1;
10596         if(stride <= 0){
10597                 stride = 1;
10598         }
10599         int offset = (int)frand_range(0.0f, (float)(aifft_list_size % stride));
10600         int idx;
10601         for(idx=offset; idx<aifft_list_size; idx+=stride){
10602                 dot = aifft_compute_turret_dot(objp, enemy_objp, &abs_gun_pos, ssp, aifft_list[idx]);                   
10603
10604                 if (dot* aifft_rank[idx] > best_dot) {
10605                         best_dot = dot*aifft_rank[idx];
10606                         best_subsysp = aifft_list[idx];
10607                 }
10608         }
10609
10610         Assert(best_subsysp != &eshipp->subsys_list);
10611
10612         *dot_out = best_dot;
10613         return best_subsysp;
10614 }
10615
10616 // Set active weapon for turret
10617 void ai_turret_select_default_weapon(ship_subsys *turret)
10618 {
10619         ship_weapon *twp;
10620
10621         twp = &turret->weapons;
10622
10623         // If a primary weapon is available, select it
10624         if ( twp->num_primary_banks > 0 ) {
10625                 turret->system_info->turret_weapon_type = twp->primary_bank_weapons[0];
10626         } else if ( twp->num_secondary_banks > 0 ) {
10627                 turret->system_info->turret_weapon_type = twp->secondary_bank_weapons[0];
10628         }
10629 }
10630
10631 // return !0 if the specified target should scan for a new target, otherwise return 0
10632 int turret_should_pick_new_target(ship_subsys *turret)
10633 {
10634 //      int target_type;
10635
10636         if ( timestamp_elapsed(turret->turret_next_enemy_check_stamp) ) {
10637                 return 1;
10638         }
10639
10640         return 0;
10641
10642 /*
10643         if ( turret->turret_enemy_objnum == -1 ) {
10644                 return 1;
10645         }
10646                 
10647         target_type = Objects[turret->turret_enemy_objnum].type;
10648         if ( (target_type != OBJ_SHIP) && (target_type != OBJ_ASTEROID) ) {
10649                 return 1;
10650         }
10651
10652         return 0;
10653 */
10654 }
10655
10656 // Set the next fire timestamp for a turret, based on weapon type and ai class
10657 void turret_set_next_fire_timestamp(ship_subsys *turret, ai_info *aip)
10658 {
10659         float   wait;
10660         int     weapon_id;
10661
10662         weapon_id = turret->system_info->turret_weapon_type;
10663
10664         wait = Weapon_info[weapon_id].fire_wait * 1000.0f;
10665
10666         // make side even for team vs. team
10667         if ((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) {
10668                 // flak guns need to fire more rapidly
10669                 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10670                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10671                         wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10672                 } else {
10673                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10674                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10675                 }
10676         } else {
10677                 // flak guns need to fire more rapidly
10678                 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10679                         if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10680                                 wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10681                         } else {
10682                                 wait *= Ship_fire_delay_scale_hostile[Game_skill_level] * 0.5f;
10683                         }       
10684                         wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10685
10686                 } else if (Weapon_info[weapon_id].wi_flags & WIF_HUGE) {
10687                         // make huge weapons fire independently of team
10688                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10689                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10690                 } else {
10691                         // give team friendly an advantage
10692                         if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10693                                 wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10694                         } else {
10695                                 wait *= Ship_fire_delay_scale_hostile[Game_skill_level];
10696                         }       
10697                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10698                 }
10699         }
10700
10701         // vary wait time +/- 10%
10702         wait *= frand_range(0.9f, 1.1f);
10703         turret->turret_next_fire_stamp = timestamp((int) wait);
10704 }
10705
10706 // Decide  if a turret should launch an aspect seeking missile
10707 int turret_should_fire_aspect(ship_subsys *turret, float dot, int weapon_class)
10708 {
10709         weapon_info *wip;
10710
10711         wip = &Weapon_info[weapon_class];
10712
10713         if ( (dot > AICODE_TURRET_DUMBFIRE_ANGLE) && (turret->turret_time_enemy_in_range >= min(wip->min_lock_time,AICODE_TURRET_MAX_TIME_IN_RANGE)) ) {
10714                 return 1;
10715         }
10716
10717         return 0;
10718 }
10719
10720 // Update how long current target has been in this turrets range
10721 void turret_update_enemy_in_range(ship_subsys *turret, float seconds)
10722 {
10723         turret->turret_time_enemy_in_range += seconds;
10724
10725         if ( turret->turret_time_enemy_in_range < 0.0f ) {
10726                 turret->turret_time_enemy_in_range = 0.0f;
10727         }
10728
10729         if ( turret->turret_time_enemy_in_range > AICODE_TURRET_MAX_TIME_IN_RANGE ) {
10730                 turret->turret_time_enemy_in_range = AICODE_TURRET_MAX_TIME_IN_RANGE;
10731         }
10732 }
10733
10734
10735
10736 // Fire a weapon from a turret
10737 void turret_fire_weapon(ship_subsys *turret, int parent_objnum, vector *turret_pos, vector *turret_fvec, vector *predicted_pos = NULL)
10738 {
10739         matrix  turret_orient;
10740         int             turret_weapon_class, weapon_objnum;
10741         ai_info *parent_aip;
10742         ship            *parent_ship;
10743         beam_fire_info fire_info;
10744         float flak_range = 0.0f;
10745
10746         parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
10747         parent_ship = &Ships[Objects[parent_objnum].instance];
10748         turret_weapon_class = turret->system_info->turret_weapon_type;
10749
10750         if (check_ok_to_fire(parent_objnum, turret->turret_enemy_objnum, &Weapon_info[turret_weapon_class])) {
10751                 vm_vector_2_matrix(&turret_orient, turret_fvec, NULL, NULL);
10752                 turret->turret_last_fire_direction = *turret_fvec;
10753
10754                 // set next fire timestamp for the turret
10755                 turret_set_next_fire_timestamp(turret, parent_aip);
10756
10757                 // if this weapon is a beam weapon, handle it specially
10758                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM){
10759                         // if this beam isn't free to fire
10760                         if (!(turret->weapons.flags & SW_FLAG_BEAM_FREE)) {
10761                                 Int3(); // should never get this far
10762                                 return;
10763                         }
10764
10765                         // stuff beam firing info
10766                         memset(&fire_info, 0, sizeof(beam_fire_info));
10767                         fire_info.accuracy = 1.0f;
10768                         fire_info.beam_info_index = turret_weapon_class;
10769                         fire_info.beam_info_override = NULL;
10770                         fire_info.shooter = &Objects[parent_objnum];
10771                         fire_info.target = &Objects[turret->turret_enemy_objnum];
10772                         fire_info.target_subsys = NULL;
10773                         fire_info.turret = turret;
10774
10775                         // fire a beam weapon
10776                         beam_fire(&fire_info);
10777                 } else {
10778
10779                         // don't fire swarm, but set up swarm info
10780                         if (Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM) {
10781                                 turret_swarm_set_up_info(parent_objnum, turret, turret_weapon_class);
10782                                 return;
10783                         } else {
10784                                 weapon_objnum = weapon_create( turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10785                                 weapon_set_tracking_info(weapon_objnum, parent_objnum, turret->turret_enemy_objnum, 1, turret->targeted_subsys);                
10786                         }
10787
10788                         //nprintf(("AI", "Turret_time_enemy_in_range = %7.3f\n", ss->turret_time_enemy_in_range));              
10789                         if (weapon_objnum != -1) {
10790                                 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10791                                 // AL 1-6-97: Store pointer to turret subsystem
10792                                 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10793
10794                                 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10795                                         // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10796                                         if ( parent_objnum != OBJ_INDEX(Player_obj) ) {                                         
10797                                                 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], turret_pos, &View_position );                                          
10798                                         }
10799                                 }               
10800
10801                                 // if the gun is a flak gun
10802                                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){                       
10803                                         // show a muzzle flash
10804                                         flak_muzzle_flash(turret_pos, turret_fvec, turret_weapon_class);
10805
10806                                         // pick a firing range so that it detonates properly                    
10807                                         flak_pick_range(&Objects[weapon_objnum], predicted_pos, ship_get_subsystem_strength(parent_ship, SUBSYSTEM_WEAPONS));
10808
10809                                         // determine what that range was
10810                                         flak_range = flak_get_range(&Objects[weapon_objnum]);
10811                                 }
10812
10813                                 // in multiplayer (and the master), then send a turret fired packet.
10814                                 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10815                                         int subsys_index;
10816
10817                                         subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10818                                         Assert( subsys_index != -1 );
10819                                         if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){                       
10820                                                 send_flak_fired_packet( parent_objnum, subsys_index, weapon_objnum, flak_range );
10821                                         } else {
10822                                                 send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10823                                         }
10824                                 }
10825                         }
10826                 }
10827         } else {
10828                 float wait = 1000.0f * frand_range(0.9f, 1.1f);
10829                 turret->turret_next_fire_stamp = timestamp((int) wait);
10830         }
10831 }
10832
10833 void turret_swarm_fire_from_turret(ship_subsys *turret, int parent_objnum, int target_objnum, ship_subsys *target_subsys)
10834 {
10835         int turret_weapon_class, weapon_objnum;
10836         matrix turret_orient;
10837         vector turret_pos, turret_fvec;
10838
10839         // parent not alive, quick out.
10840         if (Objects[parent_objnum].type != OBJ_SHIP) {
10841                 return;
10842         }
10843
10844         //      change firing point
10845         ship_get_global_turret_gun_info(&Objects[parent_objnum], turret, &turret_pos, &turret_fvec, 1, NULL);
10846         turret->turret_next_fire_pos++;
10847
10848         // get class [index into Weapon_info array
10849         turret_weapon_class = turret->system_info->turret_weapon_type;
10850         Assert(Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM);
10851
10852         // make turret_orient from turret_fvec -- turret->turret_last_fire_direction
10853         vm_vector_2_matrix(&turret_orient, &turret_fvec, NULL, NULL);
10854
10855         // create weapon and homing info
10856         weapon_objnum = weapon_create(&turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10857         weapon_set_tracking_info(weapon_objnum, parent_objnum, target_objnum, 1, target_subsys);
10858
10859         // do other cool stuff if weapon is created.
10860         if (weapon_objnum > -1) {
10861                 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10862                 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10863
10864                 // maybe sound
10865                 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10866                         // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10867                         if ( parent_objnum != OBJ_INDEX(Player_obj) ) {
10868                                 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], &turret_pos, &View_position );
10869                         }
10870                 }
10871                 
10872                 // in multiplayer (and the master), then send a turret fired packet.
10873                 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10874                         int subsys_index;
10875
10876                         subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10877                         Assert( subsys_index != -1 );
10878                         send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10879                 }
10880         }
10881 }
10882
10883 int Num_ai_firing = 0;
10884 int Num_find_turret_enemy = 0;
10885 int Num_turrets_fired = 0;
10886 //      Given a turret tp and its parent parent_objnum, fire from the turret at its enemy.
10887 void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
10888 {
10889         float           weapon_firing_range;
10890         vector  v2e;
10891         object  *lep;           //      Last enemy pointer
10892         model_subsystem *tp = ss->system_info;
10893         int             use_angles, turret_weapon_class;
10894         vector  predicted_enemy_pos;
10895         object  *objp;
10896         ai_info *aip;
10897
10898         if (!Ai_firing_enabled) {
10899                 return;
10900         }
10901
10902         if (ss->current_hits < 0.0f) {
10903                 return;
10904         }
10905
10906         if ( ship_subsys_disrupted(ss) ){               // AL 1/19/98: Make sure turret isn't suffering disruption effects
10907                 return;
10908         }
10909
10910         // Check turret free
10911         if (ss->weapons.flags & SW_FLAG_TURRET_LOCK) {
10912                 return;
10913         }
10914
10915         // If beam weapon, check beam free
10916         if ( (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) && !(ss->weapons.flags & SW_FLAG_BEAM_FREE) ) {
10917                 return;
10918         }
10919
10920         Assert( shipp->objnum == parent_objnum );
10921
10922         if ( tp->turret_weapon_type < 0 ){
10923                 return;
10924         }
10925
10926         // Monitor number of calls to ai_fire_from_turret
10927         Num_ai_firing++;
10928
10929         turret_weapon_class = tp->turret_weapon_type;
10930
10931         // AL 09/14/97: ensure ss->turret_enemy_objnum != -1 before setting lep
10932         if ( (ss->turret_enemy_objnum >= 0 && ss->turret_enemy_objnum < MAX_OBJECTS) && (ss->turret_enemy_sig == Objects[ss->turret_enemy_objnum].signature)) {
10933                 lep = &Objects[ss->turret_enemy_objnum];
10934
10935                 // MK -- here is where turret is targeting a bomb.  I simply return for now.  We should force
10936                 // a target change -- or better yet, never pick a weapon when this turret has a "huge" weapon
10937                 // loaded.
10938
10939                 // we only care about targets which are ships.
10940                 //if ( lep->type != OBJ_SHIP )
10941                 //      return;
10942
10943                 //      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.
10944                 if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HUGE ) {
10945                         if ( lep->type != OBJ_SHIP ) {
10946                                 return;
10947                         }
10948                         if ( !(Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
10949                                 return;
10950                         }
10951                 }
10952
10953                 // If targeting protected or beam protected ship, don't fire.  Reset enemy objnum
10954                 if (lep->type == OBJ_SHIP) {
10955                         // Check if we're targeting a protected ship
10956                         if (lep->flags & OF_PROTECTED) {
10957                                 ss->turret_enemy_objnum = -1;
10958                                 ss->turret_time_enemy_in_range = 0.0f;
10959                                 return;
10960                         }
10961
10962                         // Check if we're targeting a beam protected ship with a beam weapon
10963                         if ( (lep->flags & OF_BEAM_PROTECTED) && (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) ) {
10964                                 ss->turret_enemy_objnum = -1;
10965                                 ss->turret_time_enemy_in_range = 0.0f;
10966                                 return;
10967                         }
10968                 }
10969         } else {
10970                 ss->turret_enemy_objnum = -1;
10971                 lep = NULL;
10972         }
10973         
10974         Assert((parent_objnum >= 0) && (parent_objnum < MAX_OBJECTS));
10975         objp = &Objects[parent_objnum];
10976         Assert(objp->type == OBJ_SHIP);
10977         aip = &Ai_info[Ships[objp->instance].ai_index];
10978
10979         // Use the turret info for all guns, not one gun in particular.
10980         vector   gvec, gpos;
10981         ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
10982
10983         // Rotate the turret even if time hasn't elapsed, since it needs to turn to face its target.
10984         use_angles = aifft_rotate_turret(shipp, parent_objnum, ss, objp, lep, &predicted_enemy_pos, &gvec);
10985
10986         if ( !timestamp_elapsed(ss->turret_next_fire_stamp)){
10987                 return;
10988         }
10989
10990         // Don't try to fire beyond weapon_limit_range
10991         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);
10992
10993         // if beam weapon in nebula and target not tagged, decrase firing range
10994         extern int Nebula_sec_range;
10995         if (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) {
10996                 if ( !((shipp->tag_left > 0) || (shipp->level2_tag_left > 0)) ) {
10997                         if (Nebula_sec_range) {
10998                                 weapon_firing_range *= float(BEAM_NEBULA_RANGE_REDUCE_FACTOR);
10999                         }
11000                 }
11001         }
11002
11003         if (ss->turret_enemy_objnum != -1) {
11004                 float dist_to_enemy = vm_vec_normalized_dir(&v2e, &predicted_enemy_pos, &gpos) - lep->radius;
11005                 if (dist_to_enemy > weapon_firing_range) {
11006                         ss->turret_enemy_objnum = -1;           //      Force picking of new enemy.
11007                 }
11008         }
11009
11010         // Turret spawn weapons are a special case.  They fire if there are enough enemies in the 
11011         // immediate area (not necessarily in the turret fov).
11012         if ( Weapon_info[turret_weapon_class].wi_flags & WIF_SPAWN ) {
11013                 int num_ships_nearby;
11014                 num_ships_nearby = num_nearby_fighters(get_enemy_team_mask(parent_objnum), &gpos, 1500.0f);
11015                 if (( num_ships_nearby >= 3 ) || ((num_ships_nearby >= 2) && (frand() < 0.1f))) {
11016                         turret_fire_weapon(ss, parent_objnum, &gpos, &ss->turret_last_fire_direction);
11017                 } else {
11018                         ss->turret_next_fire_stamp = timestamp(1000);   //      Regardless of firing rate, don't check whether should fire for awhile.
11019                 }
11020                 return;
11021         }
11022
11023         //      Maybe pick a new enemy.
11024         if ( turret_should_pick_new_target(ss) ) {
11025                 Num_find_turret_enemy++;
11026                 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);
11027                 Assert(objnum < 0 || is_target_beam_valid(ss, objnum));
11028
11029                 if (objnum != -1) {
11030                         if (ss->turret_enemy_objnum == -1) {
11031                                 ss->turret_enemy_objnum = objnum;
11032                                 ss->turret_enemy_sig = Objects[objnum].signature;
11033                                 // why return?
11034                                 return;
11035                         } else {
11036                                 ss->turret_enemy_objnum = objnum;
11037                                 ss->turret_enemy_sig = Objects[objnum].signature;
11038                         }
11039                 } else {
11040                         ss->turret_enemy_objnum = -1;
11041                 }
11042
11043                 if (ss->turret_enemy_objnum != -1) {
11044                         float   dot = 1.0f;
11045                         lep = &Objects[ss->turret_enemy_objnum];
11046                         if ( lep->type == OBJ_SHIP ) {
11047                                 ss->targeted_subsys = aifft_find_turret_subsys(objp, ss, lep, &dot);                            
11048                         }
11049                         ss->turret_next_enemy_check_stamp = timestamp((int) (max(dot, 0.5f)*2000.0f) + 1000);
11050                 } else {
11051                         ss->turret_next_enemy_check_stamp = timestamp((int) (2000.0f * frand_range(0.9f, 1.1f)));       //      Check every two seconds
11052                 }
11053         }
11054
11055         //      If still don't have an enemy, return.  Or, if enemy is protected, return.
11056         if (ss->turret_enemy_objnum != -1) {
11057                 //      Don't shoot at ship we're going to dock with.
11058                 if (ss->turret_enemy_objnum == aip->dock_objnum) {
11059                         ss->turret_enemy_objnum = -1;
11060                         return;
11061                 }
11062
11063                 if (Objects[ss->turret_enemy_objnum].flags & OF_PROTECTED) {
11064                         //      This can happen if the enemy was selected before it became protected.
11065                         ss->turret_enemy_objnum = -1;
11066                         return;
11067                 }
11068                 lep = &Objects[ss->turret_enemy_objnum];
11069         } else {
11070                 if (timestamp_until(ss->turret_next_fire_stamp) < 500) {
11071                         ss->turret_next_fire_stamp = timestamp(500);
11072                 }
11073                 return;
11074         }
11075
11076         if ( lep == NULL ){
11077                 return;
11078         }
11079
11080         Assert(ss->turret_enemy_objnum != -1);
11081
11082         float dot = vm_vec_dot(&v2e, &gvec);
11083
11084         if (dot > tp->turret_fov ) {
11085                 // Ok, the turret is lined up... now line up a particular gun.
11086                 int ok_to_fire = 0;
11087                 float dist_to_enemy;
11088
11089                 // We're ready to fire... now get down to specifics, like where is the
11090                 // actual gun point and normal, not just the one for whole turret.
11091                 ship_get_global_turret_gun_info(&Objects[parent_objnum], ss, &gpos, &gvec, use_angles, &predicted_enemy_pos);
11092                 ss->turret_next_fire_pos++;
11093
11094                 // Fire in the direction the turret is facing, not right at the target regardless of turret dir.
11095                 vm_vec_sub(&v2e, &predicted_enemy_pos, &gpos);
11096                 dist_to_enemy = vm_vec_normalize(&v2e);
11097                 dot = vm_vec_dot(&v2e, &gvec);
11098
11099                 // 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
11100                 // and make them less lethal
11101                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){
11102                         flak_jitter_aim(&v2e, dist_to_enemy, ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS));
11103                 }
11104
11105                 // Fire if:
11106                 //              dumbfire and nearly pointing at target.
11107                 //              heat seeking and target in a fairly wide cone.
11108                 //              aspect seeking and target is locked.
11109                 turret_weapon_class = tp->turret_weapon_type;
11110
11111                 // if dumbfire (lasers and non-homing missiles)
11112                 if ( !(Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING) ) {
11113                         if ((dist_to_enemy < 75.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11114                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11115                                 ok_to_fire = 1;
11116                         }
11117                 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_HEAT ) {     // if heat seekers
11118                         if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_HEATSEEK_ANGLE )) {
11119                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11120                                 ok_to_fire = 1;
11121                         }
11122                 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_ASPECT ) {   // if aspect seeker
11123                         if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11124                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11125                         }
11126                         if ( turret_should_fire_aspect(ss, dot, turret_weapon_class) ) {
11127                                 ok_to_fire = 1;
11128                         }
11129                 }
11130
11131                 if ( ok_to_fire ) {
11132                         Num_turrets_fired++;
11133                         
11134                         turret_fire_weapon(ss, parent_objnum, &gpos, &v2e, &predicted_enemy_pos);                                               
11135                 } else {
11136                         turret_update_enemy_in_range(ss, -4*Weapon_info[tp->turret_weapon_type].fire_wait);
11137                         ss->turret_next_fire_stamp = timestamp(500);
11138                 }
11139         } else {
11140                 // Lost him!
11141                 ss->turret_enemy_objnum = -1;           //      Reset enemy objnum, find a new one next frame.
11142                 ss->turret_time_enemy_in_range = 0.0f;
11143         }
11144 }
11145
11146 // TURRET END
11147
11148 #ifndef NDEBUG
11149 #define MAX_AI_DEBUG_RENDER_STUFF       100
11150 typedef struct ai_render_stuff {
11151         ship_subsys     *ss;
11152         int                     parent_objnum;
11153 } ai_render_stuff;
11154
11155 ai_render_stuff AI_debug_render_stuff[MAX_AI_DEBUG_RENDER_STUFF];
11156
11157 int     Num_AI_debug_render_stuff = 0;
11158
11159 void ai_debug_render_stuff()
11160 {
11161         vertex  vert1, vert2;
11162         vector  gpos2;
11163         int             i;
11164
11165         for (i=0; i<Num_AI_debug_render_stuff; i++) {
11166                 ship_subsys     *ss;
11167                 int     parent_objnum;
11168                 vector  gpos, gvec;
11169                 model_subsystem *tp;
11170
11171                 ss = AI_debug_render_stuff[i].ss;
11172                 tp = ss->system_info;
11173
11174                 parent_objnum = AI_debug_render_stuff[i].parent_objnum;
11175
11176                 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
11177                 g3_rotate_vertex(&vert1, &gpos);
11178                 vm_vec_scale_add(&gpos2, &gpos, &gvec, 20.0f);
11179                 g3_rotate_vertex(&vert2, &gpos2);
11180                 gr_set_color(0, 0, 255);
11181                 g3_draw_sphere(&vert1, 2.0f);
11182                 gr_set_color(255, 0, 255);
11183                 g3_draw_sphere(&vert2, 2.0f);
11184                 g3_draw_line(&vert1, &vert2);
11185         }
11186
11187         // draw from beta to its goal point
11188 /*      for (i=0; i<6; i++) {
11189                 ai_info *aip = &Ai_info[i];
11190                 gr_set_color(0, 0, 255);
11191                 g3_rotate_vertex(&vert1, &Objects[i].pos);
11192                 g3_rotate_vertex(&vert2, &aip->goal_point);
11193                 g3_draw_line(&vert1, &vert2);
11194         } */
11195         
11196
11197         Num_AI_debug_render_stuff = 0;
11198 }
11199
11200 #endif
11201
11202 #ifndef NDEBUG
11203 int     Msg_count_4996 = 0;
11204 #endif
11205
11206 //      --------------------------------------------------------------------------
11207 // Process subobjects of object objnum.
11208 //      Deal with engines disabled.
11209 void process_subobjects(int objnum)
11210 {
11211         model_subsystem *psub;
11212         ship_subsys     *pss;
11213         object  *objp = &Objects[objnum];
11214         ship            *shipp = &Ships[objp->instance];
11215         ai_info *aip = &Ai_info[shipp->ai_index];
11216         ship_info       *sip = &Ship_info[shipp->ship_info_index];
11217
11218         for ( pss = GET_FIRST(&shipp->subsys_list); pss !=END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
11219                 psub = pss->system_info;
11220
11221                 // Don't process destroyed objects
11222                 if ( pss->current_hits <= 0.0f ) 
11223                         continue;
11224
11225                 switch (psub->type) {
11226                 case SUBSYSTEM_TURRET:
11227                         if ( psub->turret_num_firing_points > 0 )       {
11228                                 ai_fire_from_turret(shipp, pss, objnum);
11229                         } else {
11230 #ifndef NDEBUG
11231                                 if (!Msg_count_4996) {
11232                                         Warning( LOCATION, "Ship '%s' has turrets with no guns!\nProbably a model problem, so get an artist!", shipp->ship_name );
11233                                         Msg_count_4996++;
11234                                 }
11235 #endif
11236                                 }
11237                         break;
11238
11239                 case SUBSYSTEM_ENGINE:
11240                 case SUBSYSTEM_NAVIGATION:
11241                 case SUBSYSTEM_COMMUNICATION:
11242                 case SUBSYSTEM_WEAPONS:
11243                 case SUBSYSTEM_SENSORS:
11244                 case SUBSYSTEM_UNKNOWN:
11245                         break;
11246
11247                 // next set of subsystems may rotation
11248                 case SUBSYSTEM_RADAR:
11249                 case SUBSYSTEM_SOLAR:
11250                 case SUBSYSTEM_GAS_COLLECT:
11251                 case SUBSYSTEM_ACTIVATION:
11252                         break;
11253                 default:
11254                         Error(LOCATION, "Illegal subsystem type.\n");
11255                 }
11256
11257                 // do solar/radar/gas/activator rotation here
11258                 if ( psub->flags & MSS_FLAG_ROTATES )   {
11259                         if (psub->flags & MSS_FLAG_STEPPED_ROTATE       ) {
11260                                 submodel_stepped_rotate(psub, &pss->submodel_info_1);
11261                         } else {
11262                                 submodel_rotate(psub, &pss->submodel_info_1 );
11263                         }
11264                 }
11265
11266         }
11267
11268         //      Deal with a ship with blown out engines.
11269         if (ship_get_subsystem_strength(shipp, SUBSYSTEM_ENGINE) == 0.0f) {
11270                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
11271                         // AL: Only attack forever if not trying to depart to a docking bay.  Need to have this in, since
11272                         //     a ship may get repaired... and it should still try to depart.  Since docking bay departures
11273                         //     are not handled as goals, we don't want to leave the AIM_BAY_DEPART mode.
11274                         if ( aip->mode != AIM_BAY_DEPART ) {
11275                                 ai_attack_object(objp, NULL, 99, NULL);         //      Regardless of current mode, enter attack mode.
11276                                 aip->submode = SM_ATTACK_FOREVER;                               //      Never leave attack submode, don't avoid, evade, etc.
11277                         }
11278                 }
11279         }
11280
11281
11282 }
11283
11284 //      Given an object and the wing it's in, return its index in the wing list.
11285 //      This defines its location in the wing formation.
11286 //      If the object can't be found in the wing, return -1.
11287 //      *objp           object of interest
11288 //      wingnum the wing *objp is in
11289 int get_wing_index(object *objp, int wingnum)
11290 {
11291         wing    *wingp;
11292         int     i;
11293
11294         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11295
11296         wingp = &Wings[wingnum];
11297
11298         for (i=wingp->current_count-1; i>=0; i--)
11299                 if ( objp->instance == wingp->ship_index[i] )
11300                         break;
11301
11302         return i;               //      Note, returns -1 if string not found.
11303 }
11304
11305 //      Given a wing, return a pointer to the object of its leader.
11306 //      Asserts if object not found.
11307 //      Currently, the wing leader is defined as the first object in the wing.
11308 //      wingnum         Wing number in Wings array.
11309 //      If wing leader is disabled, swap it with another ship.
11310 object * get_wing_leader(int wingnum)
11311 {
11312         wing            *wingp;
11313         int             ship_num;
11314
11315         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11316
11317         wingp = &Wings[wingnum];
11318
11319         Assert(wingp->current_count != 0);                      //      Make sure there is a leader
11320
11321         ship_num = wingp->ship_index[0];
11322
11323         //      If this ship is disabled, try another ship in the wing.
11324         int n = 0;
11325         while (ship_get_subsystem_strength(&Ships[ship_num], SUBSYSTEM_ENGINE) == 0.0f) {
11326                 n++;
11327                 if (n >= wingp->current_count)
11328                         break;  
11329                 ship_num = wingp->ship_index[n];
11330         }
11331
11332         if (( n != 0) && (n != wingp->current_count)) {
11333                 int t = wingp->ship_index[0];
11334                 wingp->ship_index[0] = wingp->ship_index[n];
11335                 wingp->ship_index[n] = t;
11336         }
11337
11338         return &Objects[Ships[ship_num].objnum];
11339 }
11340
11341 #define DEFAULT_WING_X_DELTA            1.0f
11342 #define DEFAULT_WING_Y_DELTA            0.25f
11343 #define DEFAULT_WING_Z_DELTA            0.75f
11344 #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))
11345 // next constant is higher that MAX_SHIPS_IN_WINGS to deal with forming on player's wing
11346 #define MAX_FORMATION_ROWS              4
11347
11348 //      Given a position in a wing, return the desired location of the ship relative to the leader
11349 //      *_delta_vec             OUTPUT.  delta vector based on wing_index
11350 //      wing_index              position in wing.
11351 void get_wing_delta(vector *_delta_vec, int wing_index)
11352 {
11353         int     wi0;
11354
11355         Assert(wing_index >= 0);
11356
11357         int     k, row, column;
11358
11359         int bank = wing_index / (MAX_FORMATION_ROWS*(MAX_FORMATION_ROWS+1)/2);
11360         wi0 = wing_index % (MAX_FORMATION_ROWS * (MAX_FORMATION_ROWS+1)/2);
11361
11362         k = 0;
11363         for (row=1; row<MAX_FORMATION_ROWS+1; row++) {
11364                 k += row;
11365                 if (wi0 < k)
11366                         break;
11367         }
11368
11369         row--;
11370         column = wi0 - k + row + 1;
11371
11372         _delta_vec->xyz.x = ((float) column - (float) row/2.0f) * DEFAULT_WING_X_DELTA/DEFAULT_WING_MAG;
11373         _delta_vec->xyz.y = ((float)row + (float)bank*2.25f) * DEFAULT_WING_Y_DELTA/DEFAULT_WING_MAG;
11374         _delta_vec->xyz.z = - ((float)row + 0.5f * (float) bank) * DEFAULT_WING_Z_DELTA/DEFAULT_WING_MAG;
11375 }
11376
11377 //      Compute the largest radius of a ship in a *objp's wing.
11378 float gwlr_1(object *objp, ai_info *aip)
11379 {
11380         int             wingnum = aip->wing;
11381         float           max_radius;
11382         object  *o;
11383         ship_obj        *so;
11384
11385         Assert(wingnum >= 0);
11386
11387         max_radius = objp->radius;
11388
11389         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11390                 o = &Objects[so->objnum];
11391                 if (Ai_info[Ships[o->instance].ai_index].wing == wingnum)
11392                         if (o->radius > max_radius)
11393                                 max_radius = o->radius;
11394         }
11395
11396         return max_radius;
11397 }
11398
11399 //      Compute the largest radius of a ship forming on *objp's wing.
11400 float gwlr_object_1(object *objp, ai_info *aip)
11401 {
11402         float           max_radius;
11403         object  *o;
11404         ship_obj        *so;
11405
11406         max_radius = objp->radius;
11407
11408         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11409                 o = &Objects[so->objnum];
11410                 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == OBJ_INDEX(objp))
11411                         if (o->radius > max_radius)
11412                                 max_radius = o->radius;
11413         }
11414
11415         return max_radius;
11416 }
11417
11418 //      For the wing that *objp is part of, return the largest ship radius in that wing.
11419 float get_wing_largest_radius(object *objp, int formation_object_flag)
11420 {
11421         ship            *shipp;
11422         ai_info *aip;
11423
11424         Assert(objp->type == OBJ_SHIP);
11425         Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
11426         shipp = &Ships[objp->instance];
11427         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11428         aip = &Ai_info[shipp->ai_index];
11429
11430         if (formation_object_flag) {
11431                 return gwlr_object_1(objp, aip);
11432         } else {
11433                 return gwlr_1(objp, aip);
11434         }
11435
11436 }
11437
11438 float Wing_y_scale = 2.0f;
11439 float Wing_scale = 1.0f;
11440 DCF(wing_y_scale, "")
11441 {
11442         dc_get_arg(ARG_FLOAT);
11443         Wing_y_scale = Dc_arg_float;
11444 }
11445
11446 DCF(wing_scale, "")
11447 {
11448         dc_get_arg(ARG_FLOAT);
11449         Wing_scale = Dc_arg_float;
11450 }
11451
11452 // Given a wing leader and a position in the wing formation, return the desired absolute location to fly to.
11453 //      Returns result in *result_pos.
11454 void get_absolute_wing_pos(vector *result_pos, object *leader_objp, int wing_index, int formation_object_flag)
11455 {
11456         vector  wing_delta, rotated_wing_delta;
11457         float           wing_spread_size;
11458
11459         get_wing_delta(&wing_delta, wing_index);                //      Desired location in leader's reference frame
11460
11461         wing_spread_size = max(50.0f, 3.0f * get_wing_largest_radius(leader_objp, formation_object_flag) + 15.0f);
11462
11463         // for player obj (1) move ships up 20% (2) scale formation up 20%
11464         if (leader_objp->flags & OF_PLAYER_SHIP) {
11465                 wing_delta.xyz.y *= Wing_y_scale;
11466                 wing_spread_size *= Wing_scale;
11467         }
11468
11469         vm_vec_scale(&wing_delta, wing_spread_size * (1.0f + leader_objp->phys_info.speed/70.0f));
11470
11471         vm_vec_unrotate(&rotated_wing_delta, &wing_delta, &leader_objp->orient);        //      Rotate into leader's reference.
11472
11473         vm_vec_add(result_pos, &leader_objp->pos, &rotated_wing_delta); //      goal_point is absolute 3-space point.
11474 }
11475
11476 #ifndef NDEBUG
11477 int Debug_render_wing_phantoms;
11478
11479 void render_wing_phantoms(object *objp)
11480 {
11481         int             i;
11482         ship            *shipp;
11483         ai_info *aip;
11484         int             wingnum;
11485         int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11486         vector  goal_point;
11487         
11488         Assert(objp->type == OBJ_SHIP);
11489         Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11490
11491         shipp = &Ships[objp->instance];
11492         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11493
11494         aip = &Ai_info[shipp->ai_index];
11495
11496         wingnum = aip->wing;
11497
11498         if (wingnum == -1)
11499                 return;
11500
11501         wing_index = get_wing_index(objp, wingnum);
11502
11503         //      If this ship is NOT the leader, abort.
11504         if (wing_index != 0)
11505                 return;
11506
11507         for (i=0; i<32; i++)
11508                 if (Debug_render_wing_phantoms & (1 << i)) {
11509                         get_absolute_wing_pos(&goal_point, objp, i, 0);
11510         
11511                         vertex  vert;
11512                         gr_set_color(255, 0, 128);
11513                         g3_rotate_vertex(&vert, &goal_point);
11514                         g3_draw_sphere(&vert, 2.0f);
11515                 }
11516
11517         Debug_render_wing_phantoms = 0;
11518
11519 }
11520
11521 void render_wing_phantoms_all()
11522 {
11523         object  *objp;
11524         ship_obj        *so;
11525
11526         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11527                 ship            *shipp;
11528                 ai_info *aip;
11529                 int             wingnum;
11530                 int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11531
11532                 objp = &Objects[so->objnum];
11533                 
11534                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11535                 shipp = &Ships[objp->instance];
11536                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11537
11538                 aip = &Ai_info[shipp->ai_index];
11539
11540                 wingnum = aip->wing;
11541
11542                 if (wingnum == -1)
11543                         continue;
11544
11545                 wing_index = get_wing_index(objp, wingnum);
11546
11547                 //      If this ship is NOT the leader, abort.
11548                 if (wing_index != 0)
11549                         continue;
11550                 
11551                 render_wing_phantoms(objp);
11552
11553                 return;
11554         }
11555 }
11556
11557 #endif
11558
11559 //      Hook from goals code to AI.
11560 //      Force a wing to fly in formation.
11561 //      Sets AIF_FORMATION bit in ai_flags.
11562 //      wingnum         Wing to force to fly in formation
11563 void ai_fly_in_formation(int wingnum)
11564 {
11565         object  *objp;
11566         ship            *shipp;
11567         ship_obj        *so;
11568
11569         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11570                 objp = &Objects[so->objnum];
11571                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11572
11573                 shipp = &Ships[objp->instance];
11574                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11575
11576                 if (Ai_info[shipp->ai_index].wing == wingnum) {
11577                         Ai_info[shipp->ai_index].ai_flags |= AIF_FORMATION_WING;
11578                         Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_OBJECT;
11579                 }
11580         }
11581 }
11582
11583 //      Hook from goals code to AI.
11584 //      Force a wing to abandon formation flying.
11585 //      Clears AIF_FORMATION bit in ai_flags.
11586 //      wingnum         Wing to force to fly in formation
11587 void ai_disband_formation(int wingnum)
11588 {
11589         object  *objp;
11590         ship            *shipp;
11591         ship_obj        *so;
11592
11593         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11594                 objp = &Objects[so->objnum];
11595                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11596
11597                 shipp = &Ships[objp->instance];
11598                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11599
11600                 if (Ai_info[shipp->ai_index].wing == wingnum) {
11601                         Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_WING;
11602                 }
11603         }
11604 }
11605
11606 float   Leader_chaos = 0.0f;
11607 int Chaos_frame = -1;
11608
11609 //      Return true if objp is flying in an erratic manner
11610 //      Only true if objp is a player
11611 int formation_is_leader_chaotic(object *objp)
11612 {
11613         if (Game_mode & GM_MULTIPLAYER)
11614                 return 0;
11615
11616         if (objp != Player_obj)
11617                 return 0;
11618
11619         if (Framecount != Chaos_frame) {
11620                 float   speed_scale;
11621                 float   fdot, udot;
11622
11623                 speed_scale = 3.0f + objp->phys_info.speed * 0.1f;
11624
11625                 fdot = 5.0f * (1.0f - vm_vec_dot(&objp->orient.v.fvec, &objp->last_orient.v.fvec)) * flFrametime;
11626                 udot = 8.0f * (1.0f - vm_vec_dot(&objp->orient.v.uvec, &objp->last_orient.v.uvec)) * flFrametime;
11627
11628                 Leader_chaos += fdot * speed_scale + udot * speed_scale;
11629
11630                 Leader_chaos *= (1.0f - flFrametime*0.2f);
11631
11632                 if (Leader_chaos < 0.0f)
11633                         Leader_chaos = 0.0f;
11634                 else if (Leader_chaos > 1.7f)
11635                         Leader_chaos = 1.7f;
11636
11637                 //nprintf(("AI", "Frame %i: chaos = %7.4f\n", Framecount, Leader_chaos));
11638
11639                 Chaos_frame = Framecount;
11640         }
11641
11642         return (Leader_chaos > 1.0f);
11643 }
11644
11645 // Fly in formation.
11646 //      Make Pl_objp assume its proper place in formation.
11647 //      If the leader of the wing is doing something stupid, like fighting a battle,
11648 //      then the poor sap wingmates will be in for a "world of hurt"
11649 //      Return TRUE if we need to process this object's normal mode
11650 int ai_formation()
11651 {
11652         object  *leader_objp;
11653         ship            *shipp;
11654         ai_info *aip, *laip;
11655         int             wingnum;
11656         int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11657         int             player_wing;    // index of the players wingnum
11658         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;
11659         float           dot_to_goal, dist_to_goal, leader_speed;
11660
11661         Assert(Pl_objp->type == OBJ_SHIP);
11662         Assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_SHIPS));
11663
11664         shipp = &Ships[Pl_objp->instance];
11665
11666         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11667
11668         aip = &Ai_info[shipp->ai_index];
11669
11670         Assert((aip->ai_flags & AIF_FORMATION) != AIF_FORMATION);       //      Make sure not both types of formation flying in effect.
11671
11672         //      Determine which kind of formation flying.
11673         //      If tracking an object, not in waypoint mode:
11674         if (aip->ai_flags & AIF_FORMATION_OBJECT) {
11675                 if ((aip->goal_objnum < 0) || (aip->goal_objnum >= MAX_OBJECTS)) {
11676                         aip->ai_flags &= ~AIF_FORMATION_OBJECT;
11677                         return 1;
11678                 }
11679                 
11680                 wing_index = ai_formation_object_get_slotnum(aip->goal_objnum, Pl_objp);
11681                 leader_objp = &Objects[aip->goal_objnum];
11682         } else {        //      Formation flying in waypoint mode.
11683                 Assert(aip->ai_flags & AIF_FORMATION_WING);
11684                 if (aip->mode != AIM_WAYPOINTS) {
11685                         aip->ai_flags &= ~AIF_FORMATION_WING;
11686                         return 1;
11687                 }
11688
11689                 wingnum = aip->wing;
11690
11691                 if (wingnum == -1)
11692                         return 1;
11693
11694                 // disable formation flying for any ship in the players wing
11695                 player_wing = Ships[Player_obj->instance].wingnum;
11696                 if ( (player_wing != -1) && (wingnum == player_wing) )
11697                         return 1;
11698
11699                 wing_index = get_wing_index(Pl_objp, wingnum);
11700
11701                 leader_objp = get_wing_leader(wingnum);
11702
11703         }
11704
11705         //      If docked with a ship in this wing, only the more massive one actually flies in formation.
11706         if (aip->dock_objnum != -1) {
11707                 object  *other_objp = &Objects[aip->dock_objnum];
11708                 ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
11709
11710                 if (aip->wing == other_aip->wing) {
11711                         if (Pl_objp->phys_info.mass < other_objp->phys_info.mass)
11712                                 return 0;
11713                         else if (Pl_objp->phys_info.mass == other_objp->phys_info.mass) {
11714                                 if (Pl_objp->signature < other_objp->signature)
11715                                         return 0;
11716                         }
11717                 }
11718         }
11719
11720         Assert(leader_objp != NULL);
11721         laip = &Ai_info[Ships[leader_objp->instance].ai_index];
11722
11723         //      Make sure we're really in this wing.
11724         if (wing_index == -1)
11725                 return 1;
11726
11727         //      If this ship is the leader, abort, as he doesn't have to follow anyone.
11728         if (wing_index == 0) {
11729                 // nprintf(("AI", "Hmm, wing leader %s in ai_formation for no good reason.\n", shipp->ship_name));
11730                 return 1;
11731         }
11732
11733         if (aip->mode == AIM_WAYPOINTS) {
11734                 aip->wp_list = laip->wp_list;
11735                 if (laip->wp_index < Waypoint_lists[laip->wp_list].count)
11736                         aip->wp_index = laip->wp_index;
11737                 else
11738                         aip->wp_index = Waypoint_lists[laip->wp_list].count - 1;
11739                 aip->wp_flags = laip->wp_flags;
11740                 aip->wp_dir = laip->wp_dir;
11741         }
11742
11743         #ifndef NDEBUG
11744         Debug_render_wing_phantoms |= (1 << wing_index);
11745         #endif
11746
11747         leader_speed = leader_objp->phys_info.speed;
11748         vector leader_vec = leader_objp->phys_info.vel;
11749
11750         get_absolute_wing_pos(&goal_point, leader_objp, wing_index, aip->ai_flags & AIF_FORMATION_OBJECT);
11751         vm_vec_scale_add(&future_goal_point_5, &goal_point, &leader_vec, 10.0f);
11752         vm_vec_scale_add(&future_goal_point_2, &goal_point, &leader_vec, 5.0f);
11753         vm_vec_scale_add(&future_goal_point_x, &goal_point, &leader_objp->orient.v.fvec, 10.0f);        //      used when very close to destination
11754         vm_vec_scale_add(&future_goal_point_1000x, &goal_point, &leader_objp->orient.v.fvec, 1000.0f);  //      used when very close to destination
11755
11756         //      Now, get information telling this object how to turn and accelerate to get to its
11757         //      desired location.
11758         vm_vec_sub(&vec_to_goal, &goal_point, &Pl_objp->pos);
11759         if ( vm_vec_mag_quick(&vec_to_goal) < AICODE_SMALL_MAGNITUDE )
11760                 vec_to_goal.xyz.x += 0.1f;
11761
11762         vm_vec_copy_normalize(&dir_to_goal, &vec_to_goal);
11763         //dot_to_goal = vm_vec_dot(&dir_to_goal, &leader_objp->orient.v.fvec);
11764         dot_to_goal = vm_vec_dot(&dir_to_goal, &Pl_objp->orient.v.fvec);
11765         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &goal_point);
11766         float   dist_to_goal_2 = vm_vec_dist_quick(&Pl_objp->pos, &future_goal_point_2);
11767
11768         // 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));
11769
11770         int     chaotic_leader = 0;
11771
11772         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.
11773
11774         if (dist_to_goal > 500.0f) {
11775                 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11776                 accelerate_ship(aip, 1.0f);
11777         } else if (dist_to_goal > 200.0f) {
11778                 if (dot_to_goal > -0.5f) {
11779                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11780                         float range_speed = shipp->current_max_speed - leader_speed;
11781                         if (range_speed > 0.0f)
11782                                 set_accel_for_target_speed(Pl_objp, leader_speed + range_speed * (dist_to_goal+100.0f)/500.0f);
11783                         else
11784                                 set_accel_for_target_speed(Pl_objp, shipp->current_max_speed);
11785                 } else {
11786                         turn_towards_point(Pl_objp, &future_goal_point_5, NULL, 0.0f);
11787                         if (leader_speed > 10.0f)
11788                                 set_accel_for_target_speed(Pl_objp, leader_speed *(1.0f + dot_to_goal));
11789                         else
11790                                 set_accel_for_target_speed(Pl_objp, 10.0f);
11791                 }
11792         } else {
11793                 vector  v2f2;
11794                 float   dot_to_f2;
11795                 float   dist_to_f2;
11796
11797                 dist_to_f2 = vm_vec_normalized_dir(&v2f2, &future_goal_point_2, &Pl_objp->pos);
11798                 dot_to_f2 = vm_vec_dot(&v2f2, &Pl_objp->orient.v.fvec);
11799
11800                 //      Leader flying like a maniac.  Don't try hard to form on wing.
11801                 if (chaotic_leader) {
11802                         turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11803                         set_accel_for_target_speed(Pl_objp, min(leader_speed*0.8f, 20.0f));
11804                 } else if (dist_to_goal > 75.0f) {
11805                         turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11806                         float   delta_speed;
11807                         float range_speed = shipp->current_max_speed - leader_speed;
11808                         if (range_speed > 0.0f)
11809                                 delta_speed = dist_to_goal_2/500.0f * range_speed;
11810                         else
11811                                 delta_speed = shipp->current_max_speed - leader_speed;
11812                         if (dot_to_goal < 0.0f) {
11813                                 delta_speed = -delta_speed;
11814                                 if (-delta_speed > leader_speed/2)
11815                                         delta_speed = -leader_speed/2;
11816                         }
11817
11818                         if (leader_speed < 5.0f)
11819                                 if (delta_speed < 5.0f)
11820                                         delta_speed = 5.0f;
11821
11822                         float scale = dot_to_f2;
11823                         if (scale < 0.1f)
11824                                 scale = 0.0f;
11825                         else
11826                                 scale *= scale;
11827
11828                         set_accel_for_target_speed(Pl_objp, scale * (leader_speed + delta_speed));
11829                 } else {
11830                         //nprintf(("AI", "Dot = %7.3f\n", dot_to_goal));
11831
11832                         if (leader_speed < 5.0f) {
11833                                 //      Leader very slow.  If not close to goal point, get very close.  Note, keep trying to get close unless
11834                                 //      moving very slowly, else momentum can carry far away from goal.
11835
11836                                 if ((dist_to_goal > 10.0f) || ((Pl_objp->phys_info.speed > leader_speed + 2.5f) && (dot_to_goal > 0.5f))) {
11837                                         //nprintf(("MK", "(1) "));
11838                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11839                                         set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/10.0f);
11840                                 } else {
11841                                         if (Pl_objp->phys_info.speed < 0.5f) {
11842                                                 //nprintf(("MK", "(2) "));
11843                                                 turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11844                                         } else {
11845                                                 //nprintf(("MK", "(3) "));
11846                                         }
11847                                         set_accel_for_target_speed(Pl_objp, leader_speed);
11848                                 }
11849                                 //nprintf(("MK", "dist: %7.3f, dot: %6.3f, speed: %7.3f\n", dist_to_goal, dot_to_goal, Pl_objp->phys_info.speed));
11850                         } else if (dist_to_goal > 10.0f) {
11851                                 float   dv;
11852
11853                                 //future_goal_point_2;
11854
11855                                 turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11856
11857                                 if (dist_to_goal > 25.0f) {
11858                                         if (dot_to_goal < 0.3f)
11859                                                 dv = -0.1f;
11860                                         else
11861                                                 dv = dot_to_goal - 0.2f;
11862
11863                                         set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/5.0f * dv);
11864                                 } else {
11865                                         set_accel_for_target_speed(Pl_objp, leader_speed + 1.5f * dot_to_goal - 1.0f);
11866                                 }
11867                         } else {
11868                                 if (Pl_objp->phys_info.speed < 0.1f)
11869                                         turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11870                                 else
11871                                         turn_towards_point(Pl_objp, &future_goal_point_x, NULL, 0.0f);
11872                                 set_accel_for_target_speed(Pl_objp, 0.0f);
11873                         }
11874                 }
11875
11876         }
11877
11878         //      See how different this ship's bank is relative to wing leader
11879         float   up_dot = vm_vec_dot(&leader_objp->orient.v.uvec, &Pl_objp->orient.v.uvec);
11880         if (up_dot < 0.996f) {
11881                 vector  w_out;
11882                 matrix  new_orient;
11883                 vector  angular_accel;
11884
11885                 vm_vec_copy_scale(&angular_accel, &Pl_objp->phys_info.max_rotvel, 0.2f);
11886                 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);
11887
11888         //      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)));
11889                 Pl_objp->orient = new_orient;
11890                 Pl_objp->phys_info.rotvel = w_out;
11891         //      Pl_objp->phys_info.desired_rotvel = w_out;
11892         } else {
11893                 Pl_objp->phys_info.rotvel.xyz.z = 0.0f;
11894         }
11895
11896         return 0;
11897 }
11898
11899 //      Return index of object repairing object objnum.
11900 int find_repairing_objnum(int objnum)
11901 {
11902         object          *objp;
11903         ship                    *shipp;
11904         ship_info       *sip;
11905         ship_obj                *so;
11906
11907         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11908                 objp = &Objects[so->objnum];
11909
11910                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11911
11912                 shipp = &Ships[objp->instance];
11913                 sip = &Ship_info[shipp->ship_info_index];
11914
11915                 if (sip->flags & SIF_SUPPORT) {
11916                         ai_info *aip;
11917
11918                         aip = &Ai_info[shipp->ai_index];
11919
11920                         if (aip->goal_objnum == objnum) {
11921                                 return objp-Objects;
11922                         }
11923                 }
11924         }
11925
11926         return -1;
11927 }
11928
11929 //      If object *objp is being repaired, deal with it!
11930 void ai_do_repair_frame(object *objp, ai_info *aip, float frametime)
11931 {
11932         if (Ships[objp->instance].team == TEAM_TRAITOR) {
11933                 ai_abort_rearm_request(objp);
11934                 return;
11935         }
11936
11937         if (aip->ai_flags & (AIF_BEING_REPAIRED | AIF_AWAITING_REPAIR)) {
11938                 int     dock_objnum;
11939                 ai_info *repair_aip;
11940
11941                 dock_objnum = aip->dock_objnum; // find_repairing_objnum(objp-Objects);
11942                 //Assert(dock_objnum != -1);
11943                 if (dock_objnum == -1)
11944                         return;
11945                 if (Objects[dock_objnum].signature != aip->dock_signature) {
11946                         Int3();         //      Curious -- object numbers match, but signatures do not.
11947                                                         //      Must mean original repair ship died and was replaced by current ship.
11948                         return;
11949                 }
11950         
11951                 repair_aip = &Ai_info[Ships[Objects[dock_objnum].instance].ai_index];
11952                 //Assert(repair_aip->mode == AIM_DOCK);
11953
11954                 if (aip->ai_flags & AIF_BEING_REPAIRED) {
11955                         // Assert(repair_aip->submode == AIS_DOCK_4);
11956
11957                         //      Wait awhile into the mode to synchronize with sound effect.
11958                         if (Missiontime - repair_aip->submode_start_time > REARM_SOUND_DELAY) {
11959                                 int repaired;
11960
11961                                 repaired = ship_do_rearm_frame( objp, frametime );              // hook to do missile rearming
11962
11963                                 //      See if fully repaired.  If so, cause process to stop.
11964                                 if ( repaired && (repair_aip->submode == AIS_DOCK_4)) {
11965
11966                                         repair_aip->submode = AIS_UNDOCK_0;
11967                                         repair_aip->submode_start_time = Missiontime;
11968
11969                                         // if repairing player object -- tell him done with repair
11970                                         if ( !MULTIPLAYER_CLIENT ){
11971                                                 ai_do_objects_repairing_stuff( objp, &Objects[dock_objnum], REPAIR_INFO_COMPLETE );
11972                                         }
11973                                 }
11974                         }
11975                 } else if (aip->ai_flags & AIF_AWAITING_REPAIR) {
11976                         //      If this ship has been awaiting repair for 90+ seconds, abort.
11977                         if ( !MULTIPLAYER_CLIENT ) {
11978                                 if ((Game_mode & GM_MULTIPLAYER) || (objp != Player_obj)) {
11979                                         if ((repair_aip->goal_objnum == OBJ_INDEX(objp)) && (timestamp_elapsed(aip->abort_rearm_timestamp))) {
11980                                                 ai_abort_rearm_request(objp);
11981                                                 aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);
11982                                         }
11983                                 }
11984                         }
11985                 }
11986         } else {
11987                 // AL 11-24-97: If this is the player ship, ensure the repair sound isn't playing.  We need to
11988                 //              do this check, since this is a looping sound, and may continue on if rearm/repair
11989                 //              finishes abnormally once sound begins looping.
11990                 if ( objp == Player_obj ) {
11991                         player_stop_repair_sound();
11992                 }
11993         }
11994 }
11995
11996 //      Shell around dock_orient_and_approach to detect whether dock process should be aborted.
11997 //      obj1 is the ship performing the repair.
11998 //      obj2 is the ship being repaired.
11999 void call_doa(object *obj1, object *obj2, ship_info *sip1)
12000 {
12001         if (sip1->flags & SIF_SUPPORT) {
12002                 if (obj2->phys_info.speed > MAX_REPAIR_SPEED) {
12003
12004                         // call the ai_abort rearm request code
12005                         ai_abort_rearm_request( obj2 );
12006                 } else
12007                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
12008         } else {
12009                 if (Ship_info[Ships[obj1->instance].ship_info_index].flags & SIF_CARGO)
12010                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
12011                 else if (Ship_info[Ships[obj2->instance].ship_info_index].flags & SIF_CARGO)
12012                         dock_orient_and_approach(obj2, obj1, DOA_DOCK_STAY);
12013                 else {
12014                         //mprintf(("Warning: Not sure, but making %s [%s] move to stay docked with %s [%s]\n",
12015                         //      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));
12016                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
12017
12018                 }
12019         }
12020
12021 }
12022
12023 //      Maybe launch a countermeasure.
12024 //      Also, detect a supposed homing missile that no longer exists.
12025 void ai_maybe_launch_cmeasure(object *objp, ai_info *aip)
12026 {
12027         float                   dist;
12028         ship_info       *sip;
12029         ship                    *shipp;
12030
12031         shipp = &Ships[objp->instance];
12032         sip = &Ship_info[shipp->ship_info_index];
12033
12034         if (!(sip->flags & (SIF_SMALL_SHIP | SIF_TRANSPORT)))
12035                 return;
12036
12037         if (!shipp->cmeasure_count)
12038                 return;
12039
12040         if ( !timestamp_elapsed(shipp->cmeasure_fire_stamp) )
12041                 return;
12042
12043         //      If not on player's team and Skill_level + ai_class is low, never fire a countermeasure.  The ship is too dumb.
12044         if (shipp->team != Player_ship->team) {
12045                 if (Game_skill_level + aip->ai_class < 4){
12046                         return;
12047                 }
12048         }
12049
12050         if ((aip->nearest_locked_object != -1) && (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)) {
12051                 object  *weapon_objp;
12052                 weapon  *weaponp;
12053                 weapon_info     *wip;
12054
12055                 weapon_objp = &Objects[aip->nearest_locked_object];
12056                 weaponp = &Weapons[weapon_objp->instance];
12057                 wip = &Weapon_info[weaponp->weapon_info_index];
12058
12059                 if ((dist = vm_vec_dist_quick(&objp->pos, &weapon_objp->pos)) < weapon_objp->phys_info.speed*2.0f) {
12060         
12061                         aip->nearest_locked_distance = dist;
12062                         //      Verify that this object is really homing on us.
12063                         object  *weapon_objp;
12064
12065                         weapon_objp = &Objects[aip->nearest_locked_object];
12066
12067                         float   fire_chance;
12068
12069                         //      For ships on player's team, have constant, average chance to fire.
12070                         //      For enemies, increasing chance with higher skill level.
12071                         if (shipp->team == Player_ship->team)
12072                                 fire_chance = Cmeasure_fire_chance[NUM_SKILL_LEVELS/2];
12073                         else
12074                                 fire_chance = Cmeasure_fire_chance[Game_skill_level];
12075
12076                         //      Decrease chance to fire at lower ai class.
12077                         fire_chance *= (float) aip->ai_class/Num_ai_classes;
12078
12079                         float r = frand();
12080                         if (fire_chance < r) {
12081                                 //nprintf(("AI", "Not firing countermeasure due to skill level: %7.3f < %7.3f\n", fire_chance, r));
12082                                 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.
12083                                 return;
12084                         }
12085
12086                         if (weapon_objp->type == OBJ_WEAPON) {
12087                                 if (weapon_objp->instance >= 0) {
12088                                         //nprintf(("AI", "Firing countermeasure at time t=%7.3f\n", f2fl(Missiontime)));
12089                                         ship_launch_countermeasure(objp);
12090                                         shipp->cmeasure_fire_stamp = timestamp(2*CMEASURE_WAIT);
12091                                         return;
12092                                 }
12093                         }
12094         
12095                 }
12096         }
12097
12098         return;
12099 }
12100
12101 //      --------------------------------------------------------------------------
12102 void ai_preprocess_ignore_objnum(object *objp, ai_info *aip)
12103 {
12104 //      if (aip->ignore_objnum == UNUSED_OBJNUM)
12105 //              return;
12106
12107         if (aip->ai_flags & AIF_TEMPORARY_IGNORE) {
12108                 if (timestamp_elapsed(aip->ignore_expire_timestamp)) {
12109                         aip->ignore_objnum = UNUSED_OBJNUM;
12110                 }
12111         }
12112
12113         if (is_ignore_object(aip, aip->goal_objnum)) {
12114                 aip->goal_objnum = -1;
12115                 // AL 12-11-97: If in STRAFE mode, we need to ensure that target_objnum is also
12116                 //              set to -1
12117                 if ( aip->mode == AIM_STRAFE ) {
12118                         aip->target_objnum = -1;
12119                 }
12120         }
12121
12122         if (is_ignore_object(aip, aip->target_objnum))
12123                 aip->target_objnum = -1;
12124 }
12125
12126 /*
12127 void ai_safety_circle_spot()
12128 {
12129         vector  goal_point;
12130         ship_info       *sip;
12131
12132         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12133
12134         goal_point = Ai_info[Ships[Pl_objp->instance].ai_index].goal_point;
12135         turn_towards_tangent(Pl_objp, &goal_point, 50.0f);
12136
12137         set_accel_for_target_speed(Pl_objp, sip->max_speed/4.0f);
12138
12139 //      float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
12140 //      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));
12141
12142 }
12143 */
12144
12145 #define CHASE_CIRCLE_DIST               100.0f
12146
12147 void ai_chase_circle(object *objp)
12148 {
12149         float           dist_to_goal;
12150         float           target_speed;
12151         vector  goal_point;
12152         ship_info       *sip;
12153         ai_info         *aip;
12154
12155         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12156
12157         target_speed = sip->max_speed/4.0f;
12158         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12159
12160         Assert(vm_vec_mag(&aip->goal_point) >= 0.0f);           //      Supposedly detects bogus vector
12161
12162         goal_point = aip->goal_point;
12163
12164         if (aip->ignore_objnum == UNUSED_OBJNUM) {
12165                 dist_to_goal = vm_vec_dist_quick(&aip->goal_point, &objp->pos);
12166
12167                 if (dist_to_goal > 2*CHASE_CIRCLE_DIST) {
12168                         vector  vec_to_goal;
12169                         //      Too far from circle goal, create a new goal point.
12170                         vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
12171                         vm_vec_scale_add(&aip->goal_point, &objp->pos, &vec_to_goal, CHASE_CIRCLE_DIST);
12172                 }
12173
12174                 goal_point = aip->goal_point;
12175         } else if (is_ignore_object(aip, aip->ignore_objnum)) {
12176                 object  *ignore_objp = &Objects[aip->ignore_objnum];
12177
12178                 vector  tvec1;
12179                 float           dist;
12180
12181                 dist = vm_vec_normalized_dir(&tvec1, &Pl_objp->pos, &ignore_objp->pos);
12182
12183                 if (dist < ignore_objp->radius*2 + 1500.0f) {
12184                         vm_vec_scale_add(&goal_point, &Pl_objp->pos, &tvec1, ignore_objp->radius*2 + 1400.0f);
12185                         if (dist < ignore_objp->radius*2 + 1300.0f)
12186                                 target_speed = sip->max_speed * (1.25f - dist/(ignore_objp->radius*2 + 1500.0f));
12187                 }
12188         }
12189
12190         Assert(vm_vec_mag(&aip->goal_point) >= 0.0f);           //      Supposedly detects bogus vector
12191
12192         turn_towards_tangent(Pl_objp, &goal_point, 10*objp->radius + 200.0f);
12193
12194         set_accel_for_target_speed(Pl_objp, target_speed);
12195
12196 }
12197
12198 #define SHIELD_BALANCE_RATE     0.2f            //      0.1f -> takes 10 seconds to equalize shield.
12199
12200 //      Transfer shield energy to most recently hit section from others.
12201 void ai_transfer_shield(object *objp, int quadrant_num)
12202 {
12203         int     i;
12204         float   transfer_amount;
12205         float   transfer_delta;
12206         ship_info       *sip;
12207         float   max_quadrant_strength;
12208
12209         sip = &Ship_info[Ships[objp->instance].ship_info_index];
12210         max_quadrant_strength = sip->shields/MAX_SHIELD_SECTIONS;
12211
12212         transfer_amount = 0.0f;
12213         transfer_delta = (SHIELD_BALANCE_RATE/2) * max_quadrant_strength;
12214
12215         if (objp->shields[quadrant_num] + (MAX_SHIELD_SECTIONS-1)*transfer_delta > max_quadrant_strength)
12216                 transfer_delta = (max_quadrant_strength - objp->shields[quadrant_num])/(MAX_SHIELD_SECTIONS-1);
12217
12218         for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12219                 if (i != quadrant_num) {
12220                         if (objp->shields[i] >= transfer_delta) {
12221                                 objp->shields[i] -= transfer_delta;
12222                                 transfer_amount += transfer_delta;
12223                         } else {
12224                                 transfer_amount += objp->shields[i];
12225                                 objp->shields[i] = 0.0f;
12226                         }
12227                 }
12228
12229         objp->shields[quadrant_num] += transfer_amount;
12230 }
12231
12232 void ai_balance_shield(object *objp)
12233 {
12234         int     i;
12235         float   shield_strength_avg;
12236         float   delta;
12237
12238
12239         shield_strength_avg = get_shield_strength(objp)/MAX_SHIELD_SECTIONS;
12240
12241         delta = SHIELD_BALANCE_RATE * shield_strength_avg;
12242
12243         for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12244                 if (objp->shields[i] < shield_strength_avg) {
12245                         add_shield_strength(objp, delta);
12246                         if (objp->shields[i] > shield_strength_avg)
12247                                 objp->shields[i] = shield_strength_avg;
12248                 } else {
12249                         add_shield_strength(objp, -delta);
12250                         if (objp->shields[i] < shield_strength_avg)
12251                                 objp->shields[i] = shield_strength_avg;
12252                 }
12253 }
12254
12255 //      Manage the shield for this ship.
12256 //      Try to max out the side that was most recently hit.
12257 void ai_manage_shield(object *objp, ai_info *aip)
12258 {
12259         ship_info *sip;
12260
12261         sip = &Ship_info[Ships[objp->instance].ship_info_index];
12262
12263         if (timestamp_elapsed(aip->shield_manage_timestamp)) {
12264                 float           delay;
12265
12266                 //      Scale time until next manage shield based on Skill_level.
12267                 //      Ships on player's team are treated as if Skill_level is average.
12268                 if (Ships[objp->instance].team != Player_ship->team){
12269                         delay = Shield_manage_delays[Game_skill_level];
12270                 } else {
12271                         delay = Shield_manage_delays[NUM_SKILL_LEVELS/2];
12272                 }
12273
12274                 //      Scale between 1x and 3x based on ai_class
12275                 delay = delay + delay * (float) (3*(Num_ai_classes - aip->ai_class - 1) / (Num_ai_classes - 1));
12276                 aip->shield_manage_timestamp = timestamp((int) (delay * 1000.0f));
12277
12278                 if (sip->flags & SIF_SMALL_SHIP) {
12279                         if (Missiontime - aip->last_hit_time < F1_0*10)
12280                                 ai_transfer_shield(objp, aip->last_hit_quadrant);
12281                         else
12282                                 ai_balance_shield(objp);
12283                 }
12284
12285                 // 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]));
12286         }
12287 }
12288
12289 //      See if object *objp should evade an incoming missile.
12290 //      *aip is the ai_info pointer within *objp.
12291 void ai_maybe_evade_locked_missile(object *objp, ai_info *aip)
12292 {
12293         ship                    *shipp;
12294         ship_info       *sip;
12295
12296         shipp = &Ships[objp->instance];
12297         sip = &Ship_info[shipp->ship_info_index];
12298
12299         //      Only small ships evade an incoming missile.  Why would a capital ship try to swerve?
12300         if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12301                 return;
12302         }
12303
12304         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
12305                 return;
12306         }
12307
12308         if (aip->nearest_locked_object != -1) {
12309                 object  *missile_objp;
12310
12311                 missile_objp = &Objects[aip->nearest_locked_object];
12312
12313                 if (Weapons[missile_objp->instance].homing_object != objp) {
12314                         //nprintf(("AI", "\nMissile lost home!"));
12315                         aip->nearest_locked_object = -1;
12316                         return;
12317                 }
12318
12319                 if ((missile_objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[missile_objp->instance].weapon_info_index].wi_flags & WIF_HOMING)) {
12320                         float dist = vm_vec_dist_quick(&missile_objp->pos, &objp->pos);
12321                         float dist2 = 4.0f  * vm_vec_mag_quick(&missile_objp->phys_info.vel);                   
12322                         if (dist < dist2) {
12323                                 switch (aip->mode) {
12324                                 //      If in AIM_STRAFE mode, don't evade if parent of weapon is targeted ship.
12325                                 case AIM_STRAFE:
12326                                         if ((missile_objp->parent != -1) && (missile_objp->parent == aip->target_objnum)) {
12327                                                 ;
12328                                         } else {
12329                                                 ;               //      Alan -- If you want to handle incoming weapons from someone other than the ship
12330                                                                 //      the strafing ship is attacking, do it here.
12331                                         }
12332                                         break;
12333                                 case AIM_CHASE:
12334                                         //      Don't always go into evade weapon mode.  Usually, a countermeasure gets launched.
12335                                         // If low on countermeasures, more likely to try to evade.  If 8+, never evade due to low cmeasures.
12336                                         if (((((Missiontime >> 18) ^ OBJ_INDEX(objp)) & 3) == 0) || 
12337                                                 (objp->phys_info.speed < 40.0f) ||
12338                                                 (frand() < 1.0f - (float) shipp->cmeasure_count/8.0f)) {
12339                                                 if (aip->submode != SM_ATTACK_FOREVER) {        //      SM_ATTACK_FOREVER means engines blown.
12340                                                         aip->submode = SM_EVADE_WEAPON;
12341                                                         aip->submode_start_time = Missiontime;
12342                                                 }
12343                                         }
12344                                         break;
12345                                 case AIM_DOCK:  //      Ships in dock mode can evade iif they are not currently repairing or docked.
12346                                         if (aip->ai_flags & (AIF_REPAIRING | AIF_DOCKED))
12347                                                 break;
12348                                 case AIM_GUARD:
12349                                         //      If in guard mode and far away from guard object, don't pursue guy that hit me.
12350                                         if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12351                                                 if (vm_vec_dist_quick(&objp->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12352                                                         return;
12353                                                 }
12354                                         }
12355                                 case AIM_EVADE:
12356                                 case AIM_GET_BEHIND:
12357                                 case AIM_STAY_NEAR:
12358                                 case AIM_STILL:
12359                                 case AIM_AVOID:
12360                                 case AIM_WAYPOINTS:
12361                                 case AIM_NONE:
12362                                 case AIM_BIGSHIP:
12363                                 case AIM_PATH:
12364                                 case AIM_BE_REARMED:
12365                                 case AIM_SAFETY:
12366                                 case AIM_BAY_EMERGE:
12367                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12368                                         aip->previous_mode = aip->mode;
12369                                         aip->previous_submode = aip->submode;
12370                                         aip->mode = AIM_EVADE_WEAPON;
12371                                         aip->submode = -1;
12372                                         aip->submode_start_time = Missiontime;
12373                                         aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Max time to evade.
12374                                         //nprintf(("AI", "%s Evade weapon in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
12375                                         break;
12376                                 case AIM_EVADE_WEAPON:          //      Note: We don't want to change mode on another evasion, or previous_mode will get bashed.
12377                                 case AIM_PLAY_DEAD:
12378                                 case AIM_BAY_DEPART:
12379                                 case AIM_SENTRYGUN:
12380                                         break;
12381                                 case AIM_WARP_OUT:
12382                                         break;
12383                                 default:
12384                                         Int3();                 //      Hey, what mode is it?
12385                                         break;
12386                                 }
12387                         }
12388                 } else {
12389                         aip->nearest_locked_object = -1;
12390                 }
12391         }
12392 }
12393
12394 //      Maybe evade a dumbfire weapon that was fired when Pl_objp was targeted.
12395 //      Have an 80% chance of evading in a second
12396 void maybe_evade_dumbfire_weapon(ai_info *aip)
12397 {
12398         //      Only small ships evade an incoming missile.  Why would a capital ship try to swerve?
12399         if (!(Ship_info[Ships[Pl_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12400                 return;
12401         }
12402
12403         //      Make sure in a mode in which we evade dumbfire weapons.
12404         switch (aip->mode) {
12405         case AIM_CHASE:
12406                 if (aip->submode == SM_ATTACK_FOREVER) {
12407                         return;
12408                 }
12409         case AIM_GUARD:
12410                 //      If in guard mode and far away from guard object, don't pursue guy that hit me.
12411                 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12412                         if (vm_vec_dist_quick(&Objects[Ships[aip->shipnum].objnum].pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12413                                 return;
12414                         }
12415                 }
12416         case AIM_STILL:
12417         case AIM_STAY_NEAR:
12418         case AIM_EVADE:
12419         case AIM_GET_BEHIND:
12420         case AIM_AVOID:
12421         case AIM_PATH:
12422         case AIM_NONE:
12423         case AIM_WAYPOINTS:
12424         case AIM_SAFETY:
12425                 break;
12426         case AIM_STRAFE:
12427         case AIM_BIGSHIP:
12428         case AIM_DOCK:
12429         case AIM_PLAY_DEAD:
12430         case AIM_EVADE_WEAPON:
12431         case AIM_BAY_EMERGE:
12432         case AIM_BAY_DEPART:
12433         case AIM_SENTRYGUN:
12434         case AIM_WARP_OUT:
12435                 return;
12436         default:
12437                 Int3(); //      Bogus mode!
12438                 return;
12439         }
12440
12441         if (is_instructor(&Objects[Ships[aip->shipnum].objnum]))
12442                 return; //      Instructor doesn't evade.
12443
12444         float t = ai_endangered_by_weapon(aip);
12445         if ((t > 0.0f) && (t < 1.0f)) {
12446         // Check if this weapon is from a large ship Pl_objp is attacking... if so, enter strafe mode
12447                 if ( ai_big_maybe_enter_strafe_mode(Pl_objp, aip->danger_weapon_objnum) ) {
12448                         return;
12449                 }
12450
12451                 switch (aip->mode) {
12452                 case AIM_CHASE:
12453                         switch (aip->submode) {
12454                         case SM_EVADE:
12455                         case SM_ATTACK_FOREVER:
12456                         case SM_AVOID:
12457                         case SM_GET_AWAY:
12458                         case SM_EVADE_WEAPON:
12459                                 break;
12460                         default:
12461                                 if (ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
12462                                         //mprintf(("Ship %s entered super mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12463                                         aip->submode = SM_SUPER_ATTACK;
12464                                         aip->submode_start_time = Missiontime;
12465                                         aip->last_attack_time = Missiontime;
12466                                 } else {
12467                                         //mprintf(("Ship %s entered dumbfire evade mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12468                                         aip->submode = SM_EVADE_WEAPON;
12469                                         aip->submode_start_time = Missiontime;
12470                                 }
12471                                 break;
12472                         }
12473                         break;
12474                 case AIM_GUARD:
12475                 case AIM_STILL:
12476                 case AIM_STAY_NEAR:
12477                 case AIM_EVADE:
12478                 case AIM_GET_BEHIND:
12479                 case AIM_AVOID:
12480                 case AIM_PATH:
12481                 case AIM_NONE:
12482                 case AIM_WAYPOINTS:
12483                 case AIM_SAFETY:
12484                         if (!(aip->ai_flags & (AIF_NO_DYNAMIC | AIF_KAMIKAZE)) && (Ship_info[Ships[aip->shipnum].ship_info_index].flags & SIF_SMALL_SHIP)) {
12485                                 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12486                                 aip->previous_mode = aip->mode;
12487                                 aip->previous_submode = aip->submode;
12488                                 aip->mode = AIM_EVADE_WEAPON;
12489                                 aip->submode = -1;
12490                                 aip->submode_start_time = Missiontime;
12491                                 aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Evade for up to five seconds.
12492                         }
12493                         break;
12494                 case AIM_STRAFE:
12495                 case AIM_BIGSHIP:
12496                 case AIM_DOCK:
12497                 case AIM_PLAY_DEAD:
12498                 case AIM_EVADE_WEAPON:
12499                 case AIM_BAY_EMERGE:
12500                 case AIM_BAY_DEPART:
12501                 case AIM_SENTRYGUN:
12502                         break;
12503                 default:
12504                         Int3(); //      Bogus mode!
12505                 }
12506         }
12507 }
12508
12509 // determine what path to use when emerging from a fighter bay
12510 // input:       pl_objp =>      pointer to object for ship that is arriving
12511 //                              pos             =>      output parameter, it is the starting world pos for path choosen
12512 //                              v.fvec          =>      output parameter, this is the forward vector that ship has when arriving
12513 //
12514 // exit:                -1              =>      path could not be located
12515 //                               0              => success
12516 int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, vector *pos, vector *fvec)
12517 {
12518         int                     path_index, sb_path_index;
12519         ship                    *parent_sp = NULL;
12520         polymodel       *pm;
12521         ai_info         *aip;
12522         ship_bay                *sb;
12523         pnode                   *pnp;
12524         vector          *next_point;
12525
12526         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12527
12528         if ( parent_objnum == -1 ) {
12529                 Int3();
12530                 return -1;
12531         }
12532
12533         parent_sp = &Ships[Objects[parent_objnum].instance];
12534
12535         Assert(parent_sp != NULL);
12536         pm = model_get( parent_sp->modelnum );
12537         sb = pm->ship_bay;
12538
12539         if ( sb == NULL ) 
12540                 return -1;
12541
12542         if ( sb->num_paths <= 0 ) 
12543                 return -1;
12544
12545         // try to find a bay path that is not taken
12546         path_index = -1;
12547         sb_path_index = Ai_last_arrive_path++;
12548
12549         if ( sb_path_index >= sb->num_paths ) {
12550                 sb_path_index=0;
12551                 Ai_last_arrive_path=0;
12552         }
12553
12554         path_index = sb->paths[sb_path_index];
12555         if ( path_index == -1 ) 
12556                 return -1;
12557
12558         // create the path for pl_objp to follow
12559         create_model_exit_path(pl_objp, &Objects[parent_objnum], path_index, pm->paths[path_index].nverts);
12560         
12561         // Set this flag, so we don't bother recreating the path... we won't need to update the path
12562         // that has just been created.
12563 //      aip->ai_flags |= AIF_USE_STATIC_PATH;
12564
12565         // now return to the caller what the starting world pos and starting fvec for the ship will be
12566         Assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
12567         pnp = &Path_points[aip->path_start];
12568         *pos = pnp->pos;
12569
12570         // calc the forward vector using the starting two points of the path
12571         pnp = &Path_points[aip->path_start+1];
12572         next_point = &pnp->pos;
12573         vm_vec_normalized_dir(fvec, next_point, pos);
12574
12575         // record the parent objnum, since we'll need it once we're done with following the path
12576         aip->goal_objnum = parent_objnum;
12577         aip->goal_signature = Objects[parent_objnum].signature;
12578         aip->mode = AIM_BAY_EMERGE;
12579         aip->submode_start_time = Missiontime;
12580
12581         // set up starting vel
12582         vector vel;
12583         float speed;
12584         speed = Ship_info[Ships[pl_objp->instance].ship_info_index].max_speed;
12585         vel = *fvec;
12586         vm_vec_scale( &vel, speed );
12587         pl_objp->phys_info.vel = vel;
12588         pl_objp->phys_info.desired_vel = vel;
12589         pl_objp->phys_info.prev_ramp_vel.xyz.x = 0.0f;
12590         pl_objp->phys_info.prev_ramp_vel.xyz.y = 0.0f;
12591         pl_objp->phys_info.prev_ramp_vel.xyz.z = speed;
12592         pl_objp->phys_info.forward_thrust = 0.0f;               // How much the forward thruster is applied.  0-1.
12593
12594         return 0;       
12595 }
12596
12597 // clean up path data used for emerging from a fighter bay
12598 void ai_emerge_bay_path_cleanup(ai_info *aip)
12599 {
12600         aip->path_start = -1;
12601         aip->path_cur = -1;
12602         aip->path_length = 0;
12603         aip->mode = AIM_NONE;
12604 }
12605
12606 // handler for AIM_BAY_EMERGE
12607 void ai_bay_emerge()
12608 {
12609         ai_info *aip;
12610         int             parent_died=0;
12611
12612         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12613
12614         // if no path to follow, leave this mode
12615         if ( aip->path_start < 0 ) {
12616                 aip->mode = AIM_NONE;
12617                 return;
12618         }
12619
12620         // ensure parent ship is still alive
12621         if ( aip->goal_objnum < 0 ) {
12622                 parent_died=1;
12623         } 
12624         if ( !parent_died ) {
12625                 if ( Objects[aip->goal_objnum].signature != aip->goal_signature ) {
12626                         parent_died=1;
12627                 }
12628         }
12629
12630         if ( !parent_died ) {
12631                 Assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
12632                 if ( Ships[Objects[aip->goal_objnum].instance].flags & SF_DYING ) {
12633                         parent_died = 1;
12634                 }
12635         }
12636
12637         if ( parent_died ) {
12638                 ai_emerge_bay_path_cleanup(aip);
12639                 return;
12640         }
12641
12642         // follow the path to the final point
12643         ai_path();
12644
12645         // New test: must have been in AI_EMERGE mode for at least 10 seconds, and be a minimum distance from the start point
12646         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)) {
12647                 // erase path
12648                 ai_emerge_bay_path_cleanup(aip);
12649         }
12650
12651         // 2-25-99: Need this check to fix an assert for supercap ships... maybe we'll only do this check for supercaps 
12652         if (aip->path_cur > (aip->path_start+aip->path_length-1)) {
12653                 ai_emerge_bay_path_cleanup(aip);
12654         }       
12655 }
12656
12657 // Select the closest depart path
12658 //
12659 //      input:  aip     =>              ai info pointer to ship seeking to depart
12660 //                              pm              =>              pointer to polymodel for the ship contining the ship bay/depart paths
12661 //
12662 // exit:                >=0     =>              ship bay path index for depart path (ie index into sb->paths[])
12663 //                              -1              =>              no path could be found
12664 //
12665 // NOTE: this function should only be used for calculating closest depart paths for ai mode
12666 //                      AI_BAY_DEPART.  It tries to find the closest path that isn't already in use
12667 int ai_find_closest_depart_path(ai_info *aip, polymodel *pm)
12668 {
12669         int                     i, j, best_path, best_free_path;
12670         float                   dist, min_dist, min_free_dist;
12671         vector          *source;
12672         model_path      *mp;
12673         ship_bay                *sb;
12674
12675         sb = pm->ship_bay;
12676
12677         best_free_path = best_path = -1;
12678         min_free_dist = min_dist = 1e20f;
12679         Assert(aip->shipnum >= 0);
12680         source = &Objects[Ships[aip->shipnum].objnum].pos;
12681
12682         for ( i = 0; i < sb->num_paths; i++ ) {
12683
12684
12685                 mp = &pm->paths[sb->paths[i]];
12686                 for ( j = 0; j < mp->nverts; j++ ) {
12687                         dist = vm_vec_dist_squared(source, &mp->verts[j].pos);
12688
12689                         if ( dist < min_dist ) {
12690                                 min_dist = dist;
12691                                 best_path = i;
12692                         }
12693
12694                         // If this is a free path
12695                         if ( !(sb->depart_flags & (1<<i)) ) {
12696                                 if ( dist < min_free_dist ) {
12697                                         min_free_dist = dist;
12698                                         best_free_path = i;
12699                                 }
12700                         }
12701                 }
12702         }
12703
12704         if ( best_free_path >= 0 ) {
12705                 return best_free_path;          
12706         }
12707
12708         return best_path;
12709 }
12710
12711 // determine what path to use when trying to depart to a fighter bay
12712 // NOTE: this should be called when AIM_BAY_DEPART mode is set
12713 //
12714 // input:       pl_objp =>      pointer to object for ship that is departing
12715 //
12716 // exit:                -1      =>      could not find depart path
12717 //                              0       => found depart path
12718 int ai_acquire_depart_path(object *pl_objp, int parent_objnum)
12719 {
12720         int                     objnum, path_index;
12721         polymodel       *pm;
12722         ai_info         *aip;
12723         ship                    *sp;
12724         ship_bay                *sb;
12725
12726         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12727
12728         if ( parent_objnum == -1 ) {
12729                 ship_obj        *so;
12730
12731                 // for now just locate a captial ship on the same team:
12732                 so = GET_FIRST(&Ship_obj_list);
12733                 objnum = -1;
12734                 while(so != END_OF_LIST(&Ship_obj_list)){
12735                         sp = &Ships[Objects[so->objnum].instance];
12736                         if ( (Ship_info[sp->ship_info_index].flags & (SIF_HUGE_SHIP)) && (sp->team == Ships[pl_objp->instance].team) ) {
12737                                 objnum = so->objnum;
12738                                 break;
12739                         }
12740                         so = GET_NEXT(so);
12741                 } 
12742         } else {
12743                 objnum = parent_objnum;
12744         }
12745
12746         aip->path_start = -1;
12747
12748         if ( objnum == -1 )
12749                 return -1;
12750
12751         pm = model_get( Ships[Objects[objnum].instance].modelnum );
12752         sb = pm->ship_bay;
12753
12754         if ( sb == NULL ) 
12755                 return -1;
12756         if ( sb->num_paths <= 0 ) 
12757                 return -1;
12758
12759 /*
12760         
12761         path_index = -1;
12762         for ( i = 0; i < sb->num_paths; i++ ) {
12763                 if ( !(sb->depart_flags & (1<<i)) ) {
12764                         sb->depart_flags |= (1<<i);
12765                         path_index = sb->paths[i];
12766                         aip->submode_parm0 = i;                 // use mode-specific parameter to record ship bay path index
12767                         break;
12768                 }
12769         }
12770 */
12771         
12772         // take the closest path we can find
12773         int ship_bay_path;
12774         ship_bay_path = ai_find_closest_depart_path(aip, pm);
12775         path_index = sb->paths[ship_bay_path];
12776         aip->submode_parm0 = ship_bay_path;
12777         sb->depart_flags |= (1<<ship_bay_path);
12778
12779         if ( path_index == -1 ) {
12780                 return -1;
12781         }
12782
12783         Assert(pm->n_paths > path_index);
12784         ai_find_path(pl_objp, objnum, path_index, 0);
12785
12786         // Set this flag, so we don't bother recreating the path... we won't need to update the path
12787         // that has just been created.
12788         aip->ai_flags &= ~AIF_USE_STATIC_PATH;
12789
12790         aip->goal_objnum = objnum;
12791         aip->goal_signature = Objects[objnum].signature;
12792         aip->mode = AIM_BAY_DEPART;
12793
12794         Ships[pl_objp->instance].flags |= SF_DEPART_DOCKBAY;
12795         return 0;
12796 }
12797
12798 // handler for AIM_BAY_DEPART
12799 void ai_bay_depart()
12800 {
12801         ai_info *aip;
12802
12803         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12804
12805         // if no path to follow, leave this mode
12806         if ( aip->path_start < 0 ) {
12807                 aip->mode = AIM_NONE;
12808                 return;
12809         }
12810
12811         // check if parent ship still exists, if not abort depart 
12812         if ( aip->goal_signature != Objects[aip->goal_objnum].signature ) {
12813                 aip->mode = AIM_NONE;
12814                 return;
12815         }
12816
12817         // follow the path to the final point
12818         ai_path();
12819
12820         // if the final point is reached, let default AI take over
12821         if ( aip->path_cur >= (aip->path_start+aip->path_length) ) {
12822                 polymodel       *pm;
12823                 ship_bay                *sb;
12824
12825                 pm = model_get( Ships[Objects[aip->goal_objnum].instance].modelnum );
12826                 sb = pm->ship_bay;
12827                 if ( sb != NULL ) {
12828                         sb->depart_flags &= ~(1<<aip->submode_parm0);
12829                 }
12830
12831                 // make ship disappear
12832                 Pl_objp->flags |= OF_SHOULD_BE_DEAD;
12833                 ship_departed( Pl_objp->instance );
12834
12835                 // clean up path stuff
12836                 aip->path_start = -1;
12837                 aip->path_cur = -1;
12838                 aip->path_length = 0;
12839                 aip->mode = AIM_NONE;
12840         }
12841 }
12842
12843 // Handler for AIM_SENTRYGUN.  This AI mode is for sentry guns only (ie floating turrets).
12844 void ai_sentrygun()
12845 {
12846         // Nothing to do here.  Turret firing is handled via process_subobjects().
12847         // If you want the sentry guns to do anything beyond firing their turrets at enemies, add it here!
12848 }
12849
12850 //      --------------------------------------------------------------------------
12851 //      Execute behavior given by aip->mode.
12852 void ai_execute_behavior(ai_info *aip)
12853 {
12854         switch (aip->mode) {
12855         case AIM_CHASE:
12856                 if (En_objp) {
12857                         ai_chase();
12858                 } else if (aip->submode == SM_EVADE_WEAPON) {
12859                         evade_weapon();
12860                         // maybe reset submode
12861                         if (aip->danger_weapon_objnum == -1) {
12862                                 aip->submode = SM_ATTACK;
12863                                 aip->submode_start_time = Missiontime;
12864                                 aip->last_attack_time = Missiontime;
12865                         }
12866                 } else {
12867                         //      Don't circle if this is the instructor.
12868                         ship    *shipp = &Ships[aip->shipnum];
12869                         ship_info       *sip = &Ship_info[shipp->ship_info_index];
12870
12871                         if (strnicmp(shipp->ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME))) {
12872                                 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
12873                                         aip->mode = AIM_NONE;
12874                                 } else {
12875                                         ai_chase_circle(Pl_objp);
12876                                 }
12877                         }
12878                 }
12879                 break;
12880         case AIM_EVADE:
12881                 if (En_objp) {
12882                         ai_evade();
12883                 } else {
12884                         vector  tvec;
12885                         vm_vec_scale_add(&tvec, &Pl_objp->pos, &Pl_objp->orient.v.rvec, 100.0f);
12886                         turn_towards_point(Pl_objp, &tvec, NULL, 0.0f);
12887                         accelerate_ship(aip, 0.5f);
12888                 }
12889                 break;
12890         case AIM_STILL:
12891                 ai_still();
12892                 break;
12893         case AIM_STAY_NEAR:
12894                 ai_stay_near();
12895                 break;
12896         case AIM_GUARD:
12897                 ai_guard();
12898                 break;
12899         case AIM_WAYPOINTS:
12900                 ai_waypoints();
12901                 break;
12902         case AIM_DOCK:
12903                 ai_dock();
12904                 break;
12905         case AIM_NONE:
12906                 // ai_formation();
12907                 break;
12908         case AIM_BIGSHIP:
12909                 ai_big_ship(Pl_objp);
12910                 break;
12911         case AIM_PATH: {
12912                 int path_num;
12913                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], 0);
12914                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
12915                 ai_path();
12916                 break;
12917         }
12918         case AIM_SAFETY:
12919                 ai_safety();
12920                 break;
12921         case AIM_EVADE_WEAPON:
12922                 evade_weapon();
12923                 break;
12924         case AIM_STRAFE:
12925                 if (En_objp) {
12926                         Assert(En_objp->type == OBJ_SHIP);
12927                         ai_big_strafe();        // strafe a big ship
12928                 } else {
12929                         aip->mode = AIM_NONE;
12930                 }
12931                 break;
12932         case AIM_BAY_EMERGE:
12933                 ai_bay_emerge();
12934                 break;
12935         case AIM_BAY_DEPART:
12936                 ai_bay_depart();
12937                 break;
12938         case AIM_SENTRYGUN:
12939                 ai_sentrygun();
12940                 break;
12941         case AIM_WARP_OUT:
12942                 break;          //      Note, handled directly from ai_frame().
12943         default:
12944                 Int3();         //      This should never happen -- MK, 5/12/97 
12945                 break;
12946         }
12947
12948         if ( !(ship_get_SIF(aip->shipnum) & SIF_NOT_FLYABLE) ) {
12949                 maybe_evade_dumbfire_weapon(aip);
12950         }
12951 }
12952
12953 //      Auxiliary function for maybe_request_support.
12954 //      Return 1 if subsystem "type" is worthy of repair, else return 0.
12955 //      Since subsystems cannot be repaired if they are at 0 strength, don't return 1 if subsystem is dead.
12956 int mrs_subsystem(ship *shipp, int type)
12957 {
12958         float   t;
12959
12960         t = ship_get_subsystem_strength(shipp, type);
12961
12962         if (t > 0.0f) {
12963                 return (int) ((1.0f - t) * 3);
12964         } else {
12965                 return 3;
12966         }
12967 }
12968
12969 //      Return number of ships on *objp's team that are currently rearming.
12970 int num_allies_rearming(object *objp)
12971 {
12972         ship_obj        *so;
12973         int             team;
12974         int             count = 0;
12975
12976         team = Ships[objp->instance].team;
12977
12978         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
12979                 object  *A;
12980                 
12981                 Assert (so->objnum != -1);
12982                 A = &Objects[so->objnum];
12983
12984                 if (Ships[A->instance].team == team) {
12985                         if (Ai_info[Ships[A->instance].ai_index].ai_flags & (AIF_REPAIRING | AIF_AWAITING_REPAIR)) {
12986                                 count++;
12987                         }
12988                 }
12989         }
12990
12991         return count;
12992 }
12993
12994
12995 //      Maybe ship *objp should request support (rearm/repair).
12996 //      If it does, return TRUE, else return FALSE.
12997 int maybe_request_support(object *objp)
12998 {
12999         ship_info       *sip;
13000         ship                    *shipp;
13001         ai_info         *aip;
13002         int                     desire;
13003
13004         Assert(objp->type == OBJ_SHIP);
13005         shipp = &Ships[objp->instance];
13006         aip = &Ai_info[shipp->ai_index];
13007         sip = &Ship_info[shipp->ship_info_index];
13008
13009         if (!timestamp_elapsed(aip->next_rearm_request_timestamp))
13010                 return 0;
13011
13012         //      Only fighters and bombers request support.
13013         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER)))
13014                 return 0;
13015
13016         //      A ship that is currently awaiting does not need support!
13017         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
13018                 return 0;
13019
13020         if (!is_support_allowed(objp))
13021                 return 0;
13022
13023         //if (shipp->team != TEAM_FRIENDLY)
13024         //      return 0;
13025
13026         //      Compute a desire value.
13027         //      Desire of 0 means no reason to request support.
13028         //      1 is slight, 2 more, etc.  Maximum is around 20.  Anything larger than 3 is pretty strong.
13029         desire = 0;
13030
13031         //      Set desire based on hull strength.
13032         //      No: We no longer repair hull, so this would cause repeated repair requests.
13033         //desire += 6 - (int) ((objp->hull_strength/sip->initial_hull_strength) * 6.0f);
13034
13035         //      Set desire based on key subsystems.
13036         desire += 2*mrs_subsystem(shipp, SUBSYSTEM_ENGINE);     //      Note, disabled engine forces repair request, regardless of nearby enemies.
13037         desire += mrs_subsystem(shipp, SUBSYSTEM_COMMUNICATION);
13038         desire += mrs_subsystem(shipp, SUBSYSTEM_WEAPONS);
13039         desire += mrs_subsystem(shipp, SUBSYSTEM_SENSORS);
13040
13041         //      Set desire based on percentage of secondary weapons.
13042         ship_weapon *swp = &shipp->weapons;
13043
13044         for ( int i = 0; i < swp->num_secondary_banks; i++ ) {
13045                 if (swp->secondary_bank_start_ammo[i] > 0) {
13046 //                      float r = (float) swp->secondary_bank_ammo[i]*Weapon_info[swp->secondary_bank_weapons[i]].cargo_size/swp->secondary_bank_capacity[i];
13047                         float r = (float) swp->secondary_bank_ammo[i]/swp->secondary_bank_start_ammo[i];
13048                         desire += (int) ((1.0f - r) * 3.0f);
13049                 }
13050         }
13051
13052         //      If no reason to repair, don't bother to see if it's safe to repair.
13053         if (desire == 0){
13054                 return 0;
13055         }
13056
13057         //      Compute danger threshold.
13058         //      Balance this with desire and maybe request support.
13059         if (ai_good_time_to_rearm( objp )) {
13060                 ai_issue_rearm_request(objp);
13061                 return 1;
13062         } else if (num_allies_rearming(objp) < 2) {
13063                 if (desire >= 8) {      //      guarantees disabled will cause repair request
13064                         ai_issue_rearm_request(objp);
13065                 } else if (desire >= 3) {               //      >= 3 means having a single subsystem fully blown will cause repair.
13066                         int     count;
13067                         int objnum = find_nearby_hostile(OBJ_INDEX(objp), get_enemy_team_mask(OBJ_INDEX(objp)), 2000.0f, &count);
13068
13069                         if ((objnum == -1) || (count < 2) || (vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos) > 3000.0f*count/desire)) {
13070                                 ai_issue_rearm_request(objp);
13071                                 return 1;
13072                         } else {
13073                                 //nprintf(("AI", "Would like to rearm, but enemy only %7.3f units away.\n", vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos)));
13074                         }
13075                 }
13076         }
13077
13078         return 0;
13079
13080 }
13081
13082 void ai_set_mode_warp_out(object *objp, ai_info *aip)
13083 {
13084         ai_abort_rearm_request(objp);
13085         if (aip->mode != AIM_WARP_OUT) {
13086                 aip->mode = AIM_WARP_OUT;
13087                 aip->submode = AIS_WARP_1;
13088         }
13089 }
13090
13091 //      Maybe warp ship out.
13092 //      Shivan and HoL fighter/bomber warp out if their weapons subsystems have been destroyed.
13093 void ai_maybe_warp_out(object *objp)
13094 {
13095         ship    *shipp;
13096
13097         // don't do anything if in a training mission.
13098         if ( The_mission.game_type & MISSION_TYPE_TRAINING )
13099                 return;
13100
13101         Assert(objp->type == OBJ_SHIP);
13102
13103         shipp = &Ships[objp->instance];
13104         ai_info *aip = &Ai_info[shipp->ai_index];
13105
13106         if (aip->mode == AIM_WARP_OUT)
13107                 return;
13108
13109         //      If a support ship with no goals and low hull, warp out.  Be sure that there are no pending goals
13110         // in the support ships ai_goal array.  Just process this ships goals.
13111         ship_info       *sip = &Ship_info[shipp->ship_info_index];
13112         if (sip->flags & SIF_SUPPORT) {
13113                 if ( timestamp_elapsed(aip->warp_out_timestamp) ) {
13114                         ai_process_mission_orders( OBJ_INDEX(objp), aip );
13115                         if ( (aip->dock_objnum == -1) && (objp->hull_strength/sip->initial_hull_strength < 0.25f) ) {
13116                                 ai_set_mode_warp_out(objp, aip);
13117                         }
13118                 }
13119         }
13120
13121         //      Friendly don't warp out, they'll eventually request support.
13122         if (shipp->team == TEAM_FRIENDLY)
13123                 return;
13124
13125         if (!(shipp->flags & SF_DEPARTING)) {
13126                 ship_info       *sip;
13127
13128                 sip = &Ship_info[shipp->ship_info_index];
13129                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
13130                         if (aip->warp_out_timestamp == 0) {
13131                                 //if (ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS) == 0.0f) {
13132                                 //      aip->warp_out_timestamp = timestamp(((myrand() % 10) + 10) * 1000);
13133                                 //}
13134                         } else if (timestamp_elapsed(aip->warp_out_timestamp)) {
13135                                 ai_set_mode_warp_out(objp, aip);
13136                         }
13137                 }
13138         }
13139 }
13140
13141 //      Warp this ship out.
13142 void ai_warp_out(object *objp)
13143 {
13144         // if dying, don't warp out.
13145         if (Ships[objp->instance].flags & SF_DYING) {
13146                 return;
13147         }
13148
13149         ai_info *aip;
13150
13151         aip = &Ai_info[Ships[objp->instance].ai_index];
13152
13153         switch (aip->submode) {
13154         case AIS_WARP_1:
13155                 aip->force_warp_time = timestamp(10*1000);      //      Try to avoid a collision for up to ten seconds.
13156                 aip->submode = AIS_WARP_2;
13157                 break;
13158         case AIS_WARP_2:                        //      Make sure won't collide with any object.
13159                 if (timestamp_elapsed(aip->force_warp_time) || !collide_predict_large_ship(objp, objp->radius*2.0f + 100.0f)) {
13160                         aip->submode = AIS_WARP_3;
13161
13162                         // maybe recalculate collision pairs.
13163                         if (ship_get_warp_speed(objp) > ship_get_max_speed(&Ships[objp->instance])) {
13164                                 // recalculate collision pairs
13165                                 OBJ_RECALC_PAIRS(objp); 
13166                         }
13167
13168                         aip->force_warp_time = timestamp(4*1000);               //      Try to attain target speed for up to 4 seconds.
13169                 } else {
13170                         vector  goal_point;
13171                         vm_vec_scale_add(&goal_point, &objp->pos, &objp->orient.v.uvec, 100.0f);
13172                         turn_towards_point(objp, &goal_point, NULL, 0.0f);
13173                         accelerate_ship(aip, 0.0f);
13174                 }
13175                 break;
13176         case AIS_WARP_3:
13177                 //      Rampup desired_vel in here from current to desired velocity and set PF_USE_VEL. (not sure this is the right flag)
13178                 //      desired velocity is computed in shipfx_calculate_warp_time().  See shipfx#572 for sample code.
13179                 float   speed, goal_speed;
13180                 float shipfx_calculate_warp_speed(object*);
13181                 goal_speed = shipfx_calculate_warp_speed(objp);
13182
13183                 // HUGE ships go immediately to AIS_WARP_4
13184                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_HUGE_SHIP) {
13185                         aip->submode = AIS_WARP_4;
13186                         break;
13187                 }
13188                 //compute_warpout_stuff(objp, &goal_speed, &warp_time, &warp_pos);
13189                 //goal_speed = 80.0f;
13190                 //set_accel_for_target_speed(objp, 40.0f);
13191                 // 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
13192                 speed = goal_speed * flFrametime + objp->phys_info.speed * (1.0f - flFrametime);
13193                 vm_vec_copy_scale(&objp->phys_info.vel, &objp->orient.v.fvec, speed);
13194                 objp->phys_info.desired_vel = objp->phys_info.vel;
13195                 // nprintf(("AI", "Frame %i, speed = %7.3f, goal = %7.3f\n", Framecount, vm_vec_mag_quick(&objp->phys_info.vel), goal_speed));
13196                 if (timestamp_elapsed(aip->force_warp_time) || (fl_abs(objp->phys_info.speed - goal_speed) < 2.0f))
13197                         aip->submode = AIS_WARP_4;
13198                 break;
13199         case AIS_WARP_4: {
13200                 shipfx_warpout_start(objp);
13201                 aip->submode = AIS_WARP_5;
13202                 break;
13203         }
13204         case AIS_WARP_5:
13205                 break;
13206         default:
13207                 Int3();         //      Illegal submode for warping out.
13208         }
13209 }
13210
13211 //      Return object index of weapon that could produce a shockwave that should be known about to *objp.
13212 //      Return nearest one.
13213 int ai_find_shockwave_weapon(object *objp, ai_info *aip)
13214 {
13215         missile_obj     *mo;
13216         float   nearest_dist = 999999.9f;
13217         int     nearest_index = -1;
13218
13219         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
13220                 object          *A;
13221                 weapon          *wp;
13222                 weapon_info     *wip;
13223         
13224                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
13225                 A = &Objects[mo->objnum];
13226
13227                 Assert(A->type == OBJ_WEAPON);
13228                 Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
13229                 wp = &Weapons[A->instance];
13230                 wip = &Weapon_info[wp->weapon_info_index];
13231                 Assert( wip->subtype == WP_MISSILE );
13232
13233                 if (wip->shockwave_speed > 0.0f) {
13234                         float   dist;
13235
13236                         dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13237                         if (dist < nearest_dist) {
13238                                 nearest_dist = dist;
13239                                 nearest_index = mo->objnum;
13240                         }
13241                 }
13242         }
13243
13244         return nearest_index;
13245
13246 }
13247
13248 #define EVADE_SHOCKWAVE_DAMAGE_THRESHOLD                100.0f
13249
13250 //      Tell all ships to avoid a big ship that is blowing up.
13251 //      Only avoid if shockwave is fairly large.
13252 //      OK to tell everyone to avoid.  If they're too far away, that gets cleaned up in the frame interval.
13253 void ai_announce_ship_dying(object *dying_objp)
13254 {
13255         float damage = ship_get_exp_damage(dying_objp);
13256         if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {
13257                 ship_obj        *so;
13258
13259                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13260                         if (Ship_info[Ships[Objects[so->objnum].instance].ship_info_index].flags & (SIF_SMALL_SHIP | SIF_FREIGHTER)) {
13261                                 ai_info *aip;
13262
13263                                 aip = &Ai_info[Ships[Objects[so->objnum].instance].ai_index];
13264
13265                                 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
13266                                         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_SHIP;
13267                                 }
13268                         }
13269                 }
13270         }
13271 }
13272
13273
13274 //      Return object index of weapon that could produce a shockwave that should be known about to *objp.
13275 //      Return nearest one.
13276 int ai_find_shockwave_ship(object *objp, ai_info *aip)
13277 {
13278         ship_obj        *so;
13279         float   nearest_dist = 999999.9f;
13280         int     nearest_index = -1;
13281
13282         for ( so = GET_NEXT(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13283                 object          *A;
13284                 ship                    *shipp;
13285         
13286                 Assert(so->objnum >= 0 && so->objnum < MAX_OBJECTS);
13287                 A = &Objects[so->objnum];
13288
13289                 Assert(A->type == OBJ_SHIP);
13290                 Assert((A->instance >= 0) && (A->instance < MAX_SHIPS));
13291                 shipp = &Ships[A->instance];
13292                 //      Only look at objects in the process of dying.
13293                 if (shipp->flags & SF_DYING) {
13294                         float damage = ship_get_exp_damage(objp);
13295
13296                         if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {               //      Only evade quite large blasts
13297                                 float   dist;
13298
13299                                 dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13300                                 if (dist < nearest_dist) {
13301                                         nearest_dist = dist;
13302                                         nearest_index = so->objnum;
13303                                 }
13304                         }
13305                 }
13306         }
13307
13308         return nearest_index;
13309
13310 }
13311
13312 int aas_1(object *objp, ai_info *aip, vector *safe_pos)
13313 {
13314         // MAKE SURE safe_pos DOES NOT TAKE US TOWARD THE A SHIP WE'RE ATTACKING.
13315         if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_WEAPON) {
13316                 //      If we don't currently know of a weapon to avoid, try to find one.
13317                 //      If we can't find one, then clear the bit so we don't keep coming here.
13318                 if (aip->shockwave_object == -1) {
13319                         int shockwave_weapon = ai_find_shockwave_weapon(objp, aip);
13320                         if (shockwave_weapon == -1) {
13321                                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13322                                 return 0;
13323                         } else {
13324                                 aip->shockwave_object = shockwave_weapon;
13325                         }
13326                 }
13327
13328                 //      OK, we have reason to believe we should avoid aip->shockwave_object.
13329                 Assert(aip->shockwave_object > -1);
13330                 object  *weapon_objp = &Objects[aip->shockwave_object];
13331                 if (weapon_objp->type != OBJ_WEAPON) {
13332                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13333                         aip->shockwave_object = -1;
13334                         return 0;
13335                 }
13336
13337                 weapon  *weaponp = &Weapons[weapon_objp->instance];
13338                 weapon_info     *wip = &Weapon_info[weaponp->weapon_info_index];
13339                 object *target_ship_obj = NULL;
13340
13341                 if (wip->shockwave_speed == 0.0f) {
13342                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13343                         aip->shockwave_object = -1;
13344                         return 0;
13345                 }
13346
13347                 float   danger_dist;
13348                 vector  expected_pos;           //      Position at which we expect the weapon to detonate.
13349                 int             pos_set = 0;
13350
13351                 danger_dist = wip->outer_radius;
13352                 //      Set predicted position of detonation.
13353                 //      If an aspect locked missile, assume it will detonate at the homing position.
13354                 //      If not, which is not possible in a default FreeSpace weapon, then predict it will detonate at some
13355                 //      time in the future, this time based on max lifetime and life left.
13356                 if (wip->wi_flags & WIF_HOMING_ASPECT) {
13357                         expected_pos = weaponp->homing_pos;
13358                         if (weaponp->homing_object && weaponp->homing_object->type == OBJ_SHIP) {
13359                                 target_ship_obj = weaponp->homing_object;
13360                         }
13361                         pos_set = 1;
13362                         if (IS_VEC_NULL(&weaponp->homing_pos)) {
13363                                 pos_set = 0;
13364                                 if (weaponp->target_num != -1) {
13365                                         if (Objects[weaponp->target_num].type == OBJ_SHIP) {
13366                                                 target_ship_obj = &Objects[weaponp->target_num];
13367                                                 expected_pos = target_ship_obj->pos;
13368                                                 pos_set = 1;
13369                                         }
13370                                 }
13371                         }
13372                 }
13373
13374                 if (!pos_set) {
13375                         float   time_scale;
13376
13377                         if (wip->lifetime - weaponp->lifeleft > 5.0f) {
13378                                 time_scale = 1.0f;
13379                         } else {
13380                                 time_scale = weaponp->lifeleft/2.0f;
13381                         }
13382
13383                         vm_vec_scale_add(&expected_pos, &weapon_objp->pos, &weapon_objp->orient.v.fvec, time_scale);
13384                 }
13385
13386                 //      See if too far away to care about shockwave.
13387                 if (vm_vec_dist_quick(&objp->pos, &expected_pos) > danger_dist*2.0f) {
13388                         //aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13389                         return 0;
13390                 } else {
13391                         // try to find a safe position
13392                         vector vec_from_exp;
13393                         float dir = 1.0f;
13394                         vm_vec_sub(&vec_from_exp, &objp->pos, &expected_pos);
13395                         float dot = vm_vec_dotprod(&vec_from_exp, &weapon_objp->orient.v.fvec);
13396                         if (dot > -30) {
13397                                 // if we're already on the other side of the explosion, don't try to fly behind it
13398                                 dir = -1.0f;
13399                         }
13400
13401                         //      Fly towards a point behind the weapon.
13402                         vm_vec_scale_add(safe_pos, &weapon_objp->pos, &weapon_objp->orient.v.fvec, -50000.0f*dir);
13403
13404                         // verify safe_pos will not make us collide with our target objnum, else try 2 other vecs
13405                         // don't bang your head, else go
13406 //                      int go_safe = FALSE;
13407                         int go_safe = TRUE;
13408 /*                      if (target_ship_obj) {
13409                                 if (pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius)) {
13410                                         // try up to 2 other random directions
13411                                         vector dir_vec, rand_vec;
13412                                         int idx;
13413                                         for (idx=0; idx<2; idx++) {
13414                                                 vm_vec_rand_vec_quick(&rand_vec);
13415                                                 vm_vec_scale_add(&dir_vec, &weapon_objp->orient.v.fvec, &rand_vec, 0.5f);
13416                                                 vm_vec_scale_add(safe_pos, &weapon_objp->pos, &dir_vec, -50000.0f*dir);
13417                                                 if ( !pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius) ) {
13418                                                         go_safe = TRUE;
13419                                                         break;
13420                                                 }
13421                                         }
13422                                 } else { // direct path is safe
13423                                         go_safe = TRUE;
13424                                 }
13425                         } else { // no target_obj_ship
13426                                 go_safe = TRUE;
13427                         } */
13428
13429                         if (go_safe) {
13430                                 return 1;
13431                         } else {
13432                                 // can't figure out a good way to go
13433                                 return 0;
13434                         }
13435                 }
13436         } else if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_SHIP) {
13437                 if (aip->shockwave_object == -1) {
13438                         int shockwave_ship = ai_find_shockwave_ship(objp, aip);
13439                         if (shockwave_ship == -1) {
13440                                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13441                                 return 0;
13442                         } else {
13443                                 aip->shockwave_object = shockwave_ship;
13444                         }
13445                 }
13446
13447                 Assert(aip->shockwave_object > -1);
13448                 object  *ship_objp = &Objects[aip->shockwave_object];
13449                 if (ship_objp == objp) {
13450                         aip->shockwave_object = -1;
13451                         return 0;
13452                 }
13453
13454                 if (ship_objp->type != OBJ_SHIP) {
13455                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13456                         return 0;
13457                 }
13458
13459                 //      Optimize note! Don't really have to normalize.  We only need a point away from the blowing-up ship.
13460                 vector safe_vec;
13461
13462                 vm_vec_normalized_dir(&safe_vec, &objp->pos, &ship_objp->pos);
13463                 vm_vec_scale_add(safe_pos, &ship_objp->pos, &safe_vec, 50000.0f);       //      Fly away from the ship.
13464
13465                 float outer_rad = ship_get_exp_outer_rad(ship_objp);
13466
13467                 if (vm_vec_dist_quick(&objp->pos, &ship_objp->pos) > outer_rad*1.5f) {
13468                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13469                         return 0;
13470                 }
13471
13472                 return 1;
13473
13474         } else {
13475                 Int3(); //      Illegal -- supposedly avoiding a shockwave, but neither ship nor weapon.  What is it!?
13476         }
13477
13478         return 0;
13479 }
13480
13481 /*
13482 int rct_done = 0;
13483
13484 void rand_chance_test()
13485 {
13486         int     i;
13487         float   frametime;
13488
13489         if (rct_done)
13490                 return;
13491
13492         rct_done = 1;
13493
13494         for (frametime=0.02f; frametime<0.25f; frametime *= 1.25f) {
13495                 float   chance;
13496
13497                 nprintf(("AI", "%6.4f: ", frametime));
13498                 for (chance=0.25f; chance<2.5f; chance += 0.25f) {
13499                         int count = 0;
13500
13501                         for (i=0; i<100.0f/frametime; i++) {
13502                                 if (rand_chance(frametime, chance))
13503                                         count++;
13504                         }
13505                         nprintf(("AI", "%3i ", count));
13506                 }
13507                 nprintf(("AI", "\n"));
13508         }
13509 }
13510 */
13511
13512 //      --------------------------------------------------------------------------
13513 //      Make object *objp avoid the nearest dangerous shockwave-producing weapon.
13514 //      If it looks like there is no valid shockwave-producing weapon then clear the AIF_AVOID_SHOCKWAVE_WEAPON bit in ai_flags and return.
13515 //      Return 1 if avoiding a shockwave, else return 0.
13516 int ai_avoid_shockwave(object *objp, ai_info *aip)
13517 {
13518         vector  safe_pos;
13519
13520         //rand_chance_test();
13521         // BIG|HUGE do not respond to shockwaves
13522         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
13523                 // don't come here again
13524                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE;
13525                 return 0;
13526         }
13527
13528         //      Don't all react right away.
13529         if (!(aip->ai_flags & AIF_AVOID_SHOCKWAVE_STARTED))
13530                 if (!rand_chance(flFrametime, (float) aip->ai_class/4.0f + 0.25f))      //      Chance to avoid in 1 second is 0.25 + ai_class/4
13531                         return 0;
13532
13533         if (!aas_1(objp, aip, &safe_pos)) {
13534                 aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13535                 return 0;
13536         }
13537
13538         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13539
13540         //      OK, evade the shockwave!
13541         turn_towards_point(objp, &safe_pos, NULL, 0.0f);
13542         vector  vec_to_safe_pos;
13543         float           dot_to_goal;
13544
13545         vm_vec_normalized_dir(&vec_to_safe_pos, &safe_pos, &objp->pos);
13546
13547         dot_to_goal = vm_vec_dot(&objp->orient.v.fvec, &vec_to_safe_pos);
13548         if (dot_to_goal < -0.5f)
13549                 accelerate_ship(aip, 0.3f);
13550         else {
13551                 accelerate_ship(aip, 1.0f + dot_to_goal);
13552                 if (dot_to_goal > 0.2f) {
13553                         if (!(objp->phys_info.flags & PF_AFTERBURNER_ON )) {
13554                                 afterburners_start(objp);
13555                                 aip->afterburner_stop_time = Missiontime + 2*F1_0;
13556                         }
13557                 }
13558         }
13559
13560         return 1;
13561 }
13562
13563 //      Awaiting repair.  Be useful.
13564 //      Probably fly towards incoming repair ship.
13565 //      Return true if this ship is close to being repaired, else return false.
13566 int ai_await_repair_frame(object *objp, ai_info *aip)
13567 {
13568         if (!(aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)))
13569                 return 0;
13570
13571         if (aip->dock_objnum == -1)
13572                 return 0;
13573
13574         ship    *shipp;
13575         ship_info       *sip;
13576
13577         shipp = &Ships[Objects[aip->dock_objnum].instance];
13578         sip = &Ship_info[shipp->ship_info_index];
13579
13580         aip->ai_flags &= ~AIF_FORMATION_OBJECT; //      Prevents endless rotation.
13581
13582         if (!(sip->flags & SIF_SUPPORT))
13583                 return 0;
13584
13585         vector  goal_point;
13586         object  *repair_objp;
13587
13588         repair_objp = &Objects[aip->dock_objnum];
13589
13590         if (Ships[repair_objp->instance].team == TEAM_TRAITOR) {
13591                 ai_abort_rearm_request(repair_objp);
13592                 return 0;
13593         }
13594
13595         vm_vec_scale_add(&goal_point, &repair_objp->pos, &repair_objp->orient.v.uvec, -50.0f);  //      Fly towards point below repair ship.
13596
13597         vector  vtr;
13598         float dist = vm_vec_normalized_dir(&vtr, &goal_point, &objp->pos);
13599         float dot = vm_vec_dot(&vtr, &objp->orient.v.fvec);
13600
13601         if (dist > 200.0f) {
13602                 //nprintf(("AI", "%s flying towards %s for repair, dist = %7.3f\n", Ships[objp->instance].ship_name, &Ships[repair_objp->instance].ship_name, dist));
13603                 accelerate_ship(aip, (0.9f + dot) * dist/1500.0f);
13604                 turn_towards_point(objp, &goal_point, NULL, 0.0f);
13605         } else {
13606                 accelerate_ship(aip, 0.0f);
13607                 //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));
13608         }
13609
13610         return 1;
13611 }
13612
13613 //      Maybe cause this ship to self-destruct.
13614 //      Currently, any small ship (SIF_SMALL_SHIP) that has been disabled will self-destruct after awhile.
13615 //      Maybe should only do this if they are preventing their wing from re-entering.
13616 void ai_maybe_self_destruct(object *objp, ai_info *aip)
13617 {
13618         //      Friendly ships can be repaired, so no self-destruct.
13619         //      In multiplayer, just don't self-destruct.  I figured there would be a problem. -- MK, 3/19/98.
13620         if ((Ships[objp->instance].team == TEAM_FRIENDLY) || (Game_mode & GM_MULTIPLAYER))
13621                 return;
13622
13623         //      Small ships in a wing blow themselves up after awhile if engine or weapons system has been destroyed.
13624         //      Reason: Don't want them to prevent a re-emergence of the wing.
13625         //      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
13626         //      mission would be broken.
13627         if ((Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP) && (Ships[objp->instance].wingnum != -1)) {
13628                 if ((ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) ||
13629                         (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_WEAPONS) <= 0.0f)) {
13630                         if (aip->self_destruct_timestamp < 0)
13631                                 aip->self_destruct_timestamp = timestamp(90 * 1000);    //      seconds until self-destruct
13632                 } else {
13633                         aip->self_destruct_timestamp = -1;
13634                 }
13635
13636                 if (aip->self_destruct_timestamp < 0) {
13637                         return;
13638                 }
13639
13640                 if (timestamp_elapsed(aip->self_destruct_timestamp)) {
13641                         ship_apply_local_damage( objp, objp, &objp->pos, objp->hull_strength*flFrametime + 1.0f, MISS_SHIELDS);
13642                 }
13643         }
13644 }
13645
13646 // Determine if pl_objp needs a new target, called from ai_frame()
13647 int ai_need_new_target(object *pl_objp, int target_objnum)
13648 {
13649         object *objp;
13650
13651         if ( target_objnum < 0 ) {
13652                 return 1;
13653         }
13654
13655         objp = &Objects[target_objnum];
13656
13657         if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID) && (objp->type != OBJ_WEAPON) ) {
13658                 return 1;
13659         }
13660
13661         if ( objp->type == OBJ_SHIP ) {
13662                 if ( Ships[objp->instance].flags & SF_DYING ) {
13663                         return 1;
13664                 } else if (Ships[objp->instance].team == Ships[pl_objp->instance].team)
13665                         return 1;
13666         }
13667
13668         return 0;
13669 }
13670
13671 //      If *objp is recovering from a collision with a big ship, handle it.
13672 //      Return true if recovering.
13673 int maybe_big_ship_collide_recover_frame(object *objp, ai_info *aip)
13674 {
13675         float   dot, dist;
13676         vector  v2g;
13677         
13678         if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1) {
13679                 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);
13680                 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_1, &objp->pos);
13681                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
13682                 accelerate_ship(aip, dot);
13683
13684                 //      If close to desired point, or 15+ seconds since entered this mode, continue to next mode.
13685                 if ((timestamp_until(aip->big_recover_timestamp) < -15*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13686                         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_1;
13687                         aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13688                 }
13689
13690                 return 1;
13691
13692         } else if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_2) {
13693                 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);
13694                 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_2, &objp->pos);
13695                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
13696                 accelerate_ship(aip, dot);
13697
13698                 //      If close to desired point, or 30+ seconds since started avoiding collision, done avoiding.
13699                 if ((timestamp_until(aip->big_recover_timestamp) < -30*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13700                         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13701                         aip->ai_flags &= ~AIF_TARGET_COLLISION;
13702                 }
13703
13704                 return 1;
13705         }
13706
13707         if (aip->ai_flags & AIF_TARGET_COLLISION) {
13708                 aip->ai_flags &= ~AIF_TARGET_COLLISION;
13709         }
13710         return 0;
13711 }
13712
13713 void validate_mode_submode(ai_info *aip)
13714 {
13715         switch (aip->mode) {
13716         case AIM_CHASE:
13717                 // check valid submode
13718                 switch (aip->submode) {
13719                 case SM_CONTINUOUS_TURN:
13720                 case SM_ATTACK:
13721                 case SM_EVADE_SQUIGGLE:
13722                 case SM_EVADE_BRAKE:    
13723                 case SM_EVADE:          
13724                 case SM_SUPER_ATTACK:
13725                 case SM_AVOID:  
13726                 case SM_GET_BEHIND:
13727                 case SM_GET_AWAY:               
13728                 case SM_EVADE_WEAPON:
13729                 case SM_FLY_AWAY:       
13730                 case SM_ATTACK_FOREVER:
13731                         break;
13732                 default:
13733                         Int3();
13734                 }
13735                 break;
13736
13737         case AIM_STRAFE:
13738                 // check valid submode
13739                 switch(aip->submode) {
13740                 case AIS_STRAFE_ATTACK:
13741                 case AIS_STRAFE_AVOID:
13742                 case AIS_STRAFE_RETREAT1:
13743                 case AIS_STRAFE_RETREAT2:
13744                 case AIS_STRAFE_POSITION:
13745                         break;
13746                 default:
13747                         Int3();
13748                 }
13749                 break;
13750         }
13751 }
13752
13753 //      --------------------------------------------------------------------------
13754 // Process AI object "objnum".
13755 void ai_frame(int objnum)
13756 {
13757         ship            *shipp = &Ships[Objects[objnum].instance];
13758         ai_info *aip = &Ai_info[shipp->ai_index];
13759         int             target_objnum;
13760
13761 //      validate_mode_submode(aip);
13762
13763         Assert((aip->mode != AIM_WAYPOINTS) || (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC));
13764
13765         // Set globals defining the current object and its enemy object.
13766         Pl_objp = &Objects[objnum];
13767
13768         if (aip->mode == AIM_WARP_OUT) {
13769                 ai_warp_out(Pl_objp);
13770                 return;
13771         }
13772
13773 /*      //      HACK! TEST! REMOVE ME!
13774         if (Ship_info[shipp->ship_info_index].flags & SIF_BIG_SHIP)
13775                 if (shipp->team == Player_ship->team)
13776                         aip->mode = AIM_CHASE;
13777 */
13778
13779 //      if (!strnicmp(Ships[Pl_objp->instance].ship_name, "cancer", 6))
13780 //              nprintf(("AI", "Ship %s: mode = %s, submode = %i\n", Ships[Pl_objp->instance].ship_name, Mode_text[aip->mode], aip->submode));
13781
13782         ai_maybe_self_destruct(Pl_objp, aip);
13783
13784 //      if ( timestamp_elapsed(aip->goal_check_time) ) {
13785                 ai_process_mission_orders( objnum, aip );
13786 //              aip->goal_check_time = timestamp_rand(1000,2000);
13787 //      }
13788
13789         //      Avoid a shockwave, if necessary.  If a shockwave and rearming, stop rearming.
13790         if (aip->ai_flags & AIF_AVOID_SHOCKWAVE) {
13791                 if (ai_avoid_shockwave(Pl_objp, aip)) {
13792                         aip->ai_flags &= ~(AIF_BIG_SHIP_COLLIDE_RECOVER_1 | AIF_BIG_SHIP_COLLIDE_RECOVER_2);
13793                         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
13794                                 ai_abort_rearm_request(Pl_objp);
13795                         return;
13796                 }
13797         } else {
13798                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_STARTED;
13799         }
13800
13801         // moved call to ai_do_repair frame here from below because of the subsequent if statment returning
13802         // if the ship is getting repaired
13803         //      If waiting to be repaired, just stop and sit.
13804         ai_do_repair_frame(Pl_objp, aip, flFrametime);
13805         if ((aip->ai_flags & AIF_AWAITING_REPAIR) || (aip->ai_flags & AIF_BEING_REPAIRED)) {
13806                 if (ai_await_repair_frame(Pl_objp, aip))
13807                         return;
13808         }
13809
13810         if (aip->mode == AIM_PLAY_DEAD)
13811                 return;
13812
13813         //      If recovering from a collision with a big ship, don't continue.
13814         if (maybe_big_ship_collide_recover_frame(Pl_objp, aip))
13815                 return;
13816
13817         ai_preprocess_ignore_objnum(Pl_objp, aip);
13818         target_objnum = set_target_objnum(aip, aip->target_objnum);
13819
13820         // 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));
13821
13822         Assert(objnum != target_objnum);
13823
13824         ai_manage_shield(Pl_objp, aip);
13825         
13826         if ( maybe_request_support(Pl_objp) ) {
13827                 if ( Ships[Pl_objp->instance].flags & SF_FROM_PLAYER_WING ) {
13828                         ship_maybe_tell_about_rearm(shipp);
13829                 }
13830         }
13831
13832         ai_maybe_warp_out(Pl_objp);
13833
13834 /*
13835         //      If this ship is attacking an object's subsystems and someone else destroyed
13836         //      the subsystem, it could continue attacking the ship.  Need to invalidate the objnum.
13837         if (target_objnum >= 0)
13838                 if (Objects[target_objnum].flags & OF_PROTECTED) {
13839                         // if (aip->targeted_subsys != NULL)
13840                         //      ; //nprintf(("AI", "subsys hits = %7.3f\n", aip->targeted_subsys->current_hits));
13841
13842                         if ((aip->targeted_subsys == NULL) || (aip->targeted_subsys->current_hits <= 0.0f)) {
13843                                 target_objnum = -1;
13844                                 aip->target_objnum = -1;
13845                         }
13846                 }
13847 */
13848
13849
13850         //      Find an enemy if don't already have one.
13851         En_objp = NULL;
13852         if ( ai_need_new_target(Pl_objp, target_objnum) ) {
13853                 if ((aip->mode != AIM_EVADE_WEAPON) && (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
13854                         aip->resume_goal_time = -1;
13855                         aip->active_goal = AI_GOAL_NONE;
13856                 } else if (aip->resume_goal_time == -1) {
13857                         // AL 12-9-97: Don't allow cargo and navbuoys to set their aip->target_objnum
13858                         if ( !(Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS) ) {
13859                                 target_objnum = find_enemy(objnum, MAX_ENEMY_DISTANCE, Skill_level_max_attackers[Game_skill_level]);            //      Attack up to 25K units away.
13860                                 if (target_objnum != -1) {
13861                                         if (aip->target_objnum != target_objnum)
13862                                                 aip->aspect_locked_time = 0.0f;
13863                                         set_target_objnum(aip, target_objnum);
13864                                         En_objp = &Objects[target_objnum];
13865                                 }
13866                         }
13867                 }
13868         } else if (target_objnum >= 0) {
13869                 En_objp = &Objects[target_objnum];
13870         }
13871
13872         // set base stealth info each frame
13873         aip->ai_flags &= ~AIF_STEALTH_PURSIUT;
13874         if (En_objp && En_objp->type == OBJ_SHIP) {
13875                 if (Ship_info[Ships[En_objp->instance].ship_info_index].flags & SIF_STEALTH) {
13876                         int stealth_state = ai_is_stealth_visible(Pl_objp, En_objp);
13877                         float dist = vm_vec_dist_quick(&En_objp->pos, &Pl_objp->pos);
13878
13879                         if (stealth_state != STEALTH_FULLY_TARGETABLE) {
13880                                 aip->ai_flags |= AIF_STEALTH_PURSIUT;
13881                         }
13882
13883                         if ( (stealth_state == STEALTH_FULLY_TARGETABLE) || (stealth_state == STEALTH_VISIBLE) ) {
13884                                 aip->stealth_last_visible_stamp = timestamp();
13885                                 aip->stealth_last_cheat_visible_stamp = aip->stealth_last_visible_stamp;
13886                                 aip->stealth_last_pos = En_objp->pos;
13887                                 aip->stealth_velocity = En_objp->phys_info.vel;
13888                         } else if (dist < 100) {
13889                                 // get cheat timestamp
13890                                 aip->stealth_last_cheat_visible_stamp = timestamp();
13891
13892                                 // set approximate pos and vel, with increasing error as time from last_visible_stamp increases
13893                                 update_ai_stealth_info_with_error(aip/*, 0*/);
13894                         }
13895                 }
13896         }
13897
13898         /*      if ((Pl_objp != NULL) && (En_objp != NULL)) {
13899                 slide_face_ship();
13900                 return;
13901         }
13902 */
13903         // AL 12-10-97: ensure that cargo and navbuoys aip->target_objnum is always -1.
13904         if ( Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS ) {
13905                 aip->target_objnum = -1;
13906         }
13907
13908         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)) {
13909                 mprintf(("Warning: Object and its enemy have same position.  Object #%i\n", Pl_objp-Objects));
13910                 En_objp = NULL;
13911         }
13912
13913         if (aip->mode == AIM_CHASE) {
13914                 if (En_objp == NULL) {
13915                         aip->active_goal = -1;
13916                 }
13917         }
13918
13919         //      If there is a goal to resume and enough time has elapsed, resume the goal.
13920         if ((aip->resume_goal_time > 0) && (aip->resume_goal_time < Missiontime)) {
13921                 aip->active_goal = AI_GOAL_NONE;
13922                 aip->resume_goal_time = -1;
13923                 target_objnum = find_enemy(objnum, 2000.0f, Skill_level_max_attackers[Game_skill_level]);
13924                 if (target_objnum != -1) {
13925                         if (aip->target_objnum != target_objnum) {
13926                                 aip->aspect_locked_time = 0.0f;
13927                         }
13928                         set_target_objnum(aip, target_objnum);
13929                 }
13930         }
13931
13932         // check if targeted subsystem has been destroyed, if so, move onto another subsystem
13933         // if trying to disable or disarm the target
13934         if ((En_objp != NULL) && ( aip->targeted_subsys != NULL )) {
13935                 Assert(En_objp->type == OBJ_SHIP);
13936                 if ( aip->targeted_subsys->current_hits <= 0.0f ) {
13937                         int subsys_type;
13938
13939                         if ( aip->goals[0].ai_mode == AI_GOAL_DISABLE_SHIP ) {
13940                                 subsys_type = SUBSYSTEM_ENGINE;
13941                         } else if ( aip->goals[0].ai_mode == AI_GOAL_DISARM_SHIP ) {
13942                                 subsys_type = SUBSYSTEM_TURRET;
13943                         } else {
13944                                 subsys_type = -1;
13945                         }
13946
13947                         if ( subsys_type != -1 ) {
13948                                 ship_subsys *new_subsys;
13949                                 new_subsys = ship_return_next_subsys(&Ships[En_objp->instance], subsys_type, &Pl_objp->pos);
13950                                 if ( new_subsys != NULL ) {
13951                                         set_targeted_subsys(aip, new_subsys, aip->target_objnum);
13952                                 } else {
13953                                         // AL 12-16-97: no more subsystems to attack... reset targeting info
13954                                         aip->target_objnum = -1;
13955                                         set_targeted_subsys(aip, NULL, -1);
13956                                 }
13957                         } else {
13958                                 // targeted subsys is destroyed, so stop attacking it
13959                                 set_targeted_subsys(aip, NULL, -1);
13960                         }
13961                 }
13962         }
13963
13964         ai_maybe_launch_cmeasure(Pl_objp, aip);
13965         ai_maybe_evade_locked_missile(Pl_objp, aip);
13966
13967         aip->target_time += flFrametime;
13968
13969         int in_formation = 0;
13970         if (aip->ai_flags & AIF_FORMATION) {
13971                 in_formation = !ai_formation();
13972         }
13973
13974         if ( !in_formation ) {
13975                 ai_execute_behavior(aip);
13976         }
13977
13978         process_subobjects(objnum);
13979         maybe_resume_previous_mode(Pl_objp, aip);
13980         
13981         if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON ) {
13982                 if (Missiontime > aip->afterburner_stop_time) {
13983                         //nprintf(("AI", "Frame %i, turning off afterburner.\n", AI_FrameCount));
13984                         afterburners_stop(Pl_objp);
13985                 }
13986         }
13987 //      validate_mode_submode(aip);
13988 }
13989
13990 int Waypoints_created = 0;
13991
13992 //      Find the ship with the name *name in the Ship_info array.
13993 int find_ship_name(char *name)
13994 {
13995         int     i;
13996
13997         for (i=0; i<Num_ship_types; i++)
13998                 if (!strcmp(Ship_info[i].name, name))
13999                         return i;
14000
14001         return -1;
14002 }
14003
14004 void create_waypoints()
14005 {
14006         int     i, j, z;
14007
14008         // Waypoints_created = 1;
14009
14010         if (Waypoints_created)
14011                 return;
14012
14013         for (j=0; j<Num_waypoint_lists; j++)
14014                 for (i=0; i<Waypoint_lists[j].count; i++) {
14015                         z = obj_create(OBJ_WAYPOINT, 0, j * 65536 + i, NULL,
14016                                 &Waypoint_lists[j].waypoints[i], 0.0f, OF_RENDERS);
14017                 }
14018
14019         Waypoints_created = 1;
14020 }
14021
14022 int Last_ai_obj = -1;
14023
14024 void ai_process( object * obj, int ai_index, float frametime )
14025 {
14026 //      if (Ships[obj->instance].flags & SF_DYING)
14027 //              nprintf(("AI", "Frame: %i Ship %s is dying!\n", Framecount, Ships[obj->instance].ship_name));
14028
14029         if (obj->flags & OF_SHOULD_BE_DEAD)
14030                 return;
14031
14032         // 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.
14033         if ((Ships[obj->instance].flags & SF_DYING ) && !(Ship_info[Ships[obj->instance].ship_info_index].flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP))){
14034                 return;
14035         }
14036
14037         int rfc = 1;            //      Assume will be Reading Flying Controls.
14038
14039         Assert( obj->type == OBJ_SHIP );
14040         Assert( ai_index >= 0 );
14041
14042         init_ship_info();
14043
14044         create_waypoints();
14045
14046         AI_frametime = frametime;
14047         if (obj-Objects <= Last_ai_obj) {
14048                 AI_FrameCount++;
14049         }
14050
14051         memset( &AI_ci, 0, sizeof(AI_ci) );
14052
14053         ai_frame(obj-Objects);
14054
14055         AI_ci.pitch = 0.0f;
14056         AI_ci.bank = 0.0f;
14057         AI_ci.heading = 0.0f;
14058
14059         // the ships maximum velocity now depends on the energy flowing to engines
14060         obj->phys_info.max_vel.xyz.z = Ships[obj->instance].current_max_speed;
14061         ai_info *aip = &Ai_info[Ships[obj->instance].ai_index];
14062
14063         //      In certain circumstances, the AI says don't fly in the normal way.
14064         //      One circumstance is in docking and undocking, when the ship is moving
14065         //      under thruster control.
14066         switch (aip->mode) {
14067         case AIM_DOCK:
14068                 if ((aip->submode >= AIS_DOCK_2) && (aip->submode != AIS_UNDOCK_3))
14069                         rfc = 0;
14070                 break;
14071         case AIM_WARP_OUT:
14072                 if (aip->submode >= AIS_WARP_3)
14073                         rfc = 0;
14074                 break;
14075 //      case AIM_NONE:
14076 //              if (aip->submode == AIS_NONE_FORMATION)
14077 //                      rfc = 0;
14078 //              break;
14079         default:
14080                 break;
14081         }
14082
14083         if (rfc == 1) {
14084                 vector copy_desired_rotvel = obj->phys_info.rotvel;
14085                 physics_read_flying_controls( &obj->orient, &obj->phys_info, &AI_ci, frametime);
14086                 // if obj is in formation and not flight leader, don't update rotvel
14087                 if (aip->ai_flags & AIF_FORMATION) {
14088                         if (&Objects[aip->goal_objnum] != obj) {
14089                                 obj->phys_info.desired_rotvel = copy_desired_rotvel;
14090                                 obj->phys_info.rotvel = copy_desired_rotvel;
14091                         }
14092                 }
14093         }
14094
14095         Last_ai_obj = obj-Objects;
14096 }
14097
14098 //      Initialize ai_info struct of object objnum.
14099 void init_ai_object(int objnum)
14100 {
14101         int     ship_index, ai_index;
14102         ai_info *aip;
14103         int ship_type;
14104         object  *objp;
14105         vector  near_vec;                       //      A vector nearby and mainly in front of this object.
14106
14107         objp = &Objects[objnum];
14108         ship_index = objp->instance;
14109         ai_index = Ships[ship_index].ai_index;
14110         Assert((ai_index >= 0) && (ai_index < MAX_AI_INFO));
14111
14112         aip = &Ai_info[ai_index];
14113
14114         ship_type = Ships[ship_index].ship_info_index;
14115
14116         vm_vec_scale_add(&near_vec, &objp->pos, &objp->orient.v.fvec, 100.0f);
14117         vm_vec_scale_add2(&near_vec, &objp->orient.v.rvec, 10.0f);
14118
14119         // Things that shouldn't have to get initialized, but initialize them just in case!
14120         aip->ai_flags = 0;
14121         aip->previous_mode = AIM_NONE;
14122         aip->mode_time = -1;
14123         aip->target_objnum = -1;
14124         aip->target_signature = -1;
14125         aip->previous_target_objnum = -1;
14126         aip->target_time = 0.0f;
14127         aip->enemy_wing = -1;
14128         aip->attacker_objnum = -1;
14129         aip->goal_objnum = -1;
14130         aip->goal_signature = -1;
14131         aip->guard_objnum = -1;
14132         aip->guard_signature = -1;
14133         aip->guard_wingnum = -1;
14134         aip->dock_signature = -1;
14135         aip->submode = 0;
14136         aip->previous_submode = 0;
14137         aip->best_dot_to_enemy = -1.0f;
14138         aip->best_dot_from_enemy = -1.0f;
14139         aip->best_dot_to_time = 0;
14140         aip->best_dot_from_time = 0;
14141         aip->submode_start_time = 0;
14142         aip->submode_parm0 = 0;
14143         aip->active_goal = -1;
14144         aip->goal_check_time = timestamp(0);
14145         aip->last_predicted_enemy_pos = near_vec;
14146         aip->prev_goal_point = near_vec;
14147         aip->goal_point = near_vec;
14148         aip->time_enemy_in_range = 0.0f;
14149         aip->last_attack_time = 0;
14150         aip->last_hit_time = 0;
14151         aip->last_hit_quadrant = 0;
14152         aip->hitter_objnum = -1;
14153         aip->hitter_signature = -1;
14154         aip->resume_goal_time = -1;
14155         aip->prev_accel = 0.0f;
14156         aip->prev_dot_to_goal = 0.0f;
14157
14158         aip->ignore_objnum = UNUSED_OBJNUM;
14159         aip->ignore_signature = -1;
14160
14161         // aip->mode = AIM_NONE;
14162
14163         // End of Things that shouldn't have to get initialized, but initialize them just in case!
14164
14165         aip->ai_courage = Ai_classes[Ship_info[ship_type].ai_class].ai_courage[Game_skill_level];
14166         aip->ai_patience = Ai_classes[Ship_info[ship_type].ai_class].ai_patience[Game_skill_level];
14167         aip->ai_evasion = Ai_classes[Ship_info[ship_type].ai_class].ai_evasion[Game_skill_level];
14168         aip->ai_accuracy = Ai_classes[Ship_info[ship_type].ai_class].ai_accuracy[Game_skill_level];
14169
14170         if (Num_waypoint_lists > 0) {
14171                 aip->wp_index = -1;
14172                 aip->wp_list = -1;
14173         } else {
14174                 aip->wp_index = -1;
14175                 aip->wp_list = -1;
14176         }
14177
14178         aip->attacker_objnum = -1;
14179         aip->goal_signature = -1;
14180
14181         Objects[objnum].phys_info.prev_fvec = Objects[objnum].orient.v.fvec;
14182
14183         aip->last_predicted_enemy_pos.xyz.x = 0.0f;     //      Says this value needs to be recomputed!
14184         aip->time_enemy_in_range = 0.0f;
14185
14186         aip->resume_goal_time = -1;                                     //      Say there is no goal to resume.
14187
14188         aip->active_goal = -1;
14189         aip->path_start = -1;
14190         aip->path_goal_dist = -1;
14191         aip->path_length = 0;
14192         aip->path_subsystem_next_check = 1;
14193         aip->dock_path_index = -1;
14194         aip->dock_index = -1;
14195         aip->dock_objnum = -1;
14196
14197         aip->danger_weapon_objnum = -1;
14198         aip->danger_weapon_signature = -1;
14199
14200         aip->lead_scale = 0.0f;
14201         aip->last_hit_target_time = Missiontime;
14202
14203         aip->nearest_locked_object = -1;
14204         aip->nearest_locked_distance = 99999.0f;
14205
14206         aip->targeted_subsys = NULL;
14207         aip->last_subsys_target = NULL;
14208         aip->targeted_subsys_parent = -1;
14209
14210         // The next two fields are used to time the rearming to allow useful sound effects for missile rearming
14211         aip->rearm_first_missile = TRUE;                //      flag to indicate that next missile to load is the first missile
14212         aip->rearm_release_delay = 0;                   //      timestamp to delay the separation of docked ships after rearm
14213
14214         aip->next_predict_pos_time = 0;
14215
14216         aip->afterburner_stop_time = 0;
14217         aip->last_objsig_hit = -1;                              // object signature of the ship most recently hit by aip
14218
14219         aip->path_next_create_time = timestamp(1);
14220         aip->path_create_pos = Objects[objnum].pos;
14221         aip->path_create_orient = Objects[objnum].orient;
14222
14223         aip->ignore_expire_timestamp = timestamp(1);
14224         aip->warp_out_timestamp = 0;
14225         aip->next_rearm_request_timestamp = timestamp(1);
14226         aip->primary_select_timestamp = timestamp(1);
14227         aip->secondary_select_timestamp = timestamp(1);
14228         aip->scan_for_enemy_timestamp = timestamp(1);
14229
14230         aip->choose_enemy_timestamp = timestamp(3*(NUM_SKILL_LEVELS-Game_skill_level) * ((rand_alt() % 500) + 500));
14231
14232         aip->shockwave_object = -1;
14233         aip->shield_manage_timestamp = timestamp(1);
14234         aip->self_destruct_timestamp = -1;      //      This is a flag that we have not yet set this.
14235         aip->ok_to_target_timestamp = timestamp(1);
14236         aip->pick_big_attack_point_timestamp = timestamp(1);
14237         vm_vec_zero(&aip->big_attack_point);
14238
14239         aip->avoid_check_timestamp = timestamp(1);
14240
14241         aip->abort_rearm_timestamp = -1;
14242
14243         // artillery stuff
14244         aip->artillery_objnum = -1;
14245         aip->artillery_sig = -1;        
14246
14247         // waypoint speed cap
14248         aip->waypoint_speed_cap = -1;
14249
14250         // set lethality to enemy team
14251         aip->lethality = 0.0f;
14252 }
14253
14254 void init_ai_objects()
14255 {
14256         int     i;
14257
14258         for (i=0; i<num_objects; i++){
14259                 if (Objects[i].type == OBJ_SHIP){
14260                         init_ai_object(i);
14261                 }
14262         }
14263 }
14264
14265 void init_ai_system()
14266 {
14267         // MWA -- removed next line of code on 11/12/97.  When a ship is created
14268         // it calls init_ai_object() on it's objnum.  Doing this init at the point where
14269         // this function gets called messes things up.
14270         //init_ai_objects();
14271
14272         Ppfp = Path_points;
14273         Waypoints_created = 0;
14274
14275         Dock_path_warning_given = 0;
14276
14277 /*      for (int i=0; i<MAX_IGNORE_OBJECTS; i++) {
14278                 Ignore_objects[i].objnum = -1;
14279                 Ignore_objects[i].signature = -1;
14280         }
14281 */
14282
14283 }
14284
14285 void ai_set_default_behavior(object *obj, int classnum)
14286 {
14287         ai_info *aip;
14288
14289         Assert(obj != NULL);
14290         Assert(obj->instance != -1);
14291         Assert(Ships[obj->instance].ai_index != -1);
14292
14293         aip = &Ai_info[Ships[obj->instance].ai_index];
14294
14295         aip->behavior = classnum;
14296
14297 }
14298
14299 void ai_do_default_behavior(object *obj)
14300 {
14301         ai_info *aip;
14302         int             ship_flags;
14303
14304         Assert(obj != NULL);
14305         Assert(obj->instance != -1);
14306         Assert(Ships[obj->instance].ai_index != -1);
14307
14308         aip = &Ai_info[Ships[obj->instance].ai_index];
14309
14310         ship_flags = Ship_info[Ships[obj->instance].ship_info_index].flags;
14311         if (!is_instructor(obj) && (ship_flags & (SIF_FIGHTER | SIF_BOMBER))) {
14312                 int enemy_objnum = find_enemy(OBJ_INDEX(obj), 1000.0f, Skill_level_max_attackers[Game_skill_level]);
14313                 set_target_objnum(aip, enemy_objnum);
14314                 aip->mode = AIM_CHASE;
14315                 aip->submode = SM_ATTACK;
14316         } else if (ship_flags & (SIF_SUPPORT)) {
14317                 aip->mode = AIM_SAFETY;
14318                 aip->submode = AISS_1;
14319                 aip->ai_flags &= ~(AIF_REPAIRING);
14320         } else if ( ship_flags & SIF_SENTRYGUN ) {
14321                 aip->mode = AIM_SENTRYGUN;
14322         } else {
14323                 aip->mode = AIM_NONE;
14324         }
14325         
14326         aip->submode_start_time = Missiontime;
14327         aip->active_goal = AI_GOAL_NONE;
14328 }
14329
14330 #define FRIENDLY_DAMAGE_THRESHOLD       50.0f           //      Display a message at this threshold.  Note, this gets scaled by Skill_level
14331
14332 // send the given message from objp.  called from the maybe_process_friendly_hit
14333 // code below when a message must get send to the player when he fires on friendlies
14334 void process_friendly_hit_message( int message, object *objp )
14335 {
14336         int index;
14337
14338         // no traitor in multiplayer
14339         if(Game_mode & GM_MULTIPLAYER){
14340                 return;
14341         }
14342
14343         // don't send this message if a player ship was hit.
14344         if ( objp->flags & OF_PLAYER_SHIP ){
14345                 return;
14346         }
14347
14348         // check if objp is a cargo contianer -- if so, then find a new ship to send the message
14349         index = objp->instance;
14350         if ( !(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER|SIF_BOMBER)) ){
14351                 index = -1;
14352         }
14353
14354         // if the message is "oops" (the don't hit me message), always make come from Terran command
14355         if ( message == MESSAGE_OOPS ){
14356                 index = -1;
14357         }
14358
14359         if ( index >= 0){
14360                 message_send_builtin_to_player( message, &Ships[index], MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14361         } else {
14362                 message_send_builtin_to_player( message, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14363         }
14364 }
14365
14366 extern  void ship_set_subsystem_strength( ship *shipp, int type, float strength );
14367
14368 //      Object *objp_weapon, fired by *objp_hitter, hit object *objp_ship.
14369 void maybe_process_friendly_hit(object *objp_hitter, object *objp_hit, object *objp_weapon)
14370 {
14371         // no turning traitor in multiplayer
14372         if ( Game_mode & GM_MULTIPLAYER ) {
14373                 return;
14374         }
14375
14376         // ditto if mission says no traitors allowed
14377         if (The_mission.flags & MISSION_FLAG_NO_TRAITOR) {
14378                 return;
14379         }
14380
14381         if ((objp_hitter == Player_obj) && (Player_ship->team == TEAM_FRIENDLY)) {
14382
14383                 // AL 12-4-97: It is possible the Player is a OBJ_GHOST at this point.  If so, bail out.
14384                 if ( objp_hitter->type != OBJ_SHIP ) {
14385                         return;
14386                 }
14387
14388                 Assert(objp_hitter->type == OBJ_SHIP);
14389                 Assert(objp_hit->type == OBJ_SHIP);
14390                 Assert(objp_weapon->type == OBJ_WEAPON);
14391
14392                 ship    *shipp_hitter = &Ships[objp_hitter->instance];
14393                 ship    *shipp_hit = &Ships[objp_hit->instance];
14394
14395                 if (shipp_hitter->team != shipp_hit->team) {
14396                         return;
14397                 }
14398
14399                 // get the player
14400                 player *pp = &Players[Player_num];
14401
14402                 // wacky stuff here
14403                 if (pp->friendly_hits != 0) {
14404                         float   time_since_last_hit = f2fl(Missiontime - pp->friendly_last_hit_time);
14405                         if ((time_since_last_hit >= 0.0f) && (time_since_last_hit < 10000.0f)) {
14406                                 if (time_since_last_hit > 60.0f) {
14407                                         pp->friendly_hits = 0;
14408                                         pp->friendly_damage = 0.0f;
14409                                 } else if (time_since_last_hit > 2.0f) {
14410                                         pp->friendly_hits -= (int) time_since_last_hit/2;
14411                                         pp->friendly_damage -= time_since_last_hit;
14412                                 }
14413
14414                                 if (pp->friendly_damage < 0.0f) {
14415                                         pp->friendly_damage = 0.0f;
14416                                 }
14417
14418                                 if (pp->friendly_hits < 0) {
14419                                         pp->friendly_hits = 0;
14420                                 }
14421                         }
14422                 }
14423
14424                 float   damage;         //      Damage done by weapon.  Gets scaled down based on size of ship.
14425
14426                 damage = Weapon_info[Weapons[objp_weapon->instance].weapon_info_index].damage;
14427                 
14428                 // wacky stuff here
14429                 ship_info *sip = &Ship_info[Ships[objp_hit->instance].ship_info_index];
14430                 if (sip->initial_hull_strength > 1000.0f) {
14431                         float factor = sip->initial_hull_strength / 1000.0f;
14432                         factor = min(100.0f, factor);
14433                         damage /= factor;
14434                 }
14435
14436                 //      Don't penalize much at all for hitting cargo
14437                 if (sip->flags & (SIF_CARGO | SIF_SENTRYGUN)) {
14438                         damage /= 10.0f;
14439                 }
14440
14441                 //      Hit ship, but not targeting it, so it's not so heinous, maybe an accident.
14442                 if (Ai_info[shipp_hitter->ai_index].target_objnum != OBJ_INDEX(objp_hit)) {
14443                         damage /= 5.0f;
14444                 }
14445
14446                 pp->friendly_last_hit_time = Missiontime;
14447                 pp->friendly_hits++;
14448
14449                 // cap damage and number of hits done this frame
14450                 float accredited_damage = min(MAX_BURST_DAMAGE, pp->damage_this_burst + damage) - pp->damage_this_burst;
14451                 pp->friendly_damage += accredited_damage;
14452                 pp->damage_this_burst += accredited_damage;
14453
14454                 // Done with adjustments to damage.  Evaluate based on current friendly_damage
14455                 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 ));
14456                 
14457                 if (is_instructor(objp_hit)) {
14458                         // it's not nice to hit your instructor
14459                         if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD) {
14460                                 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_ATTACK, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14461                                 pp->last_warning_message_time = Missiontime;
14462                                 ship_set_subsystem_strength( Player_ship, SUBSYSTEM_WEAPONS, 0.0f);
14463
14464                                 training_fail();
14465
14466                                 //      Instructor warp out.
14467                                 ai_set_mode_warp_out(objp_hit, &Ai_info[Ships[objp_hit->instance].ai_index]);
14468                                 gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START_FORCED );     //      Force player to warp out.
14469
14470                                 //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) );
14471                                 //ship_apply_global_damage( objp_hitter, objp_hit, NULL, 1.0f );
14472                         } else if (Missiontime - pp->last_warning_message_time > F1_0*4) {
14473                                 // warning every 4 sec
14474                                 // use NULL as the message sender here since it is the Terran Command persona
14475                                 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_HIT, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14476                                 pp->last_warning_message_time = Missiontime;
14477                         }
14478
14479                 // not nice to hit your friends
14480                 } else if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f)) {
14481                         process_friendly_hit_message( MESSAGE_HAMMER_SWINE, objp_hit );
14482                         mission_goal_fail_all();
14483                         ai_abort_rearm_request( Player_obj );
14484
14485                         Player_ship->team = TEAM_TRAITOR;
14486
14487                 } else if ((damage > frand()) && (Missiontime - pp->last_warning_message_time > F1_0*4) && (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD)) {
14488                         // no closer than 4 sec intervals
14489                         //      Note: (damage > frand()) added on 12/9/97 by MK.  Since damage is now scaled down for big ships, we could get too
14490                         //      many warnings.  Kind of tedious.  frand() returns a value in 0..1, so this won't affect legit hits.
14491                         process_friendly_hit_message( MESSAGE_OOPS, objp_hit );
14492                         pp->last_warning_message_time = Missiontime;
14493                 }
14494         }
14495 }
14496
14497 //      Maybe make ship with ai_info *aip attack hitter_objnum as a dynamic goal
14498 void maybe_set_dynamic_chase(ai_info *aip, int hitter_objnum)
14499 {
14500         Assert(Ship_info[Ships[aip->shipnum].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER));
14501
14502         // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
14503         if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
14504                 return;
14505         }
14506
14507         // only set as target if can be targeted.
14508         if (awacs_get_level(&Objects[hitter_objnum], &Ships[aip->shipnum], 1) < 1) {
14509                 return;
14510         }
14511
14512         if (aip->target_objnum != hitter_objnum)
14513                 aip->aspect_locked_time = 0.0f;
14514         set_target_objnum(aip, hitter_objnum);
14515         aip->resume_goal_time = Missiontime + i2f(20);  //      Only chase up to 20 seconds.
14516         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14517
14518         set_targeted_subsys(aip, NULL, -1);             //      Say not attacking any particular subsystem.
14519
14520         aip->previous_submode = aip->mode;
14521         aip->mode = AIM_CHASE;
14522         aip->submode = SM_ATTACK;
14523 }
14524
14525
14526 //      Return true if *objp has armed an aspect seeking bomb.
14527 //      This function written so a ship with an important bomb to fire will willingly take hits in the face to fire its bomb.
14528 int firing_aspect_seeking_bomb(object *objp)
14529 {
14530         ship    *shipp;
14531         int     bank_index;
14532         ship_weapon     *swp;
14533
14534         shipp = &Ships[objp->instance];
14535
14536         swp = &shipp->weapons;
14537
14538         bank_index = swp->current_secondary_bank;
14539
14540         if (bank_index != -1)
14541                 if (swp->secondary_bank_ammo[bank_index] > 0) {
14542                         if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_BOMB) {
14543                                 if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_HOMING_ASPECT) {
14544                                         return 1;
14545                                 }
14546                         }
14547                 }
14548
14549         return 0;
14550 }
14551
14552 //      *objp collided with big ship *big_objp at global point *collide_pos
14553 //      Make it fly away from the collision point.
14554 // collision_normal is NULL, when a collision is imminent and we just want to bug out.
14555 void big_ship_collide_recover_start(object *objp, object *big_objp, vector *collide_pos, vector *collision_normal)
14556 {
14557         ai_info *aip;
14558
14559         Assert(objp->type == OBJ_SHIP);
14560
14561         aip = &Ai_info[Ships[objp->instance].ai_index];
14562
14563         if (!timestamp_elapsed(aip->big_recover_timestamp) && (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1))
14564                 return;
14565
14566         //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)));
14567         if (collision_normal) {
14568                 aip->big_recover_timestamp = timestamp(2000);
14569                 aip->big_collision_normal = *collision_normal;
14570         //      nprintf(("AI", " normal\n"));
14571         } else {
14572                 aip->big_recover_timestamp = timestamp(500);
14573         //      nprintf(("AI", " no normal\n"));
14574         }
14575
14576
14577         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
14578         aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_1;
14579
14580 //      vector  out_vec;
14581 //      vm_vec_normalized_dir(&out_vec, &objp->pos, collide_pos);
14582
14583         // big_recover_pos_1 is 100 m out along normal
14584         vector direction;
14585         if (collision_normal) {
14586                 direction = *collision_normal;
14587         } else {
14588                 vm_vec_copy_scale(&direction, &objp->orient.v.fvec, -1.0f);
14589         }
14590         vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &direction, 100.0f);
14591
14592         // go out 200 m from box closest box point
14593         get_world_closest_box_point_with_delta(&aip->big_recover_pos_2, big_objp, &aip->big_recover_pos_1, NULL, 300.0f);
14594
14595         accelerate_ship(aip, 0.0f);
14596 /*
14597         if (vm_vec_dot(collision_normal, &objp->orient.v.fvec) > 0.5f) {
14598 //              vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14599 //              vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.v.uvec, big_objp->radius/2.0f);
14600 //              vm_vec_scale_add(&aip->big_recover_pos_2, &objp->pos, &out_vec, big_objp->radius*2.0f);
14601                 accelerate_ship(aip, 2.0f);
14602         } else {
14603 //              vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14604 //              vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.v.uvec, big_objp->radius/2.0f);
14605                 accelerate_ship(aip, 0.0f);
14606         } */
14607 }
14608
14609 float max_lethality = 0.0f;
14610
14611 void ai_update_lethality(object *ship_obj, object *other_obj, float damage)
14612 {
14613         Assert(ship_obj->type == OBJ_SHIP);
14614         Assert(other_obj->type == OBJ_WEAPON || other_obj->type == OBJ_SHOCKWAVE);
14615         int dont_count = FALSE;
14616
14617         int parent = other_obj->parent;
14618         if (Objects[parent].type == OBJ_SHIP) {
14619                 if (Objects[parent].signature == other_obj->parent_sig) {
14620
14621                         // check damage done to enemy team
14622                         if (Ships[ship_obj->instance].team != Ships[Objects[parent].instance].team) {
14623
14624                                 // other is weapon
14625                                 if (other_obj->type == OBJ_WEAPON) {
14626                                         weapon *wp = &Weapons[other_obj->instance];
14627                                         weapon_info *wif = &Weapon_info[wp->weapon_info_index];
14628
14629                                         // if parent is BIG|HUGE, don't count beam
14630                                         if (Ship_info[Ships[Objects[parent].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
14631                                                 if (wif->wi_flags & WIF_BEAM) {
14632                                                         dont_count = TRUE;
14633                                                 }
14634                                         }
14635                                 }
14636
14637                                 if (!dont_count) {
14638                                         float lethality = 0.025f * damage;      // 2 cyclops (@2000) put you at 100 lethality
14639
14640                                         // increase lethality weapon's parent ship
14641                                         ai_info *aip = &Ai_info[Ships[Objects[parent].instance].ai_index];
14642                                         aip->lethality += lethality;
14643                                         aip->lethality = min(110.0f, aip->lethality);
14644                                         // if you hit, don;t be less than 0
14645                                         aip->lethality = max(0.0f, aip->lethality);
14646
14647 //                                      if (aip->lethality > max_lethality) {
14648 //                                              max_lethality = aip->lethality;
14649 //                                              mprintf(("new lethalilty high: %.1f\n", max_lethality));
14650 //                                      }
14651
14652                                         // if parent is player, show his lethality
14653 //                                      if (Objects[parent].flags & OF_PLAYER_SHIP) {
14654 //                                              mprintf(("Player lethality: %.1f\n", aip->lethality));
14655 //                                      }
14656                                 }
14657                         }
14658                 }
14659         }
14660 }
14661
14662
14663 //      Object *objp_ship was hit by either weapon *objp_weapon or collided into by ship hit_objp at point *hitpos.
14664 void ai_ship_hit(object *objp_ship, object *hit_objp, vector *hitpos, int shield_quadrant, vector *hit_normal)
14665 {
14666         int             hitter_objnum = -2;
14667         object  *objp_hitter = NULL;
14668         ship            *shipp;
14669         ai_info *aip, *hitter_aip;
14670
14671         shipp = &Ships[objp_ship->instance];
14672         aip = &Ai_info[shipp->ai_index];
14673
14674         if (objp_ship->flags & OF_PLAYER_SHIP)
14675                 return;
14676
14677         if ((aip->mode == AIM_WARP_OUT) || (aip->mode == AIM_PLAY_DEAD))
14678                 return;
14679
14680         if (hit_objp->type == OBJ_SHIP) {
14681                 //      If the object that this ship collided with is a big ship
14682                 if (Ship_info[Ships[hit_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
14683                         //      And the current object is _not_ a big ship
14684                         if (!(Ship_info[Ships[objp_ship->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
14685                                 //      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.
14686                                 big_ship_collide_recover_start(objp_ship, hit_objp, hitpos, hit_normal);
14687                         }
14688                 }
14689         }
14690
14691         if (hit_objp->type == OBJ_WEAPON) {
14692                 //      Make sure the object that fired this weapon is still alive.  If not, abort.
14693                 // Assert(hit_objp->parent >= 0);
14694                 if(hit_objp->parent < 0){
14695                         return;
14696                 }
14697                 if ( hit_objp->parent_sig != Objects[hit_objp->parent].signature ){
14698                         return;
14699                 }
14700
14701                 //      Hit by a protected ship, don't attack it.
14702                 if (Objects[hit_objp->parent].flags & OF_PROTECTED) {
14703                         if ((Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) && (aip->target_objnum == -1)) {
14704                                 if (aip->mode == AIM_CHASE) {
14705                                         if (aip->submode != SM_EVADE_WEAPON) {
14706                                                 aip->mode = AIM_CHASE;
14707                                                 aip->submode = SM_EVADE_WEAPON;
14708                                                 aip->submode_start_time = Missiontime;
14709                                         }
14710                                 } else if (aip->mode != AIM_EVADE_WEAPON) {
14711                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14712                                         aip->previous_mode = aip->mode;
14713                                         aip->previous_submode = aip->submode;
14714                                         aip->mode = AIM_EVADE_WEAPON;
14715                                         aip->submode = -1;
14716                                         aip->submode_start_time = Missiontime;
14717                                         aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Evade for up to five seconds.
14718                                 }
14719
14720                         }
14721                         return;
14722                 }
14723
14724                 hitter_objnum = hit_objp->parent;
14725                 Assert((hitter_objnum >= 0) && (hitter_objnum < MAX_OBJECTS));
14726                 objp_hitter = &Objects[hitter_objnum];
14727                 maybe_process_friendly_hit(objp_hitter, objp_ship, hit_objp);           //      Deal with player's friendly fire.
14728
14729                 if ( (shipp->team & TEAM_FRIENDLY) && !(Game_mode & GM_MULTIPLAYER) ) {
14730                         ship_maybe_ask_for_help(shipp);
14731                 }
14732         } else if (hit_objp->type == OBJ_SHIP) {
14733                 if (shipp->team == Ships[hit_objp->instance].team)              //      Don't have AI react to collisions between teammates.
14734                         return;
14735                 objp_hitter = hit_objp;
14736                 hitter_objnum = hit_objp-Objects;
14737         } else {
14738                 Int3(); //      Hmm, what kind of object hit this if not weapon or ship?  Get MikeK.
14739                 return;
14740         }
14741
14742         //      Collided into a protected ship, don't attack it.
14743         if (hit_objp->flags & OF_PROTECTED)
14744                 return;
14745
14746         Assert(objp_hitter != NULL);
14747         hitter_aip = &Ai_info[Ships[objp_hitter->instance].ai_index];
14748         hitter_aip->last_hit_target_time = Missiontime;
14749         
14750         // store the object signature of objp_ship into ai_info, since we want to track the last ship hit by 'hitter_objnum'
14751         hitter_aip->last_objsig_hit = objp_ship->signature; 
14752
14753         aip->last_hit_time = Missiontime;
14754
14755         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
14756                 return;
14757
14758         //      If this ship is awaiting repair, abort!
14759         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)) {
14760                 ship_info       *sip = &Ship_info[shipp->ship_info_index];
14761
14762                 if (objp_ship->hull_strength/sip->initial_hull_strength < 0.3f) {
14763                         //      No, only abort if hull below a certain level.
14764                         aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP/2);  //      Might request again after 15 seconds.
14765                         if ( !(objp_ship->flags & OF_PLAYER_SHIP) )                                             // mwa -- don't abort rearm for a player
14766                                 ai_abort_rearm_request(objp_ship);
14767                 }
14768         }
14769
14770         //      If firing a bomb, ignore enemy fire so we can gain lock drop the bomb.
14771         //      Only ignore fire if aspect_locked_time > 0.5f, as this means we're in range.
14772         if (firing_aspect_seeking_bomb(objp_ship)) {
14773                 if ((aip->ai_flags & AIF_SEEK_LOCK) && (aip->aspect_locked_time > 0.1f))
14774                         return;
14775         }
14776
14777         //      If in AIM_STRAFE mode and got hit by target, maybe attack turret if appropriate
14778         if (aip->mode == AIM_STRAFE) {
14779                 Assert(hitter_objnum != -2);
14780                 if (aip->target_objnum == hitter_objnum) {
14781                         if ( hit_objp->type == OBJ_WEAPON ) {
14782                                 ai_big_strafe_maybe_attack_turret(objp_ship, hit_objp);
14783                         }
14784                         return;
14785                 }
14786                 else {
14787                                 // AL 11-10-97:
14788                         ;       // do nothing here, we'll attack this hitter if it is a fighter or bomber (this is handled
14789                                 // in code later in this function
14790                 }
14791         }
14792
14793         if (objp_ship == Player_obj)
14794                 return;         //      We don't do AI for the player.
14795
14796         maybe_update_guard_object(objp_ship, objp_hitter);
14797
14798         //      Big ships don't go any further.
14799         if (!(Ship_info[shipp->ship_info_index].flags & SIF_SMALL_SHIP))
14800                 return;
14801
14802         //      If the hitter object is the ignore object, don't attack it.
14803         ship_info       *sip = &Ship_info[shipp->ship_info_index];
14804         if ((is_ignore_object(aip, objp_hitter-Objects)) && (sip->flags & (SIF_BOMBER | SIF_FIGHTER))) {
14805                 if (aip->mode == AIM_NONE) {
14806                         aip->mode = AIM_CHASE;  //      This will cause the ship to move, if not attack.
14807                         aip->submode = SM_EVADE;
14808                 }
14809                 return;
14810         }
14811
14812         //      Maybe abort based on mode.
14813         switch (aip->mode) {
14814         case AIM_CHASE:
14815                 if (aip->submode == SM_ATTACK_FOREVER)
14816                         return;
14817
14818                 if ( hit_objp->type == OBJ_WEAPON ) {
14819                         if ( ai_big_maybe_enter_strafe_mode(objp_ship, OBJ_INDEX(hit_objp), 1) )
14820                                 return;
14821                 }
14822
14823         case AIM_GUARD:
14824                 //      If in guard mode and far away from guard object, don't pursue guy that hit me.
14825                         if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
14826                                 if (vm_vec_dist_quick(&objp_ship->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
14827                                         return;
14828                                 }
14829                         }
14830         case AIM_STILL:
14831         case AIM_STAY_NEAR:
14832                 // Note: Dealt with above, at very top.  case AIM_PLAY_DEAD:
14833         case AIM_STRAFE:
14834                 break;
14835         case AIM_EVADE_WEAPON:
14836         case AIM_EVADE:
14837         case AIM_GET_BEHIND:
14838         case AIM_AVOID:
14839         case AIM_DOCK:
14840         case AIM_BIGSHIP:
14841         case AIM_PATH:
14842         case AIM_NONE:
14843         case AIM_BAY_DEPART:
14844         case AIM_SENTRYGUN:
14845                 return;
14846         case AIM_BAY_EMERGE:
14847                 // If just leaving the docking bay, don't react to enemy fire... just keep flying away from docking bay
14848                 if ( (Missiontime - aip->submode_start_time) < 5*F1_0 ) {
14849                         return;
14850                 }
14851                 break;
14852         case AIM_WAYPOINTS:
14853                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER))
14854                         break;
14855                 else
14856                         return;
14857                 break;
14858         case AIM_SAFETY:
14859                 if ((aip->submode != AISS_1) || (Missiontime - aip->submode_start_time > i2f(1))) {
14860                         aip->submode = AISS_1;
14861                         aip->submode_start_time = Missiontime;
14862                 }
14863                 return;
14864                 break;
14865         case AIM_WARP_OUT:
14866                 return;
14867                 break;
14868         default:
14869                 Int3(); //      Bogus mode!
14870         }
14871
14872         if (timestamp_elapsed(aip->ok_to_target_timestamp))
14873                 aip->ai_flags &= ~AIF_FORMATION;                        //      If flying in formation, bug out!
14874
14875         aip->hitter_objnum = hitter_objnum;
14876         aip->hitter_signature = Objects[hitter_objnum].signature;
14877
14878         //      If the hitter is not on the same team as the hittee, do some stuff.
14879         if (shipp->team != Ships[objp_hitter->instance].team) {
14880                 //nprintf(("AI", "Object %i attacking %i, who just hit him!\n", objp_ship-Objects, hitter_objnum));
14881
14882                 if ((hitter_objnum != aip->target_objnum) && (sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
14883                         maybe_set_dynamic_chase(aip, hitter_objnum);
14884                         maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14885                 } else {
14886                         if ((aip->mode == AIM_CHASE) && ((objp_ship->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp_ship)/sip->shields > 0.8f))) {
14887                                 switch (aip->submode) {
14888                                 case SM_ATTACK:
14889                                 case SM_SUPER_ATTACK:
14890                                 case SM_GET_AWAY:
14891                                         break;
14892                                 default:
14893                                         if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
14894                                                 maybe_set_dynamic_chase(aip, hitter_objnum);
14895                                         }
14896                                         maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14897                                         break;
14898                                 }
14899                         } else if (aip->mode == AIM_CHASE) {
14900                                 switch (aip->submode) {
14901                                 case SM_ATTACK:
14902                                         aip->submode = SM_EVADE;
14903                                         aip->submode_start_time = Missiontime;
14904                                         break;
14905                                 case SM_SUPER_ATTACK:
14906                                         if (Missiontime - aip->submode_start_time > i2f(1)) {
14907                                                 aip->submode = SM_EVADE;
14908                                                 aip->submode_start_time = Missiontime;
14909                                         }
14910                                         break;
14911                                 case SM_EVADE_BRAKE:
14912                                         break;
14913                                 case SM_EVADE_SQUIGGLE:
14914                                         aip->submode = SM_EVADE;
14915                                         aip->submode_start_time = Missiontime;
14916                                         break;
14917                                 default:
14918                                         if (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) {
14919                                                 maybe_set_dynamic_chase(aip, hitter_objnum);
14920                                                 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14921                                         }
14922
14923                                         break;
14924                                 }
14925                         } else {
14926                                 // AL 3-15-98: Prevent escape pods from entering chase mode
14927                                 if ( (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) ) {
14928                                         maybe_set_dynamic_chase(aip, hitter_objnum);
14929                                 }
14930                                 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14931                         }
14932                 }
14933         }
14934 }
14935
14936 //      Ship shipnum has been destroyed.
14937 //      Cleanup.
14938 // the parameter 'method' is used to tell is this ship was destroyed or it departed normally.
14939 // This function will get called in either case, and there are things that should be done if
14940 // the ship actually gets destroyed which shouldn't get done if it departed.
14941 void ai_ship_destroy(int shipnum, int method)
14942 {
14943         int             objnum;
14944         object  *other_objp;
14945         ship            *shipp;
14946         ship_obj        *so;
14947         ai_info *dead_aip;
14948
14949         Assert((shipnum >= 0) && (shipnum < MAX_SHIPS));
14950         objnum = Ships[shipnum].objnum;
14951         dead_aip = &Ai_info[Ships[shipnum].ai_index];
14952
14953         // if I was getting repaired, or awaiting repair, then cleanup the repair mode.  When awaiting repair, the dock_objnum
14954         // is -1.  When the support ship is on the way, the dock_objnum >= 0 (points to support ship).
14955         if ( dead_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14956                 if ( dead_aip->dock_objnum >= 0 )
14957                         ai_do_objects_repairing_stuff( &Objects[objnum], &Objects[dead_aip->dock_objnum], REPAIR_INFO_END);
14958                 else
14959                         ai_do_objects_repairing_stuff( &Objects[objnum], NULL, REPAIR_INFO_END );
14960         }
14961
14962         //      For all objects that had this ship as a target, wipe it out, forcing find of a new enemy.
14963         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
14964                 other_objp = &Objects[so->objnum];
14965                 Assert(other_objp->instance != -1);
14966
14967                 shipp = &Ships[other_objp->instance];
14968                 Assert(shipp->ai_index != -1);
14969
14970                 ai_info *aip = &Ai_info[shipp->ai_index];
14971
14972                 // MWA 2/11/98
14973                 // code commented out below is taken care of in ai_cleanup_dock_mode when gets called when the
14974                 // support ship starts it's death roll.
14975
14976                 //      If the destroyed ship was on its way to repair the current ship
14977                 if (aip->dock_objnum == objnum) {
14978
14979                         // clean up the flags for any kind of docking mode.  If aip was part of a goal of dock/undock
14980                         // then it will get cleaned up by the goal code.
14981                         ai_do_objects_undocked_stuff( other_objp, NULL );
14982
14983                         if ( aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14984                                 int abort_reason;
14985                                 if ( method == SEF_DEPARTED ) {
14986                                         abort_reason = REPAIR_INFO_ABORT;
14987                                 } else {
14988                                         abort_reason = REPAIR_INFO_KILLED;
14989                                 }
14990                                 ai_do_objects_repairing_stuff( other_objp, NULL, abort_reason );
14991                         }
14992                 }
14993
14994                 if (aip->target_objnum == objnum) {
14995                         set_target_objnum(aip, -1);
14996                         //      If this ship had a dynamic goal of chasing the dead ship, clear the dynamic goal.
14997                         if (aip->resume_goal_time != -1)
14998                                 aip->active_goal = AI_GOAL_NONE;
14999                 }
15000
15001                 if (aip->goal_objnum == objnum) {
15002                         aip->goal_objnum = -1;
15003                         aip->goal_signature = -1;
15004                 }
15005
15006                 if (aip->guard_objnum == objnum) {
15007                         aip->guard_objnum = -1;
15008                         aip->guard_signature = -1;
15009                 }
15010
15011                 if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[Objects[objnum].instance].ai_index].wing)) {
15012                         if (aip->guard_wingnum != aip->wing)
15013                                 ai_set_guard_wing(other_objp, aip->guard_wingnum);
15014                 }
15015
15016                 if (aip->hitter_objnum == objnum)
15017                         aip->hitter_objnum = -1;
15018
15019         }
15020
15021 }
15022
15023 /*
15024 //      Interface function to goals code.
15025 //      Make object *objp fly to point *vp and warp out.
15026 void ai_warp_out(object *objp, vector *vp)
15027 {
15028         ai_info *aip;
15029
15030         aip = &Ai_info[Ships[objp->instance].ai_index];
15031
15032         if (aip->mode != AIM_WARP_OUT) {
15033                 ai_set_mode_warp_out(objp, aip);
15034         }
15035         float   dist;
15036         float   dot;
15037         vector  v2v;
15038         ai_info *aip;
15039
15040         dist = vm_vec_normalized_dir(&v2v, vp, &objp->pos);
15041
15042         if (dist < objp->radius + 5.0f) {
15043
15044                 // Start the warp out effect 
15045                 shipfx_warpout_start(objp);
15046
15047         } else {
15048                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2v);
15049
15050                 aip = &Ai_info[Ships[objp->instance].ai_index];
15051
15052                 if (dist > 500.0f)
15053                         accelerate_ship(aip, 1.0f);
15054                 else
15055                         accelerate_ship(aip, (3*dot + 1.0f)/4.0f);
15056
15057                 turn_towards_point(objp, vp, NULL, 0.0f);
15058         }
15059 }
15060 */
15061
15062
15063 //      Do stuff at start of deathroll.
15064 void ai_deathroll_start(object *ship_obj)
15065 {
15066         ai_info *aip;
15067         ship            *shipp, *other_ship;
15068
15069         shipp = &Ships[ship_obj->instance];
15070         aip = &Ai_info[shipp->ai_index];
15071
15072         // mark object we are docked with so we can do damage and separate during deathroll
15073         // keep dock_objnum_when_dead from being changed if already set (only allow to be set when -1)
15074         if (Ships[ship_obj->instance].dock_objnum_when_dead == -1) {
15075                 Ships[ship_obj->instance].dock_objnum_when_dead = aip->dock_objnum;
15076                 // set other_ship dock_objnum_when_dead, if other_ship exits.
15077                 if (Ships[ship_obj->instance].dock_objnum_when_dead != -1) {
15078                         other_ship = &Ships[Objects[aip->dock_objnum].instance];
15079                         other_ship->dock_objnum_when_dead = shipp->objnum;
15080                 }
15081         }
15082
15083         ai_cleanup_dock_mode(aip, shipp);
15084
15085         aip->mode = AIM_NONE;
15086 }
15087
15088 //      Object *requester_objp tells rearm ship to abort rearm.
15089 //      Returns true if it succeeded, else false.
15090 //      To succeed means you were previously rearming.
15091 int ai_abort_rearm_request(object *requester_objp)
15092 {
15093         ship            *requester_shipp;
15094         ai_info *requester_aip;
15095
15096         Assert(requester_objp->type == OBJ_SHIP);
15097         if(requester_objp->type != OBJ_SHIP){
15098                 return 0;
15099         }
15100         Assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));      
15101         if((requester_objp->instance < 0) || (requester_objp->instance >= MAX_SHIPS)){
15102                 return 0;
15103         }
15104         requester_shipp = &Ships[requester_objp->instance];
15105         Assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));          
15106         if((requester_shipp->ai_index < 0) || (requester_shipp->ai_index >= MAX_AI_INFO)){
15107                 return 0;
15108         }       
15109         requester_aip = &Ai_info[requester_shipp->ai_index];
15110         
15111         if (requester_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)){
15112
15113                 // dock_objnum is always valid once a rearm repair has been requested.  It points to the
15114                 // ship that is coming to repair me.
15115                 if (requester_aip->dock_objnum != -1) {
15116                         object  *repair_objp;
15117                         ai_info *repair_aip;
15118
15119                         repair_objp = &Objects[requester_aip->dock_objnum];
15120                         repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
15121
15122                         //      Make sure signatures match.  This prevents nasty bugs in which an object
15123                         //      that was repairing another is destroyed and is replaced by another ship
15124                         //      before this code comes around.
15125                         if (repair_objp->signature == requester_aip->dock_signature) {
15126
15127                                 Assert( repair_objp->type == OBJ_SHIP );
15128
15129                                 // if support ship is in the process of undocking, don't do anything.
15130                                 if ( repair_aip->submode < AIS_UNDOCK_0 ) {
15131                                         ai_do_objects_repairing_stuff( requester_objp, repair_objp, REPAIR_INFO_ABORT );
15132
15133                                         if ( repair_aip->submode == AIS_DOCK_4 )
15134                                                 repair_aip->submode = AIS_UNDOCK_0;
15135                                         else
15136                                                 repair_aip->submode = AIS_UNDOCK_3;
15137
15138                                         repair_aip->submode_start_time = Missiontime;
15139                                 } else {
15140                                         nprintf(("AI", "Not aborting rearm since already undocking\n"));
15141                                 }
15142                         }
15143                 } else {
15144                         // setting these flags is the safe things to do.  There may not be a corresponding repair
15145                         // ship for this guys since a repair ship may be currently repairing someone else.
15146                         ai_do_objects_repairing_stuff( requester_objp, NULL, REPAIR_INFO_ABORT );
15147
15148                         // try and remove this guy from an arriving support ship.
15149                         mission_remove_scheduled_repair(requester_objp);
15150                 }
15151
15152                 return 1;
15153         } else if ( requester_aip->ai_flags & AIF_REPAIRING ) {
15154                 // a support ship can request to abort when he is told to do something else (like warp out).
15155                 // see if this support ships goal_objnum is valid.  If so, then issue this ai_abort comment
15156                 // for the ship that he is enroute to repair
15157                 if ( requester_aip->goal_objnum != -1 ) {
15158                         int val;
15159
15160                         val = ai_abort_rearm_request( &Objects[requester_aip->goal_objnum] );
15161                         return val;
15162                 }
15163         }
15164
15165         return 0;
15166 }
15167
15168 // function which gets called from ai-issue_rearm_request and from code in missionparse.cpp
15169 // to actually issue the rearm goal (support_obj to rearm requester_obj);
15170 void ai_add_rearm_goal( object *requester_objp, object *support_objp )
15171 {
15172         ship *support_shipp, *requester_shipp;
15173         ai_info *support_aip, *requester_aip;
15174
15175         support_shipp = &Ships[support_objp->instance];
15176         requester_shipp = &Ships[requester_objp->instance];
15177         requester_aip = &Ai_info[requester_shipp->ai_index];
15178
15179         Assert( support_shipp->ai_index != -1 );
15180         support_aip = &Ai_info[support_shipp->ai_index];
15181
15182         // if the requester is a player object, issue the order as the squadmate messaging code does.  Doing so
15183         // ensures that the player get a higher priority!
15184         requester_aip->ai_flags |= AIF_AWAITING_REPAIR; //      Tell that I'm awaiting repair.
15185         if ( requester_objp->flags & OF_PLAYER_SHIP )
15186                 ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_REARM_REPAIR, -1, requester_shipp->ship_name, support_aip );
15187         else
15188                 ai_add_goal_ship_internal( support_aip, AI_GOAL_REARM_REPAIR, requester_shipp->ship_name, -1, -1 );
15189
15190 }
15191
15192 //      Object *requester_objp requests rearming.
15193 //      Returns objnum of ship coming to repair requester on success
15194 //      Success means you found someone to rearm you and you weren't previously rearming.
15195 int ai_issue_rearm_request(object *requester_objp)
15196 {
15197         object  *objp;
15198         ship            *requester_shipp;
15199         ai_info *requester_aip;
15200
15201         Assert(requester_objp->type == OBJ_SHIP);
15202         Assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));
15203         requester_shipp = &Ships[requester_objp->instance];
15204         Assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));
15205         requester_aip = &Ai_info[requester_shipp->ai_index];
15206         
15207         //      Make sure not already awaiting repair.
15208         if (requester_aip->ai_flags & AIF_AWAITING_REPAIR) {
15209                 nprintf(("AI", "Ship %s already awaiting rearm by ship %s.\n", requester_shipp->ship_name, &Ships[Objects[requester_aip->dock_objnum].instance].ship_name));    
15210                 return -1;
15211         }
15212
15213         if ( !is_support_allowed(requester_objp) )
15214                 return -1;
15215
15216         //nprintf(("AI", "Ship %s requesting rearming.\n", requester_shipp->ship_name));
15217         requester_aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);  //      Might request again after this much time.
15218
15219         // call ship_find_repair_ship to get a support ship.  If none is found, then we will warp one in.  This
15220         // function will return the next available ship which can repair requester
15221         objp = ship_find_repair_ship( requester_objp );
15222         ai_do_objects_repairing_stuff( requester_objp, objp, REPAIR_INFO_QUEUE );
15223         if ( objp ) {
15224
15225                 // MWA 5/14/98 -- moved next item into the ai_do_objects_repairing_stuff function so that clients
15226                 // would properly update their hud support view
15227                 //ai_add_rearm_goal( requester_objp, objp );
15228                 return OBJ_INDEX(objp);
15229
15230         } else {
15231                 // call to warp in repair ship!!!!  for now, warp in any number of ships needed.  Should cap it to
15232                 // some reasonable max (or let support ships warp out).  We should assume here that ship_find_repair_ship()
15233                 // would have returned a valid object if there are too many support ships already in the mission
15234                 mission_warp_in_support_ship( requester_objp );
15235
15236                 return -1;
15237         }
15238
15239 }
15240
15241 // make objp rearm and repair goal_objp
15242 void ai_rearm_repair( object *objp, object  *goal_objp, int priority, int docker_index, int dockee_index )
15243 {
15244         ai_info *aip, *goal_aip;
15245
15246         aip = &Ai_info[Ships[objp->instance].ai_index];
15247         aip->goal_objnum = goal_objp-Objects;
15248
15249         // nprintf(("AI", "Ship %s preparing to rearm ship %s.\n", shipp->ship_name, requester_shipp->ship_name));
15250
15251         ai_dock_with_object(objp, goal_objp, priority, AIDO_DOCK, docker_index, dockee_index);
15252         aip->ai_flags |= AIF_REPAIRING;                                         //      Tell that repair guy is busy trying to repair someone.
15253
15254         goal_aip = &Ai_info[Ships[goal_objp->instance].ai_index];
15255         goal_aip->dock_objnum = objp-Objects;           //      Tell which object is coming to repair.
15256         goal_aip->dock_signature = objp->signature;
15257
15258         ai_do_objects_repairing_stuff( goal_objp, objp, REPAIR_INFO_ONWAY );
15259
15260         goal_aip->abort_rearm_timestamp = timestamp(NEXT_REARM_TIMESTAMP*3/2);
15261 }
15262
15263 // Given a dockee object and the index of the dockbay for that object (ie the dockbay index
15264 // into polymodel->dockbays[] for the model associated with the object), return the index
15265 // of a path_num associated with than dockbay (this is an index into polymodel->paths[])
15266 int ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index)
15267 {
15268         if ( dockbay_index < 0 || dockee_objp == NULL ) {
15269                 Int3();         // should never happen
15270                 return -1;
15271         }
15272
15273         if ( dockee_objp->type == OBJ_SHIP ) {
15274                 int                     path_num;
15275                 polymodel       *pm;
15276
15277                 pm = model_get( Ships[dockee_objp->instance].modelnum );
15278
15279                 // sanity checks
15280                 Assert(pm->n_docks > dockbay_index);
15281                 Assert(pm->docking_bays[dockbay_index].num_spline_paths > 0);
15282                 Assert(pm->docking_bays[dockbay_index].splines != NULL);
15283                 if(pm->n_docks <= dockbay_index){
15284                         return -1;
15285                 }
15286                 if(pm->docking_bays[dockbay_index].num_spline_paths <= 0){
15287                         return -1;
15288                 }
15289                 if(pm->docking_bays[dockbay_index].splines == NULL){
15290                         return -1;
15291                 }
15292
15293                 // We only need to return one path for the dockbay, so return the first
15294                 path_num = pm->docking_bays[dockbay_index].splines[0];
15295                 return path_num;
15296         } else {
15297                 return -1;
15298         }
15299 }
15300
15301 //      Actually go ahead and fire the synaptics.
15302 void cheat_fire_synaptic(object *objp, ship *shipp, ai_info *aip)
15303 {
15304         ship_weapon     *swp;
15305         swp = &shipp->weapons;
15306         int     current_bank = swp->current_secondary_bank;
15307
15308         ai_select_secondary_weapon(objp, swp, WIF_SPAWN, 0);
15309         if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
15310                 if (ship_fire_secondary(objp)) {
15311                         nprintf(("AI", "ship %s cheat fired synaptic!\n", shipp->ship_name));
15312                         swp->next_secondary_fire_stamp[current_bank] = timestamp(2500);
15313                 }
15314         }
15315 }
15316
15317 //      For the subspace mission (sm3-09a)
15318 //              for delta wing
15319 //                      if they're sufficiently far into the mission
15320 //                              if they're near one or more enemies
15321 //                                      every so often
15322 //                                              fire a synaptic if they have one.
15323 void maybe_cheat_fire_synaptic(object *objp, ai_info *aip)
15324 {
15325         //      Only do in subspace missions.
15326         if ( The_mission.flags & MISSION_FLAG_SUBSPACE )        {
15327                 ship    *shipp;
15328                 int     num, time;
15329
15330                 shipp = &Ships[objp->instance];
15331
15332                 if (!(strnicmp(shipp->ship_name, NOX("delta"), 5))) {
15333                         num = shipp->ship_name[6] - '1';
15334
15335                         if ((num >= 0) && (num <= 3)) {
15336                                 time = Missiontime >> 16;       //      Convert to seconds.
15337
15338                                 time -= 2*60;   //      Subtract off two minutes.
15339
15340                                 if (time > 0) {
15341                                         int modulus = 17 + num*3;
15342
15343                                         if ((time % modulus) < 2) {
15344                                                 int count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(objp)), &objp->pos, 1500.0f);
15345
15346                                                 if (count > 0) {
15347                                                         cheat_fire_synaptic(objp, shipp, aip);
15348                                                 }
15349                                         }
15350                                 }
15351                         }
15352                 }
15353         }
15354
15355 }
15356