]> icculus.org git repositories - taylor/freespace2.git/blob - src/ship/aicode.cpp
Fix some more warnings
[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.6  2002/07/13 19:47:02  theoddone33
19  * Fix some more warnings
20  *
21  * Change demo building, edit Makefile if you want the demo.
22  *
23  * Revision 1.5  2002/06/17 06:33:10  relnev
24  * ryan's struct patch for gcc 2.95
25  *
26  * Revision 1.4  2002/06/09 04:41:26  relnev
27  * added copyright header
28  *
29  * Revision 1.3  2002/06/01 07:12:34  relnev
30  * a few NDEBUG updates.
31  *
32  * removed a few warnings.
33  *
34  * Revision 1.2  2002/05/03 13:34:33  theoddone33
35  * More stuff compiles
36  *
37  * Revision 1.1.1.1  2002/05/03 03:28:10  root
38  * Initial import.
39  *
40  * 
41  * 107   9/15/99 4:42a Mikek
42  * Make any big ship attacking Colossus, or Colossus attacking any large
43  * ship not use big cruiser movement code.
44  * 
45  * 106   9/15/99 3:28a Jimb
46  * Make all big ships in sm3-08 not do cruiser chase code when attacking
47  * Colossus.  Added so Beast doesn't swerve away from Colossus.
48  * 
49  * 105   9/14/99 4:18p Andsager
50  * hack for mission sm3-08 to abort cruiser_chase as sathanas is about to
51  * begin circling colossus.
52  * 
53  * 104   9/08/99 10:44p Andsager
54  * Make HUGE ships not die when warping out, after warp effect started.
55  * 
56  * 103   9/03/99 11:40p Mikek
57  * Comment out an annoying nprintf().
58  * 
59  * 102   9/01/99 11:26p Dave
60  * Fixed release build warnings.
61  * 
62  * 101   9/01/99 9:12p Mikek
63  * Make it a boatload harder to become a traitor from hitting a large
64  * ship.
65  * 
66  * 100   9/01/99 4:01p Andsager
67  * Make sure BIG|HUGE ships do not respond to shockwaves
68  * 
69  * 99    9/01/99 10:09a Dave
70  * Pirate bob.
71  * 
72  * 98    8/31/99 4:24p Andsager
73  * Reduce collisions when attacking big ships.
74  * 
75  * 97    8/31/99 7:33a Mikek
76  * Improvements in formation flying, less silly behavior, especially when
77  * leader is moving very slowly.
78  * 
79  * 96    8/31/99 5:48a Mikek
80  * Making ships not overshoot so much in formation flying.  Intermediate
81  * checkin.
82  * 
83  * 95    8/30/99 12:03a Mikek
84  * Make guard behavior much less annoying.  Guarders don't get quite so
85  * close and they try to avoid striking the target they are guarding.
86  * 
87  * 94    8/29/99 4:18p Andsager
88  * New "burst" limit for friendly damage.  Also credit more damage done
89  * against large friendly ships.
90  * 
91  * 93    8/28/99 7:29p Dave
92  * Fixed wingmen persona messaging. Make sure locked turrets don't count
93  * towards the # attacking a player.
94  * 
95  * 92    8/26/99 10:46p Andsager
96  * Apply shockwave damage to lethality.
97  * 
98  * 91    8/26/99 8:52p Dave
99  * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
100  * 
101  * 90    8/26/99 5:14p Andsager
102  * 
103  * 89    8/24/99 8:55p Dave
104  * Make sure nondimming pixels work properly in tech menu.
105  * 
106  * 88    8/23/99 6:21p Jefff
107  * added "no traitor" option to missions (and fred)
108  * 
109  * 87    8/20/99 3:36p Andsager
110  * Make sure we don;t miss stealth sweep points.
111  * 
112  * 86    8/16/99 8:21a Andsager
113  * fix link error
114  * 
115  * 85    8/16/99 8:19a Andsager
116  * Add project_point_onto_bbox() to fvi and include in aicode
117  * 
118  * 84    8/15/99 1:30p Dave
119  * Removed some bounding box code because of link errors. Assuming needed
120  * function just needs to get checked in by DaveA.
121  * 
122  * 83    8/15/99 11:59a Andsager
123  * For targing big/huge ships, find nearest distance to bbox, not center.
124  * 
125  * 82    8/13/99 2:20p Andsager
126  * Add speed modification to chances turret will find stealth ship
127  * 
128  * 81    8/13/99 10:49a Andsager
129  * Knossos and HUGE ship warp out.  HUGE ship warp in.  Stealth search
130  * modes dont collide big ships.
131  * 
132  * 80    8/10/99 5:02p Andsager
133  * Fix bug where AI gets stuck in SM_EVADE_WEAPON with no target.
134  * 
135  * 79    8/10/99 11:58a Andsager
136  * Allow turrets to sometimes see stealth.
137  * 
138  * 78    7/31/99 2:57p Dave
139  * Scaled flak aim and jitter by weapon subsystem strength.
140  * 
141  * 77    7/27/99 10:33p Andsager
142  * improve ai for attacking stealth.  reduced jitter in aim.  reduced
143  * error in position when avoiding.  skill level support for attacking
144  * stealth.  Made target error same for team vs. team.
145  * 
146  * 76    7/27/99 10:49a Andsager
147  * Make turret fire rate independent of team for HUGE turrets, and also
148  * for mult team vs. team.
149  * 
150  * 75    7/26/99 12:14p Andsager
151  * Apply cap to how much slower a transport flies with cargo.  Remove
152  * limit on waypoint speed for training.  Enemy ai get stealth exact pos
153  * when stealth fires
154  * 
155  * 74    7/20/99 1:49p Dave
156  * Peter Drake build. Fixed some release build warnings.
157  * 
158  * 73    7/19/99 2:13p Dave
159  * Added some new strings for Heiko.
160  * 
161  * 72    7/19/99 12:02p Andsager
162  * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
163  * only blow up subsystem if its strength is > 0
164  * 
165  * 71    7/15/99 9:20a Andsager
166  * FS2_DEMO initial checkin
167  * 
168  * 70    7/14/99 1:44p Andsager
169  * modify ai_guard for BIG ships to circle around the long axis
170  * 
171  * 69    7/09/99 5:54p Dave
172  * Seperated cruiser types into individual types. Added tons of new
173  * briefing icons. Campaign screen.
174  * 
175  * 68    7/08/99 4:32p Andsager
176  * fix bug with turret-tagged-only
177  * 
178  * 67    7/08/99 12:06p Andsager
179  * Add turret-tagged-only and turret-tagged-clear sexp.
180  * 
181  * 66    7/02/99 3:49p Andsager
182  * Remove debug code.  Allow targeting of stealth from any weapon it
183  * fires.
184  * 
185  * 65    7/02/99 2:01p Andsager
186  * Fix bug where big ship tries to evade dumpfire weapon.
187  * 
188  * 64    7/02/99 10:58a Andsager
189  * Put in big ship - big ship attack mode.  Modify stealth sweep ai.
190  * 
191  * 63    6/30/99 5:53p Dave
192  * Put in new anti-camper code.
193  * 
194  * 62    6/28/99 3:22p Anoop
195  * Fix turret optimization, where ship may not have any valid subsystems
196  * (all blown off).
197  * 
198  * 61    6/25/99 5:56p Andsager
199  * First real pass on stealth ai.
200  * 
201  * 60    6/25/99 3:08p Dave
202  * Multiple flyby sounds.
203  * 
204  * 59    6/25/99 1:12p Danw
205  * DKA:  Make sure big ship has subsystems before trying to target them.
206  * 
207  * 58    6/25/99 10:56a Johnson
208  * Fixed dumb ai code.
209  * 
210  * 57    6/24/99 5:15p Dave
211  * Make sure stride is always at least one for checking turret subsystem
212  * targets.
213  * 
214  * 56    6/24/99 4:59p Dave
215  * Significant speedups to turret firing.
216  * 
217  * 55    6/23/99 5:51p Andsager
218  * Add waypoint-cap-speed.  Checkin stealth ai - inactive.
219  * 
220  * 54    6/16/99 10:21a Dave
221  * Added send-message-list sexpression.
222  * 
223  * 53    6/15/99 9:25a Andsager
224  * Make guard and dynamic chase (who hit you) work with stealth
225  * 
226  * 52    6/14/99 3:21p Andsager
227  * Allow collisions between ship and its debris.  Fix up collision pairs
228  * when large ship is warping out.
229  * 
230  * 51    6/14/99 10:45a Dave
231  * Made beam weapons specify accuracy by skill level in the weapons.tbl
232  * 
233  * 50    6/03/99 8:11a Andsager
234  * 
235  * 49    6/02/99 5:41p Andsager
236  * Reduce range of secondary weapons not fired from turrets in nebula.
237  * Reduce range of beams fired from turrrets in nebula
238  * 
239  * 48    6/02/99 3:23p Andsager
240  * Make AI aware of team visibility.  Allow player targeting with team
241  * visibility info.  Make stealth ships not targetable by AI in nebula
242  * unless tagged.
243  * 
244  * 47    6/02/99 12:52p Andsager
245  * Added team-wide ship visibility.  Implemented for player.
246  * 
247  * 46    6/01/99 8:35p Dave
248  * Finished lockarm weapons. Added proper supercap weapons/damage. Added
249  * awacs-set-radius sexpression.
250  * 
251  * 45    5/28/99 5:35p Andsager
252  * Make ai nebula aware
253  * 
254  * 44    5/24/99 9:55a Dave
255  * Fixed stream weapon ai firing problem. ick.
256  * 
257  * 43    5/20/99 7:00p Dave
258  * Added alternate type names for ships. Changed swarm missile table
259  * entries.
260  * 
261  * 42    5/18/99 1:30p Dave
262  * Added muzzle flash table stuff.
263  * 
264  * 41    5/12/99 2:55p Andsager
265  * Implemented level 2 tag as priority in turret object selection
266  * 
267  * 40    5/12/99 10:42a Andsager
268  * Fix turret bug allowing HUGE turrets to fire at fighters
269  * 
270  * 39    5/06/99 11:46a Andsager
271  * Bug fixes.  Don't get into illegal strafe submode.  Don't choose turret
272  * enemy objnum for beam protected.
273  * 
274  * 38    5/03/99 10:50p Andsager
275  * Make Asteroid_obj_list.  Change get_nearest_turret_objnum() to use
276  * Asteroid_obj_list, Ship_obj_list and Missile_obj_list vs.
277  * obj_used_list.
278  * 
279  * 37    4/29/99 2:29p Dave
280  * Made flak work much better in multiplayer.
281  * 
282  * 36    4/28/99 11:36p Dave
283  * Tweaked up subspace missile strike a bit,
284  * 
285  * 35    4/28/99 3:11p Andsager
286  * Stagger turret weapon fire times.  Make turrets smarter when target is
287  * protected or beam protected.  Add weaopn range to weapon info struct.
288  * 
289  * 34    4/26/99 10:58a Andsager
290  * Add OF_BEAM_PROTECTED flag to keep object from being targeted for zing.
291  * 
292  * 33    4/23/99 12:12p Andsager
293  * Modify wing positions when player is wing leader to prevent some
294  * collisions.
295  * 
296  * 32    4/23/99 12:01p Johnson
297  * Added SIF_HUGE_SHIP
298  * 
299  * 31    4/22/99 11:06p Dave
300  * Final pass at beam weapons. Solidified a lot of stuff. All that remains
301  * now is to tweak and fix bugs as they come up. No new beam weapon
302  * features.
303  * 
304  * 30    4/20/99 6:39p Dave
305  * Almost done with artillery targeting. Added support for downloading
306  * images on the PXO screen.
307  * 
308  * 29    4/20/99 3:40p Andsager
309  * Changes to big ship ai.  Uses bounding box as limit where to fly to
310  * when flying away.
311  * 
312  * 28    4/16/99 5:54p Dave
313  * Support for on/off style "stream" weapons. Real early support for
314  * target-painting lasers.
315  * 
316  * 27    4/02/99 9:55a Dave
317  * Added a few more options in the weapons.tbl for beam weapons. Attempt
318  * at putting "pain" packets into multiplayer.
319  * 
320  * 26    3/28/99 5:58p Dave
321  * Added early demo code. Make objects move. Nice and framerate
322  * independant, but not much else. Don't use yet unless you're me :)
323  * 
324  * 25    3/19/99 9:51a Dave
325  * Checkin to repair massive source safe crash. Also added support for
326  * pof-style nebulae, and some new weapons code.
327  * 
328  * 24    3/08/99 7:03p Dave
329  * First run of new object update system. Looks very promising.
330  * 
331  * 23    3/05/99 3:55p Anoop
332  * Handle some asserts properly.
333  * 
334  * 22    3/04/99 6:09p Dave
335  * Added in sexpressions for firing beams and checking for if a ship is
336  * tagged.
337  * 
338  * 21    3/02/99 9:25p Dave
339  * Added a bunch of model rendering debug code. Started work on fixing
340  * beam weapon wacky firing.
341  * 
342  * 20    2/25/99 2:32p Anoop
343  * (Alan). Fixed ai path following code for AI_BAY_EMERGE. Put in sanity
344  * check so that when the last point on the path is reached, it finishes.
345  * 
346  * 19    2/19/99 2:11p Anoop
347  * Put in some nice handling code for wacky support ship problems (like no
348  * docking paths)
349  * 
350  * 18    2/17/99 2:11p Dave
351  * First full run of squad war. All freespace and tracker side stuff
352  * works.
353  * 
354  * 17    2/11/99 5:22p Andsager
355  * Fixed bugs, generalized block Sexp_variables
356  * 
357  * 16    1/29/99 5:07p Dave
358  * Fixed multiplayer stuff. Put in multiplayer support for rapid fire
359  * missiles.
360  * 
361  * 15    1/29/99 2:25p Andsager
362  * Added turret_swarm_missiles
363  * 
364  * 14    1/27/99 9:56a Dave
365  * Temporary checkin of beam weapons for Dan to make cool sounds.
366  * 
367  * 13    1/24/99 11:37p Dave
368  * First full rev of beam weapons. Very customizable. Removed some bogus
369  * Int3()'s in low level net code.
370  * 
371  * 12    1/21/99 10:44a Dave
372  * More beam weapon stuff. Put in warmdown time.
373  * 
374  * 11    1/12/99 5:45p Dave
375  * Moved weapon pipeline in multiplayer to almost exclusively client side.
376  * Very good results. Bandwidth goes down, playability goes up for crappy
377  * connections. Fixed object update problem for ship subsystems.
378  * 
379  * 10    1/08/99 2:08p Dave
380  * Fixed software rendering for pofview. Super early support for AWACS and
381  * beam weapons.
382  * 
383  * 9     12/23/98 2:53p Andsager
384  * Added ship activation and gas collection subsystems, removed bridge
385  * 
386  * 8     11/12/98 12:13a Dave
387  * Tidied code up for multiplayer test. Put in network support for flak
388  * guns.
389  * 
390  * 7     11/05/98 5:55p Dave
391  * Big pass at reducing #includes
392  * 
393  * 6     10/26/98 9:42a Dave
394  * Early flak gun support.
395  * 
396  * 5     10/23/98 3:51p Dave
397  * Full support for tstrings.tbl and foreign languages. All that remains
398  * is to make it active in Fred.
399  * 
400  * 4     10/20/98 1:39p Andsager
401  * Make so sparks follow animated ship submodels.  Modify
402  * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
403  * submodel_num.  Add submodel_num to multiplayer hit packet.
404  * 
405  * 3     10/13/98 9:29a Dave
406  * Started neatening up freespace.h. Many variables renamed and
407  * reorganized. Added AlphaColors.[h,cpp]
408  * 
409  * 2     10/07/98 10:53a Dave
410  * Initial checkin.
411  * 
412  * 1     10/07/98 10:51a Dave
413  * 
414  * 
415  * $NoKeywords: $
416  */
417
418 // This module contains the actual AI code that does interesting stuff
419 // to objects.   The code in Ai.cpp is just for bookeeping, allocating
420 // ai slots and linking them to ships.
421
422 #include "pstypes.h"
423 #include "fix.h"
424 #include "linklist.h"
425 #include "object.h"
426 #include "physics.h"
427 #include "vecmat.h"
428 #include "ship.h"
429 #include "model.h"
430 #include "2d.h"
431 #include "3d.h"
432 #include "ai.h"
433 #include "floating.h"
434 #include "player.h"
435 #include "freespace.h"
436 #include "weapon.h"
437 #include "missiongoals.h"
438 #include "missionlog.h"
439 #include "timer.h"
440 #include "sound.h"
441 #include "aigoals.h"
442 #include "gamesnd.h"
443 #include "hudmessage.h"
444 #include "missionmessage.h"
445 #include "cmeasure.h"
446 #include "staticrand.h"
447 #include "multimsgs.h"
448 #include "afterburner.h"
449 #include "hudets.h"
450 #include "shipfx.h"
451 #include "shiphit.h"
452 #include "aibig.h"
453 #include "multiutil.h"
454 #include "hud.h"
455 #include "objcollide.h"
456 #include "asteroid.h"
457 #include "hudlock.h"
458 #include "missiontraining.h"
459 #include "gamesequence.h"
460 #include "joy_ff.h"
461 #include "localize.h"
462 #include "flak.h"
463 #include "beam.h"
464 #include "multi.h"
465 #include "swarm.h"
466 #include "multi_team.h"
467 #include "awacs.h"
468 #include "fvi.h"
469
470 #ifndef PLAT_UNIX
471 #pragma optimize("", off)
472 #pragma auto_inline(off)
473 #endif
474
475 #define UNINITIALIZED_VALUE     -99999.9f
476
477 #define INSTRUCTOR_SHIP_NAME NOX("instructor")
478
479 #define AICODE_SMALL_MAGNITUDE  0.001f          // cosider a vector NULL if mag is less than this
480
481 #define NEXT_REARM_TIMESTAMP (60*1000)                  //      Ships will re-request rearm, typically, after this long.
482
483 #define BEAM_NEBULA_RANGE_REDUCE_FACTOR         0.8
484
485 // AIM_CHASE submode defines
486 // SM_STEALTH_FIND
487 #define SM_SF_AHEAD             0
488 #define SM_SF_BEHIND    1
489 #define SM_SF_BAIL              2
490
491 // SM_STEALTH_SWEEP
492 #define SM_SS_SET_GOAL  -1
493 #define SM_SS_BOX0              0
494 #define SM_SS_LR                        1
495 #define SM_SS_UL                        2
496 #define SM_SS_BOX1              3
497 #define SM_SS_UR                        4
498 #define SM_SS_LL                        5
499 #define SM_SS_BOX2              6
500 #define SM_SS_DONE              7
501
502 //XSTR:OFF
503
504 char *Mode_text[MAX_AI_BEHAVIORS] = {
505         "CHASE",
506         "EVADE",
507         "GET_BEHIND",
508         "CHASE_LONG",
509         "SQUIGGLE",
510         "GUARD",
511         "AVOID",
512         "WAYPOINTS",
513         "DOCK",
514         "NONE",
515         "BIGSHIP",
516         "PATH",
517         "BE_REARMED",
518         "SAFETY",
519         "EV_WEAPON",
520         "STRAFE",
521         "PLAY_DEAD",
522         "BAY_EMERGE",
523         "BAY_DEPART",
524         "SENTRYGUN",
525         "WARP_OUT",
526 };
527
528 //      Submode text is only valid for CHASE mode.
529 char *Submode_text[] = {
530 "undefined",
531 "CONT_TURN",
532 "ATTACK   ",
533 "E_SQUIG  ",
534 "E_BRAKE  ",
535 "EVADE    ",
536 "SUP_ATTAK",
537 "AVOID    ",
538 "BEHIND   ",
539 "GET_AWAY ",
540 "E_WEAPON ",
541 "FLY_AWAY ",
542 "ATK_4EVER",
543 "STLTH_FND",
544 "STLTH_SWP",
545 "BIG_APPR",
546 "BIG_CIRC",
547 "BIG_PARL"
548 };
549
550 char *Strafe_submode_text[5] = {
551 "ATTACK",
552 "AVOID",
553 "RETREAT1",
554 "RETREAT2",
555 "POSITION"
556 };
557 //XSTR:ON
558
559 /*
560 //      Used for global ignore of objects.  If an object appears in the Ignore_objects array,
561 //      no one will attack it.
562 #define MAX_IGNORE_OBJECTS      16
563 typedef struct {
564         int     objnum;
565         int     signature;
566 } ignore_object;
567
568 ignore_object   Ignore_objects[MAX_IGNORE_OBJECTS];
569 */
570
571 typedef struct eval_enemy_obj_struct {
572         int                     turret_parent_objnum;                   // parent of turret
573         float                   weapon_travel_dist;                             // max targeting range of turret weapon
574         int                     enemy_team_mask;
575         int                     weapon_system_ok;                                       // is the weapon subsystem of turret ship ok
576         int                     big_only_flag;                                          // turret fires only at big and huge ships
577         vector          *tpos;
578         vector          *tvec;
579         ship_subsys *turret_subsys;
580         int                     current_enemy;
581
582
583         float                   nearest_attacker_dist;                  // nearest ship 
584         int                     nearest_attacker_objnum;
585
586         float                   nearest_homing_bomb_dist;               // nearest homing bomb
587         int                     nearest_homing_bomb_objnum;
588
589         float                   nearest_bomb_dist;                              // nearest non-homing bomb
590         int                     nearest_bomb_objnum;
591
592         float                   nearest_dist;                                           // nearest ship attacking this turret
593         int                     nearest_objnum;
594 }       eval_enemy_obj_struct;
595
596
597 control_info    AI_ci;
598
599 object *Pl_objp;
600 object *En_objp;
601
602 waypoint_list Waypoint_lists[MAX_WAYPOINT_LISTS];
603
604 // How close a turret has to be point at its target before it
605 // can fire.  If the dot of the gun normal and the vector from gun
606 // to target is greater than this, the turret fires.  The smaller
607 // the sloppier the shooting.
608 #define AICODE_TURRET_DUMBFIRE_ANGLE            (0.8f)  
609 #define AICODE_TURRET_HEATSEEK_ANGLE            (0.7f)  
610 #define AICODE_TURRET_MAX_TIME_IN_RANGE (5.0f)
611
612 #define REARM_SOUND_DELAY               (3*F1_0)                //      Amount of time to delay rearm/repair after mode start
613 #define REARM_BREAKOFF_DELAY    (3*F1_0)                //      Amount of time to wait after fully rearmed to breakoff.
614
615 #define MIN_DIST_TO_WAYPOINT_GOAL       5.0f
616 #define MAX_GUARD_DIST                                  250.0f
617 #define BIG_GUARD_RADIUS                                500.0f
618
619 #define MAX_EVADE_TIME                  (15 * 1000)     //      Max time to evade a weapon.
620
621 // defines for repair ship stuff.
622 #define MAX_REPAIR_SPEED                        25.0f
623 #define MAX_UNDOCK_ABORT_SPEED  2.0f
624
625 // defines for EMP effect stuff
626 #define MAX_EMP_INACCURACY              50.0f
627
628 // defines for stealth
629 #define MAX_STEALTH_INACCURACY  50.0f           // at max view dist
630 #define STEALTH_MAX_VIEW_DIST   400             // dist at which 1) stealth no longer visible 2) firing inaccuracy is greatest
631 #define STEALTH_VIEW_CONE_DOT   0.707           // (half angle of 45 degrees)
632
633
634 ai_class        Ai_classes[MAX_AI_CLASSES];
635 int     Ai_firing_enabled = 1;
636 int     Num_ai_classes;
637
638 int     AI_FrameCount = 0;
639 int     Ship_info_inited = 0;
640 int     AI_watch_object = 0; // Debugging, object to spew debug info for.
641 int     Num_waypoint_lists = 0;
642 int     Mission_all_attack = 0;                                 //      !0 means all teams attack all teams.
643
644 char *Skill_level_names(int level, int translate)
645 {
646         char *str = NULL;
647
648         #if NUM_SKILL_LEVELS != 5
649         #error Number of skill levels is wrong!
650         #endif
651
652         if(translate){
653                 switch( level ) {
654                 case 0:
655                         str = XSTR("Very Easy", 469);
656                         break;
657                 case 1:
658                         str = XSTR("Easy", 470);
659                         break;
660                 case 2:
661                         str = XSTR("Medium", 471);
662                         break;
663                 case 3:
664                         str = XSTR("Hard", 472);
665                         break;
666                 case 4:
667                         str = XSTR("Insane", 473);
668                         break;
669                 default:        
670                         Int3();
671                 }
672         } else {
673                 switch( level ) {
674                 case 0:
675                         str = NOX("Very Easy");
676                         break;
677                 case 1:
678                         str = NOX("Easy");
679                         break;
680                 case 2:
681                         str = NOX("Medium");
682                         break;
683                 case 3:
684                         str = NOX("Hard");
685                         break;
686                 case 4:
687                         str = NOX("Insane");
688                         break;
689                 default:        
690                         Int3();
691                 }
692         }
693
694         return str;
695 }
696
697 #define DELAY_TARGET_TIME       (12*1000)               //      time in milliseconds until a ship can target a new enemy after an order.
698
699 //      Make enemy ships turn more slowly at lower skill levels.
700 float   Turn_time_skill_level_scale[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.6f, 1.3f, 1.0f};
701
702 //      Maximum number of simultaneous homing weapons on player based on skill level.
703 int     Max_allowed_player_homers[NUM_SKILL_LEVELS] = {2, 3, 4, 7, 99};
704
705 //      Number of ships that can attack another ship at a given skill level.
706 int     Skill_level_max_attackers[NUM_SKILL_LEVELS] = {2, 3, 4, 5, 99};
707
708 //      How long until next predict position.
709 fix Skill_level_delay[NUM_SKILL_LEVELS] = {2*F1_0, 3*F1_0/2, 4*F1_0/3, F1_0/2, 0};
710
711 //      AI ships link primary weapons if energy levels greater than the following amounts:
712 float   Link_energy_levels_always[NUM_SKILL_LEVELS] = {100.0f, 80.0f, 60.0f, 40.0f, 20.0f};     //      always link
713 float   Link_energy_levels_maybe[NUM_SKILL_LEVELS] = {90.0f, 60.0f, 40.0f, 20.0f, 10.0f};       //      link if hull strength low
714
715 //      Seconds to add to time it takes to get enemy in range.  Only for player's enemies.
716 float   In_range_time[NUM_SKILL_LEVELS] = {2.0f, 1.4f, 0.75f, 0.0f, -1.0f};
717
718 //      No matter what, a random unit vector gets scaled by this amount in firing at an enemy.
719 //      Note that for shorter in-range times, these values get scaled, so a value of 0.5f is meaningful.
720 float   Aiming_error[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.3f, 0.7f, 0.2f};
721
722 //      Chance a countermeasure will be fired based on skill level.
723 float Cmeasure_fire_chance[NUM_SKILL_LEVELS] = {0.2f, 0.3f, 0.5f, 0.9f, 1.1f};  //      Note, this gets scaled by ai_class
724
725 float Shield_manage_delays[NUM_SKILL_LEVELS] = {5.0f, 4.0f, 2.5f, 1.2f, 0.1f};
726
727 // accuracy we feed into the beam weapons based upon skill system
728 // float Beam_accuracy[NUM_SKILL_LEVELS] = {2.0f, 1.5f, 1.0f, 0.7f, 0.4f};
729
730 extern float Ship_fire_delay_scale_hostile[NUM_SKILL_LEVELS];
731 extern float Ship_fire_delay_scale_friendly[NUM_SKILL_LEVELS];
732
733 pnode           Path_points[MAX_PATH_POINTS];
734 pnode           *Ppfp;                  //      Free pointer in path points.
735
736 float   AI_frametime;
737
738 char *Ai_class_names[MAX_AI_CLASSES];
739
740 // global for rearm status for teams
741 int Ai_friendly_rearm_timestamp, Ai_hostile_rearm_timestamp, Ai_neutral_rearm_timestamp, Ai_traitor_rearm_timestamp, Ai_unknown_rearm_timestamp;
742
743 // globals for dealing with when to fire huge secondary weapons
744 #define MAX_HUGE_SECONDARY_INFO 10
745
746 typedef struct {
747         int team;
748         int weapon_index;
749         int max_fire_count;
750         char    *shipname;
751 } huge_fire_info;
752
753 huge_fire_info Ai_huge_fire_info[MAX_HUGE_SECONDARY_INFO];
754
755 int Ai_last_arrive_path;        // index of ship_bay path used by last arrival from a fighter bay
756
757 // forward declarations
758 int     ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index);
759 void    create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count=1);
760 void    copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt=-1);
761
762 // ai_set_rearm_status takes a team (friendly, hostile, neutral) and a time.  This function
763 // sets the timestamp used to tell is it is a good time for this team to rearm.  Once the timestamp
764 // is no longer valid, then rearming is not a "good time"
765 // not safe.  Called from sexpression code.
766 void ai_set_rearm_status( int team, int time )
767 {
768         Assert( time >= 0 );
769
770         switch (team) {
771         case TEAM_FRIENDLY:
772                 Ai_friendly_rearm_timestamp = timestamp( time * 1000 );
773                 break;
774         case TEAM_HOSTILE:
775                 Ai_hostile_rearm_timestamp = timestamp( time * 1000 );
776                 break;
777         case TEAM_NEUTRAL:
778                 Ai_neutral_rearm_timestamp = timestamp( time * 1000 );
779                 break;
780         case TEAM_TRAITOR:
781                 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
782                 break;
783         case TEAM_UNKNOWN:
784                 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
785                 break;
786         default:
787                 Int3();
788                 break;
789         }
790 }
791
792 // int ai_good_time_to_rearm returns true(1) or false(0) if it is "safe" for the given
793 // object to rearm.  "safe" is currently defined by the mission designer using the good/bad
794 // time to rearm sexpressions.  This status is currently team based.  This function could
795 // be easily expended to further the definition of "safe"
796 int ai_good_time_to_rearm( object *objp )
797 {
798         int team, status;
799
800         Assert(objp->type == OBJ_SHIP);
801         team = Ships[objp->instance].team;
802         status = 0;
803
804         switch(team) {
805         case TEAM_FRIENDLY:
806                 status = timestamp_valid(Ai_friendly_rearm_timestamp);
807                 break;
808         case TEAM_HOSTILE:
809                 status = timestamp_valid(Ai_hostile_rearm_timestamp);
810                 break;
811         case TEAM_NEUTRAL:
812                 status = timestamp_valid(Ai_neutral_rearm_timestamp);
813                 break;
814         case TEAM_TRAITOR:
815                 status = timestamp_valid(Ai_traitor_rearm_timestamp);
816                 break;
817         case TEAM_UNKNOWN:
818                 status = timestamp_valid(Ai_unknown_rearm_timestamp);
819                 break;
820         default:
821                 Int3();
822                 break;
823         }
824
825         return status;
826 }
827
828 // functions to deal with letting the ai know about good times to fire powerful secondary
829 // weapons.
830
831 // this function is entry point from sexpression code to set internal data for use by ai code.
832 void ai_good_secondary_time( int team, int weapon_index, int max_fire_count, char *shipname )
833 {
834         int i, index;
835
836         // find an open slot to put this data
837         for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
838                 if ( Ai_huge_fire_info[i].weapon_index == -1 )
839                         break;
840         }
841
842         Assert( i < MAX_HUGE_SECONDARY_INFO );                  // we've run out of room
843
844         Ai_huge_fire_info[i].weapon_index = weapon_index;
845         Ai_huge_fire_info[i].team = team;
846         Ai_huge_fire_info[i].max_fire_count = max_fire_count;
847
848         Ai_huge_fire_info[i].shipname = ai_get_goal_ship_name( shipname, &index );
849 }
850
851 // function called internally to the ai code to tell whether or not weapon_num can be fired
852 // from firer_objp at target_objp.  This function will resolve the team for the firer.
853 // returns:
854 //              -1  -- when conditions don't allow firer to fire weapon_num on target_objp
855 //              >=0 -- when conditions allow firer to fire.  Return value is max number of weapon_nums
856 //           which can be fired on target_objp
857 int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
858 {
859         int i, firer_team, target_signature;
860         ship *firer_ship;
861         huge_fire_info *hfi = NULL;
862
863         Assert( firer_objp->type == OBJ_SHIP );
864         firer_ship = &Ships[firer_objp->instance];
865         firer_team = firer_ship->team;
866
867         // get target object's signature and try to find it in the list.
868         target_signature = target_objp->signature;
869         for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
870                 int ship_index, signature;
871
872                 hfi = &Ai_huge_fire_info[i];
873                 if ( hfi->weapon_index == -1 )
874                         continue;
875
876                 ship_index = ship_name_lookup( hfi->shipname );
877                 if ( ship_index == -1 )
878                         continue;
879
880                 signature = Objects[Ships[ship_index].objnum].signature;
881
882                 // sigatures, weapon_index, and team must match
883                 if ( (signature == target_signature) && (hfi->weapon_index == weapon_num) && (hfi->team == firer_team) )
884                         break;
885         }
886
887         // return -1 if not found
888         if ( i == MAX_HUGE_SECONDARY_INFO )
889                 return -1;
890
891         // otherwise, we can return the max number of weapons we can fire against target_objps
892
893         return hfi->max_fire_count;
894 }
895
896 // function to clear out secondary firing infomration between levels
897 void ai_init_secondary_info()
898 {
899         int i;
900
901         // clear out the data for dealing with when ai ships can fire huge secondary weapons
902         for (i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
903                 Ai_huge_fire_info[i].weapon_index = -1;
904                 Ai_huge_fire_info[i].team = -1;
905                 Ai_huge_fire_info[i].max_fire_count = -1;
906                 Ai_huge_fire_info[i].shipname = NULL;
907         }
908 }
909
910
911 //      Garbage collect the Path_points buffer.
912 //      Scans all objects, looking for used Path_points records.
913 //      Compresses Path_points buffer, updating aip->path_start and aip->path_cur indices.
914 //      Updates Ppfp to point to first free record.
915 //      This function is fairly fast.  Its worst-case running time is proportional to
916 //      3*MAX_PATH_POINTS + MAX_OBJECTS
917 //      Things to do to optimize this function:
918 //              1. if (t != 0) xlt++; can be replaced by xlt += t; assuming t can only be 0 or 1.
919 //              2. When pp_xlate is getting stuffed the first time, note highest index and use that 
920 //                      instead of MAX_PATH_POINTS in following two for loops.
921 void garbage_collect_path_points()
922 {
923         int     i;
924         int     pp_xlate[MAX_PATH_POINTS];
925         object  *A;
926         ship_obj        *so;
927
928         //      Scan all objects and create Path_points xlate table.
929         for (i=0; i<MAX_PATH_POINTS; i++)
930                 pp_xlate[i] = 0;
931
932         //      in pp_xlate, mark all used Path_point records
933         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
934                 A = &Objects[so->objnum];
935                 ship    *shipp = &Ships[A->instance];
936                 if (shipp->ai_index != -1) {
937                         ai_info *aip = &Ai_info[shipp->ai_index];
938
939                         if ((aip->path_length > 0) && (aip->path_start > -1)) {
940
941                                 for (int i=aip->path_start; i<aip->path_start + aip->path_length; i++) {
942                                         Assert(pp_xlate[i] == 0);       //      If this is not 0, then two paths use this point!
943                                         pp_xlate[i] = 1;
944                                 }
945                         }
946                 }
947         }
948
949         //      Now, stuff xlate index in pp_xlate.  This is the number to translate any path_start
950         //      or path_cur index to.
951         int     xlt = 0;
952         for (i=0; i<MAX_PATH_POINTS; i++) {
953                 int     t = pp_xlate[i];
954
955                 pp_xlate[i] = xlt;
956                 if (t != 0)
957                         xlt++;
958         }
959         
960         //      Update global Path_points free pointer.
961         Ppfp = &Path_points[xlt];
962
963         //      Now, using pp_xlate, fixup all aip->path_cur and aip->path_start indices
964         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
965                 A = &Objects[so->objnum];
966                 ship    *shipp = &Ships[A->instance];
967                 if (shipp->ai_index != -1) {
968                         ai_info *aip = &Ai_info[shipp->ai_index];
969
970                         if ((aip->path_length > 0) && (aip->path_start > -1)) {
971                                 Assert(aip->path_start < MAX_PATH_POINTS);
972                                 aip->path_start = pp_xlate[aip->path_start];
973
974                                 Assert((aip->path_cur >= 0) && (aip->path_cur < MAX_PATH_POINTS));
975                                 aip->path_cur = pp_xlate[aip->path_cur];
976                         }
977                 }
978         }
979
980         //      Now, compress the buffer.
981         for (i=0; i<MAX_PATH_POINTS; i++)
982                 if (i != pp_xlate[i])
983                         Path_points[pp_xlate[i]] = Path_points[i];
984
985 }
986
987 //      Hash two values together, return result.
988 //      Hash function: curval shifted right circular by one, newval xored in.
989 int hash(unsigned int curval, int newval)
990 {
991         int     addval = curval & 1;
992
993         curval >>= 1;
994         if (addval)
995                 curval |= 0x80000000;
996         curval ^= newval;
997
998         return curval;
999 }
1000
1001 //      Hash some information in an object together.
1002 //      On 2/20/97, the information is position and orientation.
1003 int create_object_hash(object *objp)
1004 {
1005         int     *ip;
1006         unsigned int    hashval = 0;
1007         int     i;
1008
1009         ip = (int *) &objp->orient;
1010
1011         for (i=0; i<9; i++) {
1012                 hashval = hash(hashval, *ip);
1013                 ip++;
1014         }
1015
1016         ip = (int *) &objp->pos;
1017
1018         for (i=0; i<3; i++) {
1019                 hashval = hash(hashval, *ip);
1020                 ip++;
1021         }
1022
1023         return hashval;
1024 }
1025
1026 //      Stuff a list of NUM_SKILL_LEVELS floats at *plist.
1027 void parse_float_list(float *plist)
1028 {
1029         int     i;
1030
1031         for (i=0; i<NUM_SKILL_LEVELS; i++) {
1032                 stuff_float(&plist[i]);
1033         }
1034 }
1035
1036 void parse_ai_class()
1037 {
1038         ai_class        *aicp = &Ai_classes[Num_ai_classes];
1039
1040         required_string("$Name:");
1041         stuff_string(aicp->name, F_NAME, NULL);
1042
1043         Ai_class_names[Num_ai_classes] = aicp->name;
1044
1045         required_string("$accuracy:");
1046         parse_float_list(aicp->ai_accuracy);
1047
1048         required_string("$evasion:");
1049         parse_float_list(aicp->ai_evasion);
1050
1051         required_string("$courage:");
1052         parse_float_list(aicp->ai_courage);
1053
1054         required_string("$patience:");
1055         parse_float_list(aicp->ai_patience);
1056 }
1057
1058 void parse_aitbl()
1059 {
1060         // open localization
1061         lcl_ext_open();
1062
1063         read_file_text("ai.tbl");
1064
1065         reset_parse();
1066
1067         Num_ai_classes = 0;
1068
1069         required_string("#AI Classes");
1070
1071         while (required_string_either("#End", "$Name:")) {
1072                 Assert( Num_ai_classes < MAX_AI_CLASSES);
1073
1074                 parse_ai_class();
1075
1076                 Num_ai_classes++;
1077         }
1078
1079         // close localization
1080         lcl_ext_close();
1081 }
1082
1083 LOCAL int ai_inited = 0;
1084
1085 //========================= BOOK-KEEPING FUNCTIONS =======================
1086
1087 // Called once at game start-up
1088 void ai_init()
1089 {
1090         if ( !ai_inited )       {
1091                 // Do the first time initialization stuff here
1092                 int     rval;
1093
1094                 if ((rval = setjmp(parse_abort)) != 0) {
1095                         Error(LOCATION, "Error parsing 'ai.tbl'\r\nError code = %i.\r\n", rval);
1096                 } else {                        
1097                         parse_aitbl();                  
1098                 }
1099
1100                 ai_inited = 1;
1101         }
1102
1103         init_semirand();
1104         
1105         ai_level_init();
1106 }
1107
1108 // this inits the ai.  You should be able to call this between
1109 // levels to reset everything.
1110 void ai_level_init()
1111 {
1112         int i;
1113  
1114         // Do the stuff to reset all ai stuff here
1115         for (i=0; i<MAX_AI_INFO ; i++) {
1116                 Ai_info[i].shipnum = -1;
1117         }
1118         Ai_goal_signature = 0;
1119         Ai_friendly_rearm_timestamp = timestamp(-1);
1120         Ai_hostile_rearm_timestamp = timestamp(-1);
1121         Ai_neutral_rearm_timestamp = timestamp(-1);
1122         Ai_traitor_rearm_timestamp = timestamp(-1);
1123
1124         // clear out the stuff needed for AI firing powerful secondary weapons
1125         ai_init_secondary_info();
1126
1127         Ai_last_arrive_path=0;
1128 }
1129
1130 // BEGIN STEALTH
1131 // -----------------------------------------------------------------------------
1132 // Check if object is a stealth ship
1133 int is_object_stealth_ship(object* objp)
1134 {
1135         if (objp->type == OBJ_SHIP) {
1136                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_STEALTH) {
1137                         return 1;
1138                 }
1139         }
1140
1141         // not stealth ship
1142         return 0;
1143 }
1144
1145 // -----------------------------------------------------------------------------
1146 // Init necessary ai info for new stealth target
1147 void init_ai_stealth_info(ai_info *aip, object *stealth_objp)
1148 {
1149         Assert(is_object_stealth_ship(stealth_objp));
1150
1151         // set necessary ai info for new stealth target
1152         aip->stealth_last_pos = stealth_objp->pos;
1153         aip->stealth_velocity = stealth_objp->phys_info.vel;
1154         aip->stealth_last_visible_stamp = timestamp();
1155 }
1156
1157 // -----------------------------------------------------------------------------
1158 // Check whether Pl_objp can see a stealth ship object
1159 #define STEALTH_INVISIBLE                       0
1160 #define STEALTH_VISIBLE                         1
1161 #define STEALTH_FULLY_TARGETABLE        2
1162
1163 float get_skill_stealth_dist_scaler()
1164 {
1165         // return dist scaler based on skill level
1166         switch (Game_skill_level) {
1167         case 0: // very easy
1168                 return 0.65f;
1169
1170         case 1: // easy
1171                 return 0.9f;
1172
1173         case 2: // medium
1174                 return 1.0f;
1175
1176         case 3: // hard
1177                 return 1.1f;
1178
1179         case 4: // insane
1180                 return 1.3f;
1181
1182         default:
1183                 Int3();
1184         }
1185
1186         return 1.0f;
1187 }
1188
1189 float get_skill_stealth_dot_scaler()
1190 {
1191         // return multiplier on dot based on skill level
1192         switch (Game_skill_level) {
1193         case 0: // very easy
1194                 return 1.3f;
1195
1196         case 1: // easy
1197                 return 1.1f;
1198
1199         case 2: // medium
1200                 return 1.0f;
1201
1202         case 3: // hard
1203                 return 0.9f;
1204
1205         case 4: // insane
1206                 return 0.7f;
1207
1208         default:
1209                 Int3();
1210         }
1211
1212         return 1.0f;
1213 }
1214
1215 int ai_is_stealth_visible(object *viewer_objp, object *stealth_objp)
1216 {
1217         ship *shipp;
1218         vector vec_to_stealth;
1219         float dot_to_stealth, dist_to_stealth, max_stealth_dist;
1220
1221         Assert(stealth_objp->type == OBJ_SHIP);
1222         shipp = &Ships[stealth_objp->instance];
1223         Assert(viewer_objp->type == OBJ_SHIP);
1224
1225         // check if stealth ship
1226         Assert(Ship_info[shipp->ship_info_index].flags & SIF_STEALTH);
1227
1228         // check if in neb and below awac level for visible
1229         if ( !ship_is_visible_by_team(stealth_objp->instance, Ships[viewer_objp->instance].team) ) {
1230                 vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &viewer_objp->pos);
1231                 dist_to_stealth = vm_vec_mag_quick(&vec_to_stealth);
1232                 dot_to_stealth = vm_vec_dotprod(&viewer_objp->orient.v.fvec, &vec_to_stealth) / dist_to_stealth;
1233
1234                 // get max dist at which stealth is visible
1235                 max_stealth_dist = get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST;
1236
1237                 // now check if within view frustrum
1238                 float needed_dot_to_stealth;
1239                 if (dist_to_stealth < 100) {
1240                         needed_dot_to_stealth = 0.0f;
1241                 } else {
1242                         needed_dot_to_stealth = get_skill_stealth_dot_scaler() * float(STEALTH_VIEW_CONE_DOT) * (dist_to_stealth / max_stealth_dist);
1243                 }
1244                 if (dot_to_stealth > needed_dot_to_stealth) {
1245                         if (dist_to_stealth < max_stealth_dist) {
1246                                 return STEALTH_VISIBLE;
1247                         }
1248                 }
1249
1250                 // not within frustrum
1251                 return STEALTH_INVISIBLE;
1252         }
1253
1254         // visible by awacs level
1255         return STEALTH_FULLY_TARGETABLE;
1256 }
1257
1258 // END STEALTH
1259
1260 //      Compute dot product of direction vector and forward vector.
1261 //      Direction vector is vector from one object to other object.
1262 //      Forward vector is the forward vector of the ship.
1263 //      If from_dot == NULL, don't fill it in.
1264 float compute_dots(object *objp, object *other_objp, float *to_dot, float *from_dot)
1265 {
1266         vector  v2o;
1267         float           dist;
1268
1269         dist = vm_vec_normalized_dir(&v2o, &other_objp->pos, &objp->pos);
1270
1271         *to_dot = vm_vec_dot(&objp->orient.v.fvec, &v2o);
1272
1273         if (from_dot != NULL)
1274                 *from_dot = - vm_vec_dot(&other_objp->orient.v.fvec, &v2o);
1275
1276         return dist;
1277 }
1278
1279 // -----------------------------------------------------------------------------
1280 // update estimated stealth info
1281 // this is a "cheat" update
1282 // error increases with time not seen, true distance away, dot to enemey
1283 // this is done only if we can not see the stealth target
1284 // need to infer its position either by weapon fire pos or last know pos
1285 void update_ai_stealth_info_with_error(ai_info *aip/*, int no_error*/)
1286 {
1287         object *ship;
1288         object *stealth_objp;
1289         /*
1290         float error_time_mult, error_dist_mult, error_dot_mult, error_mult;
1291         float pos_error, vel_error;
1292         vector error_vec, vec_to_stealth;
1293         float dist_to_stealth, dot_to_stealth;
1294         float delta_time, delta_capped;
1295         */
1296
1297         // make sure I am targeting a stealth ship
1298         Assert( is_object_stealth_ship(&Objects[aip->target_objnum]) );
1299         stealth_objp = &Objects[aip->target_objnum];
1300
1301         // my_ship
1302         ship = &Objects[Ships[aip->shipnum].objnum];
1303
1304         // if update is due to weapon fire, get exact stealth position
1305 //      if (no_error) {
1306         aip->stealth_last_pos = stealth_objp->pos;
1307         aip->stealth_velocity = stealth_objp->phys_info.vel;
1308         aip->stealth_last_visible_stamp = timestamp();
1309 //              return;
1310 //      }
1311 /*
1312         // get time since last seen
1313         delta_time = 0.001f * (timestamp() - aip->stealth_last_visible_stamp);
1314
1315         // we don't want our "cheat" guess to more off than what we would get from extrapolating from last visible
1316         // only update if stealth info is "old"
1317         if ( (delta_time) < 0.5 ) {
1318                 return;
1319         }
1320
1321         // find vec_to_stealth and dist
1322         vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &ship->pos);
1323         dist_to_stealth = vm_vec_normalize_quick(&vec_to_stealth);
1324         dot_to_stealth = vm_vec_dotprod(&vec_to_stealth, &ship->orient.v.fvec);
1325
1326         // put cap on time
1327         delta_capped = delta_time;
1328         if (delta_time > 5.0) {
1329                 delta_capped = 5.0f;
1330         }
1331
1332         // erorr_time_mult (for 0-5) -> (1-6)
1333         error_time_mult = (1.0f + delta_capped);
1334
1335         // error_dot_mult (-1 to 1) -> (1-3)
1336         error_dot_mult = (2 - dot_to_stealth);
1337
1338         // error_dist_mult (0-1000+) -> (1-4)
1339         error_dist_mult = dist_to_stealth * 4.0f * 0.001f;
1340         if (error_dist_mult < 1) {
1341                 error_dist_mult = 1.0f;
1342         } else if (error_dist_mult > 4) {
1343                 error_dist_mult = 4.0f;
1344         }
1345
1346         // multiply error out
1347         error_mult = error_time_mult * error_dot_mult * error_dist_mult;
1348
1349         float base_pos_error = 10;
1350         float base_vel_error = 2;
1351
1352         // find the position and velocity error magnitude;
1353         pos_error = base_pos_error * error_mult;
1354         vel_error = base_vel_error * error_mult;
1355
1356         // get an error that changes slowly over time
1357         static_randvec( ((int)aip ^ (Missiontime >> 18)) & 7, &error_vec);
1358         vm_vec_zero(&error_vec);
1359
1360         // update pos and vel with error
1361         vm_vec_scale_add(&aip->stealth_velocity, &stealth_objp->phys_info.vel, &error_vec, vel_error);
1362
1363         // revise last "known" position to arrive at last pos with given error
1364         vm_vec_scale_add(&aip->stealth_last_pos, &stealth_objp->pos, &error_vec, pos_error);
1365         vm_vec_scale_add2(&aip->stealth_last_pos, &aip->stealth_velocity, -(0.001f * delta_time));
1366         */
1367 }
1368
1369 //      Update danger_weapon_objnum and signature in ai_info to say this weapon is to be avoided.
1370 void ai_update_danger_weapon(int attacked_objnum, int weapon_objnum)
1371 {
1372         object  *objp, *weapon_objp;
1373         ai_info *aip;
1374         float           old_dist, new_dist;
1375         float           old_dot, new_dot;
1376         object  *old_weapon_objp = NULL;
1377
1378         if ((attacked_objnum == -1) || (weapon_objnum == -1)) {
1379                 return;
1380         }
1381
1382         objp = &Objects[attacked_objnum];
1383
1384         // AL 2-24-98: If this isn't a ship, we don't need to worry about updating weapon_objnum (ie it would be
1385         //                                      an asteroid or bomb).
1386         if ( objp->type != OBJ_SHIP ) {
1387                 return;
1388         }
1389
1390         weapon_objp = &Objects[weapon_objnum];
1391
1392         aip = &Ai_info[Ships[objp->instance].ai_index];
1393
1394         // if my taraget is a stealth ship and is not visible
1395         if (aip->target_objnum >= 0) {
1396                 if ( is_object_stealth_ship(&Objects[aip->target_objnum]) ) {
1397                         if ( ai_is_stealth_visible(objp, &Objects[aip->target_objnum]) == STEALTH_INVISIBLE ) {
1398                                 // and the weapon is coming from that stealth ship
1399                                 if (weapon_objp->parent == aip->target_objnum) {
1400                                         // update my position estimate for stealth ship
1401                                         update_ai_stealth_info_with_error(aip/*, 1*/);
1402                                 }
1403                         }
1404                 }
1405         }
1406
1407         if (aip->danger_weapon_objnum != -1) {
1408                 old_weapon_objp = &Objects[aip->danger_weapon_objnum];
1409                 if ((old_weapon_objp->type == OBJ_WEAPON) && (old_weapon_objp->signature == aip->danger_weapon_signature)) {
1410                         ;
1411                 } else {
1412                         aip->danger_weapon_objnum = -1;
1413                 }
1414         }
1415
1416         new_dist = compute_dots(weapon_objp, objp, &new_dot, NULL);
1417
1418         if (aip->danger_weapon_objnum == -1) {
1419                 if (new_dist < 1500.0f) {
1420                         if (new_dot > 0.5f) {
1421                                 aip->danger_weapon_objnum = weapon_objnum;
1422                                 aip->danger_weapon_signature = weapon_objp->signature;
1423                         }
1424                 }
1425         } else {
1426                 Assert(old_weapon_objp != NULL);
1427                 old_dist = compute_dots(old_weapon_objp, objp, &old_dot, NULL);
1428         
1429                 if (old_dot < 0.5f) {
1430                         aip->danger_weapon_objnum = -1;
1431                         old_dist = 9999.9f;
1432                 }
1433
1434                 if ((new_dot > 0.5f) && (new_dot > old_dot-0.01f)) {
1435                         if (new_dist < old_dist) {
1436                                 aip->danger_weapon_objnum = weapon_objnum;
1437                                 aip->danger_weapon_signature = weapon_objp->signature;
1438                         }
1439                 }
1440         }
1441 }
1442
1443 //      If rvec != NULL, use it to match bank by calling vm_matrix_interpolate.
1444 //      (rvec defaults to NULL)
1445 void ai_turn_towards_vector(vector *dest, object *objp, 
1446                                                                          float frametime, float turn_time, vector *slide_vec, vector *rel_pos, float bank_override, int flags, vector *rvec)
1447 {
1448         //matrix        goal_orient;
1449         matrix  curr_orient;
1450         vector  vel_in, vel_out, desired_fvec, src;
1451         float           delta_time;
1452         physics_info    *pip;
1453         vector  vel_limit, acc_limit;
1454         float           delta_bank;
1455
1456         //      Don't allow a ship to turn if it has no engine strength.
1457         // AL 3-12-98: objp may not always be a ship!
1458         if ( objp->type == OBJ_SHIP ) {
1459                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f)
1460                         return;
1461         }
1462                         
1463         //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));
1464         pip = &objp->phys_info;
1465
1466         vel_in = pip->rotvel;
1467         curr_orient = objp->orient;
1468         delta_time = flFrametime;
1469
1470         Assert(turn_time > 0.0f);
1471         
1472         //      Scale turn_time based on skill level and team.
1473         if (!(flags & AITTV_FAST)){
1474                 if (objp->type == OBJ_SHIP){
1475                         if (Ships[objp->instance].team != Ships[Player_obj->instance].team){
1476                                 turn_time *= Turn_time_skill_level_scale[Game_skill_level];
1477                         }
1478                 }
1479         }
1480
1481         //      Set max turn rate.
1482         vel_limit.xyz.x = 2*PI/turn_time;
1483         vel_limit.xyz.y = 2*PI/turn_time;
1484         vel_limit.xyz.z = 2*PI/turn_time;
1485
1486         //      Set rate at which ship can accelerate to its rotational velocity.
1487         //      For now, weapons just go much faster.
1488         acc_limit = vel_limit;
1489         if (objp->type == OBJ_WEAPON)
1490                 vm_vec_scale(&acc_limit, 8.0f);
1491
1492         src = objp->pos;
1493
1494         if (rel_pos != NULL) {
1495                 vector  gun_point;
1496                 vm_vec_unrotate(&gun_point, rel_pos, &objp->orient);
1497                 vm_vec_add2(&src, &gun_point);
1498         }
1499
1500         vm_vec_normalized_dir(&desired_fvec, dest, &src);
1501
1502         //      Since ship isn't necessarily moving in the direction it's pointing, sometimes it's better
1503         //      to be moving towards goal rather than just pointing.  So, if slide_vec is !NULL, try to
1504         //      make ship move towards goal, not point at goal.
1505         if (slide_vec != NULL) {
1506                 vm_vec_add2(&desired_fvec, slide_vec);
1507                 vm_vec_normalize(&desired_fvec);
1508         }
1509
1510         //      Should be more general case here.  Currently, anything that is not a weapon will bank when it turns.
1511         if (objp->type == OBJ_WEAPON)
1512                 delta_bank = 0.0f;
1513         else if ((bank_override) && (Ships[objp->instance].team & opposing_team_mask(Player_ship->team))) {     //      Theoretically, this will only happen for Shivans.
1514                 delta_bank = bank_override;
1515                 //nprintf(("AI", "%i: %7.3f\n", Framecount, bank_override));
1516         } else {
1517                 delta_bank = vm_vec_dot(&curr_orient.v.rvec, &objp->last_orient.v.rvec);
1518                 delta_bank = 100.0f * (1.0f - delta_bank);
1519                 if (vm_vec_dot(&objp->last_orient.v.fvec, &objp->orient.v.rvec) < 0.0f)
1520                         delta_bank = -delta_bank;
1521
1522                 //nprintf(("AI", "%s: Frame %i: delta bank = %7.3f\n", Ships[objp->instance].ship_name, Framecount, delta_bank));
1523         }
1524
1525         //      Dave Andsager: The non-indented lines here are debug code to help you track down the problem in the physics
1526         //      that is causing ships to inexplicably rotate very far.  If you hit the Int3(), set the next statement to be
1527         //      the one marked "HERE".  (Do this clicking the cursor there, then right clicking.  Choose the right option.)
1528         //      This will allow you to rerun vm_forward_interpolate() with the values that caused the error.
1529         //      Note, you'll need to enable the Int3() about ten lines below.
1530 #ifndef NDEBUG
1531 vector tvec = objp->orient.v.fvec;
1532 vector  vel_in_copy;
1533 matrix  objp_orient_copy;
1534
1535 vel_in_copy = vel_in;
1536 objp_orient_copy = objp->orient;
1537
1538 vel_in = vel_in_copy;   //      HERE
1539 objp->orient = objp_orient_copy;
1540 #endif
1541         if (rvec != NULL) {
1542                 matrix  out_orient, goal_orient;
1543
1544                 vm_vector_2_matrix(&goal_orient, &desired_fvec, NULL, rvec);
1545                 vm_matrix_interpolate(&goal_orient, &curr_orient, &vel_in, delta_time, &out_orient, &vel_out, &vel_limit, &acc_limit);
1546                 objp->orient = out_orient;
1547         } else {
1548                 vm_forward_interpolate(&desired_fvec, &curr_orient, &vel_in, delta_time, delta_bank, &objp->orient, &vel_out, &vel_limit, &acc_limit);
1549         }
1550 #ifndef NDEBUG
1551 if (!((objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE))) {
1552         if (delta_time < 0.25f && vm_vec_dot(&objp->orient.v.fvec, &tvec) < 0.1f)
1553                 Int3(); //      Get Andsager.  A ship has turned too far in one frame.
1554 }
1555 #endif
1556
1557         pip->rotvel = vel_out;
1558 }
1559
1560 void init_ship_info()
1561 {
1562         int     i;
1563
1564         if (Ship_info_inited)
1565                 return;
1566
1567         for (i=0; i<MAX_SHIP_TYPES; i++) {
1568                 Ship_info[i].min_speed = - Ship_info[i].max_rear_vel;
1569                 Ship_info[i].max_accel = Ship_info[i].max_vel.xyz.z;
1570         }
1571
1572         Ship_info_inited = 1;
1573
1574 }
1575
1576 //      Set aip->target_objnum to objnum
1577 //      Update aip->previous_target_objnum.
1578 //      If new target (objnum) is different than old target, reset target_time.
1579 int set_target_objnum(ai_info *aip, int objnum)
1580 {
1581 /*
1582         char    old_name[32], new_name[32];
1583
1584         if (!timestamp_elapsed(aip->ok_to_target_timestamp))
1585                 return aip->target_objnum;
1586
1587         if (Player_ship && (Ships[aip->shipnum].team == Player_ship->team)) {
1588                 if (aip->target_objnum == -1)
1589                         strcpy(old_name, "none");
1590                 else
1591                         strcpy(old_name, Ships[Objects[aip->target_objnum].instance].ship_name);
1592
1593                 if (objnum == -1)
1594                         strcpy(new_name, "none");
1595                 else
1596                         strcpy(new_name, Ships[Objects[objnum].instance].ship_name);
1597
1598                 nprintf(("AI", "Ship %s changing target from %s to %s\n", Ships[aip->shipnum].ship_name, old_name, new_name));
1599         }
1600 */
1601
1602         // AL 2-25-97: Ensure that a protected ship isn't being set as a target (for non-players only)
1603         /*
1604         if ( objnum >= 0 ) {
1605                 if ( !(Objects[Ships[aip->shipnum].objnum].flags & OF_PLAYER_SHIP) ) {
1606                         if ( Objects[objnum].flags & OF_PROTECTED ) {
1607                                 // AL 2-26-97: removing Int3() until issue with setting OF_PROTECTED in ai_set_attack_subsystem()
1608                                 //Int3();                                                               // this should not happen
1609                                 return aip->target_objnum;              // don't change targets
1610                         }
1611                 }
1612         }
1613         */
1614
1615         if ((aip != Player_ai) && (!timestamp_elapsed(aip->ok_to_target_timestamp))) {
1616                 return aip->target_objnum;
1617         }
1618
1619         if (aip->target_objnum == objnum) {
1620                 aip->previous_target_objnum = aip->target_objnum;
1621         } else {
1622                 aip->previous_target_objnum = aip->target_objnum;
1623
1624                 // ignore this assert if a multiplayer observer
1625                 if((Game_mode & GM_MULTIPLAYER) && (aip == Player_ai) && (Player_obj->type == OBJ_OBSERVER)){
1626                 } else {
1627                         Assert(objnum != Ships[aip->shipnum].objnum);   //      make sure not targeting self
1628                 }
1629
1630                 // if stealth target, init ai_info for stealth
1631                 if ( (objnum > 0) && is_object_stealth_ship(&Objects[objnum]) ) {
1632                         init_ai_stealth_info(aip, &Objects[objnum]);
1633                 }
1634
1635                 aip->target_objnum = objnum;
1636                 aip->target_time = 0.0f;
1637                 aip->target_signature = Objects[objnum].signature;
1638                 // clear targeted subsystem
1639                 set_targeted_subsys(aip, NULL, -1);
1640         }
1641         
1642         return aip->target_objnum;
1643 }
1644
1645 int ai_select_primary_weapon(object *objp, object *other_objp, int flags);
1646
1647 //      Make new_subsys the targeted subsystem of ship *aip.
1648 ship_subsys *set_targeted_subsys(ai_info *aip, ship_subsys *new_subsys, int parent_objnum)
1649 {
1650         Assert(aip != NULL);
1651
1652         aip->last_subsys_target = aip->targeted_subsys;
1653         aip->targeted_subsys = new_subsys;
1654         aip->targeted_subsys_parent = parent_objnum;
1655
1656         if ( new_subsys ) {
1657                 // Make new_subsys target
1658                 if (new_subsys->system_info->type == SUBSYSTEM_ENGINE) {
1659                         if ( aip != Player_ai ) {
1660                                 ai_select_primary_weapon(&Objects[Ships[aip->shipnum].objnum], &Objects[parent_objnum], WIF_PUNCTURE);
1661                                 ship_primary_changed(&Ships[aip->shipnum]);     // AL: maybe send multiplayer information when AI ship changes primaries
1662                         }
1663                 }
1664
1665                 if ( aip == Player_ai ) {
1666                         hud_lock_reset(0.5f);
1667                 }
1668
1669         } else {
1670                 // Cleanup any subsys path information if it exists
1671                 ai_big_subsys_path_cleanup(aip);
1672         }
1673         
1674         return aip->targeted_subsys;
1675 }                                                                                         
1676
1677 // called to init the data for single ai object.  At this point,
1678 // the ship and the object and the ai_info are are correctly
1679 // linked together. Ai_info[ai_index].shipnum is the only valid field 
1680 // in ai_info.
1681 //      This is called right when the object is parsed, so you can't assume much
1682 //      has been initialized.  For example, wings, waypoints, goals are probably
1683 //      not yet loaded. --MK, 10/8/96
1684 void ai_object_init(object * obj, int ai_index)
1685 {
1686         ai_info *aip;
1687         Assert(ai_index >= 0 && ai_index < MAX_AI_INFO);
1688
1689         aip = &Ai_info[ai_index];
1690
1691         aip->type = 0;          //      0 means not in use.
1692         aip->wing = -1;         //      Member of what wing? -1 means none.
1693         aip->ai_class = Ship_info[Ships[obj->instance].ship_info_index].ai_class;
1694         aip->behavior = AIM_NONE;
1695 }
1696
1697 //      If *aip is docked, set max acceleration to A->mass/(A->mass + B->mass) where A is *aip and B is dock object
1698 void adjust_accel_for_docking(ai_info *aip)
1699 {
1700         if (aip->dock_objnum != -1) {
1701                 object  *obj2p = &Objects[aip->dock_objnum];
1702                 object  *obj1p;
1703
1704                 obj1p = &Objects[Ships[aip->shipnum].objnum];
1705
1706                 if (obj2p->signature == aip->dock_signature) {
1707                         float   ratio;
1708
1709                         ratio = obj1p->phys_info.mass / (obj1p->phys_info.mass + obj2p->phys_info.mass);
1710
1711                         // put cap on how much ship can slow down
1712                         if (ratio < 0.8) {
1713                                 ratio = 0.8f;
1714                         }
1715
1716                         if (AI_ci.forward > ratio) {
1717                                 AI_ci.forward = ratio;
1718                         }
1719                 }
1720         }
1721 }
1722
1723 // -------------------------------------------------------------------
1724 void accelerate_ship(ai_info *aip, float accel)
1725 {
1726         aip->prev_accel = accel;
1727         AI_ci.forward = accel;
1728         adjust_accel_for_docking(aip);
1729 }
1730
1731 //      --------------------------------------------------------------------------
1732 void change_acceleration(ai_info *aip, float delta_accel)
1733 {
1734         float   new_accel;
1735
1736         if (delta_accel < 0.0f) {
1737                 if (aip->prev_accel > 0.0f)
1738                         aip->prev_accel = 0.0f;
1739         } else if (aip->prev_accel < 0.0f)
1740                 aip->prev_accel = 0.0f;
1741
1742         new_accel = aip->prev_accel + delta_accel * flFrametime;
1743
1744         if (new_accel > 1.0f)
1745                 new_accel = 1.0f;
1746         else if (new_accel < -1.0f)
1747                 new_accel = -1.0f;
1748         
1749         aip->prev_accel = new_accel;
1750
1751         AI_ci.forward = new_accel;
1752         adjust_accel_for_docking(aip);
1753 }
1754
1755 void set_accel_for_target_speed(object *objp, float tspeed)
1756 {
1757         float   max_speed;
1758         ai_info *aip;
1759
1760         aip = &Ai_info[Ships[objp->instance].ai_index];
1761
1762         max_speed = Ships[objp->instance].current_max_speed;
1763
1764         AI_ci.forward = tspeed/max_speed;
1765         aip->prev_accel = AI_ci.forward;
1766
1767         adjust_accel_for_docking(aip);
1768 }
1769
1770 //      Stuff perim_point with a point on the perimeter of the sphere defined by object *objp
1771 //      on the vector from the center of *objp through the point *vp.
1772 void project_point_to_perimeter(vector *perim_point, vector *pos, float radius, vector *vp)
1773 {
1774         vector  v1;
1775         float           mag;
1776
1777         vm_vec_sub(&v1, vp, pos);
1778         mag = vm_vec_mag(&v1);
1779
1780         if (mag == 0.0f) {
1781                 Warning(LOCATION, "projectable point is at center of sphere.");
1782                 vm_vec_make(&v1, 0.0f, radius, 0.0f);
1783         } else {
1784                 vm_vec_normalize(&v1);
1785                 vm_vec_scale(&v1, 1.1f * radius + 10.0f);
1786         }
1787
1788         vm_vec_add2(&v1, pos);
1789         *perim_point = v1;
1790 }
1791
1792 //      Stuff tan1 with tangent point on sphere.  tan1 is point nearer to *p1
1793 //      *p0 is point through which tangents pass.
1794 //      *centerp is center of sphere.
1795 //      *p1 is another point in space to define the plane in which tan1, tan2 reside.
1796 //      radius is the radius of the sphere.
1797 //      Note, this is a very approximate function just for AI.
1798 //      Note also: On 12/26/96, p1 is used to define the plane perpendicular to that which
1799 //      contains the tangent point.
1800 void get_tangent_point(vector *tan1, vector *p0, vector *centerp, vector *p1, float radius)
1801 {
1802         vector  dest_vec, v2c, perp_vec, temp_vec, v2;
1803         float           dist, ratio;
1804
1805         //      Detect condition of point inside sphere.
1806         if (vm_vec_dist(p0, centerp) < radius)
1807                 project_point_to_perimeter(tan1, centerp, radius, p0);
1808         else {
1809                 vm_vec_normalized_dir(&v2c, centerp, p0);
1810
1811                 //      Compute perpendicular vector using p0, centerp, p1
1812                 vm_vec_normal(&temp_vec, p0, centerp, p1);
1813                 vm_vec_sub(&v2, centerp, p0);
1814                 vm_vec_cross(&perp_vec, &temp_vec, &v2);
1815
1816                 vm_vec_normalize(&perp_vec);
1817
1818                 dist = vm_vec_dist_quick(p0, centerp);
1819                 ratio = dist / radius;
1820
1821                 if (ratio < 2.0f)
1822                         vm_vec_scale_add(&dest_vec, &perp_vec, &v2c, ratio-1.0f);
1823                 else
1824                         vm_vec_scale_add(&dest_vec, &v2c, &perp_vec, (1.0f + 1.0f/ratio));
1825
1826                 vm_vec_scale_add(tan1, p0, &dest_vec, dist + radius);
1827         }
1828 }
1829
1830 //      --------------------------------------------------------------------------
1831 //      Given an object and a point, turn towards the point, resulting in
1832 // approach behavior.
1833 void turn_towards_point(object *objp, vector *point, vector *slide_vec, float bank_override)
1834 {
1835         ai_info *aip;
1836         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1837         
1838         // check if in formation and if not leader, don't change rotvel.xyz.z (bank to match leader elsewhere)
1839         if (aip->ai_flags & AIF_FORMATION) {
1840                 if (&Objects[aip->goal_objnum] != objp) {
1841                         float rotvel_z = objp->phys_info.rotvel.xyz.z;
1842                         ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1843                         objp->phys_info.rotvel.xyz.z = rotvel_z;
1844                 }
1845         } else {
1846                 // normal turn
1847                 ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1848         }
1849 }
1850
1851 //      --------------------------------------------------------------------------
1852 //      Given an object and a point, turn away from the point, resulting in avoidance behavior.
1853 //      Note: Turn away at full speed, not scaled down by skill level.
1854 void turn_away_from_point(object *objp, vector *point, float bank_override)
1855 {
1856         vector  opposite_point;
1857
1858         vm_vec_sub(&opposite_point, &objp->pos, point);
1859         vm_vec_add2(&opposite_point, &objp->pos);
1860
1861         ai_turn_towards_vector(&opposite_point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, NULL, NULL, bank_override, AITTV_FAST);
1862 }
1863
1864
1865 //      --------------------------------------------------------------------------
1866 //      Given an object and a point, turn tangent to the point, resulting in
1867 // a circling behavior.
1868 //      Make object *objp turn around the point *point with a radius of radius.
1869 //      Note that this isn't the same as following a circle of radius radius with
1870 //      center *point, but it should be adequate.
1871 //      Note that if you want to circle an object without hitting it, you should use
1872 //      about twice that object's radius for radius, else you'll certainly bump into it.
1873 //      Return dot product to goal point.
1874 float turn_towards_tangent(object *objp, vector *point, float radius)
1875 {
1876         vector  vec_to_point;
1877         vector  goal_point;
1878         vector  perp_point;                             //      point radius away from *point on vector to objp->pos
1879         vector  up_vec, perp_vec;
1880
1881         vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1882         vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.v.fvec);
1883         vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1884
1885         vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1886         if (vm_vec_dot(&objp->orient.v.fvec, &perp_vec) > 0.0f) {
1887                 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, radius);
1888         } else {
1889                 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, -radius);
1890         }
1891
1892 //      Ai_info[Ships[objp->instance].ai_index].goal_point = goal_point;
1893         turn_towards_point(objp, &goal_point, NULL, 0.0f);
1894
1895         vector  v2g;
1896
1897         vm_vec_normalized_dir(&v2g, &goal_point, &objp->pos);
1898         return vm_vec_dot(&objp->orient.v.fvec, &v2g);
1899 }
1900
1901 float turn_toward_tangent_with_axis(object *objp, object *center_objp, float radius)
1902 {
1903         vector r_vec, theta_vec;
1904         vector center_vec, vec_on_cylinder, sph_r_vec;
1905         float center_obj_z;
1906
1907         // find closest z of center objp
1908         vm_vec_sub(&sph_r_vec, &objp->pos, &center_objp->pos);
1909         center_obj_z = vm_vec_dotprod(&sph_r_vec, &center_objp->orient.v.fvec);
1910
1911         // find pt on axis with closest z
1912         vm_vec_scale_add(&center_vec, &center_objp->pos, &center_objp->orient.v.fvec, center_obj_z);
1913
1914         // get r_vec
1915         vm_vec_sub(&r_vec, &objp->pos, &center_vec);
1916 //      float r_mag = vm_vec_normalize_quick(&r_vec);
1917 //      mprintf(("cur_r: %.1f, desired_r: %.1f\n", r_mag, radius));
1918         Assert( (vm_vec_dotprod(&r_vec, &center_objp->orient.v.fvec) < 0.0001));
1919
1920         // get theta vec - perp to r_vec and z_vec
1921         vm_vec_crossprod(&theta_vec, &center_objp->orient.v.fvec, &r_vec);
1922
1923 #ifndef NDEBUG
1924         float mag = vm_vec_normalize(&theta_vec);
1925         Assert(mag > 0.9999 && mag < 1.0001);
1926 #endif
1927
1928         vector temp;
1929         vm_vec_crossprod(&temp, &r_vec, &theta_vec);
1930
1931 #ifndef NDEBUG
1932         float dot = vm_vec_dotprod(&temp, &center_objp->orient.v.fvec);
1933         Assert( dot >0.9999 && dot < 1.0001);
1934 #endif
1935
1936         // find pt on clylinder with closest z
1937         vm_vec_scale_add(&vec_on_cylinder, &center_vec, &r_vec, radius);
1938
1939         vector goal_pt, v2g;
1940         vm_vec_scale_add(&goal_pt, &vec_on_cylinder, &theta_vec, radius);
1941
1942 //      Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pt;
1943         turn_towards_point(objp, &goal_pt, NULL, 0.0f);
1944
1945         vm_vec_normalized_dir(&v2g, &goal_pt, &objp->pos);
1946         return vm_vec_dot(&objp->orient.v.fvec, &v2g);
1947 }
1948
1949 //      Returns a point radius units away from *point that *objp should turn towards to orbit *point
1950 void get_tangent_point(vector *goal_point, object *objp, vector *point, float radius)
1951 {
1952         vector  vec_to_point;
1953         vector  perp_point;                             //      point radius away from *point on vector to objp->pos
1954         vector  up_vec, perp_vec;
1955
1956         vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1957         vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.v.fvec);
1958         vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1959         vm_vec_normalize(&perp_vec);
1960
1961         vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1962
1963         if (vm_vec_dot(&objp->orient.v.fvec, &perp_vec) > 0.0f) {
1964                 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, radius);
1965         } else {
1966                 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, -radius);
1967         }
1968 }
1969
1970 int     Player_attacking_enabled = 1;
1971
1972 // -----------------------------------------------------------------------------
1973 // Determine whether an object is targetable within a nebula
1974 int object_is_targetable(object *target, ship *viewer)
1975 {
1976         int stealth_ship = 0;
1977
1978         // if target is ship, check if visible by team
1979         if (target->type == OBJ_SHIP) {
1980                 stealth_ship = (Ship_info[Ships[target->instance].ship_info_index].flags & SIF_STEALTH);
1981                 if ( ship_is_visible_by_team(target->instance, viewer->team) == 1) {
1982                         return 1;
1983                 }
1984         }
1985
1986         // for AI partially targetable works as fully targetable, except for stealth ship
1987         if (stealth_ship) {
1988                 // if not team targetable, check if within frustrum
1989                 if ( ai_is_stealth_visible(&Objects[viewer->objnum], target) == STEALTH_VISIBLE ) {
1990                         return 1;
1991                 } else {
1992                         return 0;
1993                 }
1994         }
1995
1996         // if not fully targetable by team, check awacs level with viewer
1997         // allow targeting even if only only partially targetable to player
1998         float radar_return = awacs_get_level(target, viewer);
1999         if ( radar_return > 0.4 ) {
2000                 return 1;
2001         } else {
2002                 return 0;
2003         }
2004 }
2005
2006 //      Return number of enemies attacking object objnum
2007 //
2008 // AL 10.26.97: Also include turrets on large ships when couting enemies attacking
2009 int num_enemies_attacking(int objnum)
2010 {
2011         object          *objp;
2012         ship                    *sp;
2013         ship_subsys     *ssp;
2014         ship_obj                *so;
2015         int                     count;
2016
2017         count = 0;
2018
2019         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2020                 objp = &Objects[so->objnum];
2021                 Assert(objp->instance != -1);
2022                 sp = &Ships[objp->instance];
2023
2024                 if (Ai_info[sp->ai_index].target_objnum == objnum)
2025                         count++;
2026
2027                 // consider turrets that may be attacking objnum (but only turrets on SIF_BIG_SHIP ships)
2028                 if ( Ship_info[sp->ship_info_index].flags & SIF_BIG_SHIP ) {
2029
2030                         // loop through all the subsystems, check if turret has objnum as a target
2031                         ssp = GET_FIRST(&sp->subsys_list);
2032                         while ( ssp != END_OF_LIST( &sp->subsys_list ) ) {
2033
2034                                 if ( ssp->system_info->type == SUBSYSTEM_TURRET ) {
2035                                         if ( (ssp->turret_enemy_objnum == objnum) && (ssp->current_hits > 0) ) {
2036                                                 count++;
2037                                         }
2038                                 }
2039                                 ssp = GET_NEXT( ssp );
2040                         } // end while
2041                 }
2042         }
2043
2044         return count;
2045 }
2046
2047 //      Get the team to fire on given an object.
2048 int get_enemy_team_mask(int objnum)
2049 {
2050         int     my_team, enemy_team_mask;
2051
2052         my_team = Ships[Objects[objnum].instance].team;
2053
2054         if (Mission_all_attack) {
2055                 //      All teams attack all teams.
2056                 switch (my_team) {
2057                 case TEAM_FRIENDLY:
2058                         enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2059                         break;
2060                 case TEAM_HOSTILE:
2061                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2062                         break;
2063                 case TEAM_NEUTRAL:
2064                         enemy_team_mask = TEAM_FRIENDLY | TEAM_HOSTILE | TEAM_TRAITOR;
2065                         break;
2066                 case TEAM_UNKNOWN:
2067                         enemy_team_mask = TEAM_HOSTILE;
2068                         break;
2069                 case TEAM_TRAITOR:
2070                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2071                         break;
2072                 default:
2073                         enemy_team_mask = TEAM_HOSTILE;
2074                         Int3();                 //      Illegal value for team!
2075                         break;
2076                 }
2077         } else {
2078                 switch (my_team) {
2079                 case TEAM_FRIENDLY:
2080                         enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2081                         break;
2082                 case TEAM_HOSTILE:
2083                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2084                         break;
2085                 case TEAM_NEUTRAL:
2086                         enemy_team_mask = TEAM_FRIENDLY | TEAM_TRAITOR;
2087                         break;
2088                 case TEAM_UNKNOWN:
2089                         enemy_team_mask = TEAM_HOSTILE;
2090                         break;
2091                 case TEAM_TRAITOR:
2092                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2093                         break;
2094                 default:
2095                         enemy_team_mask = TEAM_HOSTILE;
2096                         Int3();                 //      Illegal value for team!
2097                         break;
2098                 }
2099         }
2100
2101         return enemy_team_mask;
2102 }
2103
2104 //      Scan all the ships in *objp's wing.
2105 //      Return the lowest maximum speed of a ship in the wing.
2106 //      Current maximum speed (based on energy settings) is shipp->current_max_speed
2107 float get_wing_lowest_max_speed(object *objp)
2108 {
2109         ship            *shipp;
2110         ai_info *aip;
2111         float           lowest_max_speed;
2112         int             wingnum;
2113         object  *o;
2114         ship_obj        *so;
2115
2116         Assert(objp->type == OBJ_SHIP);
2117         Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
2118         shipp = &Ships[objp->instance];
2119         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
2120         aip = &Ai_info[shipp->ai_index];
2121
2122         wingnum = aip->wing;
2123
2124         lowest_max_speed = shipp->current_max_speed;
2125
2126         if ( wingnum == -1 )
2127                 return lowest_max_speed;
2128
2129         Assert(wingnum >= 0);
2130
2131         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2132                 o = &Objects[so->objnum];
2133                 ship    *oshipp = &Ships[o->instance];
2134                 ai_info *oaip = &Ai_info[oshipp->ai_index];
2135
2136                 if ((oaip->mode == AIM_WAYPOINTS) && (oaip->wing == wingnum)) {
2137                         //      Note: If a ship in the wing has a super low max speed, probably its engines are disabled.  So, fly along and
2138                         //      ignore the poor guy.
2139                         float   cur_max = oshipp->current_max_speed;
2140
2141                         if (oaip->ai_flags & AIF_DOCKED) {
2142                                 if (oaip->dock_objnum > -1)
2143                                         if (Objects[oaip->dock_objnum].type == OBJ_SHIP) 
2144                                                 cur_max *= o->phys_info.mass/(o->phys_info.mass + Objects[oaip->dock_objnum].phys_info.mass);
2145                         }
2146                                                         
2147                         if ((oshipp->current_max_speed > 5.0f) && (cur_max < lowest_max_speed)) {
2148                                 lowest_max_speed = cur_max;
2149                         }
2150                 }
2151         }
2152
2153         return lowest_max_speed;
2154 }
2155
2156 /*
2157 //      Tell everyone to ignore object objnum.
2158 void set_global_ignore_object(int objnum)
2159 {
2160         int     i;
2161
2162         Assert(Objects[objnum].type == OBJ_SHIP);
2163
2164         nprintf(("AI", "Telling everyone to ignore object %s\n", Ships[Objects[objnum].instance].ship_name));
2165
2166         for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2167                 if (Ignore_objects[i].objnum == -1) {
2168                         Ignore_objects[i].objnum = objnum;
2169                         Ignore_objects[i].signature = Objects[objnum].signature;
2170                         break;
2171                 }
2172         }
2173
2174         if (i == MAX_IGNORE_OBJECTS) {
2175                 //      Couldn't find a free slot, but maybe one of these objects has died.
2176                 for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2177                         int     o = Ignore_objects[i].objnum;
2178                         if (Objects[o].type != OBJ_SHIP)
2179                                 break;          //      Not a ship, so use this slot.
2180                         if (Objects[o].signature != Ignore_objects[i].signature)
2181                                 break;          //      Signatures don't match, so use this slot.
2182                 }
2183
2184                 if (i != MAX_IGNORE_OBJECTS) {
2185                         Ignore_objects[i].objnum = objnum;
2186                         Ignore_objects[i].signature = Objects[objnum].signature;
2187                 } else {
2188                         nprintf(("Warning", "Ignore_objects buffer full.  Stealing a slot to ignore object #%i\n"));
2189                         Int3();
2190
2191                         int     r;
2192
2193                         r = objnum % MAX_IGNORE_OBJECTS;
2194
2195                         Ignore_objects[r].objnum = objnum;
2196                         Ignore_objects[r].signature = Objects[objnum].signature;
2197                 }
2198         }
2199 }
2200
2201 */
2202
2203 //      Determine if object objnum is supposed to be ignored by object with ai_info *aip.
2204 //      Return:
2205 //              TRUE    if objnum is aip->ignore_objnum (and signatures match)
2206 //                              or objnum is in ignore wing
2207 //              FALSE   otherwise
2208 int is_ignore_object(ai_info *aip, int objnum)
2209 {
2210
2211 /*      //      First, scan all objects in global array of objects to be ignored.
2212         for (int i=0; i<MAX_IGNORE_OBJECTS; i++)
2213                 if (Ignore_objects[i].objnum != -1)
2214                         if (objnum == Ignore_objects[i].objnum)
2215                                 if (Objects[Ignore_objects[i].objnum].signature == Ignore_objects[i].signature)
2216                                         return 1;
2217 */
2218
2219         //      Didn't find in global list.  Now check 
2220         if (aip->ignore_objnum == UNUSED_OBJNUM)
2221                 return 0;                                                                       //      Not ignoring anything.
2222         else if (aip->ignore_objnum >= 0) {             //      This means it's ignoring an object, not a wing.
2223                 if (aip->ignore_objnum == objnum) {
2224                         if (Objects[aip->ignore_objnum].signature == aip->ignore_signature) {
2225                                 return 1;
2226                         } else {
2227                                 aip->ignore_objnum = UNUSED_OBJNUM;
2228                                 return 0;
2229                         }
2230                 } else {
2231                         return 0;
2232                 }
2233         } else {                                                                                        //      Ignoring a wing.
2234                 Int3(); // Should never happen.  I thought I removed this behavior! -- MK, 5/17/98
2235                 return 0;
2236 /*              int     ignore_wingnum = -(aip->ignore_objnum + 1);
2237
2238                 Assert(ignore_wingnum < MAX_WINGS);
2239                 Assert(aip->shipnum >= 0);
2240                 return (Ships[Objects[objnum].instance].wingnum == ignore_wingnum);
2241 */      }
2242 }
2243
2244 // -----------------------------------------------------------------------------
2245
2246 // given a ship with bounding box and a point, find the closest point on the bbox
2247 int get_nearest_bbox_point(object *ship_obj, vector *start, vector *box_pt)
2248 {
2249         vector temp, rf_start;
2250         polymodel *pm;
2251         pm = model_get(Ship_info[Ships[ship_obj->instance].ship_info_index].modelnum);
2252
2253         // get start in ship rf
2254         vm_vec_sub(&temp, start, &ship_obj->pos);
2255         vm_vec_rotate(&rf_start, &temp, &ship_obj->orient);
2256
2257         // find box_pt
2258         int inside = project_point_onto_bbox(&pm->mins, &pm->maxs, &rf_start, &temp);
2259
2260         // get box_pt in world rf
2261         vm_vec_unrotate(box_pt, &temp, &ship_obj->orient);
2262         vm_vec_add2(box_pt, &ship_obj->pos);
2263
2264         return inside;
2265 }
2266
2267
2268 typedef struct eval_nearest_objnum {
2269         int     objnum;
2270         object *trial_objp;
2271         int     enemy_team_mask;
2272         int     enemy_wing;
2273         float   range;
2274         int     max_attackers;
2275         int     nearest_objnum;
2276         float   nearest_dist;
2277         int     check_danger_weapon_objnum;
2278 } eval_nearest_objnum;
2279
2280
2281 void evaluate_object_as_nearest_objnum(eval_nearest_objnum *eno)
2282 {
2283         ai_info *aip;
2284         ship_subsys     *attacking_subsystem;
2285
2286         aip = &Ai_info[Ships[Objects[eno->objnum].instance].ai_index];
2287
2288         attacking_subsystem = aip->targeted_subsys;
2289
2290         if ((attacking_subsystem != NULL) || !(eno->trial_objp->flags & OF_PROTECTED)) {
2291                 if ( OBJ_INDEX(eno->trial_objp) != eno->objnum ) {
2292 #ifndef NDEBUG
2293                         if (!Player_attacking_enabled && (eno->trial_objp == Player_obj))
2294                                 return;
2295 #endif
2296                         //      If only supposed to attack ship in a specific wing, don't attack other ships.
2297                         if ((eno->enemy_wing != -1) && (Ships[eno->trial_objp->instance].wingnum != eno->enemy_wing))
2298                                 return;
2299
2300                         //      Don't keep firing at a ship that is in its death throes.
2301                         if (Ships[eno->trial_objp->instance].flags & SF_DYING)
2302                                 return;
2303
2304                         if (is_ignore_object(aip, ((eno->trial_objp)-Objects)))
2305                                 return;
2306
2307                         if (eno->trial_objp->flags & OF_PROTECTED)
2308                                 return;
2309
2310                         if (Ships[eno->trial_objp->instance].flags & SF_ARRIVING)
2311                                 return;
2312
2313                         ship_info *sip = &Ship_info[Ships[eno->trial_objp->instance].ship_info_index];
2314
2315                         if (sip->flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2316                                 return;
2317
2318                         if (Ships[eno->trial_objp->instance].team & eno->enemy_team_mask) {
2319                                 float   dist;
2320                                 int     num_attacking;
2321
2322                                 // Allow targeting of stealth in nebula by his firing at me
2323                                 // This is done for a specific ship, not generally.
2324                                 if ( !eno->check_danger_weapon_objnum ) {
2325                                         // check if can be targeted if inside nebula
2326                                         if ( !object_is_targetable(eno->trial_objp, &Ships[Objects[eno->objnum].instance]) ) {
2327                                                 // check if stealth ship is visible, but not "targetable"
2328                                                 if ( !((sip->flags & SIF_STEALTH) && ai_is_stealth_visible(&Objects[eno->objnum], eno->trial_objp)) ) {
2329                                                         return;
2330                                                 }
2331                                         }
2332                                 }
2333
2334                                 // if objnum is BIG or HUGE, find distance to bbox
2335                                 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
2336                                         vector box_pt;
2337                                         // check if inside bbox
2338                                         int inside = get_nearest_bbox_point(eno->trial_objp, &Objects[eno->objnum].pos, &box_pt);
2339                                         if (inside) {
2340                                                 dist = 10.0f;
2341                                                 // on the box
2342                                         } else {
2343                                                 dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &box_pt);
2344                                         }
2345                                 } else {
2346                                         dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &eno->trial_objp->pos);
2347                                 }
2348                                 
2349                                 //      Make it more likely that fighters (or bombers) will be picked as an enemy by scaling up distance for other types.
2350                                 if ((Ship_info[Ships[eno->trial_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))) {
2351                                         dist = dist * 0.5f;
2352                                 }
2353
2354                                 num_attacking = num_enemies_attacking(eno->trial_objp-Objects);
2355                                 if ((sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) || (num_attacking < eno->max_attackers)) {
2356                                         if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))){
2357                                                 dist *= (float) (num_attacking+2)/2.0f;                         //      prevents lots of ships from attacking same target
2358                                         }
2359
2360                                         if (eno->trial_objp->flags & OF_PLAYER_SHIP){
2361                                                 dist *= 1.0f + (NUM_SKILL_LEVELS - Game_skill_level - 1)/NUM_SKILL_LEVELS;      //      Favor attacking non-players based on skill level.
2362                                         }
2363
2364                                         if (dist < eno->nearest_dist) {
2365                                                 eno->nearest_dist = dist;
2366                                                 eno->nearest_objnum = eno->trial_objp-Objects;
2367                                         }
2368                                 }
2369                         }
2370                 }
2371         }
2372
2373 }
2374
2375
2376 //      Given an object and an enemy team, return the index of the nearest enemy object.
2377 //      Unless aip->targeted_subsys != NULL, don't allow to attack objects
2378 //      with OF_PROTECTED bit set.
2379 //      Ship must be within range "range".
2380 //      Don't attack a ship that already has at least max_attackers attacking it.
2381 int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers)
2382 {
2383         object  *danger_weapon_objp;
2384         ai_info *aip;
2385         ship_obj        *so;
2386
2387         // initialize eno struct
2388         eval_nearest_objnum eno;
2389         eno.enemy_team_mask = enemy_team_mask;
2390         eno.enemy_wing = enemy_wing;
2391         eno.max_attackers = max_attackers;
2392         eno.objnum = objnum;
2393         eno.range = range;
2394         eno.nearest_dist = range;
2395         eno.nearest_objnum = -1;
2396         eno.check_danger_weapon_objnum = 0;
2397
2398         // go through the list of all ships and evaluate as potential targets
2399         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2400                 eno.trial_objp = &Objects[so->objnum];
2401                 evaluate_object_as_nearest_objnum(&eno);
2402
2403         }
2404
2405         // check if danger_weapon_objnum has will show a stealth ship
2406         aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2407         if (aip->danger_weapon_objnum >= 0) {
2408                 danger_weapon_objp = &Objects[aip->danger_weapon_objnum];
2409                 // validate weapon
2410                 if (danger_weapon_objp->signature == aip->danger_weapon_signature) {
2411                         Assert(danger_weapon_objp->type == OBJ_WEAPON);
2412                         // check if parent is a ship
2413                         if (danger_weapon_objp->parent >= 0) {
2414                                 if ( is_object_stealth_ship(&Objects[danger_weapon_objp->parent]) ) {
2415                                         // check if stealthy
2416                                         if ( ai_is_stealth_visible(&Objects[objnum], &Objects[danger_weapon_objp->parent]) != STEALTH_FULLY_TARGETABLE ) {
2417                                                 // check if weapon is laser
2418                                                 if (Weapon_info[Weapons[danger_weapon_objp->instance].weapon_info_index].subtype == WP_LASER) {
2419                                                         // check stealth ship by its laser fire
2420                                                         eno.check_danger_weapon_objnum = 1;
2421                                                         eno.trial_objp = &Objects[danger_weapon_objp->parent];
2422                                                         evaluate_object_as_nearest_objnum(&eno);
2423                                                 }
2424                                         }
2425                                 }
2426                         }
2427                 }
2428         }
2429
2430         //      If only looking for target in certain wing and couldn't find anything in
2431         //      that wing, look for any object.
2432         if ((eno.nearest_objnum == -1) && (enemy_wing != -1)) {
2433                 return get_nearest_objnum(objnum, enemy_team_mask, -1, range, max_attackers);
2434         }
2435
2436         return eno.nearest_objnum;
2437 }
2438
2439 //      Given an object and an enemy team, return the index of the nearest enemy object.
2440 //      Unlike find_enemy or find_nearest_objnum, this doesn't care about things like the protected flag or number
2441 //      of enemies attacking.
2442 //      It is used to find the nearest enemy to determine things like whether to rearm.
2443 int find_nearby_hostile(int objnum, int enemy_team_mask, float range, int *count)
2444 {
2445         int             nearest_objnum;
2446         float           nearest_dist;
2447         object  *objp;
2448         ai_info *aip;
2449         ship_obj        *so;
2450
2451         nearest_objnum = -1;
2452         nearest_dist = range;
2453
2454         aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2455
2456         *count = 0;
2457
2458         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2459                 objp = &Objects[so->objnum];
2460
2461                 if ( OBJ_INDEX(objp) != objnum ) {
2462                         if (Ships[objp->instance].flags & SF_DYING)
2463                                 continue;
2464
2465                         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2466                                 continue;
2467
2468                         if (Ships[objp->instance].team & enemy_team_mask) {
2469                                 float   dist;
2470
2471                                 dist = vm_vec_dist_quick(&Objects[objnum].pos, &objp->pos) - objp->radius*0.75f;
2472                                 
2473                                 if (dist < range) {
2474                                         (*count)++;
2475
2476                                         if (dist < nearest_dist) {
2477                                                 nearest_dist = dist;
2478                                                 nearest_objnum = objp-Objects;
2479                                         }
2480                                 }
2481                         }
2482                 }
2483         }
2484
2485         return nearest_objnum;
2486 }
2487
2488 // return !0 if objp can be considered for a turret target, 0 otherwise
2489 // input:       objp                            =>      object that turret is considering as an enemy
2490 //                              turret_parent   =>      object index for ship that turret sits on
2491 int valid_turret_enemy(object *objp, object *turret_parent)
2492 {
2493         if ( objp == turret_parent ) {
2494                 return 0;
2495         }
2496
2497         if ( objp->type == OBJ_ASTEROID ) {
2498                 return 1;
2499         }
2500
2501         if ( (objp->type == OBJ_SHIP) ) {
2502                 ship *shipp;
2503                 shipp = &Ships[objp->instance];
2504
2505                 // don't fire at ships with protected bit set!!!
2506                 if ( objp->flags & OF_PROTECTED ) {
2507                         return 0;
2508                 }
2509
2510                 if ( !(Ship_info[shipp->ship_info_index].flags & SIF_DO_COLLISION_CHECK)) {
2511                         return 0;
2512                 }
2513
2514                 if (shipp->flags & SF_ARRIVING) {
2515                         return 0;
2516                 }
2517
2518                 return 1;
2519         }
2520
2521         if ( objp->type == OBJ_WEAPON ) {
2522                 if ( Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_BOMB ) {
2523                         if ( obj_team(turret_parent) != Weapons[objp->instance].team ) {
2524                                 return 1;
2525                         }
2526                 }
2527         }
2528
2529         return 0;
2530 }
2531
2532 // return 1 if objp is in fov of the specified turret, tp.  Otherwise return 0.
2533 //      dist = distance from turret to center point of object
2534 int object_in_turret_fov(object *objp, model_subsystem *tp, vector *tvec, vector *tpos, float dist)
2535 {
2536         vector  v2e;
2537         float           dot;
2538         vm_vec_normalized_dir(&v2e, &objp->pos, tpos);
2539         dot = vm_vec_dot(&v2e, tvec);
2540
2541         dot += objp->radius / (dist + objp->radius);
2542
2543         if ( dot >= tp->turret_fov ) {
2544                 return 1;
2545         }
2546
2547         return 0;
2548 }
2549
2550 // return 1 if bomb_objp is headed towards ship_objp
2551 int bomb_headed_towards_ship(object *bomb_objp, object *ship_objp)
2552 {
2553         float           dot;
2554         vector  bomb_to_ship_vector;
2555
2556         vm_vec_normalized_dir(&bomb_to_ship_vector, &ship_objp->pos, &bomb_objp->pos);
2557         dot = vm_vec_dot(&bomb_objp->orient.v.fvec, &bomb_to_ship_vector);
2558
2559         if ( dot > 0 ) {
2560                 return 1;
2561         }
2562
2563         return 0;
2564 }
2565
2566 // nubmer of live turrets with target_objnum 
2567 int num_turrets_attacking(object *turret_parent, int target_objnum) 
2568 {
2569         ship_subsys *ss;
2570         ship *shipp;
2571         int count = 0;
2572         shipp = &Ships[turret_parent->instance];
2573
2574         Assert(turret_parent->type == OBJ_SHIP);
2575         Assert(Objects[target_objnum].type == OBJ_SHIP);
2576
2577         for (ss=GET_FIRST(&shipp->subsys_list); ss!=END_OF_LIST(&shipp->subsys_list); ss=GET_NEXT(ss)) {
2578                 // check if subsys is alive
2579                 if (ss->current_hits <= 0.0f) {
2580                         continue;
2581                 }
2582
2583                 // check if it's a turret
2584                 if (ss->system_info->type != SUBSYSTEM_TURRET) {
2585                         continue;
2586                 }
2587
2588                 // if the turret is locked
2589                 if(ss->weapons.flags & SW_FLAG_TURRET_LOCK){
2590                         continue;
2591                 }               
2592
2593                 // check if turret is targeting target_objnum
2594                 if (ss->turret_enemy_objnum == target_objnum) {
2595                         count++;
2596                 }
2597         }
2598
2599         return count;
2600 }
2601
2602 float Lethality_range_const = 2.0f;
2603 DCF(lethality_range, "N for modifying range: 1 / (1+N) at 100")
2604 {
2605         dc_get_arg(ARG_FLOAT);
2606         Lethality_range_const = Dc_arg_float;
2607 }
2608
2609 float Player_lethality_bump[NUM_SKILL_LEVELS] = {
2610         // 0.0f, 5.0f, 10.0f, 25.0f, 40.0f
2611         0.0f, 0.0f, 0.0f, 0.0f, 0.0f
2612 };
2613
2614 // evaluate obj as posssible target for turret
2615 void evaluate_obj_as_target(object *objp, eval_enemy_obj_struct *eeo)
2616 {
2617         object  *turret_parent_obj = &Objects[eeo->turret_parent_objnum];
2618         ship            *shipp;
2619         model_subsystem *tp = eeo->turret_subsys->system_info;
2620         float dist;
2621
2622         // Don't look for bombs when weapon system is not ok
2623         if (objp->type == OBJ_WEAPON && !eeo->weapon_system_ok) {
2624                 return;
2625         }
2626
2627         if ( !valid_turret_enemy(objp, turret_parent_obj) ) {
2628                 return;
2629         }
2630
2631 #ifndef NDEBUG
2632         if (!Player_attacking_enabled && (objp == Player_obj)) {
2633                 return;
2634         }
2635 #endif
2636
2637         if ( objp->type == OBJ_SHIP ) {
2638                 shipp = &Ships[objp->instance];
2639
2640                 // check on enemy team
2641                 if ( !(shipp->team & eeo->enemy_team_mask) ) {
2642                         return;
2643                 }
2644
2645                 // check if protected
2646                 if (objp->flags & OF_PROTECTED) {
2647                         return;
2648                 }
2649
2650                 // check if beam protected
2651                 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2652                         if (objp->flags & OF_BEAM_PROTECTED) {
2653                                 return;
2654                         }
2655                 }
2656
2657                 if (eeo->big_only_flag) {
2658                         if (!(Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2659                                 return;
2660                         }
2661                 }
2662
2663                 // check if     turret flagged to only target tagged ships
2664                 if ( (eeo->turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && !ship_is_tagged(objp) ) {
2665                         return;
2666                 }
2667
2668                 // check if valid target in nebula
2669                 if ( !object_is_targetable(objp, &Ships[Objects[eeo->turret_parent_objnum].instance]) ) {
2670                         // BYPASS ocassionally for stealth
2671                         int try_anyway = FALSE;
2672                         if ( is_object_stealth_ship(objp) ) {
2673                                 float turret_stealth_find_chance = 0.5f;
2674                                 float speed_mod = -0.1f + vm_vec_mag_quick(&objp->phys_info.vel) / 70.0f;
2675                                 if (frand() > (turret_stealth_find_chance + speed_mod)) {
2676                                         try_anyway = TRUE;
2677                                 }
2678                         }
2679
2680                         if (!try_anyway) {
2681                                 return;
2682                         }
2683                 }
2684
2685         } else {
2686                 shipp = NULL;
2687         }
2688
2689         // modify dist for BIG|HUGE, getting closest point on bbox, if not inside
2690         dist = vm_vec_dist_quick(eeo->tpos, &objp->pos) - objp->radius;
2691         if (dist < 0.0f) {
2692                 dist = 0.0f;
2693         }
2694
2695         // check if object is a bomb attacking the turret parent
2696         // check if bomb is homing on the turret parent ship
2697         if (objp->type == OBJ_WEAPON) {
2698                 if ( Weapons[objp->instance].homing_object == &Objects[eeo->turret_parent_objnum] ) {
2699                         if ( dist < eeo->nearest_homing_bomb_dist ) {
2700                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2701                                         eeo->nearest_homing_bomb_dist = dist;
2702                                         eeo->nearest_homing_bomb_objnum = OBJ_INDEX(objp);
2703                                 }
2704                         }
2705                 // if not homing, check if bomb is flying towards ship
2706                 } else if ( bomb_headed_towards_ship(objp, &Objects[eeo->turret_parent_objnum]) ) {
2707                         if ( dist < eeo->nearest_bomb_dist ) {
2708                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2709                                         eeo->nearest_bomb_dist = dist;
2710                                         eeo->nearest_bomb_objnum = OBJ_INDEX(objp);
2711                                 }
2712                         }
2713                 }
2714         } // end weapon section
2715
2716         // maybe recalculate dist for big or huge ship
2717 //      if (shipp && (Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2718 //              fvi_ray_boundingbox(min, max, start, direction, hit);
2719 //              dist = vm_vec_dist_quick(hit, tvec);
2720 //      }
2721
2722         // check for nearest attcker
2723         if ( (shipp) && (dist < eeo->weapon_travel_dist) ) {
2724                 ai_info *aip = &Ai_info[shipp->ai_index];
2725
2726                 // modify distance based on number of turrets from my ship attacking enemy (add 10% per turret)
2727                 // dist *= (num_enemies_attacking(OBJ_INDEX(objp))+2)/2;        //      prevents lots of ships from attacking same target
2728                 int num_att_turrets = num_turrets_attacking(turret_parent_obj, OBJ_INDEX(objp));
2729                 dist *= (1.0f + 0.1f*num_att_turrets);
2730
2731                 // return if we're over the cap
2732                 int max_turrets = 3 + Game_skill_level * Game_skill_level;
2733                 if (num_att_turrets > max_turrets) {
2734                         return;
2735                 }
2736
2737                 // modify distance based on lethality of objp to my ship
2738                 float active_lethality = aip->lethality;
2739                 if (objp->flags & OF_PLAYER_SHIP) {
2740                         active_lethality += Player_lethality_bump[Game_skill_level];
2741                 }
2742
2743                 dist /= (1.0f + 0.01f*Lethality_range_const*active_lethality);
2744
2745                 // Make level 2 tagged ships more likely to be targeted
2746                 if (shipp->level2_tag_left > 0.0f) {
2747                         dist *= 0.3f;
2748                 }
2749
2750                 // check if objp is targeting the turret's ship, or if objp has just hit the turret's ship
2751                 if ( aip->target_objnum == eeo->turret_parent_objnum || aip->last_objsig_hit == Objects[eeo->turret_parent_objnum].signature ) {
2752                         // A turret will always target a ship that is attacking itself... self-preservation!
2753                         if ( aip->targeted_subsys == eeo->turret_subsys ) {
2754                                 dist *= 0.5f;   // highest priority
2755                         }
2756                 }
2757
2758                 // maybe update nearest attacker
2759                 if ( dist < eeo->nearest_attacker_dist ) {
2760                         if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2761                                 // 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));
2762                                 eeo->nearest_attacker_dist = dist;
2763                                 eeo->nearest_attacker_objnum = OBJ_INDEX(objp);
2764                         }
2765                 }
2766         } // end ship section
2767 }
2768
2769 // return 0 only if objnum is beam protected and turret is beam turret
2770 int is_target_beam_valid(ship_subsys *turret_subsys, int objnum)
2771 {
2772         // check if turret has beam weapon
2773         model_subsystem *tp = turret_subsys->system_info;
2774
2775         if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2776                 if (Objects[objnum].flags & OF_BEAM_PROTECTED) {
2777                         return 0;
2778                 }
2779
2780                 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_HUGE) {
2781                         if (Objects[objnum].type == OBJ_SHIP && !(Ship_info[Ships[Objects[objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
2782                                 return 0;
2783                         }
2784                 }
2785         }
2786
2787         return 1;
2788 }
2789
2790
2791 //      Given an object and an enemy team, return the index of the nearest enemy object.
2792 //
2793 // input:
2794 //                              turret_parent_objnum    => parent objnum for the turret
2795 //                              turret_subsys                   => pointer to system_info for the turret subsystem
2796 //                              enemy_team_mask         => OR'ed TEAM_ flags for the enemy of the turret parent ship
2797 //                              tpos                                            => position of turret (world coords)
2798 //                              tvec                                            => forward vector of turret (world coords)
2799 //                              current_enemy                   =>      objnum of current turret target
2800 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)
2801 {
2802         float                                   weapon_travel_dist;
2803         int                                     weapon_system_ok;
2804         object                          *objp;
2805         model_subsystem *tp;
2806         eval_enemy_obj_struct eeo;
2807
2808         // list of stuff to go thru
2809         ship_obj                *so;
2810         missile_obj *mo;
2811
2812         tp = turret_subsys->system_info;
2813         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);
2814
2815         // Set flag based on strength of weapons subsystem.  If weapons subsystem is destroyed, don't let turrets fire at bombs
2816         weapon_system_ok = 0;
2817         if ( ship_get_subsystem_strength( &Ships[Objects[turret_parent_objnum].instance], SUBSYSTEM_WEAPONS ) > 0 ) {
2818                 weapon_system_ok = 1;
2819         }
2820
2821         // Initialize eeo struct.
2822         eeo.turret_parent_objnum = turret_parent_objnum;
2823         eeo.weapon_system_ok = weapon_system_ok;
2824         eeo.weapon_travel_dist = weapon_travel_dist;
2825         eeo.big_only_flag = big_only_flag;
2826         eeo.enemy_team_mask = enemy_team_mask;
2827         eeo.current_enemy = current_enemy;
2828         eeo.tpos = tpos;
2829         eeo.tvec = tvec;
2830         eeo.turret_subsys = turret_subsys;
2831
2832         eeo.nearest_attacker_dist = 99999.0f;
2833         eeo.nearest_attacker_objnum = -1;
2834
2835         eeo.nearest_homing_bomb_dist = 99999.0f;
2836         eeo.nearest_homing_bomb_objnum = -1;
2837
2838         eeo.nearest_bomb_dist = 99999.0f;
2839         eeo.nearest_bomb_objnum = -1;
2840
2841         eeo.nearest_dist = 99999.0f;
2842         eeo.nearest_objnum = -1;
2843
2844
2845         // Missile_obj_list
2846         for( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
2847                 objp = &Objects[mo->objnum];
2848                 evaluate_obj_as_target(objp, &eeo);
2849         }
2850         // highest priority
2851         if ( eeo.nearest_homing_bomb_objnum != -1 ) {                                   // highest priority is an incoming homing bomb
2852                 return eeo.nearest_homing_bomb_objnum;
2853         } else if ( eeo.nearest_bomb_objnum != -1 ) {                                   // next highest priority is an incoming dumbfire bomb
2854                 return eeo.nearest_bomb_objnum;
2855         }
2856
2857
2858         // Ship_used_list
2859         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2860                 objp = &Objects[so->objnum];
2861                 evaluate_obj_as_target(objp, &eeo);
2862         }
2863
2864         Assert(eeo.nearest_attacker_objnum < 0 || is_target_beam_valid(turret_subsys, eeo.nearest_attacker_objnum));
2865                 // next highest priority is attacking ship
2866         if ( eeo.nearest_attacker_objnum != -1 ) {                      // next highest priority is an attacking ship
2867                 return eeo.nearest_attacker_objnum;
2868          }
2869
2870
2871 #ifndef FS2_DEMO
2872                 asteroid_obj *ao;
2873         // Asteroid_obj_list
2874         for( ao = GET_FIRST(&Asteroid_obj_list); ao != END_OF_LIST(&Asteroid_obj_list); ao = GET_NEXT(ao) ) {
2875                 objp = &Objects[ao->objnum];
2876                 evaluate_obj_as_target(objp, &eeo);
2877         }
2878 #endif
2879
2880         return eeo.nearest_objnum;                                                                              // lowest priority is the closest enemy objnum
2881 }
2882
2883 //      Return timestamp until a ship can find an enemy.
2884 //      Yes, no parameters.  Based solely on skill level.
2885 int get_enemy_timestamp()
2886 {
2887         return (NUM_SKILL_LEVELS - Game_skill_level) * ( (myrand() % 500) + 500);
2888 }
2889
2890 // -------------------------------------------------------------------
2891 //      Return objnum if enemy found, else return -1;
2892 //      Don't attack a ship that already has at least max_attackers attacking it.
2893 int find_enemy(int objnum, float range, int max_attackers)
2894 {
2895         int     enemy_team_mask;
2896
2897         enemy_team_mask = get_enemy_team_mask(objnum);
2898
2899         //      if target_objnum != -1, use that as goal.
2900         ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2901         if (timestamp_elapsed(aip->choose_enemy_timestamp)) {
2902                 aip->choose_enemy_timestamp = timestamp(get_enemy_timestamp());
2903                 if (aip->target_objnum != -1) {
2904                         int     target_objnum = aip->target_objnum;
2905
2906                         // DKA don't undo object as target in nebula missions.
2907                         // This could cause attack on ship on fringe on nebula to stop if attackee moves our of nebula range.  (BAD)
2908                         if ( (Objects[target_objnum].signature == aip->target_signature) ) {
2909                                 if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
2910                                         if (!(Objects[target_objnum].flags & OF_PROTECTED)) {
2911                                                 // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
2912                                                 return target_objnum;
2913                                         }
2914                                 }
2915                         } else {
2916                                 aip->target_objnum = -1;
2917                                 aip->target_signature = -1;
2918                         }
2919                 }
2920                 return get_nearest_objnum(objnum, enemy_team_mask, aip->enemy_wing, range, max_attackers);
2921         } else {
2922                 aip->target_objnum = -1;
2923                 aip->target_signature = -1;
2924                 return -1;
2925         }
2926
2927 }
2928
2929 int Use_parent_target = 0;
2930 DCF_BOOL(use_parent_target, Use_parent_target)
2931
2932 // -------------------------------------------------------------------
2933 //      Return objnum if enemy found, else return -1;
2934 //
2935 // input:
2936 //                              turret_subsys   => pointer to turret subsystem
2937 //                              objnum                  => parent objnum for the turret
2938 //                              tpos                            => position of turret (world coords)
2939 //                              tvec                            => forward vector of turret (world coords)
2940 //                              current_enemy   =>      objnum of current turret target
2941 int find_turret_enemy(ship_subsys *turret_subsys, int objnum, vector *tpos, vector *tvec, int current_enemy, float fov, int big_only_flag = 0)
2942 {
2943         int                                     enemy_team_mask, enemy_objnum;
2944         model_subsystem *tp;
2945         ship_info                       *sip;
2946
2947         tp = turret_subsys->system_info;
2948         enemy_team_mask = get_enemy_team_mask(objnum);
2949
2950         //      If a small ship and target_objnum != -1, use that as goal.
2951         ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2952         sip = &Ship_info[Ships[Objects[objnum].instance].ship_info_index];
2953
2954         if ((sip->flags & SIF_SMALL_SHIP) && (aip->target_objnum != -1)) {
2955                 int target_objnum = aip->target_objnum;
2956
2957                 if (Objects[target_objnum].signature == aip->target_signature) {
2958                         if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
2959                                 if ( !(Objects[target_objnum].flags & OF_PROTECTED) ) {         // check this flag as well.
2960                                         // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
2961                                         return target_objnum;
2962                                 }
2963                         }
2964                 } else {
2965                         aip->target_objnum = -1;
2966                         aip->target_signature = -1;
2967                 }
2968         // Not small or small with target objnum
2969         } else {
2970                 // maybe use aip->target_objnum as next target
2971                 if ((frand() < 0.8f) && (aip->target_objnum != -1) && Use_parent_target) {
2972
2973                         //check if aip->target_objnum is valid target
2974                         int target_flags = Objects[aip->target_objnum].flags;
2975                         if ( target_flags & OF_PROTECTED ) {
2976                                 // AL 2-27-98: why is a protected ship being targeted?
2977                                 set_target_objnum(aip, -1);
2978                                 return -1;
2979                         }
2980
2981                         // maybe use ship target_objnum if valid for turret
2982                         // check for beam weapon and beam protected
2983                         if ( !((target_flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) ) {
2984                                 if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
2985                                         // check for huge weapon and huge ship
2986                                         if ( !big_only_flag || (Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
2987                                                 // check for tagged only and tagged ship
2988                                                 if ( (turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && ship_is_tagged(&Objects[aip->target_objnum]) ) {
2989                                                         // select new target if aip->target_objnum is out of field of view
2990                                                         vector v2e;
2991                                                         float dot, dist;
2992                                                         dist = vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, tpos);
2993                                                         dot = vm_vec_dot(&v2e, tvec);
2994                                                         //      MODIFY FOR ATTACKING BIG SHIP
2995                                                         // dot += (0.5f * Objects[aip->target_objnum].radius / dist);
2996                                                         if (dot > fov) {
2997                                                                 return aip->target_objnum;
2998                                                         }
2999                                                 }
3000                                         }
3001                                 }
3002                         }
3003                 }
3004         }
3005
3006         enemy_objnum = get_nearest_turret_objnum(objnum, turret_subsys, enemy_team_mask, tpos, tvec, current_enemy, big_only_flag);
3007         if ( enemy_objnum >= 0 ) {
3008                 Assert( !((Objects[enemy_objnum].flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) );
3009                 if ( Objects[enemy_objnum].flags & OF_PROTECTED ) {
3010                         Int3();
3011                         enemy_objnum = aip->target_objnum;
3012                 }
3013         }
3014
3015         return enemy_objnum;
3016 }
3017
3018 //      If issued an order to a ship that's awaiting repair, abort that process.
3019 //      However, do not abort process for an object that is currently being repaired -- let it finish.
3020 void ai_set_goal_maybe_abort_dock(object *objp, ai_info *aip)
3021 {
3022         if (aip->ai_flags & AIF_AWAITING_REPAIR) {
3023                 object  *repair_obj;
3024
3025                 if (aip->dock_objnum == -1) {
3026                         repair_obj = NULL;
3027                 } else {
3028                         repair_obj = &Objects[aip->dock_objnum];
3029                 }
3030                 ai_do_objects_repairing_stuff( objp, repair_obj, REPAIR_INFO_ABORT );
3031         }
3032         aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);    //      Might request again after 30 seconds.
3033 }
3034
3035 void force_avoid_player_check(object *objp, ai_info *aip)
3036 {
3037         if (Ships[objp->instance].team == Player_ship->team){
3038                 aip->avoid_check_timestamp = timestamp(0);              //      Force a check for collision next frame.
3039         }
3040 }
3041
3042 //      --------------------------------------------------------------------------
3043 //      Set *attacked as object to attack for object *attacker
3044 //      If attacked == NULL, then attack any enemy object.
3045 //      Attack point *rel_pos on object.  This is for supporting attacking subsystems.
3046 void ai_attack_object(object *attacker, object *attacked, int priority, ship_subsys *ssp)
3047 {
3048         ai_info *aip;
3049
3050         Assert(attacker != NULL);
3051         Assert(attacker->instance != -1);
3052         Assert(Ships[attacker->instance].ai_index != -1);
3053
3054         aip = &Ai_info[Ships[attacker->instance].ai_index];
3055         force_avoid_player_check(attacker, aip);
3056
3057         aip->ok_to_target_timestamp = timestamp(0);             //      Guarantee we can target.
3058
3059 //      if (!strnicmp(Ships[attacker->instance].ship_name, NOX("Kami"), 4)) {
3060 //              aip->ai_flags |= AIF_KAMIKAZE;
3061 //              aip->ai_flags |= AIF_NO_DYNAMIC;
3062 //      }
3063
3064         if (attacker == attacked) {
3065                 Int3();         //      Bogus!  Who tried to get me to attack myself!  Trace out and fix!
3066                 return;
3067         }
3068
3069         //      Only set to chase if a fighter or bomber, otherwise just return.
3070         if (!(Ship_info[Ships[attacker->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
3071 //              nprintf(("AI","Note: AI ship %s refusing to set AI mode to AIM_CHASE\n", Ships[attacker->instance].ship_name));
3072 //              return;
3073                 nprintf(("AI", "AI ship %s is large ship ordered to attack %s\n", Ships[attacker->instance].ship_name, Ships[attacked->instance].ship_name));
3074         }
3075
3076         //      This is how "engage enemy" gets processed
3077         if (attacked == NULL) {
3078                 aip->choose_enemy_timestamp = timestamp(0);
3079                 // nebula safe
3080                 set_target_objnum(aip, find_enemy(attacker-Objects, 99999.9f, 4));
3081         } else {
3082                 // check if we can see atacked in nebula
3083                 if (aip->target_objnum != attacked - Objects) {
3084                         aip->aspect_locked_time = 0.0f;
3085                 }
3086                 set_target_objnum(aip, attacked - Objects);
3087         }
3088
3089         ai_set_goal_maybe_abort_dock(attacker, aip);
3090         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);     //      No dynamic targeting for 7 seconds.
3091
3092         if (is_ignore_object(aip, aip->target_objnum)) {
3093                 aip->ignore_objnum = UNUSED_OBJNUM;
3094         }
3095
3096         aip->mode = AIM_CHASE;
3097         aip->submode = SM_ATTACK;       // AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
3098                                                                                 //                                       for AIM_CHASE... it may have been not set correctly here
3099         if (ssp == NULL) {
3100                 set_targeted_subsys(aip, NULL, -1);
3101                 if (aip->target_objnum != -1) {
3102                         //nprintf(("AI", "Unprotecting ship %s\n", Ships[Objects[aip->target_objnum].instance].ship_name));
3103                         Objects[aip->target_objnum].flags &= ~OF_PROTECTED;     //      If ship had been protected, unprotect it.
3104                 }
3105         } else {
3106                 Int3(); //      Not supported yet!
3107         }
3108 }
3109
3110 //      --------------------------------------------------------------------------
3111 //      Set *attacked as object to attack for object *attacker
3112 //      Attack point *rel_pos on object.  This is for supporting attacking subsystems.
3113 void ai_attack_wing(object *attacker, int wingnum, int priority)
3114 {
3115         ai_info *aip;
3116
3117         Assert(attacker != NULL);
3118         Assert(attacker->instance != -1);
3119         Assert(Ships[attacker->instance].ai_index != -1);
3120
3121         aip = &Ai_info[Ships[attacker->instance].ai_index];
3122
3123         aip->enemy_wing = wingnum;
3124         aip->mode = AIM_CHASE;
3125         aip->submode = SM_ATTACK;       // AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
3126                                                                                 //                                       for AIM_CHASE... it may have been not set correctly here
3127
3128         aip->ok_to_target_timestamp = timestamp(0);             //      Guarantee we can target.
3129
3130         int count = Wings[wingnum].current_count;
3131         if (count > 0) {
3132                 int     index;
3133
3134                 index = (int) (frand() * count);
3135
3136                 if (index >= count)
3137                         index = 0;
3138
3139                 set_target_objnum(aip, Ships[Wings[wingnum].ship_index[index]].objnum);
3140
3141                 ai_set_goal_maybe_abort_dock(attacker, aip);
3142                 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);     //      No dynamic targeting for 7 seconds.
3143         }
3144 }
3145
3146 //      --------------------------------------------------------------------------
3147 //      Set *evaded as object for *evader to evade.
3148 void ai_evade_object(object *evader, object *evaded, int priority)
3149 {
3150         ai_info *aip;
3151
3152         Assert(evader != NULL);
3153         Assert(evaded != NULL);
3154         Assert(evader->instance != -1);
3155         Assert(Ships[evader->instance].ai_index != -1);
3156
3157         if (evaded == evader) {
3158                 Int3(); //      Bogus!  Who tried to get me to evade myself!  Trace out and fix!
3159                 return;
3160         }
3161
3162         aip = &Ai_info[Ships[evader->instance].ai_index];
3163
3164         set_target_objnum(aip, evaded - Objects);
3165         aip->mode = AIM_EVADE;
3166
3167 }
3168
3169 //      Ignore some object without changing mode.
3170 void ai_ignore_object(object *ignorer, object *ignored, int priority)
3171 {
3172         ai_info *aip;
3173
3174         Assert(ignorer != NULL);
3175         Assert(ignored != NULL);
3176         Assert(ignorer->instance != -1);
3177         Assert(Ships[ignorer->instance].ai_index != -1);
3178         Assert(ignorer != ignored);
3179
3180         aip = &Ai_info[Ships[ignorer->instance].ai_index];
3181
3182         //      MK, 5/17/98, removing ignoring of wings.
3183         //      It's too confusing.  It often causes mysterious behavior in which fighters unexpectedly refuse to attack anything.
3184 /*      if (Ships[ignored->instance].wingnum > -1) {
3185                 int wingnum, i;
3186
3187                 wingnum = Ships[ignored->instance].wingnum;
3188                 aip->ignore_objnum = -(wingnum+1);
3189                 // set protected bit for each ship in a wing
3190                 //      MK, 4/23/98: Only set for fighters if they are the original "ignored" object
3191                 for (i = 0; i < Wings[wingnum].current_count; i++ ) {
3192                         object  *objp;
3193
3194                         objp = &Objects[Ships[Wings[wingnum].ship_index[i]].objnum];
3195                         if (objp != ignored) {
3196                                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))
3197                                         continue;
3198                         }
3199
3200                         Objects[Ships[Wings[wingnum].ship_index[i]].objnum].flags |= OF_PROTECTED;
3201                 }
3202
3203         } else {
3204         */ {
3205                 aip->ignore_objnum = ignored - Objects;
3206                 aip->ignore_signature = ignored->signature;
3207                 aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3208                 ignored->flags |= OF_PROTECTED;                                 // set protected bit of ignored ship.
3209         }
3210
3211 }
3212
3213 //      Ignore some object without changing mode.
3214 void ai_ignore_wing(object *ignorer, int wingnum, int priority)
3215 {
3216         ai_info *aip;
3217
3218         Assert(ignorer != NULL);
3219         Assert(ignorer->instance != -1);
3220         Assert(Ships[ignorer->instance].ai_index != -1);
3221         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
3222
3223         aip = &Ai_info[Ships[ignorer->instance].ai_index];
3224
3225         aip->ignore_objnum = -(wingnum +1);
3226         aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3227 }
3228
3229
3230 //      Add a path point in the global buffer Path_points.
3231 //      modify_index = index in Path_points at which to store path point.
3232 //      If modify_index == -1, then create a new point.
3233 //      If a new point is created (ie, modify_index == -1), then Ppfp is updated.
3234 void add_path_point(vector *pos, int path_num, int path_index, int modify_index)
3235 {
3236         pnode   *pnp;
3237
3238         if (modify_index == -1) {
3239                 Assert(Ppfp-Path_points < MAX_PATH_POINTS-1);
3240                 pnp = Ppfp;
3241                 Ppfp++;
3242         } else {
3243                 Assert((modify_index >= 0) && (modify_index < MAX_PATH_POINTS-1));
3244                 pnp = &Path_points[modify_index];
3245         }
3246
3247         pnp->pos = *pos;
3248         pnp->path_num = path_num;
3249         pnp->path_index = path_index;
3250 }
3251
3252 //      Given two points on a sphere, the center of the sphere and the radius, return a
3253 //      point on the vector through the midpoint of the chord on the sphere.
3254 void bisect_chord(vector *p0, vector *p1, vector *centerp, float radius)
3255 {
3256         vector  tvec;
3257         vector  new_pnt;
3258
3259         vm_vec_add(&tvec, p0, p1);
3260         vm_vec_sub2(&tvec, centerp);
3261         vm_vec_sub2(&tvec, centerp);
3262         if (vm_vec_mag_quick(&tvec) < 0.1f) {
3263                 vm_vec_sub(&tvec, p0, p1);
3264                 if (fl_abs(tvec.xyz.x) <= fl_abs(tvec.xyz.z)){
3265                         tvec.xyz.x = -tvec.xyz.z;
3266                 } else {
3267                         tvec.xyz.y = -tvec.xyz.x;
3268                 }
3269         }
3270
3271         vm_vec_normalize(&tvec);
3272         vm_vec_scale(&tvec, radius);
3273         vm_vec_add(&new_pnt, centerp, &tvec);
3274
3275         add_path_point(&new_pnt, -1, -1, -1);
3276 }
3277                         
3278 //      Create a path from the current position to a goal position.
3279 //      The current position is in the current object and the goal position is
3280 //      in the goal object.
3281 //      It is ok to intersect the current object, but not the goal object.
3282 //      This function is useful for creating a path to an initial point near a large
3283 //      object.
3284 //
3285 // input:       subsys_path:    optional param (default 0), indicates this is a path to a subsystem
3286 void create_path_to_point(vector *curpos, vector *goalpos, object *curobjp, object *goalobjp, int subsys_path)
3287 {
3288         //      If can't cast vector to goalpos, then create an intermediate point.
3289         if (pp_collide(curpos, goalpos, goalobjp, curobjp->radius)) {
3290                 vector  tan1;
3291                 float           radius;
3292
3293                 // If this is a path to a subsystem, use SUBSYS_PATH_DIST as the radius for the object you are
3294                 // trying to avoid.  This is needed since subsystem paths extend out to SUBSYS_PATH_DIST, and we
3295                 // want ships to reach their path destination without flying to points that sit on the radius of
3296                 // a small ship
3297                 radius = goalobjp->radius;
3298                 if (subsys_path) {
3299                         if ( SUBSYS_PATH_DIST > goalobjp->radius ) {
3300                                 radius = SUBSYS_PATH_DIST;
3301                         }
3302                 }
3303
3304                 //      The intermediate point is at the intersection of:
3305                 //              tangent to *goalobjp sphere at point *goalpos
3306                 //              tangent to *goalobjp sphere through point *curpos in plane defined by *curpos, *goalpos, goalobjp->pos
3307                 //      Note, there are two tangents through *curpos, unless *curpos is on the
3308                 //      sphere.  The tangent that causes the nearer intersection (to *goalpos) is chosen.
3309                 get_tangent_point(&tan1, curpos, &goalobjp->pos, goalpos, radius);
3310
3311                 //      If we can't reach tan1 from curpos, insert a new point.
3312                 if (pp_collide(&tan1, curpos, goalobjp, curobjp->radius))
3313                         bisect_chord(curpos, &tan1, &goalobjp->pos, radius);
3314
3315                 add_path_point(&tan1, -1, -1, -1);
3316
3317                 //      If we can't reach goalpos from tan1, insert a new point.
3318                 if (pp_collide(goalpos, &tan1, goalobjp, curobjp->radius))
3319                         bisect_chord(goalpos, &tan1, &goalobjp->pos, radius);
3320         }
3321
3322 }
3323
3324 //      Given an object and a model path, globalize the points on the model
3325 //      and copy into the global path list.
3326 //      If pnp != NULL, then modify, in place, the path points.  This is used to create new
3327 //      globalized points when the base object has moved.
3328 // input:       randomize_pnt   => optional parameter (default value -1), add random vector in sphere to this path point
3329 void copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt)
3330 {
3331         matrix  m;
3332         int             i;
3333         vector  v1;
3334         int             pp_index;               //      index in Path_points at which to store point, if this is a modify-in-place (pnp ! NULL)
3335         int             start_index, finish_index;
3336         
3337         // nprintf(("AI", "Creating path for object %s in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
3338         
3339         //      Initialize pp_index.
3340         //      If pnp == NULL, that means we're creating new points.  If not NULL, then modify in place.
3341         if (pnp == NULL)
3342                 pp_index = -1;                  //      This tells add_path_point to create a new point.
3343         else
3344                 pp_index = 0;                   //      pp_index will get assigned to index in Path_points to reuse.
3345
3346         vm_copy_transpose_matrix(&m, &objp->orient);
3347
3348         if (dir == 1) {
3349                 start_index = 0;
3350                 finish_index = min(count, mp->nverts);
3351         } else {
3352                 Assert(dir == -1);      //      direction must be up by 1 or down by 1 and it's neither!
3353                 start_index = mp->nverts-1;
3354                 finish_index = max(-1, mp->nverts-1-count);
3355         }
3356
3357         int offset = 0;
3358         for (i=start_index; i != finish_index; i += dir) {
3359                 //      Globalize the point.
3360                 vm_vec_rotate(&v1, &mp->verts[i].pos, &m);
3361                 vm_vec_add2(&v1, &objp->pos);
3362
3363                 if ( randomize_pnt == i ) {
3364                         vector v_rand;
3365                         static_randvec(OBJ_INDEX(objp), &v_rand);
3366                         vm_vec_scale(&v_rand, 30.0f);
3367                         vm_vec_add2(&v1, &v_rand);
3368                 }
3369
3370                 if (pp_index != -1)
3371                         pp_index = pnp-Path_points + offset;
3372
3373                 add_path_point(&v1, path_num, i, pp_index);
3374                 offset++;
3375         }
3376 }
3377
3378
3379 //      For pl_objp, create a path along path path_num into mobjp.
3380 //      The tricky part of this problem is creating the entry to the first point on the
3381 //      predefined path.  The points on this entry path are based on the location of Pl_objp
3382 //      relative to the start of the path.
3383 //
3384 // input:
3385 //                              subsys_path:    optional param (default 0), indicating this is a path to a subsystem
3386 void create_model_path(object *pl_objp, object *mobjp, int path_num, int subsys_path)
3387 {       
3388         ship                    *shipp = &Ships[pl_objp->instance];
3389         ai_info         *aip = &Ai_info[shipp->ai_index];
3390
3391         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3392         polymodel       *pm = model_get(osip->modelnum);
3393         int                     num_points;
3394         model_path      *mp;
3395         pnode                   *ppfp_start = Ppfp;
3396         matrix          m;
3397         vector          gp0;
3398
3399         Assert(path_num >= 0);
3400
3401         //      Do garbage collection if necessary.
3402         if (Ppfp-Path_points + 64 > MAX_PATH_POINTS) {
3403                 garbage_collect_path_points();
3404                 ppfp_start = Ppfp;
3405         }
3406
3407         aip->path_start = Ppfp - Path_points;
3408         Assert(path_num < pm->n_paths);
3409         
3410         mp = &pm->paths[path_num];
3411         num_points = mp->nverts;
3412
3413         Assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
3414
3415         vm_copy_transpose_matrix(&m, &mobjp->orient);
3416         vm_vec_rotate(&gp0, &mp->verts[0].pos, &m);
3417         vm_vec_add2(&gp0, &mobjp->pos);
3418
3419         if (pp_collide(&pl_objp->pos, &gp0, mobjp, pl_objp->radius)) {
3420                 vector  perim_point1;
3421                 vector  perim_point2;
3422
3423                 perim_point2 = pl_objp->pos;
3424                 
3425                 //      If object that wants to dock is inside bounding sphere of object it wants to dock with, make it fly out.
3426                 //      Assume it can fly "straight" out to the bounding sphere.
3427                 if (vm_vec_dist_quick(&pl_objp->pos, &mobjp->pos) < mobjp->radius) {
3428                         project_point_to_perimeter(&perim_point2, &mobjp->pos, mobjp->radius, &pl_objp->pos);
3429                         add_path_point(&perim_point2, path_num, -1, -1);
3430                 }
3431
3432                 //      If last point on pre-defined path is inside bounding sphere, create a new point on the surface of the sphere.
3433                 if (vm_vec_dist_quick(&mobjp->pos, &gp0) < mobjp->radius) {
3434                         project_point_to_perimeter(&perim_point1, &mobjp->pos, mobjp->radius, &gp0);
3435                         create_path_to_point(&perim_point2, &perim_point1, pl_objp, mobjp, subsys_path);
3436                         add_path_point(&perim_point1, path_num, -1, -1);
3437                 } else {                //      The predefined path extends outside the sphere.  Create path to that point.
3438                         create_path_to_point(&perim_point2, &gp0, pl_objp, mobjp, subsys_path);
3439                 }
3440         }
3441
3442         // AL 12-31-97: If following a subsystem path, add random vector to second last path point
3443         if ( subsys_path ) {
3444                 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL, mp->nverts-2);
3445         } else {
3446                 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL);
3447         }
3448
3449         aip->path_cur = aip->path_start;
3450         aip->path_dir = PD_FORWARD;
3451         aip->path_objnum = mobjp-Objects;
3452         aip->mp_index = path_num;
3453         aip->path_length = Ppfp - ppfp_start;
3454         aip->path_next_check_time = timestamp(1);
3455
3456         aip->path_goal_obj_hash = create_object_hash(&Objects[aip->path_objnum]);
3457
3458         aip->path_next_create_time = timestamp(1000);   //      OK to try to create one second later
3459         aip->path_create_pos = pl_objp->pos;
3460         aip->path_create_orient = pl_objp->orient;
3461
3462         aip->ai_flags &= ~AIF_USE_EXIT_PATH;                    // ensure this flag is cleared
3463 }
3464
3465 //      For pl_objp, create a path along path path_num into mobjp.
3466 //      The tricky part of this problem is creating the entry to the first point on the
3467 //      predefined path.  The points on this entry path are based on the location of pl_objp
3468 //      relative to the start of the path.
3469 void create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count)
3470 {       
3471         ship                    *shipp = &Ships[pl_objp->instance];
3472         ai_info         *aip = &Ai_info[shipp->ai_index];
3473
3474         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3475         polymodel       *pm = model_get(osip->modelnum);
3476         int                     num_points;
3477         model_path      *mp;
3478         pnode                   *ppfp_start = Ppfp;
3479
3480         aip->path_start = Ppfp - Path_points;
3481         Assert(path_num < pm->n_paths);
3482         
3483         mp = &pm->paths[path_num];
3484         num_points = mp->nverts;
3485
3486         Assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
3487
3488         copy_xlate_model_path_points(mobjp, mp, -1, count, path_num, NULL);
3489
3490         aip->path_cur = aip->path_start;
3491         aip->path_dir = PD_FORWARD;
3492         aip->path_objnum = mobjp-Objects;
3493         aip->mp_index = path_num;
3494         aip->path_length = Ppfp - ppfp_start;
3495         aip->path_next_check_time = timestamp(1);
3496
3497         aip->ai_flags |= AIF_USE_EXIT_PATH;             // mark as exit path, referenced in maybe
3498 }
3499
3500 //      Return true if the vector from curpos to goalpos intersects with any ship other than the ignore objects.
3501 //      Calls pp_collide
3502 int pp_collide_any(vector *curpos, vector *goalpos, float radius, object *ignore_objp1, object *ignore_objp2, int big_only_flag)
3503 {
3504         ship_obj        *so;    
3505
3506         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
3507                 object *objp = &Objects[so->objnum];
3508
3509                 if (big_only_flag) {
3510                         if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
3511                                 continue;
3512                 }
3513
3514                 if ((objp != ignore_objp1) && (objp != ignore_objp2)) {
3515                         if (pp_collide(curpos, goalpos, objp, radius))
3516                                 return OBJ_INDEX(objp);
3517                 }
3518         }
3519
3520         return -1;
3521 }
3522
3523 //      Used to create docking paths and other pre-defined paths through ships.
3524 //      Creates a path in absolute space.
3525 //      Create a path into the object objnum.
3526 //
3527 // input:
3528 //      pl_objp:                        object that will use the path
3529 //      objnum:                 Object to find path to.
3530 //      path_num:               model path index to use
3531 //      exit_flag:              true means this is an exit path in the model
3532 // subsys_path: optional param (default 0) that indicates this is a path to a subsystem
3533 //      Exit:
3534 //      ai_info struct in Pl_objp gets stuffed with information to enable Pl_objp to fly the path.
3535 void ai_find_path(object *pl_objp, int objnum, int path_num, int exit_flag, int subsys_path)
3536 {
3537         ai_info *aip = &Ai_info[Ships[pl_objp->instance].ai_index];
3538
3539         Assert(path_num >= 0);
3540
3541         //      This is test code, find an object with paths.
3542         if (objnum != -1) {
3543                 object  *objp = &Objects[objnum];
3544
3545                 if (objp->type == OBJ_SHIP) {
3546                         polymodel *pm;
3547
3548                         ship    *shipp = &Ships[objp->instance];
3549                         pm = model_get( shipp->modelnum );
3550                         Assert(pm->n_paths > path_num);
3551                         aip->goal_objnum = objp-Objects;
3552                         aip->goal_signature = objp->signature;
3553                         if (exit_flag)
3554                                 create_model_exit_path(pl_objp, objp, path_num);
3555                         else
3556                                 create_model_path(pl_objp, objp, path_num, subsys_path);
3557                         return;
3558                 }
3559
3560         }
3561 }
3562
3563 extern int vector_object_collision(vector *start_pos, vector *end_pos, object *objp, float radius_scale);
3564
3565 //      Maybe make *objp avoid a player object.
3566 //      For now, 4/6/98, only check Player_obj.
3567 //      If player collision would occur, set AIF_AVOIDING_SMALL_SHIP bit in ai_flags.
3568 //      Set aip->avoid_goal_point
3569 int maybe_avoid_player(object *objp, vector *goal_pos)
3570 {
3571         ai_info *aip;
3572         vector  cur_pos, new_goal_pos;
3573         object  *player_objp;
3574         vector  n_vec_to_goal, n_vec_to_player;
3575
3576         aip = &Ai_info[Ships[objp->instance].ai_index];
3577
3578         if (!timestamp_elapsed(aip->avoid_check_timestamp))
3579                 return 0;
3580
3581         player_objp = Player_obj;
3582
3583         float   speed_time;
3584
3585         //      How far two ships could be apart and still collide within one second.
3586         speed_time = player_objp->phys_info.speed + objp->phys_info.speed;
3587
3588         float   obj_obj_dist;
3589
3590         obj_obj_dist = vm_vec_dist_quick(&player_objp->pos, &objp->pos);
3591
3592         if (obj_obj_dist > speed_time*2.0f)
3593                 return 0;
3594
3595         cur_pos = objp->pos;
3596
3597         new_goal_pos = *goal_pos;
3598
3599         float dist = vm_vec_normalized_dir(&n_vec_to_goal, goal_pos, &objp->pos);
3600         vm_vec_normalized_dir(&n_vec_to_player, &player_objp->pos, &objp->pos);
3601
3602         if (dist > speed_time*2.0f) {
3603                 vm_vec_scale_add(&new_goal_pos, &objp->pos, &n_vec_to_goal, 200.0f);
3604         }
3605
3606         if (vector_object_collision(&objp->pos, &new_goal_pos, player_objp, 1.5f)) {
3607                 aip->ai_flags |= AIF_AVOIDING_SMALL_SHIP;
3608
3609                 vector  avoid_vec;
3610
3611                 vm_vec_sub(&avoid_vec, &n_vec_to_goal, &n_vec_to_player);
3612                 if (vm_vec_mag_quick(&avoid_vec) < 0.01f) {
3613                         vm_vec_copy_scale(&avoid_vec, &objp->orient.v.rvec, frand()-0.5f);
3614                         vm_vec_scale_add2(&avoid_vec, &objp->orient.v.uvec, frand()-0.5f);
3615                         vm_vec_normalize(&avoid_vec);
3616                 } else {
3617                         vector  tvec1;
3618                         vm_vec_normalize(&avoid_vec);
3619                         vm_vec_crossprod(&tvec1, &n_vec_to_goal, &avoid_vec);
3620                         vm_vec_crossprod(&avoid_vec, &tvec1, &n_vec_to_player);
3621                 }
3622
3623                 //      Now, avoid_vec is a vector perpendicular to the vector to the player and the direction *objp
3624                 //      should fly in to avoid the player while still approaching its goal.
3625                 vm_vec_scale_add(&aip->avoid_goal_point, &player_objp->pos, &avoid_vec, 400.0f);
3626
3627                 aip->avoid_check_timestamp = timestamp(1000);
3628
3629                 return 1;
3630         } else {
3631                 aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
3632                 aip->avoid_check_timestamp = timestamp((int) (obj_obj_dist/200.0f) + 500);
3633
3634                 return 0;
3635         }
3636 }
3637
3638 //      Make object *still_objp enter AIM_STILL mode.
3639 //      Make it point at view_pos.
3640 void ai_stay_still(object *still_objp, vector *view_pos)
3641 {
3642         ship    *shipp;
3643         ai_info *aip;
3644
3645         Assert(still_objp->type == OBJ_SHIP);
3646         Assert((still_objp->instance >= 0) && (still_objp->instance < MAX_OBJECTS));
3647
3648         shipp = &Ships[still_objp->instance];
3649         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
3650
3651         aip = &Ai_info[shipp->ai_index];
3652
3653         aip->mode = AIM_STILL;
3654
3655         //      If view_pos not NULL, point at that point.  Else, point at a point directly in front of ship.  Ie, don't turn.
3656         if (view_pos != NULL)
3657                 aip->goal_point = *view_pos;
3658         else
3659                 vm_vec_scale_add(&aip->goal_point, &still_objp->pos, &still_objp->orient.v.fvec, 100.0f);
3660 }
3661
3662 // code which is called from ai_dock_with_object and ai_dock to set flags and apprioriate variable
3663 // when two objects have completed docking.  used because we can dock object initially at misison load
3664 // time (meaning that ai_dock() might never get called).  docker has docked with dockee (i.e. docker
3665 // would be a freighter and dockee would be a cargo).
3666 void ai_do_objects_docked_stuff(object *docker, object *dockee)
3667 {
3668         ai_info *aip, *other_aip;
3669
3670         aip = &Ai_info[Ships[docker->instance].ai_index];
3671         other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3672
3673         // set the flags and dock_objnum for both objects
3674         aip->ai_flags |= AIF_DOCKED;
3675         aip->dock_objnum = OBJ_INDEX(dockee);
3676         other_aip->ai_flags |= AIF_DOCKED;
3677         other_aip->dock_objnum = OBJ_INDEX(docker);
3678         aip->dock_signature = dockee->signature;
3679         other_aip->dock_signature = docker->signature;
3680
3681         // add multiplayer hook here to deal with docked objects.  We need to only send information
3682         // about the object that is docking.  Both flags will get updated.
3683         if ( MULTIPLAYER_MASTER )
3684                 send_ai_info_update_packet( docker, AI_UPDATE_DOCK );
3685
3686 }
3687
3688 // code which is called when objects become undocked. Equivalent of above function.
3689 // dockee might not be valid since this code can get called to cleanup after a ship
3690 // has blown up!
3691 void ai_do_objects_undocked_stuff( object *docker, object *dockee )
3692 {
3693         ai_info *aip, *other_aip;
3694
3695         // add multiplayer hook here to deal with undocked objects.  Do it before we
3696         // do anything else.  We don't need to send info for both objects, since we can find
3697         // it be dock_objnum
3698         if ( MULTIPLAYER_MASTER )
3699                 send_ai_info_update_packet( docker, AI_UPDATE_UNDOCK );
3700
3701         aip = &Ai_info[Ships[docker->instance].ai_index];
3702
3703         // set the flags and dock_objnum for both objects
3704         aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3705         aip->dock_objnum = -1;
3706         
3707         if ( dockee != NULL ) {
3708                 other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3709                 other_aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3710                 other_aip->dock_objnum = -1;
3711         }
3712
3713 }
3714
3715
3716 //      --------------------------------------------------------------------------
3717 //      Interface from goals code to AI.
3718 //      Cause *docker to dock with *dockee.
3719 //      priority is priority of goal from goals code.
3720 //      dock_type is:
3721 //              AIDO_DOCK               set goal of docking
3722 //              AIDO_DOCK_NOW   immediately dock, used for ships that need to be docked at mission start
3723 //              AIDO_UNDOCK             set goal of undocking
3724 void ai_dock_with_object(object *docker, object *dockee, int priority, int dock_type, int docker_index, int dockee_index)
3725 {
3726         ai_info         *aip;
3727         polymodel       *pm;
3728         ai_info         *dockee_aip;
3729
3730         Assert(docker != NULL);
3731         Assert(dockee != NULL);
3732         Assert(docker->instance != -1);
3733         Assert(Ships[docker->instance].ai_index != -1);
3734         Assert(Ships[dockee->instance].ai_index != -1);
3735         Assert( docker_index != -1 );
3736         Assert( dockee_index != -1 );
3737
3738         aip = &Ai_info[Ships[docker->instance].ai_index];
3739
3740         if ((aip->ai_flags & AIF_DOCKED) && (dock_type == AIDO_DOCK)) {
3741                 object  *dockee2;
3742                 int             docker_index2, dockee_index2;
3743
3744                 Assert(aip->dock_objnum > -1);
3745                 dockee2 = &Objects[aip->dock_objnum];
3746                 docker_index2 = aip->dock_index;
3747                 dockee_index2 = aip->dockee_index;
3748                 // MWA -- 2/9/98.  use the goal code to undock the ships since goals might need to get removed
3749                 // and that code will do it properly.  I'd actually be surprised if we got into this code anymore
3750                 // since the outer layer goal code should deal with this issue....but who knows...
3751                 ai_add_goal_ship_internal( aip, AI_GOAL_UNDOCK, NULL, -1, -1, 0 );
3752
3753                 // old code below
3754                 //ai_dock_with_object(docker, dockee2, priority, AIDO_UNDOCK, docker_index2, dockee_index2);
3755                 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));
3756                 nprintf(("AI", "...so ship %s will now undock.\n", Ships[docker->instance].ship_name));
3757                 return;
3758         }
3759
3760         dockee_aip = &Ai_info[Ships[dockee->instance].ai_index];
3761
3762         aip->goal_objnum = dockee - Objects;
3763         aip->goal_signature = dockee->signature;
3764
3765         aip->mode = AIM_DOCK;
3766
3767         switch (dock_type) {
3768         case AIDO_DOCK:
3769                 aip->submode = AIS_DOCK_0;
3770                 break;
3771         case AIDO_DOCK_NOW:
3772                 aip->submode = AIS_DOCK_3A;
3773                 break;
3774         case AIDO_UNDOCK:
3775                 aip->submode = AIS_UNDOCK_0;
3776                 break;
3777         default:
3778                 Int3();         //      Bogus dock_type.
3779         }
3780
3781         aip->submode_start_time = Missiontime;
3782         aip->dock_index = docker_index;
3783         aip->dockee_index = dockee_index;
3784
3785         dockee_aip->dock_index = dockee_index;
3786         dockee_aip->dockee_index = docker_index;
3787
3788         // get the path number to the docking point on the dockee.  Each docking point contains a list
3789         // of paths that the point can be reached by.  Pick the first path in the path list for now.
3790         // We only want to do this stuff if we are docking!!!  Be sure to set the path index
3791         if ((dock_type == AIDO_DOCK) || (dock_type == AIDO_DOCK_NOW)) {
3792                 pm = model_get( Ships[dockee->instance].modelnum );
3793                 Assert( pm->docking_bays[dockee_index].num_spline_paths > 0 );
3794
3795                 // only set the dock path index if we are docking.  undocking will assume that dock_path_index
3796                 // already set from some other docking command
3797                 aip->dock_path_index = dockee_index;
3798                 dockee_aip->dock_path_index = docker_index;
3799         }
3800
3801         if (dock_type != AIDO_DOCK_NOW) {
3802                 int path_num;
3803                 //      Note: Second parameter is dock path index.  This should be specified as an
3804                 //      _input_ to this function and passed through.  The path index should be already
3805                 // set for the undock function
3806                 path_num = ai_return_path_num_from_dockbay(dockee, dockee_index);
3807                 ai_find_path(docker, dockee-Objects, path_num, 0);
3808 //              ai_find_path(dockee-Objects, dockee_index, 0);
3809         } else {
3810                 dock_orient_and_approach(docker, dockee, DOA_DOCK_STAY);
3811                 //aip->dock_objnum = OBJ_INDEX(dockee);
3812                 ai_do_objects_docked_stuff( docker, dockee );
3813         }
3814
3815 }
3816
3817 //      Cause a ship to fly its waypoints.
3818 //      flags tells:
3819 //              WPF_REPEAT      Set -> repeat waypoints.
3820 void ai_start_waypoints(object *objp, int waypoint_list_index, int wp_flags)
3821 {
3822         ai_info *aip;
3823
3824         Assert(waypoint_list_index < Num_waypoint_lists);
3825
3826         //nprintf(("AI", "Frame %i: Ship %s instructed to fly waypoint list #%i\n", AI_FrameCount, Ships[objp->instance].ship_name, waypoint_list_index));
3827         aip = &Ai_info[Ships[objp->instance].ai_index];
3828
3829         if ( (aip->mode == AIM_WAYPOINTS) && (aip->wp_index == waypoint_list_index) )
3830                 return;
3831
3832         aip->ai_flags |= AIF_FORMATION_WING;
3833         aip->ai_flags &= ~AIF_FORMATION_OBJECT;
3834         aip->wp_list = waypoint_list_index;
3835         aip->wp_index = 0;
3836         aip->wp_flags = wp_flags;
3837         aip->mode = AIM_WAYPOINTS;
3838
3839         Assert(aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC);
3840 }
3841
3842 //      Make *objp stay within dist units of *other_objp
3843 void ai_do_stay_near(object *objp, object *other_objp, float dist)
3844 {
3845         ai_info *aip;
3846
3847         Assert(objp != other_objp);             //      Bogus!  Told to stay near self.
3848         Assert(objp->type == OBJ_SHIP);
3849         Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
3850
3851         aip = &Ai_info[Ships[objp->instance].ai_index];
3852
3853         aip->mode = AIM_STAY_NEAR;
3854         aip->submode = -1;
3855         aip->stay_near_distance = dist;
3856         aip->goal_objnum = other_objp-Objects;
3857         aip->goal_signature = other_objp->signature;
3858
3859 }
3860
3861 //      Make object *objp form on wing of object *goal_objp
3862 void ai_form_on_wing(object *objp, object *goal_objp)
3863 {
3864         ai_info *aip;
3865         ship                    *shipp;
3866         ship_info       *sip;
3867
3868         // objp == goal_objp sometimes in multiplayer when someone leaves a game -- make a simple
3869         // out for this case.
3870         if ( Game_mode & GM_MULTIPLAYER ) {
3871                 if ( objp == goal_objp ) {
3872                         return;
3873                 }
3874         }
3875
3876         Assert(objp != goal_objp);              //      Bogus!  Told to form on own's wing!
3877
3878         shipp = &Ships[objp->instance];
3879         sip = &Ship_info[shipp->ship_info_index];
3880
3881         //      Only fighters or bombers allowed to form on wing.
3882         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
3883                 nprintf(("AI", "Warning: Ship %s tried to form on player's wing, but not fighter or bomber.\n", shipp->ship_name));
3884                 return;
3885         }
3886
3887         aip = &Ai_info[Ships[objp->instance].ai_index];
3888
3889         aip->ai_flags &= ~AIF_FORMATION_WING;
3890         aip->ai_flags |= AIF_FORMATION_OBJECT;
3891
3892         aip->goal_objnum = goal_objp-Objects;
3893         ai_set_goal_maybe_abort_dock(objp, aip);
3894         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME*4);           //      Super extra long time until can target another ship.
3895
3896 }
3897
3898 //      Given an object and an object on whose wing to form, return slot to use.
3899 //      Optimize:
3900 //              This function is called per object in formation per frame.  Should store slot in ai_info struct.
3901 int ai_formation_object_get_slotnum(int objnum, object *objp)
3902 {
3903         int     slotnum = 1;                    //      Note: Slot #0 means leader, which isn't someone who was told to form-on-wing.
3904         object *o;
3905
3906         for ( o = GET_FIRST(&obj_used_list); o != END_OF_LIST(&obj_used_list); o = GET_NEXT(o) ) {
3907                 if (objp == o)
3908                         break;
3909                 else if (o->type == OBJ_SHIP)
3910                         if (Ai_info[Ships[o->instance].ai_index].ai_flags & AIF_FORMATION_OBJECT)
3911                                 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == objnum)
3912                                         slotnum++;
3913         }
3914
3915         Assert(o != END_OF_LIST(&obj_used_list));       //      Didn't find objp in list of used ships.  Impossible!
3916
3917         return slotnum;
3918 }
3919
3920 #define BIGNUM  100000.0f
3921
3922 int Debug_k = 0;
3923
3924 //      Given an attacker's position and a target's position and velocity, compute the time of
3925 //      intersection of a weapon fired by the attacker with speed weapon_speed.
3926 //      Return this value.  Return value of 0.0f means no collision is possible.
3927 float compute_collision_time(vector *targpos, vector *targvel, vector *attackpos, float weapon_speed)
3928 {
3929         vector  vec_to_target;
3930         float           pos_dot_vel;
3931         float           vel_sqr;
3932         float           discrim;
3933
3934         vm_vec_sub(&vec_to_target, targpos, attackpos);
3935         pos_dot_vel = vm_vec_dot(&vec_to_target, targvel);
3936         vel_sqr = vm_vec_dot(targvel, targvel) - weapon_speed*weapon_speed;
3937         discrim = pos_dot_vel*pos_dot_vel - vel_sqr*vm_vec_dot(&vec_to_target, &vec_to_target);
3938
3939         if (discrim > 0.0f) {
3940                 float   t1, t2, t_solve;
3941
3942                 t1 = (-pos_dot_vel + fl_sqrt(discrim)) / vel_sqr;
3943                 t2 = (-pos_dot_vel - fl_sqrt(discrim)) / vel_sqr;
3944
3945                 t_solve = BIGNUM;
3946
3947                 if (t1 > 0.0f)
3948                         t_solve = t1;
3949                 if ((t2 > 0.0f) && (t2 < t_solve))
3950                         t_solve = t2;
3951
3952                 if (t_solve < BIGNUM-1.0f) {
3953                         return t_solve + Debug_k * flFrametime;
3954                 }
3955         }
3956
3957         return 0.0f;
3958 }
3959
3960
3961 //      --------------------------------------------------------------------------
3962 //      If far away, use player's speed.
3963 //      If in between, lerp between player and laser speed
3964 //      If close, use laser speed.
3965 // Want to know how much time it will take to get to the enemy.
3966 // This function doesn't account for the fact that by the time the player
3967 // (or his laser) gets to the current enemy position, the enemy will have moved.
3968 // This is dealt with in polish_predicted_enemy_pos.
3969 float compute_time_to_enemy(float dist_to_enemy, object *pobjp, object *eobjp)
3970 {
3971         float   time_to_enemy;
3972         float   pl_speed = pobjp->phys_info.speed;
3973         float   max_laser_distance, max_laser_speed;
3974         int     bank_num, weapon_num;
3975         ship    *shipp = &Ships[pobjp->instance];
3976
3977         bank_num = shipp->weapons.current_primary_bank;
3978         weapon_num = shipp->weapons.primary_bank_weapons[bank_num];
3979         max_laser_speed = Weapon_info[weapon_num].max_speed;
3980         max_laser_distance = max_laser_speed * Weapon_info[weapon_num].lifetime;
3981
3982         //      If pretty far away, use player's speed to predict position, else
3983         //      use laser's speed because when close, we care more about hitting
3984         //      with a laser than about causing ship:ship rendezvous.
3985         if (dist_to_enemy > 1.5 * max_laser_distance) {
3986                 if (pl_speed > 0.0f)
3987                         time_to_enemy = dist_to_enemy/pl_speed;
3988                 else
3989                         time_to_enemy = 1.0f;
3990         } else if (dist_to_enemy > 1.1*max_laser_distance) {
3991                 if (pl_speed > 0.1f) {
3992                         float   scale;
3993
3994                         scale = (float) ((dist_to_enemy - max_laser_distance) / max_laser_distance);
3995                 
3996                         time_to_enemy = (float) (dist_to_enemy/(pl_speed * scale + max_laser_speed * (1.0f - scale)));
3997                 } else
3998                         time_to_enemy = 2.0f;
3999         } else
4000                 time_to_enemy = (float) (dist_to_enemy/max_laser_speed);
4001
4002         // return time_to_enemy * (1.0f + Ai_info[Ships[pobjp->instance].ai_index].lead_scale);
4003         return time_to_enemy + flFrametime;
4004 }
4005
4006 //      Stuff *dot and *tts.
4007 //      *dot is always computed.  If dot is less than zero, the magnitude is
4008 //      incorrect, not having been divided by distance.
4009 //      If *dot is > 0.0f, then tts is computed.  This is the time it will take object
4010 //      *objp to get to *pos, assuming it moves right at it.
4011 void fds_aux(float *dot, float *tts, vector *pos, float dtime, object *objp)
4012 {
4013         vector  v2s;
4014
4015         vm_vec_sub(&v2s, pos, &objp->pos);
4016         *dot = vm_vec_dot(&v2s, &objp->orient.v.fvec);
4017
4018         if (*dot > 0.0f) {
4019                 float   dist;
4020
4021                 dist = vm_vec_dist(&objp->pos, pos);
4022
4023                 if (dist > 0.1f)
4024                         *dot /= dist;
4025                 else
4026                         *dot = 1.0f;
4027
4028                 if (objp->phys_info.speed > 0.1f)
4029                         *tts = dist / objp->phys_info.speed;
4030                 else
4031                         *tts = dist * 100.0f;
4032         }
4033 }
4034
4035 /*
4036 //      Return index of weapon that could hit object *sobjp within dtime seconds.
4037 //      Actual time until impact returned in *atime.
4038 int find_danger_weapon(object *sobjp, float dtime, float *atime, float dot_threshhold)
4039 {
4040         object  *objp, *best_objp = NULL;
4041         float           best_tts = 1000.0f;
4042
4043         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
4044                 if ((objp->type == OBJ_WEAPON) && (sobjp-Objects != objp->parent)) {
4045                         float           dot, tts;
4046                         // vector       psp;            //      Predicted ship position.
4047
4048                         //      Get dot and time to current ship position.
4049                         fds_aux(&dot, &tts, &sobjp->pos, dtime, objp);
4050
4051                         //      If dot and tts are in plausible range, do more expensive stuff.
4052                         if (dot > 0.98f) {
4053 //                              float   dot_from_sobjp;
4054                                 vector  v2e;
4055
4056                                 vm_vec_normalized_dir(&v2e, &objp->pos, &sobjp->pos);
4057 //                              dot_from_sobjp = vm_vec_dot(&sobjp->orient.v.fvec, &v2e);
4058 //                              if (dot_from_sobjp >= dot_threshhold)
4059                                         if (tts < dtime) {
4060                                                 if (tts < best_tts) {
4061                                                         best_tts = tts;
4062                                                         best_objp = objp;
4063                                                 }
4064                                         }
4065                         }
4066                 }
4067         }
4068
4069         *atime = best_tts;
4070
4071         if (best_objp != NULL)
4072                 return best_objp-Objects;
4073         else
4074                 return -1;
4075 }
4076 */
4077
4078 //      --------------------------------------------------------------------------
4079 void ai_set_positions(object *pl_objp, object *en_objp, ai_info *aip, vector *player_pos, vector *enemy_pos)
4080 {
4081         *player_pos = pl_objp->pos;
4082
4083         if (aip->next_predict_pos_time > Missiontime) {
4084                 *enemy_pos = aip->last_predicted_enemy_pos;
4085         } else {
4086                 *enemy_pos = en_objp->pos;
4087
4088                 aip->next_predict_pos_time = Missiontime + Skill_level_delay[Game_skill_level];
4089                 aip->last_predicted_enemy_pos = *enemy_pos;
4090         }
4091
4092
4093 }
4094
4095 //      --------------------------------------------------------------------------
4096 int find_nearest_waypoint(object *objp)
4097 {
4098         int     i;
4099         float   dist, min_dist, dot;
4100         int     min_ind;
4101         ship    *shipp;
4102         int     wp_listnum;
4103         waypoint_list   *wpl;
4104
4105         shipp = &Ships[objp->instance];
4106         wp_listnum = Ai_info[Ships[objp->instance].ai_index].wp_list;
4107         Assert(wp_listnum > 0);
4108         wpl = &Waypoint_lists[wp_listnum];
4109
4110         min_dist = 999999.0f;
4111         min_ind = -1;
4112
4113         for (i=0; i<wpl->count; i++) {
4114                 dist = vm_vec_dist_quick(&objp->pos, &wpl->waypoints[i]);
4115                 dot = vm_vec_dot_to_point(&objp->orient.v.fvec, &objp->pos, &wpl->waypoints[i]);
4116                 dist = (float) (dist * (1.25 - dot));
4117                 if (dist < min_dist) {
4118                         min_dist = dist;
4119                         min_ind = i;
4120                 }
4121         }
4122
4123         Assert(min_ind != -1);
4124
4125         return min_ind;
4126 }
4127
4128 //      Given an ai_info struct, by reading current goal and path information,
4129 //      extract base path information and return in pmp and pmpv.
4130 //      Return true if found, else return false.
4131 //      false means the current point is not on the original path.
4132 int get_base_path_info(int path_cur, int goal_objnum, model_path **pmp, mp_vert **pmpv)
4133 {
4134         pnode                   *pn = &Path_points[path_cur];
4135         ship_info       *sip = &Ship_info[Ships[Objects[goal_objnum].instance].ship_info_index];
4136         polymodel       *pm = model_get(sip->modelnum);
4137         static          int     debug_last_index = -1;
4138         *pmpv = NULL;
4139         *pmp = NULL;
4140
4141         if (pn->path_num != -1) {
4142                 *pmp = &pm->paths[pn->path_num];
4143                 if (pn->path_index != -1)
4144                         *pmpv = &(*pmp)->verts[pn->path_index];
4145                 else
4146                         return 0;
4147         } else
4148                 return 0;
4149
4150 /*      if (debug_last_index != *pmpv-(*pmp)->verts) {
4151                 debug_last_index = *pmpv-(*pmp)->verts;
4152                 nprintf(("AI", "Point %i has %i turrets: ", *pmpv-(*pmp)->verts, (*pmpv)->nturrets));
4153                 for (int i=0; i<(*pmpv)->nturrets; i++) {
4154                         nprintf(("AI", "%i ", (*pmpv)->turret_ids[i]));
4155                 }
4156                 nprintf(("AI", "\n"));
4157         }
4158 */
4159         return 1;
4160 }
4161
4162 //      Modify, in place, the points in a global model path.
4163 //      Only modify those points that are defined in the model path.  Don't modify the
4164 //      leadin points, such as those that are necessary to get the model on the path.
4165 void modify_model_path_points(object *objp)
4166 {       
4167         ai_info         *aip = &Ai_info[Ships[objp->instance].ai_index];
4168         object          *mobjp = &Objects[aip->path_objnum];
4169         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
4170         polymodel       *pm = model_get(osip->modelnum);
4171         pnode                   *pnp;
4172         int                     path_num, dir;
4173
4174         Assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
4175
4176         pnp = &Path_points[aip->path_start];
4177         while ((pnp->path_index == -1) && (pnp-Path_points - aip->path_start < aip->path_length))
4178                 pnp++;
4179
4180         path_num = pnp->path_num;
4181         Assert((path_num >= 0) && (path_num < pm->n_paths));
4182         
4183         Assert(pnp->path_index != -1);  //      If this is -1, that means we never found the model path points
4184
4185         dir = 1;
4186         if ( aip->ai_flags & AIF_USE_EXIT_PATH ) {
4187                 dir = -1;
4188         }
4189
4190         copy_xlate_model_path_points(mobjp, &pm->paths[path_num], dir, pm->paths[path_num].nverts, path_num, pnp);
4191 }
4192
4193 //      Return an indication of the distance between two matrices.
4194 //      This is the sum of the distances of their dot products from 1.0f.
4195 float ai_matrix_dist(matrix *mat1, matrix *mat2)
4196 {
4197         float   t;
4198
4199         t =  1.0f - vm_vec_dot(&mat1->v.fvec, &mat2->v.fvec);
4200         t += 1.0f - vm_vec_dot(&mat1->v.uvec, &mat2->v.uvec);
4201         t += 1.0f - vm_vec_dot(&mat1->v.rvec, &mat2->v.rvec);
4202
4203         return t;
4204 }
4205
4206
4207 //      Paths are created in absolute space, so a moving object needs to have model paths within it recreated.
4208 //      This uses the hash functions which means the slightest movement will cause a recreate, though the timestamp
4209 //      prevents this from happening too often.
4210 //      force_recreate_flag TRUE means to recreate regardless of timestamp.
4211 //      Returns TRUE if path recreated.
4212 float maybe_recreate_path(object *objp, ai_info *aip, int force_recreate_flag)
4213 {
4214         int     hashval;
4215
4216         Assert(&Ai_info[Ships[objp->instance].ai_index] == aip);
4217
4218         if ((aip->mode == AIM_BAY_EMERGE) || (aip->mode == AIM_BAY_DEPART))
4219                 if ((OBJ_INDEX(objp) % 4) == (Framecount % 4))
4220                         force_recreate_flag = 1;
4221
4222         //      If no path, that means we don't need one.
4223         if (aip->path_start == -1)
4224                 return 0.0f;
4225
4226         // AL 11-12-97: If AIF_USE_STATIC_PATH is set, don't try to recreate.  This is needed when ships
4227         //                                  emerge from fighter bays.  We don't need to recreate the path.. and in case the 
4228         //              parent ship dies, we still want to be able to continue on the path
4229         if ( aip->ai_flags & AIF_USE_STATIC_PATH ) 
4230                 return 0.0f;
4231
4232         if (force_recreate_flag || timestamp_elapsed(aip->path_next_create_time)) {
4233                 object  *path_objp;
4234
4235                 path_objp = &Objects[aip->path_objnum];
4236
4237                 if ((hashval = create_object_hash(path_objp)) != aip->path_goal_obj_hash) {
4238                         float dist;
4239                         
4240                         dist = vm_vec_dist_quick(&path_objp->pos, &aip->path_create_pos);
4241                         dist += ai_matrix_dist(&path_objp->orient, &aip->path_create_orient) * 25.0f;
4242
4243                         if (force_recreate_flag || (dist > 2.0f)) {
4244                                 aip->path_next_create_time = timestamp(1000);   //      Update again in as little as 1000 milliseconds, ie 1 second.
4245                                 aip->path_goal_obj_hash = hashval;
4246                                 modify_model_path_points(objp);
4247
4248                                 aip->path_create_pos = path_objp->pos;
4249                                 aip->path_create_orient = path_objp->orient;
4250                                 
4251                                 return dist;
4252                         }
4253                 }
4254         }
4255
4256         return 0.0f;
4257 }
4258
4259 //      Set acceleration for ai_dock().
4260 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)
4261 {
4262         float prev_dot_to_goal = aip->prev_dot_to_goal;
4263         
4264         aip->prev_dot_to_goal = dot;
4265
4266         if (objp->phys_info.speed < 0.0f) {
4267                 accelerate_ship(aip, 1.0f/32.0f);
4268         } else if ((prev_dot_to_goal-dot) > 0.01) {
4269                 if (prev_dot_to_goal > dot + 0.05f) {
4270                         accelerate_ship(aip, 0.0f);
4271                 } else {
4272                         change_acceleration(aip, -1.0f);        //      -1.0f means subtract off flFrametime from acceleration value in 0.0..1.0
4273                 }
4274         } else {
4275                 if ((aip->mode == AIM_DOCK) && (dist_to_next < 150.0f) && (aip->path_start + aip->path_length - 2 == aip->path_cur)) {
4276                         set_accel_for_target_speed(objp, sip->max_speed * max(dist_to_next/500.0f, 1.0f));
4277                         //mprintf(("dist = %7.3f, speed = %7.3f\n", dist_to_next, objp->phys_info.speed));
4278                 } else if ((dot_to_next >= dot * .9) || (dist_to_next > 100.0f)) {
4279                         if (dist_to_goal > 200.0f)
4280                                 set_accel_for_target_speed(objp, sip->max_speed * (dot + 1.0f) / 2.0f);
4281                         else {
4282                                 float   xdot;
4283
4284                                 xdot = (dot_to_next + dot)/2.0f;
4285                                 if (xdot < 0.0f)
4286                                         xdot = 0.0f;
4287
4288                                 // AL: if following a path not in dock mode, move full speed
4289                                 if (( aip->mode != AIM_DOCK ) && (dot > 0.9f)) {
4290                                         set_accel_for_target_speed(objp, sip->max_speed*dot*dot*dot);
4291                                 } else {
4292                                         if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4293                                                 //nprintf(("AI", "Target speed = %7.3f\n", dist_to_goal/8.0f));
4294                                                 set_accel_for_target_speed(objp, dist_to_goal/8.0f + 2.0f);
4295                                         } else {
4296                                                 set_accel_for_target_speed(objp, sip->max_speed * (2*xdot + 0.25f)/4.0f);
4297                                         }
4298                                 }
4299                         }
4300                 } else {
4301                         float   xdot;
4302
4303                         xdot = max(dot_to_next, 0.1f);
4304                         if ( aip->mode != AIM_DOCK ) {
4305                                 set_accel_for_target_speed(objp, sip->max_speed);
4306                         } else {
4307                                 float   speed;
4308                                 if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4309                                         speed = dist_to_goal/8.0f + 2.0f;
4310                                 } else if (dist_to_goal < 4*objp->radius + 50.0f) {
4311                                         speed = dist_to_goal/4.0f + 4.0f;
4312                                 } else {
4313                                         speed = sip->max_speed * (3*xdot + 1.0f)/4.0f;
4314                                 }
4315                                 if (aip->mode == AIM_DOCK) {
4316                                         speed = speed * 2.0f + 1.0f;
4317                                         if (aip->goal_objnum != -1) {
4318                                                 speed += Objects[aip->goal_objnum].phys_info.speed;
4319                                         }
4320                                 }
4321
4322                                 set_accel_for_target_speed(objp, speed);
4323                         }
4324                 }
4325         }
4326 }
4327
4328 //      --------------------------------------------------------------------------
4329 //      Follow a path associated with a large object, such as a capital ship.
4330 //      The points defined on the path are in the object's reference frame.
4331 //      The object of interest is goal_objnum.
4332 //      The paths are defined in the model.  The path of interest is wp_list.
4333 //      The next goal point in the path is wp_index.
4334 //      wp_flags contain special information specific to the path.
4335
4336 // The path vertices are defined by model_path structs:
4337 //              typedef struct model_path {
4338 //                      char            name[MAX_NAME_LEN];                                     // name of the subsystem.  Probably displayed on HUD
4339 //                      int             nverts;
4340 //                      vector  *verts;
4341 //              } model_path;
4342
4343 //      The polymodel struct for the object contains the following:
4344 //              int                     n_paths;
4345 //              model_path      *paths;
4346
4347 //      Returns distance to goal point.
4348 float ai_path()
4349 {
4350         polymodel       *pm;
4351         int             num_paths, num_points;
4352         float           dot, dist_to_goal, dist_to_next, speed, dot_to_next;
4353         ship            *shipp = &Ships[Pl_objp->instance];
4354         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4355         ai_info *aip;
4356         vector  nvel_vec;
4357         float           mag, prev_dot_to_goal;
4358         vector  temp_vec, *slop_vec;
4359         object  *gobjp;
4360         ship            *gshipp;
4361         vector  *cvp, *nvp, next_vec, gcvp, gnvp;               //      current and next vertices in global coordinates.
4362
4363         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4364
4365         Assert(aip->goal_objnum != -1);
4366         Assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
4367
4368         gobjp = &Objects[aip->goal_objnum];
4369         gshipp = &Ships[gobjp->instance];
4370
4371         pm = model_get( gshipp->modelnum );
4372         num_paths = pm->n_paths;
4373         Assert(num_paths > 0);
4374
4375         if (aip->path_start == -1) {
4376                 int path_num;
4377                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
4378                 Assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
4379                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
4380         }
4381
4382         // nprintf(("AI", "Frame: %i, Path index = %i/%i\n", AI_FrameCount, aip->path_cur-aip->path_start, aip->path_length));
4383
4384         maybe_recreate_path(Pl_objp, aip, 0);
4385
4386         num_points = aip->path_length;
4387
4388         //      Set cvp and nvp as pointers to current and next vertices of interest on path.
4389         cvp = &Path_points[aip->path_cur].pos;
4390         if ((aip->path_cur + aip->path_dir - aip->path_start < num_points) || (aip->path_cur + aip->path_dir < aip->path_start))
4391                 nvp = &Path_points[aip->path_cur + aip->path_dir].pos;
4392         else {
4393                 //      If this is 0, then path length must be 1 which means we have no direction!
4394                 Assert((aip->path_cur - aip->path_dir >= aip->path_start) && (aip->path_cur - aip->path_dir - aip->path_start < num_points));
4395                 //      Cleanup for above Assert() which we hit too near release. -- MK, 5/24/98.
4396                 if (aip->path_cur - aip->path_dir - aip->path_start >= num_points) {
4397                         if (aip->path_dir == 1)
4398                                 aip->path_cur = aip->path_start;
4399                         else
4400                                 aip->path_cur = aip->path_start + num_points - 1;
4401                 }
4402
4403                 vector  delvec;
4404                 vm_vec_sub(&delvec, cvp, &Path_points[aip->path_cur - aip->path_dir].pos);
4405                 vm_vec_normalize(&delvec);
4406                 vm_vec_scale_add(&next_vec, cvp, &delvec, 10.0f);
4407                 nvp = &next_vec;
4408         }
4409
4410         //      Interrupt if can't get to current goal point.  Debug only.
4411 /*      if (pp_collide(&Pl_objp->pos, cvp, gobjp, Pl_objp->radius)) {
4412                 Int3();
4413         }
4414 */
4415         //      See if can reach next point (as opposed to current point)
4416         //      However, don't do this if docking and next point is last point.
4417         //      That is, we don't want to pursue the last point under control of the
4418         //      path code.  In docking, this is a special hack.
4419         if ((aip->mode != AIM_DOCK) || ((aip->path_cur-aip->path_start) < num_points - 2)) {
4420                 if ((aip->path_cur + aip->path_dir > aip->path_start) && (aip->path_cur + aip->path_dir < aip->path_start + num_points-2)) {
4421                         if ( timestamp_elapsed(aip->path_next_check_time)) {
4422                                 aip->path_next_check_time = timestamp( 3000 );
4423                                 if (!pp_collide(&Pl_objp->pos, nvp, gobjp, 1.1f * Pl_objp->radius)) {
4424                                         cvp = nvp;
4425                                         aip->path_cur += aip->path_dir;
4426                                         nvp = &Path_points[aip->path_cur].pos;
4427                                         //nprintf(("AI", "Reach: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4428                                 }
4429                         }
4430                 }
4431         }
4432
4433         gcvp = *cvp;
4434         gnvp = *nvp;
4435
4436         speed = Pl_objp->phys_info.speed;
4437
4438         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &gcvp);
4439         dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, &gnvp);
4440         //      Can't use fvec, need to use velocity vector because we aren't necessarily
4441         //      moving in the direction we're facing.
4442
4443 //      if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4444         if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4445                 mag = 0.0f;
4446                 vm_vec_zero(&nvel_vec);
4447         } else
4448                 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4449
4450         //      If moving not-very-slowly and sliding, then try to slide at goal, rather than
4451         //      point at goal.
4452         slop_vec = NULL;
4453         if (mag < 1.0f)
4454                 nvel_vec = Pl_objp->orient.v.fvec;
4455         else if (mag > 5.0f) {
4456                 float   nv_dot;
4457                 nv_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4458                 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4459                         slop_vec = &temp_vec;
4460                         vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.v.fvec);
4461                 }
4462         }
4463
4464         if (dist_to_goal > 0.1f)
4465                 ai_turn_towards_vector(&gcvp, Pl_objp, flFrametime, sip->srotation_time, slop_vec, NULL, 0.0f, 0);
4466
4467         //      Code to control speed is MUCH less forgiving in path following than in waypoint
4468         //      following.  Must be very close to path or might hit objects.
4469         prev_dot_to_goal = aip->prev_dot_to_goal;
4470         dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gcvp);
4471         dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gnvp);
4472
4473         set_accel_for_docking(Pl_objp, aip, dot, dot_to_next, dist_to_next, dist_to_goal, sip);
4474         aip->prev_dot_to_goal = dot;
4475
4476 //mprintf(("Goal index = %i, dist = %7.3f, dot = %7.3f\n", wp_index, dist_to_goal, dot));
4477
4478         //      If moving at a non-tiny velocity, detect attaining path point by its being close to
4479         //      line between previous and current object location.
4480         if ((dist_to_goal < MIN_DIST_TO_WAYPOINT_GOAL) || (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f)) {
4481                 vector  nearest_point;
4482                 float           r, min_dist_to_goal;
4483
4484                 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, &gcvp);
4485
4486                 //      Set min_dist_to_goal = how close must be to waypoint to pick next one.
4487                 //      If docking and this is the second last waypoint, must be very close.
4488                 if ((aip->mode == AIM_DOCK) && (aip->path_cur >= aip->path_length-2))
4489                         min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL;
4490                 else
4491                         min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius;
4492
4493                 if ( (vm_vec_dist_quick(&Pl_objp->pos, &gcvp) < min_dist_to_goal) ||
4494                         ((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, &gcvp) < (MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius))) {
4495                         aip->path_cur += aip->path_dir;
4496                         //nprintf(("AI", " Near: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4497                         if (((aip->path_cur - aip->path_start) > (num_points+1)) || (aip->path_cur < aip->path_start)) {
4498                                 Assert(aip->mode != AIM_DOCK);          //      If docking, should never get this far, getting to last point handled outside ai_path()
4499                                 aip->path_dir = -aip->path_dir;
4500 //                              aip->path_cur += aip->path_dir;
4501                         }
4502                 }
4503         }
4504
4505         return dist_to_goal;
4506 }
4507
4508 void update_min_max(float val, float *min, float *max)
4509 {
4510         if (val < *min)
4511                 *min = val;
4512         else if (val > *max)
4513                 *max = val;
4514 }
4515
4516 //      Stuff bounding box of all enemy objects within "range" units of object *my_objp.
4517 //      Stuff ni min_vec and max_vec.
4518 //      Return value: Number of enemy objects in bounding box.
4519 int get_enemy_team_range(object *my_objp, float range, int enemy_team_mask, vector *min_vec, vector *max_vec)
4520 {
4521         object  *objp;
4522         ship_obj        *so;
4523         int             count = 0;
4524
4525         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4526                 objp = &Objects[so->objnum];
4527                 if (Ships[objp->instance].team & enemy_team_mask) {
4528                         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))
4529                                 if (vm_vec_dist_quick(&my_objp->pos, &objp->pos) < range) {
4530                                         if (count == 0) {
4531                                                 *min_vec = objp->pos;
4532                                                 *max_vec = objp->pos;
4533                                                 count++;
4534                                         } else {
4535                                                 update_min_max(objp->pos.xyz.x, &min_vec->xyz.x, &max_vec->xyz.x);
4536                                                 update_min_max(objp->pos.xyz.y, &min_vec->xyz.y, &max_vec->xyz.y);
4537                                                 update_min_max(objp->pos.xyz.z, &min_vec->xyz.z, &max_vec->xyz.z);
4538                                         }
4539                                 }
4540
4541                 }
4542         }
4543
4544         return count;
4545 }
4546
4547 //      Pick a relatively safe spot for objp to fly to.
4548 //      Problem:
4549 //              Finds a spot away from any enemy within a bounding box.
4550 //              Doesn't verify that "safe spot" is not near some other enemy.
4551 void ai_safety_pick_spot(object *objp)
4552 {
4553         int             objnum;
4554         int             enemy_team_mask;
4555         vector  min_vec, max_vec;
4556         vector  vec_to_center, center;
4557         vector  goal_pos;
4558
4559         objnum = OBJ_INDEX(objp);
4560
4561         enemy_team_mask = get_enemy_team_mask(objnum);
4562
4563         if (get_enemy_team_range(objp, 1000.0f, enemy_team_mask, &min_vec, &max_vec)) {
4564                 vm_vec_avg(&center, &min_vec, &max_vec);
4565                 vm_vec_normalized_dir(&vec_to_center, &center, &objp->pos);
4566
4567                 vm_vec_scale_add(&goal_pos, &center, &vec_to_center, 2000.0f);
4568         } else
4569                 vm_vec_scale_add(&goal_pos, &objp->pos, &objp->orient.v.fvec, 100.0f);
4570
4571         Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pos;
4572 }
4573
4574 //      Fly to desired safe point.
4575 // Returns distance to that point.
4576 float ai_safety_goto_spot(object *objp)
4577 {
4578         float   dot, dist;
4579         ai_info *aip;
4580         vector  vec_to_goal;
4581         ship_info       *sip;
4582         float   dot_val;
4583
4584         sip = &Ship_info[Ships[objp->instance].ship_info_index];
4585
4586         aip = &Ai_info[Ships[objp->instance].ai_index];
4587         dist = vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
4588         dot = vm_vec_dot(&vec_to_goal, &objp->orient.v.fvec);
4589
4590         dot_val = (1.1f + dot) / 2.0f;
4591         if (dist > 200.0f) {
4592                 set_accel_for_target_speed(objp, sip->max_speed * dot_val);
4593         } else
4594                 set_accel_for_target_speed(objp, sip->max_speed * dot_val * (dist/200.0f + 0.2f));
4595
4596         return dist;
4597 }
4598
4599 void ai_safety_circle_spot(object *objp)
4600 {
4601         vector  goal_point;
4602         ship_info       *sip;
4603         float           dot;
4604
4605         sip = &Ship_info[Ships[objp->instance].ship_info_index];
4606
4607         goal_point = Ai_info[Ships[objp->instance].ai_index].goal_point;
4608         dot = turn_towards_tangent(objp, &goal_point, 250.0f);  //      Increased from 50 to 250 to make circling not look so wacky.
4609
4610         set_accel_for_target_speed(objp, 0.5f * (1.0f + dot) * sip->max_speed/4.0f);
4611
4612 //      float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
4613 //      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));
4614
4615 }
4616
4617 //      --------------------------------------------------------------------------
4618 void ai_safety()
4619 {
4620         ai_info *aip;
4621
4622         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4623
4624         switch (aip->submode) {
4625         case AISS_1:
4626                 ai_safety_pick_spot(Pl_objp);
4627                 aip->submode = AISS_2;
4628                 aip->submode_start_time = Missiontime;
4629                 break;
4630         case AISS_1a:   //      Pick a safe point because we just got whacked!
4631                 Int3();
4632                 break;
4633         case AISS_2:
4634                 if (ai_safety_goto_spot(Pl_objp) < 25.0f) {
4635                         aip->submode = AISS_3;
4636                         aip->submode_start_time = Missiontime;
4637                 }
4638                 break;
4639         case AISS_3:
4640                 ai_safety_circle_spot(Pl_objp);
4641                 break;
4642         default:
4643                 Int3();         //      Illegal submode for ai_safety();
4644                 break;
4645         }
4646 }
4647
4648 //      --------------------------------------------------------------------------
4649 //      make Pl_objp fly waypoints.
4650 void ai_waypoints()
4651 {
4652         int             wp_index;
4653         vector  *wp_cur, *wp_next;
4654         float           dot, dist_to_goal, dist_to_next, speed, dot_to_next;
4655         ship            *shipp = &Ships[Pl_objp->instance];
4656         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4657         waypoint_list   *wpl;
4658         ai_info *aip;
4659         vector  nvel_vec;
4660         float           mag;
4661         float           prev_dot_to_goal;
4662         vector  temp_vec;
4663         vector  *slop_vec;
4664
4665         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4666
4667         wp_index = aip->wp_index;
4668
4669         if (wp_index == -1) {
4670                 ai_start_waypoints(Pl_objp, 0, WPF_REPEAT);
4671                 wp_index = aip->wp_index;
4672                 aip->wp_dir = 1;
4673         }
4674
4675         wpl = &Waypoint_lists[Ai_info[Ships[Pl_objp->instance].ai_index].wp_list];
4676
4677         Assert(wpl->count);     // What? Is this zero? Probably wp_index never got initialized!
4678
4679         wp_cur = &wpl->waypoints[wp_index];
4680         wp_next = &wpl->waypoints[(wp_index+1) % wpl->count];
4681         speed = Pl_objp->phys_info.speed;
4682
4683         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, wp_cur);
4684         dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, wp_next);
4685
4686         //      Can't use fvec, need to use velocity vector because we aren't necessarily
4687         //      moving in the direction we're facing.
4688         // AL 23-3-98: Account for very small velocities by checking result of vm_vec_mag().
4689         //                                      If we don't vm_vec_copy_normalize() will think it is normalizing a null vector.
4690 //      if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4691         if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4692                 mag = 0.0f;
4693                 vm_vec_zero(&nvel_vec);
4694         } else {
4695                 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4696         }
4697
4698         //      If moving not-very-slowly and sliding, then try to slide at goal, rather than
4699         //      point at goal.
4700         slop_vec = NULL;
4701         if (mag < 1.0f) {
4702                 nvel_vec = Pl_objp->orient.v.fvec;
4703         } else if (mag > 5.0f) {
4704                 float   nv_dot;
4705                 nv_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4706                 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4707                         slop_vec = &temp_vec;
4708                         vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.v.fvec);
4709                 }
4710         }
4711
4712         //      If a wing leader, take turns more slowly, based on size of wing.
4713         int     scale;
4714
4715         if (Ai_info[Ships[Pl_objp->instance].ai_index].wing >= 0) {
4716                 scale = Wings[Ai_info[Ships[Pl_objp->instance].ai_index].wing].current_count;
4717                 scale = (int) ((scale+1)/2);
4718         } else {
4719                 scale = 1;
4720         }
4721
4722         if (dist_to_goal > 0.1f) {
4723                 ai_turn_towards_vector(wp_cur, Pl_objp, flFrametime, sip->srotation_time*3.0f*scale, slop_vec, NULL, 0.0f, 0);
4724         }
4725
4726         prev_dot_to_goal = aip->prev_dot_to_goal;
4727         dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_cur);
4728         dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_next);
4729         aip->prev_dot_to_goal = dot;
4730
4731         //      If there is no next point on the path, don't care about dot to next.
4732         if (wp_index + 1 >= wpl->count) {
4733                 dot_to_next = dot;
4734         }
4735
4736         // nprintf(("AI", "Wp #%i, dot = %6.3f, next dot = %6.3f, dist = %7.2f\n", wp_index, dot, dot_to_next, dist_to_goal));
4737
4738         if (Pl_objp->phys_info.speed < 0.0f) {
4739                 accelerate_ship(aip, 1.0f/32);
4740         } else if (prev_dot_to_goal > dot+0.01f) {
4741                 //      We are further from pointing at our goal this frame than last frame, so slow down.
4742                 set_accel_for_target_speed(Pl_objp, Pl_objp->phys_info.speed * 0.95f);
4743         } else if (dist_to_goal < 100.0f) {
4744                 float slew_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4745                 if (fl_abs(slew_dot) < 0.9f) {
4746                         accelerate_ship(aip, 0.0f);
4747                 } else if (dot < 0.88f + 0.1f*(100.0f - dist_to_goal)/100.0f) {
4748                         accelerate_ship(aip, 0.0f);
4749                 } else {
4750                         accelerate_ship(aip, 0.5f * dot * dot);
4751                 }
4752         } else {
4753                 float   dot1;
4754                 if (dist_to_goal < 250.0f) {
4755                         dot1 = dot*dot*dot;                             //      Very important to be pointing towards goal when nearby.  Note, cubing preserves sign.
4756                 } else {
4757                         if (dot > 0.0f) {
4758                                 dot1 = dot*dot;
4759                         } else {
4760                                 dot1 = dot;
4761                         }
4762                 }
4763
4764                 if (dist_to_goal > 100.0f + Pl_objp->radius * 2) {
4765                         if (dot < 0.2f) {
4766                                 dot1 = 0.2f;
4767                         }
4768                 }
4769
4770                 if (sip->flags & SIF_SMALL_SHIP) {
4771                         set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/5.0f);
4772                 } else {
4773                         set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/10.0f);
4774                 }
4775         }
4776
4777         //      Make sure not travelling too fast for someone to keep up.
4778         float   max_allowed_speed = 9999.9f;
4779
4780         if (shipp->wingnum != -1) {
4781                 max_allowed_speed = 0.9f * get_wing_lowest_max_speed(Pl_objp);
4782         }
4783
4784         // check if waypoint speed cap is set and adjust max speed
4785         if (aip->waypoint_speed_cap > 0) {
4786                 max_allowed_speed = (float) aip->waypoint_speed_cap;
4787         }
4788
4789         if (aip->prev_accel * shipp->current_max_speed > max_allowed_speed) {
4790                 accelerate_ship(aip, max_allowed_speed / shipp->current_max_speed);
4791         }
4792
4793         if (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f) {
4794                 vector  nearest_point;
4795                 float           r;
4796
4797                 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, wp_cur);
4798
4799                 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))) ||
4800                         ((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, wp_cur) < (MIN_DIST_TO_WAYPOINT_GOAL + fl_sqrt(Pl_objp->radius)))) {
4801                         wp_index++;
4802                         if (wp_index >= wpl->count)
4803                                 if (aip->wp_flags & WPF_REPEAT) {
4804                                         wp_index = 0;
4805                                 } else {
4806                                         int treat_as_ship;
4807
4808                                         // when not repeating waypoints -- mark the goal as done and put and entry into the mission log
4809                                         // we must be careful when dealing with wings.  A ship in a wing might be completing
4810                                         // a waypoint for for the entire wing, or it might be completing a goal for itself.  If
4811                                         // for itself and in a wing, treat the completion as we would a ship
4812                                         treat_as_ship = 1;
4813                                         if ( Ships[Pl_objp->instance].wingnum != -1 ) {
4814                                                 int type;
4815
4816                                                 // I don't think that you can fly waypoints as dynamic goals!!!
4817                                                 // -- This is legal, just stupid. -- Assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4818                                                 
4819                                                 //      Clean up from above Assert, just in case we ship without fixing it.  (Encountered by JimB on 2/9/98)
4820                                                 if ( (aip->active_goal == AI_GOAL_NONE) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC) ) {
4821                                                         aip->mode = AIM_NONE;
4822                                                         Int3(); //      Look at the ship, find out of it's supposed to be flying waypoints. -- MK.
4823                                                 }
4824
4825                                                 type = aip->goals[aip->active_goal].type;
4826                                                 if ( (type == AIG_TYPE_EVENT_WING) || (type == AIG_TYPE_PLAYER_WING) ) {
4827                                                         treat_as_ship = 0;
4828                                                 } else {
4829                                                         treat_as_ship = 1;
4830                                                 }
4831                                         }
4832
4833                                         // if the ship is not in a wing, remove the goal and continue on
4834                                         if ( treat_as_ship ) {
4835                                                 ai_mission_goal_complete( aip );                                        // this call should reset the AI mode
4836                                                 mission_log_add_entry(LOG_WAYPOINTS_DONE, Ships[Pl_objp->instance].ship_name, wpl->name, -1 );
4837                                         } else {
4838                                                 // this ship is in a wing.  We must mark the goal as being completed for all ships
4839                                                 // in the wing.  We will also mark an entry in the log that the wing completed the goal
4840                                                 // not the individual ship.
4841                                                 ai_mission_wing_goal_complete( Ships[Pl_objp->instance].wingnum, &(aip->goals[aip->active_goal]) );
4842                                                 mission_log_add_entry( LOG_WAYPOINTS_DONE, Wings[Ships[Pl_objp->instance].wingnum].name, wpl->name, -1 );
4843                                         }
4844                                         //wp_index = wpl->count-1;
4845                                 }
4846
4847                         aip->wp_index = wp_index;
4848                 }
4849         }
4850 }
4851
4852 //      Make Pl_objp avoid En_objp
4853 //      Not like evading.  This is for avoiding a collision!
4854 //      Note, use sliding if available.
4855 void avoid_ship()
4856 {
4857         //      To avoid an object, turn towards right or left vector until facing away from object.
4858         //      To choose right vs. left, pick one that is further from center of avoid object.
4859         //      Keep turning away from until pointing away from ship.
4860         //      Stay in avoid mode until at least 3 enemy ship radii away.
4861
4862         //      Speed setting:
4863         //      If inside sphere, zero speed and turn towards outside.
4864         //      If outside sphere, inside 2x sphere, set speed percent of max to:
4865         //              max(away_dot, (dist-rad)/rad)
4866         //      where away_dot is dot(Pl_objp->v.fvec, vec_En_objp_to_Pl_objp)
4867
4868         vector  vec_to_enemy;
4869         float           away_dot;
4870         float           dist;
4871         ship            *shipp = &Ships[Pl_objp->instance];
4872         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4873         ai_info *aip = &Ai_info[shipp->ai_index];
4874         vector  player_pos, enemy_pos;
4875
4876         // if we're avoiding a stealth ship, then we know where he is, update with no error
4877         if ( is_object_stealth_ship(En_objp) ) {
4878                 update_ai_stealth_info_with_error(aip/*, 1*/);
4879         }
4880
4881         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
4882         vm_vec_sub(&vec_to_enemy, &enemy_pos, &Pl_objp->pos);
4883
4884         dist = vm_vec_normalize(&vec_to_enemy);
4885         away_dot = -vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_to_enemy);
4886         
4887         if ((sip->max_vel.xyz.x > 0.0f) || (sip->max_vel.xyz.y > 0.0f)) {
4888                 if (vm_vec_dot(&Pl_objp->orient.v.rvec, &vec_to_enemy) > 0.0f) {
4889                         AI_ci.sideways = -1.0f;
4890                 } else {
4891                         AI_ci.sideways = 1.0f;
4892                 }
4893                 if (vm_vec_dot(&Pl_objp->orient.v.uvec, &vec_to_enemy) > 0.0f) {
4894                         AI_ci.vertical = -1.0f;
4895                 } else {
4896                         AI_ci.vertical = 1.0f;
4897                 }
4898         }               
4899
4900         //nprintf(("AI", "Frame %i: Sliding: %s %s\n", Framecount, AI_ci.sideways < 0 ? "left" : "right", AI_ci.vertical < 0 ? "down" : "up" ));
4901         // nprintf(("AI", "away_dot = %6.3f, dist = %7.2f, dist/radsum = %6.3f\n", away_dot, dist, dist/(Pl_objp->radius + En_objp->radius)));
4902
4903         //      If in front of enemy, turn away from it.
4904         //      If behind enemy, try to get fully behind it.
4905         if (away_dot < 0.0f) {
4906                 turn_away_from_point(Pl_objp, &enemy_pos, Pl_objp->phys_info.speed);
4907         } else {
4908                 vector  goal_pos;
4909
4910                 vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.fvec, -100.0f);
4911                 turn_towards_point(Pl_objp, &goal_pos, NULL, Pl_objp->phys_info.speed);
4912         }
4913
4914         //      Set speed.
4915         float   radsum = Pl_objp->radius + En_objp->radius;
4916
4917         if (dist < radsum)
4918                 accelerate_ship(aip, max(away_dot, 0.2f));
4919         else if (dist < 2*radsum)
4920                 accelerate_ship(aip, max(away_dot, (dist - radsum) / radsum)+0.2f);
4921         else
4922                 accelerate_ship(aip, 1.0f);
4923
4924 }
4925
4926 //      Maybe it's time to resume the previous AI mode in aip->previous_mode.
4927 //      Each type of previous_mode has its own criteria on when to resume.
4928 //      Return true if previous mode was resumed.
4929 int maybe_resume_previous_mode(object *objp, ai_info *aip)
4930 {
4931         //      Only (maybe) resume previous goal if current goal is dynamic.
4932         if (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC)
4933                 return 0;
4934
4935         if (aip->mode == AIM_EVADE_WEAPON) {
4936                 if (timestamp_elapsed(aip->mode_time) || (((aip->nearest_locked_object == -1) || (Objects[aip->nearest_locked_object].type != OBJ_WEAPON)) && (aip->danger_weapon_objnum == -1))) {
4937                         Assert(aip->previous_mode != AIM_EVADE_WEAPON);
4938                         aip->mode = aip->previous_mode;
4939                         aip->submode = aip->previous_submode;
4940                         aip->submode_start_time = Missiontime;
4941                         aip->active_goal = AI_GOAL_NONE;
4942                         aip->mode_time = -1;                    //      Means do forever.
4943                         return 1;
4944                 }
4945         } else if ( aip->previous_mode == AIM_GUARD) {
4946                 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
4947                         object  *guard_objp;
4948                         float   dist;
4949
4950                         guard_objp = &Objects[aip->guard_objnum];
4951                         dist = vm_vec_dist_quick(&guard_objp->pos, &objp->pos);
4952
4953                         //      If guarding ship is far away from guardee and enemy is far away from guardee,
4954                         //      then stop chasing and resume guarding.
4955                         if (dist > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4956                                 if ((En_objp != NULL) && (En_objp->type == OBJ_SHIP)) {
4957                                         if (vm_vec_dist_quick(&guard_objp->pos, &En_objp->pos) > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4958                                                 Assert(aip->previous_mode == AIM_GUARD);
4959                                                 aip->mode = aip->previous_mode;
4960                                                 aip->submode = AIS_GUARD_PATROL;
4961                                                 aip->active_goal = AI_GOAL_NONE;
4962                                                 return 1;
4963                                         }
4964                                 }
4965                         }
4966                 }
4967         }
4968
4969         return 0;
4970
4971 }
4972
4973 //      Call this function if you want something to happen on average every N quarters of a second.
4974 //      The truth value returned by this function will be the same for any given quarter second interval.
4975 //      The value "num" is only passed in to get asynchronous behavior for different objects.
4976 //      modulus == 1 will always return true.
4977 //      modulus == 2 will return true half the time.
4978 //      modulus == 16 will return true for one quarter second interval every four seconds.
4979 int static_rand_timed(int num, int modulus)
4980 {
4981         if (modulus < 2)
4982                 return 1;
4983         else {
4984                 int     t;
4985
4986                 t = Missiontime >> 18;          //      Get time in quarters of a second
4987                 t += num;
4988
4989                 return !(t % modulus);
4990         }
4991 }
4992
4993 //      Maybe fire afterburner based on AI class
4994 int ai_maybe_fire_afterburner(object *objp, ai_info *aip)
4995 {
4996         if (aip->ai_class == 0)
4997                 return 0;               //      Lowest level never aburners away
4998         else  {
4999                 //      Maybe don't afterburner because of a potential collision with the player.
5000                 //      If not multiplayer, near player and player in front, probably don't afterburner.
5001                 if (!(Game_mode & GM_MULTIPLAYER)) {
5002                         if (Ships[objp->instance].team == Player_ship->team) {
5003                                 float   dist;
5004
5005                                 dist = vm_vec_dist_quick(&objp->pos, &Player_obj->pos) - Player_obj->radius - objp->radius;
5006                                 if (dist < 150.0f) {
5007                                         vector  v2p;
5008                                         float           dot;
5009
5010                                         vm_vec_normalized_dir(&v2p, &Player_obj->pos, &objp->pos);
5011                                         dot = vm_vec_dot(&v2p, &objp->orient.v.fvec);
5012
5013                                         if (dot > 0.0f) {
5014                                                 if (dot * dist > 50.0f)
5015                                                         return 0;
5016                                         }
5017                                 }
5018                         }
5019                 }
5020
5021                 if (aip->ai_class >= Num_ai_classes-2)
5022                         return 1;               //      Highest two levels always aburner away.
5023                 else {
5024                         return static_rand_timed(objp-Objects, Num_ai_classes - aip->ai_class);
5025                 }
5026         }
5027 }
5028
5029 //      Maybe engage afterburner after being hit by an object.
5030 void maybe_afterburner_after_ship_hit(object *objp, ai_info *aip, object *en_objp)
5031 {
5032         //      Only do if facing a little away.
5033         if (en_objp != NULL) {
5034                 vector  v2e;
5035
5036                 vm_vec_normalized_dir(&v2e, &en_objp->pos, &objp->pos);
5037                 if (vm_vec_dot(&v2e, &objp->orient.v.fvec) > -0.5f)
5038                         return;
5039         }
5040
5041         if (!( objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5042                 if (ai_maybe_fire_afterburner(objp, aip)) {
5043                         afterburners_start(objp);
5044                         aip->afterburner_stop_time = Missiontime + F1_0/2;
5045                 }
5046         }
5047 }
5048
5049 //      Return true if object *objp is an instructor.
5050 //      Is an instructor if name begins INSTRUCTOR_SHIP_NAME else not.
5051 int is_instructor(object *objp)
5052 {
5053         return !strnicmp(Ships[objp->instance].ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME));
5054 }
5055
5056 //      Evade the weapon aip->danger_weapon_objnum
5057 //      If it's not valid, do a quick out.
5058 //      Evade by accelerating hard.
5059 //      If necessary, turn hard left or hard right.
5060 void evade_weapon()
5061 {
5062         object  *weapon_objp = NULL;
5063         object  *unlocked_weapon_objp = NULL, *locked_weapon_objp = NULL;
5064         vector  weapon_pos, player_pos, goal_point;
5065         vector  vec_from_enemy;
5066         float           dot_from_enemy, dot_to_enemy;
5067         float           dist;
5068         ship            *shipp = &Ships[Pl_objp->instance];
5069         ai_info *aip = &Ai_info[shipp->ai_index];
5070
5071         if (is_instructor(Pl_objp))
5072                 return;
5073
5074         //      Make sure we're actually being attacked.
5075         //      Favor locked objects.
5076         if (aip->nearest_locked_object != -1) {
5077                 if (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)
5078                         locked_weapon_objp = &Objects[aip->nearest_locked_object];
5079         }
5080         
5081         if (aip->danger_weapon_objnum != -1)
5082                 if (Objects[aip->danger_weapon_objnum].signature == aip->danger_weapon_signature)
5083                         unlocked_weapon_objp = &Objects[aip->danger_weapon_objnum];
5084                 else
5085                         aip->danger_weapon_objnum = -1;         //      Signatures don't match, so no longer endangered.
5086
5087         if (locked_weapon_objp != NULL) {
5088                 if (unlocked_weapon_objp != NULL) {
5089                         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))
5090                                 weapon_objp = locked_weapon_objp;
5091                         else
5092                                 weapon_objp = unlocked_weapon_objp;
5093                 } else
5094                         weapon_objp = locked_weapon_objp;
5095         } else if (unlocked_weapon_objp != NULL)
5096                 weapon_objp = unlocked_weapon_objp;
5097         else {
5098                 if (aip->mode == AIM_EVADE_WEAPON)
5099                         maybe_resume_previous_mode(Pl_objp, aip);
5100                 return;
5101         }
5102
5103         Assert(weapon_objp != NULL);
5104
5105         if (weapon_objp->type != OBJ_WEAPON) {
5106                 if (aip->mode == AIM_EVADE_WEAPON)
5107                         maybe_resume_previous_mode(Pl_objp, aip);
5108                 return;
5109         }
5110         
5111         weapon_pos = weapon_objp->pos;
5112         player_pos = Pl_objp->pos;
5113
5114         //      Make speed based on skill level, varying at highest skill level, which is harder to hit.
5115         accelerate_ship(aip, 1.0f);
5116
5117         dist = vm_vec_normalized_dir(&vec_from_enemy, &player_pos, &weapon_pos);
5118
5119         dot_to_enemy = -vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_from_enemy);
5120         dot_from_enemy = vm_vec_dot(&weapon_objp->orient.v.fvec, &vec_from_enemy);
5121         //nprintf(("AI", "dot from enemy = %7.3f\n", dot_from_enemy));
5122
5123         //      If shot is incoming...
5124         if (dot_from_enemy < 0.3f) {
5125                 if (weapon_objp == unlocked_weapon_objp)
5126                         aip->danger_weapon_objnum = -1;
5127                 return;
5128         } else if (dot_from_enemy > 0.7f) {
5129                 if (dist < 200.0f) {
5130                         if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5131                                 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
5132                                         //nprintf(("AI", "Frame %i, turning on afterburner.\n", AI_FrameCount));
5133                                         afterburners_start(Pl_objp);
5134                                         aip->afterburner_stop_time = Missiontime + F1_0/2;
5135                                 }
5136                         }
5137                 }
5138
5139                 //      If we're sort of pointing towards it...
5140                 if ((dot_to_enemy < -0.5f) || (dot_to_enemy > 0.5f)) {
5141                         float   rdot;
5142
5143                         //      Turn hard left or right, depending on which gets out of way quicker.
5144                         rdot = vm_vec_dot(&Pl_objp->orient.v.rvec, &vec_from_enemy);
5145
5146                         if ((rdot < -0.5f) || (rdot > 0.5f))
5147                                 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.v.rvec, -200.0f);
5148                         else
5149                                 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.v.rvec, 200.0f);
5150
5151                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
5152                 }
5153         }
5154
5155 }
5156
5157 //      Use sliding and backwards moving to face enemy.
5158 //      (Coded 2/20/98.  Works fine, but it's hard to see how to integrate it into the AI system.
5159 //       Typically ships are moving so fast that a little sliding isn't enough to gain an advantage.
5160 //       It's currently used to avoid collisions and could be used to evade weapon fire, but the latter
5161 //       would be frustrating, I think.
5162 //       This function is currently not called.)
5163 void slide_face_ship()
5164 {
5165         ship_info       *sip;
5166
5167         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
5168
5169         //      If can't slide, return.
5170         if ((sip->max_vel.xyz.x == 0.0f) && (sip->max_vel.xyz.y == 0.0f))
5171                 return;
5172
5173         vector  goal_pos;
5174         float           dot_from_enemy, dot_to_enemy;
5175         vector  vec_from_enemy, vec_to_goal;
5176         float           dist;
5177         float           up, right;
5178         ai_info         *aip;
5179
5180         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
5181
5182         dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
5183
5184         ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
5185
5186         dot_from_enemy = vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.fvec);
5187         dot_to_enemy = -vm_vec_dot(&vec_from_enemy, &Pl_objp->orient.v.fvec);
5188
5189         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.rvec) > 0.0f)
5190                 right = 1.0f;
5191         else
5192                 right = -1.0f;
5193
5194         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.uvec) > 0.0f)
5195                 up = 1.0f;
5196         else
5197                 up = -1.0f;
5198
5199         vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.rvec, right * 200.0f);
5200         vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.uvec, up * 200.0f);
5201
5202         vm_vec_normalized_dir(&vec_to_goal, &goal_pos, &Pl_objp->pos);
5203
5204         if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.v.rvec) > 0.0f)
5205                 AI_ci.sideways = 1.0f;
5206         else
5207                 AI_ci.sideways = -1.0f;
5208
5209         if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.v.uvec) > 0.0f)
5210                 AI_ci.vertical = 1.0f;
5211         else
5212                 AI_ci.vertical = -1.0f;
5213
5214         if (dist < 200.0f) {
5215                 if (dot_from_enemy < 0.7f)
5216                         accelerate_ship(aip, -1.0f);
5217                 else
5218                         accelerate_ship(aip, dot_from_enemy + 0.5f);
5219         } else {
5220                 if (dot_from_enemy < 0.7f) {
5221                         accelerate_ship(aip, 0.2f);
5222                 } else {
5223                         accelerate_ship(aip, 1.0f);
5224                 }
5225         }
5226 }
5227
5228 //      General code for handling one ship evading another.
5229 //      Problem: This code is also used for avoiding an impending collision.
5230 //      In such a case, it is not good to go to max speed, which is often good
5231 //      for a certain kind of evasion.
5232 void evade_ship()
5233 {
5234         vector  player_pos, enemy_pos, goal_point;
5235         vector  vec_from_enemy;
5236         float           dot_from_enemy;
5237         float           dist;
5238         ship            *shipp = &Ships[Pl_objp->instance];
5239         ship_info       *sip = &Ship_info[shipp->ship_info_index];
5240         ai_info *aip = &Ai_info[shipp->ai_index];
5241         float           bank_override = 0.0f;
5242
5243         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
5244
5245         //      Make speed based on skill level, varying at highest skill level, which is harder to hit.
5246         if (Game_skill_level == NUM_SKILL_LEVELS-1) {
5247                 int     rand_int;
5248                 float   accel_val;
5249
5250                 rand_int = static_rand(Pl_objp-Objects);
5251                 accel_val = (float) (((Missiontime^rand_int) >> 14) & 0x0f)/32.0f + 0.5f;
5252                 accelerate_ship(aip, accel_val);
5253                 //nprintf(("AI", "Accel value = %7.3f\n", accel_val));
5254         } else
5255                 accelerate_ship(aip, (float) (Game_skill_level+2) / (NUM_SKILL_LEVELS+1));
5256
5257         if ((Missiontime - aip->submode_start_time > F1_0/2) && (sip->afterburner_fuel_capacity > 0.0f)) {
5258                 float percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
5259                 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
5260                         afterburners_start(Pl_objp);
5261                         aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
5262                 }
5263         }
5264
5265         vm_vec_sub(&vec_from_enemy, &player_pos, &enemy_pos);
5266
5267         dist = vm_vec_normalize(&vec_from_enemy);
5268         dot_from_enemy = vm_vec_dot(&En_objp->orient.v.fvec, &vec_from_enemy);
5269
5270         if (dist > 250.0f) {
5271                 vector  gp1, gp2;
5272                 //      If far away from enemy, circle, going to nearer of point far off left or right wing
5273                 vm_vec_scale_add(&gp1, &enemy_pos, &En_objp->orient.v.rvec, 250.0f);
5274                 vm_vec_scale_add(&gp2, &enemy_pos, &En_objp->orient.v.rvec, -250.0f);
5275                 if (vm_vec_dist_quick(&gp1, &Pl_objp->pos) < vm_vec_dist_quick(&gp2, &Pl_objp->pos))
5276                         goal_point = gp1;
5277                 else
5278                         goal_point = gp2;
5279         } else if (dot_from_enemy < 0.1f) {
5280                 //      If already close to behind, goal is to get completely behind.
5281                 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.fvec, -1000.0f);
5282         } else if (dot_from_enemy > 0.9f) {
5283                 //      If enemy pointing almost right at self, and self pointing close to enemy, turn away from
5284                 vector  vec_to_enemy;
5285                 float           dot_to_enemy;
5286
5287                 vm_vec_sub(&vec_to_enemy, &enemy_pos, &player_pos);
5288
5289                 vm_vec_normalize(&vec_to_enemy);
5290                 dot_to_enemy = vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_to_enemy);
5291                 if (dot_to_enemy > 0.75f) {
5292                         //      Used to go to En_objp's right vector, but due to banking while turning, that
5293                         //      caused flying in an odd spiral.
5294                         vm_vec_scale_add(&goal_point, &enemy_pos, &Pl_objp->orient.v.rvec, 1000.0f);
5295                         if (dist < 100.0f)
5296                                 bank_override = Pl_objp->phys_info.speed; 
5297                 } else {
5298                         bank_override = Pl_objp->phys_info.speed;                       //      In enemy's sights, not pointing at him, twirl away.
5299                         // nprintf(("Mike", " Do sumpin' else."));
5300                         goto evade_ship_l1;
5301                 }
5302         } else {
5303 evade_ship_l1: ;
5304                 if (aip->ai_evasion > myrand()*100.0f/32767.0f) {
5305                         int     temp;
5306                         float   scale;
5307                         float   psrandval;      //      some value close to zero to choose whether to turn right or left.
5308
5309                         psrandval = (float) (((Missiontime >> 14) & 0x0f) - 8); //      Value between -8 and 7
5310                         psrandval = psrandval/16.0f;                                                    //      Value between -1/2 and 1/2 (approx)
5311
5312                         //      If not close to behind, turn towards his right or left vector, whichever won't cross his path.
5313                         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.rvec) > psrandval) {
5314                                 scale = 1000.0f;
5315                         } else {
5316                                 scale = -1000.0f;
5317                         }
5318
5319                         vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.rvec, scale);
5320
5321                         temp = ((Missiontime >> 16) & 0x07);
5322                         temp = ((temp * (temp+1)) % 16)/2 - 4;
5323                         if ((psrandval == 0) && (temp == 0))
5324                                 temp = 3;
5325
5326                         scale = 200.0f * temp;
5327
5328                         vm_vec_scale_add2(&goal_point, &En_objp->orient.v.uvec, scale);
5329                 } else {
5330                         //      No evasion this frame, but continue with previous turn.
5331                         //      Reason: If you don't, you lose rotational momentum.  Turning every other frame,
5332                         //      and not in between results in a very slow turn because of loss of momentum.
5333                         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))
5334                                 goal_point = aip->prev_goal_point;
5335                         else
5336                                 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.rvec, 100.0f);
5337                 }
5338         }
5339
5340         // nprintf(("Mike", "Goal point = %7.1f %7.1f %7.1f\n", goal_point.xyz.x, goal_point.xyz.y, goal_point.xyz.z));
5341         turn_towards_point(Pl_objp, &goal_point, NULL, bank_override);
5342
5343         aip->prev_goal_point = goal_point;
5344 }
5345
5346 //      --------------------------------------------------------------------------
5347 //      Fly in a manner making it difficult for opponent to attack.
5348 void ai_evade()
5349 {
5350         evade_ship();
5351 }
5352
5353 /*
5354 // -------------------------------------------------------------------
5355 //      Refine predicted enemy position because enemy will move while we move
5356 //      towards predicted enemy position.
5357 //      last_delta_vec is stuffed with size of polishing in last step.  This small amount
5358 //      can be used to perturb the predicted position to make firing not be exact.
5359 //      This function will almost always undershoot actual position, assuming both ships
5360 //      are moving at constant speed.  But with even one polishing step, the error should
5361 //      be under 1%. The number of polishing steps is specified in the parameter num_polish_steps.
5362 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)
5363 {
5364         int     iteration;
5365         vector  player_pos = pobjp->pos;
5366         vector  enemy_pos = *predicted_enemy_pos;
5367         physics_info    *en_physp = &eobjp->phys_info;
5368         float           time_to_enemy;
5369         vector  last_predicted_enemy_pos = *predicted_enemy_pos;
5370         
5371         vm_vec_zero(last_delta_vec);
5372
5373         for (iteration=0; iteration < num_polish_steps; iteration++) {
5374                 dist_to_enemy = vm_vec_dist_quick(predicted_enemy_pos, &player_pos);
5375                 time_to_enemy = compute_time_to_enemy(dist_to_enemy, pobjp, eobjp);
5376                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, time_to_enemy);
5377                 vm_vec_sub(last_delta_vec, predicted_enemy_pos, &last_predicted_enemy_pos);
5378                 last_predicted_enemy_pos= *predicted_enemy_pos;
5379         }
5380 }
5381 */
5382
5383 /*
5384 Relevant variables are:
5385         best_dot_to_enemy               best dot product to enemy in last BEST_DOT_TIME seconds
5386         best_dot_to_time                time at which best dot occurred
5387         best_dot_from_enemy     best dot product for enemy to player in last BEST_DOT_TIME seconds
5388         best_dot_from_time      time at which best dot occurred
5389         submode_start_time      time at which we entered the current submode
5390         previous_submode                previous submode, get it?
5391 Legal submodes are:
5392         CONTINUOUS_TURN vector_id {0..3 = right, -right, up, -up}
5393         ATTACK
5394         EVADE_SQUIGGLE
5395         EVADE_BRAKE
5396 */
5397
5398 float   G_collision_time;
5399 vector  G_predicted_pos, G_fire_pos;
5400
5401 /*
5402 void show_firing_diag()
5403 {
5404         float           dot;
5405         vector  v2t;
5406         vector  pos1, pos2;
5407         float           dist;
5408
5409         if (G_collision_time == 0.0f)
5410                 return;
5411
5412         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",
5413                 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));
5414         vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5415         dot = vm_vec_dot(&v2t, &Pl_objp->orient.v.fvec);
5416         mprintf(("Dot of v.fvec and vector to predicted position = %10.7f (%7.3f degrees)\n", dot, acos(dot)*180.0f/3.141592654f));
5417
5418         vm_vec_scale_add(&pos1, &En_objp->pos, &En_objp->phys_info.vel, G_collision_time);
5419         vm_vec_scale_add(&pos2, &G_fire_pos, &Pl_objp->orient.v.fvec, G_collision_time*300.0f);
5420         dist = vm_vec_dist(&pos1, &pos2);
5421
5422         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));
5423 }
5424 */
5425
5426 //      If:
5427 //              flags & WIF_PUNCTURE
5428 //      Then Select a Puncture weapon.
5429 //      Else
5430 //              Select Any ol' weapon.
5431 //      Returns primary_bank index.
5432 int ai_select_primary_weapon(object *objp, object *other_objp, int flags)
5433 {
5434         ship    *shipp = &Ships[objp->instance];
5435         ship_weapon *swp = &shipp->weapons;
5436         ship_info *sip;
5437
5438         //Assert( other_objp != NULL );
5439         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5440
5441         sip = &Ship_info[shipp->ship_info_index];
5442
5443         if (flags & WIF_PUNCTURE) {
5444                 if (swp->current_primary_bank >= 0) {
5445                         int     bank_index;
5446
5447                         bank_index = swp->current_primary_bank;
5448
5449                         if (Weapon_info[swp->primary_bank_weapons[bank_index]].wi_flags & WIF_PUNCTURE) {
5450                                 //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[bank_index]].name));
5451                                 return swp->current_primary_bank;
5452                         }
5453                 }
5454                 for (int i=0; i<swp->num_primary_banks; i++) {
5455                         int     weapon_info_index;
5456
5457                         weapon_info_index = swp->primary_bank_weapons[i];
5458
5459                         if (weapon_info_index > -1){
5460                                 if (Weapon_info[weapon_info_index].wi_flags & WIF_PUNCTURE) {
5461                                         swp->current_primary_bank = i;
5462                                         //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5463                                         return i;
5464                                 }
5465                         }
5466                 }
5467                 
5468                 // AL 26-3-98: If we couldn't find a puncture weapon, pick first available weapon if one isn't active
5469                 if ( swp->current_primary_bank < 0 ) {
5470                         if ( swp->num_primary_banks > 0 ) {
5471                                 swp->current_primary_bank = 0;
5472                         }
5473                 }
5474
5475         } else {                //      Don't need to be using a puncture weapon.
5476                 if (swp->current_primary_bank >= 0) {
5477                         if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)){
5478                                 return swp->current_primary_bank;
5479                         }
5480                 }
5481                 for (int i=0; i<swp->num_primary_banks; i++) {
5482                         if (swp->primary_bank_weapons[i] > -1) {
5483                                 if (!(Weapon_info[swp->primary_bank_weapons[i]].wi_flags & WIF_PUNCTURE)) {
5484                                         swp->current_primary_bank = i;
5485                                         nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5486                                         return i;
5487                                 }
5488                         }
5489                 }
5490                 //      Wasn't able to find a non-puncture weapon.  Stick with what we have.
5491         }
5492
5493         Assert( swp->current_primary_bank != -1 );              // get Alan or Allender
5494
5495         return swp->current_primary_bank;
5496 }
5497
5498 //      --------------------------------------------------------------------------
5499 //      Maybe link primary weapons.
5500 void set_primary_weapon_linkage(object *objp)
5501 {
5502         ship            *shipp;
5503         ai_info *aip;
5504
5505         shipp = &Ships[objp->instance];
5506         aip     = &Ai_info[shipp->ai_index];
5507
5508         shipp->flags &= ~SF_PRIMARY_LINKED;
5509
5510         if (Num_weapons > (int) (MAX_WEAPONS * 0.75f)) {
5511                 if (shipp->flags & SF_PRIMARY_LINKED)
5512                         nprintf(("AI", "Frame %i, ship %s: Unlinking primaries.\n", Framecount, shipp->ship_name));
5513                 shipp->flags &= ~SF_PRIMARY_LINKED;
5514                 return;         //      If low on slots, don't link.
5515         }
5516
5517         shipp->flags &= ~SF_PRIMARY_LINKED;
5518
5519         // AL: ensure target is a ship!
5520         if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
5521                 // If trying to destroy a big ship (i.e., not disable/disarm), always unleash all weapons
5522                 if ( ship_get_SIF(&Ships[Objects[aip->target_objnum].instance]) & SIF_BIG_SHIP) {
5523                         if ( aip->targeted_subsys == NULL ) {
5524                                 shipp->flags |= SF_PRIMARY_LINKED;
5525                                 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5526                                 return;
5527                         }
5528                 }
5529         }
5530
5531         // AL 2-11-98: If ship has a disarm or disable goal, don't link unless both weapons are
5532         //                                      puncture weapons
5533         if ( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) ) {
5534                 if ( aip->goals[aip->active_goal].ai_mode & (AI_GOAL_DISABLE_SHIP|AI_GOAL_DISARM_SHIP) ) {
5535                         ship_weapon     *swp;
5536                         swp = &shipp->weapons;
5537                         // only continue if both primaries are puncture weapons
5538                         if ( swp->num_primary_banks == 2 ) {
5539                                 if ( !(Weapon_info[swp->primary_bank_weapons[0]].wi_flags & WIF_PUNCTURE) ) 
5540                                         return;
5541                                 if ( !(Weapon_info[swp->primary_bank_weapons[1]].wi_flags & WIF_PUNCTURE) ) 
5542                                         return;
5543                         }
5544                 }
5545         }
5546
5547         //      Don't want all ships always linking weapons at start, so asynchronize.
5548         if (Missiontime < i2f(30))
5549                 return;
5550         else if (Missiontime < i2f(120)) {
5551                 int r = static_rand((Missiontime >> 17) ^ OBJ_INDEX(objp));
5552                 if ( (r&3) != 0)
5553                         return;
5554         }
5555
5556         if (shipp->weapon_energy > Link_energy_levels_always[Game_skill_level]) {
5557                 shipp->flags |= SF_PRIMARY_LINKED;
5558         } else if (shipp->weapon_energy > Link_energy_levels_maybe[Game_skill_level]) {
5559                 if (objp->hull_strength < Ship_info[shipp->ship_info_index].initial_hull_strength/3.0f)
5560                         shipp->flags |= SF_PRIMARY_LINKED;
5561         }
5562 }
5563
5564 //      --------------------------------------------------------------------------
5565 //      Fire the current primary weapon.
5566 //      *objp is the object to fire from.
5567 void ai_fire_primary_weapon(object *objp)
5568 {
5569         ship                    *shipp = &Ships[objp->instance];
5570         ship_weapon     *swp = &shipp->weapons;
5571         ship_info       *sip;
5572         ai_info         *aip;
5573         object          *enemy_objp;
5574
5575         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5576         sip = &Ship_info[shipp->ship_info_index];
5577
5578         aip = &Ai_info[shipp->ai_index];
5579
5580         //      If low on slots, fire a little less often.
5581         if (Num_weapons > (int) (0.9f * MAX_WEAPONS)) {
5582                 if (frand() > 0.5f) {
5583                         nprintf(("AI", "Frame %i, %s not fire.\n", Framecount, shipp->ship_name));
5584                         return;
5585                 }
5586         }
5587
5588         if (!Ai_firing_enabled){
5589                 return;
5590         }
5591
5592         if (aip->target_objnum != -1){
5593                 enemy_objp = &Objects[aip->target_objnum];
5594         } else {
5595                 enemy_objp = NULL;
5596         }
5597
5598         if ( (swp->current_primary_bank < 0) || (swp->current_primary_bank >= swp->num_primary_banks) || timestamp_elapsed(aip->primary_select_timestamp)) {
5599                 int     flags = 0;
5600                 // AL 2-11-98: If attacking any subsystem (not just engines), use disrupter weapon
5601 //              if ((aip->targeted_subsys != NULL) && (aip->targeted_subsys->system_info->type == SUBSYSTEM_ENGINE)) {
5602                 if ( aip->targeted_subsys != NULL ) {
5603                         flags = WIF_PUNCTURE;
5604                 }
5605                 ai_select_primary_weapon(objp, enemy_objp, flags);
5606                 ship_primary_changed(shipp);    // AL: maybe send multiplayer information when AI ship changes primaries
5607                 aip->primary_select_timestamp = timestamp(5 * 1000);    //      Maybe change primary weapon five seconds from now.
5608         }
5609
5610         //      If pointing nearly at predicted collision point of target, bash orientation to be perfectly pointing.
5611         float   dot;
5612         vector  v2t;
5613
5614 //      if (!IS_VEC_NULL(&G_predicted_pos)) {
5615         if (!( vm_vec_mag_quick(&G_predicted_pos) < AICODE_SMALL_MAGNITUDE )) {
5616                 if ( !vm_vec_cmp(&G_predicted_pos, &G_fire_pos) ) {
5617                         nprintf(("Warning", "Avoid NULL vector assert.. why are G_predicted_pos and G_fire_pos the same?\n"));
5618                 } else {
5619                         vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5620                         dot = vm_vec_dot(&v2t, &objp->orient.v.fvec);
5621                         if (dot > .998629534f){ //      if within 3.0 degrees of desired heading, bash
5622                                 vm_vector_2_matrix(&objp->orient, &v2t, &objp->orient.v.uvec, NULL);
5623                         }
5624                 }
5625         }
5626
5627         //      Make sure not firing at a protected ship unless firing at a live subsystem.
5628         //      Note: This happens every time the ship tries to fire, perhaps every frame.
5629         //      Should be wrapped in a timestamp, same one that enables it to fire, but that is complicated
5630         //      by multiple banks it can fire from.
5631         if (aip->target_objnum != -1) {
5632                 object  *tobjp = &Objects[aip->target_objnum];
5633                 if (tobjp->flags & OF_PROTECTED) {
5634                         if (aip->targeted_subsys != NULL) {
5635                                 int     type;
5636
5637                                 type = aip->targeted_subsys->system_info->type;
5638                                 if (ship_get_subsystem_strength(&Ships[tobjp->instance], type) == 0.0f) {
5639                                         aip->target_objnum = -1;
5640                                         return;
5641                                 }
5642                         } else {
5643                                 aip->target_objnum = -1;
5644                                 return;
5645                         }
5646                 }
5647         }
5648
5649         //      If enemy is protected, not firing a puncture weapon and enemy's hull is low, don't fire.
5650         if ((enemy_objp != NULL) && (enemy_objp->flags & OF_PROTECTED)) {
5651                 // AL: 3-6-98: Check if current_primary_bank is valid
5652                 if ((enemy_objp->hull_strength < 750.0f) && 
5653                         ((aip->targeted_subsys == NULL) || (enemy_objp->hull_strength < aip->targeted_subsys->current_hits + 50.0f)) &&
5654                         (swp->current_primary_bank >= 0) ) {
5655                         if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)) {
5656                                 //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));
5657                                 swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(1000);
5658                                 return;
5659                         }
5660
5661                         /*
5662                         int     num_attacking;
5663                         num_attacking = num_enemies_attacking(enemy_objp-Objects);
5664                         if (enemy_objp->hull_strength / num_attacking < 200.0f) {
5665                                 if (frand() < 0.75f) {
5666                                         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));
5667                                         swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(500);
5668                                         return;
5669                                 }
5670                         }
5671                         */
5672                 }
5673         }
5674
5675         set_primary_weapon_linkage(objp);
5676         
5677         // I think this will properly solve the problem
5678         // fire non-streaming weapons
5679         ship_fire_primary(objp, 0);
5680         
5681         // fire streaming weapons
5682         shipp->flags |= SF_TRIGGER_DOWN;
5683         ship_fire_primary(objp, 1);
5684         shipp->flags &= ~SF_TRIGGER_DOWN;
5685 }
5686
5687 //      --------------------------------------------------------------------------
5688 //      Return number of nearby enemy fighters.
5689 //      threshold is the distance within which a ship is considered near.
5690 //
5691 // input:       enemy_team_mask =>      teams that are considered as an enemy
5692 //                              pos                                     =>      world position to measure ship distances from
5693 //                              threshold                       =>      max distance from pos to be considered "near"
5694 //
5695 // exit:                number of ships within threshold units of pos
5696 int num_nearby_fighters(int enemy_team_mask, vector *pos, float threshold)
5697 {
5698         ship_obj        *so;
5699         object  *ship_objp;
5700         int             count = 0;
5701
5702         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5703
5704                 ship_objp = &Objects[so->objnum];
5705
5706                 if (Ships[ship_objp->instance].team & enemy_team_mask) {
5707                         if (Ship_info[Ships[ship_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) {
5708                                 if (vm_vec_dist_quick(pos, &ship_objp->pos) < threshold)
5709                                         count++;
5710                         }
5711                 }
5712         }
5713
5714         return count;
5715 }
5716
5717 //      --------------------------------------------------------------------------
5718 //      Select secondary weapon to fire.
5719 //      Currently, 1/16/98:
5720 //              If 0 secondary weapons available, return -1
5721 //              If 1 available, use it.
5722 //              If 2 or more, if the current weapon is one of them, stick with it, otherwise choose a random one.
5723 //      priority1 and priority2 are Weapon_info[] bitmasks such as WIF_HOMING_ASPECT.  If any weapon has any bit in priority1
5724 //      set, that weapon will be selected.  If not, apply to priority2.  If neither, return -1, meaning no weapon selected.
5725 //      Note, priorityX have default values of -1, meaning if not set, they will match any weapon.
5726 //      Return value:
5727 //              bank index
5728 //      Should do this:
5729 //              Favor aspect seekers when attacking small ships faraway.
5730 //              Favor rapid fire dumbfire when attacking a large ship.
5731 //              Ignore heat seekers because we're not sure how they'll work.
5732 void ai_select_secondary_weapon(object *objp, ship_weapon *swp, int priority1 = -1, int priority2 = -1)
5733 {
5734         int     num_weapon_types;
5735         int     weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
5736         int     i;
5737         int     ignore_mask;
5738         int     initial_bank;
5739
5740         initial_bank = swp->current_secondary_bank;
5741
5742         //      Ignore bombs unless one of the priorities asks for them to be selected.
5743         if (WIF_HUGE & (priority1 | priority2))
5744                 ignore_mask = 0;
5745         else
5746                 ignore_mask = WIF_HUGE;
5747
5748         if (!(WIF_BOMBER_PLUS & (priority1 | priority2)))
5749                 ignore_mask |= WIF_BOMBER_PLUS;
5750
5751 #ifndef NDEBUG
5752         for (i=0; i<MAX_WEAPON_TYPES; i++) {
5753                 weapon_id_list[i] = -1;
5754                 weapon_bank_list[i] = -1;
5755         }
5756 #endif
5757
5758         //      Stuff weapon_bank_list with bank index of available weapons.
5759         num_weapon_types = get_available_secondary_weapons(objp, weapon_id_list, weapon_bank_list);
5760
5761         int     priority2_index = -1;
5762
5763         for (i=0; i<num_weapon_types; i++) {
5764                 int     wi_flags;
5765
5766                 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5767                 if (!(wi_flags & ignore_mask)) {                                        //      Maybe bombs are illegal.
5768                         if (wi_flags & priority1) {
5769                                 swp->current_secondary_bank = weapon_bank_list[i];                              //      Found first priority, return it.
5770                                 break;
5771                         } else if (wi_flags & priority2)
5772                                 priority2_index = weapon_bank_list[i];  //      Found second priority, but might still find first priority.
5773                 }
5774         }
5775
5776         //      If didn't find anything above, then pick any secondary weapon.
5777         if (i == num_weapon_types) {
5778                 swp->current_secondary_bank = priority2_index;  //      Assume we won't find anything.
5779                 if (priority2_index == -1) {
5780                         for (i=0; i<num_weapon_types; i++) {
5781                                 int     wi_flags;
5782
5783                                 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5784                                 if (!(wi_flags & ignore_mask)) {                                        //      Maybe bombs are illegal.
5785                                         if (swp->secondary_bank_ammo[i] > 0) {
5786                                                 swp->current_secondary_bank = i;
5787                                                 break;
5788                                         }
5789                                 }
5790                         }
5791                 }
5792         }
5793
5794         //      If switched banks, force reacquisition of aspect lock.
5795         if (swp->current_secondary_bank != initial_bank) {
5796                 ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
5797                 
5798                 aip->aspect_locked_time = 0.0f;
5799                 aip->current_target_is_locked = 0;
5800         }
5801
5802
5803         ship_secondary_changed(&Ships[objp->instance]); // AL: let multiplayer know if secondary bank has changed
5804         // nprintf(("AI", "Ship %s selected weapon %s\n", Ships[objp->instance].ship_name, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
5805 }
5806
5807 //      Return number of objects homing on object *target_objp
5808 int compute_num_homing_objects(object *target_objp)
5809 {
5810         object  *objp;
5811         int             count = 0;
5812
5813         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
5814                 if (objp->type == OBJ_WEAPON) {
5815                         if (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_HOMING) {
5816                                 if (Weapons[objp->instance].homing_object == target_objp) {
5817                                         count++;
5818                                 }
5819                         }
5820                 }
5821         }
5822
5823         return count;
5824 }
5825
5826 //      Object *firing_objp just fired weapon weapon_index (index in Weapon_info).
5827 //      If it's a shockwave weapon, tell your team about it!
5828 void ai_maybe_announce_shockwave_weapon(object *firing_objp, int weapon_index)
5829 {
5830         if ((firing_objp->type == OBJ_SHIP) && (Weapon_info[weapon_index].shockwave_speed > 0.0f)) {
5831                 ship_obj        *so;
5832                 int             firing_ship_team;
5833
5834                 firing_ship_team = Ships[firing_objp->instance].team;
5835
5836                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5837                         object  *A = &Objects[so->objnum];
5838                         Assert(A->type == OBJ_SHIP);
5839
5840                         if (Ships[A->instance].team == firing_ship_team) {
5841                                 ai_info *aip = &Ai_info[Ships[A->instance].ai_index];
5842                                 // AL 1-5-98: only avoid shockwave if not docked or repairing
5843                                 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
5844                                         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_WEAPON;
5845                                 }
5846                         }
5847                 }
5848         }
5849 }
5850
5851 //      Return total payload of all incoming missiles.
5852 float compute_incoming_payload(object *target_objp)
5853 {
5854         missile_obj     *mo;
5855         float                   payload = 0.0f;
5856
5857         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
5858                 object  *objp;
5859
5860                 objp = &Objects[mo->objnum];
5861                 Assert(objp->type == OBJ_WEAPON);
5862                 if (Weapons[objp->instance].homing_object == target_objp) {
5863                         payload += Weapon_info[Weapons[objp->instance].weapon_info_index].damage;
5864                 }
5865         }
5866
5867         return payload;
5868 }
5869
5870 //      --------------------------------------------------------------------------
5871 //      Return true if OK for *aip to fire its current weapon at its current target.
5872 //      Only reason this function returns false is:
5873 //              weapon is a homer
5874 //              targeted at player
5875 //                      OR:     player has too many homers targeted at him
5876 //                                      Missiontime in that dead zone in which can't fire at this player
5877 //      Note: If player is attacking a ship, that ship is allowed to fire at player.  Otherwise, we get in a situation in which
5878 //      player is attacking a large ship, but that large ship is not defending itself with missiles.
5879 int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip)
5880 {
5881         int     num_homers = 0;
5882         object  *tobjp = &Objects[target_objnum];
5883
5884         if (target_objnum > -1) {
5885                 // AL 3-4-98: Ensure objp target is a ship first 
5886                 if ( tobjp->type == OBJ_SHIP ) {
5887
5888                         // should not get this far. check if ship is protected from beam and weapon is type beam
5889                         if ( (wip->wi_flags & WIF_BEAM) && (tobjp->flags & OF_BEAM_PROTECTED) ) {
5890                                 Int3();
5891                                 return 0;
5892                         }
5893                         if (Ship_info[Ships[tobjp->instance].ship_info_index].flags & SIF_SMALL_SHIP) {
5894                                 num_homers = compute_num_homing_objects(&Objects[target_objnum]);
5895                         }
5896                 }
5897
5898                 //      If player, maybe fire based on Skill_level and number of incoming weapons.
5899                 //      If non-player, maybe fire based on payload of incoming weapons.
5900                 if (wip->wi_flags & WIF_HOMING) {
5901                         if ((target_objnum > -1) && (tobjp->flags & OF_PLAYER_SHIP)) {
5902                                 if (Ai_info[Ships[tobjp->instance].ai_index].target_objnum != objnum) {
5903                                         //      Don't allow AI ships to fire at player for fixed periods of time based on skill level.
5904                                         //      With 5 skill levels, at Very Easy, they fire in 1/7 of every 10 second interval.
5905                                         //      At Easy, 2/7...at Expert, 5/7
5906                                         int t = ((Missiontime /(65536*10)) ^ target_objnum ^ 0x01) % (NUM_SKILL_LEVELS+2);
5907                                         if (t > Game_skill_level) {
5908                                                 //nprintf(("AI", "Not OK to fire homer at time thing %i\n", t));
5909                                                 return 0;
5910                                         }
5911                                 }
5912                                 //nprintf(("AI", " IS OK to fire homer at time thing %i ***\n", t));
5913                                 int     swarmers = 0;
5914                                 if (wip->wi_flags & WIF_SWARM)
5915                                         swarmers = 2;   //      Note, always want to be able to fire swarmers if no currently incident homers.
5916                                 if (Max_allowed_player_homers[Game_skill_level] < num_homers + swarmers) {
5917                                         return 0;
5918                                 }
5919                         } else if (num_homers > 3) {
5920                                 float   incoming_payload;
5921
5922                                 incoming_payload = compute_incoming_payload(&Objects[target_objnum]);
5923
5924                                 if (incoming_payload > tobjp->hull_strength) {
5925                                         return 0;
5926                                 }
5927                         }
5928                 }
5929         }
5930
5931         return 1;
5932 }
5933
5934 //      --------------------------------------------------------------------------
5935 //      Fire a secondary weapon.
5936 //      Maybe choose to fire a different one.
5937 //      priority1 and priority2 are optional parameters with defaults = -1
5938 int ai_fire_secondary_weapon(object *objp, int priority1, int priority2)
5939 {
5940         ship_weapon *swp;
5941         ship    *shipp;
5942         ship_info *sip;
5943         int             current_bank;
5944         int             rval = 0;
5945
5946 #ifndef NDEBUG
5947         if (!Ai_firing_enabled)
5948                 return rval;
5949 #endif
5950
5951         Assert( objp != NULL );
5952         Assert(objp->type == OBJ_SHIP);
5953         shipp = &Ships[objp->instance];
5954         swp = &shipp->weapons;
5955
5956         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5957         sip = &Ship_info[shipp->ship_info_index];
5958
5959         //      Select secondary weapon.
5960         current_bank = swp->current_secondary_bank; //ai_select_secondary_weapon(objp, swp, priority1, priority2);
5961
5962         //nprintf(("AI", "Frame %i: Current bank = %i, ammo remaining = %i\n", Framecount, current_bank, swp->secondary_bank_ammo[current_bank]));
5963         if (current_bank == -1) {
5964                 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5965                 return rval;
5966         }
5967
5968         Assert(current_bank < shipp->weapons.num_secondary_banks);
5969
5970         weapon_info     *wip = &Weapon_info[shipp->weapons.secondary_bank_weapons[current_bank]];
5971
5972         if ((wip->wi_flags & WIF_HOMING_ASPECT) && (!Ai_info[shipp->ai_index].current_target_is_locked)) {
5973                 //nprintf(("AI", "Not firing secondary weapon because not aspect locked.\n"));
5974                 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
5975         } else if ((wip->wi_flags & WIF_BOMB) || (vm_vec_dist_quick(&objp->pos, &En_objp->pos) > 50.0f)) {
5976                 //      This might look dumb, firing a bomb even if closer than 50 meters, but the reason is, if you're carrying
5977                 //      bombs, delivering them is probably more important than surviving.
5978                 ai_info *aip;
5979
5980                 aip = &Ai_info[shipp->ai_index];
5981                 
5982                 //      Note, maybe don't fire if firing at player and any homers yet fired.
5983                 //      Decreasing chance to fire the more homers are incoming on player.
5984                 if (check_ok_to_fire(OBJ_INDEX(objp), aip->target_objnum, wip)) {
5985                         if (ship_fire_secondary(objp)) {
5986                                 rval = 1;
5987                                 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5988                                 //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));
5989                         }
5990
5991                 } else {
5992                         swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5993                 }
5994         }
5995
5996         return rval;
5997 }
5998
5999 //      Return true if it looks like obj1, if continuing to move along current vector, will
6000 //      collide with obj2.
6001 int might_collide_with_ship(object *obj1, object *obj2, float dot_to_enemy, float dist_to_enemy, float duration)
6002 {
6003         if (obj1->phys_info.speed * duration + 2*(obj1->radius + obj2->radius) > dist_to_enemy)
6004                 if (dot_to_enemy > 0.8f - 2*(obj1->radius + obj2->radius)/dist_to_enemy)
6005                         return objects_will_collide(obj1, obj2, duration, 2.0f);
6006
6007 //              BABY - 
6008 //              CONDITION 1, dist_to_enemy < o1_rad + o2_rad + (obj1.speed + obj2.speed) * time + 50
6009         
6010         return 0;
6011
6012 }
6013
6014 //      --------------------------------------------------------------------------
6015 //      Return true if ship *objp firing a laser believes it will hit a teammate.
6016 int might_hit_teammate(object *firing_objp)
6017 {
6018         int             team;
6019         object  *objp;
6020         ship_obj        *so;
6021
6022         team = Ships[firing_objp->instance].team;
6023
6024         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6025                 objp = &Objects[so->objnum];
6026                 if (Ships[objp->instance].team == team) {
6027                         float           dist, dot;
6028                         vector  vec_to_objp;
6029
6030                         vm_vec_sub(&vec_to_objp, &firing_objp->pos, &objp->pos);
6031                         dist = vm_vec_mag_quick(&vec_to_objp);
6032                         dot = vm_vec_dot(&firing_objp->orient.v.fvec, &vec_to_objp)/dist;
6033                         if (might_collide_with_ship(firing_objp, objp, dot, dist, 2.0f))
6034                                 return 1;
6035                 }
6036         }
6037
6038         return 0;
6039
6040 }
6041
6042 //int   Team_not_fire_count=0, Team_hit_count = 0;
6043
6044 void render_all_ship_bay_paths(object *objp)
6045 {
6046         int             i,j,color;
6047         ship            *sp = &Ships[objp->instance];
6048         polymodel       *pm;
6049         model_path      *mp;
6050
6051         pm = model_get(sp->modelnum);
6052         vector  global_path_point;
6053         vertex  v, prev_vertex;
6054
6055         if ( pm->ship_bay == NULL )
6056                 return;
6057
6058         for ( i = 0; i < pm->ship_bay->num_paths; i++ ) {
6059                 mp = &pm->paths[pm->ship_bay->paths[i]];
6060
6061                 for ( j = 0; j < mp->nverts; j++ ) {
6062                         vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6063                         vm_vec_add2(&global_path_point, &objp->pos);
6064                         g3_rotate_vertex(&v, &global_path_point);
6065                         color = 255 - j*50;
6066                         if ( color < 50 ) 
6067                                 color = 100;
6068                         gr_set_color(0, color, 0);
6069
6070                         if ( j == mp->nverts-1 ) {
6071                                 gr_set_color(255, 0, 0);
6072                         }
6073
6074                         g3_draw_sphere( &v, 1.5f);
6075
6076                         if ( j > 0 )
6077                                 g3_draw_line(&v, &prev_vertex);
6078
6079                         prev_vertex = v;
6080         
6081                 }
6082         }
6083 }
6084
6085 // debug function to show all path points associated with an object
6086 void render_all_subsys_paths(object *objp)
6087 {
6088         int             i,j,color;
6089         ship            *sp = &Ships[objp->instance];
6090         polymodel       *pm;
6091         model_path      *mp;
6092
6093         pm = model_get(sp->modelnum);
6094         vector  global_path_point;
6095         vertex  v, prev_vertex;
6096
6097         if ( pm->ship_bay == NULL )
6098                 return;
6099
6100         for ( i = 0; i < pm->n_paths; i++ ) {
6101                 mp = &pm->paths[i];
6102                 for ( j = 0; j < mp->nverts; j++ ) {
6103                         vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6104                         vm_vec_add2(&global_path_point, &objp->pos);
6105                         g3_rotate_vertex(&v, &global_path_point);
6106                         color = 255 - j*50;
6107                         if ( color < 50 ) 
6108                                 color = 100;
6109                         gr_set_color(0, color, 0);
6110
6111                         if ( j == mp->nverts-1 ) {
6112                                 gr_set_color(255, 0, 0);
6113                         }
6114
6115                         g3_draw_sphere( &v, 1.5f);
6116
6117                         if ( j > 0 )
6118                                 g3_draw_line(&v, &prev_vertex);
6119
6120                         prev_vertex = v;
6121                 }
6122         }
6123 }
6124
6125 void render_path_points(object *objp)
6126 {
6127         ship            *shipp = &Ships[objp->instance];
6128         ai_info *aip = &Ai_info[shipp->ai_index];
6129         object  *dobjp;
6130         polymodel       *pm;
6131
6132         render_all_subsys_paths(objp);
6133         render_all_ship_bay_paths(objp);
6134
6135         if (aip->goal_objnum < 0)
6136                 return;
6137
6138         dobjp = &Objects[aip->goal_objnum];
6139         pm = model_get(Ships[dobjp->instance].modelnum);
6140         vector  dock_point, global_dock_point;
6141         vertex  v;
6142
6143         ship_model_start(&Objects[aip->goal_objnum]);
6144         if (pm->n_docks) {
6145                 dock_point = pm->docking_bays[0].pnt[0];
6146                 model_find_world_point(&global_dock_point, &dock_point, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6147                 g3_rotate_vertex(&v, &global_dock_point);
6148                 gr_set_color(255, 255, 255);
6149                 g3_draw_sphere( &v, 1.5f);
6150         }
6151
6152         if (aip->path_start != -1) {
6153                 vertex          prev_vertex;
6154                 pnode                   *pp = &Path_points[aip->path_start];
6155                 int                     num_points = aip->path_length;
6156                 int                     i;
6157
6158                 for (i=0; i<num_points; i++) {
6159                         vertex  v0;
6160
6161                         g3_rotate_vertex( &v0, &pp->pos );
6162
6163                         gr_set_color(0, 128, 96);
6164                         if (i != 0)
6165                                 g3_draw_line(&v0, &prev_vertex);
6166
6167                         if (pp-Path_points == aip->path_cur)
6168                                 gr_set_color(255,255,0);
6169                         
6170                         g3_draw_sphere( &v0, 4.5f);
6171
6172                         //      Connect all the turrets that can fire upon this point to this point.
6173 /*                      if (0) { //pp->path_index != -1) {
6174                                 model_path      *pmp;
6175                                 mp_vert         *pmpv;
6176
6177                                 get_base_path_info(pp->path_index, aip->goal_objnum, &pmp, &pmpv);
6178
6179                                 if (pmpv->nturrets) {
6180                                         for (int j = 0; j<pmpv->nturrets; j++) {
6181                                                 vertex  v1;
6182                                                 vector  turret_pos;
6183                                                 ship_subsys     *ssp;
6184
6185                                                 ssp = ship_get_indexed_subsys(&Ships[Objects[aip->goal_objnum].instance], pmpv->turret_ids[j]);
6186
6187 model_find_world_point(&turret_pos, &ssp->system_info->pnt, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6188         
6189                                                 g3_rotate_vertex(&v1, &turret_pos);
6190                                                 gr_set_color(255, 255, 0);
6191                                                 g3_draw_line(&v0, &v1);
6192                                                 g3_draw_sphere( &v1, 1.5f);
6193                                         }
6194                                 }
6195                         } */
6196
6197                         prev_vertex = v0;
6198
6199                         pp++;
6200                 }
6201         }
6202
6203         ship_model_stop(&Objects[aip->goal_objnum]);
6204 }
6205
6206 // Return the distance that the current AI weapon will travel
6207 float ai_get_weapon_dist(ship_weapon *swp)
6208 {
6209         int     bank_num, weapon_num;
6210
6211         bank_num = swp->current_primary_bank;
6212         weapon_num = swp->primary_bank_weapons[bank_num];
6213
6214         //      If weapon_num is illegal, return a reasonable value.  A valid weapon
6215         //      will get selected when this ship tries to fire.
6216         if (weapon_num == -1) {
6217                 // Int3();
6218                 return 1000.0f;
6219         }
6220
6221         return Weapon_info[weapon_num].max_speed * Weapon_info[weapon_num].lifetime;
6222 }
6223
6224 float ai_get_weapon_speed(ship_weapon *swp)
6225 {
6226         int     bank_num, weapon_num;
6227
6228         bank_num = swp->current_primary_bank;
6229         if (bank_num < 0)
6230                 return 100.0f;
6231
6232         weapon_num = swp->primary_bank_weapons[bank_num];
6233
6234         if (weapon_num == -1) {
6235                 //Int3();
6236                 return 100.0f;
6237         }
6238
6239         return Weapon_info[weapon_num].max_speed;
6240 }
6241
6242 //      Compute the predicted position of a ship to be fired upon from a turret.
6243 //      This is based on position of firing gun, enemy object, weapon speed and skill level constraints.
6244 //      Return value in *predicted_enemy_pos.
6245 //      Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6246 //      *pobjp          object firing the weapon
6247 //      *eobjp          object being fired upon
6248 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)
6249 {
6250         ship    *shipp = &Ships[pobjp->instance];
6251         float   range_time;
6252
6253         //weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6254
6255         if (weapon_speed < 1.0f)
6256                 weapon_speed = 1.0f;
6257
6258         range_time = 2.0f;
6259
6260         //      Make it take longer for enemies to get player's allies in range based on skill level.
6261         if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team)
6262                 range_time += In_range_time[Game_skill_level];
6263
6264         //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6265
6266         if (time_enemy_in_range < range_time) {
6267                 float   dist;
6268
6269                 dist = vm_vec_dist_quick(&pobjp->pos, enemy_pos);
6270                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, time_enemy_in_range * dist/weapon_speed);
6271         } else {
6272                 float   collision_time, scale;
6273                 vector  rand_vec;
6274                 ai_info *aip = &Ai_info[shipp->ai_index];
6275
6276                 collision_time = compute_collision_time(enemy_pos, enemy_vel, gun_pos, weapon_speed);
6277
6278                 if (collision_time == 0.0f){
6279                         collision_time = 100.0f;
6280                 }
6281
6282                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, collision_time);
6283                 if (time_enemy_in_range > 2*range_time){
6284                         scale = (1.0f - aip->ai_accuracy) * 4.0f;
6285                 } else {
6286                         scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - time_enemy_in_range/(2*range_time)));
6287                 }               
6288
6289                 static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6290
6291                 vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6292                 G_collision_time = collision_time;
6293                 G_fire_pos = *gun_pos;
6294         }
6295
6296         G_predicted_pos = *predicted_enemy_pos;
6297 }
6298
6299 //      Compute the predicted position of a ship to be fired upon.
6300 //      This is based on current position of firing object, enemy object, relative position of gun on firing object,
6301 //      weapon speed and skill level constraints.
6302 //      Return value in *predicted_enemy_pos.
6303 //      Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6304 void set_predicted_enemy_pos(vector *predicted_enemy_pos, object *pobjp, object *eobjp, ai_info *aip)
6305 {
6306         float   weapon_speed, range_time;
6307         ship    *shipp = &Ships[pobjp->instance];
6308
6309         weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6310         weapon_speed = max(weapon_speed, 1.0f);         // set not less than 1
6311
6312         range_time = 2.0f;
6313
6314         //      Make it take longer for enemies to get player's allies in range based on skill level.
6315         // but don't bias team v. team missions
6316         if ( !((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) ) {
6317                 if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team) {
6318                         range_time += In_range_time[Game_skill_level];
6319                 }
6320         }
6321         //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6322
6323         if (aip->time_enemy_in_range < range_time) {
6324                 float   dist;
6325
6326                 dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6327                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, aip->time_enemy_in_range * dist/weapon_speed);
6328         } else {
6329                 float   collision_time;
6330                 vector  gun_pos, pnt;
6331                 polymodel *po = model_get( Ship_info[shipp->ship_info_index].modelnum );
6332
6333                 //      Compute position of gun in absolute space and use that as fire position.
6334                 if(po->gun_banks != NULL){
6335                         pnt = po->gun_banks[0].pnt[0];
6336                 } else {
6337                         pnt = Objects[shipp->objnum].pos;
6338                 }
6339                 vm_vec_unrotate(&gun_pos, &pnt, &pobjp->orient);
6340                 vm_vec_add2(&gun_pos, &pobjp->pos);
6341
6342                 collision_time = compute_collision_time(&eobjp->pos, &eobjp->phys_info.vel, &gun_pos, weapon_speed);
6343
6344                 if (collision_time == 0.0f) {
6345                         collision_time = 100.0f;
6346                 }
6347
6348                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, collision_time);
6349
6350                 // set globals
6351                 G_collision_time = collision_time;
6352                 G_fire_pos = gun_pos;
6353         }
6354
6355         // Now add error terms (1) regular aim (2) EMP (3) stealth
6356         float scale = 0.0f;
6357         vector rand_vec;
6358
6359         // regular skill level error in aim
6360         if (aip->time_enemy_in_range > 2*range_time) {
6361                 scale = (1.0f - aip->ai_accuracy) * 4.0f;
6362         } else {
6363                 scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - aip->time_enemy_in_range/(2*range_time)));
6364         }
6365
6366         // if this ship is under the effect of an EMP blast, throw his aim off a bit
6367         if (shipp->emp_intensity > 0.0f) {
6368                 // never go lower than 1/2 of the EMP effect max, otherwise things aren't noticeable
6369                 scale += (MAX_EMP_INACCURACY * (shipp->emp_intensity < 0.5f ? 0.5f : shipp->emp_intensity));
6370                 mprintf(("AI miss scale factor (EMP) %f\n",scale));
6371         }
6372
6373         // if stealthy ship, throw his aim off, more when farther away and when dot is small
6374         if ( aip->ai_flags & AIF_STEALTH_PURSIUT ) {
6375                 float dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6376                 vector temp;
6377                 vm_vec_sub(&temp, &eobjp->pos, &pobjp->pos);
6378                 vm_vec_normalize_quick(&temp);
6379                 float dot = vm_vec_dotprod(&temp, &pobjp->orient.v.fvec);
6380                 float st_err = 3.0f * (1.4f - dot) * (1.0f + dist / (get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST)) * (1 - aip->ai_accuracy);
6381                 scale += st_err;
6382                 // mprintf(("error term: %.1f, total %.1f, dot %.3f\n", st_err, scale, dot));
6383         }
6384
6385         // get a random vector that changes slowly over time (1x / sec)
6386         static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6387
6388         vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6389
6390         // set global
6391         G_predicted_pos = *predicted_enemy_pos;
6392 }
6393
6394 //      Handler of submode for Chase.  Go into a continuous turn for awhile.
6395 void ai_chase_ct()
6396 {
6397         vector          tvec;
6398         ship_info       *sip;
6399         ai_info         *aip;
6400
6401         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6402         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6403         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6404         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6405
6406         //      Make a continuous turn towards any combination of possibly negated
6407         // up and right vectors.
6408         tvec = Pl_objp->pos;
6409
6410         if (aip->submode_parm0 & 0x01)
6411                 vm_vec_add2(&tvec, &Pl_objp->orient.v.rvec);
6412         if (aip->submode_parm0 & 0x02)
6413                 vm_vec_sub2(&tvec, &Pl_objp->orient.v.rvec);
6414         if (aip->submode_parm0 & 0x04)
6415                 vm_vec_add2(&tvec, &Pl_objp->orient.v.uvec);
6416         if (aip->submode_parm0 & 0x08)
6417                 vm_vec_sub2(&tvec, &Pl_objp->orient.v.uvec);
6418
6419         //      Detect degenerate cases that cause tvec to be same as player pos.
6420         if (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
6421                 aip->submode_parm0 &= 0x05;
6422                 if (aip->submode_parm0 == 0)
6423                         aip->submode_parm0 = 1;
6424                 vm_vec_add2(&tvec, &Pl_objp->orient.v.rvec);
6425         }
6426
6427         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6428         accelerate_ship(aip, 1.0f);
6429 }
6430
6431 //      ATTACK submode handler for chase mode.
6432 void ai_chase_eb(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
6433 {
6434         vector  _pep;
6435         float           dot_to_enemy, dot_from_enemy;
6436
6437         compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
6438
6439         //      If we're trying to slow down to get behind, then point to turn towards is different.
6440         _pep = *predicted_enemy_pos;
6441         if ((dot_to_enemy > dot_from_enemy + 0.1f) || (dot_to_enemy > 0.9f))
6442                 vm_vec_scale_add(&_pep, &Pl_objp->pos, &En_objp->orient.v.fvec, 100.0f);
6443
6444         ai_turn_towards_vector(&_pep, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6445
6446         accelerate_ship(aip, 0.0f);
6447 }
6448
6449 //      Return time until weapon_objp might hit ship_objp.
6450 //      Assumes ship_objp is not moving.
6451 //      Returns negative time if not going to hit.
6452 //      This is a very approximate function, but is pretty fast.
6453 float ai_endangered_time(object *ship_objp, object *weapon_objp)
6454 {
6455         float           to_dot, from_dot, dist;
6456
6457         dist = compute_dots(ship_objp, weapon_objp, &to_dot, &from_dot);
6458
6459         //      Note, this is bogus.  It assumes only the weapon is moving.
6460         //      Only proceed if weapon sort of pointing at object and object pointing towards or away from weapon
6461         //      (Ie, if object moving at right angle to weapon, just continue for now...)
6462         if (weapon_objp->phys_info.speed < 1.0f)
6463                 return dist + 1.0f;
6464         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))
6465                 return dist / weapon_objp->phys_info.speed;
6466         else
6467                 return -1.0f;
6468 }
6469
6470 //      Return time until danger weapon could hit this ai object.
6471 //      Return negative time if not endangered.
6472 float ai_endangered_by_weapon(ai_info *aip)
6473 {
6474         object  *weapon_objp;
6475
6476         if (aip->danger_weapon_objnum == -1) {
6477                 return -1.0f;
6478         }
6479
6480         weapon_objp = &Objects[aip->danger_weapon_objnum];
6481
6482         if (weapon_objp->signature != aip->danger_weapon_signature) {
6483                 aip->danger_weapon_objnum = -1;
6484                 return -1.0f;
6485         }
6486
6487         return ai_endangered_time(&Objects[Ships[aip->shipnum].objnum], weapon_objp);
6488 }
6489
6490 //      Return true if this ship is near full strength.
6491 int ai_near_full_strength(object *objp, ship_info *sip)
6492 {
6493         return (objp->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp)/sip->shields > 0.8f);
6494 }
6495                                 
6496 //      Set acceleration while in attack mode.
6497 void attack_set_accel(ai_info *aip, float dist_to_enemy, float dot_to_enemy, float dot_from_enemy)
6498 {
6499         float   speed_ratio;
6500
6501         if (En_objp->phys_info.speed > 1.0f)
6502                 speed_ratio = Pl_objp->phys_info.speed/En_objp->phys_info.speed;
6503         else
6504                 speed_ratio = 5.0f;
6505
6506         //      Sometimes, told to attack slowly.  Allows to get in more hits.
6507         if (aip->ai_flags & AIF_ATTACK_SLOWLY) {
6508                 if ((dist_to_enemy > 200.0f) && (dist_to_enemy < 800.0f)) {
6509                         if ((dot_from_enemy < 0.9f) || ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
6510                                 //nprintf(("AI", " slowly "));
6511                                 accelerate_ship(aip, max(1.0f - (dist_to_enemy-200.0f)/600.0f, 0.1f));
6512                                 return;
6513                         }
6514                 } else
6515                         aip->ai_flags &= ~AIF_ATTACK_SLOWLY;
6516         }
6517
6518         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) {
6519                 //nprintf(("AI", "1"));
6520                 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6521                         if (dist_to_enemy > 800.0f) {
6522                                 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6523                                         float percent_left;
6524                                         ship    *shipp;
6525                                         ship_info *sip;
6526
6527                                         shipp = &Ships[Pl_objp->instance];
6528                                         sip = &Ship_info[shipp->ship_info_index];
6529
6530                                         if (sip->afterburner_fuel_capacity > 0.0f) {
6531                                                 percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
6532                                                 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
6533                                                         afterburners_start(Pl_objp);
6534                                                         aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
6535                                                 }
6536                                         }
6537                                 }
6538                         }
6539                 }
6540
6541                 accelerate_ship(aip, 1.0f);
6542         } else if ((Missiontime - aip->last_hit_time > F1_0*7)
6543                 && (En_objp->phys_info.speed < 10.0f) 
6544                 && (dist_to_enemy > 25.0f) 
6545                 && (dot_to_enemy > 0.8f)
6546                 && (dot_from_enemy < 0.8f)) {
6547                 accelerate_ship(aip, 0.0f);             //      No one attacking us, so don't need to move.
6548         } else if ((dot_from_enemy < 0.25f) && (dot_to_enemy > 0.5f)) {
6549                 set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed);
6550         } else if (Pl_objp->phys_info.speed < 15.0f) {
6551                 accelerate_ship(aip, 1.0f);
6552         } else if (Pl_objp->phys_info.speed > En_objp->phys_info.speed - 1.0f) {
6553                 if (dot_from_enemy > 0.75f)
6554                         accelerate_ship(aip, 1.0f);
6555                 else
6556                         set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed*0.75f + 3.0f);
6557         } else {
6558                 change_acceleration(aip, 0.5f);
6559         }
6560 }
6561
6562 //      Pl_objp (aip) tries to get behind En_objp.
6563 //      New on 2/21/98: If this ship can move backwards and slide, maybe do that to get behind.
6564 void get_behind_ship(ai_info *aip, ship_info *sip, float dist_to_enemy)
6565 {
6566         vector  new_pos;
6567         float           dot;
6568         vector  vec_from_enemy;
6569         float           dist;
6570
6571         dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
6572
6573         vm_vec_scale_add(&new_pos, &En_objp->pos, &En_objp->orient.v.fvec, -100.0f);            //      Pick point 100 units behind.
6574         ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6575
6576         dot = vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.fvec);
6577
6578         if (dot > 0.25f) {
6579                 accelerate_ship(aip, 1.0f);
6580         } else {
6581                 accelerate_ship(aip, (dot + 1.0f)/2.0f);
6582         }
6583 }
6584
6585 int avoid_player(object *objp, vector *goal_pos)
6586 {
6587         maybe_avoid_player(Pl_objp, goal_pos);
6588         ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
6589
6590         if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6591                 ship_info *sip = &Ship_info[Ships[objp->instance].ship_info_index];
6592
6593                 if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6594                         ai_turn_towards_vector(&aip->avoid_goal_point, objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6595                         accelerate_ship(aip, 0.5f);
6596                         return 1;
6597                 }
6598         }
6599
6600         return 0;
6601 }
6602
6603 //      Determine if a cylinder of width radius from p0 to p1 will collide with big_objp.
6604 //      If so, stuff *collision_point.
6605 int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6606 {
6607         mc_info mc;
6608
6609         mc.model_num = Ships[big_objp->instance].modelnum;              // Fill in the model to check
6610         mc.orient = &big_objp->orient;                  // The object's orient
6611         mc.pos = &big_objp->pos;                                        // The object's position
6612         mc.p0 = p0;                                                                             // Point 1 of ray to check
6613         mc.p1 = p1;
6614         mc.flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE | MC_SUBMODEL;                                  // flags
6615
6616         mc.radius = radius;
6617
6618         // Only check the 2nd lowest hull object
6619         polymodel *pm = model_get(Ships[big_objp->instance].modelnum);
6620         mc.submodel_num = pm->detail[0]; //pm->submodel->num_details-2];
6621         model_collide(&mc);
6622
6623         if (mc.num_hits)
6624                 *collision_point = mc.hit_point_world;
6625
6626         return mc.num_hits;
6627 }
6628
6629 //      Return true/false if *objp will collide with *big_objp
6630 //      Stuff distance in *distance to collision point if *objp will collide with *big_objp within delta_time seconds.
6631 //      Global collision point stuffed in *collision_point
6632 int will_collide_with_big_ship(object *objp, vector *goal_point, object *big_objp, vector *collision_point, float delta_time)
6633 {
6634         float           radius;
6635         vector  end_pos;
6636
6637         radius = big_objp->radius + delta_time * objp->phys_info.speed;
6638
6639         if (vm_vec_dist_quick(&big_objp->pos, &objp->pos) > radius) {
6640                 return 0;
6641         }
6642
6643         if (goal_point == NULL) {
6644                 vm_vec_scale_add(&end_pos, &objp->pos, &objp->phys_info.vel, delta_time);                                       // Point 2 of ray to check
6645         } else {
6646                 end_pos = *goal_point;
6647         }
6648
6649         return will_collide_pp(&objp->pos, &end_pos, objp->radius, big_objp, collision_point);
6650 }
6651
6652 //      Return true if *objp is expected to collide with a large ship.
6653 //      Stuff global collision point in *collision_point.
6654 //      If *goal_point is not NULL, use that as the point towards which *objp will be flying.  Don't use *objp velocity
6655 //      *ignore_objp will typically be the target this ship is pursuing, either to attack or guard.  We don't want to avoid it.
6656 int will_collide_with_big_ship_all(object *objp, object *ignore_objp, vector *goal_point, vector *collision_point, float *distance, float delta_time)
6657 {
6658         ship_obj        *so;
6659         object  *big_objp;
6660         int             collision_obj_index = -1;
6661         float           min_dist = 999999.9f;
6662         float           collision_time = -1.0f;
6663
6664         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6665                 float   time = 0.0f;
6666                 big_objp = &Objects[so->objnum];
6667
6668                 if (big_objp == ignore_objp)
6669                         continue;
6670
6671                 if (Ship_info[Ships[big_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
6672                         vector  cur_collision_point;
6673                         float           cur_dist;
6674
6675                         if (will_collide_with_big_ship(objp, goal_point, big_objp, &cur_collision_point, delta_time)) {
6676
6677                                 cur_dist = vm_vec_dist(&cur_collision_point, &objp->pos);
6678
6679                                 if (cur_dist < min_dist) {
6680                                         min_dist = cur_dist;
6681                                         *collision_point = cur_collision_point;
6682                                         collision_time = time;
6683                                         collision_obj_index = OBJ_INDEX(big_objp);
6684                                 }
6685                         }
6686                 }
6687         }
6688
6689         *distance = min_dist;
6690         return collision_obj_index;
6691
6692 }
6693
6694 typedef struct {
6695         float           dist;
6696         int             collide;
6697         vector  pos;
6698 } sgoal;
6699
6700 //int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6701 //      Pick a point for *objp to fly towards to avoid a collision with *big_objp at *collision_point
6702 //      Return result in *avoid_pos
6703 void mabs_pick_goal_point(object *objp, object *big_objp, vector *collision_point, vector *avoid_pos)
6704 {
6705         matrix  mat1;
6706         sgoal           goals[4];
6707         vector  v2b;
6708
6709         vm_vec_normalized_dir(&v2b, collision_point, &objp->pos);
6710         vm_vector_2_matrix(&mat1, &v2b, NULL, NULL);
6711
6712         int     found = 0;
6713
6714         //      Try various scales, in 0.5f, 0.75f, 1.0f, 1.25f.
6715         //      First try 0.5f to see if we can find a point that near the center of the target ship, which presumably
6716         //      means less of a turn.
6717         //      Try going as far as 1.25f * radius.
6718         float   s;
6719         for (s=0.5f; s<1.3f; s += 0.25f) {
6720                 int     i;
6721                 for (i=0; i<4; i++) {
6722                         vector p = big_objp->pos;
6723                         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
6724                         float kr = big_objp->radius*s + objp->radius * ((OBJ_INDEX(objp) % 4) ^ 2)/4;
6725                         if (i&1)
6726                                 ku = -ku;
6727                         if (i&2)
6728                                 kr = -kr;
6729                         vm_vec_scale_add2(&p, &mat1.v.uvec, ku);
6730                         vm_vec_scale_add2(&p, &mat1.v.rvec, kr);
6731                         goals[i].pos = p;
6732                         goals[i].dist = vm_vec_dist_quick(&objp->pos, &p);
6733                         goals[i].collide = will_collide_pp(&objp->pos, &p, objp->radius, big_objp, collision_point);
6734                         if (!goals[i].collide)
6735                                 found = 1;
6736                 }
6737
6738                 //      If we found a point that doesn't collide, find the nearest one and make that the *avoid_pos.
6739                 if (found) {
6740                         float   min_dist = 9999999.9f;
6741                         int     min_index = -1;
6742
6743                         for (i=0; i<4; i++) {
6744                                 if (!goals[i].collide && (goals[i].dist < min_dist)) {
6745                                         min_dist = goals[i].dist;
6746                                         min_index = i;
6747                                 }
6748                         }
6749
6750                         Assert(i != -1);
6751                         if (i != -1) {
6752                                 *avoid_pos = goals[min_index].pos;
6753                                 return;
6754                         }
6755                 }
6756         }
6757
6758         //      Drat.  We tried and tried and could not find a point that did not cause a collision.
6759         //      Get this dump pilot far away from the problem ship.
6760         vector  away_vec;
6761         vm_vec_normalized_dir(&away_vec, &objp->pos, collision_point);
6762         vm_vec_scale_add(avoid_pos, &objp->pos, &away_vec, big_objp->radius*1.5f);
6763
6764 }
6765
6766 //      Return true if a large ship is being ignored.
6767 int maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vector *goal_point, float delta_time)
6768 {
6769         if (timestamp_elapsed(aip->avoid_check_timestamp)) {
6770                 float           distance;
6771                 vector  collision_point;
6772                 int             ship_num;
6773                 if ((ship_num = will_collide_with_big_ship_all(Pl_objp, ignore_objp, goal_point, &collision_point, &distance, delta_time)) != -1) {
6774                         aip->ai_flags |= AIF_AVOIDING_BIG_SHIP;
6775                         mabs_pick_goal_point(objp, &Objects[ship_num], &collision_point, &aip->avoid_goal_point);
6776                         float dist = vm_vec_dist_quick(&aip->avoid_goal_point, &objp->pos);
6777                         aip->avoid_check_timestamp = timestamp(2000 + min(1000, (int) (dist * 2.0f)));  //      Delay until check again is based on distance to avoid point.
6778                         aip->avoid_ship_num = ship_num;
6779                 } else {
6780                         aip->ai_flags &= ~AIF_AVOIDING_BIG_SHIP;
6781                         aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
6782                         aip->avoid_ship_num = -1;
6783                         aip->avoid_check_timestamp = timestamp(1500);
6784                 }
6785         }
6786         
6787         if (aip->ai_flags & AIF_AVOIDING_BIG_SHIP) {
6788                 ship_info *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6789
6790                 vector  v2g;
6791
6792                 ai_turn_towards_vector(&aip->avoid_goal_point, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6793                 vm_vec_normalized_dir(&v2g, &aip->avoid_goal_point, &Pl_objp->pos);
6794                 float dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
6795                 float d2 = (1.0f + dot) * (1.0f + dot);
6796                 accelerate_ship(aip, d2/4.0f);
6797                 return 1;
6798         }
6799
6800         return 0;
6801 }
6802
6803 //      Set desired right vector for ships flying towards another ship.
6804 //      Since this is governed only by vector to target, it causes ships to align bank and look less chaotic.
6805 void compute_desired_rvec(vector *rvec, vector *goal_pos, vector *cur_pos)
6806 {
6807         vector  v2e;
6808
6809         vm_vec_normalized_dir(&v2e, goal_pos, cur_pos);
6810         rvec->xyz.x = v2e.xyz.z;
6811         rvec->xyz.y = 0.0f;
6812         rvec->xyz.z = -v2e.xyz.x;
6813         if (vm_vec_mag_squared(rvec) < 0.001f)
6814                 rvec->xyz.y = 1.0f;
6815 }
6816
6817 // Handler for stealth find submode of Chase.
6818 void ai_stealth_find()
6819 {
6820         ai_info         *aip;
6821         ship_info       *sip;
6822
6823         vector new_pos, vec_to_enemy;
6824         float dist_to_enemy, dot_to_enemy, dot_from_enemy;
6825
6826         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6827         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6828         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6829         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6830
6831         // get time since last seen
6832         int delta_time = (timestamp() - aip->stealth_last_visible_stamp);
6833
6834         // if delta_time is really big, i'm real confused, start sweep
6835         if (delta_time > 10000) {
6836                 aip->submode_parm0 = SM_SF_BAIL;
6837         }
6838
6839         // guestimate new position
6840         vm_vec_scale_add(&new_pos, &aip->stealth_last_pos, &aip->stealth_velocity, (delta_time * 0.001f));
6841
6842         // if I think he's behind me, go to the goal point
6843         if ( aip->submode_parm0 == SM_SF_BEHIND ) {
6844                 new_pos = aip->goal_point;
6845         }
6846
6847         // check for collision with big ships
6848         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &new_pos, 10.0f)) {
6849                 // reset ai submode to chase
6850                 return;
6851         }
6852
6853         // if dist is near max and dot is close to 1, accel, afterburn
6854         vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6855         dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6856         dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.v.fvec);
6857
6858         // 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
6859         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) ) {
6860                 // do turn around)
6861                 vm_vec_scale_add(&aip->goal_point, &Pl_objp->pos, &Pl_objp->orient.v.fvec, -300.0f);
6862                 aip->submode_parm0 = SM_SF_BEHIND;
6863                 vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6864                 dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6865                 dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.v.fvec);
6866         }
6867
6868         if ( (dist_to_enemy > get_skill_stealth_dist_scaler()*STEALTH_MAX_VIEW_DIST) && (dot_to_enemy > 0.94f) ) {              // 20 degree half angle
6869                 // accelerate ship
6870                 accelerate_ship(aip, 1.0f);
6871
6872                 // engage afterburner
6873                 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6874                         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6875                                 afterburners_start(Pl_objp);
6876                                 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
6877                         }
6878                 }
6879
6880                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6881                 return;
6882         }
6883
6884         //      If enemy more than 500 meters away, all ships flying there will tend to match bank.
6885         //      They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
6886         //      to interpolate a matrix rather than just a vector.
6887         if (dist_to_enemy > 500.0f) {
6888                 vector  rvec;
6889                 compute_desired_rvec(&rvec, &new_pos, &Pl_objp->pos);
6890                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0, &rvec);
6891         } else {
6892                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6893         }
6894
6895         dot_from_enemy = -vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec);
6896
6897         attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
6898 }
6899
6900 // -----------------------------------------------------------------------------
6901 // try to find stealth ship by sweeping an area
6902 void ai_stealth_sweep()
6903 {
6904         ai_info         *aip;
6905         ship_info       *sip;
6906
6907         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6908         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6909         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6910         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6911
6912         vector goal_pt;
6913         vector forward, right, up;
6914         int lost_time;
6915
6916         // time since stealth last seen
6917         lost_time = (timestamp() - aip->stealth_last_visible_stamp);
6918
6919         // determine which pt to fly to in sweep by keeping track of parm0
6920         if (aip->submode_parm0 == SM_SS_SET_GOAL) {
6921
6922                 // don't make goal pt more than 2k from current pos
6923                 vm_vec_scale_add(&goal_pt, &aip->stealth_last_pos, &aip->stealth_velocity, (0.001f * lost_time));
6924
6925                 // make box size based on speed of stealth and expected time to intercept (keep box in range 200-500)
6926                 float box_size = vm_vec_mag_quick(&aip->stealth_velocity) * (0.001f * lost_time);
6927                 box_size = min(200.0f, box_size);
6928                 box_size = max(500.0f, box_size);
6929                 aip->stealth_sweep_box_size = box_size;
6930
6931                 aip->goal_point = goal_pt;
6932                 aip->submode_parm0 = SM_SS_BOX0;
6933         }
6934
6935         // GET UP, RIGHT, FORWARD FOR BOX based on stealth ship's velocity
6936         // if velocity changes in stealth mode, then ship is *seen*, and falls out of sweep mode
6937         // if stealth has no velocity make a velocity
6938         if ( vm_vec_mag_quick(&aip->stealth_velocity) < 1 ) {
6939                 vm_vec_rand_vec_quick(&aip->stealth_velocity);
6940         }
6941
6942         // get "right" vector for box
6943         vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_y_vector);
6944
6945         if ( vm_vec_mag_quick(&right) < 0.01 ) {
6946                 vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_z_vector);
6947         }
6948
6949         vm_vec_normalize_quick(&right);
6950
6951         // get forward for box
6952         vm_vec_copy_normalize_quick(&forward, &aip->stealth_velocity);
6953
6954         // get "up" for box
6955         vm_vec_crossprod(&up, &forward, &right);
6956         
6957         // lost far away ahead (do box)
6958         switch(aip->submode_parm0) {
6959         case SM_SS_BOX0:
6960                 goal_pt = aip->goal_point;
6961                 break;
6962
6963         // pt1 -U +R
6964         case SM_SS_LR:
6965                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
6966                 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
6967                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6968                 break;
6969
6970         // pt2 +U -R
6971         case SM_SS_UL:
6972                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
6973                 vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
6974                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6975                 break;
6976
6977         // pt3 back
6978         case SM_SS_BOX1:
6979                 goal_pt = aip->goal_point;
6980                 break;
6981
6982         // pt4 +U +R
6983         case SM_SS_UR:
6984                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
6985                 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
6986                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6987                 break;
6988
6989         // pt5 -U -R
6990         case SM_SS_LL:
6991                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
6992                 vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
6993                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6994                 break;
6995
6996         // pt6 back
6997         case SM_SS_BOX2:
6998                 goal_pt = aip->goal_point;
6999                 break;
7000
7001         default:
7002                 Int3();
7003
7004         }
7005
7006         // when close to goal_pt, update next goal pt
7007         float dist_to_goal = vm_vec_dist(&goal_pt, &Pl_objp->pos);
7008         if (dist_to_goal < 15) {
7009                 aip->submode_parm0++;
7010         }
7011
7012         // check for collision with big ship
7013         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &goal_pt, 10.0f)) {
7014                 // skip to the next pt on box
7015                 aip->submode_parm0++;
7016                 return;
7017         }
7018
7019         ai_turn_towards_vector(&goal_pt, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
7020
7021         float dot = 1.0f;
7022         if (dist_to_goal < 100) {
7023                 vector vec_to_goal;
7024                 vm_vec_normalized_dir(&vec_to_goal, &goal_pt, &Pl_objp->pos);
7025                 dot = vm_vec_dotprod(&vec_to_goal, &Pl_objp->orient.v.fvec);
7026         }
7027
7028         accelerate_ship(aip, 0.8f*dot);
7029 }
7030
7031 //      ATTACK submode handler for chase mode.
7032 void ai_chase_attack(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
7033 {
7034         int             start_bank;
7035         float           dot_to_enemy, dot_from_enemy; //, time_to_hit;
7036         float           bank_override = 0.0f;
7037
7038         if (avoid_player(Pl_objp, predicted_enemy_pos))
7039                 return;
7040
7041         compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
7042
7043         polymodel *po = model_get( sip->modelnum );
7044
7045         vector  *rel_pos;
7046         float           scale;
7047         vector  randvec;
7048         vector  new_pos;
7049
7050         start_bank = Ships[aip->shipnum].weapons.current_primary_bank;
7051         if (po->n_guns && start_bank != -1 ) {
7052                 rel_pos = &po->gun_banks[start_bank].pnt[0];
7053         } else
7054                 rel_pos = NULL;
7055
7056         //      If ship moving slowly relative to its size, then don't attack its center point.
7057         //      How far from center we attack is based on speed, size and distance to enemy
7058         if (En_objp->radius > En_objp->phys_info.speed) {
7059                 static_randvec(Pl_objp-Objects, &randvec);
7060                 scale = dist_to_enemy/(dist_to_enemy + En_objp->radius) * En_objp->radius;
7061                 scale *= 0.5f * En_objp->radius/(En_objp->phys_info.speed + En_objp->radius);   // scale downward by 1/2 to 1/4
7062                 vm_vec_scale_add(&new_pos, predicted_enemy_pos, &randvec, scale);
7063         } else
7064                 new_pos = *predicted_enemy_pos;
7065
7066         if (dist_to_enemy < 250.0f) {
7067                 if (dot_from_enemy > 0.7f) {
7068                         bank_override = Pl_objp->phys_info.speed;
7069                 }
7070         }
7071
7072         //      If enemy more than 500 meters away, all ships flying there will tend to match bank.
7073         //      They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
7074         //      to interpolate a matrix rather than just a vector.
7075         if (dist_to_enemy > 500.0f) {
7076                 vector  rvec;
7077                 compute_desired_rvec(&rvec, predicted_enemy_pos, &Pl_objp->pos);
7078                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0, &rvec);
7079         } else {
7080                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0);
7081         }
7082
7083         attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
7084 }
7085
7086 //      EVADE_SQUIGGLE submode handler for chase mode.
7087 //      Changed by MK on 5/5/97.
7088 //      Used to evade towards a point off the right or up vector.
7089 //      Now, evade straight away to try to get far away.
7090 //      The squiggling should protect against laser fire.
7091 void ai_chase_es(ai_info *aip, ship_info *sip)
7092 {
7093         vector  tvec;
7094         fix             timeslice;
7095         fix             scale;
7096         float           bank_override = 0.0f;
7097
7098         tvec = Pl_objp->pos;
7099
7100         timeslice = (Missiontime >> 16) & 0x0f;
7101         scale = ((Missiontime >> 16) & 0x0f) << 14;
7102
7103         if (timeslice & 0x01)
7104                 vm_vec_scale_add2(&tvec, &Pl_objp->orient.v.rvec, f2fl(scale ^ 0x10000));
7105         if (timeslice & 0x02)
7106                 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.v.rvec, f2fl(scale));
7107         if (timeslice & 0x04)
7108                 vm_vec_scale_add2(&tvec, &Pl_objp->orient.v.uvec, f2fl(scale ^ 0x10000));
7109         if (timeslice & 0x08)
7110                 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.v.uvec, f2fl(scale));
7111
7112         while (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
7113                 tvec.xyz.x += frand();
7114                 tvec.xyz.y += frand();
7115         }
7116
7117         bank_override = Pl_objp->phys_info.speed;
7118
7119         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7120         accelerate_ship(aip, 1.0f);
7121 }
7122
7123 //      Trying to get away from opponent.
7124 void ai_chase_ga(ai_info *aip, ship_info *sip)
7125 {
7126         //      If not near end of this submode, evade squiggly.  If near end, just fly straight for a bit
7127         vector  tvec;
7128         float           bank_override;
7129         vector  vec_from_enemy;
7130
7131         if (En_objp != NULL) {
7132                 vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
7133         } else
7134                 vec_from_enemy = Pl_objp->orient.v.fvec;
7135
7136         static_randvec(Missiontime >> 15, &tvec);
7137         vm_vec_scale(&tvec, 100.0f);
7138         vm_vec_scale_add2(&tvec, &vec_from_enemy, 300.0f);
7139         vm_vec_add2(&tvec, &Pl_objp->pos);
7140
7141         bank_override = Pl_objp->phys_info.speed;
7142
7143         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7144
7145         accelerate_ship(aip, 2.0f);
7146
7147         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
7148                 if (!(Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
7149                         float percent_left = 100.0f * Ships[Pl_objp->instance].afterburner_fuel / sip->afterburner_fuel_capacity;
7150                         if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
7151                                 afterburners_start(Pl_objp);
7152                                 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7153                         }
7154                         afterburners_start(Pl_objp);
7155                         aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7156                 }
7157         }
7158
7159 }
7160
7161 //      Make object *objp attack subsystem with ID = subnum.
7162 //      Return true if found a subsystem to attack, else return false.
7163 //      Note, can fail if subsystem exists, but has no hits.
7164 int ai_set_attack_subsystem(object *objp, int subnum)
7165 {
7166         ship                    *shipp, *attacker_shipp;
7167         ai_info         *aip;
7168         ship_subsys     *ssp;
7169         object          *attacked_objp;
7170
7171         Assert(objp->type == OBJ_SHIP);
7172         Assert(objp->instance >= 0);
7173
7174         attacker_shipp = &Ships[objp->instance];
7175         Assert(attacker_shipp->ai_index >= 0);
7176
7177         aip = &Ai_info[attacker_shipp->ai_index];
7178
7179         // MWA -- 2/27/98.  Due to AL's changes, target_objnum is now not always valid (at least sometimes
7180         // in terms of goals).  So, bail if we don't have a valid target.
7181         if ( aip->target_objnum == -1 )
7182                 return 0;
7183
7184         attacked_objp = &Objects[aip->target_objnum];
7185         shipp = &Ships[attacked_objp->instance];                //  need to get our target's ship pointer!!!
7186
7187         ssp = ship_get_indexed_subsys(shipp, subnum, &objp->pos);
7188         if (ssp == NULL)
7189                 return 0;
7190
7191         set_targeted_subsys(aip, ssp, aip->target_objnum);
7192         
7193         if (aip->ignore_objnum == aip->target_objnum)
7194                 aip->ignore_objnum = UNUSED_OBJNUM;
7195
7196         // -- Done at caller in ai_process_mission_orders -- attacked_objp->flags |= OF_PROTECTED;
7197
7198         ai_set_goal_maybe_abort_dock(objp, aip);
7199         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7200
7201         return 1;
7202 }
7203
7204 void ai_set_guard_vec(object *objp, object *guard_objp)
7205 {
7206         ai_info *aip;
7207         float   radius;
7208
7209         aip = &Ai_info[Ships[objp->instance].ai_index];
7210
7211         //      Handle case of bogus call in which ship is told to guard self.
7212         Assert(objp != guard_objp);
7213         if (objp == guard_objp) {
7214                 vm_vec_rand_vec_quick(&aip->guard_vec);
7215                 vm_vec_scale(&aip->guard_vec, 100.0f);
7216                 return;
7217         }
7218
7219         // check if guard_objp is BIG
7220         radius = 5.0f * (objp->radius + guard_objp->radius) + 50.0f;
7221         if (radius > 300.0f) {
7222                 radius = guard_objp->radius * 1.25f;
7223         }
7224
7225         vm_vec_sub(&aip->guard_vec, &objp->pos, &guard_objp->pos);
7226
7227         if (vm_vec_mag(&aip->guard_vec) > 3.0f*radius) {
7228                 //      Far away, don't just use vector to object, causes clustering of guard ships.
7229                 vector  tvec, rvec;
7230                 float   mag;
7231                 mag = vm_vec_copy_normalize(&tvec, &aip->guard_vec);
7232                 vm_vec_rand_vec_quick(&rvec);                   
7233                 vm_vec_scale_add2(&tvec, &rvec, 0.5f);
7234                 vm_vec_copy_scale(&aip->guard_vec, &tvec, mag);
7235         }
7236
7237         vm_vec_normalize_quick(&aip->guard_vec);
7238         vm_vec_scale(&aip->guard_vec, radius);
7239 }
7240
7241 //      Make object *objp guard object *other_objp.
7242 //      To be called from the goals code.
7243 void ai_set_guard_wing(object *objp, int wingnum)
7244 {
7245         ship            *shipp;
7246         ai_info *aip;
7247         int             leader_objnum, leader_shipnum;
7248
7249         Assert(wingnum >= 0);
7250
7251         Assert(objp->type == OBJ_SHIP);
7252         Assert(objp->instance >= 0);
7253
7254         // shouldn't set the ai mode for the player
7255         if ( objp == Player_obj ) {
7256                 return;
7257         }
7258
7259         shipp = &Ships[objp->instance];
7260
7261         Assert(shipp->ai_index >= 0);
7262
7263         aip = &Ai_info[shipp->ai_index];
7264         force_avoid_player_check(objp, aip);
7265
7266         ai_set_goal_maybe_abort_dock(objp, aip);
7267         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7268
7269         //      This function is called whenever a guarded ship is destroyed, so this code
7270         //      prevents a ship from trying to guard a non-existent wing.
7271         if (Wings[wingnum].current_count < 1) {
7272                 aip->guard_objnum = -1;
7273                 aip->guard_wingnum = -1;
7274                 aip->mode = AIM_NONE;
7275         } else {
7276                 leader_shipnum = Wings[wingnum].ship_index[0];
7277                 leader_objnum = Ships[leader_shipnum].objnum;
7278
7279                 Assert((leader_objnum >= 0) && (leader_objnum < MAX_OBJECTS));
7280                 //Assert(leader_objnum != objp-Objects);        //      Don't allow ships to guard themselves.
7281                 if (leader_objnum == OBJ_INDEX(objp)) {
7282                         //Int3();       //      Seems illegal, but let's clean up.  Get MikeK.
7283                         return;
7284                 }
7285
7286                 aip->guard_wingnum = wingnum;
7287                 aip->guard_objnum = leader_objnum;
7288                 aip->guard_signature = Objects[leader_objnum].signature;
7289                 aip->mode = AIM_GUARD;
7290                 aip->submode = AIS_GUARD_STATIC;
7291
7292                 ai_set_guard_vec(objp, &Objects[leader_objnum]);
7293         }
7294 }
7295
7296 //      Make object *objp guard object *other_objp.
7297 //      To be called from the goals code.
7298 void ai_set_evade_object(object *objp, object *other_objp)
7299 {
7300         ship            *shipp;
7301         ai_info *aip;
7302         int             other_objnum;
7303
7304         Assert(objp->type == OBJ_SHIP);
7305         Assert(objp->instance >= 0);
7306
7307         shipp = &Ships[objp->instance];
7308
7309         Assert(shipp->ai_index >= 0);
7310
7311         aip = &Ai_info[shipp->ai_index];
7312
7313         other_objnum = OBJ_INDEX(other_objp);
7314         Assert(other_objnum >= 0);
7315
7316         Assert(other_objnum != Ships[aip->shipnum].objnum);     //      make sure not targeting self
7317         aip->target_objnum = other_objnum;
7318
7319         aip->mode = AIM_EVADE;
7320 }
7321
7322 //      Make objp guard other_objp
7323 //      If other_objp is a member of a wing, objp will guard that whole wing
7324 //      UNLESS objp is also a member of the wing!
7325 void ai_set_guard_object(object *objp, object *other_objp)
7326 {
7327         ship            *shipp;
7328         ai_info *aip;
7329         int             other_objnum;
7330
7331         Assert(objp->type == OBJ_SHIP);
7332         Assert(objp->instance >= 0);
7333         Assert(objp != other_objp);
7334
7335         shipp = &Ships[objp->instance];
7336
7337         Assert(shipp->ai_index >= 0);
7338
7339         aip = &Ai_info[shipp->ai_index];
7340         aip->avoid_check_timestamp = timestamp(1);
7341
7342         //      If ship to guard is in a wing, guard that whole wing.
7343         ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
7344         if ((other_aip->wing != -1) && (other_aip->wing != aip->wing)) {
7345                 ai_set_guard_wing(objp, Ai_info[Ships[other_objp->instance].ai_index].wing);
7346         } else {
7347
7348                 other_objnum = other_objp-Objects;
7349
7350                 aip->guard_objnum = other_objnum;
7351                 aip->guard_signature = other_objp->signature;
7352                 aip->guard_wingnum = -1;
7353
7354                 aip->mode = AIM_GUARD;
7355                 aip->submode = AIS_GUARD_STATIC;
7356
7357                 Assert(other_objnum >= 0);      //      Hmm, bogus object and we need its position for guard_vec.
7358
7359                 // vm_vec_sub(&aip->guard_vec, &objp->pos, &Objects[other_objnum].pos);
7360                 ai_set_guard_vec(objp, &Objects[other_objnum]);
7361
7362                 ai_set_goal_maybe_abort_dock(objp, aip);
7363                 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7364         }
7365 }
7366
7367 //      Update the aspect_locked_time field based on whether enemy is in view cone.
7368 //      Also set/clear AIF_SEEK_LOCK.
7369 void update_aspect_lock_information(ai_info *aip, vector *vec_to_enemy, float dist_to_enemy, float enemy_radius)
7370 {
7371         float   dot_to_enemy;
7372         int     num_weapon_types;
7373         int     weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
7374         ship    *shipp;
7375         ship_weapon     *swp;
7376         weapon_info     *wip;
7377
7378         shipp = &Ships[aip->shipnum];
7379         swp = &shipp->weapons;
7380
7381         // AL 3-7-98: This probably should never happen, but check to ensure that current_secondary_bank is valid
7382         if ( (swp->current_secondary_bank < 0) || (swp->current_secondary_bank > swp->num_secondary_banks) ) {
7383                 return;
7384         }
7385
7386         num_weapon_types = get_available_secondary_weapons(Pl_objp, weapon_id_list, weapon_bank_list);
7387
7388         wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
7389
7390         if (num_weapon_types && (wip->wi_flags & WIF_HOMING_ASPECT)) {
7391                 if (dist_to_enemy > 300.0f - min(enemy_radius, 100.0f))
7392                         aip->ai_flags |= AIF_SEEK_LOCK;
7393                 else
7394                         aip->ai_flags &= ~AIF_SEEK_LOCK;
7395
7396                 //      Update locking information for aspect seeking missiles.
7397                 aip->current_target_is_locked = 0;
7398                 dot_to_enemy = vm_vec_dot(vec_to_enemy, &Pl_objp->orient.v.fvec);
7399
7400                 float   needed_dot = 0.9f - 0.5f * enemy_radius/(dist_to_enemy + enemy_radius); //      Replaced MIN_TRACKABLE_DOT with 0.9f
7401                 if (dot_to_enemy > needed_dot) {
7402                         aip->aspect_locked_time += flFrametime;
7403                         // nprintf(("AI", "+ Lock time = %7.3f\n", aip->aspect_locked_time));
7404                         if (aip->aspect_locked_time >= wip->min_lock_time) {
7405                                 aip->aspect_locked_time = wip->min_lock_time;
7406                                 aip->current_target_is_locked = 1;
7407                         }
7408                 } else {
7409                         aip->aspect_locked_time -= flFrametime*2;
7410                         // nprintf(("AI", "- Lock time = %7.3f\n", aip->aspect_locked_time));
7411                         if (aip->aspect_locked_time < 0.0f)
7412                                 aip->aspect_locked_time = 0.0f;
7413                 }
7414                 //nprintf(("AI", "dot = %7.3f, time = %7.3f\n", dot_to_enemy, aip->aspect_locked_time));
7415         
7416         } else {
7417                 aip->current_target_is_locked = 0;
7418                 aip->aspect_locked_time = 0.0f; // Used to be this, why?: wip->min_lock_time;
7419                 aip->ai_flags &= ~AIF_SEEK_LOCK;
7420         }
7421
7422 }
7423
7424 //      We're in chase mode and we've recently collided with our target.
7425 //      Fly away from it!
7426 void ai_chase_fly_away(object *objp, ai_info *aip)
7427 {
7428         int     abort_flag = 0;
7429
7430         if (aip->ai_flags & AIF_TARGET_COLLISION) {
7431                 aip->ai_flags &= ~AIF_TARGET_COLLISION; //      Don't process this hit again next frame.
7432                 aip->submode = SM_FLY_AWAY;                                     //      Focus on avoiding target
7433                 aip->submode_start_time = Missiontime;
7434         }
7435
7436         if ((aip->target_objnum == -1) || (Objects[aip->target_objnum].signature != aip->target_signature)) {
7437                 abort_flag = 1;
7438         }
7439
7440         if (abort_flag || (Missiontime > aip->submode_start_time + F1_0)) {
7441                 aip->last_attack_time = Missiontime;
7442                 aip->submode = SM_ATTACK;
7443                 aip->submode_start_time = Missiontime;
7444         } else {
7445                 vector  v2e;
7446                 float           dot;
7447
7448                 vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, &objp->pos);
7449
7450                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2e);
7451                 if (dot < 0.0f)
7452                         accelerate_ship(aip, 1.0f);
7453                 else
7454                         accelerate_ship(aip, 1.0f - dot);
7455                 turn_away_from_point(objp, &Objects[aip->target_objnum].pos, 0.0f);
7456         }
7457 }
7458
7459 //      Return bank index of favored secondary weapon.
7460 //      Return -1 if nothing favored.
7461 //      "favored" means SEXPs have specified the weapon as being good to fire at en_objp.
7462 int has_preferred_secondary(object *objp, object *en_objp, ship_weapon *swp)
7463 {
7464 // int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
7465         int     i;
7466
7467         for (i=0; i<swp->num_secondary_banks; i++) {
7468                 if (swp->secondary_bank_capacity[i] > 0) {
7469                         if (swp->secondary_bank_ammo[i] > 0) {
7470                                 if (is_preferred_weapon(swp->secondary_bank_weapons[i], objp, en_objp) != -1){
7471                                         return i;
7472                                 }
7473                         }
7474                 }
7475         }
7476
7477         return -1;
7478 }
7479
7480 //      Choose which secondary weapon to fire.
7481 //      Note, this is not like ai_select_secondary_weapon().  "choose" means make a choice.
7482 //      "select" means execute an order.  Get it?
7483 //      This function calls ai_select_secondary_weapon() with the characteristics it should search for.
7484 void ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp)
7485 {
7486         float                   subsystem_strength = 0.0f;
7487         int                     is_big_ship, priority1, priority2;
7488         ship_weapon     *swp;
7489         ship_info       *esip;
7490
7491         if ( en_objp->type == OBJ_SHIP ) {
7492                 esip = &Ship_info[Ships[en_objp->instance].ship_info_index];
7493         } else {
7494                 esip = NULL;
7495         }
7496
7497         swp = &Ships[objp->instance].weapons;
7498
7499         // AL 3-5-98: do a quick out if the ship has no secondaries
7500         if ( swp->num_secondary_banks <= 0 ) {
7501                 swp->current_secondary_bank = -1;
7502                 return;
7503         }
7504
7505         int preferred_secondary = has_preferred_secondary(objp, en_objp, swp);
7506
7507         if (preferred_secondary != -1) {
7508                 if (swp->current_secondary_bank != preferred_secondary) {
7509                         aip->current_target_is_locked = 0;
7510                         aip->aspect_locked_time = 0.0f;
7511                         swp->current_secondary_bank = preferred_secondary;
7512                 }
7513                 //nprintf(("AI", "Favored secondary = %s\n", Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7514                 aip->ai_flags |= AIF_UNLOAD_SECONDARIES;
7515         } else {
7516                 aip->ai_flags &= ~AIF_UNLOAD_SECONDARIES;
7517                 if (aip->targeted_subsys) {
7518                         subsystem_strength = aip->targeted_subsys->current_hits;
7519                 }
7520
7521                 if ( esip ) {
7522                         is_big_ship = esip->flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP);
7523                 } else {
7524                         is_big_ship=0;
7525                 }
7526
7527                 if (is_big_ship) {
7528                         priority1 = WIF_HUGE;
7529                         priority2 = WIF_HOMING;
7530                 } else if ( (esip != NULL) && (esip->flags & SIF_BOMBER) ) {
7531                         priority1 = WIF_BOMBER_PLUS;
7532                         priority2 = WIF_HOMING;
7533                 } else if (subsystem_strength > 100.0f) {
7534                         priority1 = WIF_PUNCTURE;
7535                         priority2 = WIF_HOMING;
7536                 } else {
7537                         priority1 = WIF_HOMING;
7538                         priority2 = 0;
7539                 }
7540                 
7541                 ai_select_secondary_weapon(objp, swp, priority1, priority2);
7542         }
7543
7544         // nprintf(("AI", "Frame %i: Chose secondary %s\n", Framecount, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7545 }
7546
7547 //      Return time, in seconds, at which this ship can next fire its current secondary weapon.
7548 float set_secondary_fire_delay(ai_info *aip, ship *shipp, weapon_info *swip)
7549 {
7550         float t = swip->fire_wait;              //      Base delay for this weapon.
7551         if (shipp->team == Player_ship->team) {
7552                 //      On player's team, _lower_ skill level = faster firing
7553                 t = t * (Game_skill_level+2) / (NUM_SKILL_LEVELS);
7554         } else {                //      Not on player's team, higher skill level = faster firing
7555                 t = t * (NUM_SKILL_LEVELS - Game_skill_level+2) / (NUM_SKILL_LEVELS);
7556         }
7557
7558         t += (Num_ai_classes - aip->ai_class + 1) * 0.5f;
7559         t *= frand_range(0.8f, 1.2f);
7560
7561         //      For the missiles that fire fairly quickly, occasionally add an additional substantial delay.
7562         if (t < 5.0f)
7563                 if (frand() < 0.5f)
7564                         t = t * 2.0f + 2.0f;
7565
7566         return t;
7567 }
7568
7569
7570 void ai_chase_big_approach_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7571 {
7572         float dist_to_goal;
7573
7574         // head straight toward him and maybe circle later
7575         vm_vec_avg(goal_pos, &attack_objp->pos, &target_objp->pos);
7576
7577         // get distance to goal
7578         dist_to_goal = vm_vec_dist(goal_pos, &attack_objp->pos);
7579         
7580         // set accel
7581         if (dist_to_goal > 400.0f) {
7582                 *accel = 1.0f;
7583         } else {
7584                 *accel = dist_to_goal/400.0f;
7585         }
7586 }
7587
7588 void ai_chase_big_circle_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7589 {
7590         get_tangent_point(goal_pos, attack_objp, &target_objp->pos, attack_objp->radius + target_objp->radius + 100.0f);
7591
7592         *accel = 1.0f;
7593 }
7594
7595 // get the current and desired horizontal separations between target
7596 void ai_chase_big_get_separations(object *attack_objp, object *target_objp, vector *horz_vec_to_target, float *desired_separation, float *cur_separation)
7597 {
7598         float temp, r_target, r_attacker, h_attacker, h_target;
7599         float perp_dist;
7600         vector vec_to_target;
7601         polymodel *pm;
7602
7603         // get parameters of ships (as cylinders - radius and height)
7604         // get radius of attacker (for rotations about forward)
7605         pm = model_get(Ships[attack_objp->instance].modelnum);
7606         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7607         r_attacker = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7608         r_attacker = max(temp, r_attacker);
7609         h_attacker = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7610
7611         // get radius of target (for rotations about forward)
7612         pm = model_get(Ships[attack_objp->instance].modelnum);
7613         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7614         r_target = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7615         r_target = max(temp, r_target);
7616         h_target = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7617
7618         // find separation between cylinders [if parallel]
7619         vm_vec_sub(&vec_to_target, &attack_objp->pos, &target_objp->pos);
7620
7621         // find the distance between centers along forward direction of ships
7622         perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.v.fvec);
7623
7624         // subtract off perp component to get "horizontal" separation vector between cylinders [ASSUMING parallel]
7625         vm_vec_scale_add(horz_vec_to_target, &vec_to_target, &target_objp->orient.v.fvec, -perp_dist);
7626         *cur_separation = vm_vec_mag_quick(horz_vec_to_target);
7627
7628         // choose "optimal" separation of 1000 + r_target + r_attacker
7629         *desired_separation = 1000 + r_target + r_attacker;
7630 }
7631
7632 void ai_chase_big_parallel_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7633 {
7634         int opposing;
7635         float temp, r_target, r_attacker, h_attacker, h_target;
7636         float separation, optimal_separation;
7637         vector  horz_vec_to_target;
7638         polymodel *pm;
7639
7640         // get parameters of ships (as cylinders - radius and height)
7641         // get radius of attacker (for rotations about forward)
7642         pm = model_get(Ships[attack_objp->instance].modelnum);
7643         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7644         r_attacker = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7645         r_attacker = max(temp, r_attacker);
7646         h_attacker = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7647
7648         // get radius of target (for rotations about forward)
7649         pm = model_get(Ships[attack_objp->instance].modelnum);
7650         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7651         r_target = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7652         r_target = max(temp, r_target);
7653         h_target = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7654
7655         // are we opposing (only when other ship is not moving)
7656         opposing = ( vm_vec_dotprod(&attack_objp->orient.v.fvec, &target_objp->orient.v.fvec) < 0 );
7657
7658         ai_chase_big_get_separations(attack_objp, target_objp, &horz_vec_to_target, &optimal_separation, &separation);
7659
7660         // choose dist (2000) so that we don't bash
7661         float dist = 2000;
7662         if (opposing) {
7663                 dist = - dist;
7664         }
7665
7666         // set the goal pos as dist forward from target along target forward
7667         vm_vec_scale_add(goal_pos, &target_objp->pos, &target_objp->orient.v.fvec, dist);
7668         // then add horizontal separation
7669         vm_vec_scale_add2(goal_pos, &horz_vec_to_target, optimal_separation/separation);
7670
7671         // find the distance between centers along forward direction of ships
7672         vector vec_to_target;
7673         vm_vec_sub(&vec_to_target, &target_objp->pos, &attack_objp->pos);
7674         float perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.v.fvec);
7675
7676         float match_accel = target_objp->phys_info.vel.xyz.z / Ship_info[Ships[attack_objp->instance].ship_info_index].max_vel.xyz.z;
7677         float length_scale = attack_objp->radius;
7678
7679         // if we're heading toward enemy ship, we want to keep going if we're ahead
7680         if (opposing) {
7681                 perp_dist = -perp_dist;
7682         }
7683
7684         if (perp_dist > 0) {
7685                 // falling behind, so speed up
7686                 *accel = match_accel + (1.0f - match_accel) / length_scale * (perp_dist);
7687         } else {
7688                 // up in front, so slow down
7689                 *accel = match_accel  - match_accel / length_scale * -perp_dist;
7690                 *accel = max(0.0f, *accel);
7691         }
7692
7693 }
7694
7695
7696 //      Return *goal_pos for one cruiser to attack another (big ship).
7697 //      Choose point fairly nearby that is not occupied by another cruiser.
7698 void ai_cruiser_chase_set_goal_pos(vector *goal_pos, object *pl_objp, object *en_objp)
7699 {
7700         ai_info *aip;
7701
7702         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
7703         float accel;
7704
7705         switch (aip->submode) {
7706         case SM_BIG_APPROACH:
7707                 // do approach stuff;
7708                 ai_chase_big_approach_set_goal(goal_pos, pl_objp, en_objp, &accel);
7709                 break;
7710
7711         case SM_BIG_CIRCLE:
7712                 // do circle stuff
7713                 ai_chase_big_circle_set_goal(goal_pos, pl_objp, en_objp, &accel);
7714                 break;
7715
7716         case SM_BIG_PARALLEL:
7717                 // do parallel stuff
7718                 ai_chase_big_parallel_set_goal(goal_pos, pl_objp, en_objp, &accel);
7719                 break;
7720         }
7721 }
7722
7723 int maybe_hack_cruiser_chase_abort()
7724 {
7725         ship                    *shipp = &Ships[Pl_objp->instance];     
7726         ship                    *eshipp = &Ships[En_objp->instance];
7727         ai_info         *aip = &Ai_info[shipp->ai_index];
7728
7729         // mission sm3-08, sathanos chasing collosus
7730         if ( stricmp(Mission_filename, "sm3-08.fs2") == 0 ) {
7731                 if (( stricmp(eshipp->ship_name, "colossus") == 0 ) || ( stricmp(shipp->ship_name, "colossus") == 0 )) {
7732                         // Changed so all big ships attacking the Colossus will not do the chase code.
7733                         // Did this so Beast wouldn't swerve away from Colossus. -- MK, 9/14/99
7734                         //if ( stricmp(shipp->ship_name, "Sathanas") == 0 ) {
7735                                 // do cool hack stuff here
7736                                 ai_clear_ship_goals( aip );
7737                                 aip->mode = AIM_NONE;
7738                                 return 1;
7739                         //}
7740                 }
7741         }
7742
7743         return 0;
7744 }
7745
7746 //      Make a big ship pursue another big ship.
7747 //      (Note, called "ai_cruiser_chase" because we already have ai_chase_big() which means fighter chases big ship.
7748 void ai_cruiser_chase()
7749 {
7750         ship_info       *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7751         ship                    *shipp = &Ships[Pl_objp->instance];     
7752         ai_info         *aip = &Ai_info[shipp->ai_index];
7753
7754         if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7755                 Int3(); //      Hmm, not a very big ship, how did we get in this function?
7756                 aip->mode = AIM_NONE;
7757                 return;
7758         }
7759
7760         if (En_objp->type != OBJ_SHIP) {
7761                 Int3();
7762                 return;
7763         }
7764
7765         if (En_objp->instance < 0) {
7766                 Int3();
7767                 return;
7768         }
7769
7770         ship                    *eshipp;
7771         ship_info       *esip;
7772
7773         eshipp = &Ships[En_objp->instance];
7774         esip = &Ship_info[eshipp->ship_info_index];
7775
7776         if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7777                 // Int3();      //      Hmm, we're big and we're pursuing something other than a big ship?
7778                 aip->mode = AIM_NONE;
7779                 return;
7780         }
7781
7782         vector  goal_pos;
7783         float turn_time = Ship_info[Ships[Pl_objp->instance].ship_info_index].srotation_time;
7784
7785         // kamikaze - ram and explode
7786         if (aip->ai_flags & AIF_KAMIKAZE) {
7787                 ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
7788                 accelerate_ship(aip, 1.0f);
7789         } 
7790         
7791         // really track down and chase
7792         else {
7793                 // check valid submode
7794                 Assert( (aip->submode == SM_ATTACK) || (aip->submode == SM_BIG_APPROACH) || (aip->submode == SM_BIG_CIRCLE) || (aip->submode == SM_BIG_PARALLEL) );
7795
7796                 // just entering, approach enemy ship
7797                 if (aip->submode == SM_ATTACK) {
7798                         aip->submode = SM_BIG_APPROACH;
7799                 }
7800
7801                 // desired accel
7802                 float accel = 0.0f;
7803                 vector *rvecp = NULL;
7804
7805                 switch (aip->submode) {
7806                 case SM_BIG_APPROACH:
7807                         // do approach stuff;
7808                         ai_chase_big_approach_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7809                         // maybe set rvec
7810                         break;
7811
7812                 case SM_BIG_CIRCLE:
7813                         // do circle stuff
7814                         ai_chase_big_circle_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7815                         // maybe set rvec
7816                         break;
7817
7818                 case SM_BIG_PARALLEL:
7819                         // do parallel stuff
7820                         ai_chase_big_parallel_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7821                         //maybe set rvec
7822                         break;
7823                 }
7824
7825
7826                 // now move as desired
7827                 ai_turn_towards_vector(&goal_pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0, rvecp);
7828                 accelerate_ship(aip, accel);
7829
7830
7831                 // maybe switch to new mode
7832                 vector vec_to_enemy;
7833                 float dist_to_enemy;
7834                 int moving = (En_objp->phys_info.vel.xyz.z > 0.5f);
7835                 vm_vec_sub(&vec_to_enemy, &En_objp->pos, &Pl_objp->pos);
7836                 dist_to_enemy = vm_vec_mag_quick(&vec_to_enemy);
7837
7838                 switch (aip->submode) {
7839                 case SM_BIG_APPROACH:
7840                         if ( dist_to_enemy < (Pl_objp->radius + En_objp->radius)*1.25f + 200.0f ) {
7841                                 // moving
7842                                 if (moving) {
7843                                         // if within 90 degrees of en forward, go into parallel, otherwise circle
7844                                         if ( vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0 ) {
7845                                                 aip->submode = SM_BIG_PARALLEL;
7846                                         }
7847                                 }
7848
7849                                 // otherwise cirle
7850                                 if ( !maybe_hack_cruiser_chase_abort() ) {
7851                                         aip->submode = SM_BIG_CIRCLE;
7852                                 }
7853                         }
7854                         break;
7855
7856                 case SM_BIG_CIRCLE:
7857                         // moving
7858                         if (moving) {
7859                                 vector temp;
7860                                 float desired_sep, cur_sep;
7861                                 // we're behind the enemy ship
7862                                 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec) > 0) {
7863                                         // and we're turning toward the enemy
7864                                         if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0) {
7865                                                 // get separation
7866                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7867                                                 // and the separation is > 0.9 desired
7868                                                 if (cur_sep > 0.9 * desired_sep) {
7869                                                         aip->submode = SM_BIG_PARALLEL;
7870                                                 }
7871                                         }
7872                                 }
7873                         } else {
7874                                 // still
7875                                 vector temp;
7876                                 float desired_sep, cur_sep;
7877                                 // we're behind the enemy ship
7878                                 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec) > 0) {
7879                                         // and we're turning toward the enemy
7880                                         if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0) {
7881                                                 // get separation
7882                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7883                                                 //and the separation is [0.9 to 1.1] desired
7884                                                 if ( (cur_sep > 0.9f * desired_sep) ) {
7885                                                         aip->submode = SM_BIG_PARALLEL;
7886                                                 }
7887                                         }
7888                                 }
7889                                 // in front of ship
7890                                 else {
7891                                         // and we're turning toward the enemy
7892                                         if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) < 0) {
7893                                                 // get separation
7894                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7895                                                 //and the separation is [0.9 to 1.1] desired
7896                                                 if ( (cur_sep > 0.9f * desired_sep) ) {
7897                                                         aip->submode = SM_BIG_PARALLEL;
7898                                                 }
7899                                         }
7900                                 }
7901                         }
7902                         break;
7903
7904                 case SM_BIG_PARALLEL:
7905                         // we're opposing
7906                         if ( vm_vec_dotprod(&Pl_objp->orient.v.fvec, &En_objp->orient.v.fvec) < 0 ) {
7907                                 // and the other ship is moving
7908                                 if (moving) {
7909                                         // and we no longer overlap
7910                                         if ( dist_to_enemy > (0.75 * (En_objp->radius + Pl_objp->radius)) ) {
7911                                                 aip->submode = SM_BIG_APPROACH;
7912                                         }
7913                                 }
7914                         }
7915                         break;
7916                 }
7917         }
7918 }
7919
7920 // --------------------------------------------------------------------------
7921 // Make object Pl_objp chase object En_objp
7922 void ai_chase()
7923 {
7924         float                   dist_to_enemy, time_to_enemy;
7925         float                   dot_to_enemy, dot_from_enemy, real_dot_to_enemy;
7926         vector          player_pos, enemy_pos, predicted_enemy_pos, real_vec_to_enemy, predicted_vec_to_enemy;
7927         ship_info       *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7928         ship                    *shipp = &Ships[Pl_objp->instance];
7929         ship_weapon     *swp = &shipp->weapons;
7930         ai_info         *aip = &Ai_info[shipp->ai_index];
7931         int                     enemy_sip_flags;
7932
7933         if (aip->mode != AIM_CHASE) {
7934                 Int3();
7935         }
7936
7937         if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
7938                 ai_cruiser_chase();
7939                 return;
7940         }
7941
7942         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER | SIF_ESCAPEPOD))) {
7943                 Warning(LOCATION, "Ship %s is not 'small', but is in chase mode.\nSwitching to AI=none.\n", shipp->ship_name);
7944                 aip->mode = AIM_NONE;
7945                 return;
7946         }
7947
7948         //nprintf(("AI", "%7s ", Submode_text[aip->submode]));
7949
7950         if ( En_objp->type == OBJ_SHIP ) {
7951                 enemy_sip_flags = Ship_info[Ships[En_objp->instance].ship_info_index].flags;
7952         } else {
7953                 enemy_sip_flags = 0;
7954         }
7955
7956         if ( enemy_sip_flags > 0 ) {
7957                 if (enemy_sip_flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
7958                         ai_big_chase();
7959                         return;
7960                 }
7961         }
7962
7963         //      If collided with target_objnum last frame, avoid that ship.
7964         //      This should prevent the embarrassing behavior of ships getting stuck on each other
7965         //      as if they were magnetically attracted. -- MK, 11/13/97.
7966         if ((aip->ai_flags & AIF_TARGET_COLLISION) || (aip->submode == SM_FLY_AWAY)) {
7967                 ai_chase_fly_away(Pl_objp, aip);
7968                 return;
7969         }
7970
7971         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
7972         dist_to_enemy = vm_vec_dist_quick(&player_pos, &enemy_pos);
7973         time_to_enemy = compute_time_to_enemy(dist_to_enemy, Pl_objp, En_objp);
7974         vm_vec_sub(&real_vec_to_enemy, &enemy_pos, &player_pos);
7975
7976         vm_vec_normalize(&real_vec_to_enemy);
7977
7978         real_dot_to_enemy = vm_vec_dot(&real_vec_to_enemy, &Pl_objp->orient.v.fvec);
7979
7980         int is_stealthy_ship = 0;
7981         if ( (enemy_sip_flags > 0) && (enemy_sip_flags & SIF_STEALTH) ) {
7982                 if ( ai_is_stealth_visible(Pl_objp, En_objp) != STEALTH_FULLY_TARGETABLE ) {
7983                         is_stealthy_ship = 1;
7984                 }
7985         }
7986
7987         // Can only acquire lock on a target that isn't hidden from sensors
7988         if ( !(Ships[En_objp->instance].flags & SF_HIDDEN_FROM_SENSORS) && !is_stealthy_ship ) {
7989                 update_aspect_lock_information(aip, &real_vec_to_enemy, dist_to_enemy, En_objp->radius);
7990         } else {
7991                 aip->current_target_is_locked = 0;
7992                 aip->ai_flags &= ~AIF_SEEK_LOCK;
7993         }
7994
7995         //      If seeking lock, try to point directly at ship, else predict position so lasers can hit it.
7996         //      If just acquired target, or target is not in reasonable cone, don't refine believed enemy position.
7997         if ((real_dot_to_enemy < 0.25f) || (aip->target_time < 1.0f) || (aip->ai_flags & AIF_SEEK_LOCK)) {
7998                 predicted_enemy_pos = enemy_pos;
7999         } else {
8000                 //      Set predicted_enemy_pos.
8001                 //      See if attacking a subsystem.
8002                 if (aip->targeted_subsys != NULL) {
8003                         Assert(En_objp->type == OBJ_SHIP);
8004                         ship_info       *esip = &Ship_info[Ships[En_objp->instance].ship_info_index];
8005                         if (get_shield_strength(En_objp)/esip->shields < HULL_DAMAGE_THRESHOLD_PERCENT) {
8006                                 //int   rval;
8007
8008                                 if (aip->targeted_subsys != NULL) {
8009                                         get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
8010                                         predicted_enemy_pos = enemy_pos;
8011                                         predicted_vec_to_enemy = real_vec_to_enemy;
8012                                 } else {
8013                                         set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8014                                         set_target_objnum(aip, -1);
8015                                 }
8016                                 // 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));
8017
8018                         } else {
8019                                 set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8020                                 // 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));
8021                         }
8022                 } else {
8023                         set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8024                 }
8025         }
8026
8027         vm_vec_sub(&predicted_vec_to_enemy, &predicted_enemy_pos, &player_pos);
8028
8029         vm_vec_normalize(&predicted_vec_to_enemy);
8030
8031         dot_to_enemy = vm_vec_dot(&Pl_objp->orient.v.fvec, &predicted_vec_to_enemy);
8032         dot_from_enemy= - vm_vec_dot(&En_objp->orient.v.fvec, &real_vec_to_enemy);
8033
8034         //
8035         //      Set turn and acceleration based on submode.
8036         //
8037         switch (aip->submode) {
8038         case SM_CONTINUOUS_TURN:
8039                 ai_chase_ct();
8040                 break;
8041
8042         case SM_STEALTH_FIND:
8043                 ai_stealth_find();
8044                 break;
8045
8046         case SM_STEALTH_SWEEP:
8047                 ai_stealth_sweep();
8048                 break;
8049
8050         case SM_ATTACK:
8051         case SM_SUPER_ATTACK:
8052         case SM_ATTACK_FOREVER:
8053                 if (vm_vec_dist_quick(&Pl_objp->pos, &predicted_enemy_pos) > 100.0f + En_objp->radius * 2.0f) {
8054                         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &predicted_enemy_pos, 10.0f))
8055                                 return;
8056                 }
8057
8058                 ai_chase_attack(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8059                 break;
8060
8061         case SM_EVADE_SQUIGGLE:
8062                 ai_chase_es(aip, sip);
8063                 break;
8064
8065         case SM_EVADE_BRAKE:
8066                 ai_chase_eb(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8067                 break;
8068
8069         case SM_EVADE:
8070                 evade_ship();
8071                 break;
8072
8073         case SM_AVOID:
8074                 avoid_ship();
8075                 break;
8076
8077         case SM_GET_BEHIND:
8078                 get_behind_ship(aip, sip, dist_to_enemy);
8079                 break;
8080
8081         case SM_GET_AWAY:               //      Used to get away from opponent to prevent endless circling.
8082                 ai_chase_ga(aip, sip);
8083                 break;
8084
8085         case SM_EVADE_WEAPON:
8086                 evade_weapon();
8087                 break;
8088
8089         default:
8090                 // Int3();
8091                 aip->last_attack_time = Missiontime;
8092                 aip->submode = SM_ATTACK;
8093                 aip->submode_start_time = Missiontime;
8094         }
8095
8096         //
8097         //      Maybe choose a new submode.
8098         //
8099         if ( (aip->submode != SM_AVOID) && (aip->submode != SM_ATTACK_FOREVER) ) {
8100                 //      If a very long time since attacked, attack no matter what!
8101                 if ( (aip->submode != SM_SUPER_ATTACK) && (aip->submode != SM_GET_AWAY) && !(aip->ai_flags & AIF_STEALTH_PURSIUT) ) {
8102                         if (Missiontime - aip->last_attack_time > i2f(6)) {
8103                                 aip->submode = SM_SUPER_ATTACK;
8104                                 aip->submode_start_time = Missiontime;
8105                                 aip->last_attack_time = Missiontime;
8106                         }
8107                 }
8108
8109                 //      If a collision is expected, pull out!
8110                 //      If enemy is pointing away and moving a bit, don't worry about collision detection.
8111                 if ((dot_from_enemy > 0.5f) || (En_objp->phys_info.speed < 10.0f)) {
8112                         if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 4.0f)) {
8113                                 if ((Missiontime - aip->last_hit_time > F1_0*4) && (dist_to_enemy < Pl_objp->radius*2 + En_objp->radius*2)) {
8114                                         accelerate_ship(aip, -1.0f);
8115                                 } else {
8116                                         aip->submode = SM_AVOID;
8117                                         aip->submode_start_time = Missiontime;
8118                                 }
8119                         }
8120                 }
8121         }
8122
8123         switch (aip->submode) {
8124         case SM_CONTINUOUS_TURN:
8125                 if (Missiontime - aip->submode_start_time > i2f(3)) {
8126                         aip->last_attack_time = Missiontime;
8127                         aip->submode = SM_ATTACK;
8128                         aip->submode_start_time = Missiontime;
8129                 }
8130                 break;
8131
8132         case SM_ATTACK:
8133                 // if taraget is stealth and stealth not visible, then enter stealth find mode
8134                 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8135                         aip->submode = SM_STEALTH_FIND;
8136                         aip->submode_start_time = Missiontime;
8137                         aip->submode_parm0 = SM_SF_AHEAD;
8138                 } 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)) {
8139                         aip->submode = SM_SUPER_ATTACK;
8140                         aip->submode_start_time = Missiontime;
8141                         aip->last_attack_time = Missiontime;
8142                 } else if ((Missiontime - aip->last_hit_target_time > i2f(6)) &&
8143                         (dist_to_enemy < 500.0f) && (dot_to_enemy < 0.2f) &&
8144                         (frand() < (float) Game_skill_level/NUM_SKILL_LEVELS)) {
8145                         aip->submode = SM_GET_AWAY;
8146                         aip->submode_start_time = Missiontime;
8147                         aip->last_hit_target_time = Missiontime;
8148                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP)
8149                         && (dot_to_enemy < dot_from_enemy)
8150                         && (En_objp->phys_info.speed > 15.0f) 
8151                         && (dist_to_enemy < 200.0f) 
8152                         && (dist_to_enemy > 50.0f)
8153                         && (dot_to_enemy < 0.1f)
8154                         && (Missiontime - aip->submode_start_time > i2f(2))) {
8155                         aip->submode = SM_EVADE_BRAKE;
8156                         aip->submode_start_time = Missiontime;
8157                 } else if ((dot_to_enemy > 0.2f) && (dot_from_enemy > -0.2f) && (dot_from_enemy < 0.1f)) {
8158                         aip->submode = SM_GET_BEHIND;
8159                         aip->submode_start_time = Missiontime;
8160                 } 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)) {
8161                         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;
8162                                 aip->submode_start_time = Missiontime;
8163                                 aip->last_hit_target_time = Missiontime;
8164                         } else {
8165                                 aip->submode = SM_EVADE_SQUIGGLE;
8166                                 aip->submode_start_time = Missiontime;
8167                         }
8168                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP) && (Missiontime - aip->submode_start_time > F1_0*2)) {
8169                         if ((dot_to_enemy < 0.8f) && (dot_from_enemy > dot_to_enemy)) {
8170                                 if (frand() > 0.5f) {
8171                                         aip->submode = SM_CONTINUOUS_TURN;
8172                                         aip->submode_parm0 = myrand() & 0x0f;
8173                                         aip->submode_start_time = Missiontime;
8174                                 } else {
8175                                         aip->submode = SM_EVADE;
8176                                         aip->submode_start_time = Missiontime;
8177                                 }
8178                         } else {
8179                                 aip->submode_start_time = Missiontime;
8180                         }
8181                 }
8182
8183                 aip->last_attack_time = Missiontime;
8184
8185                 break;
8186                 
8187         case SM_EVADE_SQUIGGLE:
8188                 if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > 300.0f)) {
8189                         if ((dist_to_enemy < 100.0f) && (dot_to_enemy < 0.0f) && (dot_from_enemy > 0.5f)) {
8190                                 aip->submode = SM_EVADE_BRAKE;
8191                                 aip->submode_start_time = Missiontime;
8192                         } else {
8193                                 aip->last_attack_time = Missiontime;
8194                                 aip->submode = SM_ATTACK;
8195                                 aip->submode_start_time = Missiontime;
8196                         }
8197                 }
8198                 break;
8199         
8200         case SM_EVADE_BRAKE:
8201                 if ((dist_to_enemy < 15.0f) || (En_objp->phys_info.speed < 10.0f)) {
8202                         aip->submode = SM_AVOID;
8203                         aip->submode_start_time = Missiontime;
8204                 } else if ((dot_to_enemy > 0.9f) || ((dot_from_enemy > 0.9f) && (Missiontime - aip->submode_start_time > i2f(1)))) {
8205                         aip->last_attack_time = Missiontime;
8206                         aip->submode = SM_ATTACK;
8207                         aip->submode_start_time = Missiontime;
8208                 } else if (Missiontime - aip->submode_start_time > i2f(4)) {
8209                         aip->last_attack_time = Missiontime;
8210                         aip->submode = SM_ATTACK;
8211                         aip->submode_start_time = Missiontime;
8212                 }
8213                 break;
8214
8215         case SM_EVADE:
8216                 //      Modified by MK on 5/5/97 to keep trying to regain attack mode.  It's what a human would do.
8217                 if ((dot_to_enemy < 0.2f) && (dot_from_enemy < 0.8f) && (dist_to_enemy < 100.0f) && (En_objp->phys_info.speed > 15.0f)) {
8218                         aip->last_attack_time = Missiontime;
8219                         aip->submode = SM_EVADE_BRAKE;
8220                         aip->submode_start_time = Missiontime;
8221                 } else if (((dot_to_enemy > dot_from_enemy - 0.1f)
8222                         && (Missiontime > aip->submode_start_time + i2f(1)))
8223                         || (dist_to_enemy > 150.0f + 2*(Pl_objp->radius + En_objp->radius))) {
8224                         aip->last_attack_time = Missiontime;
8225                         aip->submode = SM_ATTACK;
8226                         aip->submode_start_time = Missiontime;
8227                 } else if (Missiontime - aip->submode_start_time > i2f(2))
8228                         if (dot_from_enemy > 0.8f) {
8229                                 aip->submode = SM_EVADE_SQUIGGLE;
8230                                 aip->submode_start_time = Missiontime;
8231                         }
8232
8233                 break;
8234
8235         case SM_SUPER_ATTACK:
8236                 // if stealth and invisible, enter stealth find mode
8237                 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8238                         aip->submode = SM_STEALTH_FIND;
8239                         aip->submode_start_time = Missiontime;
8240                         aip->submode_parm0 = SM_SF_AHEAD;
8241                 } 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) )) {
8242                         aip->ai_flags &= ~AIF_ATTACK_SLOWLY;    //      Just in case, clear here.
8243
8244                         switch (myrand() % 5) {
8245                         case 0:
8246                                 aip->submode = SM_CONTINUOUS_TURN;
8247                                 aip->submode_start_time = Missiontime;
8248                                 break;
8249                         case 1:
8250                                 aip->submode_start_time = Missiontime;  //      Stay in super attack mode
8251                                 break;
8252                         case 2:
8253                         case 3:
8254                                 if (frand() < (float) 0.5f * (aip->ai_class + Game_skill_level)/(Num_ai_classes + NUM_SKILL_LEVELS)) {
8255                                         aip->submode = SM_GET_AWAY;
8256                                         aip->submode_start_time = Missiontime;
8257                                 } else {
8258                                         aip->submode = SM_EVADE;
8259                                         aip->submode_start_time = Missiontime;
8260                                 }
8261                                 break;
8262                         case 4:
8263                                 if (dot_from_enemy + (NUM_SKILL_LEVELS - Game_skill_level) * 0.1f > dot_to_enemy) {     //      Less likely to GET_AWAY at lower skill levels.
8264                                         aip->submode = SM_EVADE;
8265                                         aip->submode_start_time = Missiontime;
8266                                 } else {
8267                                         aip->submode = SM_GET_AWAY;
8268                                         aip->submode_start_time = Missiontime;
8269                                 }
8270                                 break;
8271                         default:
8272                                 Int3(); //      Impossible!
8273                         }
8274                 }
8275
8276                 aip->last_attack_time = Missiontime;
8277
8278                 break;
8279
8280         case SM_AVOID:
8281                 if ((dot_to_enemy > -0.2f) && (dist_to_enemy / (dot_to_enemy + 0.3f) < 100.0f)) {
8282                         aip->submode_start_time = Missiontime;
8283                 } else if (Missiontime - aip->submode_start_time > i2f(1)/2)
8284                         if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 3.0f)) {
8285                                 aip->submode_start_time = Missiontime;
8286                         } else {
8287                                 aip->submode = SM_GET_BEHIND;
8288                                 aip->submode_start_time = Missiontime;
8289                         }
8290
8291                 break;
8292
8293         case SM_GET_BEHIND:
8294                 if ((dot_from_enemy < -0.7f) || (Missiontime - aip->submode_start_time > i2f(2))) {
8295                         aip->submode = SM_ATTACK;
8296                         aip->submode_start_time = Missiontime;
8297                         aip->last_attack_time = Missiontime;
8298                 }
8299                 break;
8300
8301         case SM_GET_AWAY:
8302                 if (Missiontime - aip->submode_start_time > i2f(2)) {
8303                         float   rand_dist;
8304
8305                         rand_dist = ((Missiontime >> 17) & 0x03) * 100.0f + 200.0f;     //      Some value in 200..500
8306                         if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > rand_dist) || (dot_from_enemy < 0.4f)) {
8307                                 aip->ai_flags |= AIF_ATTACK_SLOWLY;
8308                                 aip->submode = SM_ATTACK;
8309                                 aip->time_enemy_in_range = 2.0f;                //      Cheat.  Presumably if they were running away from you, they were monitoring you!
8310                                 aip->submode_start_time = Missiontime;
8311                                 aip->last_attack_time = Missiontime;
8312                         }
8313                 }
8314                 break;
8315
8316         case SM_EVADE_WEAPON:
8317                 if (aip->danger_weapon_objnum == -1) {
8318                         aip->submode = SM_ATTACK;
8319                         aip->submode_start_time = Missiontime;
8320                         aip->last_attack_time = Missiontime;
8321                 }
8322                 break;
8323
8324         // Either change to SM_ATTACK or AIM_FIND_STEALTH
8325         case SM_STEALTH_FIND:
8326                 // if time > 5 sec change mode to sweep
8327                 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8328                         aip->submode = SM_ATTACK;
8329                         aip->submode_start_time = Missiontime;
8330                         aip->last_attack_time = Missiontime;
8331                         // sweep if I can't find in 5 sec or bail from find
8332                 } else if ( ((Missiontime - aip->submode_start_time) > i2f(5)) || (aip->submode_parm0 == SM_SF_BAIL) ) {
8333                         // begin sweep mode
8334                         aip->submode = SM_STEALTH_SWEEP;
8335                         aip->submode_start_time = Missiontime;
8336                         aip->last_attack_time = Missiontime;
8337                         aip->submode_parm0 = SM_SS_SET_GOAL;
8338                 }
8339                 break;
8340
8341         case SM_STEALTH_SWEEP:
8342                 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8343                         aip->submode = SM_ATTACK;
8344                         aip->submode_start_time = Missiontime;
8345                         aip->last_attack_time = Missiontime;
8346                 } else if ( (timestamp() - aip->stealth_last_visible_stamp) < 5000 ) {
8347                         // go back to find mode
8348                         aip->submode = SM_STEALTH_FIND;
8349                         aip->submode_start_time = Missiontime;
8350                         aip->submode_parm0 = SM_SF_AHEAD;
8351                 } else if ( /*(Missiontime - aip->submode_start_time) > i2f(30) || */(aip->submode_parm0 == SM_SS_DONE) ) {
8352                         // set target objnum = -1
8353                         set_target_objnum(aip, -1);
8354
8355                         // set submode to attack
8356                         aip->submode = SM_ATTACK;
8357                         aip->submode_start_time = Missiontime;
8358                         aip->last_attack_time = Missiontime;
8359                 }
8360                 break;
8361
8362         case SM_ATTACK_FOREVER: //      Engines blown, just attack.
8363                 break;
8364
8365         default:
8366                 //Int3();
8367                 aip->submode = SM_ATTACK;
8368                 aip->last_attack_time = Missiontime;
8369
8370                 aip->submode_start_time = Missiontime;
8371         }
8372
8373         //
8374         //      Maybe fire primary weapon and update time_enemy_in_range
8375         //
8376         //nprintf(("AI", "time_enemy_in_range = %7.3f, dot = %7.3f\n", aip->time_enemy_in_range, dot_to_enemy));
8377
8378         if (aip->mode != AIM_EVADE) {
8379                 if (dot_to_enemy > 0.95f - 0.5f * En_objp->radius/max(1.0f, En_objp->radius + dist_to_enemy)) {
8380                         aip->time_enemy_in_range += flFrametime;
8381                         
8382                         //      Chance of hitting ship is based on dot product of firing ship's forward vector with vector to ship
8383                         //      and also the size of the target relative to distance to target.
8384                         if (dot_to_enemy > max(0.5f, 0.90f + aip->ai_accuracy/10.0f - En_objp->radius/max(1.0f,dist_to_enemy))) {
8385
8386                                 ship *temp_shipp;
8387                                 ship_weapon *tswp;
8388
8389                                 temp_shipp = &Ships[Pl_objp->instance];
8390                                 tswp = &temp_shipp->weapons;
8391                                 if ( tswp->num_primary_banks > 0 ) {
8392                                         float   scale;
8393                                         Assert(tswp->current_primary_bank < tswp->num_primary_banks);
8394                                         weapon_info     *pwip = &Weapon_info[tswp->primary_bank_weapons[tswp->current_primary_bank]];
8395
8396                                         //      Less likely to fire if far away and moving.
8397                                         scale = pwip->max_speed/(En_objp->phys_info.speed + pwip->max_speed);
8398                                         if (scale > 0.6f)
8399                                                 scale = (scale - 0.6f) * 1.5f;
8400                                         else
8401                                                 scale = 0.0f;
8402                                         if (dist_to_enemy < pwip->max_speed * (1.0f + scale)) {
8403                                                 ai_fire_primary_weapon(Pl_objp);
8404                                         }
8405
8406                                         //      Don't fire secondaries at a protected ship.
8407                                         if (!(En_objp->flags & OF_PROTECTED)) {
8408                                                 ai_choose_secondary_weapon(Pl_objp, aip, En_objp);
8409                                                 int current_bank = tswp->current_secondary_bank;
8410                                                 weapon_info     *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8411
8412                                                 if (current_bank > -1) {
8413                                                         if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8414                                                                 if (timestamp_until(swp->next_secondary_fire_stamp[current_bank]) > swip->fire_wait*1000.0f) {
8415                                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (swip->fire_wait*1000.0f));
8416                                                                 }
8417                                                         }
8418
8419                                                         if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
8420                                                                 if (tswp->current_secondary_bank >= 0) {
8421                                                                         weapon_info     *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8422                                                                         float firing_range;
8423                                                                         
8424                                                                         if (swip->wi_flags & WIF_BOMB)
8425                                                                                 firing_range = swip->max_speed * swip->lifetime * 0.75f;
8426                                                                         else
8427                                                                                 firing_range = swip->max_speed * swip->lifetime * (Game_skill_level + 1 + aip->ai_class/2)/NUM_SKILL_LEVELS;
8428
8429                                                                         // reduce firing range in nebula
8430                                                                         extern int Nebula_sec_range;
8431                                                                         if ((The_mission.flags & MISSION_FLAG_FULLNEB) && Nebula_sec_range) {
8432                                                                                 firing_range *= 0.8f;
8433                                                                         }
8434
8435                                                                         //      If firing a spawn weapon, distance doesn't matter.
8436                                                                         int     spawn_fire = 0;
8437
8438                                                                         if (swip->wi_flags & WIF_SPAWN) {
8439                                                                                 int     count;
8440
8441                                                                                 count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(Pl_objp)), &Pl_objp->pos, 1000.0f);
8442
8443                                                                                 if (count > 3)
8444                                                                                         spawn_fire = 1;
8445                                                                                 else if (count >= 1) {
8446                                                                                         float hull_percent = Pl_objp->hull_strength/sip->initial_hull_strength;
8447
8448                                                                                         if (hull_percent < 0.01f)
8449                                                                                                 hull_percent = 0.01f;
8450
8451                                                                                         if (frand() < 0.25f/(30.0f*hull_percent) * count)       //      With timestamp below, this means could fire in 30 seconds if one enemy.
8452                                                                                                 spawn_fire = 1;
8453                                                                                 }
8454                                                                         }
8455
8456                                                                         if (spawn_fire || (dist_to_enemy < firing_range)) {
8457                                                                                 if (ai_fire_secondary_weapon(Pl_objp)) {
8458                                                                                         //      Only if weapon was fired do we specify time until next fire.  If not fired, done in ai_fire_secondary...
8459                                                                                         float t;
8460                                                                                         
8461                                                                                         if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8462                                                                                                 t = swip->fire_wait;
8463                                                                                         } else {
8464                                                                                                 t = set_secondary_fire_delay(aip, temp_shipp, swip);
8465                                                                                         }
8466                                                                                         //nprintf(("AI", "Next secondary to be fired in %7.3f seconds.\n", t));
8467                                                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
8468                                                                                 }
8469                                                                         } else {
8470                                                                                 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
8471                                                                         }
8472                                                                 }
8473                                                         }
8474                                                 }
8475                                         }
8476                                 }
8477                         }
8478                 } else {
8479                         aip->time_enemy_in_range *= (1.0f - flFrametime);
8480                 }
8481         } else
8482                 aip->time_enemy_in_range *= (1.0f - flFrametime);
8483
8484 }
8485
8486 //      Make the object *objp move so that the point *dp on the object moves towards the point *vp
8487 //      Return distance.
8488 void dock_move_towards_point(object *objp, vector *dp, vector *vp, float speed_scale, float other_obj_speed = 0.0f)
8489 {
8490         physics_info    *pi = &objp->phys_info;
8491         float                           dist;                   //      dist to goal
8492         vector                  v2g;                    //      vector to goal
8493         vector                  abs_pnt;                //      location of dock point, ie objp->pos + db
8494
8495         if (dp == NULL)
8496                 abs_pnt = objp->pos;
8497         else
8498                 vm_vec_add(&abs_pnt, &objp->pos, dp);
8499
8500         dist = vm_vec_dist_quick(vp, &abs_pnt);
8501         if (dist > 0.0f) {
8502                 float   speed;
8503
8504                 dist = vm_vec_normalized_dir(&v2g, vp, &abs_pnt);
8505                 speed = fl_sqrt(dist) * speed_scale;
8506                 if (other_obj_speed < MAX_REPAIR_SPEED*0.75f)
8507                         speed += other_obj_speed;
8508                 else
8509                         speed += MAX_REPAIR_SPEED*0.75f;
8510
8511                 vm_vec_copy_scale(&pi->desired_vel, &v2g, speed);
8512         } else
8513                 vm_vec_zero(&pi->desired_vel);
8514 }
8515
8516 //      Set the orientation in the global reference frame for an object to attain
8517 //      to dock with another object.
8518 //      *dom            resultant global matrix
8519 //      *db_dest        pointer to destination docking bay information
8520 //      *db_src pointer to source docking bay information
8521 //      *dorient        pointer to global orientation of docking bay (ie, the dockee object's orient)
8522 //      *sorient        pointer to global orientation of docker
8523 void set_goal_dock_orient(matrix *dom, dock_bay *db_dest, dock_bay *db_src, matrix *dorient, matrix *sorient)
8524 {
8525         vector  fvec, uvec;
8526         matrix  m1, m2, m3;
8527
8528         //      Compute the global orientation of the docker's (dest) docking bay.
8529         fvec = db_dest->norm[0];
8530         vm_vec_negate(&fvec);
8531
8532         vm_vec_normalized_dir(&uvec, &db_dest->pnt[1], &db_dest->pnt[0]);
8533         vm_vector_2_matrix(&m1, &fvec, &uvec, NULL);
8534
8535         vm_matrix_x_matrix(&m3, dorient, &m1);
8536
8537         //      Compute the matrix given by the source docking bay.
8538         //      Pre-multiply the orientation of the source object (sorient) by the transpose
8539         //      of the docking bay's orientation, ie unrotate the source object's matrix.
8540         fvec = db_src->norm[0];
8541         vm_vec_normalized_dir(&uvec, &db_src->pnt[1], &db_src->pnt[0]);
8542         vm_vector_2_matrix(&m2, &fvec, &uvec, NULL);
8543         vm_transpose(&m2);
8544
8545         vm_matrix_x_matrix(dom, &m3, &m2);
8546 }
8547
8548 #define DOCK_BACKUP_RETURN_VAL  99999.9f
8549
8550 //      Make objp dock with dobjp
8551 //      Returns distance to goal, defined as distance between corresponding dock points, plus 10.0f * rotational velocity vector (DOA_DOCK only)
8552 //      DOA_APPROACH    means   approach point aip->path_cur
8553 //      DOA_DOCK                        means dock
8554 //      DOA_UNDOCK_1    means undock, moving to point nearest dock bay
8555 //      DOA_UNDOCK_2    means undock, moving to point nearest dock bay and facing away from ship
8556 //      DOA_DOCK_STAY   means rigidly maintain position in dock bay.
8557 float dock_orient_and_approach(object *objp, object *dobjp, int dock_mode)
8558 {
8559         ship_info       *sip0, *sip1;
8560         polymodel       *pm0, *pm1;
8561         ai_info         *aip;
8562         matrix          dom, nm;
8563         vector          goal_point, docker_point;
8564         float                   fdist = UNINITIALIZED_VALUE;
8565         int                     docker_index, dockee_index;             // index into docking_bays[] array for objects docking
8566                                                                                                                                 // docker is Pl_objp -- dockee is dobjp
8567         aip = &Ai_info[Ships[objp->instance].ai_index];
8568
8569         //      If dockee has moved much, then path will be recreated.
8570         //      Might need to change state if moved too far.
8571         if ((dock_mode != DOA_DOCK_STAY) && (dock_mode != DOA_DOCK)) {
8572                 if (maybe_recreate_path(objp, &Ai_info[Ships[objp->instance].ai_index], 0) > 5.0f) {
8573 /*                      if (dock_mode == DOA_APPROACH) {
8574                                 return DOCK_BACKUP_RETURN_VAL;
8575                         } else if (dock_mode == DOA_DOCK) {
8576                                 return DOCK_BACKUP_RETURN_VAL;          
8577                         }
8578 */              }
8579         }
8580
8581         objp->phys_info.forward_thrust = 0.0f;          //      Kill thrust so we don't have a sputtering thruster.
8582
8583         sip0 = &Ship_info[Ships[objp->instance].ship_info_index];
8584         sip1 = &Ship_info[Ships[dobjp->instance].ship_info_index];
8585         pm0 = model_get( sip0->modelnum );
8586         pm1 = model_get( sip1->modelnum );
8587
8588         docker_index = aip->dock_index;
8589         dockee_index = aip->dockee_index;
8590
8591         Assert( docker_index >= 0 );
8592         Assert( dockee_index >= 0 );
8593
8594         Assert(pm0->docking_bays[docker_index].num_slots == 2);
8595         Assert(pm1->docking_bays[dockee_index].num_slots == 2);
8596
8597         float speed_scale = 1.0f;
8598         if (sip0->flags & SIF_SUPPORT) {
8599                 speed_scale = 3.0f;
8600         }
8601
8602         switch (dock_mode) {
8603         case DOA_APPROACH:
8604                 {
8605                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8606                         return 9999.9f;
8607                 }
8608                 
8609                 //      Compute the desired global orientation matrix for the docker's station.
8610                 //      That is, the normal vector of the docking station must be the same as the
8611                 //      forward vector and the vector between its two points must be the uvec.
8612                 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8613
8614                 //      Compute new orientation matrix and update rotational velocity.
8615                 vector  w_in, w_out, vel_limit, acc_limit;
8616                 float           tdist, mdist, ss1;
8617
8618                 w_in = objp->phys_info.rotvel;
8619                 vel_limit = objp->phys_info.max_rotvel;
8620                 vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8621                 
8622                 if (sip0->flags & SIF_SUPPORT)
8623                         vm_vec_scale(&acc_limit, 2.0f);
8624
8625                 // 1 at end of line prevent overshoot
8626                 vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit, 1);
8627                 objp->phys_info.rotvel = w_out;
8628                 objp->orient = nm;
8629
8630                 //      Translate towards goal and note distance to goal.
8631                 goal_point = Path_points[aip->path_cur].pos;
8632                 mdist = ai_matrix_dist(&objp->orient, &dom);
8633                 tdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8634
8635                 //      If translation is badly lagging rotation, speed up translation.
8636                 if (mdist > 0.1f) {
8637                         ss1 = tdist/(10.0f * mdist);
8638                         if (ss1 > 2.0f)
8639                                 ss1 = 2.0f;
8640                 } else
8641                         ss1 = 2.0f;
8642
8643                 // nprintf(("AI", "speed scale = %7.3f\n", ss1));
8644                 speed_scale *= 1.0f + ss1;
8645
8646                 dock_move_towards_point(objp, NULL, &goal_point, speed_scale, dobjp->phys_info.speed);
8647
8648                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8649
8650                 //      Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8651                 // nprintf(("AI", "matrix dist = %7.3f, threshold = %7.3f\n", mdist, 2*flFrametime));
8652                 fdist += 2.0f * mdist;
8653
8654                 break;
8655         }
8656         case DOA_DOCK:
8657                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8658                         return 9999.9f;
8659                 }
8660         case DOA_DOCK_STAY:
8661                 //      Compute the desired global orientation matrix for the docker's station.
8662                 //      That is, the normal vector of the docking station must be the same as the
8663                 //      forward vector and the vector between its two points must be the uvec.
8664                 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8665
8666                 //      Compute distance between dock bay points.
8667                 vector  db0, db1, db2, db3;
8668
8669                 vm_vec_unrotate(&db0, &pm0->docking_bays[docker_index].pnt[0], &objp->orient);
8670                 vm_vec_add2(&db0, &objp->pos);
8671
8672                 vm_vec_unrotate(&db1, &pm0->docking_bays[docker_index].pnt[1], &objp->orient);
8673                 vm_vec_add2(&db1, &objp->pos);
8674
8675                 vm_vec_unrotate(&db2, &pm1->docking_bays[dockee_index].pnt[0], &dobjp->orient);
8676                 vm_vec_add2(&db2, &dobjp->pos);
8677
8678                 vm_vec_unrotate(&db3, &pm1->docking_bays[dockee_index].pnt[1], &dobjp->orient);
8679                 vm_vec_add2(&db3, &dobjp->pos);
8680
8681                 vm_vec_avg(&goal_point, &db2, &db3);
8682
8683                 vm_vec_avg(&docker_point, &db0, &db1);
8684                 vm_vec_sub2(&docker_point, &objp->pos);
8685
8686                 if (dock_mode == DOA_DOCK) {
8687                         vector  t1, t2;
8688                         vector  w_in, w_out, vel_limit, acc_limit;
8689
8690                         fdist = vm_vec_dist_quick(vm_vec_avg(&t1, &db0, &db1), vm_vec_avg(&t2, &db2, &db3));
8691
8692                         //      Compute new orientation matrix and update rotational velocity.
8693                         w_in = objp->phys_info.rotvel;
8694                         vel_limit = objp->phys_info.max_rotvel;
8695                         vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8696
8697                         if (sip0->flags & SIF_SUPPORT)
8698                                 vm_vec_scale(&acc_limit, 2.0f);
8699
8700                         vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit);
8701                         objp->phys_info.rotvel = w_out;
8702                         objp->orient = nm;
8703
8704                         //      Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8705                         fdist += 10.0f * vm_vec_mag_quick(&w_out);
8706
8707                         dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale, dobjp->phys_info.speed);
8708                 } else {
8709                         Assert(dock_mode == DOA_DOCK_STAY);
8710                         objp->orient = dom;
8711                         vector  temp;
8712                         vm_vec_sub(&temp, &goal_point, &docker_point);
8713                         vm_vec_sub(&objp->pos, &goal_point, &docker_point);
8714                 }
8715
8716                 break;
8717         case DOA_UNDOCK_1: {
8718                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8719                         return 9999.9f;
8720                 }
8721
8722                 //      Undocking.
8723                 //      Move to point on dock path nearest to dock station.
8724                 Assert(aip->path_length >= 2);
8725                 goal_point = Path_points[aip->path_start + aip->path_length-2].pos;
8726
8727                 vm_vec_zero(&docker_point);
8728                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8729
8730                 dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale);
8731
8732                 break;
8733                           }
8734
8735         case DOA_UNDOCK_2: {
8736                 //      Undocking.
8737                 //      Move to point on dock path nearest to dock station and orient away from big ship.
8738                 int             desired_index;
8739
8740                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8741                         return 9999.9f;
8742                 }
8743
8744                 Assert(aip->path_length >= 2);
8745 //              if (aip->path_length >= 3)
8746 //                      desired_index = aip->path_length-3;
8747 //              else
8748                         desired_index = aip->path_length-2;
8749
8750                 goal_point = Path_points[aip->path_start + desired_index].pos;
8751
8752                 dock_move_towards_point(objp, NULL, &goal_point, speed_scale);
8753
8754                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8755                 break;
8756                           }
8757         case DOA_UNDOCK_3: {
8758                 float           dist, goal_dist;
8759                 vector  away_vec;
8760
8761                 goal_dist = objp->radius + dobjp->radius + 25.0f;
8762
8763                 dist = vm_vec_normalized_dir(&away_vec, &objp->pos, &dobjp->pos);
8764                 vm_vec_scale_add(&goal_point, &dobjp->pos, &away_vec, goal_dist);
8765                 if (vm_vec_dist_quick(&goal_point, &dobjp->pos) < vm_vec_dist_quick(&objp->pos, &dobjp->pos))
8766                         fdist = 0.0f;
8767                 else {
8768                         float   dot, accel;
8769                         float turn_time = Ship_info[Ships[objp->instance].ship_info_index].srotation_time;
8770                         ai_turn_towards_vector(&goal_point, objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
8771
8772                         dot = vm_vec_dot(&objp->orient.v.fvec, &away_vec);
8773                         accel = 0.1f;
8774                         if (dot > accel)
8775                                 accel = dot;
8776                         if (dist > goal_dist/2)
8777                                 accel *= 1.2f - 0.5f*goal_dist/dist;
8778
8779                         accelerate_ship(aip, accel);
8780                         fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8781                 }
8782
8783                 break;
8784                                                          }
8785         }
8786
8787 #ifndef NDEBUG
8788         //      For debug purposes, compute global orientation of both dock vectors and show
8789         //      how close they are.
8790         vector  d0, d1;
8791
8792         vm_vec_unrotate(&d0, &pm0->docking_bays[docker_index].norm[0], &objp->orient);
8793         vm_vec_unrotate(&d1, &pm1->docking_bays[dockee_index].norm[0], &dobjp->orient);
8794
8795         //nprintf(("AI", "or/app: dist = %7.3f/%7.3f, dot = %7.3f, global dot = %7.3f\n", 
8796         //      vm_vec_dist_quick(&goal_point, &objp->pos), fdist,
8797         //      vm_vec_dot(&objp->orient.v.fvec, &dom.v.fvec), 
8798         //      vm_vec_dot(&d0, &d1)));
8799 #endif
8800
8801         // -- Note, A lot of callers don't care about fdist, so OK to return ERROR value: Assert(fdist != UNINITIALIZED_VALUE);
8802         return fdist;
8803
8804 }
8805
8806 void debug_find_guard_object()
8807 {
8808         ship                    *shipp = &Ships[Pl_objp->instance];     
8809         object          *objp;
8810
8811         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
8812                 if ((Pl_objp != objp) && (objp->type == OBJ_SHIP)) {
8813                         if (objp->instance != -1) {
8814                                 if (Ships[objp->instance].team == shipp->team)  {
8815                                         // nprintf(("AI", "Setting guard object for %s to %s\n", shipp->ship_name, Ships[objp->instance].ship_name));
8816                                         ai_set_guard_object(Pl_objp, objp);
8817                                 }
8818                         }
8819                 }
8820         }
8821
8822 }
8823
8824 //      Given an object number, return the number of ships attacking it.
8825 int num_ships_attacking(int objnum)
8826 {
8827         object  *objp;
8828         ship_obj        *so;
8829         int             count = 0;
8830
8831         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8832                 objp = &Objects[so->objnum];
8833                 if (objp->instance != -1) {
8834                         ai_info *aip;
8835                         aip = &Ai_info[Ships[objp->instance].ai_index];
8836
8837                         if ((aip->mode == AIM_CHASE) && (aip->target_objnum == objnum))
8838                                 if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team)
8839                                         count++;
8840                 }
8841         }
8842
8843         return count;
8844 }
8845
8846 //      For all objects attacking object #objnum, remove the one that is farthest away.
8847 //      Do this by resuming previous behavior, if any.  If not, set target_objnum to -1.
8848 void remove_farthest_attacker(int objnum)
8849 {
8850         object  *objp, *objp2, *farthest_objp;
8851         ship_obj        *so;
8852         float           farthest_dist;
8853
8854         objp2 = &Objects[objnum];
8855
8856         farthest_dist = 9999999.9f;
8857         farthest_objp = NULL;
8858
8859         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8860                 objp = &Objects[so->objnum];
8861                 if ( !(objp->flags & OF_PLAYER_SHIP)) {
8862                         if (objp->instance != -1) {
8863                                 ai_info *aip2;
8864
8865                                 aip2 = &Ai_info[Ships[objp->instance].ai_index];
8866
8867                                 if ((aip2->mode == AIM_CHASE) && (aip2->target_objnum == objnum)) {
8868                                         if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team) {
8869                                                 float   dist;
8870
8871                                                 dist = vm_vec_dist_quick(&objp->pos, &objp2->pos);
8872                                                 if (dist < farthest_dist) {
8873                                                         farthest_dist = dist;
8874                                                         farthest_objp = objp;
8875                                                 }
8876                                         }
8877                                 }
8878                         }
8879                 }
8880         }
8881
8882         if (farthest_objp != NULL) {
8883                 ai_info *aip;
8884                 Assert(farthest_objp->type == OBJ_SHIP);
8885                 Assert((farthest_objp->instance > -1) && (farthest_objp->instance < MAX_SHIPS));
8886                 Assert(Ships[farthest_objp->instance].ai_index > -1);
8887
8888                 aip = &Ai_info[Ships[farthest_objp->instance].ai_index];
8889
8890                 if (!maybe_resume_previous_mode(Pl_objp, aip)) {
8891                         //      If already ignoring something under player's orders, don't ignore current target.
8892                         if ((aip->ignore_objnum == UNUSED_OBJNUM) || (aip->ai_flags & AIF_TEMPORARY_IGNORE)) {
8893                                 aip->ignore_objnum = aip->target_objnum;
8894                                 aip->ignore_signature = Objects[aip->target_objnum].signature;
8895                                 aip->ai_flags |= AIF_TEMPORARY_IGNORE;
8896                                 aip->ignore_expire_timestamp = timestamp(((myrand() % 10) + 20) * 1000);        //      OK to attack again in 20 to 24 seconds.
8897                         }
8898                         aip->target_objnum = -1;
8899                         ai_do_default_behavior(farthest_objp);
8900                 }
8901         }
8902 }
8903
8904 // Maybe limit the number of attackers on attack_objnum.  For now, only limit attackers
8905 // in attacked_objnum is the player
8906 // input:       attacked_objnum =>              object index for ship we want to limit attacks on
8907 //
8908 //      exit:                   1       =>      num attackers exceeds maximum, abort
8909 //                                      0       =>      removed the farthest attacker
8910 //                                      -1      =>      nothing was done
8911 int ai_maybe_limit_attackers(int attacked_objnum)
8912 {
8913         int rval=-1;
8914
8915         // limit the number of ships attacking the _player_ only
8916 //      if ( attacked_objnum == OBJ_INDEX(Player_obj) ) {
8917         if ( Objects[attacked_objnum].flags & OF_PLAYER_SHIP) {
8918                 int num_attacking;
8919                 num_attacking = num_ships_attacking(attacked_objnum);
8920
8921                 if (num_attacking == Skill_level_max_attackers[Game_skill_level]) {
8922                         remove_farthest_attacker(attacked_objnum);
8923                         rval=0;
8924                 } else if (num_attacking > Skill_level_max_attackers[Game_skill_level]) {
8925                         rval=1;
8926                 }
8927                 //nprintf(("AI", "Num attacking player = %i\n", num_attacking));
8928         }
8929
8930         return rval;
8931 }
8932
8933 //      Object being guarded by object *guard_objp was hit by object *hitter_objp
8934 void guard_object_was_hit(object *guard_objp, object *hitter_objp)
8935 {
8936         int             hitter_objnum;
8937         ai_info *aip;
8938
8939         aip = &Ai_info[Ships[guard_objp->instance].ai_index];
8940
8941         if (guard_objp == hitter_objp) {
8942                 // Int3();      //      Bogus!  Who tried to get me to attack myself!  Trace out and fix!
8943                 return;
8944         }
8945
8946         if (guard_objp->type == OBJ_GHOST || hitter_objp->type == OBJ_GHOST)
8947                 return;
8948
8949         if (aip->ai_flags & AIF_NO_DYNAMIC)     //      Not allowed to pursue dynamic goals.  So, why are we guarding?
8950                 return;
8951
8952         Assert( (hitter_objp->type == OBJ_SHIP) || (hitter_objp->type == OBJ_ASTEROID) || (hitter_objp->type == OBJ_WEAPON) );
8953
8954         hitter_objnum = OBJ_INDEX(hitter_objp);
8955
8956         if ( hitter_objp->type == OBJ_SHIP ) {
8957                 //      If the hitter object is the ignore object, don't attack it.
8958                 if (is_ignore_object(aip, hitter_objp-Objects))
8959                         return;
8960
8961                 //      If hitter is on same team as me, don't attack him.
8962                 if (Ships[guard_objp->instance].team == Ships[hitter_objp->instance].team)
8963                         return;
8964
8965                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
8966                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
8967                         return;
8968                 }
8969
8970                 // dont attack if you can't see him
8971                 if ( awacs_get_level(hitter_objp, &Ships[aip->shipnum], 1) < 1 ) {
8972                         // if he's a stealth and visible, but not targetable, ok to attack.
8973                         if ( is_object_stealth_ship(hitter_objp) ) {
8974                                 if ( ai_is_stealth_visible(guard_objp, hitter_objp) != STEALTH_VISIBLE ) {
8975                                         return;
8976                                 }
8977                         }
8978                 }
8979         }
8980
8981         if (aip->target_objnum == -1) {
8982                 aip->ok_to_target_timestamp = timestamp(0);
8983         }
8984
8985         if ((aip->submode == AIS_GUARD_PATROL) || (aip->submode == AIS_GUARD_STATIC)) {
8986
8987                 if ( hitter_objp->type == OBJ_SHIP ) {
8988                         if (!(Ship_info[Ships[guard_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
8989                                 return;
8990                         }
8991
8992                         // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
8993                         if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
8994                                 return;
8995                         }
8996                 }
8997
8998                 if (aip->target_objnum != hitter_objnum) {
8999                         aip->aspect_locked_time = 0.0f;
9000                 }
9001
9002                 aip->ok_to_target_timestamp = timestamp(0);
9003
9004                 set_target_objnum(aip, hitter_objnum);
9005                 //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));
9006                 aip->previous_mode = AIM_GUARD;
9007                 aip->previous_submode = aip->submode;
9008                 aip->mode = AIM_CHASE;
9009                 aip->submode = SM_ATTACK;
9010                 aip->submode_start_time = Missiontime;
9011                 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9012         } else if (aip->previous_mode == AIM_GUARD) {
9013                 if (aip->target_objnum == -1) {
9014
9015                         if ( hitter_objp->type == OBJ_SHIP ) {
9016                                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9017                                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9018                                         return;
9019                                 }
9020                         }
9021
9022                         set_target_objnum(aip, hitter_objnum);
9023                 //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));
9024                         aip->mode = AIM_CHASE;
9025                         aip->submode = SM_ATTACK;
9026                         aip->submode_start_time = Missiontime;
9027                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9028                 } else {
9029                         int     num_attacking_cur, num_attacking_new;
9030
9031                         num_attacking_cur = num_ships_attacking(aip->target_objnum);
9032                         if (num_attacking_cur > 1) {
9033                                 num_attacking_new = num_ships_attacking(hitter_objnum);
9034
9035                                 if (num_attacking_new < num_attacking_cur) {
9036
9037                                         if ( hitter_objp->type == OBJ_SHIP ) {
9038                                                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9039                                                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9040                                                         return;
9041                                                 }
9042                                         }
9043                                         set_target_objnum(aip, hitter_objp-Objects);
9044                 //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));
9045                                         aip->mode = AIM_CHASE;
9046                                         aip->submode = SM_ATTACK;
9047                                         aip->submode_start_time = Missiontime;
9048                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9049                                 }
9050                         }
9051                 }
9052         }
9053 }
9054
9055 //      Ship object *hit_objp was hit by ship object *hitter_objp.
9056 //      See if anyone is guarding hit_objp and, if so, do something useful.
9057 void maybe_update_guard_object(object *hit_objp, object *hitter_objp)
9058 {
9059         object  *objp;
9060         ship_obj        *so;
9061
9062         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9063                 objp = &Objects[so->objnum];
9064                 if (objp->instance != -1) {
9065                         ai_info *aip;
9066                         aip = &Ai_info[Ships[objp->instance].ai_index];
9067
9068                         if ((aip->mode == AIM_GUARD) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
9069                                 if (aip->guard_objnum == hit_objp-Objects) {
9070                                         guard_object_was_hit(objp, hitter_objp);
9071                                 } else if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[hit_objp->instance].ai_index].wing)) {
9072                                         guard_object_was_hit(objp, hitter_objp);
9073                                 }
9074                         }
9075                 }
9076         }
9077 }
9078
9079 // Scan missile list looking for bombs homing on guarded_objp
9080 // return 1 if bomb is found (and targeted by guarding_objp), otherwise return 0
9081 int ai_guard_find_nearby_bomb(object *guarding_objp, object *guarded_objp)
9082 {       
9083         missile_obj     *mo;
9084         object          *bomb_objp, *closest_bomb_objp=NULL;
9085         float                   dist, dist_to_guarding_obj,closest_dist_to_guarding_obj=999999.0f;
9086         weapon          *wp;
9087         weapon_info     *wip;
9088
9089         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
9090                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
9091                 bomb_objp = &Objects[mo->objnum];
9092
9093                 wp = &Weapons[bomb_objp->instance];
9094                 wip = &Weapon_info[wp->weapon_info_index];
9095
9096                 if ( !(wip->wi_flags & WIF_BOMB) ) {
9097                         continue;
9098                 }
9099
9100                 if ( wp->homing_object != guarded_objp ) {
9101                         continue;
9102                 }
9103
9104                 dist = vm_vec_dist_quick(&bomb_objp->pos, &guarded_objp->pos);
9105
9106                 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9107                         dist_to_guarding_obj = vm_vec_dist_quick(&bomb_objp->pos, &guarding_objp->pos);
9108                         if ( dist_to_guarding_obj < closest_dist_to_guarding_obj ) {
9109                                 closest_dist_to_guarding_obj = dist_to_guarding_obj;
9110                                 closest_bomb_objp = bomb_objp;
9111                         }
9112                 }
9113         }
9114
9115         if ( closest_bomb_objp ) {
9116                 guard_object_was_hit(guarding_objp, closest_bomb_objp);
9117                 return 1;
9118         }
9119
9120         return 0;
9121 }
9122
9123 //      Scan enemy ships and see if one is near enough to guard object to be pursued.
9124 void ai_guard_find_nearby_ship(object *guarding_objp, object *guarded_objp)
9125 {
9126         ship            *guarding_shipp = &Ships[guarding_objp->instance];
9127         ai_info *guarding_aip = &Ai_info[guarding_shipp->ai_index];
9128         ship_obj        *so;
9129         object  *enemy_objp;
9130         float           dist;
9131
9132         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9133                 enemy_objp = &Objects[so->objnum];
9134
9135                 if (enemy_objp->instance < 0) {
9136                         continue;
9137                 }
9138
9139                 ship    *eshipp = &Ships[enemy_objp->instance];
9140
9141                 //      Don't attack a cargo container or other harmless ships
9142                 if (!(Ship_info[eshipp->ship_info_index].flags & SIF_HARMLESS)) {
9143                         if (guarding_shipp->team != eshipp->team)       {
9144                                 dist = vm_vec_dist_quick(&enemy_objp->pos, &guarded_objp->pos);
9145                                 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9146                                         guard_object_was_hit(guarding_objp, enemy_objp);
9147                                 } else if ((dist < 3000.0f) && (Ai_info[eshipp->ai_index].target_objnum == guarding_aip->guard_objnum)) {
9148                                         //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));
9149                                         guard_object_was_hit(guarding_objp, enemy_objp);
9150                                 }
9151                         }
9152                 }
9153         }
9154 }
9155
9156 // Scan for nearby asteroids.  Favor asteroids which have their collide_objnum set to that of the
9157 // guarded ship.  Also, favor asteroids that are closer to the guarding ship, since it looks cooler
9158 // when a ship blows up an asteroid then goes after the pieces that break off.
9159 void ai_guard_find_nearby_asteroid(object *guarding_objp, object *guarded_objp)
9160 {       
9161         float           dist;
9162
9163         object  *closest_asteroid_objp=NULL, *danger_asteroid_objp=NULL, *asteroid_objp;
9164         float           dist_to_self, closest_danger_asteroid_dist=999999.0f, closest_asteroid_dist=999999.0f;
9165
9166         for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp != END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) {
9167                 if ( asteroid_objp->type == OBJ_ASTEROID ) {
9168                         // Attack asteroid if near guarded ship
9169                         dist = vm_vec_dist_quick(&asteroid_objp->pos, &guarded_objp->pos);
9170                         if ( dist < (MAX_GUARD_DIST + guarded_objp->radius)*2) {
9171                                 dist_to_self = vm_vec_dist_quick(&asteroid_objp->pos, &guarding_objp->pos);
9172                                 if ( OBJ_INDEX(guarded_objp) == asteroid_collide_objnum(asteroid_objp) ) {
9173                                         if( dist_to_self < closest_danger_asteroid_dist ) {
9174                                                 danger_asteroid_objp=asteroid_objp;
9175                                                 closest_danger_asteroid_dist=dist_to_self;
9176                                         }
9177                                 } 
9178                                 if ( dist_to_self < closest_asteroid_dist ) {
9179                                         // only attack if moving slower than own max speed
9180                                         if ( vm_vec_mag_quick(&asteroid_objp->phys_info.vel) < guarding_objp->phys_info.max_vel.xyz.z ) {
9181                                                 closest_asteroid_dist = dist_to_self;
9182                                                 closest_asteroid_objp = asteroid_objp;
9183                                         }
9184                                 }
9185                         }
9186                 }
9187         }
9188
9189         if ( danger_asteroid_objp ) {
9190                 guard_object_was_hit(guarding_objp, danger_asteroid_objp);
9191         } else if ( closest_asteroid_objp ) {
9192                 guard_object_was_hit(guarding_objp, closest_asteroid_objp);
9193         }
9194 }
9195
9196 //      Scan potential harmful objects and see if one is near enough to guard object to be pursued.
9197 void ai_guard_find_nearby_object()
9198 {
9199         ship                    *shipp = &Ships[Pl_objp->instance];
9200         ai_info         *aip = &Ai_info[shipp->ai_index];
9201         object          *guardobjp;
9202         int                     bomb_found=0;
9203
9204         guardobjp = &Objects[aip->guard_objnum];
9205         
9206         // highest priority is a bomb fired on guarded ship
9207         bomb_found = ai_guard_find_nearby_bomb(Pl_objp, guardobjp);
9208
9209         if ( !bomb_found ) {
9210                 // check for ships if there are no bombs fired at guarded ship
9211                 ai_guard_find_nearby_ship(Pl_objp, guardobjp);
9212
9213                 // if not attacking anything, go for asteroid close to guarded ship
9214                 if ( (aip->target_objnum == -1) && asteroid_count() ) {
9215                         ai_guard_find_nearby_asteroid(Pl_objp, guardobjp);
9216                 }
9217         }
9218 }
9219
9220 // gets closest point on extended axis of cylinder, r_vec, and radius of cylinder
9221 // returns z of axis_point in cyl_objp reference frame
9222 float get_cylinder_points(object *other_objp, object *cyl_objp, vector *axis_pt, vector *r_vec, float *radius)
9223 {
9224         Assert(other_objp->type == OBJ_SHIP);
9225         Assert(cyl_objp->type == OBJ_SHIP);
9226
9227         // get radius of cylinder
9228         polymodel *pm = model_get(Ships[cyl_objp->instance].modelnum);
9229         float tempx, tempy;
9230         tempx = max(-pm->mins.xyz.x, pm->maxs.xyz.x);
9231         tempy = max(-pm->mins.xyz.y, pm->maxs.xyz.y);
9232         *radius = max(tempx, tempy);
9233
9234         // get vec from cylinder to other_obj
9235         vector r_sph;
9236         vm_vec_sub(&r_sph, &other_objp->pos, &cyl_objp->pos);
9237
9238         // get point on axis and on cylinder
9239         // extended_cylinder_z is along extended cylinder
9240         // cylinder_z is capped within cylinder
9241         float extended_cylinder_z = vm_vec_dotprod(&r_sph, &cyl_objp->orient.v.fvec);
9242
9243         // get pt on axis of extended cylinder
9244         vm_vec_scale_add(axis_pt, &cyl_objp->pos, &cyl_objp->orient.v.fvec, extended_cylinder_z);
9245
9246         // get r_vec (pos - axis_pt) normalized
9247         vm_vec_normalized_dir(r_vec, &other_objp->pos, axis_pt);
9248
9249         return extended_cylinder_z;
9250 }
9251
9252 // handler for guard behavior when guarding BIG ships
9253 //      When someone has attacked guarded ship, then attack that ship.
9254 // To attack another ship, switch out of guard mode into chase mode.
9255 void ai_big_guard()
9256 {
9257         
9258         ship                    *shipp = &Ships[Pl_objp->instance];
9259         ai_info         *aip = &Ai_info[shipp->ai_index];
9260         object          *guard_objp;
9261
9262         // sanity checks already done in ai_guard()
9263         guard_objp = &Objects[aip->guard_objnum];
9264
9265         switch (aip->submode) {
9266         case AIS_GUARD_STATIC:
9267         case AIS_GUARD_PATROL:
9268                 {
9269                 vector axis_pt, r_vec, theta_vec;
9270                 float radius, extended_z;
9271
9272                 // get random [0 to 1] based on OBJNUM
9273                 float objval = static_randf(Pl_objp-Objects);
9274
9275                 // get position relative to cylinder of guard_objp              
9276                 extended_z = get_cylinder_points(Pl_objp, guard_objp, &axis_pt, &r_vec, &radius);
9277                 vm_vec_crossprod(&theta_vec, &guard_objp->orient.v.fvec, &r_vec);
9278
9279                 // half ships circle each way
9280                 if (objval > 0.5f) {
9281                         vm_vec_negate(&theta_vec);
9282                 }
9283
9284                 float min_guard_dist = radius + Pl_objp->radius + 50.0f;
9285                 float desired_guard_dist = min_guard_dist + 0.5f * ((1.0f + objval) * MAX_GUARD_DIST);
9286                 float max_guard_dist =     min_guard_dist + 1.0f * ((1.0f + objval) * MAX_GUARD_DIST);
9287
9288                 // get z extents
9289                 float min_z, max_z, length;
9290                 polymodel *pm = model_get(Ships[guard_objp->instance].modelnum);
9291                 min_z = pm->mins.xyz.z;
9292                 max_z = pm->maxs.xyz.z;
9293                 length = max_z - min_z;
9294
9295                 // get desired z
9296                 // how often to choose new desired_z
9297                 // 1*(64) sec < 2000, 2*(64) < 2-4000 3*(64) > 4-8000, etc (Missiontime >> 22 is 64 sec intervals)
9298                 int time_choose = int(floor(log(length * 0.001) / log(2)));
9299                 float desired_z = min_z + length * static_randf( Pl_objp-Objects ^ (Missiontime >> (22 + time_choose)) );
9300
9301                 // get r from guard_ship
9302                 float cur_guard_rad = vm_vec_dist(&Pl_objp->pos, &axis_pt);
9303
9304                 // is ship within extents of cylinder of ship it is guarding
9305                 int inside = (extended_z > min_z) && (extended_z < min_z + length);
9306
9307                 vector goal_pt;
9308                 // maybe go into orbit mode
9309                 if (cur_guard_rad < max_guard_dist) {
9310                         if ( cur_guard_rad > min_guard_dist ) {
9311                                 if (inside) {
9312                                         // orbit
9313                                         vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, desired_guard_dist);
9314                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9315                                 } else {
9316                                         // move to where I can orbit
9317                                         if (extended_z < min_z) {
9318                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, min_z);
9319                                         } else {
9320                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, max_z);
9321                                         }
9322                                         vm_vec_scale_add2(&goal_pt, &r_vec, desired_guard_dist);
9323                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9324                                 }
9325                         } else {
9326                                 // too close for orbit mode
9327                                 if (inside) {
9328                                         // inside (fly straight out and return circle)
9329                                         vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, max_guard_dist);
9330                                 } else {
9331                                         // outside (fly to edge and circle)
9332                                         if (extended_z < min_z) {
9333                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, min_z);
9334                                         } else {
9335                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, max_z);
9336                                         }
9337                                         vm_vec_scale_add2(&goal_pt, &r_vec, max_guard_dist);
9338                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9339                                 }
9340                         }
9341
9342                         if (Pl_objp->phys_info.fspeed > 0) {
9343                                 // modify goal_pt to take account moving guard objp
9344                                 float dist = vm_vec_dist_quick(&Pl_objp->pos, &goal_pt);
9345                                 float time = dist / Pl_objp->phys_info.fspeed;
9346                                 vm_vec_scale_add2(&goal_pt, &guard_objp->phys_info.vel, time);
9347
9348                                 // now modify to move to desired z (at a max of 20 m/s)
9349                                 float delta_z = desired_z - extended_z;
9350                                 float v_z = delta_z * 0.2f;
9351                                 if (v_z < -20) {
9352                                         v_z = -20.0f;
9353                                 } else if (v_z > 20) {
9354                                         v_z = 20.0f;
9355                                 }
9356
9357                                 vm_vec_scale_add2(&goal_pt, &guard_objp->orient.v.fvec, v_z*time);
9358                         }
9359
9360                 } else {
9361                         // cast vector to center of guard_ship adjusted by desired_z
9362                         float delta_z = desired_z - extended_z;
9363                         vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, delta_z);
9364                 }
9365
9366                 // try not to bump into things along the way
9367                 if ( (cur_guard_rad > max_guard_dist) || (extended_z < min_z) || (extended_z > max_z) ) {
9368                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9369                                 return;
9370                         }
9371
9372                         if (avoid_player(Pl_objp, &goal_pt)) {
9373                                 return;
9374                         }
9375                 } else {
9376                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9377                                 return;
9378                         }
9379                 }
9380
9381                 // got the point, now let's go there
9382                 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);
9383 //              aip->goal_point = goal_pt;
9384                 accelerate_ship(aip, 1.0f);
9385
9386                 //      Periodically, scan for a nearby ship to attack.
9387                 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9388                         ai_guard_find_nearby_object();
9389                 }
9390                 }
9391                 break;
9392
9393         case AIS_GUARD_ATTACK:
9394                 //      The guarded ship has been attacked.  Do something useful!
9395                 ai_chase();
9396                 break;
9397
9398         default:
9399                 //Int3();       //      Illegal submode for Guard mode.
9400                 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9401                 aip->submode = AIS_GUARD_PATROL;
9402                 break;
9403         }
9404 }
9405
9406 //      Main handler for guard behavior.
9407 //      When someone has attacked guarded ship, then attack that ship.
9408 // To attack another ship, switch out of guard mode into chase mode.
9409 void ai_guard()
9410 {
9411         ship                    *shipp = &Ships[Pl_objp->instance];
9412         ai_info         *aip = &Ai_info[shipp->ai_index];
9413         object          *guard_objp;    
9414         ship                    *gshipp;
9415         float                   dist_to_guardobj, dot_to_guardobj;
9416         vector          vec_to_guardobj;
9417
9418         /*      //      Debug code, find an object to guard.
9419         int finding_guard_objnum = 0;   //      Debug code, to see if body of "if" below gets executed. 
9420         if (aip->guard_objnum == -1) {
9421                 finding_guard_objnum = 1;
9422                 debug_find_guard_object();
9423                 if (aip->guard_objnum == -1)
9424                         return;
9425         }
9426 */
9427         if (aip->guard_objnum == -1) {
9428                 aip->mode = AIM_NONE;
9429                 return;
9430         }
9431
9432         Assert(aip->guard_objnum != -1);
9433
9434         guard_objp = &Objects[aip->guard_objnum];
9435
9436         if (guard_objp == Pl_objp) {
9437                 Int3();         //      This seems illegal.  Why is a ship guarding itself?
9438                 aip->guard_objnum = -1;
9439                 return;
9440         }
9441
9442         // check that I have someone to guard
9443         if (guard_objp->instance == -1) {
9444                 return;
9445         }
9446
9447         //      Not sure whether this should be impossible, or a reasonable cleanup condition.
9448         //      For now (3/31/97), it's getting trapped by an Assert, so clean it up.
9449         if (guard_objp->type != OBJ_SHIP) {
9450                 aip->guard_objnum = -1;
9451                 return;
9452         }
9453
9454         // handler for gurad object with BIG radius
9455         if (guard_objp->radius > BIG_GUARD_RADIUS) {
9456                 ai_big_guard();
9457                 return;
9458         }
9459
9460         gshipp = &Ships[guard_objp->instance];
9461
9462         float                   objval;
9463         vector          goal_point;
9464         vector          rel_vec;
9465         float                   dist_to_goal_point, dot_to_goal_point, accel_scale;
9466         vector          v2g, rvec;
9467
9468         // get random [0 to 1] based on OBJNUM
9469         objval = static_randf(Pl_objp-Objects);
9470
9471         switch (aip->submode) {
9472         case AIS_GUARD_STATIC:
9473         case AIS_GUARD_PATROL:
9474                 //      Stay near ship
9475                 dist_to_guardobj = vm_vec_normalized_dir(&vec_to_guardobj, &guard_objp->pos, &Pl_objp->pos);
9476                 dot_to_guardobj = vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_to_guardobj);
9477
9478                 rel_vec = aip->guard_vec;
9479                 vm_vec_add(&goal_point, &guard_objp->pos, &rel_vec);
9480
9481                 vm_vec_normalized_dir(&v2g, &goal_point, &Pl_objp->pos);
9482                 dist_to_goal_point = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
9483                 dot_to_goal_point = vm_vec_dot(&v2g, &Pl_objp->orient.v.fvec);
9484                 accel_scale = (1.0f + dot_to_goal_point)/2.0f;
9485
9486                 //      If far away, get closer
9487                 if (dist_to_goal_point > MAX_GUARD_DIST + 1.5 * (Pl_objp->radius + guard_objp->radius)) {
9488                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 5.0f)) {
9489                                 return;
9490                         }
9491
9492                         if (avoid_player(Pl_objp, &goal_point)) {
9493                                 return;
9494                         }
9495
9496                         // quite far away, so try to go straight to 
9497                         compute_desired_rvec(&rvec, &goal_point, &Pl_objp->pos);
9498                         ai_turn_towards_vector(&goal_point, Pl_objp, flFrametime, Ship_info[shipp->ship_info_index].srotation_time, NULL, NULL, 0.0f, 0, &rvec);
9499
9500                         accelerate_ship(aip, accel_scale * (0.25f + dist_to_goal_point/700.0f));
9501                 } else {
9502                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 2.0f)) {
9503                                 return;
9504                         }
9505
9506                         // get max of guard_objp (1) normal speed (2) dock speed
9507                         float speed = guard_objp->phys_info.speed;
9508
9509                         if (guard_objp->type == OBJ_SHIP) {
9510                                 ai_info *guard_aip = &Ai_info[Ships[guard_objp->instance].ai_index];
9511
9512                                 if (guard_aip->dock_objnum != -1) {
9513                                         speed = max(speed, Objects[guard_aip->dock_objnum].phys_info.speed);
9514                                 }
9515                         }
9516                         
9517                         //      Deal with guarding a small object.
9518                         //      If going to guard_vec might cause a collision with guarded object, pick a new guard point.
9519                         if (vm_vec_dot(&v2g, &vec_to_guardobj) > 0.8f) {
9520                                 if (dist_to_guardobj < dist_to_goal_point) {
9521                                         ai_set_guard_vec(Pl_objp, guard_objp);  //      OK to return here.
9522                                         return;
9523                                 }
9524                         } 
9525
9526                         if (speed > 10.0f) {
9527                                 //      If goal ship is moving more than a tiny bit, don't orbit it, get near it.
9528                                 if (vm_vec_dist_quick(&goal_point, &Pl_objp->pos) > 40.0f) {
9529                                         if (vm_vec_dot(&Pl_objp->orient.v.fvec, &v2g) < 0.0f) {
9530                                                 //      Just slow down, don't turn.
9531                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point/10.0f);
9532                                         } else {
9533                                                 //      Goal point is in front.
9534
9535                                                 //      If close to goal point, don't change direction, just change speed.
9536                                                 if (dist_to_goal_point > Pl_objp->radius + 10.0f) {
9537                                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9538                                                 }
9539                                                 
9540                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + (dist_to_goal_point-40.0f)/20.0f);
9541                                         }
9542                                 } else {
9543                                         if (dot_to_goal_point > 0.8f) {
9544                                                 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9545                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + dist_to_goal_point*0.1f);
9546                                         } else {
9547                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point*0.1f - 1.0f);
9548                                         }
9549                                 }
9550                         // consider guard object STILL
9551                         } else if (guard_objp->radius < 50.0f) {
9552                                 if (dist_to_goal_point > 15.0f) {
9553                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9554                                         set_accel_for_target_speed(Pl_objp, (dist_to_goal_point-10.0f)/2.0f);
9555                                 } else if (Pl_objp->phys_info.speed < 1.0f) {
9556                                         turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9557                                 }
9558                                 //      It's a big ship
9559                         } else if (dist_to_guardobj > MAX_GUARD_DIST + Pl_objp->radius + guard_objp->radius) {
9560                                 //      Orbiting ship, too far away
9561                                 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.0f + objval/2) * guard_objp->radius);
9562                                 accelerate_ship(aip, (1.0f + dot)/2.0f);
9563                         } else if (dist_to_guardobj < Pl_objp->radius + guard_objp->radius) {
9564                                 //      Orbiting ship, got too close
9565                                 turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9566                                 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9567                                         change_acceleration(aip, 0.25f);
9568                                 else
9569                                         accelerate_ship(aip, 0.5f + objval/4.0f);
9570                         } else {
9571                                 //      Orbiting ship, about the right distance away.
9572                                 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.5f + objval/2.0f)*guard_objp->radius);
9573                                 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9574                                         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));
9575                                 else
9576                                         accelerate_ship(aip, 0.5f * (1.0f + dot) * (0.3f + objval/3.0f));
9577                         }
9578                 }
9579
9580                 //      Periodically, scan for a nearby ship to attack.
9581                 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9582                         ai_guard_find_nearby_object();
9583                 }
9584                 break;
9585
9586         case AIS_GUARD_ATTACK:
9587                 //      The guarded ship has been attacked.  Do something useful!
9588                 ai_chase();
9589
9590                 break;
9591         default:
9592                 //Int3();       //      Illegal submode for Guard mode.
9593                 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9594                 aip->submode = AIS_GUARD_PATROL;
9595                 break;
9596         }
9597
9598 }
9599
9600 // Return the object of the ship that the given object is docked
9601 // with.  Currently, we know a ship is docked when his ai_mode is AIM_DOCK,
9602 // and his submode is AIS_DOCK_3.  I suppose that this is likely to change though.
9603 // Also, the objnum that was is passed in may not be the object that actually
9604 // performed the docking maneuver.  This code will account for that case.
9605 object *ai_find_docked_object( object *docker )
9606 {
9607         ai_info *aip;
9608
9609         // we are trying to find the dockee of docker.  (Note that that these terms
9610         // are totally relative to what is passed in as a parameter.)
9611
9612         // first thing to attempt is to check and see if this object is docked with something.
9613         Assert( docker->type == OBJ_SHIP );             // this had probably better be a ship!!!
9614         aip = &Ai_info[Ships[docker->instance].ai_index];
9615         if ( !(aip->ai_flags & AIF_DOCKED) )            // flag not set if not docked with anything
9616                 return NULL;
9617
9618         if ( aip->dock_objnum == -1 ) {
9619                 Int3();                                                                                 // mwa says this is wrong wrong wrong
9620                 ai_do_objects_undocked_stuff( docker, NULL );
9621                 return NULL;
9622         }
9623
9624         return &Objects[aip->dock_objnum];
9625
9626 }
9627
9628
9629 // define for the points subtracted from score for a rearm started on a player.
9630 #define REPAIR_PENALTY          50
9631
9632
9633 // function to clean up ai flags, variables, and other interesting information
9634 // for a ship that was getting repaired.  The how parameter is useful for multiplayer
9635 // only in that it tells us why the repaired ship is being cleaned up.
9636 void ai_do_objects_repairing_stuff( object *repaired_objp, object *repair_objp, int how )
9637 {
9638         ai_info *aip, *repair_aip;
9639         int             stamp = -1;
9640
9641         Assert( repaired_objp->type == OBJ_SHIP);
9642         aip = &Ai_info[Ships[repaired_objp->instance].ai_index];
9643
9644         // multiplayer
9645         int p_index;
9646         p_index = -1;
9647         if(Game_mode & GM_MULTIPLAYER){
9648                 p_index = multi_find_player_by_object(repaired_objp);           
9649         }               
9650         else {          
9651                 if(repaired_objp == Player_obj){
9652                         p_index = Player_num;
9653                 }
9654         }
9655
9656         switch( how ) {
9657         case REPAIR_INFO_BEGIN:
9658                 aip->ai_flags |= AIF_BEING_REPAIRED;
9659                 aip->ai_flags &= ~AIF_AWAITING_REPAIR;
9660                 stamp = timestamp(-1);
9661
9662                 // if this is a player ship, then subtract the repair penalty from this player's score
9663                 if ( repaired_objp->flags & OF_PLAYER_SHIP ) {
9664                         if ( !(Game_mode & GM_MULTIPLAYER) ) {
9665                                 Player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());                    // subtract the penalty
9666                         } else {
9667                                 /*
9668                                 int pnum;
9669
9670                                 // multiplayer game -- find the player, then subtract the score
9671                                 pnum = multi_find_player_by_object( repaired_objp );
9672                                 if ( pnum != -1 ) {
9673                                         Net_players[pnum].player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());
9674
9675                                         // squad war
9676                                         multi_team_maybe_add_score(-(int)(REPAIR_PENALTY * scoring_get_scale_factor()), Net_players[pnum].p_info.team);
9677                                 } else {
9678                                         nprintf(("Network", "Couldn't find player for ship %s for repair penalty\n", Ships[repaired_objp->instance].ship_name));
9679                                 }
9680                                 */
9681                         }
9682                 }
9683                 break;
9684
9685         case REPAIR_INFO_BROKEN:
9686                 aip->ai_flags &= ~AIF_BEING_REPAIRED;
9687                 aip->ai_flags |= AIF_AWAITING_REPAIR;
9688                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9689                 break;
9690
9691         case REPAIR_INFO_END:
9692                 // when only awaiting repair, and the repair is ended, then set dock_objnum to -1.
9693                 if ( aip->ai_flags & AIF_AWAITING_REPAIR ){
9694                         aip->dock_objnum = -1;
9695                 }
9696                 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9697                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9698                 break;
9699
9700         case REPAIR_INFO_QUEUE:
9701                 aip->ai_flags |= AIF_AWAITING_REPAIR;
9702                 if ( aip == Player_ai ){
9703                         hud_support_view_start();
9704                 }
9705                 stamp = timestamp(-1);
9706                 break;
9707
9708         case REPAIR_INFO_ABORT:
9709         case REPAIR_INFO_KILLED:
9710                 // 5/4/98 -- MWA -- Need to set dock objnum to -1 to let code know this guy who was getting
9711                 // repaired (or queued for repair), isn't really going to be docked with anyone anymore.
9712                 aip->dock_objnum = -1;
9713                 aip->ai_flags &= ~AIF_DOCKED;
9714                 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9715                 if (repair_objp != NULL) {
9716                         repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9717                         repair_aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9718                 }               
9719
9720                 if ( p_index >= 0 ) {
9721                         hud_support_view_abort();
9722
9723                         // send appropriate message to player here
9724                         if ( how == REPAIR_INFO_KILLED ){
9725                                 message_send_builtin_to_player( MESSAGE_SUPPORT_KILLED, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9726                         } else {
9727                                 if ( repair_objp ){
9728                                         message_send_builtin_to_player( MESSAGE_REPAIR_ABORTED, &Ships[repair_objp->instance], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9729                                 }
9730                         }
9731                 }
9732
9733                 // add log entry if this is a player
9734                 if ( repaired_objp->flags & OF_PLAYER_SHIP ){
9735                         mission_log_add_entry(LOG_PLAYER_REARM_ABORT, Ships[repaired_objp->instance].ship_name, NULL);
9736                 }
9737
9738                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9739                 break;
9740
9741         case REPAIR_INFO_COMPLETE:
9742                 // clear the being repaired flag -- and 
9743                 if ( p_index >= 0 ) {
9744                         Assert( repair_objp );
9745                         
9746                         hud_support_view_stop();                        
9747
9748                         message_send_builtin_to_player(MESSAGE_REPAIR_DONE, &Ships[repair_objp->instance], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, p_index, -1);
9749                 }
9750                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9751                 break;
9752
9753         case REPAIR_INFO_ONWAY:
9754                 // need to set the dock_signature so that clients in multiplayer games rearm correctly
9755                 Assert( repair_objp );
9756                 aip->dock_signature = repair_objp->signature; 
9757                 aip->dock_objnum = OBJ_INDEX(repair_objp);
9758                 stamp = timestamp(-1);
9759                 break;
9760
9761         default:
9762                 Int3();                 // bogus type of repair info
9763         }
9764
9765         if (repair_objp){
9766                 Ai_info[Ships[repair_objp->instance].ai_index].warp_out_timestamp = stamp;
9767         }
9768
9769         // repair_objp might be NULL is we are cleaning up this mode because of the support ship
9770         // getting killed.
9771         if ( repair_objp ) {
9772                 aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9773                 switch ( how ) {
9774                 case REPAIR_INFO_ONWAY:
9775                         Assert( repaired_objp != NULL );
9776                         aip->goal_objnum = OBJ_INDEX(repaired_objp);
9777                         aip->ai_flags |= AIF_REPAIRING;
9778                         break;
9779
9780                 case REPAIR_INFO_BROKEN:
9781                         break;
9782
9783                 case REPAIR_INFO_END:
9784                 case REPAIR_INFO_ABORT:
9785                 case REPAIR_INFO_KILLED:
9786                         if ( how == REPAIR_INFO_ABORT )
9787                                 aip->goal_objnum = -1;
9788
9789                         aip->ai_flags &= ~AIF_REPAIRING;
9790                         break;
9791                         
9792                 case REPAIR_INFO_QUEUE:
9793                         ai_add_rearm_goal( repaired_objp, repair_objp );
9794                         break;
9795
9796                 case REPAIR_INFO_BEGIN:
9797                 case REPAIR_INFO_COMPLETE:
9798                         break;
9799
9800                 default:
9801                         Int3();         // bogus type of repair info
9802                 }
9803         }
9804
9805         multi_maybe_send_repair_info( repaired_objp, repair_objp, how );
9806 }
9807
9808 //      Cleanup AI stuff for when a ship was supposed to dock with another, but the ship
9809 //      it was supposed to dock with is no longer valid.
9810 void ai_cleanup_dock_mode(ai_info *aip, ship *shipp)
9811 {
9812         object *objp;
9813
9814         objp = &Objects[shipp->objnum];
9815         aip->mode = AIM_NONE;
9816
9817         if (aip->ai_flags & AIF_REPAIRING) {
9818                 Assert( aip->goal_objnum != -1 );
9819                 ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], &Objects[shipp->objnum], REPAIR_INFO_KILLED );
9820         } else if ( aip->ai_flags & AIF_BEING_REPAIRED ) {
9821                 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9822                 Assert( aip->dock_objnum != -1 );
9823                 ai_do_objects_repairing_stuff( &Objects[shipp->objnum], &Objects[aip->dock_objnum], REPAIR_INFO_KILLED );
9824         } else if ( aip->ai_flags & AIF_AWAITING_REPAIR ) {
9825                 // need to find the support ship that has me as a goal_objnum
9826                 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9827                 // MWA -- 3/38/98  Check to see if this guy is queued for a support ship, or there is already
9828                 // one in the mission
9829                 if ( mission_is_repair_scheduled(objp) ) {
9830                         mission_remove_scheduled_repair( objp );                        // this function will notify multiplayer clients.
9831                 } else {
9832                         if ( aip->dock_objnum != -1 )
9833                                 ai_do_objects_repairing_stuff( objp, &Objects[aip->dock_objnum], REPAIR_INFO_ABORT );
9834                         else
9835                                 ai_do_objects_repairing_stuff( objp, NULL, REPAIR_INFO_ABORT );
9836                 }
9837         }
9838
9839         if ( aip->ai_flags & AIF_DOCKED ) {
9840                 ai_info *other_aip;
9841
9842                 Assert( aip->dock_objnum != -1 );
9843
9844                 // if docked, and the dock_objnum is not undocking, force them to near last stage
9845                 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
9846                 if ( (other_aip->mode == AIM_DOCK) && (other_aip->submode < AIS_UNDOCK_3) )
9847                         other_aip->submode = AIS_UNDOCK_3;
9848                 ai_do_objects_undocked_stuff( objp, &Objects[aip->dock_objnum] );
9849         }
9850 }
9851
9852 /*
9853 //      Make dockee_objp shake a bit due to docking.
9854 void ai_dock_shake(object *docker_objp, object *dockee_objp)
9855 {
9856         vector  tangles;
9857         matrix  rotmat, tmp;
9858         float           scale;
9859         angles  *ap;
9860
9861         scale = 0.25f;          //      Compute this based on mass and speed at time of docking.
9862
9863         vm_vec_rand_vec_quick(&tangles);
9864         vm_vec_scale(&tangles, scale);
9865
9866         ap = (angles *) &tangles;
9867
9868         vm_angles_2_matrix(&rotmat, ap);
9869         vm_matrix_x_matrix( &tmp, &dockee_objp->orient, &rotmat );
9870         dockee_objp->orient = tmp;
9871
9872         vm_orthogonalize_matrix(&dockee_objp->orient);
9873
9874         dock_orient_and_approach(docker_objp, dockee_objp, DOA_DOCK_STAY);
9875
9876 }
9877 */
9878
9879 //      Make Pl_objp point at aip->goal_point.
9880 void ai_still()
9881 {
9882         ship    *shipp;
9883         ai_info *aip;
9884
9885         Assert(Pl_objp->type == OBJ_SHIP);
9886         Assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_OBJECTS));
9887
9888         shipp = &Ships[Pl_objp->instance];
9889         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
9890
9891         aip = &Ai_info[shipp->ai_index];
9892
9893         turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
9894 }
9895
9896 //      Make *Pl_objp stay near another ship.
9897 void ai_stay_near()
9898 {
9899         ai_info *aip;
9900         int             goal_objnum;
9901
9902         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
9903
9904         goal_objnum = aip->goal_objnum;
9905
9906         if ((goal_objnum < 0) || (Objects[goal_objnum].type != OBJ_SHIP) || (Objects[goal_objnum].signature != aip->goal_signature)) {
9907                 aip->mode = AIM_NONE;
9908         } else {
9909                 float           dist, max_dist, scale;
9910                 vector  rand_vec, goal_pos, vec_to_goal;
9911                 object  *goal_objp;
9912
9913                 goal_objp = &Objects[goal_objnum];
9914
9915                 //      Make not all ships pursue same point.
9916                 static_randvec(Pl_objp-Objects, &rand_vec);
9917
9918                 //      Make sure point is in front hemisphere (relative to Pl_objp's position.
9919                 vm_vec_sub(&vec_to_goal, &goal_objp->pos, &Pl_objp->pos);
9920                 if (vm_vec_dot(&rand_vec, &vec_to_goal) > 1.0f) {
9921                         vm_vec_negate(&rand_vec);
9922                 }
9923
9924                 //      Scale the random vector by an amount proportional to the distance from Pl_objp to the true goal.
9925                 dist = vm_vec_dist_quick(&goal_objp->pos, &Pl_objp->pos);
9926                 max_dist = aip->stay_near_distance;
9927                 scale = dist - max_dist/2;
9928                 if (scale < 0.0f)
9929                         scale = 0.0f;
9930
9931                 vm_vec_scale_add(&goal_pos, &goal_objp->pos, &rand_vec, scale);
9932
9933                 if (max_dist < Pl_objp->radius + goal_objp->radius + 25.0f)
9934                         max_dist = Pl_objp->radius + goal_objp->radius + 25.0f;
9935
9936                 if (dist > max_dist) {
9937                         turn_towards_point(Pl_objp, &goal_pos, NULL, 0.0f);
9938                         accelerate_ship(aip, dist / max_dist - 0.8f);
9939                 }
9940         
9941         }
9942
9943 }
9944
9945 //      Warn player if dock path is obstructed.
9946 int maybe_dock_obstructed(object *cur_objp, object *goal_objp, int big_only_flag)
9947 {
9948         vector  *goalpos, *curpos;
9949         float           radius;
9950         ai_info *aip;
9951         int             collide_objnum;
9952
9953         aip = &Ai_info[Ships[cur_objp->instance].ai_index];
9954
9955         Ai_info[Ships[goal_objp->instance].ai_index].ai_flags &= ~AIF_REPAIR_OBSTRUCTED;
9956
9957         if (goal_objp != Player_obj)
9958                 return -1;
9959
9960         curpos = &cur_objp->pos;
9961         radius = cur_objp->radius;
9962         goalpos = &Path_points[aip->path_cur].pos;
9963         collide_objnum = pp_collide_any(curpos, goalpos, radius, cur_objp, goal_objp, big_only_flag);
9964
9965         if (collide_objnum != -1)
9966                 Ai_info[Ships[goal_objp->instance].ai_index].ai_flags |= AIF_REPAIR_OBSTRUCTED;
9967
9968         return collide_objnum;
9969 }
9970
9971
9972 int Dock_path_warning_given = 0;
9973
9974 //      Docking behavior.
9975 //      Approach a ship, follow path to docking platform, approach platform, after awhile,
9976 //      undock.
9977 void ai_dock()
9978 {
9979         ship                    *shipp = &Ships[Pl_objp->instance];
9980         ai_info         *aip = &Ai_info[shipp->ai_index];
9981         object          *goal_objp;
9982         ship_info       *sip = &Ship_info[shipp->ship_info_index];
9983
9984         //      Make sure object we're supposed to dock with still exists.
9985         if ((aip->goal_objnum == -1) || (Objects[aip->goal_objnum].signature != aip->goal_signature)) {
9986                 ai_cleanup_dock_mode(aip, shipp);
9987                 return;
9988         }
9989
9990         goal_objp = &Objects[aip->goal_objnum];
9991
9992         //      For docking submodes (ie, not undocking), follow path.  Once at second last
9993         //      point on path (point just before point on dock platform), orient into position.
9994         // For undocking, first mode pushes docked ship straight back from docking point
9995         // second mode turns ship and moves to point on docking radius
9996         switch (aip->submode) {
9997
9998                 //      This mode means to find the path to the docking point.
9999         case AIS_DOCK_0:
10000                 //aip->path_start = -1;
10001                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10002                 ai_path();
10003                 if (!Dock_path_warning_given && (aip->path_length < 4)) {
10004                         Warning( LOCATION, "Ship '%s' has only %i points on dock path.  Docking will look strange.  Contact Adam.", shipp->ship_name, aip->path_length );
10005                         Dock_path_warning_given = 1;            //      This is on a mission-wide basis, but it's just a hack for now...
10006                 }
10007
10008                 aip->submode = AIS_DOCK_1;
10009                 aip->path_start = -1;
10010                 aip->submode_start_time = Missiontime;
10011                 break;
10012
10013                 //      This mode means to follow the path until just before the end.
10014         case AIS_DOCK_1: {
10015                 float   dist;
10016                 int     r;
10017
10018                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp, 1)) != -1) {
10019                         int     r1;
10020                         if ((r1 = maybe_avoid_big_ship(Pl_objp, goal_objp, aip, &goal_objp->pos, 7.0f)) != 0) {
10021                                 nprintf(("AI", "Support ship %s avoiding large ship %s\n", Ships[Pl_objp->instance].ship_name, Ships[Objects[r1].instance].ship_name));
10022                                 break;
10023                         } /*else {
10024                                 nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10025                                 accelerate_ship(aip, 0.0f);
10026                                 aip->submode = AIS_DOCK_0;
10027                         } */
10028                 } //else {
10029                 {
10030                         dist = ai_path();
10031                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10032                         //nprintf(("AI", "Dock 1: Frame: %i, goal point = %i, dist = %7.3f\n", Framecount, aip->path_cur-aip->path_start, dist));
10033
10034                         if (aip->path_cur-aip->path_start >= aip->path_length-1) {              //      If got this far, advance no matter what.
10035                                 aip->submode = AIS_DOCK_2;
10036                                 aip->submode_start_time = Missiontime;
10037                                 aip->path_cur--;
10038                                 Assert(aip->path_cur-aip->path_start >= 0);
10039                         } else if (aip->path_cur-aip->path_start >= aip->path_length-2) {
10040                                 if (Pl_objp->phys_info.speed > goal_objp->phys_info.speed + 1.5f) {
10041                                         set_accel_for_target_speed(Pl_objp, goal_objp->phys_info.speed);
10042                                 } else {
10043                                         aip->submode = AIS_DOCK_2;
10044                                         aip->submode_start_time = Missiontime;
10045                                 }
10046                         }
10047                 }
10048                 break;
10049                                           }
10050         //      This mode means to drag oneself right to the second last point on the path.
10051         //      Path code allows it to overshoot.
10052         case AIS_DOCK_2: {
10053                 float           dist;
10054                 int     r;
10055
10056                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10057                         nprintf(("AI", "Dock 2: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10058                         accelerate_ship(aip, 0.0f);
10059                         aip->submode = AIS_DOCK_1;
10060                 } else {
10061                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10062                         dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_APPROACH);
10063                         Assert(dist != UNINITIALIZED_VALUE);
10064
10065                         if (dist == DOCK_BACKUP_RETURN_VAL) {
10066                                 int path_num;
10067                                 aip->submode = AIS_DOCK_1;
10068                                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
10069                                 Assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
10070                                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
10071                                 break;
10072                         }
10073
10074                         //nprintf(("AI", "Dock 2: dist = %7.3f\n", vm_vec_dist_quick(&Pl_objp->pos, &goal_point)));
10075                         float   tolerance;
10076                         if (Objects[aip->goal_objnum].flags & OF_PLAYER_SHIP)
10077                                 tolerance = 6*flFrametime + 1.0f;
10078                         else
10079                                 tolerance = 4*flFrametime + 0.5f;
10080
10081                         if ( dist < tolerance) {
10082                                 aip->submode = AIS_DOCK_3;
10083                                 aip->submode_start_time = Missiontime;
10084                                 aip->path_cur++;
10085                         }
10086                 }
10087                 break;
10088                                                   }
10089
10090         case AIS_DOCK_3:
10091         case AIS_DOCK_3A:
10092                 {
10093                 Assert(aip->goal_objnum != -1);
10094                 int     r;
10095
10096                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10097                         nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10098                         accelerate_ship(aip, 0.0f);
10099                         aip->submode = AIS_DOCK_2;
10100                 } else {
10101
10102                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10103                         float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10104                         Assert(dist != UNINITIALIZED_VALUE);
10105
10106                         if (dist == DOCK_BACKUP_RETURN_VAL) {
10107                                 aip->submode = AIS_DOCK_2;
10108                                 break;
10109                         }
10110
10111                         //nprintf(("AI", "Dock 3: dist = %7.3f\n", dist));
10112
10113                         if (dist < 2*flFrametime * (1.0f + fl_sqrt(goal_objp->phys_info.speed))) {
10114                                 // - Removed by MK on 11/7/97, causes errors for ships docked at mission start: maybe_recreate_path(Pl_objp, aip, 1);
10115                                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10116                                 Assert(dist != UNINITIALIZED_VALUE);
10117
10118                                 physics_ship_init(Pl_objp);
10119
10120                                 ai_do_objects_docked_stuff( Pl_objp, goal_objp );
10121
10122                                 if (aip->submode == AIS_DOCK_3) {
10123                                         snd_play_3d( &Snds[SND_DOCK_ATTACH], &Pl_objp->pos, &View_position );
10124                                         hud_maybe_flash_docking_text(Pl_objp);
10125                                         // ai_dock_shake(Pl_objp, goal_objp);
10126
10127                                         if ((Pl_objp == Player_obj) || (goal_objp == Player_obj))
10128                                                 joy_ff_docked();  // shake player's joystick a little
10129                                 }
10130
10131                                 //      If this ship is repairing another ship...
10132                                 if (aip->ai_flags & AIF_REPAIRING) {
10133                                         aip->submode = AIS_DOCK_4;                      //      Special rearming only dock mode.
10134                                         aip->submode_start_time = Missiontime;
10135                                 } else {
10136                                         aip->submode = AIS_DOCK_4A;
10137                                         aip->submode_start_time = Missiontime;
10138                                 }
10139                         }
10140                 }
10141                 break;
10142                 }
10143
10144                 //      Yes, we just sit here.  We wait for further orders.  No, it's not a bug.
10145         case AIS_DOCK_4A:
10146                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10147                 //nprintf(("AI", "."));
10148                 if (aip->active_goal >= 0) {
10149                         mission_log_add_entry(LOG_SHIP_DOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10150
10151                         if (aip->goals[aip->active_goal].ai_mode == AI_GOAL_DOCK) {
10152                                 ai_mission_goal_complete( aip );                                        // Note, this calls ai_set_default_behavior().
10153                         } 
10154                 } else {        //      Can happen for initially docked ships.
10155                         ai_do_default_behavior( &Objects[Ships[aip->shipnum].objnum] );         // do the default behavior
10156                 }
10157                 
10158                 break;
10159
10160         case AIS_DOCK_4: {
10161                 //      This mode is only for rearming/repairing.
10162                 //      The ship that is performing the rearm enters this mode after it docks.
10163                 Assert((aip->goal_objnum >= -1) && (aip->goal_objnum < MAX_OBJECTS));
10164
10165                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10166                 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10167                 Assert(dist != UNINITIALIZED_VALUE);
10168
10169                 object  *goal_objp = &Objects[aip->goal_objnum];
10170                 Assert(goal_objp->type == OBJ_SHIP);
10171                 ship                    *goal_shipp = &Ships[goal_objp->instance];              
10172                 ai_info         *goal_aip = &Ai_info[goal_shipp->ai_index];
10173
10174                 //nprintf(("AI", "Dock 4: dist = %7.3f\n", dist));
10175
10176                 //      Make sure repair has not broken off.
10177                 if (dist > 5.0f) {      //      Oops, too far away!
10178                         if ( goal_aip->ai_flags & AIF_BEING_REPAIRED )
10179                                 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BROKEN);
10180
10181                         if (dist > Pl_objp->radius*2 + goal_objp->radius*2) {
10182                                 //      Got real far away from goal, so move back a couple modes and try again.
10183                                 aip->submode = AIS_DOCK_2;
10184                                 aip->submode_start_time = Missiontime;
10185                         }
10186                 } else {
10187                         if ( goal_aip->ai_flags & AIF_AWAITING_REPAIR )
10188                                 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BEGIN );
10189                 }
10190
10191                 break;
10192                                                   }
10193
10194         case AIS_UNDOCK_0: {
10195                 int path_num;
10196                 //      First stage of undocking.
10197
10198                 //nprintf(("AI", "Undock 0:\n"));
10199
10200                 aip->submode = AIS_UNDOCK_1;
10201                 aip->submode_start_time = Missiontime;
10202                 if (aip->dock_objnum == -1) {
10203                         aip->submode = AIS_UNDOCK_3;
10204                 } else {
10205
10206                         // set up the path points for the undocking procedure.  dock_path_index member should
10207                         // have gotten set in the docking code.
10208                         Assert( aip->dock_path_index != -1 );
10209                         path_num = ai_return_path_num_from_dockbay(goal_objp, aip->dock_path_index);
10210                         ai_find_path(Pl_objp, goal_objp-Objects, path_num, 0);
10211
10212                         // Play a ship docking detach sound
10213                         snd_play_3d( &Snds[SND_DOCK_DETACH], &Pl_objp->pos, &View_position );
10214                 }
10215                 break;
10216                                                          }
10217         case AIS_UNDOCK_1: {
10218                 //      Using thrusters, exit from dock station to nearest next dock path point.
10219                 float   dist;
10220                 
10221                 //nprintf(("AI", "Undock 1: time in this mode = %7.3f\n", f2fl(Missiontime - aip->submode_start_time)));
10222
10223                 if (Missiontime - aip->submode_start_time < REARM_BREAKOFF_DELAY) {
10224                         break;          //      Waiting for one second to elapse to let detach sound effect play out.
10225                 }
10226                 else {  // AL - added 05/16/97.  Hack to play depart sound.  Will probably take out.
10227                                         // Assumes that the submode_start_time is not used for AIS_UNDOCK_1 anymore
10228                         if ( aip->submode_start_time != 0 )
10229                                 snd_play_3d( &Snds[SND_DOCK_DEPART], &Pl_objp->pos, &View_position );
10230                         aip->submode_start_time = 0;
10231                 }
10232
10233                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_1);
10234                 Assert(dist != UNINITIALIZED_VALUE);
10235
10236                 float dist_to_dock_obj = vm_vec_dist_quick(&Pl_objp->pos, &Objects[aip->goal_objnum].pos);
10237
10238                 //      Move to within 0.1 units of second last point on path before orienting, or just plain far away from docked-to ship.
10239                 //      This allows undock to complete if first ship flies away.
10240                 if ((dist < 2*flFrametime) || (dist_to_dock_obj > 2*Pl_objp->radius)) {
10241                         aip->submode = AIS_UNDOCK_2;
10242                         aip->submode_start_time = Missiontime;
10243                 }
10244                 break;
10245                                                          }
10246         case AIS_UNDOCK_2: {
10247                 float dist;
10248                 ai_info *other_aip;
10249
10250                 // get pointer to docked object's aip to reset flags, etc
10251                 Assert( aip->dock_objnum != -1 );
10252                 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
10253
10254                 //      Second stage of undocking.
10255                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_2);
10256                 Assert(dist != UNINITIALIZED_VALUE);
10257
10258
10259                 //nprintf(("AI", "Undock 2: dist = %7.3f\n", dist));
10260                 
10261                 //      If at goal point, or quite far away from dock object
10262                 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) ) {
10263                         // reset the dock flags.  If rearm/repair, reset rearm repair flags for those ships as well.
10264                         if ( sip->flags & SIF_SUPPORT ) {
10265                                 ai_do_objects_repairing_stuff( &Objects[aip->dock_objnum], Pl_objp, REPAIR_INFO_END );
10266                         }
10267
10268                         // clear out flags for AIF_DOCKED for both objects.
10269                         ai_do_objects_undocked_stuff( Pl_objp, goal_objp );
10270                         physics_ship_init(Pl_objp);
10271                         aip->submode = AIS_UNDOCK_3;                            //      The do-nothing mode, until another order is issued
10272
10273                         //aip->ai_flags &= ~AIF_DOCKED;         //      @MK, 9/18/97
10274                         //other_aip->ai_flags &= ~AIF_DOCKED;
10275                         //aip->dock_objnum = -1;                                        // invalidate who obj is docked with
10276                         //other_aip->dock_objnum = -1;                  // MWA 10/07/97 invalide docked objects dock_objnum value as well
10277
10278                         // don't add undock log entries for support ships.
10279                         if ( !(sip->flags & SIF_SUPPORT) )
10280                                 mission_log_add_entry(LOG_SHIP_UNDOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10281
10282                 }
10283                 break;
10284                 }
10285         case AIS_UNDOCK_3: {
10286                 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_3);
10287                 Assert(dist != UNINITIALIZED_VALUE);
10288
10289                 if (dist < Pl_objp->radius/2 + 5.0f) {
10290                         aip->submode = AIS_UNDOCK_4;
10291                 }
10292
10293                 // possible that this flag hasn't been cleared yet.  When aborting a rearm, this submode might
10294                 // be entered directly.
10295                 if ( (sip->flags & SIF_SUPPORT) && (aip->ai_flags & AIF_REPAIRING) ) {
10296                         ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], Pl_objp, REPAIR_INFO_ABORT );
10297                 }
10298
10299                 break;
10300                                                  }
10301         case AIS_UNDOCK_4: {
10302                 ai_info *other_aip;
10303
10304                 // MWA 10/07/97  I'm slightly confused by the dual use of goal_objnum and dock_objnum.  Seems to me
10305                 // that goal_objnum and dock_objnum are the same through this whole docking/undocking process, although
10306                 // I could be wrong.  dock_objnum was reset in undock_2 submode so try to use goal_objnum here to
10307                 // get other ships ai_info pointer
10308                 Assert( aip->goal_objnum != -1 );
10309                 other_aip = &Ai_info[Ships[Objects[aip->goal_objnum].instance].ai_index];
10310
10311                 aip->mode = AIM_NONE;
10312                 aip->dock_path_index = -1;              // invalidate the docking path index
10313
10314                 // these flags should have been cleared long ago!
10315                 // Get Allender if you hit one of these!!!!!
10316                 // removed by allender on 2/16 since a ship may be docked with some other ship, but still be the
10317                 // goal_objnum of this ship ending it's undocking mode.
10318                 //Assert( !(aip->ai_flags & AIF_DOCKED) );
10319                 //Assert( !(other_aip->ai_flags & AIF_DOCKED) );
10320                 //Assert( !(aip->ai_flags & AIF_REPAIRING) );
10321                 //Assert( !(other_aip->ai_flags & AIF_BEING_REPAIRED) );
10322                 //Assert( !(other_aip->ai_flags & AIF_AWAITING_REPAIR) );
10323
10324                 // only call mission goal complete if this was indeed an undock goal
10325                 if ( aip->active_goal > -1 ) {
10326                         if ( aip->goals[aip->active_goal].ai_mode == AI_GOAL_UNDOCK )
10327                                 ai_mission_goal_complete( aip );                        // this call should reset the AI mode
10328                         //else
10329                         //      aip->active_goal = -1;                                          // this ensures that this ship might get new goal
10330                 }
10331
10332                 break;
10333                                                          }
10334         default:
10335                 Int3(); //      Error, bogus submode
10336         }
10337
10338 }
10339
10340 // TURRET BEGIN
10341
10342 //      Given an object and a turret on that object, return the global position and forward vector
10343 //      of the turret.   The gun normal is the unrotated gun normal, (the center of the FOV cone), not
10344 // the actual gun normal given using the current turret heading.  But it _is_ rotated into the model's orientation
10345 //      in global space.
10346 void ship_get_global_turret_info(object *objp, model_subsystem *tp, vector *gpos, vector *gvec)
10347 {
10348         matrix  m;
10349         vm_copy_transpose_matrix(&m, &objp->orient);
10350 //      vm_vec_rotate(gpos, &tp->turret_avg_firing_point, &m);
10351         vm_vec_rotate(gpos, &tp->pnt, &m);
10352         vm_vec_add2(gpos, &objp->pos);
10353         vm_vec_rotate(gvec, &tp->turret_norm, &m);      
10354 }
10355
10356 // Given an object and a turret on that object, return the actual firing point of the gun
10357 // and its normal.   This uses the current turret angles.  We are keeping track of which
10358 // gun to fire next in the ship specific info for this turret subobject.  Use this info
10359 // to determine which position to fire from next.
10360 //      Stuffs:
10361 //              *gpos: absolute position of gun firing point
10362 //              *gvec: vector fro *gpos to *targetp
10363 void ship_get_global_turret_gun_info(object *objp, ship_subsys *ssp, vector *gpos, vector *gvec, int use_angles, vector *targetp)
10364 {
10365         vector * gun_pos;
10366         model_subsystem *tp = ssp->system_info;
10367
10368         ship_model_start(objp);
10369
10370         gun_pos = &tp->turret_firing_point[ssp->turret_next_fire_pos % tp->turret_num_firing_points];
10371
10372         model_find_world_point(gpos, gun_pos, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10373
10374         if (use_angles)
10375                 model_find_world_dir(gvec, &tp->turret_norm, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10376         else {
10377                 //vector        gun_pos2;
10378                 //vm_vec_add(&gun_pos2, gpos, gun_pos);
10379                 vm_vec_normalized_dir(gvec, targetp, gpos);
10380         }
10381
10382         ship_model_stop(objp);  
10383 }
10384
10385 //      Rotate a turret towards an enemy.
10386 //      Return TRUE if caller should use angles in subsequent rotations.
10387 //      Some obscure model thing only John Slagel knows about.
10388 //      Sets predicted enemy position.
10389 //      If the turret (*ss) has a subsystem targeted, the subsystem is used as the predicted point.
10390 int aifft_rotate_turret(ship *shipp, int parent_objnum, ship_subsys *ss, object *objp, object *lep, vector *predicted_enemy_pos, vector *gvec)
10391 {
10392         if (ss->turret_enemy_objnum != -1)      {
10393                 model_subsystem *tp = ss->system_info;
10394                 vector  gun_pos, gun_vec;
10395                 float           weapon_speed;
10396                 float           weapon_system_strength;
10397
10398                 //      weapon_system_strength scales time enemy in range in 0..1.  So, the lower this is, the worse the aiming will be.
10399                 weapon_system_strength = ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS);
10400
10401                 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gun_pos, &gun_vec);
10402
10403                 weapon_speed = Weapon_info[tp->turret_weapon_type].max_speed;
10404                 float weapon_travel_dist = weapon_speed * Weapon_info[tp->turret_weapon_type].lifetime;
10405
10406                 vector  enemy_point;
10407                 if (ss->targeted_subsys != NULL) {
10408                         if (ss->turret_enemy_objnum != -1) {
10409                                 vm_vec_unrotate(&enemy_point, &ss->targeted_subsys->system_info->pnt, &Objects[ss->turret_enemy_objnum].orient);
10410                                 vm_vec_add2(&enemy_point, &Objects[ss->turret_enemy_objnum].pos);
10411                         }
10412                 } else {
10413                         if ((lep->type == OBJ_SHIP) && (Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
10414                                 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));
10415                         } else {
10416                                 enemy_point = lep->pos;
10417                         }
10418                 }
10419
10420                 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);
10421
10422                 if (weapon_system_strength < 0.7f) {
10423                         vector  rand_vec;
10424
10425                         static_randvec(Missiontime >> 18, &rand_vec);   //      Return same random number for two seconds.
10426                         //      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.
10427                         vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, (1.0f - weapon_system_strength)*1.5f * lep->radius);
10428                 }
10429
10430                 vector  v2e;
10431                 vm_vec_normalized_dir(&v2e, predicted_enemy_pos, &gun_pos);
10432                 if (vm_vec_dot(&v2e, gvec) > tp->turret_fov) {
10433                         int     rval;
10434
10435                         rval = model_rotate_gun(shipp->modelnum, ss->system_info, &Objects[parent_objnum].orient, 
10436                                 &ss->submodel_info_1.angs, &ss->submodel_info_2.angs,
10437                                 &Objects[parent_objnum].pos, predicted_enemy_pos);
10438                 }
10439         }
10440
10441         return 0;
10442 }
10443
10444 //      Determine if subsystem *enemy_subsysp is hittable from objp.
10445 //      If so, return dot product of vector from point abs_gunposp to *enemy_subsysp
10446 float   aifft_compute_turret_dot(object *objp, object *enemy_objp, vector *abs_gunposp, ship_subsys *turret_subsysp, ship_subsys *enemy_subsysp)
10447 {
10448         float   dot_out;
10449         vector  subobj_pos, vector_out;
10450
10451         vm_vec_unrotate(&subobj_pos, &enemy_subsysp->system_info->pnt, &enemy_objp->orient);
10452         vm_vec_add2(&subobj_pos, &enemy_objp->pos);
10453
10454         if (ship_subsystem_in_sight(enemy_objp, enemy_subsysp, abs_gunposp, &subobj_pos, 1, &dot_out, &vector_out)) {
10455                 vector  turret_norm;
10456
10457                 vm_vec_rotate(&turret_norm, &turret_subsysp->system_info->turret_norm, &objp->orient);
10458                 return vm_vec_dot(&turret_norm, &vector_out);
10459         } else
10460                 return -1.0f;
10461
10462 }
10463
10464 #define MAX_AIFFT_TURRETS                       60
10465 ship_subsys *aifft_list[MAX_AIFFT_TURRETS];
10466 float aifft_rank[MAX_AIFFT_TURRETS];
10467 int aifft_list_size = 0;
10468 int aifft_max_checks = 5;
10469 DCF(mf, "")
10470 {
10471         dc_get_arg(ARG_INT);
10472         aifft_max_checks = Dc_arg_int;
10473 }
10474
10475
10476 //      Pick a subsystem to attack on enemy_objp.
10477 //      Only pick one if enemy_objp is a big ship or a capital ship.
10478 //      Returns dot product from turret to subsystem in *dot_out
10479 ship_subsys *aifft_find_turret_subsys(object *objp, ship_subsys *ssp, object *enemy_objp, float *dot_out)
10480 {
10481         ship    *eshipp, *shipp;
10482         ship_info       *esip;
10483         ship_subsys     *best_subsysp = NULL;
10484         float dot;
10485
10486         Assert(enemy_objp->type == OBJ_SHIP);
10487
10488         eshipp = &Ships[enemy_objp->instance];
10489         esip = &Ship_info[eshipp->ship_info_index];
10490
10491         shipp = &Ships[objp->instance];
10492
10493         float   best_dot = 0.0f;
10494         *dot_out = best_dot;
10495
10496         //      Compute absolute gun position.
10497         vector  abs_gun_pos;
10498         vm_vec_unrotate(&abs_gun_pos, &ssp->system_info->pnt, &objp->orient);
10499         vm_vec_add2(&abs_gun_pos, &objp->pos);
10500
10501         //      Only pick a turret to attack on large ships.
10502         if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
10503                 return best_subsysp;
10504
10505         // Make sure big or huge ship *actually* has subsystems  (ie, knossos)
10506         if (esip->n_subsystems == 0) {
10507                 return best_subsysp;
10508         }
10509
10510         // first build up a list subsystems to traverse
10511         ship_subsys     *pss;
10512         aifft_list_size = 0;
10513         for ( pss = GET_FIRST(&eshipp->subsys_list); pss !=END_OF_LIST(&eshipp->subsys_list); pss = GET_NEXT(pss) ) {
10514                 model_subsystem *psub = pss->system_info;
10515
10516                 // if we've reached max turrets bail
10517                 if(aifft_list_size >= MAX_AIFFT_TURRETS){
10518                         break;
10519                 }
10520
10521                 // Don't process destroyed objects
10522                 if ( pss->current_hits <= 0.0f ){
10523                         continue;
10524                 }
10525                 
10526                 switch (psub->type) {
10527                 case SUBSYSTEM_WEAPONS:
10528                         aifft_list[aifft_list_size] = pss;
10529                         aifft_rank[aifft_list_size++] = 1.4f;
10530                         break;
10531
10532                 case SUBSYSTEM_TURRET:
10533                         aifft_list[aifft_list_size] = pss;
10534                         aifft_rank[aifft_list_size++] = 1.2f;
10535                         break;
10536
10537                 case SUBSYSTEM_SENSORS:
10538                 case SUBSYSTEM_ENGINE:
10539                         aifft_list[aifft_list_size] = pss;
10540                         aifft_rank[aifft_list_size++] = 1.0f;
10541                         break;
10542                 }
10543         }
10544
10545         // DKA:  6/28/99 all subsystems can be destroyed.
10546         //Assert(aifft_list_size > 0);
10547         if (aifft_list_size == 0) {
10548                 return best_subsysp;
10549         }
10550
10551         // determine a stride value so we're not checking too many turrets
10552         int stride = aifft_list_size > aifft_max_checks ? aifft_list_size / aifft_max_checks : 1;
10553         if(stride <= 0){
10554                 stride = 1;
10555         }
10556         int offset = (int)frand_range(0.0f, (float)(aifft_list_size % stride));
10557         int idx;
10558         for(idx=offset; idx<aifft_list_size; idx+=stride){
10559                 dot = aifft_compute_turret_dot(objp, enemy_objp, &abs_gun_pos, ssp, aifft_list[idx]);                   
10560
10561                 if (dot* aifft_rank[idx] > best_dot) {
10562                         best_dot = dot*aifft_rank[idx];
10563                         best_subsysp = aifft_list[idx];
10564                 }
10565         }
10566
10567         Assert(best_subsysp != &eshipp->subsys_list);
10568
10569         *dot_out = best_dot;
10570         return best_subsysp;
10571 }
10572
10573 // Set active weapon for turret
10574 void ai_turret_select_default_weapon(ship_subsys *turret)
10575 {
10576         ship_weapon *twp;
10577
10578         twp = &turret->weapons;
10579
10580         // If a primary weapon is available, select it
10581         if ( twp->num_primary_banks > 0 ) {
10582                 turret->system_info->turret_weapon_type = twp->primary_bank_weapons[0];
10583         } else if ( twp->num_secondary_banks > 0 ) {
10584                 turret->system_info->turret_weapon_type = twp->secondary_bank_weapons[0];
10585         }
10586 }
10587
10588 // return !0 if the specified target should scan for a new target, otherwise return 0
10589 int turret_should_pick_new_target(ship_subsys *turret)
10590 {
10591 //      int target_type;
10592
10593         if ( timestamp_elapsed(turret->turret_next_enemy_check_stamp) ) {
10594                 return 1;
10595         }
10596
10597         return 0;
10598
10599 /*
10600         if ( turret->turret_enemy_objnum == -1 ) {
10601                 return 1;
10602         }
10603                 
10604         target_type = Objects[turret->turret_enemy_objnum].type;
10605         if ( (target_type != OBJ_SHIP) && (target_type != OBJ_ASTEROID) ) {
10606                 return 1;
10607         }
10608
10609         return 0;
10610 */
10611 }
10612
10613 // Set the next fire timestamp for a turret, based on weapon type and ai class
10614 void turret_set_next_fire_timestamp(ship_subsys *turret, ai_info *aip)
10615 {
10616         float   wait;
10617         int     weapon_id;
10618
10619         weapon_id = turret->system_info->turret_weapon_type;
10620
10621         wait = Weapon_info[weapon_id].fire_wait * 1000.0f;
10622
10623         // make side even for team vs. team
10624         if ((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) {
10625                 // flak guns need to fire more rapidly
10626                 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10627                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10628                         wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10629                 } else {
10630                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10631                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10632                 }
10633         } else {
10634                 // flak guns need to fire more rapidly
10635                 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10636                         if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10637                                 wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10638                         } else {
10639                                 wait *= Ship_fire_delay_scale_hostile[Game_skill_level] * 0.5f;
10640                         }       
10641                         wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10642
10643                 } else if (Weapon_info[weapon_id].wi_flags & WIF_HUGE) {
10644                         // make huge weapons fire independently of team
10645                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10646                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10647                 } else {
10648                         // give team friendly an advantage
10649                         if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10650                                 wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10651                         } else {
10652                                 wait *= Ship_fire_delay_scale_hostile[Game_skill_level];
10653                         }       
10654                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10655                 }
10656         }
10657
10658         // vary wait time +/- 10%
10659         wait *= frand_range(0.9f, 1.1f);
10660         turret->turret_next_fire_stamp = timestamp((int) wait);
10661 }
10662
10663 // Decide  if a turret should launch an aspect seeking missile
10664 int turret_should_fire_aspect(ship_subsys *turret, float dot, int weapon_class)
10665 {
10666         weapon_info *wip;
10667
10668         wip = &Weapon_info[weapon_class];
10669
10670         if ( (dot > AICODE_TURRET_DUMBFIRE_ANGLE) && (turret->turret_time_enemy_in_range >= min(wip->min_lock_time,AICODE_TURRET_MAX_TIME_IN_RANGE)) ) {
10671                 return 1;
10672         }
10673
10674         return 0;
10675 }
10676
10677 // Update how long current target has been in this turrets range
10678 void turret_update_enemy_in_range(ship_subsys *turret, float seconds)
10679 {
10680         turret->turret_time_enemy_in_range += seconds;
10681
10682         if ( turret->turret_time_enemy_in_range < 0.0f ) {
10683                 turret->turret_time_enemy_in_range = 0.0f;
10684         }
10685
10686         if ( turret->turret_time_enemy_in_range > AICODE_TURRET_MAX_TIME_IN_RANGE ) {
10687                 turret->turret_time_enemy_in_range = AICODE_TURRET_MAX_TIME_IN_RANGE;
10688         }
10689 }
10690
10691
10692
10693 // Fire a weapon from a turret
10694 void turret_fire_weapon(ship_subsys *turret, int parent_objnum, vector *turret_pos, vector *turret_fvec, vector *predicted_pos = NULL)
10695 {
10696         matrix  turret_orient;
10697         int             turret_weapon_class, weapon_objnum;
10698         ai_info *parent_aip;
10699         ship            *parent_ship;
10700         beam_fire_info fire_info;
10701         float flak_range = 0.0f;
10702
10703         parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
10704         parent_ship = &Ships[Objects[parent_objnum].instance];
10705         turret_weapon_class = turret->system_info->turret_weapon_type;
10706
10707         if (check_ok_to_fire(parent_objnum, turret->turret_enemy_objnum, &Weapon_info[turret_weapon_class])) {
10708                 vm_vector_2_matrix(&turret_orient, turret_fvec, NULL, NULL);
10709                 turret->turret_last_fire_direction = *turret_fvec;
10710
10711                 // set next fire timestamp for the turret
10712                 turret_set_next_fire_timestamp(turret, parent_aip);
10713
10714                 // if this weapon is a beam weapon, handle it specially
10715                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM){
10716                         // if this beam isn't free to fire
10717                         if (!(turret->weapons.flags & SW_FLAG_BEAM_FREE)) {
10718                                 Int3(); // should never get this far
10719                                 return;
10720                         }
10721
10722                         // stuff beam firing info
10723                         memset(&fire_info, 0, sizeof(beam_fire_info));
10724                         fire_info.accuracy = 1.0f;
10725                         fire_info.beam_info_index = turret_weapon_class;
10726                         fire_info.beam_info_override = NULL;
10727                         fire_info.shooter = &Objects[parent_objnum];
10728                         fire_info.target = &Objects[turret->turret_enemy_objnum];
10729                         fire_info.target_subsys = NULL;
10730                         fire_info.turret = turret;
10731
10732                         // fire a beam weapon
10733                         beam_fire(&fire_info);
10734                 } else {
10735
10736                         // don't fire swarm, but set up swarm info
10737                         if (Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM) {
10738                                 turret_swarm_set_up_info(parent_objnum, turret, turret_weapon_class);
10739                                 return;
10740                         } else {
10741                                 weapon_objnum = weapon_create( turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10742                                 weapon_set_tracking_info(weapon_objnum, parent_objnum, turret->turret_enemy_objnum, 1, turret->targeted_subsys);                
10743                         }
10744
10745                         //nprintf(("AI", "Turret_time_enemy_in_range = %7.3f\n", ss->turret_time_enemy_in_range));              
10746                         if (weapon_objnum != -1) {
10747                                 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10748                                 // AL 1-6-97: Store pointer to turret subsystem
10749                                 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10750
10751                                 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10752                                         // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10753                                         if ( parent_objnum != OBJ_INDEX(Player_obj) ) {                                         
10754                                                 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], turret_pos, &View_position );                                          
10755                                         }
10756                                 }               
10757
10758                                 // if the gun is a flak gun
10759                                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){                       
10760                                         // show a muzzle flash
10761                                         flak_muzzle_flash(turret_pos, turret_fvec, turret_weapon_class);
10762
10763                                         // pick a firing range so that it detonates properly                    
10764                                         flak_pick_range(&Objects[weapon_objnum], predicted_pos, ship_get_subsystem_strength(parent_ship, SUBSYSTEM_WEAPONS));
10765
10766                                         // determine what that range was
10767                                         flak_range = flak_get_range(&Objects[weapon_objnum]);
10768                                 }
10769
10770                                 // in multiplayer (and the master), then send a turret fired packet.
10771                                 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10772                                         int subsys_index;
10773
10774                                         subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10775                                         Assert( subsys_index != -1 );
10776                                         if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){                       
10777                                                 send_flak_fired_packet( parent_objnum, subsys_index, weapon_objnum, flak_range );
10778                                         } else {
10779                                                 send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10780                                         }
10781                                 }
10782                         }
10783                 }
10784         } else {
10785                 float wait = 1000.0f * frand_range(0.9f, 1.1f);
10786                 turret->turret_next_fire_stamp = timestamp((int) wait);
10787         }
10788 }
10789
10790 void turret_swarm_fire_from_turret(ship_subsys *turret, int parent_objnum, int target_objnum, ship_subsys *target_subsys)
10791 {
10792         int turret_weapon_class, weapon_objnum;
10793         matrix turret_orient;
10794         vector turret_pos, turret_fvec;
10795
10796         // parent not alive, quick out.
10797         if (Objects[parent_objnum].type != OBJ_SHIP) {
10798                 return;
10799         }
10800
10801         //      change firing point
10802         ship_get_global_turret_gun_info(&Objects[parent_objnum], turret, &turret_pos, &turret_fvec, 1, NULL);
10803         turret->turret_next_fire_pos++;
10804
10805         // get class [index into Weapon_info array
10806         turret_weapon_class = turret->system_info->turret_weapon_type;
10807         Assert(Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM);
10808
10809         // make turret_orient from turret_fvec -- turret->turret_last_fire_direction
10810         vm_vector_2_matrix(&turret_orient, &turret_fvec, NULL, NULL);
10811
10812         // create weapon and homing info
10813         weapon_objnum = weapon_create(&turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10814         weapon_set_tracking_info(weapon_objnum, parent_objnum, target_objnum, 1, target_subsys);
10815
10816         // do other cool stuff if weapon is created.
10817         if (weapon_objnum > -1) {
10818                 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10819                 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10820
10821                 // maybe sound
10822                 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10823                         // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10824                         if ( parent_objnum != OBJ_INDEX(Player_obj) ) {
10825                                 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], &turret_pos, &View_position );
10826                         }
10827                 }
10828                 
10829                 // in multiplayer (and the master), then send a turret fired packet.
10830                 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10831                         int subsys_index;
10832
10833                         subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10834                         Assert( subsys_index != -1 );
10835                         send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10836                 }
10837         }
10838 }
10839
10840 int Num_ai_firing = 0;
10841 int Num_find_turret_enemy = 0;
10842 int Num_turrets_fired = 0;
10843 //      Given a turret tp and its parent parent_objnum, fire from the turret at its enemy.
10844 void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
10845 {
10846         float           weapon_firing_range;
10847         vector  v2e;
10848         object  *lep;           //      Last enemy pointer
10849         model_subsystem *tp = ss->system_info;
10850         int             use_angles, turret_weapon_class;
10851         vector  predicted_enemy_pos;
10852         object  *objp;
10853         ai_info *aip;
10854
10855         if (!Ai_firing_enabled) {
10856                 return;
10857         }
10858
10859         if (ss->current_hits < 0.0f) {
10860                 return;
10861         }
10862
10863         if ( ship_subsys_disrupted(ss) ){               // AL 1/19/98: Make sure turret isn't suffering disruption effects
10864                 return;
10865         }
10866
10867         // Check turret free
10868         if (ss->weapons.flags & SW_FLAG_TURRET_LOCK) {
10869                 return;
10870         }
10871
10872         // If beam weapon, check beam free
10873         if ( (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) && !(ss->weapons.flags & SW_FLAG_BEAM_FREE) ) {
10874                 return;
10875         }
10876
10877         Assert( shipp->objnum == parent_objnum );
10878
10879         if ( tp->turret_weapon_type < 0 ){
10880                 return;
10881         }
10882
10883         // Monitor number of calls to ai_fire_from_turret
10884         Num_ai_firing++;
10885
10886         turret_weapon_class = tp->turret_weapon_type;
10887
10888         // AL 09/14/97: ensure ss->turret_enemy_objnum != -1 before setting lep
10889         if ( (ss->turret_enemy_objnum >= 0 && ss->turret_enemy_objnum < MAX_OBJECTS) && (ss->turret_enemy_sig == Objects[ss->turret_enemy_objnum].signature)) {
10890                 lep = &Objects[ss->turret_enemy_objnum];
10891
10892                 // MK -- here is where turret is targeting a bomb.  I simply return for now.  We should force
10893                 // a target change -- or better yet, never pick a weapon when this turret has a "huge" weapon
10894                 // loaded.
10895
10896                 // we only care about targets which are ships.
10897                 //if ( lep->type != OBJ_SHIP )
10898                 //      return;
10899
10900                 //      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.
10901                 if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HUGE ) {
10902                         if ( lep->type != OBJ_SHIP ) {
10903                                 return;
10904                         }
10905                         if ( !(Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
10906                                 return;
10907                         }
10908                 }
10909
10910                 // If targeting protected or beam protected ship, don't fire.  Reset enemy objnum
10911                 if (lep->type == OBJ_SHIP) {
10912                         // Check if we're targeting a protected ship
10913                         if (lep->flags & OF_PROTECTED) {
10914                                 ss->turret_enemy_objnum = -1;
10915                                 ss->turret_time_enemy_in_range = 0.0f;
10916                                 return;
10917                         }
10918
10919                         // Check if we're targeting a beam protected ship with a beam weapon
10920                         if ( (lep->flags & OF_BEAM_PROTECTED) && (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) ) {
10921                                 ss->turret_enemy_objnum = -1;
10922                                 ss->turret_time_enemy_in_range = 0.0f;
10923                                 return;
10924                         }
10925                 }
10926         } else {
10927                 ss->turret_enemy_objnum = -1;
10928                 lep = NULL;
10929         }
10930         
10931         Assert((parent_objnum >= 0) && (parent_objnum < MAX_OBJECTS));
10932         objp = &Objects[parent_objnum];
10933         Assert(objp->type == OBJ_SHIP);
10934         aip = &Ai_info[Ships[objp->instance].ai_index];
10935
10936         // Use the turret info for all guns, not one gun in particular.
10937         vector   gvec, gpos;
10938         ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
10939
10940         // Rotate the turret even if time hasn't elapsed, since it needs to turn to face its target.
10941         use_angles = aifft_rotate_turret(shipp, parent_objnum, ss, objp, lep, &predicted_enemy_pos, &gvec);
10942
10943         if ( !timestamp_elapsed(ss->turret_next_fire_stamp)){
10944                 return;
10945         }
10946
10947         // Don't try to fire beyond weapon_limit_range
10948         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);
10949
10950         // if beam weapon in nebula and target not tagged, decrase firing range
10951         extern int Nebula_sec_range;
10952         if (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) {
10953                 if ( !((shipp->tag_left > 0) || (shipp->level2_tag_left > 0)) ) {
10954                         if (Nebula_sec_range) {
10955                                 weapon_firing_range *= float(BEAM_NEBULA_RANGE_REDUCE_FACTOR);
10956                         }
10957                 }
10958         }
10959
10960         if (ss->turret_enemy_objnum != -1) {
10961                 float dist_to_enemy = vm_vec_normalized_dir(&v2e, &predicted_enemy_pos, &gpos) - lep->radius;
10962                 if (dist_to_enemy > weapon_firing_range) {
10963                         ss->turret_enemy_objnum = -1;           //      Force picking of new enemy.
10964                 }
10965         }
10966
10967         // Turret spawn weapons are a special case.  They fire if there are enough enemies in the 
10968         // immediate area (not necessarily in the turret fov).
10969         if ( Weapon_info[turret_weapon_class].wi_flags & WIF_SPAWN ) {
10970                 int num_ships_nearby;
10971                 num_ships_nearby = num_nearby_fighters(get_enemy_team_mask(parent_objnum), &gpos, 1500.0f);
10972                 if (( num_ships_nearby >= 3 ) || ((num_ships_nearby >= 2) && (frand() < 0.1f))) {
10973                         turret_fire_weapon(ss, parent_objnum, &gpos, &ss->turret_last_fire_direction);
10974                 } else {
10975                         ss->turret_next_fire_stamp = timestamp(1000);   //      Regardless of firing rate, don't check whether should fire for awhile.
10976                 }
10977                 return;
10978         }
10979
10980         //      Maybe pick a new enemy.
10981         if ( turret_should_pick_new_target(ss) ) {
10982                 Num_find_turret_enemy++;
10983                 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);
10984                 Assert(objnum < 0 || is_target_beam_valid(ss, objnum));
10985
10986                 if (objnum != -1) {
10987                         if (ss->turret_enemy_objnum == -1) {
10988                                 ss->turret_enemy_objnum = objnum;
10989                                 ss->turret_enemy_sig = Objects[objnum].signature;
10990                                 // why return?
10991                                 return;
10992                         } else {
10993                                 ss->turret_enemy_objnum = objnum;
10994                                 ss->turret_enemy_sig = Objects[objnum].signature;
10995                         }
10996                 } else {
10997                         ss->turret_enemy_objnum = -1;
10998                 }
10999
11000                 if (ss->turret_enemy_objnum != -1) {
11001                         float   dot = 1.0f;
11002                         lep = &Objects[ss->turret_enemy_objnum];
11003                         if ( lep->type == OBJ_SHIP ) {
11004                                 ss->targeted_subsys = aifft_find_turret_subsys(objp, ss, lep, &dot);                            
11005                         }
11006                         ss->turret_next_enemy_check_stamp = timestamp((int) (max(dot, 0.5f)*2000.0f) + 1000);
11007                 } else {
11008                         ss->turret_next_enemy_check_stamp = timestamp((int) (2000.0f * frand_range(0.9f, 1.1f)));       //      Check every two seconds
11009                 }
11010         }
11011
11012         //      If still don't have an enemy, return.  Or, if enemy is protected, return.
11013         if (ss->turret_enemy_objnum != -1) {
11014                 //      Don't shoot at ship we're going to dock with.
11015                 if (ss->turret_enemy_objnum == aip->dock_objnum) {
11016                         ss->turret_enemy_objnum = -1;
11017                         return;
11018                 }
11019
11020                 if (Objects[ss->turret_enemy_objnum].flags & OF_PROTECTED) {
11021                         //      This can happen if the enemy was selected before it became protected.
11022                         ss->turret_enemy_objnum = -1;
11023                         return;
11024                 }
11025                 lep = &Objects[ss->turret_enemy_objnum];
11026         } else {
11027                 if (timestamp_until(ss->turret_next_fire_stamp) < 500) {
11028                         ss->turret_next_fire_stamp = timestamp(500);
11029                 }
11030                 return;
11031         }
11032
11033         if ( lep == NULL ){
11034                 return;
11035         }
11036
11037         Assert(ss->turret_enemy_objnum != -1);
11038
11039         float dot = vm_vec_dot(&v2e, &gvec);
11040
11041         if (dot > tp->turret_fov ) {
11042                 // Ok, the turret is lined up... now line up a particular gun.
11043                 int ok_to_fire = 0;
11044                 float dist_to_enemy;
11045
11046                 // We're ready to fire... now get down to specifics, like where is the
11047                 // actual gun point and normal, not just the one for whole turret.
11048                 ship_get_global_turret_gun_info(&Objects[parent_objnum], ss, &gpos, &gvec, use_angles, &predicted_enemy_pos);
11049                 ss->turret_next_fire_pos++;
11050
11051                 // Fire in the direction the turret is facing, not right at the target regardless of turret dir.
11052                 vm_vec_sub(&v2e, &predicted_enemy_pos, &gpos);
11053                 dist_to_enemy = vm_vec_normalize(&v2e);
11054                 dot = vm_vec_dot(&v2e, &gvec);
11055
11056                 // 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
11057                 // and make them less lethal
11058                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){
11059                         flak_jitter_aim(&v2e, dist_to_enemy, ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS));
11060                 }
11061
11062                 // Fire if:
11063                 //              dumbfire and nearly pointing at target.
11064                 //              heat seeking and target in a fairly wide cone.
11065                 //              aspect seeking and target is locked.
11066                 turret_weapon_class = tp->turret_weapon_type;
11067
11068                 // if dumbfire (lasers and non-homing missiles)
11069                 if ( !(Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING) ) {
11070                         if ((dist_to_enemy < 75.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11071                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11072                                 ok_to_fire = 1;
11073                         }
11074                 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_HEAT ) {     // if heat seekers
11075                         if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_HEATSEEK_ANGLE )) {
11076                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11077                                 ok_to_fire = 1;
11078                         }
11079                 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_ASPECT ) {   // if aspect seeker
11080                         if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11081                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11082                         }
11083                         if ( turret_should_fire_aspect(ss, dot, turret_weapon_class) ) {
11084                                 ok_to_fire = 1;
11085                         }
11086                 }
11087
11088                 if ( ok_to_fire ) {
11089                         Num_turrets_fired++;
11090                         
11091                         turret_fire_weapon(ss, parent_objnum, &gpos, &v2e, &predicted_enemy_pos);                                               
11092                 } else {
11093                         turret_update_enemy_in_range(ss, -4*Weapon_info[tp->turret_weapon_type].fire_wait);
11094                         ss->turret_next_fire_stamp = timestamp(500);
11095                 }
11096         } else {
11097                 // Lost him!
11098                 ss->turret_enemy_objnum = -1;           //      Reset enemy objnum, find a new one next frame.
11099                 ss->turret_time_enemy_in_range = 0.0f;
11100         }
11101 }
11102
11103 // TURRET END
11104
11105 #ifndef NDEBUG
11106 #define MAX_AI_DEBUG_RENDER_STUFF       100
11107 typedef struct ai_render_stuff {
11108         ship_subsys     *ss;
11109         int                     parent_objnum;
11110 } ai_render_stuff;
11111
11112 ai_render_stuff AI_debug_render_stuff[MAX_AI_DEBUG_RENDER_STUFF];
11113
11114 int     Num_AI_debug_render_stuff = 0;
11115
11116 void ai_debug_render_stuff()
11117 {
11118         vertex  vert1, vert2;
11119         vector  gpos2;
11120         int             i;
11121
11122         for (i=0; i<Num_AI_debug_render_stuff; i++) {
11123                 ship_subsys     *ss;
11124                 int     parent_objnum;
11125                 vector  gpos, gvec;
11126                 model_subsystem *tp;
11127
11128                 ss = AI_debug_render_stuff[i].ss;
11129                 tp = ss->system_info;
11130
11131                 parent_objnum = AI_debug_render_stuff[i].parent_objnum;
11132
11133                 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
11134                 g3_rotate_vertex(&vert1, &gpos);
11135                 vm_vec_scale_add(&gpos2, &gpos, &gvec, 20.0f);
11136                 g3_rotate_vertex(&vert2, &gpos2);
11137                 gr_set_color(0, 0, 255);
11138                 g3_draw_sphere(&vert1, 2.0f);
11139                 gr_set_color(255, 0, 255);
11140                 g3_draw_sphere(&vert2, 2.0f);
11141                 g3_draw_line(&vert1, &vert2);
11142         }
11143
11144         // draw from beta to its goal point
11145 /*      for (i=0; i<6; i++) {
11146                 ai_info *aip = &Ai_info[i];
11147                 gr_set_color(0, 0, 255);
11148                 g3_rotate_vertex(&vert1, &Objects[i].pos);
11149                 g3_rotate_vertex(&vert2, &aip->goal_point);
11150                 g3_draw_line(&vert1, &vert2);
11151         } */
11152         
11153
11154         Num_AI_debug_render_stuff = 0;
11155 }
11156
11157 #endif
11158
11159 #ifndef NDEBUG
11160 int     Msg_count_4996 = 0;
11161 #endif
11162
11163 //      --------------------------------------------------------------------------
11164 // Process subobjects of object objnum.
11165 //      Deal with engines disabled.
11166 void process_subobjects(int objnum)
11167 {
11168         model_subsystem *psub;
11169         ship_subsys     *pss;
11170         object  *objp = &Objects[objnum];
11171         ship            *shipp = &Ships[objp->instance];
11172         ai_info *aip = &Ai_info[shipp->ai_index];
11173         ship_info       *sip = &Ship_info[shipp->ship_info_index];
11174
11175         for ( pss = GET_FIRST(&shipp->subsys_list); pss !=END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
11176                 psub = pss->system_info;
11177
11178                 // Don't process destroyed objects
11179                 if ( pss->current_hits <= 0.0f ) 
11180                         continue;
11181
11182                 switch (psub->type) {
11183                 case SUBSYSTEM_TURRET:
11184                         if ( psub->turret_num_firing_points > 0 )       {
11185                                 ai_fire_from_turret(shipp, pss, objnum);
11186                         } else {
11187 #ifndef NDEBUG
11188                                 if (!Msg_count_4996) {
11189                                         Warning( LOCATION, "Ship '%s' has turrets with no guns!\nProbably a model problem, so get an artist!", shipp->ship_name );
11190                                         Msg_count_4996++;
11191                                 }
11192 #endif
11193                                 }
11194                         break;
11195
11196                 case SUBSYSTEM_ENGINE:
11197                 case SUBSYSTEM_NAVIGATION:
11198                 case SUBSYSTEM_COMMUNICATION:
11199                 case SUBSYSTEM_WEAPONS:
11200                 case SUBSYSTEM_SENSORS:
11201                 case SUBSYSTEM_UNKNOWN:
11202                         break;
11203
11204                 // next set of subsystems may rotation
11205                 case SUBSYSTEM_RADAR:
11206                 case SUBSYSTEM_SOLAR:
11207                 case SUBSYSTEM_GAS_COLLECT:
11208                 case SUBSYSTEM_ACTIVATION:
11209                         break;
11210                 default:
11211                         Error(LOCATION, "Illegal subsystem type.\n");
11212                 }
11213
11214                 // do solar/radar/gas/activator rotation here
11215                 if ( psub->flags & MSS_FLAG_ROTATES )   {
11216                         if (psub->flags & MSS_FLAG_STEPPED_ROTATE       ) {
11217                                 submodel_stepped_rotate(psub, &pss->submodel_info_1);
11218                         } else {
11219                                 submodel_rotate(psub, &pss->submodel_info_1 );
11220                         }
11221                 }
11222
11223         }
11224
11225         //      Deal with a ship with blown out engines.
11226         if (ship_get_subsystem_strength(shipp, SUBSYSTEM_ENGINE) == 0.0f) {
11227                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
11228                         // AL: Only attack forever if not trying to depart to a docking bay.  Need to have this in, since
11229                         //     a ship may get repaired... and it should still try to depart.  Since docking bay departures
11230                         //     are not handled as goals, we don't want to leave the AIM_BAY_DEPART mode.
11231                         if ( aip->mode != AIM_BAY_DEPART ) {
11232                                 ai_attack_object(objp, NULL, 99, NULL);         //      Regardless of current mode, enter attack mode.
11233                                 aip->submode = SM_ATTACK_FOREVER;                               //      Never leave attack submode, don't avoid, evade, etc.
11234                         }
11235                 }
11236         }
11237
11238
11239 }
11240
11241 //      Given an object and the wing it's in, return its index in the wing list.
11242 //      This defines its location in the wing formation.
11243 //      If the object can't be found in the wing, return -1.
11244 //      *objp           object of interest
11245 //      wingnum the wing *objp is in
11246 int get_wing_index(object *objp, int wingnum)
11247 {
11248         wing    *wingp;
11249         int     i;
11250
11251         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11252
11253         wingp = &Wings[wingnum];
11254
11255         for (i=wingp->current_count-1; i>=0; i--)
11256                 if ( objp->instance == wingp->ship_index[i] )
11257                         break;
11258
11259         return i;               //      Note, returns -1 if string not found.
11260 }
11261
11262 //      Given a wing, return a pointer to the object of its leader.
11263 //      Asserts if object not found.
11264 //      Currently, the wing leader is defined as the first object in the wing.
11265 //      wingnum         Wing number in Wings array.
11266 //      If wing leader is disabled, swap it with another ship.
11267 object * get_wing_leader(int wingnum)
11268 {
11269         wing            *wingp;
11270         int             ship_num;
11271
11272         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11273
11274         wingp = &Wings[wingnum];
11275
11276         Assert(wingp->current_count != 0);                      //      Make sure there is a leader
11277
11278         ship_num = wingp->ship_index[0];
11279
11280         //      If this ship is disabled, try another ship in the wing.
11281         int n = 0;
11282         while (ship_get_subsystem_strength(&Ships[ship_num], SUBSYSTEM_ENGINE) == 0.0f) {
11283                 n++;
11284                 if (n >= wingp->current_count)
11285                         break;  
11286                 ship_num = wingp->ship_index[n];
11287         }
11288
11289         if (( n != 0) && (n != wingp->current_count)) {
11290                 int t = wingp->ship_index[0];
11291                 wingp->ship_index[0] = wingp->ship_index[n];
11292                 wingp->ship_index[n] = t;
11293         }
11294
11295         return &Objects[Ships[ship_num].objnum];
11296 }
11297
11298 #define DEFAULT_WING_X_DELTA            1.0f
11299 #define DEFAULT_WING_Y_DELTA            0.25f
11300 #define DEFAULT_WING_Z_DELTA            0.75f
11301 #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))
11302 // next constant is higher that MAX_SHIPS_IN_WINGS to deal with forming on player's wing
11303 #define MAX_FORMATION_ROWS              4
11304
11305 //      Given a position in a wing, return the desired location of the ship relative to the leader
11306 //      *_delta_vec             OUTPUT.  delta vector based on wing_index
11307 //      wing_index              position in wing.
11308 void get_wing_delta(vector *_delta_vec, int wing_index)
11309 {
11310         int     wi0;
11311
11312         Assert(wing_index >= 0);
11313
11314         int     k, row, column;
11315
11316         int bank = wing_index / (MAX_FORMATION_ROWS*(MAX_FORMATION_ROWS+1)/2);
11317         wi0 = wing_index % (MAX_FORMATION_ROWS * (MAX_FORMATION_ROWS+1)/2);
11318
11319         k = 0;
11320         for (row=1; row<MAX_FORMATION_ROWS+1; row++) {
11321                 k += row;
11322                 if (wi0 < k)
11323                         break;
11324         }
11325
11326         row--;
11327         column = wi0 - k + row + 1;
11328
11329         _delta_vec->xyz.x = ((float) column - (float) row/2.0f) * DEFAULT_WING_X_DELTA/DEFAULT_WING_MAG;
11330         _delta_vec->xyz.y = ((float)row + (float)bank*2.25f) * DEFAULT_WING_Y_DELTA/DEFAULT_WING_MAG;
11331         _delta_vec->xyz.z = - ((float)row + 0.5f * (float) bank) * DEFAULT_WING_Z_DELTA/DEFAULT_WING_MAG;
11332 }
11333
11334 //      Compute the largest radius of a ship in a *objp's wing.
11335 float gwlr_1(object *objp, ai_info *aip)
11336 {
11337         int             wingnum = aip->wing;
11338         float           max_radius;
11339         object  *o;
11340         ship_obj        *so;
11341
11342         Assert(wingnum >= 0);
11343
11344         max_radius = objp->radius;
11345
11346         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11347                 o = &Objects[so->objnum];
11348                 if (Ai_info[Ships[o->instance].ai_index].wing == wingnum)
11349                         if (o->radius > max_radius)
11350                                 max_radius = o->radius;
11351         }
11352
11353         return max_radius;
11354 }
11355
11356 //      Compute the largest radius of a ship forming on *objp's wing.
11357 float gwlr_object_1(object *objp, ai_info *aip)
11358 {
11359         float           max_radius;
11360         object  *o;
11361         ship_obj        *so;
11362
11363         max_radius = objp->radius;
11364
11365         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11366                 o = &Objects[so->objnum];
11367                 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == OBJ_INDEX(objp))
11368                         if (o->radius > max_radius)
11369                                 max_radius = o->radius;
11370         }
11371
11372         return max_radius;
11373 }
11374
11375 //      For the wing that *objp is part of, return the largest ship radius in that wing.
11376 float get_wing_largest_radius(object *objp, int formation_object_flag)
11377 {
11378         ship            *shipp;
11379         ai_info *aip;
11380
11381         Assert(objp->type == OBJ_SHIP);
11382         Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
11383         shipp = &Ships[objp->instance];
11384         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11385         aip = &Ai_info[shipp->ai_index];
11386
11387         if (formation_object_flag) {
11388                 return gwlr_object_1(objp, aip);
11389         } else {
11390                 return gwlr_1(objp, aip);
11391         }
11392
11393 }
11394
11395 float Wing_y_scale = 2.0f;
11396 float Wing_scale = 1.0f;
11397 DCF(wing_y_scale, "")
11398 {
11399         dc_get_arg(ARG_FLOAT);
11400         Wing_y_scale = Dc_arg_float;
11401 }
11402
11403 DCF(wing_scale, "")
11404 {
11405         dc_get_arg(ARG_FLOAT);
11406         Wing_scale = Dc_arg_float;
11407 }
11408
11409 // Given a wing leader and a position in the wing formation, return the desired absolute location to fly to.
11410 //      Returns result in *result_pos.
11411 void get_absolute_wing_pos(vector *result_pos, object *leader_objp, int wing_index, int formation_object_flag)
11412 {
11413         vector  wing_delta, rotated_wing_delta;
11414         float           wing_spread_size;
11415
11416         get_wing_delta(&wing_delta, wing_index);                //      Desired location in leader's reference frame
11417
11418         wing_spread_size = max(50.0f, 3.0f * get_wing_largest_radius(leader_objp, formation_object_flag) + 15.0f);
11419
11420         // for player obj (1) move ships up 20% (2) scale formation up 20%
11421         if (leader_objp->flags & OF_PLAYER_SHIP) {
11422                 wing_delta.xyz.y *= Wing_y_scale;
11423                 wing_spread_size *= Wing_scale;
11424         }
11425
11426         vm_vec_scale(&wing_delta, wing_spread_size * (1.0f + leader_objp->phys_info.speed/70.0f));
11427
11428         vm_vec_unrotate(&rotated_wing_delta, &wing_delta, &leader_objp->orient);        //      Rotate into leader's reference.
11429
11430         vm_vec_add(result_pos, &leader_objp->pos, &rotated_wing_delta); //      goal_point is absolute 3-space point.
11431 }
11432
11433 #ifndef NDEBUG
11434 int Debug_render_wing_phantoms;
11435
11436 void render_wing_phantoms(object *objp)
11437 {
11438         int             i;
11439         ship            *shipp;
11440         ai_info *aip;
11441         int             wingnum;
11442         int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11443         vector  goal_point;
11444         
11445         Assert(objp->type == OBJ_SHIP);
11446         Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11447
11448         shipp = &Ships[objp->instance];
11449         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11450
11451         aip = &Ai_info[shipp->ai_index];
11452
11453         wingnum = aip->wing;
11454
11455         if (wingnum == -1)
11456                 return;
11457
11458         wing_index = get_wing_index(objp, wingnum);
11459
11460         //      If this ship is NOT the leader, abort.
11461         if (wing_index != 0)
11462                 return;
11463
11464         for (i=0; i<32; i++)
11465                 if (Debug_render_wing_phantoms & (1 << i)) {
11466                         get_absolute_wing_pos(&goal_point, objp, i, 0);
11467         
11468                         vertex  vert;
11469                         gr_set_color(255, 0, 128);
11470                         g3_rotate_vertex(&vert, &goal_point);
11471                         g3_draw_sphere(&vert, 2.0f);
11472                 }
11473
11474         Debug_render_wing_phantoms = 0;
11475
11476 }
11477
11478 void render_wing_phantoms_all()
11479 {
11480         object  *objp;
11481         ship_obj        *so;
11482
11483         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11484                 ship            *shipp;
11485                 ai_info *aip;
11486                 int             wingnum;
11487                 int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11488
11489                 objp = &Objects[so->objnum];
11490                 
11491                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11492                 shipp = &Ships[objp->instance];
11493                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11494
11495                 aip = &Ai_info[shipp->ai_index];
11496
11497                 wingnum = aip->wing;
11498
11499                 if (wingnum == -1)
11500                         continue;
11501
11502                 wing_index = get_wing_index(objp, wingnum);
11503
11504                 //      If this ship is NOT the leader, abort.
11505                 if (wing_index != 0)
11506                         continue;
11507                 
11508                 render_wing_phantoms(objp);
11509
11510                 return;
11511         }
11512 }
11513
11514 #endif
11515
11516 //      Hook from goals code to AI.
11517 //      Force a wing to fly in formation.
11518 //      Sets AIF_FORMATION bit in ai_flags.
11519 //      wingnum         Wing to force to fly in formation
11520 void ai_fly_in_formation(int wingnum)
11521 {
11522         object  *objp;
11523         ship            *shipp;
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                 objp = &Objects[so->objnum];
11528                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11529
11530                 shipp = &Ships[objp->instance];
11531                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11532
11533                 if (Ai_info[shipp->ai_index].wing == wingnum) {
11534                         Ai_info[shipp->ai_index].ai_flags |= AIF_FORMATION_WING;
11535                         Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_OBJECT;
11536                 }
11537         }
11538 }
11539
11540 //      Hook from goals code to AI.
11541 //      Force a wing to abandon formation flying.
11542 //      Clears AIF_FORMATION bit in ai_flags.
11543 //      wingnum         Wing to force to fly in formation
11544 void ai_disband_formation(int wingnum)
11545 {
11546         object  *objp;
11547         ship            *shipp;
11548         ship_obj        *so;
11549
11550         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11551                 objp = &Objects[so->objnum];
11552                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11553
11554                 shipp = &Ships[objp->instance];
11555                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11556
11557                 if (Ai_info[shipp->ai_index].wing == wingnum) {
11558                         Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_WING;
11559                 }
11560         }
11561 }
11562
11563 float   Leader_chaos = 0.0f;
11564 int Chaos_frame = -1;
11565
11566 //      Return true if objp is flying in an erratic manner
11567 //      Only true if objp is a player
11568 int formation_is_leader_chaotic(object *objp)
11569 {
11570         if (Game_mode & GM_MULTIPLAYER)
11571                 return 0;
11572
11573         if (objp != Player_obj)
11574                 return 0;
11575
11576         if (Framecount != Chaos_frame) {
11577                 float   speed_scale;
11578                 float   fdot, udot;
11579
11580                 speed_scale = 3.0f + objp->phys_info.speed * 0.1f;
11581
11582                 fdot = 5.0f * (1.0f - vm_vec_dot(&objp->orient.v.fvec, &objp->last_orient.v.fvec)) * flFrametime;
11583                 udot = 8.0f * (1.0f - vm_vec_dot(&objp->orient.v.uvec, &objp->last_orient.v.uvec)) * flFrametime;
11584
11585                 Leader_chaos += fdot * speed_scale + udot * speed_scale;
11586
11587                 Leader_chaos *= (1.0f - flFrametime*0.2f);
11588
11589                 if (Leader_chaos < 0.0f)
11590                         Leader_chaos = 0.0f;
11591                 else if (Leader_chaos > 1.7f)
11592                         Leader_chaos = 1.7f;
11593
11594                 //nprintf(("AI", "Frame %i: chaos = %7.4f\n", Framecount, Leader_chaos));
11595
11596                 Chaos_frame = Framecount;
11597         }
11598
11599         return (Leader_chaos > 1.0f);
11600 }
11601
11602 // Fly in formation.
11603 //      Make Pl_objp assume its proper place in formation.
11604 //      If the leader of the wing is doing something stupid, like fighting a battle,
11605 //      then the poor sap wingmates will be in for a "world of hurt"
11606 //      Return TRUE if we need to process this object's normal mode
11607 int ai_formation()
11608 {
11609         object  *leader_objp;
11610         ship            *shipp;
11611         ai_info *aip, *laip;
11612         int             wingnum;
11613         int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11614         int             player_wing;    // index of the players wingnum
11615         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;
11616         float           dot_to_goal, dist_to_goal, leader_speed;
11617
11618         Assert(Pl_objp->type == OBJ_SHIP);
11619         Assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_SHIPS));
11620
11621         shipp = &Ships[Pl_objp->instance];
11622
11623         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11624
11625         aip = &Ai_info[shipp->ai_index];
11626
11627         Assert((aip->ai_flags & AIF_FORMATION) != AIF_FORMATION);       //      Make sure not both types of formation flying in effect.
11628
11629         //      Determine which kind of formation flying.
11630         //      If tracking an object, not in waypoint mode:
11631         if (aip->ai_flags & AIF_FORMATION_OBJECT) {
11632                 if ((aip->goal_objnum < 0) || (aip->goal_objnum >= MAX_OBJECTS)) {
11633                         aip->ai_flags &= ~AIF_FORMATION_OBJECT;
11634                         return 1;
11635                 }
11636                 
11637                 wing_index = ai_formation_object_get_slotnum(aip->goal_objnum, Pl_objp);
11638                 leader_objp = &Objects[aip->goal_objnum];
11639         } else {        //      Formation flying in waypoint mode.
11640                 Assert(aip->ai_flags & AIF_FORMATION_WING);
11641                 if (aip->mode != AIM_WAYPOINTS) {
11642                         aip->ai_flags &= ~AIF_FORMATION_WING;
11643                         return 1;
11644                 }
11645
11646                 wingnum = aip->wing;
11647
11648                 if (wingnum == -1)
11649                         return 1;
11650
11651                 // disable formation flying for any ship in the players wing
11652                 player_wing = Ships[Player_obj->instance].wingnum;
11653                 if ( (player_wing != -1) && (wingnum == player_wing) )
11654                         return 1;
11655
11656                 wing_index = get_wing_index(Pl_objp, wingnum);
11657
11658                 leader_objp = get_wing_leader(wingnum);
11659
11660         }
11661
11662         //      If docked with a ship in this wing, only the more massive one actually flies in formation.
11663         if (aip->dock_objnum != -1) {
11664                 object  *other_objp = &Objects[aip->dock_objnum];
11665                 ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
11666
11667                 if (aip->wing == other_aip->wing) {
11668                         if (Pl_objp->phys_info.mass < other_objp->phys_info.mass)
11669                                 return 0;
11670                         else if (Pl_objp->phys_info.mass == other_objp->phys_info.mass) {
11671                                 if (Pl_objp->signature < other_objp->signature)
11672                                         return 0;
11673                         }
11674                 }
11675         }
11676
11677         Assert(leader_objp != NULL);
11678         laip = &Ai_info[Ships[leader_objp->instance].ai_index];
11679
11680         //      Make sure we're really in this wing.
11681         if (wing_index == -1)
11682                 return 1;
11683
11684         //      If this ship is the leader, abort, as he doesn't have to follow anyone.
11685         if (wing_index == 0) {
11686                 // nprintf(("AI", "Hmm, wing leader %s in ai_formation for no good reason.\n", shipp->ship_name));
11687                 return 1;
11688         }
11689
11690         if (aip->mode == AIM_WAYPOINTS) {
11691                 aip->wp_list = laip->wp_list;
11692                 if (laip->wp_index < Waypoint_lists[laip->wp_list].count)
11693                         aip->wp_index = laip->wp_index;
11694                 else
11695                         aip->wp_index = Waypoint_lists[laip->wp_list].count - 1;
11696                 aip->wp_flags = laip->wp_flags;
11697                 aip->wp_dir = laip->wp_dir;
11698         }
11699
11700         #ifndef NDEBUG
11701         Debug_render_wing_phantoms |= (1 << wing_index);
11702         #endif
11703
11704         leader_speed = leader_objp->phys_info.speed;
11705         vector leader_vec = leader_objp->phys_info.vel;
11706
11707         get_absolute_wing_pos(&goal_point, leader_objp, wing_index, aip->ai_flags & AIF_FORMATION_OBJECT);
11708         vm_vec_scale_add(&future_goal_point_5, &goal_point, &leader_vec, 10.0f);
11709         vm_vec_scale_add(&future_goal_point_2, &goal_point, &leader_vec, 5.0f);
11710         vm_vec_scale_add(&future_goal_point_x, &goal_point, &leader_objp->orient.v.fvec, 10.0f);        //      used when very close to destination
11711         vm_vec_scale_add(&future_goal_point_1000x, &goal_point, &leader_objp->orient.v.fvec, 1000.0f);  //      used when very close to destination
11712
11713         //      Now, get information telling this object how to turn and accelerate to get to its
11714         //      desired location.
11715         vm_vec_sub(&vec_to_goal, &goal_point, &Pl_objp->pos);
11716         if ( vm_vec_mag_quick(&vec_to_goal) < AICODE_SMALL_MAGNITUDE )
11717                 vec_to_goal.xyz.x += 0.1f;
11718
11719         vm_vec_copy_normalize(&dir_to_goal, &vec_to_goal);
11720         //dot_to_goal = vm_vec_dot(&dir_to_goal, &leader_objp->orient.v.fvec);
11721         dot_to_goal = vm_vec_dot(&dir_to_goal, &Pl_objp->orient.v.fvec);
11722         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &goal_point);
11723         float   dist_to_goal_2 = vm_vec_dist_quick(&Pl_objp->pos, &future_goal_point_2);
11724
11725         // 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));
11726
11727         int     chaotic_leader = 0;
11728
11729         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.
11730
11731         if (dist_to_goal > 500.0f) {
11732                 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11733                 accelerate_ship(aip, 1.0f);
11734         } else if (dist_to_goal > 200.0f) {
11735                 if (dot_to_goal > -0.5f) {
11736                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11737                         float range_speed = shipp->current_max_speed - leader_speed;
11738                         if (range_speed > 0.0f)
11739                                 set_accel_for_target_speed(Pl_objp, leader_speed + range_speed * (dist_to_goal+100.0f)/500.0f);
11740                         else
11741                                 set_accel_for_target_speed(Pl_objp, shipp->current_max_speed);
11742                 } else {
11743                         turn_towards_point(Pl_objp, &future_goal_point_5, NULL, 0.0f);
11744                         if (leader_speed > 10.0f)
11745                                 set_accel_for_target_speed(Pl_objp, leader_speed *(1.0f + dot_to_goal));
11746                         else
11747                                 set_accel_for_target_speed(Pl_objp, 10.0f);
11748                 }
11749         } else {
11750                 vector  v2f2;
11751                 float   dot_to_f2;
11752                 float   dist_to_f2;
11753
11754                 dist_to_f2 = vm_vec_normalized_dir(&v2f2, &future_goal_point_2, &Pl_objp->pos);
11755                 dot_to_f2 = vm_vec_dot(&v2f2, &Pl_objp->orient.v.fvec);
11756
11757                 //      Leader flying like a maniac.  Don't try hard to form on wing.
11758                 if (chaotic_leader) {
11759                         turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11760                         set_accel_for_target_speed(Pl_objp, min(leader_speed*0.8f, 20.0f));
11761                 } else if (dist_to_goal > 75.0f) {
11762                         turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11763                         float   delta_speed;
11764                         float range_speed = shipp->current_max_speed - leader_speed;
11765                         if (range_speed > 0.0f)
11766                                 delta_speed = dist_to_goal_2/500.0f * range_speed;
11767                         else
11768                                 delta_speed = shipp->current_max_speed - leader_speed;
11769                         if (dot_to_goal < 0.0f) {
11770                                 delta_speed = -delta_speed;
11771                                 if (-delta_speed > leader_speed/2)
11772                                         delta_speed = -leader_speed/2;
11773                         }
11774
11775                         if (leader_speed < 5.0f)
11776                                 if (delta_speed < 5.0f)
11777                                         delta_speed = 5.0f;
11778
11779                         float scale = dot_to_f2;
11780                         if (scale < 0.1f)
11781                                 scale = 0.0f;
11782                         else
11783                                 scale *= scale;
11784
11785                         set_accel_for_target_speed(Pl_objp, scale * (leader_speed + delta_speed));
11786                 } else {
11787                         //nprintf(("AI", "Dot = %7.3f\n", dot_to_goal));
11788
11789                         if (leader_speed < 5.0f) {
11790                                 //      Leader very slow.  If not close to goal point, get very close.  Note, keep trying to get close unless
11791                                 //      moving very slowly, else momentum can carry far away from goal.
11792
11793                                 if ((dist_to_goal > 10.0f) || ((Pl_objp->phys_info.speed > leader_speed + 2.5f) && (dot_to_goal > 0.5f))) {
11794                                         //nprintf(("MK", "(1) "));
11795                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11796                                         set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/10.0f);
11797                                 } else {
11798                                         if (Pl_objp->phys_info.speed < 0.5f) {
11799                                                 //nprintf(("MK", "(2) "));
11800                                                 turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11801                                         } else {
11802                                                 //nprintf(("MK", "(3) "));
11803                                         }
11804                                         set_accel_for_target_speed(Pl_objp, leader_speed);
11805                                 }
11806                                 //nprintf(("MK", "dist: %7.3f, dot: %6.3f, speed: %7.3f\n", dist_to_goal, dot_to_goal, Pl_objp->phys_info.speed));
11807                         } else if (dist_to_goal > 10.0f) {
11808                                 float   dv;
11809
11810                                 //future_goal_point_2;
11811
11812                                 turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11813
11814                                 if (dist_to_goal > 25.0f) {
11815                                         if (dot_to_goal < 0.3f)
11816                                                 dv = -0.1f;
11817                                         else
11818                                                 dv = dot_to_goal - 0.2f;
11819
11820                                         set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/5.0f * dv);
11821                                 } else {
11822                                         set_accel_for_target_speed(Pl_objp, leader_speed + 1.5f * dot_to_goal - 1.0f);
11823                                 }
11824                         } else {
11825                                 if (Pl_objp->phys_info.speed < 0.1f)
11826                                         turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11827                                 else
11828                                         turn_towards_point(Pl_objp, &future_goal_point_x, NULL, 0.0f);
11829                                 set_accel_for_target_speed(Pl_objp, 0.0f);
11830                         }
11831                 }
11832
11833         }
11834
11835         //      See how different this ship's bank is relative to wing leader
11836         float   up_dot = vm_vec_dot(&leader_objp->orient.v.uvec, &Pl_objp->orient.v.uvec);
11837         if (up_dot < 0.996f) {
11838                 vector  w_out;
11839                 matrix  new_orient;
11840                 vector  angular_accel;
11841
11842                 vm_vec_copy_scale(&angular_accel, &Pl_objp->phys_info.max_rotvel, 0.2f);
11843                 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);
11844
11845         //      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)));
11846                 Pl_objp->orient = new_orient;
11847                 Pl_objp->phys_info.rotvel = w_out;
11848         //      Pl_objp->phys_info.desired_rotvel = w_out;
11849         } else {
11850                 Pl_objp->phys_info.rotvel.xyz.z = 0.0f;
11851         }
11852
11853         return 0;
11854 }
11855
11856 //      Return index of object repairing object objnum.
11857 int find_repairing_objnum(int objnum)
11858 {
11859         object          *objp;
11860         ship                    *shipp;
11861         ship_info       *sip;
11862         ship_obj                *so;
11863
11864         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11865                 objp = &Objects[so->objnum];
11866
11867                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11868
11869                 shipp = &Ships[objp->instance];
11870                 sip = &Ship_info[shipp->ship_info_index];
11871
11872                 if (sip->flags & SIF_SUPPORT) {
11873                         ai_info *aip;
11874
11875                         aip = &Ai_info[shipp->ai_index];
11876
11877                         if (aip->goal_objnum == objnum) {
11878                                 return objp-Objects;
11879                         }
11880                 }
11881         }
11882
11883         return -1;
11884 }
11885
11886 //      If object *objp is being repaired, deal with it!
11887 void ai_do_repair_frame(object *objp, ai_info *aip, float frametime)
11888 {
11889         if (Ships[objp->instance].team == TEAM_TRAITOR) {
11890                 ai_abort_rearm_request(objp);
11891                 return;
11892         }
11893
11894         if (aip->ai_flags & (AIF_BEING_REPAIRED | AIF_AWAITING_REPAIR)) {
11895                 int     dock_objnum;
11896                 ai_info *repair_aip;
11897
11898                 dock_objnum = aip->dock_objnum; // find_repairing_objnum(objp-Objects);
11899                 //Assert(dock_objnum != -1);
11900                 if (dock_objnum == -1)
11901                         return;
11902                 if (Objects[dock_objnum].signature != aip->dock_signature) {
11903                         Int3();         //      Curious -- object numbers match, but signatures do not.
11904                                                         //      Must mean original repair ship died and was replaced by current ship.
11905                         return;
11906                 }
11907         
11908                 repair_aip = &Ai_info[Ships[Objects[dock_objnum].instance].ai_index];
11909                 //Assert(repair_aip->mode == AIM_DOCK);
11910
11911                 if (aip->ai_flags & AIF_BEING_REPAIRED) {
11912                         // Assert(repair_aip->submode == AIS_DOCK_4);
11913
11914                         //      Wait awhile into the mode to synchronize with sound effect.
11915                         if (Missiontime - repair_aip->submode_start_time > REARM_SOUND_DELAY) {
11916                                 int repaired;
11917
11918                                 repaired = ship_do_rearm_frame( objp, frametime );              // hook to do missile rearming
11919
11920                                 //      See if fully repaired.  If so, cause process to stop.
11921                                 if ( repaired && (repair_aip->submode == AIS_DOCK_4)) {
11922
11923                                         repair_aip->submode = AIS_UNDOCK_0;
11924                                         repair_aip->submode_start_time = Missiontime;
11925
11926                                         // if repairing player object -- tell him done with repair
11927                                         if ( !MULTIPLAYER_CLIENT ){
11928                                                 ai_do_objects_repairing_stuff( objp, &Objects[dock_objnum], REPAIR_INFO_COMPLETE );
11929                                         }
11930                                 }
11931                         }
11932                 } else if (aip->ai_flags & AIF_AWAITING_REPAIR) {
11933                         //      If this ship has been awaiting repair for 90+ seconds, abort.
11934                         if ( !MULTIPLAYER_CLIENT ) {
11935                                 if ((Game_mode & GM_MULTIPLAYER) || (objp != Player_obj)) {
11936                                         if ((repair_aip->goal_objnum == OBJ_INDEX(objp)) && (timestamp_elapsed(aip->abort_rearm_timestamp))) {
11937                                                 ai_abort_rearm_request(objp);
11938                                                 aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);
11939                                         }
11940                                 }
11941                         }
11942                 }
11943         } else {
11944                 // AL 11-24-97: If this is the player ship, ensure the repair sound isn't playing.  We need to
11945                 //              do this check, since this is a looping sound, and may continue on if rearm/repair
11946                 //              finishes abnormally once sound begins looping.
11947                 if ( objp == Player_obj ) {
11948                         player_stop_repair_sound();
11949                 }
11950         }
11951 }
11952
11953 //      Shell around dock_orient_and_approach to detect whether dock process should be aborted.
11954 //      obj1 is the ship performing the repair.
11955 //      obj2 is the ship being repaired.
11956 void call_doa(object *obj1, object *obj2, ship_info *sip1)
11957 {
11958         if (sip1->flags & SIF_SUPPORT) {
11959                 if (obj2->phys_info.speed > MAX_REPAIR_SPEED) {
11960
11961                         // call the ai_abort rearm request code
11962                         ai_abort_rearm_request( obj2 );
11963                 } else
11964                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11965         } else {
11966                 if (Ship_info[Ships[obj1->instance].ship_info_index].flags & SIF_CARGO)
11967                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11968                 else if (Ship_info[Ships[obj2->instance].ship_info_index].flags & SIF_CARGO)
11969                         dock_orient_and_approach(obj2, obj1, DOA_DOCK_STAY);
11970                 else {
11971                         //mprintf(("Warning: Not sure, but making %s [%s] move to stay docked with %s [%s]\n",
11972                         //      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));
11973                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11974
11975                 }
11976         }
11977
11978 }
11979
11980 //      Maybe launch a countermeasure.
11981 //      Also, detect a supposed homing missile that no longer exists.
11982 void ai_maybe_launch_cmeasure(object *objp, ai_info *aip)
11983 {
11984         float                   dist;
11985         ship_info       *sip;
11986         ship                    *shipp;
11987
11988         shipp = &Ships[objp->instance];
11989         sip = &Ship_info[shipp->ship_info_index];
11990
11991         if (!(sip->flags & (SIF_SMALL_SHIP | SIF_TRANSPORT)))
11992                 return;
11993
11994         if (!shipp->cmeasure_count)
11995                 return;
11996
11997         if ( !timestamp_elapsed(shipp->cmeasure_fire_stamp) )
11998                 return;
11999
12000         //      If not on player's team and Skill_level + ai_class is low, never fire a countermeasure.  The ship is too dumb.
12001         if (shipp->team != Player_ship->team) {
12002                 if (Game_skill_level + aip->ai_class < 4){
12003                         return;
12004                 }
12005         }
12006
12007         if ((aip->nearest_locked_object != -1) && (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)) {
12008                 object  *weapon_objp;
12009                 weapon  *weaponp;
12010                 weapon_info     *wip;
12011
12012                 weapon_objp = &Objects[aip->nearest_locked_object];
12013                 weaponp = &Weapons[weapon_objp->instance];
12014                 wip = &Weapon_info[weaponp->weapon_info_index];
12015
12016                 if ((dist = vm_vec_dist_quick(&objp->pos, &weapon_objp->pos)) < weapon_objp->phys_info.speed*2.0f) {
12017         
12018                         aip->nearest_locked_distance = dist;
12019                         //      Verify that this object is really homing on us.
12020                         object  *weapon_objp;
12021
12022                         weapon_objp = &Objects[aip->nearest_locked_object];
12023
12024                         float   fire_chance;
12025
12026                         //      For ships on player's team, have constant, average chance to fire.
12027                         //      For enemies, increasing chance with higher skill level.
12028                         if (shipp->team == Player_ship->team)
12029                                 fire_chance = Cmeasure_fire_chance[NUM_SKILL_LEVELS/2];
12030                         else
12031                                 fire_chance = Cmeasure_fire_chance[Game_skill_level];
12032
12033                         //      Decrease chance to fire at lower ai class.
12034                         fire_chance *= (float) aip->ai_class/Num_ai_classes;
12035
12036                         float r = frand();
12037                         if (fire_chance < r) {
12038                                 //nprintf(("AI", "Not firing countermeasure due to skill level: %7.3f < %7.3f\n", fire_chance, r));
12039                                 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.
12040                                 return;
12041                         }
12042
12043                         if (weapon_objp->type == OBJ_WEAPON) {
12044                                 if (weapon_objp->instance >= 0) {
12045                                         //nprintf(("AI", "Firing countermeasure at time t=%7.3f\n", f2fl(Missiontime)));
12046                                         ship_launch_countermeasure(objp);
12047                                         shipp->cmeasure_fire_stamp = timestamp(2*CMEASURE_WAIT);
12048                                         return;
12049                                 }
12050                         }
12051         
12052                 }
12053         }
12054
12055         return;
12056 }
12057
12058 //      --------------------------------------------------------------------------
12059 void ai_preprocess_ignore_objnum(object *objp, ai_info *aip)
12060 {
12061 //      if (aip->ignore_objnum == UNUSED_OBJNUM)
12062 //              return;
12063
12064         if (aip->ai_flags & AIF_TEMPORARY_IGNORE) {
12065                 if (timestamp_elapsed(aip->ignore_expire_timestamp)) {
12066                         aip->ignore_objnum = UNUSED_OBJNUM;
12067                 }
12068         }
12069
12070         if (is_ignore_object(aip, aip->goal_objnum)) {
12071                 aip->goal_objnum = -1;
12072                 // AL 12-11-97: If in STRAFE mode, we need to ensure that target_objnum is also
12073                 //              set to -1
12074                 if ( aip->mode == AIM_STRAFE ) {
12075                         aip->target_objnum = -1;
12076                 }
12077         }
12078
12079         if (is_ignore_object(aip, aip->target_objnum))
12080                 aip->target_objnum = -1;
12081 }
12082
12083 /*
12084 void ai_safety_circle_spot()
12085 {
12086         vector  goal_point;
12087         ship_info       *sip;
12088
12089         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12090
12091         goal_point = Ai_info[Ships[Pl_objp->instance].ai_index].goal_point;
12092         turn_towards_tangent(Pl_objp, &goal_point, 50.0f);
12093
12094         set_accel_for_target_speed(Pl_objp, sip->max_speed/4.0f);
12095
12096 //      float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
12097 //      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));
12098
12099 }
12100 */
12101
12102 #define CHASE_CIRCLE_DIST               100.0f
12103
12104 void ai_chase_circle(object *objp)
12105 {
12106         float           dist_to_goal;
12107         float           target_speed;
12108         vector  goal_point;
12109         ship_info       *sip;
12110         ai_info         *aip;
12111
12112         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12113
12114         target_speed = sip->max_speed/4.0f;
12115         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12116
12117         Assert(vm_vec_mag(&aip->goal_point) >= 0.0f);           //      Supposedly detects bogus vector
12118
12119         goal_point = aip->goal_point;
12120
12121         if (aip->ignore_objnum == UNUSED_OBJNUM) {
12122                 dist_to_goal = vm_vec_dist_quick(&aip->goal_point, &objp->pos);
12123
12124                 if (dist_to_goal > 2*CHASE_CIRCLE_DIST) {
12125                         vector  vec_to_goal;
12126                         //      Too far from circle goal, create a new goal point.
12127                         vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
12128                         vm_vec_scale_add(&aip->goal_point, &objp->pos, &vec_to_goal, CHASE_CIRCLE_DIST);
12129                 }
12130
12131                 goal_point = aip->goal_point;
12132         } else if (is_ignore_object(aip, aip->ignore_objnum)) {
12133                 object  *ignore_objp = &Objects[aip->ignore_objnum];
12134
12135                 vector  tvec1;
12136                 float           dist;
12137
12138                 dist = vm_vec_normalized_dir(&tvec1, &Pl_objp->pos, &ignore_objp->pos);
12139
12140                 if (dist < ignore_objp->radius*2 + 1500.0f) {
12141                         vm_vec_scale_add(&goal_point, &Pl_objp->pos, &tvec1, ignore_objp->radius*2 + 1400.0f);
12142                         if (dist < ignore_objp->radius*2 + 1300.0f)
12143                                 target_speed = sip->max_speed * (1.25f - dist/(ignore_objp->radius*2 + 1500.0f));
12144                 }
12145         }
12146
12147         Assert(vm_vec_mag(&aip->goal_point) >= 0.0f);           //      Supposedly detects bogus vector
12148
12149         turn_towards_tangent(Pl_objp, &goal_point, 10*objp->radius + 200.0f);
12150
12151         set_accel_for_target_speed(Pl_objp, target_speed);
12152
12153 }
12154
12155 #define SHIELD_BALANCE_RATE     0.2f            //      0.1f -> takes 10 seconds to equalize shield.
12156
12157 //      Transfer shield energy to most recently hit section from others.
12158 void ai_transfer_shield(object *objp, int quadrant_num)
12159 {
12160         int     i;
12161         float   transfer_amount;
12162         float   transfer_delta;
12163         ship_info       *sip;
12164         float   max_quadrant_strength;
12165
12166         sip = &Ship_info[Ships[objp->instance].ship_info_index];
12167         max_quadrant_strength = sip->shields/MAX_SHIELD_SECTIONS;
12168
12169         transfer_amount = 0.0f;
12170         transfer_delta = (SHIELD_BALANCE_RATE/2) * max_quadrant_strength;
12171
12172         if (objp->shields[quadrant_num] + (MAX_SHIELD_SECTIONS-1)*transfer_delta > max_quadrant_strength)
12173                 transfer_delta = (max_quadrant_strength - objp->shields[quadrant_num])/(MAX_SHIELD_SECTIONS-1);
12174
12175         for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12176                 if (i != quadrant_num) {
12177                         if (objp->shields[i] >= transfer_delta) {
12178                                 objp->shields[i] -= transfer_delta;
12179                                 transfer_amount += transfer_delta;
12180                         } else {
12181                                 transfer_amount += objp->shields[i];
12182                                 objp->shields[i] = 0.0f;
12183                         }
12184                 }
12185
12186         objp->shields[quadrant_num] += transfer_amount;
12187 }
12188
12189 void ai_balance_shield(object *objp)
12190 {
12191         int     i;
12192         float   shield_strength_avg;
12193         float   delta;
12194
12195
12196         shield_strength_avg = get_shield_strength(objp)/MAX_SHIELD_SECTIONS;
12197
12198         delta = SHIELD_BALANCE_RATE * shield_strength_avg;
12199
12200         for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12201                 if (objp->shields[i] < shield_strength_avg) {
12202                         add_shield_strength(objp, delta);
12203                         if (objp->shields[i] > shield_strength_avg)
12204                                 objp->shields[i] = shield_strength_avg;
12205                 } else {
12206                         add_shield_strength(objp, -delta);
12207                         if (objp->shields[i] < shield_strength_avg)
12208                                 objp->shields[i] = shield_strength_avg;
12209                 }
12210 }
12211
12212 //      Manage the shield for this ship.
12213 //      Try to max out the side that was most recently hit.
12214 void ai_manage_shield(object *objp, ai_info *aip)
12215 {
12216         ship_info *sip;
12217
12218         sip = &Ship_info[Ships[objp->instance].ship_info_index];
12219
12220         if (timestamp_elapsed(aip->shield_manage_timestamp)) {
12221                 float           delay;
12222
12223                 //      Scale time until next manage shield based on Skill_level.
12224                 //      Ships on player's team are treated as if Skill_level is average.
12225                 if (Ships[objp->instance].team != Player_ship->team){
12226                         delay = Shield_manage_delays[Game_skill_level];
12227                 } else {
12228                         delay = Shield_manage_delays[NUM_SKILL_LEVELS/2];
12229                 }
12230
12231                 //      Scale between 1x and 3x based on ai_class
12232                 delay = delay + delay * (float) (3*(Num_ai_classes - aip->ai_class - 1) / (Num_ai_classes - 1));
12233                 aip->shield_manage_timestamp = timestamp((int) (delay * 1000.0f));
12234
12235                 if (sip->flags & SIF_SMALL_SHIP) {
12236                         if (Missiontime - aip->last_hit_time < F1_0*10)
12237                                 ai_transfer_shield(objp, aip->last_hit_quadrant);
12238                         else
12239                                 ai_balance_shield(objp);
12240                 }
12241
12242                 // 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]));
12243         }
12244 }
12245
12246 //      See if object *objp should evade an incoming missile.
12247 //      *aip is the ai_info pointer within *objp.
12248 void ai_maybe_evade_locked_missile(object *objp, ai_info *aip)
12249 {
12250         ship                    *shipp;
12251         ship_info       *sip;
12252
12253         shipp = &Ships[objp->instance];
12254         sip = &Ship_info[shipp->ship_info_index];
12255
12256         //      Only small ships evade an incoming missile.  Why would a capital ship try to swerve?
12257         if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12258                 return;
12259         }
12260
12261         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
12262                 return;
12263         }
12264
12265         if (aip->nearest_locked_object != -1) {
12266                 object  *missile_objp;
12267
12268                 missile_objp = &Objects[aip->nearest_locked_object];
12269
12270                 if (Weapons[missile_objp->instance].homing_object != objp) {
12271                         //nprintf(("AI", "\nMissile lost home!"));
12272                         aip->nearest_locked_object = -1;
12273                         return;
12274                 }
12275
12276                 if ((missile_objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[missile_objp->instance].weapon_info_index].wi_flags & WIF_HOMING)) {
12277                         float dist = vm_vec_dist_quick(&missile_objp->pos, &objp->pos);
12278                         float dist2 = 4.0f  * vm_vec_mag_quick(&missile_objp->phys_info.vel);                   
12279                         if (dist < dist2) {
12280                                 switch (aip->mode) {
12281                                 //      If in AIM_STRAFE mode, don't evade if parent of weapon is targeted ship.
12282                                 case AIM_STRAFE:
12283                                         if ((missile_objp->parent != -1) && (missile_objp->parent == aip->target_objnum)) {
12284                                                 ;
12285                                         } else {
12286                                                 ;               //      Alan -- If you want to handle incoming weapons from someone other than the ship
12287                                                                 //      the strafing ship is attacking, do it here.
12288                                         }
12289                                         break;
12290                                 case AIM_CHASE:
12291                                         //      Don't always go into evade weapon mode.  Usually, a countermeasure gets launched.
12292                                         // If low on countermeasures, more likely to try to evade.  If 8+, never evade due to low cmeasures.
12293                                         if (((((Missiontime >> 18) ^ OBJ_INDEX(objp)) & 3) == 0) || 
12294                                                 (objp->phys_info.speed < 40.0f) ||
12295                                                 (frand() < 1.0f - (float) shipp->cmeasure_count/8.0f)) {
12296                                                 if (aip->submode != SM_ATTACK_FOREVER) {        //      SM_ATTACK_FOREVER means engines blown.
12297                                                         aip->submode = SM_EVADE_WEAPON;
12298                                                         aip->submode_start_time = Missiontime;
12299                                                 }
12300                                         }
12301                                         break;
12302                                 case AIM_DOCK:  //      Ships in dock mode can evade iif they are not currently repairing or docked.
12303                                         if (aip->ai_flags & (AIF_REPAIRING | AIF_DOCKED))
12304                                                 break;
12305                                 case AIM_GUARD:
12306                                         //      If in guard mode and far away from guard object, don't pursue guy that hit me.
12307                                         if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12308                                                 if (vm_vec_dist_quick(&objp->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12309                                                         return;
12310                                                 }
12311                                         }
12312                                 case AIM_EVADE:
12313                                 case AIM_GET_BEHIND:
12314                                 case AIM_STAY_NEAR:
12315                                 case AIM_STILL:
12316                                 case AIM_AVOID:
12317                                 case AIM_WAYPOINTS:
12318                                 case AIM_NONE:
12319                                 case AIM_BIGSHIP:
12320                                 case AIM_PATH:
12321                                 case AIM_BE_REARMED:
12322                                 case AIM_SAFETY:
12323                                 case AIM_BAY_EMERGE:
12324                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12325                                         aip->previous_mode = aip->mode;
12326                                         aip->previous_submode = aip->submode;
12327                                         aip->mode = AIM_EVADE_WEAPON;
12328                                         aip->submode = -1;
12329                                         aip->submode_start_time = Missiontime;
12330                                         aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Max time to evade.
12331                                         //nprintf(("AI", "%s Evade weapon in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
12332                                         break;
12333                                 case AIM_EVADE_WEAPON:          //      Note: We don't want to change mode on another evasion, or previous_mode will get bashed.
12334                                 case AIM_PLAY_DEAD:
12335                                 case AIM_BAY_DEPART:
12336                                 case AIM_SENTRYGUN:
12337                                         break;
12338                                 case AIM_WARP_OUT:
12339                                         break;
12340                                 default:
12341                                         Int3();                 //      Hey, what mode is it?
12342                                         break;
12343                                 }
12344                         }
12345                 } else {
12346                         aip->nearest_locked_object = -1;
12347                 }
12348         }
12349 }
12350
12351 //      Maybe evade a dumbfire weapon that was fired when Pl_objp was targeted.
12352 //      Have an 80% chance of evading in a second
12353 void maybe_evade_dumbfire_weapon(ai_info *aip)
12354 {
12355         //      Only small ships evade an incoming missile.  Why would a capital ship try to swerve?
12356         if (!(Ship_info[Ships[Pl_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12357                 return;
12358         }
12359
12360         //      Make sure in a mode in which we evade dumbfire weapons.
12361         switch (aip->mode) {
12362         case AIM_CHASE:
12363                 if (aip->submode == SM_ATTACK_FOREVER) {
12364                         return;
12365                 }
12366         case AIM_GUARD:
12367                 //      If in guard mode and far away from guard object, don't pursue guy that hit me.
12368                 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12369                         if (vm_vec_dist_quick(&Objects[Ships[aip->shipnum].objnum].pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12370                                 return;
12371                         }
12372                 }
12373         case AIM_STILL:
12374         case AIM_STAY_NEAR:
12375         case AIM_EVADE:
12376         case AIM_GET_BEHIND:
12377         case AIM_AVOID:
12378         case AIM_PATH:
12379         case AIM_NONE:
12380         case AIM_WAYPOINTS:
12381         case AIM_SAFETY:
12382                 break;
12383         case AIM_STRAFE:
12384         case AIM_BIGSHIP:
12385         case AIM_DOCK:
12386         case AIM_PLAY_DEAD:
12387         case AIM_EVADE_WEAPON:
12388         case AIM_BAY_EMERGE:
12389         case AIM_BAY_DEPART:
12390         case AIM_SENTRYGUN:
12391         case AIM_WARP_OUT:
12392                 return;
12393         default:
12394                 Int3(); //      Bogus mode!
12395                 return;
12396         }
12397
12398         if (is_instructor(&Objects[Ships[aip->shipnum].objnum]))
12399                 return; //      Instructor doesn't evade.
12400
12401         float t = ai_endangered_by_weapon(aip);
12402         if ((t > 0.0f) && (t < 1.0f)) {
12403         // Check if this weapon is from a large ship Pl_objp is attacking... if so, enter strafe mode
12404                 if ( ai_big_maybe_enter_strafe_mode(Pl_objp, aip->danger_weapon_objnum) ) {
12405                         return;
12406                 }
12407
12408                 switch (aip->mode) {
12409                 case AIM_CHASE:
12410                         switch (aip->submode) {
12411                         case SM_EVADE:
12412                         case SM_ATTACK_FOREVER:
12413                         case SM_AVOID:
12414                         case SM_GET_AWAY:
12415                         case SM_EVADE_WEAPON:
12416                                 break;
12417                         default:
12418                                 if (ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
12419                                         //mprintf(("Ship %s entered super mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12420                                         aip->submode = SM_SUPER_ATTACK;
12421                                         aip->submode_start_time = Missiontime;
12422                                         aip->last_attack_time = Missiontime;
12423                                 } else {
12424                                         //mprintf(("Ship %s entered dumbfire evade mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12425                                         aip->submode = SM_EVADE_WEAPON;
12426                                         aip->submode_start_time = Missiontime;
12427                                 }
12428                                 break;
12429                         }
12430                         break;
12431                 case AIM_GUARD:
12432                 case AIM_STILL:
12433                 case AIM_STAY_NEAR:
12434                 case AIM_EVADE:
12435                 case AIM_GET_BEHIND:
12436                 case AIM_AVOID:
12437                 case AIM_PATH:
12438                 case AIM_NONE:
12439                 case AIM_WAYPOINTS:
12440                 case AIM_SAFETY:
12441                         if (!(aip->ai_flags & (AIF_NO_DYNAMIC | AIF_KAMIKAZE)) && (Ship_info[Ships[aip->shipnum].ship_info_index].flags & SIF_SMALL_SHIP)) {
12442                                 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12443                                 aip->previous_mode = aip->mode;
12444                                 aip->previous_submode = aip->submode;
12445                                 aip->mode = AIM_EVADE_WEAPON;
12446                                 aip->submode = -1;
12447                                 aip->submode_start_time = Missiontime;
12448                                 aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Evade for up to five seconds.
12449                         }
12450                         break;
12451                 case AIM_STRAFE:
12452                 case AIM_BIGSHIP:
12453                 case AIM_DOCK:
12454                 case AIM_PLAY_DEAD:
12455                 case AIM_EVADE_WEAPON:
12456                 case AIM_BAY_EMERGE:
12457                 case AIM_BAY_DEPART:
12458                 case AIM_SENTRYGUN:
12459                         break;
12460                 default:
12461                         Int3(); //      Bogus mode!
12462                 }
12463         }
12464 }
12465
12466 // determine what path to use when emerging from a fighter bay
12467 // input:       pl_objp =>      pointer to object for ship that is arriving
12468 //                              pos             =>      output parameter, it is the starting world pos for path choosen
12469 //                              v.fvec          =>      output parameter, this is the forward vector that ship has when arriving
12470 //
12471 // exit:                -1              =>      path could not be located
12472 //                               0              => success
12473 int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, vector *pos, vector *fvec)
12474 {
12475         int                     path_index, sb_path_index;
12476         ship                    *parent_sp = NULL;
12477         polymodel       *pm;
12478         ai_info         *aip;
12479         ship_bay                *sb;
12480         pnode                   *pnp;
12481         vector          *next_point;
12482
12483         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12484
12485         if ( parent_objnum == -1 ) {
12486                 Int3();
12487                 return -1;
12488         }
12489
12490         parent_sp = &Ships[Objects[parent_objnum].instance];
12491
12492         Assert(parent_sp != NULL);
12493         pm = model_get( parent_sp->modelnum );
12494         sb = pm->ship_bay;
12495
12496         if ( sb == NULL ) 
12497                 return -1;
12498
12499         if ( sb->num_paths <= 0 ) 
12500                 return -1;
12501
12502         // try to find a bay path that is not taken
12503         path_index = -1;
12504         sb_path_index = Ai_last_arrive_path++;
12505
12506         if ( sb_path_index >= sb->num_paths ) {
12507                 sb_path_index=0;
12508                 Ai_last_arrive_path=0;
12509         }
12510
12511         path_index = sb->paths[sb_path_index];
12512         if ( path_index == -1 ) 
12513                 return -1;
12514
12515         // create the path for pl_objp to follow
12516         create_model_exit_path(pl_objp, &Objects[parent_objnum], path_index, pm->paths[path_index].nverts);
12517         
12518         // Set this flag, so we don't bother recreating the path... we won't need to update the path
12519         // that has just been created.
12520 //      aip->ai_flags |= AIF_USE_STATIC_PATH;
12521
12522         // now return to the caller what the starting world pos and starting fvec for the ship will be
12523         Assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
12524         pnp = &Path_points[aip->path_start];
12525         *pos = pnp->pos;
12526
12527         // calc the forward vector using the starting two points of the path
12528         pnp = &Path_points[aip->path_start+1];
12529         next_point = &pnp->pos;
12530         vm_vec_normalized_dir(fvec, next_point, pos);
12531
12532         // record the parent objnum, since we'll need it once we're done with following the path
12533         aip->goal_objnum = parent_objnum;
12534         aip->goal_signature = Objects[parent_objnum].signature;
12535         aip->mode = AIM_BAY_EMERGE;
12536         aip->submode_start_time = Missiontime;
12537
12538         // set up starting vel
12539         vector vel;
12540         float speed;
12541         speed = Ship_info[Ships[pl_objp->instance].ship_info_index].max_speed;
12542         vel = *fvec;
12543         vm_vec_scale( &vel, speed );
12544         pl_objp->phys_info.vel = vel;
12545         pl_objp->phys_info.desired_vel = vel;
12546         pl_objp->phys_info.prev_ramp_vel.xyz.x = 0.0f;
12547         pl_objp->phys_info.prev_ramp_vel.xyz.y = 0.0f;
12548         pl_objp->phys_info.prev_ramp_vel.xyz.z = speed;
12549         pl_objp->phys_info.forward_thrust = 0.0f;               // How much the forward thruster is applied.  0-1.
12550
12551         return 0;       
12552 }
12553
12554 // clean up path data used for emerging from a fighter bay
12555 void ai_emerge_bay_path_cleanup(ai_info *aip)
12556 {
12557         aip->path_start = -1;
12558         aip->path_cur = -1;
12559         aip->path_length = 0;
12560         aip->mode = AIM_NONE;
12561 }
12562
12563 // handler for AIM_BAY_EMERGE
12564 void ai_bay_emerge()
12565 {
12566         ai_info *aip;
12567         int             parent_died=0;
12568
12569         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12570
12571         // if no path to follow, leave this mode
12572         if ( aip->path_start < 0 ) {
12573                 aip->mode = AIM_NONE;
12574                 return;
12575         }
12576
12577         // ensure parent ship is still alive
12578         if ( aip->goal_objnum < 0 ) {
12579                 parent_died=1;
12580         } 
12581         if ( !parent_died ) {
12582                 if ( Objects[aip->goal_objnum].signature != aip->goal_signature ) {
12583                         parent_died=1;
12584                 }
12585         }
12586
12587         if ( !parent_died ) {
12588                 Assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
12589                 if ( Ships[Objects[aip->goal_objnum].instance].flags & SF_DYING ) {
12590                         parent_died = 1;
12591                 }
12592         }
12593
12594         if ( parent_died ) {
12595                 ai_emerge_bay_path_cleanup(aip);
12596                 return;
12597         }
12598
12599         // follow the path to the final point
12600         ai_path();
12601
12602         // New test: must have been in AI_EMERGE mode for at least 10 seconds, and be a minimum distance from the start point
12603         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)) {
12604                 // erase path
12605                 ai_emerge_bay_path_cleanup(aip);
12606         }
12607
12608         // 2-25-99: Need this check to fix an assert for supercap ships... maybe we'll only do this check for supercaps 
12609         if (aip->path_cur > (aip->path_start+aip->path_length-1)) {
12610                 ai_emerge_bay_path_cleanup(aip);
12611         }       
12612 }
12613
12614 // Select the closest depart path
12615 //
12616 //      input:  aip     =>              ai info pointer to ship seeking to depart
12617 //                              pm              =>              pointer to polymodel for the ship contining the ship bay/depart paths
12618 //
12619 // exit:                >=0     =>              ship bay path index for depart path (ie index into sb->paths[])
12620 //                              -1              =>              no path could be found
12621 //
12622 // NOTE: this function should only be used for calculating closest depart paths for ai mode
12623 //                      AI_BAY_DEPART.  It tries to find the closest path that isn't already in use
12624 int ai_find_closest_depart_path(ai_info *aip, polymodel *pm)
12625 {
12626         int                     i, j, best_path, best_free_path;
12627         float                   dist, min_dist, min_free_dist;
12628         vector          *source;
12629         model_path      *mp;
12630         ship_bay                *sb;
12631
12632         sb = pm->ship_bay;
12633
12634         best_free_path = best_path = -1;
12635         min_free_dist = min_dist = 1e20f;
12636         Assert(aip->shipnum >= 0);
12637         source = &Objects[Ships[aip->shipnum].objnum].pos;
12638
12639         for ( i = 0; i < sb->num_paths; i++ ) {
12640
12641
12642                 mp = &pm->paths[sb->paths[i]];
12643                 for ( j = 0; j < mp->nverts; j++ ) {
12644                         dist = vm_vec_dist_squared(source, &mp->verts[j].pos);
12645
12646                         if ( dist < min_dist ) {
12647                                 min_dist = dist;
12648                                 best_path = i;
12649                         }
12650
12651                         // If this is a free path
12652                         if ( !(sb->depart_flags & (1<<i)) ) {
12653                                 if ( dist < min_free_dist ) {
12654                                         min_free_dist = dist;
12655                                         best_free_path = i;
12656                                 }
12657                         }
12658                 }
12659         }
12660
12661         if ( best_free_path >= 0 ) {
12662                 return best_free_path;          
12663         }
12664
12665         return best_path;
12666 }
12667
12668 // determine what path to use when trying to depart to a fighter bay
12669 // NOTE: this should be called when AIM_BAY_DEPART mode is set
12670 //
12671 // input:       pl_objp =>      pointer to object for ship that is departing
12672 //
12673 // exit:                -1      =>      could not find depart path
12674 //                              0       => found depart path
12675 int ai_acquire_depart_path(object *pl_objp, int parent_objnum)
12676 {
12677         int                     objnum, path_index;
12678         polymodel       *pm;
12679         ai_info         *aip;
12680         ship                    *sp;
12681         ship_bay                *sb;
12682
12683         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12684
12685         if ( parent_objnum == -1 ) {
12686                 ship_obj        *so;
12687
12688                 // for now just locate a captial ship on the same team:
12689                 so = GET_FIRST(&Ship_obj_list);
12690                 objnum = -1;
12691                 while(so != END_OF_LIST(&Ship_obj_list)){
12692                         sp = &Ships[Objects[so->objnum].instance];
12693                         if ( (Ship_info[sp->ship_info_index].flags & (SIF_HUGE_SHIP)) && (sp->team == Ships[pl_objp->instance].team) ) {
12694                                 objnum = so->objnum;
12695                                 break;
12696                         }
12697                         so = GET_NEXT(so);
12698                 } 
12699         } else {
12700                 objnum = parent_objnum;
12701         }
12702
12703         aip->path_start = -1;
12704
12705         if ( objnum == -1 )
12706                 return -1;
12707
12708         pm = model_get( Ships[Objects[objnum].instance].modelnum );
12709         sb = pm->ship_bay;
12710
12711         if ( sb == NULL ) 
12712                 return -1;
12713         if ( sb->num_paths <= 0 ) 
12714                 return -1;
12715
12716 /*
12717         
12718         path_index = -1;
12719         for ( i = 0; i < sb->num_paths; i++ ) {
12720                 if ( !(sb->depart_flags & (1<<i)) ) {
12721                         sb->depart_flags |= (1<<i);
12722                         path_index = sb->paths[i];
12723                         aip->submode_parm0 = i;                 // use mode-specific parameter to record ship bay path index
12724                         break;
12725                 }
12726         }
12727 */
12728         
12729         // take the closest path we can find
12730         int ship_bay_path;
12731         ship_bay_path = ai_find_closest_depart_path(aip, pm);
12732         path_index = sb->paths[ship_bay_path];
12733         aip->submode_parm0 = ship_bay_path;
12734         sb->depart_flags |= (1<<ship_bay_path);
12735
12736         if ( path_index == -1 ) {
12737                 return -1;
12738         }
12739
12740         Assert(pm->n_paths > path_index);
12741         ai_find_path(pl_objp, objnum, path_index, 0);
12742
12743         // Set this flag, so we don't bother recreating the path... we won't need to update the path
12744         // that has just been created.
12745         aip->ai_flags &= ~AIF_USE_STATIC_PATH;
12746
12747         aip->goal_objnum = objnum;
12748         aip->goal_signature = Objects[objnum].signature;
12749         aip->mode = AIM_BAY_DEPART;
12750
12751         Ships[pl_objp->instance].flags |= SF_DEPART_DOCKBAY;
12752         return 0;
12753 }
12754
12755 // handler for AIM_BAY_DEPART
12756 void ai_bay_depart()
12757 {
12758         ai_info *aip;
12759
12760         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12761
12762         // if no path to follow, leave this mode
12763         if ( aip->path_start < 0 ) {
12764                 aip->mode = AIM_NONE;
12765                 return;
12766         }
12767
12768         // check if parent ship still exists, if not abort depart 
12769         if ( aip->goal_signature != Objects[aip->goal_objnum].signature ) {
12770                 aip->mode = AIM_NONE;
12771                 return;
12772         }
12773
12774         // follow the path to the final point
12775         ai_path();
12776
12777         // if the final point is reached, let default AI take over
12778         if ( aip->path_cur >= (aip->path_start+aip->path_length) ) {
12779                 polymodel       *pm;
12780                 ship_bay                *sb;
12781
12782                 pm = model_get( Ships[Objects[aip->goal_objnum].instance].modelnum );
12783                 sb = pm->ship_bay;
12784                 if ( sb != NULL ) {
12785                         sb->depart_flags &= ~(1<<aip->submode_parm0);
12786                 }
12787
12788                 // make ship disappear
12789                 Pl_objp->flags |= OF_SHOULD_BE_DEAD;
12790                 ship_departed( Pl_objp->instance );
12791
12792                 // clean up path stuff
12793                 aip->path_start = -1;
12794                 aip->path_cur = -1;
12795                 aip->path_length = 0;
12796                 aip->mode = AIM_NONE;
12797         }
12798 }
12799
12800 // Handler for AIM_SENTRYGUN.  This AI mode is for sentry guns only (ie floating turrets).
12801 void ai_sentrygun()
12802 {
12803         // Nothing to do here.  Turret firing is handled via process_subobjects().
12804         // If you want the sentry guns to do anything beyond firing their turrets at enemies, add it here!
12805 }
12806
12807 //      --------------------------------------------------------------------------
12808 //      Execute behavior given by aip->mode.
12809 void ai_execute_behavior(ai_info *aip)
12810 {
12811         switch (aip->mode) {
12812         case AIM_CHASE:
12813                 if (En_objp) {
12814                         ai_chase();
12815                 } else if (aip->submode == SM_EVADE_WEAPON) {
12816                         evade_weapon();
12817                         // maybe reset submode
12818                         if (aip->danger_weapon_objnum == -1) {
12819                                 aip->submode = SM_ATTACK;
12820                                 aip->submode_start_time = Missiontime;
12821                                 aip->last_attack_time = Missiontime;
12822                         }
12823                 } else {
12824                         //      Don't circle if this is the instructor.
12825                         ship    *shipp = &Ships[aip->shipnum];
12826                         ship_info       *sip = &Ship_info[shipp->ship_info_index];
12827
12828                         if (strnicmp(shipp->ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME))) {
12829                                 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
12830                                         aip->mode = AIM_NONE;
12831                                 } else {
12832                                         ai_chase_circle(Pl_objp);
12833                                 }
12834                         }
12835                 }
12836                 break;
12837         case AIM_EVADE:
12838                 if (En_objp) {
12839                         ai_evade();
12840                 } else {
12841                         vector  tvec;
12842                         vm_vec_scale_add(&tvec, &Pl_objp->pos, &Pl_objp->orient.v.rvec, 100.0f);
12843                         turn_towards_point(Pl_objp, &tvec, NULL, 0.0f);
12844                         accelerate_ship(aip, 0.5f);
12845                 }
12846                 break;
12847         case AIM_STILL:
12848                 ai_still();
12849                 break;
12850         case AIM_STAY_NEAR:
12851                 ai_stay_near();
12852                 break;
12853         case AIM_GUARD:
12854                 ai_guard();
12855                 break;
12856         case AIM_WAYPOINTS:
12857                 ai_waypoints();
12858                 break;
12859         case AIM_DOCK:
12860                 ai_dock();
12861                 break;
12862         case AIM_NONE:
12863                 // ai_formation();
12864                 break;
12865         case AIM_BIGSHIP:
12866                 ai_big_ship(Pl_objp);
12867                 break;
12868         case AIM_PATH: {
12869                 int path_num;
12870                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], 0);
12871                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
12872                 ai_path();
12873                 break;
12874         }
12875         case AIM_SAFETY:
12876                 ai_safety();
12877                 break;
12878         case AIM_EVADE_WEAPON:
12879                 evade_weapon();
12880                 break;
12881         case AIM_STRAFE:
12882                 if (En_objp) {
12883                         Assert(En_objp->type == OBJ_SHIP);
12884                         ai_big_strafe();        // strafe a big ship
12885                 } else {
12886                         aip->mode = AIM_NONE;
12887                 }
12888                 break;
12889         case AIM_BAY_EMERGE:
12890                 ai_bay_emerge();
12891                 break;
12892         case AIM_BAY_DEPART:
12893                 ai_bay_depart();
12894                 break;
12895         case AIM_SENTRYGUN:
12896                 ai_sentrygun();
12897                 break;
12898         case AIM_WARP_OUT:
12899                 break;          //      Note, handled directly from ai_frame().
12900         default:
12901                 Int3();         //      This should never happen -- MK, 5/12/97 
12902                 break;
12903         }
12904
12905         if ( !(ship_get_SIF(aip->shipnum) & SIF_NOT_FLYABLE) ) {
12906                 maybe_evade_dumbfire_weapon(aip);
12907         }
12908 }
12909
12910 //      Auxiliary function for maybe_request_support.
12911 //      Return 1 if subsystem "type" is worthy of repair, else return 0.
12912 //      Since subsystems cannot be repaired if they are at 0 strength, don't return 1 if subsystem is dead.
12913 int mrs_subsystem(ship *shipp, int type)
12914 {
12915         float   t;
12916
12917         t = ship_get_subsystem_strength(shipp, type);
12918
12919         if (t > 0.0f) {
12920                 return (int) ((1.0f - t) * 3);
12921         } else {
12922                 return 3;
12923         }
12924 }
12925
12926 //      Return number of ships on *objp's team that are currently rearming.
12927 int num_allies_rearming(object *objp)
12928 {
12929         ship_obj        *so;
12930         int             team;
12931         int             count = 0;
12932
12933         team = Ships[objp->instance].team;
12934
12935         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
12936                 object  *A;
12937                 
12938                 Assert (so->objnum != -1);
12939                 A = &Objects[so->objnum];
12940
12941                 if (Ships[A->instance].team == team) {
12942                         if (Ai_info[Ships[A->instance].ai_index].ai_flags & (AIF_REPAIRING | AIF_AWAITING_REPAIR)) {
12943                                 count++;
12944                         }
12945                 }
12946         }
12947
12948         return count;
12949 }
12950
12951
12952 //      Maybe ship *objp should request support (rearm/repair).
12953 //      If it does, return TRUE, else return FALSE.
12954 int maybe_request_support(object *objp)
12955 {
12956         ship_info       *sip;
12957         ship                    *shipp;
12958         ai_info         *aip;
12959         int                     desire;
12960
12961         Assert(objp->type == OBJ_SHIP);
12962         shipp = &Ships[objp->instance];
12963         aip = &Ai_info[shipp->ai_index];
12964         sip = &Ship_info[shipp->ship_info_index];
12965
12966         if (!timestamp_elapsed(aip->next_rearm_request_timestamp))
12967                 return 0;
12968
12969         //      Only fighters and bombers request support.
12970         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER)))
12971                 return 0;
12972
12973         //      A ship that is currently awaiting does not need support!
12974         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
12975                 return 0;
12976
12977         if (!is_support_allowed(objp))
12978                 return 0;
12979
12980         //if (shipp->team != TEAM_FRIENDLY)
12981         //      return 0;
12982
12983         //      Compute a desire value.
12984         //      Desire of 0 means no reason to request support.
12985         //      1 is slight, 2 more, etc.  Maximum is around 20.  Anything larger than 3 is pretty strong.
12986         desire = 0;
12987
12988         //      Set desire based on hull strength.
12989         //      No: We no longer repair hull, so this would cause repeated repair requests.
12990         //desire += 6 - (int) ((objp->hull_strength/sip->initial_hull_strength) * 6.0f);
12991
12992         //      Set desire based on key subsystems.
12993         desire += 2*mrs_subsystem(shipp, SUBSYSTEM_ENGINE);     //      Note, disabled engine forces repair request, regardless of nearby enemies.
12994         desire += mrs_subsystem(shipp, SUBSYSTEM_COMMUNICATION);
12995         desire += mrs_subsystem(shipp, SUBSYSTEM_WEAPONS);
12996         desire += mrs_subsystem(shipp, SUBSYSTEM_SENSORS);
12997
12998         //      Set desire based on percentage of secondary weapons.
12999         ship_weapon *swp = &shipp->weapons;
13000
13001         for ( int i = 0; i < swp->num_secondary_banks; i++ ) {
13002                 if (swp->secondary_bank_start_ammo[i] > 0) {
13003 //                      float r = (float) swp->secondary_bank_ammo[i]*Weapon_info[swp->secondary_bank_weapons[i]].cargo_size/swp->secondary_bank_capacity[i];
13004                         float r = (float) swp->secondary_bank_ammo[i]/swp->secondary_bank_start_ammo[i];
13005                         desire += (int) ((1.0f - r) * 3.0f);
13006                 }
13007         }
13008
13009         //      If no reason to repair, don't bother to see if it's safe to repair.
13010         if (desire == 0){
13011                 return 0;
13012         }
13013
13014         //      Compute danger threshold.
13015         //      Balance this with desire and maybe request support.
13016         if (ai_good_time_to_rearm( objp )) {
13017                 ai_issue_rearm_request(objp);
13018                 return 1;
13019         } else if (num_allies_rearming(objp) < 2) {
13020                 if (desire >= 8) {      //      guarantees disabled will cause repair request
13021                         ai_issue_rearm_request(objp);
13022                 } else if (desire >= 3) {               //      >= 3 means having a single subsystem fully blown will cause repair.
13023                         int     count;
13024                         int objnum = find_nearby_hostile(OBJ_INDEX(objp), get_enemy_team_mask(OBJ_INDEX(objp)), 2000.0f, &count);
13025
13026                         if ((objnum == -1) || (count < 2) || (vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos) > 3000.0f*count/desire)) {
13027                                 ai_issue_rearm_request(objp);
13028                                 return 1;
13029                         } else {
13030                                 //nprintf(("AI", "Would like to rearm, but enemy only %7.3f units away.\n", vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos)));
13031                         }
13032                 }
13033         }
13034
13035         return 0;
13036
13037 }
13038
13039 void ai_set_mode_warp_out(object *objp, ai_info *aip)
13040 {
13041         ai_abort_rearm_request(objp);
13042         if (aip->mode != AIM_WARP_OUT) {
13043                 aip->mode = AIM_WARP_OUT;
13044                 aip->submode = AIS_WARP_1;
13045         }
13046 }
13047
13048 //      Maybe warp ship out.
13049 //      Shivan and HoL fighter/bomber warp out if their weapons subsystems have been destroyed.
13050 void ai_maybe_warp_out(object *objp)
13051 {
13052         ship    *shipp;
13053
13054         // don't do anything if in a training mission.
13055         if ( The_mission.game_type & MISSION_TYPE_TRAINING )
13056                 return;
13057
13058         Assert(objp->type == OBJ_SHIP);
13059
13060         shipp = &Ships[objp->instance];
13061         ai_info *aip = &Ai_info[shipp->ai_index];
13062
13063         if (aip->mode == AIM_WARP_OUT)
13064                 return;
13065
13066         //      If a support ship with no goals and low hull, warp out.  Be sure that there are no pending goals
13067         // in the support ships ai_goal array.  Just process this ships goals.
13068         ship_info       *sip = &Ship_info[shipp->ship_info_index];
13069         if (sip->flags & SIF_SUPPORT) {
13070                 if ( timestamp_elapsed(aip->warp_out_timestamp) ) {
13071                         ai_process_mission_orders( OBJ_INDEX(objp), aip );
13072                         if ( (aip->dock_objnum == -1) && (objp->hull_strength/sip->initial_hull_strength < 0.25f) ) {
13073                                 ai_set_mode_warp_out(objp, aip);
13074                         }
13075                 }
13076         }
13077
13078         //      Friendly don't warp out, they'll eventually request support.
13079         if (shipp->team == TEAM_FRIENDLY)
13080                 return;
13081
13082         if (!(shipp->flags & SF_DEPARTING)) {
13083                 ship_info       *sip;
13084
13085                 sip = &Ship_info[shipp->ship_info_index];
13086                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
13087                         if (aip->warp_out_timestamp == 0) {
13088                                 //if (ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS) == 0.0f) {
13089                                 //      aip->warp_out_timestamp = timestamp(((myrand() % 10) + 10) * 1000);
13090                                 //}
13091                         } else if (timestamp_elapsed(aip->warp_out_timestamp)) {
13092                                 ai_set_mode_warp_out(objp, aip);
13093                         }
13094                 }
13095         }
13096 }
13097
13098 //      Warp this ship out.
13099 void ai_warp_out(object *objp)
13100 {
13101         // if dying, don't warp out.
13102         if (Ships[objp->instance].flags & SF_DYING) {
13103                 return;
13104         }
13105
13106         ai_info *aip;
13107
13108         aip = &Ai_info[Ships[objp->instance].ai_index];
13109
13110         switch (aip->submode) {
13111         case AIS_WARP_1:
13112                 aip->force_warp_time = timestamp(10*1000);      //      Try to avoid a collision for up to ten seconds.
13113                 aip->submode = AIS_WARP_2;
13114                 break;
13115         case AIS_WARP_2:                        //      Make sure won't collide with any object.
13116                 if (timestamp_elapsed(aip->force_warp_time) || !collide_predict_large_ship(objp, objp->radius*2.0f + 100.0f)) {
13117                         aip->submode = AIS_WARP_3;
13118
13119                         // maybe recalculate collision pairs.
13120                         if (ship_get_warp_speed(objp) > ship_get_max_speed(&Ships[objp->instance])) {
13121                                 // recalculate collision pairs
13122                                 OBJ_RECALC_PAIRS(objp); 
13123                         }
13124
13125                         aip->force_warp_time = timestamp(4*1000);               //      Try to attain target speed for up to 4 seconds.
13126                 } else {
13127                         vector  goal_point;
13128                         vm_vec_scale_add(&goal_point, &objp->pos, &objp->orient.v.uvec, 100.0f);
13129                         turn_towards_point(objp, &goal_point, NULL, 0.0f);
13130                         accelerate_ship(aip, 0.0f);
13131                 }
13132                 break;
13133         case AIS_WARP_3:
13134                 //      Rampup desired_vel in here from current to desired velocity and set PF_USE_VEL. (not sure this is the right flag)
13135                 //      desired velocity is computed in shipfx_calculate_warp_time().  See shipfx#572 for sample code.
13136                 float   speed, goal_speed;
13137                 float shipfx_calculate_warp_speed(object*);
13138                 goal_speed = shipfx_calculate_warp_speed(objp);
13139
13140                 // HUGE ships go immediately to AIS_WARP_4
13141                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_HUGE_SHIP) {
13142                         aip->submode = AIS_WARP_4;
13143                         break;
13144                 }
13145                 //compute_warpout_stuff(objp, &goal_speed, &warp_time, &warp_pos);
13146                 //goal_speed = 80.0f;
13147                 //set_accel_for_target_speed(objp, 40.0f);
13148                 // 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
13149                 speed = goal_speed * flFrametime + objp->phys_info.speed * (1.0f - flFrametime);
13150                 vm_vec_copy_scale(&objp->phys_info.vel, &objp->orient.v.fvec, speed);
13151                 objp->phys_info.desired_vel = objp->phys_info.vel;
13152                 // nprintf(("AI", "Frame %i, speed = %7.3f, goal = %7.3f\n", Framecount, vm_vec_mag_quick(&objp->phys_info.vel), goal_speed));
13153                 if (timestamp_elapsed(aip->force_warp_time) || (fl_abs(objp->phys_info.speed - goal_speed) < 2.0f))
13154                         aip->submode = AIS_WARP_4;
13155                 break;
13156         case AIS_WARP_4: {
13157                 shipfx_warpout_start(objp);
13158                 aip->submode = AIS_WARP_5;
13159                 break;
13160         }
13161         case AIS_WARP_5:
13162                 break;
13163         default:
13164                 Int3();         //      Illegal submode for warping out.
13165         }
13166 }
13167
13168 //      Return object index of weapon that could produce a shockwave that should be known about to *objp.
13169 //      Return nearest one.
13170 int ai_find_shockwave_weapon(object *objp, ai_info *aip)
13171 {
13172         missile_obj     *mo;
13173         float   nearest_dist = 999999.9f;
13174         int     nearest_index = -1;
13175
13176         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
13177                 object          *A;
13178                 weapon          *wp;
13179                 weapon_info     *wip;
13180         
13181                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
13182                 A = &Objects[mo->objnum];
13183
13184                 Assert(A->type == OBJ_WEAPON);
13185                 Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
13186                 wp = &Weapons[A->instance];
13187                 wip = &Weapon_info[wp->weapon_info_index];
13188                 Assert( wip->subtype == WP_MISSILE );
13189
13190                 if (wip->shockwave_speed > 0.0f) {
13191                         float   dist;
13192
13193                         dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13194                         if (dist < nearest_dist) {
13195                                 nearest_dist = dist;
13196                                 nearest_index = mo->objnum;
13197                         }
13198                 }
13199         }
13200
13201         return nearest_index;
13202
13203 }
13204
13205 #define EVADE_SHOCKWAVE_DAMAGE_THRESHOLD                100.0f
13206
13207 //      Tell all ships to avoid a big ship that is blowing up.
13208 //      Only avoid if shockwave is fairly large.
13209 //      OK to tell everyone to avoid.  If they're too far away, that gets cleaned up in the frame interval.
13210 void ai_announce_ship_dying(object *dying_objp)
13211 {
13212         float damage = ship_get_exp_damage(dying_objp);
13213         if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {
13214                 ship_obj        *so;
13215
13216                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13217                         if (Ship_info[Ships[Objects[so->objnum].instance].ship_info_index].flags & (SIF_SMALL_SHIP | SIF_FREIGHTER)) {
13218                                 ai_info *aip;
13219
13220                                 aip = &Ai_info[Ships[Objects[so->objnum].instance].ai_index];
13221
13222                                 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
13223                                         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_SHIP;
13224                                 }
13225                         }
13226                 }
13227         }
13228 }
13229
13230
13231 //      Return object index of weapon that could produce a shockwave that should be known about to *objp.
13232 //      Return nearest one.
13233 int ai_find_shockwave_ship(object *objp, ai_info *aip)
13234 {
13235         ship_obj        *so;
13236         float   nearest_dist = 999999.9f;
13237         int     nearest_index = -1;
13238
13239         for ( so = GET_NEXT(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13240                 object          *A;
13241                 ship                    *shipp;
13242         
13243                 Assert(so->objnum >= 0 && so->objnum < MAX_OBJECTS);
13244                 A = &Objects[so->objnum];
13245
13246                 Assert(A->type == OBJ_SHIP);
13247                 Assert((A->instance >= 0) && (A->instance < MAX_SHIPS));
13248                 shipp = &Ships[A->instance];
13249                 //      Only look at objects in the process of dying.
13250                 if (shipp->flags & SF_DYING) {
13251                         float damage = ship_get_exp_damage(objp);
13252
13253                         if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {               //      Only evade quite large blasts
13254                                 float   dist;
13255
13256                                 dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13257                                 if (dist < nearest_dist) {
13258                                         nearest_dist = dist;
13259                                         nearest_index = so->objnum;
13260                                 }
13261                         }
13262                 }
13263         }
13264
13265         return nearest_index;
13266
13267 }
13268
13269 int aas_1(object *objp, ai_info *aip, vector *safe_pos)
13270 {
13271         // MAKE SURE safe_pos DOES NOT TAKE US TOWARD THE A SHIP WE'RE ATTACKING.
13272         if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_WEAPON) {
13273                 //      If we don't currently know of a weapon to avoid, try to find one.
13274                 //      If we can't find one, then clear the bit so we don't keep coming here.
13275                 if (aip->shockwave_object == -1) {
13276                         int shockwave_weapon = ai_find_shockwave_weapon(objp, aip);
13277                         if (shockwave_weapon == -1) {
13278                                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13279                                 return 0;
13280                         } else {
13281                                 aip->shockwave_object = shockwave_weapon;
13282                         }
13283                 }
13284
13285                 //      OK, we have reason to believe we should avoid aip->shockwave_object.
13286                 Assert(aip->shockwave_object > -1);
13287                 object  *weapon_objp = &Objects[aip->shockwave_object];
13288                 if (weapon_objp->type != OBJ_WEAPON) {
13289                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13290                         aip->shockwave_object = -1;
13291                         return 0;
13292                 }
13293
13294                 weapon  *weaponp = &Weapons[weapon_objp->instance];
13295                 weapon_info     *wip = &Weapon_info[weaponp->weapon_info_index];
13296                 object *target_ship_obj = NULL;
13297
13298                 if (wip->shockwave_speed == 0.0f) {
13299                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13300                         aip->shockwave_object = -1;
13301                         return 0;
13302                 }
13303
13304                 float   danger_dist;
13305                 vector  expected_pos;           //      Position at which we expect the weapon to detonate.
13306                 int             pos_set = 0;
13307
13308                 danger_dist = wip->outer_radius;
13309                 //      Set predicted position of detonation.
13310                 //      If an aspect locked missile, assume it will detonate at the homing position.
13311                 //      If not, which is not possible in a default FreeSpace weapon, then predict it will detonate at some
13312                 //      time in the future, this time based on max lifetime and life left.
13313                 if (wip->wi_flags & WIF_HOMING_ASPECT) {
13314                         expected_pos = weaponp->homing_pos;
13315                         if (weaponp->homing_object && weaponp->homing_object->type == OBJ_SHIP) {
13316                                 target_ship_obj = weaponp->homing_object;
13317                         }
13318                         pos_set = 1;
13319                         if (IS_VEC_NULL(&weaponp->homing_pos)) {
13320                                 pos_set = 0;
13321                                 if (weaponp->target_num != -1) {
13322                                         if (Objects[weaponp->target_num].type == OBJ_SHIP) {
13323                                                 target_ship_obj = &Objects[weaponp->target_num];
13324                                                 expected_pos = target_ship_obj->pos;
13325                                                 pos_set = 1;
13326                                         }
13327                                 }
13328                         }
13329                 }
13330
13331                 if (!pos_set) {
13332                         float   time_scale;
13333
13334                         if (wip->lifetime - weaponp->lifeleft > 5.0f) {
13335                                 time_scale = 1.0f;
13336                         } else {
13337                                 time_scale = weaponp->lifeleft/2.0f;
13338                         }
13339
13340                         vm_vec_scale_add(&expected_pos, &weapon_objp->pos, &weapon_objp->orient.v.fvec, time_scale);
13341                 }
13342
13343                 //      See if too far away to care about shockwave.
13344                 if (vm_vec_dist_quick(&objp->pos, &expected_pos) > danger_dist*2.0f) {
13345                         //aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13346                         return 0;
13347                 } else {
13348                         // try to find a safe position
13349                         vector vec_from_exp;
13350                         float dir = 1.0f;
13351                         vm_vec_sub(&vec_from_exp, &objp->pos, &expected_pos);
13352                         float dot = vm_vec_dotprod(&vec_from_exp, &weapon_objp->orient.v.fvec);
13353                         if (dot > -30) {
13354                                 // if we're already on the other side of the explosion, don't try to fly behind it
13355                                 dir = -1.0f;
13356                         }
13357
13358                         //      Fly towards a point behind the weapon.
13359                         vm_vec_scale_add(safe_pos, &weapon_objp->pos, &weapon_objp->orient.v.fvec, -50000.0f*dir);
13360
13361                         // verify safe_pos will not make us collide with our target objnum, else try 2 other vecs
13362                         // don't bang your head, else go
13363 //                      int go_safe = FALSE;
13364                         int go_safe = TRUE;
13365 /*                      if (target_ship_obj) {
13366                                 if (pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius)) {
13367                                         // try up to 2 other random directions
13368                                         vector dir_vec, rand_vec;
13369                                         int idx;
13370                                         for (idx=0; idx<2; idx++) {
13371                                                 vm_vec_rand_vec_quick(&rand_vec);
13372                                                 vm_vec_scale_add(&dir_vec, &weapon_objp->orient.v.fvec, &rand_vec, 0.5f);
13373                                                 vm_vec_scale_add(safe_pos, &weapon_objp->pos, &dir_vec, -50000.0f*dir);
13374                                                 if ( !pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius) ) {
13375                                                         go_safe = TRUE;
13376                                                         break;
13377                                                 }
13378                                         }
13379                                 } else { // direct path is safe
13380                                         go_safe = TRUE;
13381                                 }
13382                         } else { // no target_obj_ship
13383                                 go_safe = TRUE;
13384                         } */
13385
13386                         if (go_safe) {
13387                                 return 1;
13388                         } else {
13389                                 // can't figure out a good way to go
13390                                 return 0;
13391                         }
13392                 }
13393         } else if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_SHIP) {
13394                 if (aip->shockwave_object == -1) {
13395                         int shockwave_ship = ai_find_shockwave_ship(objp, aip);
13396                         if (shockwave_ship == -1) {
13397                                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13398                                 return 0;
13399                         } else {
13400                                 aip->shockwave_object = shockwave_ship;
13401                         }
13402                 }
13403
13404                 Assert(aip->shockwave_object > -1);
13405                 object  *ship_objp = &Objects[aip->shockwave_object];
13406                 if (ship_objp == objp) {
13407                         aip->shockwave_object = -1;
13408                         return 0;
13409                 }
13410
13411                 if (ship_objp->type != OBJ_SHIP) {
13412                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13413                         return 0;
13414                 }
13415
13416                 //      Optimize note! Don't really have to normalize.  We only need a point away from the blowing-up ship.
13417                 vector safe_vec;
13418
13419                 vm_vec_normalized_dir(&safe_vec, &objp->pos, &ship_objp->pos);
13420                 vm_vec_scale_add(safe_pos, &ship_objp->pos, &safe_vec, 50000.0f);       //      Fly away from the ship.
13421
13422                 float outer_rad = ship_get_exp_outer_rad(ship_objp);
13423
13424                 if (vm_vec_dist_quick(&objp->pos, &ship_objp->pos) > outer_rad*1.5f) {
13425                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13426                         return 0;
13427                 }
13428
13429                 return 1;
13430
13431         } else {
13432                 Int3(); //      Illegal -- supposedly avoiding a shockwave, but neither ship nor weapon.  What is it!?
13433         }
13434
13435         return 0;
13436 }
13437
13438 /*
13439 int rct_done = 0;
13440
13441 void rand_chance_test()
13442 {
13443         int     i;
13444         float   frametime;
13445
13446         if (rct_done)
13447                 return;
13448
13449         rct_done = 1;
13450
13451         for (frametime=0.02f; frametime<0.25f; frametime *= 1.25f) {
13452                 float   chance;
13453
13454                 nprintf(("AI", "%6.4f: ", frametime));
13455                 for (chance=0.25f; chance<2.5f; chance += 0.25f) {
13456                         int count = 0;
13457
13458                         for (i=0; i<100.0f/frametime; i++) {
13459                                 if (rand_chance(frametime, chance))
13460                                         count++;
13461                         }
13462                         nprintf(("AI", "%3i ", count));
13463                 }
13464                 nprintf(("AI", "\n"));
13465         }
13466 }
13467 */
13468
13469 //      --------------------------------------------------------------------------
13470 //      Make object *objp avoid the nearest dangerous shockwave-producing weapon.
13471 //      If it looks like there is no valid shockwave-producing weapon then clear the AIF_AVOID_SHOCKWAVE_WEAPON bit in ai_flags and return.
13472 //      Return 1 if avoiding a shockwave, else return 0.
13473 int ai_avoid_shockwave(object *objp, ai_info *aip)
13474 {
13475         vector  safe_pos;
13476
13477         //rand_chance_test();
13478         // BIG|HUGE do not respond to shockwaves
13479         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
13480                 // don't come here again
13481                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE;
13482                 return 0;
13483         }
13484
13485         //      Don't all react right away.
13486         if (!(aip->ai_flags & AIF_AVOID_SHOCKWAVE_STARTED))
13487                 if (!rand_chance(flFrametime, (float) aip->ai_class/4.0f + 0.25f))      //      Chance to avoid in 1 second is 0.25 + ai_class/4
13488                         return 0;
13489
13490         if (!aas_1(objp, aip, &safe_pos)) {
13491                 aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13492                 return 0;
13493         }
13494
13495         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13496
13497         //      OK, evade the shockwave!
13498         turn_towards_point(objp, &safe_pos, NULL, 0.0f);
13499         vector  vec_to_safe_pos;
13500         float           dot_to_goal;
13501
13502         vm_vec_normalized_dir(&vec_to_safe_pos, &safe_pos, &objp->pos);
13503
13504         dot_to_goal = vm_vec_dot(&objp->orient.v.fvec, &vec_to_safe_pos);
13505         if (dot_to_goal < -0.5f)
13506                 accelerate_ship(aip, 0.3f);
13507         else {
13508                 accelerate_ship(aip, 1.0f + dot_to_goal);
13509                 if (dot_to_goal > 0.2f) {
13510                         if (!(objp->phys_info.flags & PF_AFTERBURNER_ON )) {
13511                                 afterburners_start(objp);
13512                                 aip->afterburner_stop_time = Missiontime + 2*F1_0;
13513                         }
13514                 }
13515         }
13516
13517         return 1;
13518 }
13519
13520 //      Awaiting repair.  Be useful.
13521 //      Probably fly towards incoming repair ship.
13522 //      Return true if this ship is close to being repaired, else return false.
13523 int ai_await_repair_frame(object *objp, ai_info *aip)
13524 {
13525         if (!(aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)))
13526                 return 0;
13527
13528         if (aip->dock_objnum == -1)
13529                 return 0;
13530
13531         ship    *shipp;
13532         ship_info       *sip;
13533
13534         shipp = &Ships[Objects[aip->dock_objnum].instance];
13535         sip = &Ship_info[shipp->ship_info_index];
13536
13537         aip->ai_flags &= ~AIF_FORMATION_OBJECT; //      Prevents endless rotation.
13538
13539         if (!(sip->flags & SIF_SUPPORT))
13540                 return 0;
13541
13542         vector  goal_point;
13543         object  *repair_objp;
13544
13545         repair_objp = &Objects[aip->dock_objnum];
13546
13547         if (Ships[repair_objp->instance].team == TEAM_TRAITOR) {
13548                 ai_abort_rearm_request(repair_objp);
13549                 return 0;
13550         }
13551
13552         vm_vec_scale_add(&goal_point, &repair_objp->pos, &repair_objp->orient.v.uvec, -50.0f);  //      Fly towards point below repair ship.
13553
13554         vector  vtr;
13555         float dist = vm_vec_normalized_dir(&vtr, &goal_point, &objp->pos);
13556         float dot = vm_vec_dot(&vtr, &objp->orient.v.fvec);
13557
13558         if (dist > 200.0f) {
13559                 //nprintf(("AI", "%s flying towards %s for repair, dist = %7.3f\n", Ships[objp->instance].ship_name, &Ships[repair_objp->instance].ship_name, dist));
13560                 accelerate_ship(aip, (0.9f + dot) * dist/1500.0f);
13561                 turn_towards_point(objp, &goal_point, NULL, 0.0f);
13562         } else {
13563                 accelerate_ship(aip, 0.0f);
13564                 //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));
13565         }
13566
13567         return 1;
13568 }
13569
13570 //      Maybe cause this ship to self-destruct.
13571 //      Currently, any small ship (SIF_SMALL_SHIP) that has been disabled will self-destruct after awhile.
13572 //      Maybe should only do this if they are preventing their wing from re-entering.
13573 void ai_maybe_self_destruct(object *objp, ai_info *aip)
13574 {
13575         //      Friendly ships can be repaired, so no self-destruct.
13576         //      In multiplayer, just don't self-destruct.  I figured there would be a problem. -- MK, 3/19/98.
13577         if ((Ships[objp->instance].team == TEAM_FRIENDLY) || (Game_mode & GM_MULTIPLAYER))
13578                 return;
13579
13580         //      Small ships in a wing blow themselves up after awhile if engine or weapons system has been destroyed.
13581         //      Reason: Don't want them to prevent a re-emergence of the wing.
13582         //      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
13583         //      mission would be broken.
13584         if ((Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP) && (Ships[objp->instance].wingnum != -1)) {
13585                 if ((ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) ||
13586                         (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_WEAPONS) <= 0.0f)) {
13587                         if (aip->self_destruct_timestamp < 0)
13588                                 aip->self_destruct_timestamp = timestamp(90 * 1000);    //      seconds until self-destruct
13589                 } else {
13590                         aip->self_destruct_timestamp = -1;
13591                 }
13592
13593                 if (aip->self_destruct_timestamp < 0) {
13594                         return;
13595                 }
13596
13597                 if (timestamp_elapsed(aip->self_destruct_timestamp)) {
13598                         ship_apply_local_damage( objp, objp, &objp->pos, objp->hull_strength*flFrametime + 1.0f, MISS_SHIELDS);
13599                 }
13600         }
13601 }
13602
13603 // Determine if pl_objp needs a new target, called from ai_frame()
13604 int ai_need_new_target(object *pl_objp, int target_objnum)
13605 {
13606         object *objp;
13607
13608         if ( target_objnum < 0 ) {
13609                 return 1;
13610         }
13611
13612         objp = &Objects[target_objnum];
13613
13614         if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID) && (objp->type != OBJ_WEAPON) ) {
13615                 return 1;
13616         }
13617
13618         if ( objp->type == OBJ_SHIP ) {
13619                 if ( Ships[objp->instance].flags & SF_DYING ) {
13620                         return 1;
13621                 } else if (Ships[objp->instance].team == Ships[pl_objp->instance].team)
13622                         return 1;
13623         }
13624
13625         return 0;
13626 }
13627
13628 //      If *objp is recovering from a collision with a big ship, handle it.
13629 //      Return true if recovering.
13630 int maybe_big_ship_collide_recover_frame(object *objp, ai_info *aip)
13631 {
13632         float   dot, dist;
13633         vector  v2g;
13634         
13635         if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1) {
13636                 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);
13637                 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_1, &objp->pos);
13638                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
13639                 accelerate_ship(aip, dot);
13640
13641                 //      If close to desired point, or 15+ seconds since entered this mode, continue to next mode.
13642                 if ((timestamp_until(aip->big_recover_timestamp) < -15*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13643                         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_1;
13644                         aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13645                 }
13646
13647                 return 1;
13648
13649         } else if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_2) {
13650                 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);
13651                 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_2, &objp->pos);
13652                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
13653                 accelerate_ship(aip, dot);
13654
13655                 //      If close to desired point, or 30+ seconds since started avoiding collision, done avoiding.
13656                 if ((timestamp_until(aip->big_recover_timestamp) < -30*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13657                         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13658                         aip->ai_flags &= ~AIF_TARGET_COLLISION;
13659                 }
13660
13661                 return 1;
13662         }
13663
13664         if (aip->ai_flags & AIF_TARGET_COLLISION) {
13665                 aip->ai_flags &= ~AIF_TARGET_COLLISION;
13666         }
13667         return 0;
13668 }
13669
13670 void validate_mode_submode(ai_info *aip)
13671 {
13672         switch (aip->mode) {
13673         case AIM_CHASE:
13674                 // check valid submode
13675                 switch (aip->submode) {
13676                 case SM_CONTINUOUS_TURN:
13677                 case SM_ATTACK:
13678                 case SM_EVADE_SQUIGGLE:
13679                 case SM_EVADE_BRAKE:    
13680                 case SM_EVADE:          
13681                 case SM_SUPER_ATTACK:
13682                 case SM_AVOID:  
13683                 case SM_GET_BEHIND:
13684                 case SM_GET_AWAY:               
13685                 case SM_EVADE_WEAPON:
13686                 case SM_FLY_AWAY:       
13687                 case SM_ATTACK_FOREVER:
13688                         break;
13689                 default:
13690                         Int3();
13691                 }
13692                 break;
13693
13694         case AIM_STRAFE:
13695                 // check valid submode
13696                 switch(aip->submode) {
13697                 case AIS_STRAFE_ATTACK:
13698                 case AIS_STRAFE_AVOID:
13699                 case AIS_STRAFE_RETREAT1:
13700                 case AIS_STRAFE_RETREAT2:
13701                 case AIS_STRAFE_POSITION:
13702                         break;
13703                 default:
13704                         Int3();
13705                 }
13706                 break;
13707         }
13708 }
13709
13710 //      --------------------------------------------------------------------------
13711 // Process AI object "objnum".
13712 void ai_frame(int objnum)
13713 {
13714         ship            *shipp = &Ships[Objects[objnum].instance];
13715         ai_info *aip = &Ai_info[shipp->ai_index];
13716         int             target_objnum;
13717
13718 //      validate_mode_submode(aip);
13719
13720         Assert((aip->mode != AIM_WAYPOINTS) || (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC));
13721
13722         // Set globals defining the current object and its enemy object.
13723         Pl_objp = &Objects[objnum];
13724
13725         if (aip->mode == AIM_WARP_OUT) {
13726                 ai_warp_out(Pl_objp);
13727                 return;
13728         }
13729
13730 /*      //      HACK! TEST! REMOVE ME!
13731         if (Ship_info[shipp->ship_info_index].flags & SIF_BIG_SHIP)
13732                 if (shipp->team == Player_ship->team)
13733                         aip->mode = AIM_CHASE;
13734 */
13735
13736 //      if (!strnicmp(Ships[Pl_objp->instance].ship_name, "cancer", 6))
13737 //              nprintf(("AI", "Ship %s: mode = %s, submode = %i\n", Ships[Pl_objp->instance].ship_name, Mode_text[aip->mode], aip->submode));
13738
13739         ai_maybe_self_destruct(Pl_objp, aip);
13740
13741 //      if ( timestamp_elapsed(aip->goal_check_time) ) {
13742                 ai_process_mission_orders( objnum, aip );
13743 //              aip->goal_check_time = timestamp_rand(1000,2000);
13744 //      }
13745
13746         //      Avoid a shockwave, if necessary.  If a shockwave and rearming, stop rearming.
13747         if (aip->ai_flags & AIF_AVOID_SHOCKWAVE) {
13748                 if (ai_avoid_shockwave(Pl_objp, aip)) {
13749                         aip->ai_flags &= ~(AIF_BIG_SHIP_COLLIDE_RECOVER_1 | AIF_BIG_SHIP_COLLIDE_RECOVER_2);
13750                         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
13751                                 ai_abort_rearm_request(Pl_objp);
13752                         return;
13753                 }
13754         } else {
13755                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_STARTED;
13756         }
13757
13758         // moved call to ai_do_repair frame here from below because of the subsequent if statment returning
13759         // if the ship is getting repaired
13760         //      If waiting to be repaired, just stop and sit.
13761         ai_do_repair_frame(Pl_objp, aip, flFrametime);
13762         if ((aip->ai_flags & AIF_AWAITING_REPAIR) || (aip->ai_flags & AIF_BEING_REPAIRED)) {
13763                 if (ai_await_repair_frame(Pl_objp, aip))
13764                         return;
13765         }
13766
13767         if (aip->mode == AIM_PLAY_DEAD)
13768                 return;
13769
13770         //      If recovering from a collision with a big ship, don't continue.
13771         if (maybe_big_ship_collide_recover_frame(Pl_objp, aip))
13772                 return;
13773
13774         ai_preprocess_ignore_objnum(Pl_objp, aip);
13775         target_objnum = set_target_objnum(aip, aip->target_objnum);
13776
13777         // 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));
13778
13779         Assert(objnum != target_objnum);
13780
13781         ai_manage_shield(Pl_objp, aip);
13782         
13783         if ( maybe_request_support(Pl_objp) ) {
13784                 if ( Ships[Pl_objp->instance].flags & SF_FROM_PLAYER_WING ) {
13785                         ship_maybe_tell_about_rearm(shipp);
13786                 }
13787         }
13788
13789         ai_maybe_warp_out(Pl_objp);
13790
13791 /*
13792         //      If this ship is attacking an object's subsystems and someone else destroyed
13793         //      the subsystem, it could continue attacking the ship.  Need to invalidate the objnum.
13794         if (target_objnum >= 0)
13795                 if (Objects[target_objnum].flags & OF_PROTECTED) {
13796                         // if (aip->targeted_subsys != NULL)
13797                         //      ; //nprintf(("AI", "subsys hits = %7.3f\n", aip->targeted_subsys->current_hits));
13798
13799                         if ((aip->targeted_subsys == NULL) || (aip->targeted_subsys->current_hits <= 0.0f)) {
13800                                 target_objnum = -1;
13801                                 aip->target_objnum = -1;
13802                         }
13803                 }
13804 */
13805
13806
13807         //      Find an enemy if don't already have one.
13808         En_objp = NULL;
13809         if ( ai_need_new_target(Pl_objp, target_objnum) ) {
13810                 if ((aip->mode != AIM_EVADE_WEAPON) && (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
13811                         aip->resume_goal_time = -1;
13812                         aip->active_goal = AI_GOAL_NONE;
13813                 } else if (aip->resume_goal_time == -1) {
13814                         // AL 12-9-97: Don't allow cargo and navbuoys to set their aip->target_objnum
13815                         if ( !(Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS) ) {
13816                                 target_objnum = find_enemy(objnum, MAX_ENEMY_DISTANCE, Skill_level_max_attackers[Game_skill_level]);            //      Attack up to 25K units away.
13817                                 if (target_objnum != -1) {
13818                                         if (aip->target_objnum != target_objnum)
13819                                                 aip->aspect_locked_time = 0.0f;
13820                                         set_target_objnum(aip, target_objnum);
13821                                         En_objp = &Objects[target_objnum];
13822                                 }
13823                         }
13824                 }
13825         } else if (target_objnum >= 0) {
13826                 En_objp = &Objects[target_objnum];
13827         }
13828
13829         // set base stealth info each frame
13830         aip->ai_flags &= ~AIF_STEALTH_PURSIUT;
13831         if (En_objp && En_objp->type == OBJ_SHIP) {
13832                 if (Ship_info[Ships[En_objp->instance].ship_info_index].flags & SIF_STEALTH) {
13833                         int stealth_state = ai_is_stealth_visible(Pl_objp, En_objp);
13834                         float dist = vm_vec_dist_quick(&En_objp->pos, &Pl_objp->pos);
13835
13836                         if (stealth_state != STEALTH_FULLY_TARGETABLE) {
13837                                 aip->ai_flags |= AIF_STEALTH_PURSIUT;
13838                         }
13839
13840                         if ( (stealth_state == STEALTH_FULLY_TARGETABLE) || (stealth_state == STEALTH_VISIBLE) ) {
13841                                 aip->stealth_last_visible_stamp = timestamp();
13842                                 aip->stealth_last_cheat_visible_stamp = aip->stealth_last_visible_stamp;
13843                                 aip->stealth_last_pos = En_objp->pos;
13844                                 aip->stealth_velocity = En_objp->phys_info.vel;
13845                         } else if (dist < 100) {
13846                                 // get cheat timestamp
13847                                 aip->stealth_last_cheat_visible_stamp = timestamp();
13848
13849                                 // set approximate pos and vel, with increasing error as time from last_visible_stamp increases
13850                                 update_ai_stealth_info_with_error(aip/*, 0*/);
13851                         }
13852                 }
13853         }
13854
13855         /*      if ((Pl_objp != NULL) && (En_objp != NULL)) {
13856                 slide_face_ship();
13857                 return;
13858         }
13859 */
13860         // AL 12-10-97: ensure that cargo and navbuoys aip->target_objnum is always -1.
13861         if ( Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS ) {
13862                 aip->target_objnum = -1;
13863         }
13864
13865         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)) {
13866                 mprintf(("Warning: Object and its enemy have same position.  Object #%i\n", Pl_objp-Objects));
13867                 En_objp = NULL;
13868         }
13869
13870         if (aip->mode == AIM_CHASE) {
13871                 if (En_objp == NULL) {
13872                         aip->active_goal = -1;
13873                 }
13874         }
13875
13876         //      If there is a goal to resume and enough time has elapsed, resume the goal.
13877         if ((aip->resume_goal_time > 0) && (aip->resume_goal_time < Missiontime)) {
13878                 aip->active_goal = AI_GOAL_NONE;
13879                 aip->resume_goal_time = -1;
13880                 target_objnum = find_enemy(objnum, 2000.0f, Skill_level_max_attackers[Game_skill_level]);
13881                 if (target_objnum != -1) {
13882                         if (aip->target_objnum != target_objnum) {
13883                                 aip->aspect_locked_time = 0.0f;
13884                         }
13885                         set_target_objnum(aip, target_objnum);
13886                 }
13887         }
13888
13889         // check if targeted subsystem has been destroyed, if so, move onto another subsystem
13890         // if trying to disable or disarm the target
13891         if ((En_objp != NULL) && ( aip->targeted_subsys != NULL )) {
13892                 Assert(En_objp->type == OBJ_SHIP);
13893                 if ( aip->targeted_subsys->current_hits <= 0.0f ) {
13894                         int subsys_type;
13895
13896                         if ( aip->goals[0].ai_mode == AI_GOAL_DISABLE_SHIP ) {
13897                                 subsys_type = SUBSYSTEM_ENGINE;
13898                         } else if ( aip->goals[0].ai_mode == AI_GOAL_DISARM_SHIP ) {
13899                                 subsys_type = SUBSYSTEM_TURRET;
13900                         } else {
13901                                 subsys_type = -1;
13902                         }
13903
13904                         if ( subsys_type != -1 ) {
13905                                 ship_subsys *new_subsys;
13906                                 new_subsys = ship_return_next_subsys(&Ships[En_objp->instance], subsys_type, &Pl_objp->pos);
13907                                 if ( new_subsys != NULL ) {
13908                                         set_targeted_subsys(aip, new_subsys, aip->target_objnum);
13909                                 } else {
13910                                         // AL 12-16-97: no more subsystems to attack... reset targeting info
13911                                         aip->target_objnum = -1;
13912                                         set_targeted_subsys(aip, NULL, -1);
13913                                 }
13914                         } else {
13915                                 // targeted subsys is destroyed, so stop attacking it
13916                                 set_targeted_subsys(aip, NULL, -1);
13917                         }
13918                 }
13919         }
13920
13921         ai_maybe_launch_cmeasure(Pl_objp, aip);
13922         ai_maybe_evade_locked_missile(Pl_objp, aip);
13923
13924         aip->target_time += flFrametime;
13925
13926         int in_formation = 0;
13927         if (aip->ai_flags & AIF_FORMATION) {
13928                 in_formation = !ai_formation();
13929         }
13930
13931         if ( !in_formation ) {
13932                 ai_execute_behavior(aip);
13933         }
13934
13935         process_subobjects(objnum);
13936         maybe_resume_previous_mode(Pl_objp, aip);
13937         
13938         if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON ) {
13939                 if (Missiontime > aip->afterburner_stop_time) {
13940                         //nprintf(("AI", "Frame %i, turning off afterburner.\n", AI_FrameCount));
13941                         afterburners_stop(Pl_objp);
13942                 }
13943         }
13944 //      validate_mode_submode(aip);
13945 }
13946
13947 int Waypoints_created = 0;
13948
13949 //      Find the ship with the name *name in the Ship_info array.
13950 int find_ship_name(char *name)
13951 {
13952         int     i;
13953
13954         for (i=0; i<Num_ship_types; i++)
13955                 if (!strcmp(Ship_info[i].name, name))
13956                         return i;
13957
13958         return -1;
13959 }
13960
13961 void create_waypoints()
13962 {
13963         int     i, j, z;
13964
13965         // Waypoints_created = 1;
13966
13967         if (Waypoints_created)
13968                 return;
13969
13970         for (j=0; j<Num_waypoint_lists; j++)
13971                 for (i=0; i<Waypoint_lists[j].count; i++) {
13972                         z = obj_create(OBJ_WAYPOINT, 0, j * 65536 + i, NULL,
13973                                 &Waypoint_lists[j].waypoints[i], 0.0f, OF_RENDERS);
13974                 }
13975
13976         Waypoints_created = 1;
13977 }
13978
13979 int Last_ai_obj = -1;
13980
13981 void ai_process( object * obj, int ai_index, float frametime )
13982 {
13983 //      if (Ships[obj->instance].flags & SF_DYING)
13984 //              nprintf(("AI", "Frame: %i Ship %s is dying!\n", Framecount, Ships[obj->instance].ship_name));
13985
13986         if (obj->flags & OF_SHOULD_BE_DEAD)
13987                 return;
13988
13989         // 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.
13990         if ((Ships[obj->instance].flags & SF_DYING ) && !(Ship_info[Ships[obj->instance].ship_info_index].flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP))){
13991                 return;
13992         }
13993
13994         int rfc = 1;            //      Assume will be Reading Flying Controls.
13995
13996         Assert( obj->type == OBJ_SHIP );
13997         Assert( ai_index >= 0 );
13998
13999         init_ship_info();
14000
14001         create_waypoints();
14002
14003         AI_frametime = frametime;
14004         if (obj-Objects <= Last_ai_obj) {
14005                 AI_FrameCount++;
14006         }
14007
14008         memset( &AI_ci, 0, sizeof(AI_ci) );
14009
14010         ai_frame(obj-Objects);
14011
14012         AI_ci.pitch = 0.0f;
14013         AI_ci.bank = 0.0f;
14014         AI_ci.heading = 0.0f;
14015
14016         // the ships maximum velocity now depends on the energy flowing to engines
14017         obj->phys_info.max_vel.xyz.z = Ships[obj->instance].current_max_speed;
14018         ai_info *aip = &Ai_info[Ships[obj->instance].ai_index];
14019
14020         //      In certain circumstances, the AI says don't fly in the normal way.
14021         //      One circumstance is in docking and undocking, when the ship is moving
14022         //      under thruster control.
14023         switch (aip->mode) {
14024         case AIM_DOCK:
14025                 if ((aip->submode >= AIS_DOCK_2) && (aip->submode != AIS_UNDOCK_3))
14026                         rfc = 0;
14027                 break;
14028         case AIM_WARP_OUT:
14029                 if (aip->submode >= AIS_WARP_3)
14030                         rfc = 0;
14031                 break;
14032 //      case AIM_NONE:
14033 //              if (aip->submode == AIS_NONE_FORMATION)
14034 //                      rfc = 0;
14035 //              break;
14036         default:
14037                 break;
14038         }
14039
14040         if (rfc == 1) {
14041                 vector copy_desired_rotvel = obj->phys_info.rotvel;
14042                 physics_read_flying_controls( &obj->orient, &obj->phys_info, &AI_ci, frametime);
14043                 // if obj is in formation and not flight leader, don't update rotvel
14044                 if (aip->ai_flags & AIF_FORMATION) {
14045                         if (&Objects[aip->goal_objnum] != obj) {
14046                                 obj->phys_info.desired_rotvel = copy_desired_rotvel;
14047                                 obj->phys_info.rotvel = copy_desired_rotvel;
14048                         }
14049                 }
14050         }
14051
14052         Last_ai_obj = obj-Objects;
14053 }
14054
14055 //      Initialize ai_info struct of object objnum.
14056 void init_ai_object(int objnum)
14057 {
14058         int     ship_index, ai_index;
14059         ai_info *aip;
14060         int ship_type;
14061         object  *objp;
14062         vector  near_vec;                       //      A vector nearby and mainly in front of this object.
14063
14064         objp = &Objects[objnum];
14065         ship_index = objp->instance;
14066         ai_index = Ships[ship_index].ai_index;
14067         Assert((ai_index >= 0) && (ai_index < MAX_AI_INFO));
14068
14069         aip = &Ai_info[ai_index];
14070
14071         ship_type = Ships[ship_index].ship_info_index;
14072
14073         vm_vec_scale_add(&near_vec, &objp->pos, &objp->orient.v.fvec, 100.0f);
14074         vm_vec_scale_add2(&near_vec, &objp->orient.v.rvec, 10.0f);
14075
14076         // Things that shouldn't have to get initialized, but initialize them just in case!
14077         aip->ai_flags = 0;
14078         aip->previous_mode = AIM_NONE;
14079         aip->mode_time = -1;
14080         aip->target_objnum = -1;
14081         aip->target_signature = -1;
14082         aip->previous_target_objnum = -1;
14083         aip->target_time = 0.0f;
14084         aip->enemy_wing = -1;
14085         aip->attacker_objnum = -1;
14086         aip->goal_objnum = -1;
14087         aip->goal_signature = -1;
14088         aip->guard_objnum = -1;
14089         aip->guard_signature = -1;
14090         aip->guard_wingnum = -1;
14091         aip->dock_signature = -1;
14092         aip->submode = 0;
14093         aip->previous_submode = 0;
14094         aip->best_dot_to_enemy = -1.0f;
14095         aip->best_dot_from_enemy = -1.0f;
14096         aip->best_dot_to_time = 0;
14097         aip->best_dot_from_time = 0;
14098         aip->submode_start_time = 0;
14099         aip->submode_parm0 = 0;
14100         aip->active_goal = -1;
14101         aip->goal_check_time = timestamp(0);
14102         aip->last_predicted_enemy_pos = near_vec;
14103         aip->prev_goal_point = near_vec;
14104         aip->goal_point = near_vec;
14105         aip->time_enemy_in_range = 0.0f;
14106         aip->last_attack_time = 0;
14107         aip->last_hit_time = 0;
14108         aip->last_hit_quadrant = 0;
14109         aip->hitter_objnum = -1;
14110         aip->hitter_signature = -1;
14111         aip->resume_goal_time = -1;
14112         aip->prev_accel = 0.0f;
14113         aip->prev_dot_to_goal = 0.0f;
14114
14115         aip->ignore_objnum = UNUSED_OBJNUM;
14116         aip->ignore_signature = -1;
14117
14118         // aip->mode = AIM_NONE;
14119
14120         // End of Things that shouldn't have to get initialized, but initialize them just in case!
14121
14122         aip->ai_courage = Ai_classes[Ship_info[ship_type].ai_class].ai_courage[Game_skill_level];
14123         aip->ai_patience = Ai_classes[Ship_info[ship_type].ai_class].ai_patience[Game_skill_level];
14124         aip->ai_evasion = Ai_classes[Ship_info[ship_type].ai_class].ai_evasion[Game_skill_level];
14125         aip->ai_accuracy = Ai_classes[Ship_info[ship_type].ai_class].ai_accuracy[Game_skill_level];
14126
14127         if (Num_waypoint_lists > 0) {
14128                 aip->wp_index = -1;
14129                 aip->wp_list = -1;
14130         } else {
14131                 aip->wp_index = -1;
14132                 aip->wp_list = -1;
14133         }
14134
14135         aip->attacker_objnum = -1;
14136         aip->goal_signature = -1;
14137
14138         Objects[objnum].phys_info.prev_fvec = Objects[objnum].orient.v.fvec;
14139
14140         aip->last_predicted_enemy_pos.xyz.x = 0.0f;     //      Says this value needs to be recomputed!
14141         aip->time_enemy_in_range = 0.0f;
14142
14143         aip->resume_goal_time = -1;                                     //      Say there is no goal to resume.
14144
14145         aip->active_goal = -1;
14146         aip->path_start = -1;
14147         aip->path_goal_dist = -1;
14148         aip->path_length = 0;
14149         aip->path_subsystem_next_check = 1;
14150         aip->dock_path_index = -1;
14151         aip->dock_index = -1;
14152         aip->dock_objnum = -1;
14153
14154         aip->danger_weapon_objnum = -1;
14155         aip->danger_weapon_signature = -1;
14156
14157         aip->lead_scale = 0.0f;
14158         aip->last_hit_target_time = Missiontime;
14159
14160         aip->nearest_locked_object = -1;
14161         aip->nearest_locked_distance = 99999.0f;
14162
14163         aip->targeted_subsys = NULL;
14164         aip->last_subsys_target = NULL;
14165         aip->targeted_subsys_parent = -1;
14166
14167         // The next two fields are used to time the rearming to allow useful sound effects for missile rearming
14168         aip->rearm_first_missile = TRUE;                //      flag to indicate that next missile to load is the first missile
14169         aip->rearm_release_delay = 0;                   //      timestamp to delay the separation of docked ships after rearm
14170
14171         aip->next_predict_pos_time = 0;
14172
14173         aip->afterburner_stop_time = 0;
14174         aip->last_objsig_hit = -1;                              // object signature of the ship most recently hit by aip
14175
14176         aip->path_next_create_time = timestamp(1);
14177         aip->path_create_pos = Objects[objnum].pos;
14178         aip->path_create_orient = Objects[objnum].orient;
14179
14180         aip->ignore_expire_timestamp = timestamp(1);
14181         aip->warp_out_timestamp = 0;
14182         aip->next_rearm_request_timestamp = timestamp(1);
14183         aip->primary_select_timestamp = timestamp(1);
14184         aip->secondary_select_timestamp = timestamp(1);
14185         aip->scan_for_enemy_timestamp = timestamp(1);
14186
14187         aip->choose_enemy_timestamp = timestamp(3*(NUM_SKILL_LEVELS-Game_skill_level) * ((rand_alt() % 500) + 500));
14188
14189         aip->shockwave_object = -1;
14190         aip->shield_manage_timestamp = timestamp(1);
14191         aip->self_destruct_timestamp = -1;      //      This is a flag that we have not yet set this.
14192         aip->ok_to_target_timestamp = timestamp(1);
14193         aip->pick_big_attack_point_timestamp = timestamp(1);
14194         vm_vec_zero(&aip->big_attack_point);
14195
14196         aip->avoid_check_timestamp = timestamp(1);
14197
14198         aip->abort_rearm_timestamp = -1;
14199
14200         // artillery stuff
14201         aip->artillery_objnum = -1;
14202         aip->artillery_sig = -1;        
14203
14204         // waypoint speed cap
14205         aip->waypoint_speed_cap = -1;
14206
14207         // set lethality to enemy team
14208         aip->lethality = 0.0f;
14209 }
14210
14211 void init_ai_objects()
14212 {
14213         int     i;
14214
14215         for (i=0; i<num_objects; i++){
14216                 if (Objects[i].type == OBJ_SHIP){
14217                         init_ai_object(i);
14218                 }
14219         }
14220 }
14221
14222 void init_ai_system()
14223 {
14224         // MWA -- removed next line of code on 11/12/97.  When a ship is created
14225         // it calls init_ai_object() on it's objnum.  Doing this init at the point where
14226         // this function gets called messes things up.
14227         //init_ai_objects();
14228
14229         Ppfp = Path_points;
14230         Waypoints_created = 0;
14231
14232         Dock_path_warning_given = 0;
14233
14234 /*      for (int i=0; i<MAX_IGNORE_OBJECTS; i++) {
14235                 Ignore_objects[i].objnum = -1;
14236                 Ignore_objects[i].signature = -1;
14237         }
14238 */
14239
14240 }
14241
14242 void ai_set_default_behavior(object *obj, int classnum)
14243 {
14244         ai_info *aip;
14245
14246         Assert(obj != NULL);
14247         Assert(obj->instance != -1);
14248         Assert(Ships[obj->instance].ai_index != -1);
14249
14250         aip = &Ai_info[Ships[obj->instance].ai_index];
14251
14252         aip->behavior = classnum;
14253
14254 }
14255
14256 void ai_do_default_behavior(object *obj)
14257 {
14258         ai_info *aip;
14259         int             ship_flags;
14260
14261         Assert(obj != NULL);
14262         Assert(obj->instance != -1);
14263         Assert(Ships[obj->instance].ai_index != -1);
14264
14265         aip = &Ai_info[Ships[obj->instance].ai_index];
14266
14267         ship_flags = Ship_info[Ships[obj->instance].ship_info_index].flags;
14268         if (!is_instructor(obj) && (ship_flags & (SIF_FIGHTER | SIF_BOMBER))) {
14269                 int enemy_objnum = find_enemy(OBJ_INDEX(obj), 1000.0f, Skill_level_max_attackers[Game_skill_level]);
14270                 set_target_objnum(aip, enemy_objnum);
14271                 aip->mode = AIM_CHASE;
14272                 aip->submode = SM_ATTACK;
14273         } else if (ship_flags & (SIF_SUPPORT)) {
14274                 aip->mode = AIM_SAFETY;
14275                 aip->submode = AISS_1;
14276                 aip->ai_flags &= ~(AIF_REPAIRING);
14277         } else if ( ship_flags & SIF_SENTRYGUN ) {
14278                 aip->mode = AIM_SENTRYGUN;
14279         } else {
14280                 aip->mode = AIM_NONE;
14281         }
14282         
14283         aip->submode_start_time = Missiontime;
14284         aip->active_goal = AI_GOAL_NONE;
14285 }
14286
14287 #define FRIENDLY_DAMAGE_THRESHOLD       50.0f           //      Display a message at this threshold.  Note, this gets scaled by Skill_level
14288
14289 // send the given message from objp.  called from the maybe_process_friendly_hit
14290 // code below when a message must get send to the player when he fires on friendlies
14291 void process_friendly_hit_message( int message, object *objp )
14292 {
14293         int index;
14294
14295         // no traitor in multiplayer
14296         if(Game_mode & GM_MULTIPLAYER){
14297                 return;
14298         }
14299
14300         // don't send this message if a player ship was hit.
14301         if ( objp->flags & OF_PLAYER_SHIP ){
14302                 return;
14303         }
14304
14305         // check if objp is a cargo contianer -- if so, then find a new ship to send the message
14306         index = objp->instance;
14307         if ( !(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER|SIF_BOMBER)) ){
14308                 index = -1;
14309         }
14310
14311         // if the message is "oops" (the don't hit me message), always make come from Terran command
14312         if ( message == MESSAGE_OOPS ){
14313                 index = -1;
14314         }
14315
14316         if ( index >= 0){
14317                 message_send_builtin_to_player( message, &Ships[index], MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14318         } else {
14319                 message_send_builtin_to_player( message, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14320         }
14321 }
14322
14323 extern  void ship_set_subsystem_strength( ship *shipp, int type, float strength );
14324
14325 //      Object *objp_weapon, fired by *objp_hitter, hit object *objp_ship.
14326 void maybe_process_friendly_hit(object *objp_hitter, object *objp_hit, object *objp_weapon)
14327 {
14328         // no turning traitor in multiplayer
14329         if ( Game_mode & GM_MULTIPLAYER ) {
14330                 return;
14331         }
14332
14333         // ditto if mission says no traitors allowed
14334         if (The_mission.flags & MISSION_FLAG_NO_TRAITOR) {
14335                 return;
14336         }
14337
14338         if ((objp_hitter == Player_obj) && (Player_ship->team == TEAM_FRIENDLY)) {
14339
14340                 // AL 12-4-97: It is possible the Player is a OBJ_GHOST at this point.  If so, bail out.
14341                 if ( objp_hitter->type != OBJ_SHIP ) {
14342                         return;
14343                 }
14344
14345                 Assert(objp_hitter->type == OBJ_SHIP);
14346                 Assert(objp_hit->type == OBJ_SHIP);
14347                 Assert(objp_weapon->type == OBJ_WEAPON);
14348
14349                 ship    *shipp_hitter = &Ships[objp_hitter->instance];
14350                 ship    *shipp_hit = &Ships[objp_hit->instance];
14351
14352                 if (shipp_hitter->team != shipp_hit->team) {
14353                         return;
14354                 }
14355
14356                 // get the player
14357                 player *pp = &Players[Player_num];
14358
14359                 // wacky stuff here
14360                 if (pp->friendly_hits != 0) {
14361                         float   time_since_last_hit = f2fl(Missiontime - pp->friendly_last_hit_time);
14362                         if ((time_since_last_hit >= 0.0f) && (time_since_last_hit < 10000.0f)) {
14363                                 if (time_since_last_hit > 60.0f) {
14364                                         pp->friendly_hits = 0;
14365                                         pp->friendly_damage = 0.0f;
14366                                 } else if (time_since_last_hit > 2.0f) {
14367                                         pp->friendly_hits -= (int) time_since_last_hit/2;
14368                                         pp->friendly_damage -= time_since_last_hit;
14369                                 }
14370
14371                                 if (pp->friendly_damage < 0.0f) {
14372                                         pp->friendly_damage = 0.0f;
14373                                 }
14374
14375                                 if (pp->friendly_hits < 0) {
14376                                         pp->friendly_hits = 0;
14377                                 }
14378                         }
14379                 }
14380
14381                 float   damage;         //      Damage done by weapon.  Gets scaled down based on size of ship.
14382
14383                 damage = Weapon_info[Weapons[objp_weapon->instance].weapon_info_index].damage;
14384                 
14385                 // wacky stuff here
14386                 ship_info *sip = &Ship_info[Ships[objp_hit->instance].ship_info_index];
14387                 if (sip->initial_hull_strength > 1000.0f) {
14388                         float factor = sip->initial_hull_strength / 1000.0f;
14389                         factor = min(100.0f, factor);
14390                         damage /= factor;
14391                 }
14392
14393                 //      Don't penalize much at all for hitting cargo
14394                 if (sip->flags & (SIF_CARGO | SIF_SENTRYGUN)) {
14395                         damage /= 10.0f;
14396                 }
14397
14398                 //      Hit ship, but not targeting it, so it's not so heinous, maybe an accident.
14399                 if (Ai_info[shipp_hitter->ai_index].target_objnum != OBJ_INDEX(objp_hit)) {
14400                         damage /= 5.0f;
14401                 }
14402
14403                 pp->friendly_last_hit_time = Missiontime;
14404                 pp->friendly_hits++;
14405
14406                 // cap damage and number of hits done this frame
14407                 float accredited_damage = min(MAX_BURST_DAMAGE, pp->damage_this_burst + damage) - pp->damage_this_burst;
14408                 pp->friendly_damage += accredited_damage;
14409                 pp->damage_this_burst += accredited_damage;
14410
14411                 // Done with adjustments to damage.  Evaluate based on current friendly_damage
14412                 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 ));
14413                 
14414                 if (is_instructor(objp_hit)) {
14415                         // it's not nice to hit your instructor
14416                         if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD) {
14417                                 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_ATTACK, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14418                                 pp->last_warning_message_time = Missiontime;
14419                                 ship_set_subsystem_strength( Player_ship, SUBSYSTEM_WEAPONS, 0.0f);
14420
14421                                 training_fail();
14422
14423                                 //      Instructor warp out.
14424                                 ai_set_mode_warp_out(objp_hit, &Ai_info[Ships[objp_hit->instance].ai_index]);
14425                                 gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START_FORCED );     //      Force player to warp out.
14426
14427                                 //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) );
14428                                 //ship_apply_global_damage( objp_hitter, objp_hit, NULL, 1.0f );
14429                         } else if (Missiontime - pp->last_warning_message_time > F1_0*4) {
14430                                 // warning every 4 sec
14431                                 // use NULL as the message sender here since it is the Terran Command persona
14432                                 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_HIT, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14433                                 pp->last_warning_message_time = Missiontime;
14434                         }
14435
14436                 // not nice to hit your friends
14437                 } else if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f)) {
14438                         process_friendly_hit_message( MESSAGE_HAMMER_SWINE, objp_hit );
14439                         mission_goal_fail_all();
14440                         ai_abort_rearm_request( Player_obj );
14441
14442                         Player_ship->team = TEAM_TRAITOR;
14443
14444                 } else if ((damage > frand()) && (Missiontime - pp->last_warning_message_time > F1_0*4) && (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD)) {
14445                         // no closer than 4 sec intervals
14446                         //      Note: (damage > frand()) added on 12/9/97 by MK.  Since damage is now scaled down for big ships, we could get too
14447                         //      many warnings.  Kind of tedious.  frand() returns a value in 0..1, so this won't affect legit hits.
14448                         process_friendly_hit_message( MESSAGE_OOPS, objp_hit );
14449                         pp->last_warning_message_time = Missiontime;
14450                 }
14451         }
14452 }
14453
14454 //      Maybe make ship with ai_info *aip attack hitter_objnum as a dynamic goal
14455 void maybe_set_dynamic_chase(ai_info *aip, int hitter_objnum)
14456 {
14457         Assert(Ship_info[Ships[aip->shipnum].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER));
14458
14459         // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
14460         if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
14461                 return;
14462         }
14463
14464         // only set as target if can be targeted.
14465         if (awacs_get_level(&Objects[hitter_objnum], &Ships[aip->shipnum], 1) < 1) {
14466                 return;
14467         }
14468
14469         if (aip->target_objnum != hitter_objnum)
14470                 aip->aspect_locked_time = 0.0f;
14471         set_target_objnum(aip, hitter_objnum);
14472         aip->resume_goal_time = Missiontime + i2f(20);  //      Only chase up to 20 seconds.
14473         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14474
14475         set_targeted_subsys(aip, NULL, -1);             //      Say not attacking any particular subsystem.
14476
14477         aip->previous_submode = aip->mode;
14478         aip->mode = AIM_CHASE;
14479         aip->submode = SM_ATTACK;
14480 }
14481
14482
14483 //      Return true if *objp has armed an aspect seeking bomb.
14484 //      This function written so a ship with an important bomb to fire will willingly take hits in the face to fire its bomb.
14485 int firing_aspect_seeking_bomb(object *objp)
14486 {
14487         ship    *shipp;
14488         int     bank_index;
14489         ship_weapon     *swp;
14490
14491         shipp = &Ships[objp->instance];
14492
14493         swp = &shipp->weapons;
14494
14495         bank_index = swp->current_secondary_bank;
14496
14497         if (bank_index != -1)
14498                 if (swp->secondary_bank_ammo[bank_index] > 0) {
14499                         if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_BOMB) {
14500                                 if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_HOMING_ASPECT) {
14501                                         return 1;
14502                                 }
14503                         }
14504                 }
14505
14506         return 0;
14507 }
14508
14509 //      *objp collided with big ship *big_objp at global point *collide_pos
14510 //      Make it fly away from the collision point.
14511 // collision_normal is NULL, when a collision is imminent and we just want to bug out.
14512 void big_ship_collide_recover_start(object *objp, object *big_objp, vector *collide_pos, vector *collision_normal)
14513 {
14514         ai_info *aip;
14515
14516         Assert(objp->type == OBJ_SHIP);
14517
14518         aip = &Ai_info[Ships[objp->instance].ai_index];
14519
14520         if (!timestamp_elapsed(aip->big_recover_timestamp) && (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1))
14521                 return;
14522
14523         //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)));
14524         if (collision_normal) {
14525                 aip->big_recover_timestamp = timestamp(2000);
14526                 aip->big_collision_normal = *collision_normal;
14527         //      nprintf(("AI", " normal\n"));
14528         } else {
14529                 aip->big_recover_timestamp = timestamp(500);
14530         //      nprintf(("AI", " no normal\n"));
14531         }
14532
14533
14534         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
14535         aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_1;
14536
14537 //      vector  out_vec;
14538 //      vm_vec_normalized_dir(&out_vec, &objp->pos, collide_pos);
14539
14540         // big_recover_pos_1 is 100 m out along normal
14541         vector direction;
14542         if (collision_normal) {
14543                 direction = *collision_normal;
14544         } else {
14545                 vm_vec_copy_scale(&direction, &objp->orient.v.fvec, -1.0f);
14546         }
14547         vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &direction, 100.0f);
14548
14549         // go out 200 m from box closest box point
14550         get_world_closest_box_point_with_delta(&aip->big_recover_pos_2, big_objp, &aip->big_recover_pos_1, NULL, 300.0f);
14551
14552         accelerate_ship(aip, 0.0f);
14553 /*
14554         if (vm_vec_dot(collision_normal, &objp->orient.v.fvec) > 0.5f) {
14555 //              vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14556 //              vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.v.uvec, big_objp->radius/2.0f);
14557 //              vm_vec_scale_add(&aip->big_recover_pos_2, &objp->pos, &out_vec, big_objp->radius*2.0f);
14558                 accelerate_ship(aip, 2.0f);
14559         } else {
14560 //              vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14561 //              vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.v.uvec, big_objp->radius/2.0f);
14562                 accelerate_ship(aip, 0.0f);
14563         } */
14564 }
14565
14566 float max_lethality = 0.0f;
14567
14568 void ai_update_lethality(object *ship_obj, object *other_obj, float damage)
14569 {
14570         Assert(ship_obj->type == OBJ_SHIP);
14571         Assert(other_obj->type == OBJ_WEAPON || other_obj->type == OBJ_SHOCKWAVE);
14572         int dont_count = FALSE;
14573
14574         int parent = other_obj->parent;
14575         if (Objects[parent].type == OBJ_SHIP) {
14576                 if (Objects[parent].signature == other_obj->parent_sig) {
14577
14578                         // check damage done to enemy team
14579                         if (Ships[ship_obj->instance].team != Ships[Objects[parent].instance].team) {
14580
14581                                 // other is weapon
14582                                 if (other_obj->type == OBJ_WEAPON) {
14583                                         weapon *wp = &Weapons[other_obj->instance];
14584                                         weapon_info *wif = &Weapon_info[wp->weapon_info_index];
14585
14586                                         // if parent is BIG|HUGE, don't count beam
14587                                         if (Ship_info[Ships[Objects[parent].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
14588                                                 if (wif->wi_flags & WIF_BEAM) {
14589                                                         dont_count = TRUE;
14590                                                 }
14591                                         }
14592                                 }
14593
14594                                 if (!dont_count) {
14595                                         float lethality = 0.025f * damage;      // 2 cyclops (@2000) put you at 100 lethality
14596
14597                                         // increase lethality weapon's parent ship
14598                                         ai_info *aip = &Ai_info[Ships[Objects[parent].instance].ai_index];
14599                                         aip->lethality += lethality;
14600                                         aip->lethality = min(110.0f, aip->lethality);
14601                                         // if you hit, don;t be less than 0
14602                                         aip->lethality = max(0.0f, aip->lethality);
14603
14604 //                                      if (aip->lethality > max_lethality) {
14605 //                                              max_lethality = aip->lethality;
14606 //                                              mprintf(("new lethalilty high: %.1f\n", max_lethality));
14607 //                                      }
14608
14609                                         // if parent is player, show his lethality
14610 //                                      if (Objects[parent].flags & OF_PLAYER_SHIP) {
14611 //                                              mprintf(("Player lethality: %.1f\n", aip->lethality));
14612 //                                      }
14613                                 }
14614                         }
14615                 }
14616         }
14617 }
14618
14619
14620 //      Object *objp_ship was hit by either weapon *objp_weapon or collided into by ship hit_objp at point *hitpos.
14621 void ai_ship_hit(object *objp_ship, object *hit_objp, vector *hitpos, int shield_quadrant, vector *hit_normal)
14622 {
14623         int             hitter_objnum = -2;
14624         object  *objp_hitter = NULL;
14625         ship            *shipp;
14626         ai_info *aip, *hitter_aip;
14627
14628         shipp = &Ships[objp_ship->instance];
14629         aip = &Ai_info[shipp->ai_index];
14630
14631         if (objp_ship->flags & OF_PLAYER_SHIP)
14632                 return;
14633
14634         if ((aip->mode == AIM_WARP_OUT) || (aip->mode == AIM_PLAY_DEAD))
14635                 return;
14636
14637         if (hit_objp->type == OBJ_SHIP) {
14638                 //      If the object that this ship collided with is a big ship
14639                 if (Ship_info[Ships[hit_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
14640                         //      And the current object is _not_ a big ship
14641                         if (!(Ship_info[Ships[objp_ship->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
14642                                 //      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.
14643                                 big_ship_collide_recover_start(objp_ship, hit_objp, hitpos, hit_normal);
14644                         }
14645                 }
14646         }
14647
14648         if (hit_objp->type == OBJ_WEAPON) {
14649                 //      Make sure the object that fired this weapon is still alive.  If not, abort.
14650                 // Assert(hit_objp->parent >= 0);
14651                 if(hit_objp->parent < 0){
14652                         return;
14653                 }
14654                 if ( hit_objp->parent_sig != Objects[hit_objp->parent].signature ){
14655                         return;
14656                 }
14657
14658                 //      Hit by a protected ship, don't attack it.
14659                 if (Objects[hit_objp->parent].flags & OF_PROTECTED) {
14660                         if ((Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) && (aip->target_objnum == -1)) {
14661                                 if (aip->mode == AIM_CHASE) {
14662                                         if (aip->submode != SM_EVADE_WEAPON) {
14663                                                 aip->mode = AIM_CHASE;
14664                                                 aip->submode = SM_EVADE_WEAPON;
14665                                                 aip->submode_start_time = Missiontime;
14666                                         }
14667                                 } else if (aip->mode != AIM_EVADE_WEAPON) {
14668                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14669                                         aip->previous_mode = aip->mode;
14670                                         aip->previous_submode = aip->submode;
14671                                         aip->mode = AIM_EVADE_WEAPON;
14672                                         aip->submode = -1;
14673                                         aip->submode_start_time = Missiontime;
14674                                         aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Evade for up to five seconds.
14675                                 }
14676
14677                         }
14678                         return;
14679                 }
14680
14681                 hitter_objnum = hit_objp->parent;
14682                 Assert((hitter_objnum >= 0) && (hitter_objnum < MAX_OBJECTS));
14683                 objp_hitter = &Objects[hitter_objnum];
14684                 maybe_process_friendly_hit(objp_hitter, objp_ship, hit_objp);           //      Deal with player's friendly fire.
14685
14686                 if ( (shipp->team & TEAM_FRIENDLY) && !(Game_mode & GM_MULTIPLAYER) ) {
14687                         ship_maybe_ask_for_help(shipp);
14688                 }
14689         } else if (hit_objp->type == OBJ_SHIP) {
14690                 if (shipp->team == Ships[hit_objp->instance].team)              //      Don't have AI react to collisions between teammates.
14691                         return;
14692                 objp_hitter = hit_objp;
14693                 hitter_objnum = hit_objp-Objects;
14694         } else {
14695                 Int3(); //      Hmm, what kind of object hit this if not weapon or ship?  Get MikeK.
14696                 return;
14697         }
14698
14699         //      Collided into a protected ship, don't attack it.
14700         if (hit_objp->flags & OF_PROTECTED)
14701                 return;
14702
14703         Assert(objp_hitter != NULL);
14704         hitter_aip = &Ai_info[Ships[objp_hitter->instance].ai_index];
14705         hitter_aip->last_hit_target_time = Missiontime;
14706         
14707         // store the object signature of objp_ship into ai_info, since we want to track the last ship hit by 'hitter_objnum'
14708         hitter_aip->last_objsig_hit = objp_ship->signature; 
14709
14710         aip->last_hit_time = Missiontime;
14711
14712         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
14713                 return;
14714
14715         //      If this ship is awaiting repair, abort!
14716         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)) {
14717                 ship_info       *sip = &Ship_info[shipp->ship_info_index];
14718
14719                 if (objp_ship->hull_strength/sip->initial_hull_strength < 0.3f) {
14720                         //      No, only abort if hull below a certain level.
14721                         aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP/2);  //      Might request again after 15 seconds.
14722                         if ( !(objp_ship->flags & OF_PLAYER_SHIP) )                                             // mwa -- don't abort rearm for a player
14723                                 ai_abort_rearm_request(objp_ship);
14724                 }
14725         }
14726
14727         //      If firing a bomb, ignore enemy fire so we can gain lock drop the bomb.
14728         //      Only ignore fire if aspect_locked_time > 0.5f, as this means we're in range.
14729         if (firing_aspect_seeking_bomb(objp_ship)) {
14730                 if ((aip->ai_flags & AIF_SEEK_LOCK) && (aip->aspect_locked_time > 0.1f))
14731                         return;
14732         }
14733
14734         //      If in AIM_STRAFE mode and got hit by target, maybe attack turret if appropriate
14735         if (aip->mode == AIM_STRAFE) {
14736                 Assert(hitter_objnum != -2);
14737                 if (aip->target_objnum == hitter_objnum) {
14738                         if ( hit_objp->type == OBJ_WEAPON ) {
14739                                 ai_big_strafe_maybe_attack_turret(objp_ship, hit_objp);
14740                         }
14741                         return;
14742                 }
14743                 else {
14744                                 // AL 11-10-97:
14745                         ;       // do nothing here, we'll attack this hitter if it is a fighter or bomber (this is handled
14746                                 // in code later in this function
14747                 }
14748         }
14749
14750         if (objp_ship == Player_obj)
14751                 return;         //      We don't do AI for the player.
14752
14753         maybe_update_guard_object(objp_ship, objp_hitter);
14754
14755         //      Big ships don't go any further.
14756         if (!(Ship_info[shipp->ship_info_index].flags & SIF_SMALL_SHIP))
14757                 return;
14758
14759         //      If the hitter object is the ignore object, don't attack it.
14760         ship_info       *sip = &Ship_info[shipp->ship_info_index];
14761         if ((is_ignore_object(aip, objp_hitter-Objects)) && (sip->flags & (SIF_BOMBER | SIF_FIGHTER))) {
14762                 if (aip->mode == AIM_NONE) {
14763                         aip->mode = AIM_CHASE;  //      This will cause the ship to move, if not attack.
14764                         aip->submode = SM_EVADE;
14765                 }
14766                 return;
14767         }
14768
14769         //      Maybe abort based on mode.
14770         switch (aip->mode) {
14771         case AIM_CHASE:
14772                 if (aip->submode == SM_ATTACK_FOREVER)
14773                         return;
14774
14775                 if ( hit_objp->type == OBJ_WEAPON ) {
14776                         if ( ai_big_maybe_enter_strafe_mode(objp_ship, OBJ_INDEX(hit_objp), 1) )
14777                                 return;
14778                 }
14779
14780         case AIM_GUARD:
14781                 //      If in guard mode and far away from guard object, don't pursue guy that hit me.
14782                         if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
14783                                 if (vm_vec_dist_quick(&objp_ship->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
14784                                         return;
14785                                 }
14786                         }
14787         case AIM_STILL:
14788         case AIM_STAY_NEAR:
14789                 // Note: Dealt with above, at very top.  case AIM_PLAY_DEAD:
14790         case AIM_STRAFE:
14791                 break;
14792         case AIM_EVADE_WEAPON:
14793         case AIM_EVADE:
14794         case AIM_GET_BEHIND:
14795         case AIM_AVOID:
14796         case AIM_DOCK:
14797         case AIM_BIGSHIP:
14798         case AIM_PATH:
14799         case AIM_NONE:
14800         case AIM_BAY_DEPART:
14801         case AIM_SENTRYGUN:
14802                 return;
14803         case AIM_BAY_EMERGE:
14804                 // If just leaving the docking bay, don't react to enemy fire... just keep flying away from docking bay
14805                 if ( (Missiontime - aip->submode_start_time) < 5*F1_0 ) {
14806                         return;
14807                 }
14808                 break;
14809         case AIM_WAYPOINTS:
14810                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER))
14811                         break;
14812                 else
14813                         return;
14814                 break;
14815         case AIM_SAFETY:
14816                 if ((aip->submode != AISS_1) || (Missiontime - aip->submode_start_time > i2f(1))) {
14817                         aip->submode = AISS_1;
14818                         aip->submode_start_time = Missiontime;
14819                 }
14820                 return;
14821                 break;
14822         case AIM_WARP_OUT:
14823                 return;
14824                 break;
14825         default:
14826                 Int3(); //      Bogus mode!
14827         }
14828
14829         if (timestamp_elapsed(aip->ok_to_target_timestamp))
14830                 aip->ai_flags &= ~AIF_FORMATION;                        //      If flying in formation, bug out!
14831
14832         aip->hitter_objnum = hitter_objnum;
14833         aip->hitter_signature = Objects[hitter_objnum].signature;
14834
14835         //      If the hitter is not on the same team as the hittee, do some stuff.
14836         if (shipp->team != Ships[objp_hitter->instance].team) {
14837                 //nprintf(("AI", "Object %i attacking %i, who just hit him!\n", objp_ship-Objects, hitter_objnum));
14838
14839                 if ((hitter_objnum != aip->target_objnum) && (sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
14840                         maybe_set_dynamic_chase(aip, hitter_objnum);
14841                         maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14842                 } else {
14843                         if ((aip->mode == AIM_CHASE) && ((objp_ship->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp_ship)/sip->shields > 0.8f))) {
14844                                 switch (aip->submode) {
14845                                 case SM_ATTACK:
14846                                 case SM_SUPER_ATTACK:
14847                                 case SM_GET_AWAY:
14848                                         break;
14849                                 default:
14850                                         if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
14851                                                 maybe_set_dynamic_chase(aip, hitter_objnum);
14852                                         }
14853                                         maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14854                                         break;
14855                                 }
14856                         } else if (aip->mode == AIM_CHASE) {
14857                                 switch (aip->submode) {
14858                                 case SM_ATTACK:
14859                                         aip->submode = SM_EVADE;
14860                                         aip->submode_start_time = Missiontime;
14861                                         break;
14862                                 case SM_SUPER_ATTACK:
14863                                         if (Missiontime - aip->submode_start_time > i2f(1)) {
14864                                                 aip->submode = SM_EVADE;
14865                                                 aip->submode_start_time = Missiontime;
14866                                         }
14867                                         break;
14868                                 case SM_EVADE_BRAKE:
14869                                         break;
14870                                 case SM_EVADE_SQUIGGLE:
14871                                         aip->submode = SM_EVADE;
14872                                         aip->submode_start_time = Missiontime;
14873                                         break;
14874                                 default:
14875                                         if (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) {
14876                                                 maybe_set_dynamic_chase(aip, hitter_objnum);
14877                                                 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14878                                         }
14879
14880                                         break;
14881                                 }
14882                         } else {
14883                                 // AL 3-15-98: Prevent escape pods from entering chase mode
14884                                 if ( (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) ) {
14885                                         maybe_set_dynamic_chase(aip, hitter_objnum);
14886                                 }
14887                                 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14888                         }
14889                 }
14890         }
14891 }
14892
14893 //      Ship shipnum has been destroyed.
14894 //      Cleanup.
14895 // the parameter 'method' is used to tell is this ship was destroyed or it departed normally.
14896 // This function will get called in either case, and there are things that should be done if
14897 // the ship actually gets destroyed which shouldn't get done if it departed.
14898 void ai_ship_destroy(int shipnum, int method)
14899 {
14900         int             objnum;
14901         object  *other_objp;
14902         ship            *shipp;
14903         ship_obj        *so;
14904         ai_info *dead_aip;
14905
14906         Assert((shipnum >= 0) && (shipnum < MAX_SHIPS));
14907         objnum = Ships[shipnum].objnum;
14908         dead_aip = &Ai_info[Ships[shipnum].ai_index];
14909
14910         // if I was getting repaired, or awaiting repair, then cleanup the repair mode.  When awaiting repair, the dock_objnum
14911         // is -1.  When the support ship is on the way, the dock_objnum >= 0 (points to support ship).
14912         if ( dead_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14913                 if ( dead_aip->dock_objnum >= 0 )
14914                         ai_do_objects_repairing_stuff( &Objects[objnum], &Objects[dead_aip->dock_objnum], REPAIR_INFO_END);
14915                 else
14916                         ai_do_objects_repairing_stuff( &Objects[objnum], NULL, REPAIR_INFO_END );
14917         }
14918
14919         //      For all objects that had this ship as a target, wipe it out, forcing find of a new enemy.
14920         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
14921                 other_objp = &Objects[so->objnum];
14922                 Assert(other_objp->instance != -1);
14923
14924                 shipp = &Ships[other_objp->instance];
14925                 Assert(shipp->ai_index != -1);
14926
14927                 ai_info *aip = &Ai_info[shipp->ai_index];
14928
14929                 // MWA 2/11/98
14930                 // code commented out below is taken care of in ai_cleanup_dock_mode when gets called when the
14931                 // support ship starts it's death roll.
14932
14933                 //      If the destroyed ship was on its way to repair the current ship
14934                 if (aip->dock_objnum == objnum) {
14935
14936                         // clean up the flags for any kind of docking mode.  If aip was part of a goal of dock/undock
14937                         // then it will get cleaned up by the goal code.
14938                         ai_do_objects_undocked_stuff( other_objp, NULL );
14939
14940                         if ( aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14941                                 int abort_reason;
14942                                 if ( method == SEF_DEPARTED ) {
14943                                         abort_reason = REPAIR_INFO_ABORT;
14944                                 } else {
14945                                         abort_reason = REPAIR_INFO_KILLED;
14946                                 }
14947                                 ai_do_objects_repairing_stuff( other_objp, NULL, abort_reason );
14948                         }
14949                 }
14950
14951                 if (aip->target_objnum == objnum) {
14952                         set_target_objnum(aip, -1);
14953                         //      If this ship had a dynamic goal of chasing the dead ship, clear the dynamic goal.
14954                         if (aip->resume_goal_time != -1)
14955                                 aip->active_goal = AI_GOAL_NONE;
14956                 }
14957
14958                 if (aip->goal_objnum == objnum) {
14959                         aip->goal_objnum = -1;
14960                         aip->goal_signature = -1;
14961                 }
14962
14963                 if (aip->guard_objnum == objnum) {
14964                         aip->guard_objnum = -1;
14965                         aip->guard_signature = -1;
14966                 }
14967
14968                 if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[Objects[objnum].instance].ai_index].wing)) {
14969                         if (aip->guard_wingnum != aip->wing)
14970                                 ai_set_guard_wing(other_objp, aip->guard_wingnum);
14971                 }
14972
14973                 if (aip->hitter_objnum == objnum)
14974                         aip->hitter_objnum = -1;
14975
14976         }
14977
14978 }
14979
14980 /*
14981 //      Interface function to goals code.
14982 //      Make object *objp fly to point *vp and warp out.
14983 void ai_warp_out(object *objp, vector *vp)
14984 {
14985         ai_info *aip;
14986
14987         aip = &Ai_info[Ships[objp->instance].ai_index];
14988
14989         if (aip->mode != AIM_WARP_OUT) {
14990                 ai_set_mode_warp_out(objp, aip);
14991         }
14992         float   dist;
14993         float   dot;
14994         vector  v2v;
14995         ai_info *aip;
14996
14997         dist = vm_vec_normalized_dir(&v2v, vp, &objp->pos);
14998
14999         if (dist < objp->radius + 5.0f) {
15000
15001                 // Start the warp out effect 
15002                 shipfx_warpout_start(objp);
15003
15004         } else {
15005                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2v);
15006
15007                 aip = &Ai_info[Ships[objp->instance].ai_index];
15008
15009                 if (dist > 500.0f)
15010                         accelerate_ship(aip, 1.0f);
15011                 else
15012                         accelerate_ship(aip, (3*dot + 1.0f)/4.0f);
15013
15014                 turn_towards_point(objp, vp, NULL, 0.0f);
15015         }
15016 }
15017 */
15018
15019
15020 //      Do stuff at start of deathroll.
15021 void ai_deathroll_start(object *ship_obj)
15022 {
15023         ai_info *aip;
15024         ship            *shipp, *other_ship;
15025
15026         shipp = &Ships[ship_obj->instance];
15027         aip = &Ai_info[shipp->ai_index];
15028
15029         // mark object we are docked with so we can do damage and separate during deathroll
15030         // keep dock_objnum_when_dead from being changed if already set (only allow to be set when -1)
15031         if (Ships[ship_obj->instance].dock_objnum_when_dead == -1) {
15032                 Ships[ship_obj->instance].dock_objnum_when_dead = aip->dock_objnum;
15033                 // set other_ship dock_objnum_when_dead, if other_ship exits.
15034                 if (Ships[ship_obj->instance].dock_objnum_when_dead != -1) {
15035                         other_ship = &Ships[Objects[aip->dock_objnum].instance];
15036                         other_ship->dock_objnum_when_dead = shipp->objnum;
15037                 }
15038         }
15039
15040         ai_cleanup_dock_mode(aip, shipp);
15041
15042         aip->mode = AIM_NONE;
15043 }
15044
15045 //      Object *requester_objp tells rearm ship to abort rearm.
15046 //      Returns true if it succeeded, else false.
15047 //      To succeed means you were previously rearming.
15048 int ai_abort_rearm_request(object *requester_objp)
15049 {
15050         ship            *requester_shipp;
15051         ai_info *requester_aip;
15052
15053         Assert(requester_objp->type == OBJ_SHIP);
15054         if(requester_objp->type != OBJ_SHIP){
15055                 return 0;
15056         }
15057         Assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));      
15058         if((requester_objp->instance < 0) || (requester_objp->instance >= MAX_SHIPS)){
15059                 return 0;
15060         }
15061         requester_shipp = &Ships[requester_objp->instance];
15062         Assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));          
15063         if((requester_shipp->ai_index < 0) || (requester_shipp->ai_index >= MAX_AI_INFO)){
15064                 return 0;
15065         }       
15066         requester_aip = &Ai_info[requester_shipp->ai_index];
15067         
15068         if (requester_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)){
15069
15070                 // dock_objnum is always valid once a rearm repair has been requested.  It points to the
15071                 // ship that is coming to repair me.
15072                 if (requester_aip->dock_objnum != -1) {
15073                         object  *repair_objp;
15074                         ai_info *repair_aip;
15075
15076                         repair_objp = &Objects[requester_aip->dock_objnum];
15077                         repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
15078
15079                         //      Make sure signatures match.  This prevents nasty bugs in which an object
15080                         //      that was repairing another is destroyed and is replaced by another ship
15081                         //      before this code comes around.
15082                         if (repair_objp->signature == requester_aip->dock_signature) {
15083
15084                                 Assert( repair_objp->type == OBJ_SHIP );
15085
15086                                 // if support ship is in the process of undocking, don't do anything.
15087                                 if ( repair_aip->submode < AIS_UNDOCK_0 ) {
15088                                         ai_do_objects_repairing_stuff( requester_objp, repair_objp, REPAIR_INFO_ABORT );
15089
15090                                         if ( repair_aip->submode == AIS_DOCK_4 )
15091                                                 repair_aip->submode = AIS_UNDOCK_0;
15092                                         else
15093                                                 repair_aip->submode = AIS_UNDOCK_3;
15094
15095                                         repair_aip->submode_start_time = Missiontime;
15096                                 } else {
15097                                         nprintf(("AI", "Not aborting rearm since already undocking\n"));
15098                                 }
15099                         }
15100                 } else {
15101                         // setting these flags is the safe things to do.  There may not be a corresponding repair
15102                         // ship for this guys since a repair ship may be currently repairing someone else.
15103                         ai_do_objects_repairing_stuff( requester_objp, NULL, REPAIR_INFO_ABORT );
15104
15105                         // try and remove this guy from an arriving support ship.
15106                         mission_remove_scheduled_repair(requester_objp);
15107                 }
15108
15109                 return 1;
15110         } else if ( requester_aip->ai_flags & AIF_REPAIRING ) {
15111                 // a support ship can request to abort when he is told to do something else (like warp out).
15112                 // see if this support ships goal_objnum is valid.  If so, then issue this ai_abort comment
15113                 // for the ship that he is enroute to repair
15114                 if ( requester_aip->goal_objnum != -1 ) {
15115                         int val;
15116
15117                         val = ai_abort_rearm_request( &Objects[requester_aip->goal_objnum] );
15118                         return val;
15119                 }
15120         }
15121
15122         return 0;
15123 }
15124
15125 // function which gets called from ai-issue_rearm_request and from code in missionparse.cpp
15126 // to actually issue the rearm goal (support_obj to rearm requester_obj);
15127 void ai_add_rearm_goal( object *requester_objp, object *support_objp )
15128 {
15129         ship *support_shipp, *requester_shipp;
15130         ai_info *support_aip, *requester_aip;
15131
15132         support_shipp = &Ships[support_objp->instance];
15133         requester_shipp = &Ships[requester_objp->instance];
15134         requester_aip = &Ai_info[requester_shipp->ai_index];
15135
15136         Assert( support_shipp->ai_index != -1 );
15137         support_aip = &Ai_info[support_shipp->ai_index];
15138
15139         // if the requester is a player object, issue the order as the squadmate messaging code does.  Doing so
15140         // ensures that the player get a higher priority!
15141         requester_aip->ai_flags |= AIF_AWAITING_REPAIR; //      Tell that I'm awaiting repair.
15142         if ( requester_objp->flags & OF_PLAYER_SHIP )
15143                 ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_REARM_REPAIR, -1, requester_shipp->ship_name, support_aip );
15144         else
15145                 ai_add_goal_ship_internal( support_aip, AI_GOAL_REARM_REPAIR, requester_shipp->ship_name, -1, -1 );
15146
15147 }
15148
15149 //      Object *requester_objp requests rearming.
15150 //      Returns objnum of ship coming to repair requester on success
15151 //      Success means you found someone to rearm you and you weren't previously rearming.
15152 int ai_issue_rearm_request(object *requester_objp)
15153 {
15154         object  *objp;
15155         ship            *requester_shipp;
15156         ai_info *requester_aip;
15157
15158         Assert(requester_objp->type == OBJ_SHIP);
15159         Assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));
15160         requester_shipp = &Ships[requester_objp->instance];
15161         Assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));
15162         requester_aip = &Ai_info[requester_shipp->ai_index];
15163         
15164         //      Make sure not already awaiting repair.
15165         if (requester_aip->ai_flags & AIF_AWAITING_REPAIR) {
15166                 nprintf(("AI", "Ship %s already awaiting rearm by ship %s.\n", requester_shipp->ship_name, &Ships[Objects[requester_aip->dock_objnum].instance].ship_name));    
15167                 return -1;
15168         }
15169
15170         if ( !is_support_allowed(requester_objp) )
15171                 return -1;
15172
15173         //nprintf(("AI", "Ship %s requesting rearming.\n", requester_shipp->ship_name));
15174         requester_aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);  //      Might request again after this much time.
15175
15176         // call ship_find_repair_ship to get a support ship.  If none is found, then we will warp one in.  This
15177         // function will return the next available ship which can repair requester
15178         objp = ship_find_repair_ship( requester_objp );
15179         ai_do_objects_repairing_stuff( requester_objp, objp, REPAIR_INFO_QUEUE );
15180         if ( objp ) {
15181
15182                 // MWA 5/14/98 -- moved next item into the ai_do_objects_repairing_stuff function so that clients
15183                 // would properly update their hud support view
15184                 //ai_add_rearm_goal( requester_objp, objp );
15185                 return OBJ_INDEX(objp);
15186
15187         } else {
15188                 // call to warp in repair ship!!!!  for now, warp in any number of ships needed.  Should cap it to
15189                 // some reasonable max (or let support ships warp out).  We should assume here that ship_find_repair_ship()
15190                 // would have returned a valid object if there are too many support ships already in the mission
15191                 mission_warp_in_support_ship( requester_objp );
15192
15193                 return -1;
15194         }
15195
15196 }
15197
15198 // make objp rearm and repair goal_objp
15199 void ai_rearm_repair( object *objp, object  *goal_objp, int priority, int docker_index, int dockee_index )
15200 {
15201         ai_info *aip, *goal_aip;
15202
15203         aip = &Ai_info[Ships[objp->instance].ai_index];
15204         aip->goal_objnum = goal_objp-Objects;
15205
15206         // nprintf(("AI", "Ship %s preparing to rearm ship %s.\n", shipp->ship_name, requester_shipp->ship_name));
15207
15208         ai_dock_with_object(objp, goal_objp, priority, AIDO_DOCK, docker_index, dockee_index);
15209         aip->ai_flags |= AIF_REPAIRING;                                         //      Tell that repair guy is busy trying to repair someone.
15210
15211         goal_aip = &Ai_info[Ships[goal_objp->instance].ai_index];
15212         goal_aip->dock_objnum = objp-Objects;           //      Tell which object is coming to repair.
15213         goal_aip->dock_signature = objp->signature;
15214
15215         ai_do_objects_repairing_stuff( goal_objp, objp, REPAIR_INFO_ONWAY );
15216
15217         goal_aip->abort_rearm_timestamp = timestamp(NEXT_REARM_TIMESTAMP*3/2);
15218 }
15219
15220 // Given a dockee object and the index of the dockbay for that object (ie the dockbay index
15221 // into polymodel->dockbays[] for the model associated with the object), return the index
15222 // of a path_num associated with than dockbay (this is an index into polymodel->paths[])
15223 int ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index)
15224 {
15225         if ( dockbay_index < 0 || dockee_objp == NULL ) {
15226                 Int3();         // should never happen
15227                 return -1;
15228         }
15229
15230         if ( dockee_objp->type == OBJ_SHIP ) {
15231                 int                     path_num;
15232                 polymodel       *pm;
15233
15234                 pm = model_get( Ships[dockee_objp->instance].modelnum );
15235
15236                 // sanity checks
15237                 Assert(pm->n_docks > dockbay_index);
15238                 Assert(pm->docking_bays[dockbay_index].num_spline_paths > 0);
15239                 Assert(pm->docking_bays[dockbay_index].splines != NULL);
15240                 if(pm->n_docks <= dockbay_index){
15241                         return -1;
15242                 }
15243                 if(pm->docking_bays[dockbay_index].num_spline_paths <= 0){
15244                         return -1;
15245                 }
15246                 if(pm->docking_bays[dockbay_index].splines == NULL){
15247                         return -1;
15248                 }
15249
15250                 // We only need to return one path for the dockbay, so return the first
15251                 path_num = pm->docking_bays[dockbay_index].splines[0];
15252                 return path_num;
15253         } else {
15254                 return -1;
15255         }
15256 }
15257
15258 //      Actually go ahead and fire the synaptics.
15259 void cheat_fire_synaptic(object *objp, ship *shipp, ai_info *aip)
15260 {
15261         ship_weapon     *swp;
15262         swp = &shipp->weapons;
15263         int     current_bank = swp->current_secondary_bank;
15264
15265         ai_select_secondary_weapon(objp, swp, WIF_SPAWN, 0);
15266         if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
15267                 if (ship_fire_secondary(objp)) {
15268                         nprintf(("AI", "ship %s cheat fired synaptic!\n", shipp->ship_name));
15269                         swp->next_secondary_fire_stamp[current_bank] = timestamp(2500);
15270                 }
15271         }
15272 }
15273
15274 //      For the subspace mission (sm3-09a)
15275 //              for delta wing
15276 //                      if they're sufficiently far into the mission
15277 //                              if they're near one or more enemies
15278 //                                      every so often
15279 //                                              fire a synaptic if they have one.
15280 void maybe_cheat_fire_synaptic(object *objp, ai_info *aip)
15281 {
15282         //      Only do in subspace missions.
15283         if ( The_mission.flags & MISSION_FLAG_SUBSPACE )        {
15284                 ship    *shipp;
15285                 int     num, time;
15286
15287                 shipp = &Ships[objp->instance];
15288
15289                 if (!(strnicmp(shipp->ship_name, NOX("delta"), 5))) {
15290                         num = shipp->ship_name[6] - '1';
15291
15292                         if ((num >= 0) && (num <= 3)) {
15293                                 time = Missiontime >> 16;       //      Convert to seconds.
15294
15295                                 time -= 2*60;   //      Subtract off two minutes.
15296
15297                                 if (time > 0) {
15298                                         int modulus = 17 + num*3;
15299
15300                                         if ((time % modulus) < 2) {
15301                                                 int count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(objp)), &objp->pos, 1500.0f);
15302
15303                                                 if (count > 0) {
15304                                                         cheat_fire_synaptic(objp, shipp, aip);
15305                                                 }
15306                                         }
15307                                 }
15308                         }
15309                 }
15310         }
15311
15312 }
15313