]> icculus.org git repositories - taylor/freespace2.git/blob - src/ship/aicode.cpp
GCC 3.4 fixes.
[taylor/freespace2.git] / src / ship / aicode.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Ship/AiCode.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  * 
15  * AI code that does interesting stuff
16  *
17  * $Log$
18  * Revision 1.9  2004/09/20 01:31:44  theoddone33
19  * GCC 3.4 fixes.
20  *
21  * Revision 1.8  2003/08/03 16:10:30  taylor
22  * cleanup; compile warning fixes
23  *
24  * Revision 1.7  2003/05/25 02:30:43  taylor
25  * Freespace 1 support
26  *
27  * Revision 1.6  2002/07/13 19:47:02  theoddone33
28  * Fix some more warnings
29  *
30  * Change demo building, edit Makefile if you want the demo.
31  *
32  * Revision 1.5  2002/06/17 06:33:10  relnev
33  * ryan's struct patch for gcc 2.95
34  *
35  * Revision 1.4  2002/06/09 04:41:26  relnev
36  * added copyright header
37  *
38  * Revision 1.3  2002/06/01 07:12:34  relnev
39  * a few NDEBUG updates.
40  *
41  * removed a few warnings.
42  *
43  * Revision 1.2  2002/05/03 13:34:33  theoddone33
44  * More stuff compiles
45  *
46  * Revision 1.1.1.1  2002/05/03 03:28:10  root
47  * Initial import.
48  *
49  * 
50  * 107   9/15/99 4:42a Mikek
51  * Make any big ship attacking Colossus, or Colossus attacking any large
52  * ship not use big cruiser movement code.
53  * 
54  * 106   9/15/99 3:28a Jimb
55  * Make all big ships in sm3-08 not do cruiser chase code when attacking
56  * Colossus.  Added so Beast doesn't swerve away from Colossus.
57  * 
58  * 105   9/14/99 4:18p Andsager
59  * hack for mission sm3-08 to abort cruiser_chase as sathanas is about to
60  * begin circling colossus.
61  * 
62  * 104   9/08/99 10:44p Andsager
63  * Make HUGE ships not die when warping out, after warp effect started.
64  * 
65  * 103   9/03/99 11:40p Mikek
66  * Comment out an annoying nprintf().
67  * 
68  * 102   9/01/99 11:26p Dave
69  * Fixed release build warnings.
70  * 
71  * 101   9/01/99 9:12p Mikek
72  * Make it a boatload harder to become a traitor from hitting a large
73  * ship.
74  * 
75  * 100   9/01/99 4:01p Andsager
76  * Make sure BIG|HUGE ships do not respond to shockwaves
77  * 
78  * 99    9/01/99 10:09a Dave
79  * Pirate bob.
80  * 
81  * 98    8/31/99 4:24p Andsager
82  * Reduce collisions when attacking big ships.
83  * 
84  * 97    8/31/99 7:33a Mikek
85  * Improvements in formation flying, less silly behavior, especially when
86  * leader is moving very slowly.
87  * 
88  * 96    8/31/99 5:48a Mikek
89  * Making ships not overshoot so much in formation flying.  Intermediate
90  * checkin.
91  * 
92  * 95    8/30/99 12:03a Mikek
93  * Make guard behavior much less annoying.  Guarders don't get quite so
94  * close and they try to avoid striking the target they are guarding.
95  * 
96  * 94    8/29/99 4:18p Andsager
97  * New "burst" limit for friendly damage.  Also credit more damage done
98  * against large friendly ships.
99  * 
100  * 93    8/28/99 7:29p Dave
101  * Fixed wingmen persona messaging. Make sure locked turrets don't count
102  * towards the # attacking a player.
103  * 
104  * 92    8/26/99 10:46p Andsager
105  * Apply shockwave damage to lethality.
106  * 
107  * 91    8/26/99 8:52p Dave
108  * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
109  * 
110  * 90    8/26/99 5:14p Andsager
111  * 
112  * 89    8/24/99 8:55p Dave
113  * Make sure nondimming pixels work properly in tech menu.
114  * 
115  * 88    8/23/99 6:21p Jefff
116  * added "no traitor" option to missions (and fred)
117  * 
118  * 87    8/20/99 3:36p Andsager
119  * Make sure we don;t miss stealth sweep points.
120  * 
121  * 86    8/16/99 8:21a Andsager
122  * fix link error
123  * 
124  * 85    8/16/99 8:19a Andsager
125  * Add project_point_onto_bbox() to fvi and include in aicode
126  * 
127  * 84    8/15/99 1:30p Dave
128  * Removed some bounding box code because of link errors. Assuming needed
129  * function just needs to get checked in by DaveA.
130  * 
131  * 83    8/15/99 11:59a Andsager
132  * For targing big/huge ships, find nearest distance to bbox, not center.
133  * 
134  * 82    8/13/99 2:20p Andsager
135  * Add speed modification to chances turret will find stealth ship
136  * 
137  * 81    8/13/99 10:49a Andsager
138  * Knossos and HUGE ship warp out.  HUGE ship warp in.  Stealth search
139  * modes dont collide big ships.
140  * 
141  * 80    8/10/99 5:02p Andsager
142  * Fix bug where AI gets stuck in SM_EVADE_WEAPON with no target.
143  * 
144  * 79    8/10/99 11:58a Andsager
145  * Allow turrets to sometimes see stealth.
146  * 
147  * 78    7/31/99 2:57p Dave
148  * Scaled flak aim and jitter by weapon subsystem strength.
149  * 
150  * 77    7/27/99 10:33p Andsager
151  * improve ai for attacking stealth.  reduced jitter in aim.  reduced
152  * error in position when avoiding.  skill level support for attacking
153  * stealth.  Made target error same for team vs. team.
154  * 
155  * 76    7/27/99 10:49a Andsager
156  * Make turret fire rate independent of team for HUGE turrets, and also
157  * for mult team vs. team.
158  * 
159  * 75    7/26/99 12:14p Andsager
160  * Apply cap to how much slower a transport flies with cargo.  Remove
161  * limit on waypoint speed for training.  Enemy ai get stealth exact pos
162  * when stealth fires
163  * 
164  * 74    7/20/99 1:49p Dave
165  * Peter Drake build. Fixed some release build warnings.
166  * 
167  * 73    7/19/99 2:13p Dave
168  * Added some new strings for Heiko.
169  * 
170  * 72    7/19/99 12:02p Andsager
171  * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
172  * only blow up subsystem if its strength is > 0
173  * 
174  * 71    7/15/99 9:20a Andsager
175  * FS2_DEMO initial checkin
176  * 
177  * 70    7/14/99 1:44p Andsager
178  * modify ai_guard for BIG ships to circle around the long axis
179  * 
180  * 69    7/09/99 5:54p Dave
181  * Seperated cruiser types into individual types. Added tons of new
182  * briefing icons. Campaign screen.
183  * 
184  * 68    7/08/99 4:32p Andsager
185  * fix bug with turret-tagged-only
186  * 
187  * 67    7/08/99 12:06p Andsager
188  * Add turret-tagged-only and turret-tagged-clear sexp.
189  * 
190  * 66    7/02/99 3:49p Andsager
191  * Remove debug code.  Allow targeting of stealth from any weapon it
192  * fires.
193  * 
194  * 65    7/02/99 2:01p Andsager
195  * Fix bug where big ship tries to evade dumpfire weapon.
196  * 
197  * 64    7/02/99 10:58a Andsager
198  * Put in big ship - big ship attack mode.  Modify stealth sweep ai.
199  * 
200  * 63    6/30/99 5:53p Dave
201  * Put in new anti-camper code.
202  * 
203  * 62    6/28/99 3:22p Anoop
204  * Fix turret optimization, where ship may not have any valid subsystems
205  * (all blown off).
206  * 
207  * 61    6/25/99 5:56p Andsager
208  * First real pass on stealth ai.
209  * 
210  * 60    6/25/99 3:08p Dave
211  * Multiple flyby sounds.
212  * 
213  * 59    6/25/99 1:12p Danw
214  * DKA:  Make sure big ship has subsystems before trying to target them.
215  * 
216  * 58    6/25/99 10:56a Johnson
217  * Fixed dumb ai code.
218  * 
219  * 57    6/24/99 5:15p Dave
220  * Make sure stride is always at least one for checking turret subsystem
221  * targets.
222  * 
223  * 56    6/24/99 4:59p Dave
224  * Significant speedups to turret firing.
225  * 
226  * 55    6/23/99 5:51p Andsager
227  * Add waypoint-cap-speed.  Checkin stealth ai - inactive.
228  * 
229  * 54    6/16/99 10:21a Dave
230  * Added send-message-list sexpression.
231  * 
232  * 53    6/15/99 9:25a Andsager
233  * Make guard and dynamic chase (who hit you) work with stealth
234  * 
235  * 52    6/14/99 3:21p Andsager
236  * Allow collisions between ship and its debris.  Fix up collision pairs
237  * when large ship is warping out.
238  * 
239  * 51    6/14/99 10:45a Dave
240  * Made beam weapons specify accuracy by skill level in the weapons.tbl
241  * 
242  * 50    6/03/99 8:11a Andsager
243  * 
244  * 49    6/02/99 5:41p Andsager
245  * Reduce range of secondary weapons not fired from turrets in nebula.
246  * Reduce range of beams fired from turrrets in nebula
247  * 
248  * 48    6/02/99 3:23p Andsager
249  * Make AI aware of team visibility.  Allow player targeting with team
250  * visibility info.  Make stealth ships not targetable by AI in nebula
251  * unless tagged.
252  * 
253  * 47    6/02/99 12:52p Andsager
254  * Added team-wide ship visibility.  Implemented for player.
255  * 
256  * 46    6/01/99 8:35p Dave
257  * Finished lockarm weapons. Added proper supercap weapons/damage. Added
258  * awacs-set-radius sexpression.
259  * 
260  * 45    5/28/99 5:35p Andsager
261  * Make ai nebula aware
262  * 
263  * 44    5/24/99 9:55a Dave
264  * Fixed stream weapon ai firing problem. ick.
265  * 
266  * 43    5/20/99 7:00p Dave
267  * Added alternate type names for ships. Changed swarm missile table
268  * entries.
269  * 
270  * 42    5/18/99 1:30p Dave
271  * Added muzzle flash table stuff.
272  * 
273  * 41    5/12/99 2:55p Andsager
274  * Implemented level 2 tag as priority in turret object selection
275  * 
276  * 40    5/12/99 10:42a Andsager
277  * Fix turret bug allowing HUGE turrets to fire at fighters
278  * 
279  * 39    5/06/99 11:46a Andsager
280  * Bug fixes.  Don't get into illegal strafe submode.  Don't choose turret
281  * enemy objnum for beam protected.
282  * 
283  * 38    5/03/99 10:50p Andsager
284  * Make Asteroid_obj_list.  Change get_nearest_turret_objnum() to use
285  * Asteroid_obj_list, Ship_obj_list and Missile_obj_list vs.
286  * obj_used_list.
287  * 
288  * 37    4/29/99 2:29p Dave
289  * Made flak work much better in multiplayer.
290  * 
291  * 36    4/28/99 11:36p Dave
292  * Tweaked up subspace missile strike a bit,
293  * 
294  * 35    4/28/99 3:11p Andsager
295  * Stagger turret weapon fire times.  Make turrets smarter when target is
296  * protected or beam protected.  Add weaopn range to weapon info struct.
297  * 
298  * 34    4/26/99 10:58a Andsager
299  * Add OF_BEAM_PROTECTED flag to keep object from being targeted for zing.
300  * 
301  * 33    4/23/99 12:12p Andsager
302  * Modify wing positions when player is wing leader to prevent some
303  * collisions.
304  * 
305  * 32    4/23/99 12:01p Johnson
306  * Added SIF_HUGE_SHIP
307  * 
308  * 31    4/22/99 11:06p Dave
309  * Final pass at beam weapons. Solidified a lot of stuff. All that remains
310  * now is to tweak and fix bugs as they come up. No new beam weapon
311  * features.
312  * 
313  * 30    4/20/99 6:39p Dave
314  * Almost done with artillery targeting. Added support for downloading
315  * images on the PXO screen.
316  * 
317  * 29    4/20/99 3:40p Andsager
318  * Changes to big ship ai.  Uses bounding box as limit where to fly to
319  * when flying away.
320  * 
321  * 28    4/16/99 5:54p Dave
322  * Support for on/off style "stream" weapons. Real early support for
323  * target-painting lasers.
324  * 
325  * 27    4/02/99 9:55a Dave
326  * Added a few more options in the weapons.tbl for beam weapons. Attempt
327  * at putting "pain" packets into multiplayer.
328  * 
329  * 26    3/28/99 5:58p Dave
330  * Added early demo code. Make objects move. Nice and framerate
331  * independant, but not much else. Don't use yet unless you're me :)
332  * 
333  * 25    3/19/99 9:51a Dave
334  * Checkin to repair massive source safe crash. Also added support for
335  * pof-style nebulae, and some new weapons code.
336  * 
337  * 24    3/08/99 7:03p Dave
338  * First run of new object update system. Looks very promising.
339  * 
340  * 23    3/05/99 3:55p Anoop
341  * Handle some asserts properly.
342  * 
343  * 22    3/04/99 6:09p Dave
344  * Added in sexpressions for firing beams and checking for if a ship is
345  * tagged.
346  * 
347  * 21    3/02/99 9:25p Dave
348  * Added a bunch of model rendering debug code. Started work on fixing
349  * beam weapon wacky firing.
350  * 
351  * 20    2/25/99 2:32p Anoop
352  * (Alan). Fixed ai path following code for AI_BAY_EMERGE. Put in sanity
353  * check so that when the last point on the path is reached, it finishes.
354  * 
355  * 19    2/19/99 2:11p Anoop
356  * Put in some nice handling code for wacky support ship problems (like no
357  * docking paths)
358  * 
359  * 18    2/17/99 2:11p Dave
360  * First full run of squad war. All freespace and tracker side stuff
361  * works.
362  * 
363  * 17    2/11/99 5:22p Andsager
364  * Fixed bugs, generalized block Sexp_variables
365  * 
366  * 16    1/29/99 5:07p Dave
367  * Fixed multiplayer stuff. Put in multiplayer support for rapid fire
368  * missiles.
369  * 
370  * 15    1/29/99 2:25p Andsager
371  * Added turret_swarm_missiles
372  * 
373  * 14    1/27/99 9:56a Dave
374  * Temporary checkin of beam weapons for Dan to make cool sounds.
375  * 
376  * 13    1/24/99 11:37p Dave
377  * First full rev of beam weapons. Very customizable. Removed some bogus
378  * Int3()'s in low level net code.
379  * 
380  * 12    1/21/99 10:44a Dave
381  * More beam weapon stuff. Put in warmdown time.
382  * 
383  * 11    1/12/99 5:45p Dave
384  * Moved weapon pipeline in multiplayer to almost exclusively client side.
385  * Very good results. Bandwidth goes down, playability goes up for crappy
386  * connections. Fixed object update problem for ship subsystems.
387  * 
388  * 10    1/08/99 2:08p Dave
389  * Fixed software rendering for pofview. Super early support for AWACS and
390  * beam weapons.
391  * 
392  * 9     12/23/98 2:53p Andsager
393  * Added ship activation and gas collection subsystems, removed bridge
394  * 
395  * 8     11/12/98 12:13a Dave
396  * Tidied code up for multiplayer test. Put in network support for flak
397  * guns.
398  * 
399  * 7     11/05/98 5:55p Dave
400  * Big pass at reducing #includes
401  * 
402  * 6     10/26/98 9:42a Dave
403  * Early flak gun support.
404  * 
405  * 5     10/23/98 3:51p Dave
406  * Full support for tstrings.tbl and foreign languages. All that remains
407  * is to make it active in Fred.
408  * 
409  * 4     10/20/98 1:39p Andsager
410  * Make so sparks follow animated ship submodels.  Modify
411  * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
412  * submodel_num.  Add submodel_num to multiplayer hit packet.
413  * 
414  * 3     10/13/98 9:29a Dave
415  * Started neatening up freespace.h. Many variables renamed and
416  * reorganized. Added AlphaColors.[h,cpp]
417  * 
418  * 2     10/07/98 10:53a Dave
419  * Initial checkin.
420  * 
421  * 1     10/07/98 10:51a Dave
422  * 
423  * 
424  * $NoKeywords: $
425  */
426
427 // This module contains the actual AI code that does interesting stuff
428 // to objects.   The code in Ai.cpp is just for bookeeping, allocating
429 // ai slots and linking them to ships.
430
431 #include "pstypes.h"
432 #include "fix.h"
433 #include "linklist.h"
434 #include "object.h"
435 #include "physics.h"
436 #include "vecmat.h"
437 #include "ship.h"
438 #include "model.h"
439 #include "2d.h"
440 #include "3d.h"
441 #include "ai.h"
442 #include "floating.h"
443 #include "player.h"
444 #include "freespace.h"
445 #include "weapon.h"
446 #include "missiongoals.h"
447 #include "missionlog.h"
448 #include "timer.h"
449 #include "sound.h"
450 #include "aigoals.h"
451 #include "gamesnd.h"
452 #include "hudmessage.h"
453 #include "missionmessage.h"
454 #include "cmeasure.h"
455 #include "staticrand.h"
456 #include "multimsgs.h"
457 #include "afterburner.h"
458 #include "hudets.h"
459 #include "shipfx.h"
460 #include "shiphit.h"
461 #include "aibig.h"
462 #include "multiutil.h"
463 #include "hud.h"
464 #include "objcollide.h"
465 #include "asteroid.h"
466 #include "hudlock.h"
467 #include "missiontraining.h"
468 #include "gamesequence.h"
469 #include "joy_ff.h"
470 #include "localize.h"
471 #include "flak.h"
472 #include "beam.h"
473 #include "multi.h"
474 #include "swarm.h"
475 #include "multi_team.h"
476 #include "awacs.h"
477 #include "fvi.h"
478
479 #ifndef PLAT_UNIX
480 #pragma optimize("", off)
481 #pragma auto_inline(off)
482 #endif
483
484 #define UNINITIALIZED_VALUE     -99999.9f
485
486 #define INSTRUCTOR_SHIP_NAME NOX("instructor")
487
488 #define AICODE_SMALL_MAGNITUDE  0.001f          // cosider a vector NULL if mag is less than this
489
490 #define NEXT_REARM_TIMESTAMP (60*1000)                  //      Ships will re-request rearm, typically, after this long.
491
492 #define BEAM_NEBULA_RANGE_REDUCE_FACTOR         0.8
493
494 // AIM_CHASE submode defines
495 // SM_STEALTH_FIND
496 #define SM_SF_AHEAD             0
497 #define SM_SF_BEHIND    1
498 #define SM_SF_BAIL              2
499
500 // SM_STEALTH_SWEEP
501 #define SM_SS_SET_GOAL  -1
502 #define SM_SS_BOX0              0
503 #define SM_SS_LR                        1
504 #define SM_SS_UL                        2
505 #define SM_SS_BOX1              3
506 #define SM_SS_UR                        4
507 #define SM_SS_LL                        5
508 #define SM_SS_BOX2              6
509 #define SM_SS_DONE              7
510
511 //XSTR:OFF
512
513 char *Mode_text[MAX_AI_BEHAVIORS] = {
514         "CHASE",
515         "EVADE",
516         "GET_BEHIND",
517         "CHASE_LONG",
518         "SQUIGGLE",
519         "GUARD",
520         "AVOID",
521         "WAYPOINTS",
522         "DOCK",
523         "NONE",
524         "BIGSHIP",
525         "PATH",
526         "BE_REARMED",
527         "SAFETY",
528         "EV_WEAPON",
529         "STRAFE",
530         "PLAY_DEAD",
531         "BAY_EMERGE",
532         "BAY_DEPART",
533         "SENTRYGUN",
534         "WARP_OUT",
535 };
536
537 //      Submode text is only valid for CHASE mode.
538 char *Submode_text[] = {
539 "undefined",
540 "CONT_TURN",
541 "ATTACK   ",
542 "E_SQUIG  ",
543 "E_BRAKE  ",
544 "EVADE    ",
545 "SUP_ATTAK",
546 "AVOID    ",
547 "BEHIND   ",
548 "GET_AWAY ",
549 "E_WEAPON ",
550 "FLY_AWAY ",
551 "ATK_4EVER",
552 "STLTH_FND",
553 "STLTH_SWP",
554 "BIG_APPR",
555 "BIG_CIRC",
556 "BIG_PARL"
557 };
558
559 char *Strafe_submode_text[5] = {
560 "ATTACK",
561 "AVOID",
562 "RETREAT1",
563 "RETREAT2",
564 "POSITION"
565 };
566 //XSTR:ON
567
568 /*
569 //      Used for global ignore of objects.  If an object appears in the Ignore_objects array,
570 //      no one will attack it.
571 #define MAX_IGNORE_OBJECTS      16
572 typedef struct {
573         int     objnum;
574         int     signature;
575 } ignore_object;
576
577 ignore_object   Ignore_objects[MAX_IGNORE_OBJECTS];
578 */
579
580 typedef struct eval_enemy_obj_struct {
581         int                     turret_parent_objnum;                   // parent of turret
582         float                   weapon_travel_dist;                             // max targeting range of turret weapon
583         int                     enemy_team_mask;
584         int                     weapon_system_ok;                                       // is the weapon subsystem of turret ship ok
585         int                     big_only_flag;                                          // turret fires only at big and huge ships
586         vector          *tpos;
587         vector          *tvec;
588         ship_subsys *turret_subsys;
589         int                     current_enemy;
590
591
592         float                   nearest_attacker_dist;                  // nearest ship 
593         int                     nearest_attacker_objnum;
594
595         float                   nearest_homing_bomb_dist;               // nearest homing bomb
596         int                     nearest_homing_bomb_objnum;
597
598         float                   nearest_bomb_dist;                              // nearest non-homing bomb
599         int                     nearest_bomb_objnum;
600
601         float                   nearest_dist;                                           // nearest ship attacking this turret
602         int                     nearest_objnum;
603 }       eval_enemy_obj_struct;
604
605
606 control_info    AI_ci;
607
608 object *Pl_objp;
609 object *En_objp;
610
611 waypoint_list Waypoint_lists[MAX_WAYPOINT_LISTS];
612
613 // How close a turret has to be point at its target before it
614 // can fire.  If the dot of the gun normal and the vector from gun
615 // to target is greater than this, the turret fires.  The smaller
616 // the sloppier the shooting.
617 #define AICODE_TURRET_DUMBFIRE_ANGLE            (0.8f)  
618 #define AICODE_TURRET_HEATSEEK_ANGLE            (0.7f)  
619 #define AICODE_TURRET_MAX_TIME_IN_RANGE (5.0f)
620
621 #define REARM_SOUND_DELAY               (3*F1_0)                //      Amount of time to delay rearm/repair after mode start
622 #define REARM_BREAKOFF_DELAY    (3*F1_0)                //      Amount of time to wait after fully rearmed to breakoff.
623
624 #define MIN_DIST_TO_WAYPOINT_GOAL       5.0f
625 #define MAX_GUARD_DIST                                  250.0f
626 #define BIG_GUARD_RADIUS                                500.0f
627
628 #define MAX_EVADE_TIME                  (15 * 1000)     //      Max time to evade a weapon.
629
630 // defines for repair ship stuff.
631 #define MAX_REPAIR_SPEED                        25.0f
632 #define MAX_UNDOCK_ABORT_SPEED  2.0f
633
634 // defines for EMP effect stuff
635 #define MAX_EMP_INACCURACY              50.0f
636
637 // defines for stealth
638 #define MAX_STEALTH_INACCURACY  50.0f           // at max view dist
639 #define STEALTH_MAX_VIEW_DIST   400             // dist at which 1) stealth no longer visible 2) firing inaccuracy is greatest
640 #define STEALTH_VIEW_CONE_DOT   0.707           // (half angle of 45 degrees)
641
642
643 ai_class        Ai_classes[MAX_AI_CLASSES];
644 int     Ai_firing_enabled = 1;
645 int     Num_ai_classes;
646
647 int     AI_FrameCount = 0;
648 int     Ship_info_inited = 0;
649 int     AI_watch_object = 0; // Debugging, object to spew debug info for.
650 int     Num_waypoint_lists = 0;
651 int     Mission_all_attack = 0;                                 //      !0 means all teams attack all teams.
652
653 char *Skill_level_names(int level, int translate)
654 {
655         char *str = NULL;
656
657         #if NUM_SKILL_LEVELS != 5
658         #error Number of skill levels is wrong!
659         #endif
660
661         if(translate){
662                 switch( level ) {
663                 case 0:
664                         str = XSTR("Very Easy", 469);
665                         break;
666                 case 1:
667                         str = XSTR("Easy", 470);
668                         break;
669                 case 2:
670                         str = XSTR("Medium", 471);
671                         break;
672                 case 3:
673                         str = XSTR("Hard", 472);
674                         break;
675                 case 4:
676                         str = XSTR("Insane", 473);
677                         break;
678                 default:        
679                         Int3();
680                 }
681         } else {
682                 switch( level ) {
683                 case 0:
684                         str = NOX("Very Easy");
685                         break;
686                 case 1:
687                         str = NOX("Easy");
688                         break;
689                 case 2:
690                         str = NOX("Medium");
691                         break;
692                 case 3:
693                         str = NOX("Hard");
694                         break;
695                 case 4:
696                         str = NOX("Insane");
697                         break;
698                 default:        
699                         Int3();
700                 }
701         }
702
703         return str;
704 }
705
706 #define DELAY_TARGET_TIME       (12*1000)               //      time in milliseconds until a ship can target a new enemy after an order.
707
708 //      Make enemy ships turn more slowly at lower skill levels.
709 float   Turn_time_skill_level_scale[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.6f, 1.3f, 1.0f};
710
711 //      Maximum number of simultaneous homing weapons on player based on skill level.
712 int     Max_allowed_player_homers[NUM_SKILL_LEVELS] = {2, 3, 4, 7, 99};
713
714 //      Number of ships that can attack another ship at a given skill level.
715 int     Skill_level_max_attackers[NUM_SKILL_LEVELS] = {2, 3, 4, 5, 99};
716
717 //      How long until next predict position.
718 fix Skill_level_delay[NUM_SKILL_LEVELS] = {2*F1_0, 3*F1_0/2, 4*F1_0/3, F1_0/2, 0};
719
720 //      AI ships link primary weapons if energy levels greater than the following amounts:
721 float   Link_energy_levels_always[NUM_SKILL_LEVELS] = {100.0f, 80.0f, 60.0f, 40.0f, 20.0f};     //      always link
722 float   Link_energy_levels_maybe[NUM_SKILL_LEVELS] = {90.0f, 60.0f, 40.0f, 20.0f, 10.0f};       //      link if hull strength low
723
724 //      Seconds to add to time it takes to get enemy in range.  Only for player's enemies.
725 float   In_range_time[NUM_SKILL_LEVELS] = {2.0f, 1.4f, 0.75f, 0.0f, -1.0f};
726
727 //      No matter what, a random unit vector gets scaled by this amount in firing at an enemy.
728 //      Note that for shorter in-range times, these values get scaled, so a value of 0.5f is meaningful.
729 float   Aiming_error[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.3f, 0.7f, 0.2f};
730
731 //      Chance a countermeasure will be fired based on skill level.
732 float Cmeasure_fire_chance[NUM_SKILL_LEVELS] = {0.2f, 0.3f, 0.5f, 0.9f, 1.1f};  //      Note, this gets scaled by ai_class
733
734 float Shield_manage_delays[NUM_SKILL_LEVELS] = {5.0f, 4.0f, 2.5f, 1.2f, 0.1f};
735
736 // accuracy we feed into the beam weapons based upon skill system
737 // float Beam_accuracy[NUM_SKILL_LEVELS] = {2.0f, 1.5f, 1.0f, 0.7f, 0.4f};
738
739 extern float Ship_fire_delay_scale_hostile[NUM_SKILL_LEVELS];
740 extern float Ship_fire_delay_scale_friendly[NUM_SKILL_LEVELS];
741
742 pnode           Path_points[MAX_PATH_POINTS];
743 pnode           *Ppfp;                  //      Free pointer in path points.
744
745 float   AI_frametime;
746
747 char *Ai_class_names[MAX_AI_CLASSES];
748
749 // global for rearm status for teams
750 int Ai_friendly_rearm_timestamp, Ai_hostile_rearm_timestamp, Ai_neutral_rearm_timestamp, Ai_traitor_rearm_timestamp, Ai_unknown_rearm_timestamp;
751
752 // globals for dealing with when to fire huge secondary weapons
753 #define MAX_HUGE_SECONDARY_INFO 10
754
755 typedef struct {
756         int team;
757         int weapon_index;
758         int max_fire_count;
759         char    *shipname;
760 } huge_fire_info;
761
762 huge_fire_info Ai_huge_fire_info[MAX_HUGE_SECONDARY_INFO];
763
764 int Ai_last_arrive_path;        // index of ship_bay path used by last arrival from a fighter bay
765
766 // forward declarations
767 int     ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index);
768 void    create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count=1);
769 void    copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt=-1);
770
771 // ai_set_rearm_status takes a team (friendly, hostile, neutral) and a time.  This function
772 // sets the timestamp used to tell is it is a good time for this team to rearm.  Once the timestamp
773 // is no longer valid, then rearming is not a "good time"
774 // not safe.  Called from sexpression code.
775 void ai_set_rearm_status( int team, int time )
776 {
777         Assert( time >= 0 );
778
779         switch (team) {
780         case TEAM_FRIENDLY:
781                 Ai_friendly_rearm_timestamp = timestamp( time * 1000 );
782                 break;
783         case TEAM_HOSTILE:
784                 Ai_hostile_rearm_timestamp = timestamp( time * 1000 );
785                 break;
786         case TEAM_NEUTRAL:
787                 Ai_neutral_rearm_timestamp = timestamp( time * 1000 );
788                 break;
789         case TEAM_TRAITOR:
790                 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
791                 break;
792         case TEAM_UNKNOWN:
793                 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
794                 break;
795         default:
796                 Int3();
797                 break;
798         }
799 }
800
801 // int ai_good_time_to_rearm returns true(1) or false(0) if it is "safe" for the given
802 // object to rearm.  "safe" is currently defined by the mission designer using the good/bad
803 // time to rearm sexpressions.  This status is currently team based.  This function could
804 // be easily expended to further the definition of "safe"
805 int ai_good_time_to_rearm( object *objp )
806 {
807         int team, status;
808
809         Assert(objp->type == OBJ_SHIP);
810         team = Ships[objp->instance].team;
811         status = 0;
812
813         switch(team) {
814         case TEAM_FRIENDLY:
815                 status = timestamp_valid(Ai_friendly_rearm_timestamp);
816                 break;
817         case TEAM_HOSTILE:
818                 status = timestamp_valid(Ai_hostile_rearm_timestamp);
819                 break;
820         case TEAM_NEUTRAL:
821                 status = timestamp_valid(Ai_neutral_rearm_timestamp);
822                 break;
823         case TEAM_TRAITOR:
824                 status = timestamp_valid(Ai_traitor_rearm_timestamp);
825                 break;
826         case TEAM_UNKNOWN:
827                 status = timestamp_valid(Ai_unknown_rearm_timestamp);
828                 break;
829         default:
830                 Int3();
831                 break;
832         }
833
834         return status;
835 }
836
837 // functions to deal with letting the ai know about good times to fire powerful secondary
838 // weapons.
839
840 // this function is entry point from sexpression code to set internal data for use by ai code.
841 void ai_good_secondary_time( int team, int weapon_index, int max_fire_count, char *shipname )
842 {
843         int i, index;
844
845         // find an open slot to put this data
846         for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
847                 if ( Ai_huge_fire_info[i].weapon_index == -1 )
848                         break;
849         }
850
851         Assert( i < MAX_HUGE_SECONDARY_INFO );                  // we've run out of room
852
853         Ai_huge_fire_info[i].weapon_index = weapon_index;
854         Ai_huge_fire_info[i].team = team;
855         Ai_huge_fire_info[i].max_fire_count = max_fire_count;
856
857         Ai_huge_fire_info[i].shipname = ai_get_goal_ship_name( shipname, &index );
858 }
859
860 // function called internally to the ai code to tell whether or not weapon_num can be fired
861 // from firer_objp at target_objp.  This function will resolve the team for the firer.
862 // returns:
863 //              -1  -- when conditions don't allow firer to fire weapon_num on target_objp
864 //              >=0 -- when conditions allow firer to fire.  Return value is max number of weapon_nums
865 //           which can be fired on target_objp
866 int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
867 {
868         int i, firer_team, target_signature;
869         ship *firer_ship;
870         huge_fire_info *hfi = NULL;
871
872         Assert( firer_objp->type == OBJ_SHIP );
873         firer_ship = &Ships[firer_objp->instance];
874         firer_team = firer_ship->team;
875
876         // get target object's signature and try to find it in the list.
877         target_signature = target_objp->signature;
878         for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
879                 int ship_index, signature;
880
881                 hfi = &Ai_huge_fire_info[i];
882                 if ( hfi->weapon_index == -1 )
883                         continue;
884
885                 ship_index = ship_name_lookup( hfi->shipname );
886                 if ( ship_index == -1 )
887                         continue;
888
889                 signature = Objects[Ships[ship_index].objnum].signature;
890
891                 // sigatures, weapon_index, and team must match
892                 if ( (signature == target_signature) && (hfi->weapon_index == weapon_num) && (hfi->team == firer_team) )
893                         break;
894         }
895
896         // return -1 if not found
897         if ( i == MAX_HUGE_SECONDARY_INFO )
898                 return -1;
899
900         // otherwise, we can return the max number of weapons we can fire against target_objps
901
902         return hfi->max_fire_count;
903 }
904
905 // function to clear out secondary firing infomration between levels
906 void ai_init_secondary_info()
907 {
908         int i;
909
910         // clear out the data for dealing with when ai ships can fire huge secondary weapons
911         for (i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
912                 Ai_huge_fire_info[i].weapon_index = -1;
913                 Ai_huge_fire_info[i].team = -1;
914                 Ai_huge_fire_info[i].max_fire_count = -1;
915                 Ai_huge_fire_info[i].shipname = NULL;
916         }
917 }
918
919
920 //      Garbage collect the Path_points buffer.
921 //      Scans all objects, looking for used Path_points records.
922 //      Compresses Path_points buffer, updating aip->path_start and aip->path_cur indices.
923 //      Updates Ppfp to point to first free record.
924 //      This function is fairly fast.  Its worst-case running time is proportional to
925 //      3*MAX_PATH_POINTS + MAX_OBJECTS
926 //      Things to do to optimize this function:
927 //              1. if (t != 0) xlt++; can be replaced by xlt += t; assuming t can only be 0 or 1.
928 //              2. When pp_xlate is getting stuffed the first time, note highest index and use that 
929 //                      instead of MAX_PATH_POINTS in following two for loops.
930 void garbage_collect_path_points()
931 {
932         int     i;
933         int     pp_xlate[MAX_PATH_POINTS];
934         object  *A;
935         ship_obj        *so;
936
937         //      Scan all objects and create Path_points xlate table.
938         for (i=0; i<MAX_PATH_POINTS; i++)
939                 pp_xlate[i] = 0;
940
941         //      in pp_xlate, mark all used Path_point records
942         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
943                 A = &Objects[so->objnum];
944                 ship    *shipp = &Ships[A->instance];
945                 if (shipp->ai_index != -1) {
946                         ai_info *aip = &Ai_info[shipp->ai_index];
947
948                         if ((aip->path_length > 0) && (aip->path_start > -1)) {
949
950                                 for (int i=aip->path_start; i<aip->path_start + aip->path_length; i++) {
951                                         Assert(pp_xlate[i] == 0);       //      If this is not 0, then two paths use this point!
952                                         pp_xlate[i] = 1;
953                                 }
954                         }
955                 }
956         }
957
958         //      Now, stuff xlate index in pp_xlate.  This is the number to translate any path_start
959         //      or path_cur index to.
960         int     xlt = 0;
961         for (i=0; i<MAX_PATH_POINTS; i++) {
962                 int     t = pp_xlate[i];
963
964                 pp_xlate[i] = xlt;
965                 if (t != 0)
966                         xlt++;
967         }
968         
969         //      Update global Path_points free pointer.
970         Ppfp = &Path_points[xlt];
971
972         //      Now, using pp_xlate, fixup all aip->path_cur and aip->path_start indices
973         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
974                 A = &Objects[so->objnum];
975                 ship    *shipp = &Ships[A->instance];
976                 if (shipp->ai_index != -1) {
977                         ai_info *aip = &Ai_info[shipp->ai_index];
978
979                         if ((aip->path_length > 0) && (aip->path_start > -1)) {
980                                 Assert(aip->path_start < MAX_PATH_POINTS);
981                                 aip->path_start = pp_xlate[aip->path_start];
982
983                                 Assert((aip->path_cur >= 0) && (aip->path_cur < MAX_PATH_POINTS));
984                                 aip->path_cur = pp_xlate[aip->path_cur];
985                         }
986                 }
987         }
988
989         //      Now, compress the buffer.
990         for (i=0; i<MAX_PATH_POINTS; i++)
991                 if (i != pp_xlate[i])
992                         Path_points[pp_xlate[i]] = Path_points[i];
993
994 }
995
996 //      Hash two values together, return result.
997 //      Hash function: curval shifted right circular by one, newval xored in.
998 int hash(unsigned int curval, int newval)
999 {
1000         int     addval = curval & 1;
1001
1002         curval >>= 1;
1003         if (addval)
1004                 curval |= 0x80000000;
1005         curval ^= newval;
1006
1007         return curval;
1008 }
1009
1010 //      Hash some information in an object together.
1011 //      On 2/20/97, the information is position and orientation.
1012 int create_object_hash(object *objp)
1013 {
1014         int     *ip;
1015         unsigned int    hashval = 0;
1016         int     i;
1017
1018         ip = (int *) &objp->orient;
1019
1020         for (i=0; i<9; i++) {
1021                 hashval = hash(hashval, *ip);
1022                 ip++;
1023         }
1024
1025         ip = (int *) &objp->pos;
1026
1027         for (i=0; i<3; i++) {
1028                 hashval = hash(hashval, *ip);
1029                 ip++;
1030         }
1031
1032         return hashval;
1033 }
1034
1035 //      Stuff a list of NUM_SKILL_LEVELS floats at *plist.
1036 void parse_float_list(float *plist)
1037 {
1038         int     i;
1039
1040         for (i=0; i<NUM_SKILL_LEVELS; i++) {
1041                 stuff_float(&plist[i]);
1042         }
1043 }
1044
1045 void parse_ai_class()
1046 {
1047         ai_class        *aicp = &Ai_classes[Num_ai_classes];
1048
1049         required_string("$Name:");
1050         stuff_string(aicp->name, F_NAME, NULL);
1051
1052         Ai_class_names[Num_ai_classes] = aicp->name;
1053
1054         required_string("$accuracy:");
1055         parse_float_list(aicp->ai_accuracy);
1056
1057         required_string("$evasion:");
1058         parse_float_list(aicp->ai_evasion);
1059
1060         required_string("$courage:");
1061         parse_float_list(aicp->ai_courage);
1062
1063         required_string("$patience:");
1064         parse_float_list(aicp->ai_patience);
1065 }
1066
1067 void parse_aitbl()
1068 {
1069         // open localization
1070         lcl_ext_open();
1071
1072         read_file_text("ai.tbl");
1073
1074         reset_parse();
1075
1076         Num_ai_classes = 0;
1077
1078         required_string("#AI Classes");
1079
1080         while (required_string_either("#End", "$Name:")) {
1081                 Assert( Num_ai_classes < MAX_AI_CLASSES);
1082
1083                 parse_ai_class();
1084
1085                 Num_ai_classes++;
1086         }
1087
1088         // close localization
1089         lcl_ext_close();
1090 }
1091
1092 LOCAL int ai_inited = 0;
1093
1094 //========================= BOOK-KEEPING FUNCTIONS =======================
1095
1096 // Called once at game start-up
1097 void ai_init()
1098 {
1099         if ( !ai_inited )       {
1100                 // Do the first time initialization stuff here
1101                 int     rval;
1102
1103                 if ((rval = setjmp(parse_abort)) != 0) {
1104                         Error(LOCATION, "Error parsing 'ai.tbl'\r\nError code = %i.\r\n", rval);
1105                 } else {                        
1106                         parse_aitbl();                  
1107                 }
1108
1109                 ai_inited = 1;
1110         }
1111
1112         init_semirand();
1113         
1114         ai_level_init();
1115 }
1116
1117 // this inits the ai.  You should be able to call this between
1118 // levels to reset everything.
1119 void ai_level_init()
1120 {
1121         int i;
1122  
1123         // Do the stuff to reset all ai stuff here
1124         for (i=0; i<MAX_AI_INFO ; i++) {
1125                 Ai_info[i].shipnum = -1;
1126         }
1127         Ai_goal_signature = 0;
1128         Ai_friendly_rearm_timestamp = timestamp(-1);
1129         Ai_hostile_rearm_timestamp = timestamp(-1);
1130         Ai_neutral_rearm_timestamp = timestamp(-1);
1131         Ai_traitor_rearm_timestamp = timestamp(-1);
1132
1133         // clear out the stuff needed for AI firing powerful secondary weapons
1134         ai_init_secondary_info();
1135
1136         Ai_last_arrive_path=0;
1137 }
1138
1139 // BEGIN STEALTH
1140 // -----------------------------------------------------------------------------
1141 // Check if object is a stealth ship
1142 int is_object_stealth_ship(object* objp)
1143 {
1144         if (objp->type == OBJ_SHIP) {
1145                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_STEALTH) {
1146                         return 1;
1147                 }
1148         }
1149
1150         // not stealth ship
1151         return 0;
1152 }
1153
1154 // -----------------------------------------------------------------------------
1155 // Init necessary ai info for new stealth target
1156 void init_ai_stealth_info(ai_info *aip, object *stealth_objp)
1157 {
1158         Assert(is_object_stealth_ship(stealth_objp));
1159
1160         // set necessary ai info for new stealth target
1161         aip->stealth_last_pos = stealth_objp->pos;
1162         aip->stealth_velocity = stealth_objp->phys_info.vel;
1163         aip->stealth_last_visible_stamp = timestamp();
1164 }
1165
1166 // -----------------------------------------------------------------------------
1167 // Check whether Pl_objp can see a stealth ship object
1168 #define STEALTH_INVISIBLE                       0
1169 #define STEALTH_VISIBLE                         1
1170 #define STEALTH_FULLY_TARGETABLE        2
1171
1172 float get_skill_stealth_dist_scaler()
1173 {
1174         // return dist scaler based on skill level
1175         switch (Game_skill_level) {
1176         case 0: // very easy
1177                 return 0.65f;
1178
1179         case 1: // easy
1180                 return 0.9f;
1181
1182         case 2: // medium
1183                 return 1.0f;
1184
1185         case 3: // hard
1186                 return 1.1f;
1187
1188         case 4: // insane
1189                 return 1.3f;
1190
1191         default:
1192                 Int3();
1193         }
1194
1195         return 1.0f;
1196 }
1197
1198 float get_skill_stealth_dot_scaler()
1199 {
1200         // return multiplier on dot based on skill level
1201         switch (Game_skill_level) {
1202         case 0: // very easy
1203                 return 1.3f;
1204
1205         case 1: // easy
1206                 return 1.1f;
1207
1208         case 2: // medium
1209                 return 1.0f;
1210
1211         case 3: // hard
1212                 return 0.9f;
1213
1214         case 4: // insane
1215                 return 0.7f;
1216
1217         default:
1218                 Int3();
1219         }
1220
1221         return 1.0f;
1222 }
1223
1224 int ai_is_stealth_visible(object *viewer_objp, object *stealth_objp)
1225 {
1226         ship *shipp;
1227         vector vec_to_stealth;
1228         float dot_to_stealth, dist_to_stealth, max_stealth_dist;
1229
1230         Assert(stealth_objp->type == OBJ_SHIP);
1231         shipp = &Ships[stealth_objp->instance];
1232         Assert(viewer_objp->type == OBJ_SHIP);
1233
1234         // check if stealth ship
1235         Assert(Ship_info[shipp->ship_info_index].flags & SIF_STEALTH);
1236
1237         // check if in neb and below awac level for visible
1238         if ( !ship_is_visible_by_team(stealth_objp->instance, Ships[viewer_objp->instance].team) ) {
1239                 vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &viewer_objp->pos);
1240                 dist_to_stealth = vm_vec_mag_quick(&vec_to_stealth);
1241                 dot_to_stealth = vm_vec_dotprod(&viewer_objp->orient.v.fvec, &vec_to_stealth) / dist_to_stealth;
1242
1243                 // get max dist at which stealth is visible
1244                 max_stealth_dist = get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST;
1245
1246                 // now check if within view frustrum
1247                 float needed_dot_to_stealth;
1248                 if (dist_to_stealth < 100) {
1249                         needed_dot_to_stealth = 0.0f;
1250                 } else {
1251                         needed_dot_to_stealth = get_skill_stealth_dot_scaler() * float(STEALTH_VIEW_CONE_DOT) * (dist_to_stealth / max_stealth_dist);
1252                 }
1253                 if (dot_to_stealth > needed_dot_to_stealth) {
1254                         if (dist_to_stealth < max_stealth_dist) {
1255                                 return STEALTH_VISIBLE;
1256                         }
1257                 }
1258
1259                 // not within frustrum
1260                 return STEALTH_INVISIBLE;
1261         }
1262
1263         // visible by awacs level
1264         return STEALTH_FULLY_TARGETABLE;
1265 }
1266
1267 // END STEALTH
1268
1269 //      Compute dot product of direction vector and forward vector.
1270 //      Direction vector is vector from one object to other object.
1271 //      Forward vector is the forward vector of the ship.
1272 //      If from_dot == NULL, don't fill it in.
1273 float compute_dots(object *objp, object *other_objp, float *to_dot, float *from_dot)
1274 {
1275         vector  v2o;
1276         float           dist;
1277
1278         dist = vm_vec_normalized_dir(&v2o, &other_objp->pos, &objp->pos);
1279
1280         *to_dot = vm_vec_dot(&objp->orient.v.fvec, &v2o);
1281
1282         if (from_dot != NULL)
1283                 *from_dot = - vm_vec_dot(&other_objp->orient.v.fvec, &v2o);
1284
1285         return dist;
1286 }
1287
1288 // -----------------------------------------------------------------------------
1289 // update estimated stealth info
1290 // this is a "cheat" update
1291 // error increases with time not seen, true distance away, dot to enemey
1292 // this is done only if we can not see the stealth target
1293 // need to infer its position either by weapon fire pos or last know pos
1294 void update_ai_stealth_info_with_error(ai_info *aip/*, int no_error*/)
1295 {
1296         object *ship;
1297         object *stealth_objp;
1298         /*
1299         float error_time_mult, error_dist_mult, error_dot_mult, error_mult;
1300         float pos_error, vel_error;
1301         vector error_vec, vec_to_stealth;
1302         float dist_to_stealth, dot_to_stealth;
1303         float delta_time, delta_capped;
1304         */
1305
1306         // make sure I am targeting a stealth ship
1307         Assert( is_object_stealth_ship(&Objects[aip->target_objnum]) );
1308         stealth_objp = &Objects[aip->target_objnum];
1309
1310         // my_ship
1311         ship = &Objects[Ships[aip->shipnum].objnum];
1312
1313         // if update is due to weapon fire, get exact stealth position
1314 //      if (no_error) {
1315         aip->stealth_last_pos = stealth_objp->pos;
1316         aip->stealth_velocity = stealth_objp->phys_info.vel;
1317         aip->stealth_last_visible_stamp = timestamp();
1318 //              return;
1319 //      }
1320 /*
1321         // get time since last seen
1322         delta_time = 0.001f * (timestamp() - aip->stealth_last_visible_stamp);
1323
1324         // we don't want our "cheat" guess to more off than what we would get from extrapolating from last visible
1325         // only update if stealth info is "old"
1326         if ( (delta_time) < 0.5 ) {
1327                 return;
1328         }
1329
1330         // find vec_to_stealth and dist
1331         vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &ship->pos);
1332         dist_to_stealth = vm_vec_normalize_quick(&vec_to_stealth);
1333         dot_to_stealth = vm_vec_dotprod(&vec_to_stealth, &ship->orient.v.fvec);
1334
1335         // put cap on time
1336         delta_capped = delta_time;
1337         if (delta_time > 5.0) {
1338                 delta_capped = 5.0f;
1339         }
1340
1341         // erorr_time_mult (for 0-5) -> (1-6)
1342         error_time_mult = (1.0f + delta_capped);
1343
1344         // error_dot_mult (-1 to 1) -> (1-3)
1345         error_dot_mult = (2 - dot_to_stealth);
1346
1347         // error_dist_mult (0-1000+) -> (1-4)
1348         error_dist_mult = dist_to_stealth * 4.0f * 0.001f;
1349         if (error_dist_mult < 1) {
1350                 error_dist_mult = 1.0f;
1351         } else if (error_dist_mult > 4) {
1352                 error_dist_mult = 4.0f;
1353         }
1354
1355         // multiply error out
1356         error_mult = error_time_mult * error_dot_mult * error_dist_mult;
1357
1358         float base_pos_error = 10;
1359         float base_vel_error = 2;
1360
1361         // find the position and velocity error magnitude;
1362         pos_error = base_pos_error * error_mult;
1363         vel_error = base_vel_error * error_mult;
1364
1365         // get an error that changes slowly over time
1366         static_randvec( ((int)aip ^ (Missiontime >> 18)) & 7, &error_vec);
1367         vm_vec_zero(&error_vec);
1368
1369         // update pos and vel with error
1370         vm_vec_scale_add(&aip->stealth_velocity, &stealth_objp->phys_info.vel, &error_vec, vel_error);
1371
1372         // revise last "known" position to arrive at last pos with given error
1373         vm_vec_scale_add(&aip->stealth_last_pos, &stealth_objp->pos, &error_vec, pos_error);
1374         vm_vec_scale_add2(&aip->stealth_last_pos, &aip->stealth_velocity, -(0.001f * delta_time));
1375         */
1376 }
1377
1378 //      Update danger_weapon_objnum and signature in ai_info to say this weapon is to be avoided.
1379 void ai_update_danger_weapon(int attacked_objnum, int weapon_objnum)
1380 {
1381         object  *objp, *weapon_objp;
1382         ai_info *aip;
1383         float           old_dist, new_dist;
1384         float           old_dot, new_dot;
1385         object  *old_weapon_objp = NULL;
1386
1387         if ((attacked_objnum == -1) || (weapon_objnum == -1)) {
1388                 return;
1389         }
1390
1391         objp = &Objects[attacked_objnum];
1392
1393         // AL 2-24-98: If this isn't a ship, we don't need to worry about updating weapon_objnum (ie it would be
1394         //                                      an asteroid or bomb).
1395         if ( objp->type != OBJ_SHIP ) {
1396                 return;
1397         }
1398
1399         weapon_objp = &Objects[weapon_objnum];
1400
1401         aip = &Ai_info[Ships[objp->instance].ai_index];
1402
1403         // if my taraget is a stealth ship and is not visible
1404         if (aip->target_objnum >= 0) {
1405                 if ( is_object_stealth_ship(&Objects[aip->target_objnum]) ) {
1406                         if ( ai_is_stealth_visible(objp, &Objects[aip->target_objnum]) == STEALTH_INVISIBLE ) {
1407                                 // and the weapon is coming from that stealth ship
1408                                 if (weapon_objp->parent == aip->target_objnum) {
1409                                         // update my position estimate for stealth ship
1410                                         update_ai_stealth_info_with_error(aip/*, 1*/);
1411                                 }
1412                         }
1413                 }
1414         }
1415
1416         if (aip->danger_weapon_objnum != -1) {
1417                 old_weapon_objp = &Objects[aip->danger_weapon_objnum];
1418                 if ((old_weapon_objp->type == OBJ_WEAPON) && (old_weapon_objp->signature == aip->danger_weapon_signature)) {
1419                         ;
1420                 } else {
1421                         aip->danger_weapon_objnum = -1;
1422                 }
1423         }
1424
1425         new_dist = compute_dots(weapon_objp, objp, &new_dot, NULL);
1426
1427         if (aip->danger_weapon_objnum == -1) {
1428                 if (new_dist < 1500.0f) {
1429                         if (new_dot > 0.5f) {
1430                                 aip->danger_weapon_objnum = weapon_objnum;
1431                                 aip->danger_weapon_signature = weapon_objp->signature;
1432                         }
1433                 }
1434         } else {
1435                 Assert(old_weapon_objp != NULL);
1436                 old_dist = compute_dots(old_weapon_objp, objp, &old_dot, NULL);
1437         
1438                 if (old_dot < 0.5f) {
1439                         aip->danger_weapon_objnum = -1;
1440                         old_dist = 9999.9f;
1441                 }
1442
1443                 if ((new_dot > 0.5f) && (new_dot > old_dot-0.01f)) {
1444                         if (new_dist < old_dist) {
1445                                 aip->danger_weapon_objnum = weapon_objnum;
1446                                 aip->danger_weapon_signature = weapon_objp->signature;
1447                         }
1448                 }
1449         }
1450 }
1451
1452 //      If rvec != NULL, use it to match bank by calling vm_matrix_interpolate.
1453 //      (rvec defaults to NULL)
1454 void ai_turn_towards_vector(vector *dest, object *objp, 
1455                                                                          float frametime, float turn_time, vector *slide_vec, vector *rel_pos, float bank_override, int flags, vector *rvec)
1456 {
1457         //matrix        goal_orient;
1458         matrix  curr_orient;
1459         vector  vel_in, vel_out, desired_fvec, src;
1460         float           delta_time;
1461         physics_info    *pip;
1462         vector  vel_limit, acc_limit;
1463         float           delta_bank;
1464
1465         //      Don't allow a ship to turn if it has no engine strength.
1466         // AL 3-12-98: objp may not always be a ship!
1467         if ( objp->type == OBJ_SHIP ) {
1468                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f)
1469                         return;
1470         }
1471                         
1472         //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));
1473         pip = &objp->phys_info;
1474
1475         vel_in = pip->rotvel;
1476         curr_orient = objp->orient;
1477         delta_time = flFrametime;
1478
1479         Assert(turn_time > 0.0f);
1480         
1481         //      Scale turn_time based on skill level and team.
1482         if (!(flags & AITTV_FAST)){
1483                 if (objp->type == OBJ_SHIP){
1484                         if (Ships[objp->instance].team != Ships[Player_obj->instance].team){
1485                                 turn_time *= Turn_time_skill_level_scale[Game_skill_level];
1486                         }
1487                 }
1488         }
1489
1490         //      Set max turn rate.
1491         vel_limit.xyz.x = 2*PI/turn_time;
1492         vel_limit.xyz.y = 2*PI/turn_time;
1493         vel_limit.xyz.z = 2*PI/turn_time;
1494
1495         //      Set rate at which ship can accelerate to its rotational velocity.
1496         //      For now, weapons just go much faster.
1497         acc_limit = vel_limit;
1498         if (objp->type == OBJ_WEAPON)
1499                 vm_vec_scale(&acc_limit, 8.0f);
1500
1501         src = objp->pos;
1502
1503         if (rel_pos != NULL) {
1504                 vector  gun_point;
1505                 vm_vec_unrotate(&gun_point, rel_pos, &objp->orient);
1506                 vm_vec_add2(&src, &gun_point);
1507         }
1508
1509         vm_vec_normalized_dir(&desired_fvec, dest, &src);
1510
1511         //      Since ship isn't necessarily moving in the direction it's pointing, sometimes it's better
1512         //      to be moving towards goal rather than just pointing.  So, if slide_vec is !NULL, try to
1513         //      make ship move towards goal, not point at goal.
1514         if (slide_vec != NULL) {
1515                 vm_vec_add2(&desired_fvec, slide_vec);
1516                 vm_vec_normalize(&desired_fvec);
1517         }
1518
1519         //      Should be more general case here.  Currently, anything that is not a weapon will bank when it turns.
1520         if (objp->type == OBJ_WEAPON)
1521                 delta_bank = 0.0f;
1522         else if ((bank_override) && (Ships[objp->instance].team & opposing_team_mask(Player_ship->team))) {     //      Theoretically, this will only happen for Shivans.
1523                 delta_bank = bank_override;
1524                 //nprintf(("AI", "%i: %7.3f\n", Framecount, bank_override));
1525         } else {
1526                 delta_bank = vm_vec_dot(&curr_orient.v.rvec, &objp->last_orient.v.rvec);
1527                 delta_bank = 100.0f * (1.0f - delta_bank);
1528                 if (vm_vec_dot(&objp->last_orient.v.fvec, &objp->orient.v.rvec) < 0.0f)
1529                         delta_bank = -delta_bank;
1530
1531                 //nprintf(("AI", "%s: Frame %i: delta bank = %7.3f\n", Ships[objp->instance].ship_name, Framecount, delta_bank));
1532         }
1533
1534         //      Dave Andsager: The non-indented lines here are debug code to help you track down the problem in the physics
1535         //      that is causing ships to inexplicably rotate very far.  If you hit the Int3(), set the next statement to be
1536         //      the one marked "HERE".  (Do this clicking the cursor there, then right clicking.  Choose the right option.)
1537         //      This will allow you to rerun vm_forward_interpolate() with the values that caused the error.
1538         //      Note, you'll need to enable the Int3() about ten lines below.
1539 #ifndef NDEBUG
1540 vector tvec = objp->orient.v.fvec;
1541 vector  vel_in_copy;
1542 matrix  objp_orient_copy;
1543
1544 vel_in_copy = vel_in;
1545 objp_orient_copy = objp->orient;
1546
1547 vel_in = vel_in_copy;   //      HERE
1548 objp->orient = objp_orient_copy;
1549 #endif
1550         if (rvec != NULL) {
1551                 matrix  out_orient, goal_orient;
1552
1553                 vm_vector_2_matrix(&goal_orient, &desired_fvec, NULL, rvec);
1554                 vm_matrix_interpolate(&goal_orient, &curr_orient, &vel_in, delta_time, &out_orient, &vel_out, &vel_limit, &acc_limit);
1555                 objp->orient = out_orient;
1556         } else {
1557                 vm_forward_interpolate(&desired_fvec, &curr_orient, &vel_in, delta_time, delta_bank, &objp->orient, &vel_out, &vel_limit, &acc_limit);
1558         }
1559 #ifndef NDEBUG
1560 if (!((objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE))) {
1561         if (delta_time < 0.25f && vm_vec_dot(&objp->orient.v.fvec, &tvec) < 0.1f)
1562                 Int3(); //      Get Andsager.  A ship has turned too far in one frame.
1563 }
1564 #endif
1565
1566         pip->rotvel = vel_out;
1567 }
1568
1569 void init_ship_info()
1570 {
1571         int     i;
1572
1573         if (Ship_info_inited)
1574                 return;
1575
1576         for (i=0; i<MAX_SHIP_TYPES; i++) {
1577                 Ship_info[i].min_speed = - Ship_info[i].max_rear_vel;
1578                 Ship_info[i].max_accel = Ship_info[i].max_vel.xyz.z;
1579         }
1580
1581         Ship_info_inited = 1;
1582
1583 }
1584
1585 //      Set aip->target_objnum to objnum
1586 //      Update aip->previous_target_objnum.
1587 //      If new target (objnum) is different than old target, reset target_time.
1588 int set_target_objnum(ai_info *aip, int objnum)
1589 {
1590 /*
1591         char    old_name[32], new_name[32];
1592
1593         if (!timestamp_elapsed(aip->ok_to_target_timestamp))
1594                 return aip->target_objnum;
1595
1596         if (Player_ship && (Ships[aip->shipnum].team == Player_ship->team)) {
1597                 if (aip->target_objnum == -1)
1598                         strcpy(old_name, "none");
1599                 else
1600                         strcpy(old_name, Ships[Objects[aip->target_objnum].instance].ship_name);
1601
1602                 if (objnum == -1)
1603                         strcpy(new_name, "none");
1604                 else
1605                         strcpy(new_name, Ships[Objects[objnum].instance].ship_name);
1606
1607                 nprintf(("AI", "Ship %s changing target from %s to %s\n", Ships[aip->shipnum].ship_name, old_name, new_name));
1608         }
1609 */
1610
1611         // AL 2-25-97: Ensure that a protected ship isn't being set as a target (for non-players only)
1612         /*
1613         if ( objnum >= 0 ) {
1614                 if ( !(Objects[Ships[aip->shipnum].objnum].flags & OF_PLAYER_SHIP) ) {
1615                         if ( Objects[objnum].flags & OF_PROTECTED ) {
1616                                 // AL 2-26-97: removing Int3() until issue with setting OF_PROTECTED in ai_set_attack_subsystem()
1617                                 //Int3();                                                               // this should not happen
1618                                 return aip->target_objnum;              // don't change targets
1619                         }
1620                 }
1621         }
1622         */
1623
1624         if ((aip != Player_ai) && (!timestamp_elapsed(aip->ok_to_target_timestamp))) {
1625                 return aip->target_objnum;
1626         }
1627
1628         if (aip->target_objnum == objnum) {
1629                 aip->previous_target_objnum = aip->target_objnum;
1630         } else {
1631                 aip->previous_target_objnum = aip->target_objnum;
1632
1633                 // ignore this assert if a multiplayer observer
1634                 if((Game_mode & GM_MULTIPLAYER) && (aip == Player_ai) && (Player_obj->type == OBJ_OBSERVER)){
1635                 } else {
1636                         Assert(objnum != Ships[aip->shipnum].objnum);   //      make sure not targeting self
1637                 }
1638
1639                 // if stealth target, init ai_info for stealth
1640                 if ( (objnum > 0) && is_object_stealth_ship(&Objects[objnum]) ) {
1641                         init_ai_stealth_info(aip, &Objects[objnum]);
1642                 }
1643
1644                 aip->target_objnum = objnum;
1645                 aip->target_time = 0.0f;
1646                 aip->target_signature = Objects[objnum].signature;
1647                 // clear targeted subsystem
1648                 set_targeted_subsys(aip, NULL, -1);
1649         }
1650         
1651         return aip->target_objnum;
1652 }
1653
1654 int ai_select_primary_weapon(object *objp, object *other_objp, int flags);
1655
1656 //      Make new_subsys the targeted subsystem of ship *aip.
1657 ship_subsys *set_targeted_subsys(ai_info *aip, ship_subsys *new_subsys, int parent_objnum)
1658 {
1659         Assert(aip != NULL);
1660
1661         aip->last_subsys_target = aip->targeted_subsys;
1662         aip->targeted_subsys = new_subsys;
1663         aip->targeted_subsys_parent = parent_objnum;
1664
1665         if ( new_subsys ) {
1666                 // Make new_subsys target
1667                 if (new_subsys->system_info->type == SUBSYSTEM_ENGINE) {
1668                         if ( aip != Player_ai ) {
1669                                 ai_select_primary_weapon(&Objects[Ships[aip->shipnum].objnum], &Objects[parent_objnum], WIF_PUNCTURE);
1670                                 ship_primary_changed(&Ships[aip->shipnum]);     // AL: maybe send multiplayer information when AI ship changes primaries
1671                         }
1672                 }
1673
1674                 if ( aip == Player_ai ) {
1675                         hud_lock_reset(0.5f);
1676                 }
1677
1678         } else {
1679                 // Cleanup any subsys path information if it exists
1680                 ai_big_subsys_path_cleanup(aip);
1681         }
1682         
1683         return aip->targeted_subsys;
1684 }                                                                                         
1685
1686 // called to init the data for single ai object.  At this point,
1687 // the ship and the object and the ai_info are are correctly
1688 // linked together. Ai_info[ai_index].shipnum is the only valid field 
1689 // in ai_info.
1690 //      This is called right when the object is parsed, so you can't assume much
1691 //      has been initialized.  For example, wings, waypoints, goals are probably
1692 //      not yet loaded. --MK, 10/8/96
1693 void ai_object_init(object * obj, int ai_index)
1694 {
1695         ai_info *aip;
1696         Assert(ai_index >= 0 && ai_index < MAX_AI_INFO);
1697
1698         aip = &Ai_info[ai_index];
1699
1700         aip->type = 0;          //      0 means not in use.
1701         aip->wing = -1;         //      Member of what wing? -1 means none.
1702         aip->ai_class = Ship_info[Ships[obj->instance].ship_info_index].ai_class;
1703         aip->behavior = AIM_NONE;
1704 }
1705
1706 //      If *aip is docked, set max acceleration to A->mass/(A->mass + B->mass) where A is *aip and B is dock object
1707 void adjust_accel_for_docking(ai_info *aip)
1708 {
1709         if (aip->dock_objnum != -1) {
1710                 object  *obj2p = &Objects[aip->dock_objnum];
1711                 object  *obj1p;
1712
1713                 obj1p = &Objects[Ships[aip->shipnum].objnum];
1714
1715                 if (obj2p->signature == aip->dock_signature) {
1716                         float   ratio;
1717
1718                         ratio = obj1p->phys_info.mass / (obj1p->phys_info.mass + obj2p->phys_info.mass);
1719
1720                         // put cap on how much ship can slow down
1721                         if (ratio < 0.8) {
1722                                 ratio = 0.8f;
1723                         }
1724
1725                         if (AI_ci.forward > ratio) {
1726                                 AI_ci.forward = ratio;
1727                         }
1728                 }
1729         }
1730 }
1731
1732 // -------------------------------------------------------------------
1733 void accelerate_ship(ai_info *aip, float accel)
1734 {
1735         aip->prev_accel = accel;
1736         AI_ci.forward = accel;
1737         adjust_accel_for_docking(aip);
1738 }
1739
1740 //      --------------------------------------------------------------------------
1741 void change_acceleration(ai_info *aip, float delta_accel)
1742 {
1743         float   new_accel;
1744
1745         if (delta_accel < 0.0f) {
1746                 if (aip->prev_accel > 0.0f)
1747                         aip->prev_accel = 0.0f;
1748         } else if (aip->prev_accel < 0.0f)
1749                 aip->prev_accel = 0.0f;
1750
1751         new_accel = aip->prev_accel + delta_accel * flFrametime;
1752
1753         if (new_accel > 1.0f)
1754                 new_accel = 1.0f;
1755         else if (new_accel < -1.0f)
1756                 new_accel = -1.0f;
1757         
1758         aip->prev_accel = new_accel;
1759
1760         AI_ci.forward = new_accel;
1761         adjust_accel_for_docking(aip);
1762 }
1763
1764 void set_accel_for_target_speed(object *objp, float tspeed)
1765 {
1766         float   max_speed;
1767         ai_info *aip;
1768
1769         aip = &Ai_info[Ships[objp->instance].ai_index];
1770
1771         max_speed = Ships[objp->instance].current_max_speed;
1772
1773         AI_ci.forward = tspeed/max_speed;
1774         aip->prev_accel = AI_ci.forward;
1775
1776         adjust_accel_for_docking(aip);
1777 }
1778
1779 //      Stuff perim_point with a point on the perimeter of the sphere defined by object *objp
1780 //      on the vector from the center of *objp through the point *vp.
1781 void project_point_to_perimeter(vector *perim_point, vector *pos, float radius, vector *vp)
1782 {
1783         vector  v1;
1784         float           mag;
1785
1786         vm_vec_sub(&v1, vp, pos);
1787         mag = vm_vec_mag(&v1);
1788
1789         if (mag == 0.0f) {
1790                 Warning(LOCATION, "projectable point is at center of sphere.");
1791                 (void) vm_vec_make(&v1, 0.0f, radius, 0.0f);
1792         } else {
1793                 vm_vec_normalize(&v1);
1794                 vm_vec_scale(&v1, 1.1f * radius + 10.0f);
1795         }
1796
1797         vm_vec_add2(&v1, pos);
1798         *perim_point = v1;
1799 }
1800
1801 //      Stuff tan1 with tangent point on sphere.  tan1 is point nearer to *p1
1802 //      *p0 is point through which tangents pass.
1803 //      *centerp is center of sphere.
1804 //      *p1 is another point in space to define the plane in which tan1, tan2 reside.
1805 //      radius is the radius of the sphere.
1806 //      Note, this is a very approximate function just for AI.
1807 //      Note also: On 12/26/96, p1 is used to define the plane perpendicular to that which
1808 //      contains the tangent point.
1809 void get_tangent_point(vector *tan1, vector *p0, vector *centerp, vector *p1, float radius)
1810 {
1811         vector  dest_vec, v2c, perp_vec, temp_vec, v2;
1812         float           dist, ratio;
1813
1814         //      Detect condition of point inside sphere.
1815         if (vm_vec_dist(p0, centerp) < radius)
1816                 project_point_to_perimeter(tan1, centerp, radius, p0);
1817         else {
1818                 vm_vec_normalized_dir(&v2c, centerp, p0);
1819
1820                 //      Compute perpendicular vector using p0, centerp, p1
1821                 vm_vec_normal(&temp_vec, p0, centerp, p1);
1822                 vm_vec_sub(&v2, centerp, p0);
1823                 vm_vec_cross(&perp_vec, &temp_vec, &v2);
1824
1825                 vm_vec_normalize(&perp_vec);
1826
1827                 dist = vm_vec_dist_quick(p0, centerp);
1828                 ratio = dist / radius;
1829
1830                 if (ratio < 2.0f)
1831                         vm_vec_scale_add(&dest_vec, &perp_vec, &v2c, ratio-1.0f);
1832                 else
1833                         vm_vec_scale_add(&dest_vec, &v2c, &perp_vec, (1.0f + 1.0f/ratio));
1834
1835                 vm_vec_scale_add(tan1, p0, &dest_vec, dist + radius);
1836         }
1837 }
1838
1839 //      --------------------------------------------------------------------------
1840 //      Given an object and a point, turn towards the point, resulting in
1841 // approach behavior.
1842 void turn_towards_point(object *objp, vector *point, vector *slide_vec, float bank_override)
1843 {
1844         ai_info *aip;
1845         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1846         
1847         // check if in formation and if not leader, don't change rotvel.xyz.z (bank to match leader elsewhere)
1848         if (aip->ai_flags & AIF_FORMATION) {
1849                 if (&Objects[aip->goal_objnum] != objp) {
1850                         float rotvel_z = objp->phys_info.rotvel.xyz.z;
1851                         ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1852                         objp->phys_info.rotvel.xyz.z = rotvel_z;
1853                 }
1854         } else {
1855                 // normal turn
1856                 ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1857         }
1858 }
1859
1860 //      --------------------------------------------------------------------------
1861 //      Given an object and a point, turn away from the point, resulting in avoidance behavior.
1862 //      Note: Turn away at full speed, not scaled down by skill level.
1863 void turn_away_from_point(object *objp, vector *point, float bank_override)
1864 {
1865         vector  opposite_point;
1866
1867         vm_vec_sub(&opposite_point, &objp->pos, point);
1868         vm_vec_add2(&opposite_point, &objp->pos);
1869
1870         ai_turn_towards_vector(&opposite_point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, NULL, NULL, bank_override, AITTV_FAST);
1871 }
1872
1873
1874 //      --------------------------------------------------------------------------
1875 //      Given an object and a point, turn tangent to the point, resulting in
1876 // a circling behavior.
1877 //      Make object *objp turn around the point *point with a radius of radius.
1878 //      Note that this isn't the same as following a circle of radius radius with
1879 //      center *point, but it should be adequate.
1880 //      Note that if you want to circle an object without hitting it, you should use
1881 //      about twice that object's radius for radius, else you'll certainly bump into it.
1882 //      Return dot product to goal point.
1883 float turn_towards_tangent(object *objp, vector *point, float radius)
1884 {
1885         vector  vec_to_point;
1886         vector  goal_point;
1887         vector  perp_point;                             //      point radius away from *point on vector to objp->pos
1888         vector  up_vec, perp_vec;
1889
1890         vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1891         vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.v.fvec);
1892         vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1893
1894         vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1895         if (vm_vec_dot(&objp->orient.v.fvec, &perp_vec) > 0.0f) {
1896                 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, radius);
1897         } else {
1898                 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, -radius);
1899         }
1900
1901 //      Ai_info[Ships[objp->instance].ai_index].goal_point = goal_point;
1902         turn_towards_point(objp, &goal_point, NULL, 0.0f);
1903
1904         vector  v2g;
1905
1906         vm_vec_normalized_dir(&v2g, &goal_point, &objp->pos);
1907         return vm_vec_dot(&objp->orient.v.fvec, &v2g);
1908 }
1909
1910 float turn_toward_tangent_with_axis(object *objp, object *center_objp, float radius)
1911 {
1912         vector r_vec, theta_vec;
1913         vector center_vec, vec_on_cylinder, sph_r_vec;
1914         float center_obj_z;
1915
1916         // find closest z of center objp
1917         vm_vec_sub(&sph_r_vec, &objp->pos, &center_objp->pos);
1918         center_obj_z = vm_vec_dotprod(&sph_r_vec, &center_objp->orient.v.fvec);
1919
1920         // find pt on axis with closest z
1921         vm_vec_scale_add(&center_vec, &center_objp->pos, &center_objp->orient.v.fvec, center_obj_z);
1922
1923         // get r_vec
1924         vm_vec_sub(&r_vec, &objp->pos, &center_vec);
1925 //      float r_mag = vm_vec_normalize_quick(&r_vec);
1926 //      mprintf(("cur_r: %.1f, desired_r: %.1f\n", r_mag, radius));
1927         Assert( (vm_vec_dotprod(&r_vec, &center_objp->orient.v.fvec) < 0.0001));
1928
1929         // get theta vec - perp to r_vec and z_vec
1930         vm_vec_crossprod(&theta_vec, &center_objp->orient.v.fvec, &r_vec);
1931
1932 #ifndef NDEBUG
1933         float mag = vm_vec_normalize(&theta_vec);
1934         Assert(mag > 0.9999 && mag < 1.0001);
1935 #endif
1936
1937         vector temp;
1938         vm_vec_crossprod(&temp, &r_vec, &theta_vec);
1939
1940 #ifndef NDEBUG
1941         float dot = vm_vec_dotprod(&temp, &center_objp->orient.v.fvec);
1942         Assert( dot >0.9999 && dot < 1.0001);
1943 #endif
1944
1945         // find pt on clylinder with closest z
1946         vm_vec_scale_add(&vec_on_cylinder, &center_vec, &r_vec, radius);
1947
1948         vector goal_pt, v2g;
1949         vm_vec_scale_add(&goal_pt, &vec_on_cylinder, &theta_vec, radius);
1950
1951 //      Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pt;
1952         turn_towards_point(objp, &goal_pt, NULL, 0.0f);
1953
1954         vm_vec_normalized_dir(&v2g, &goal_pt, &objp->pos);
1955         return vm_vec_dot(&objp->orient.v.fvec, &v2g);
1956 }
1957
1958 //      Returns a point radius units away from *point that *objp should turn towards to orbit *point
1959 void get_tangent_point(vector *goal_point, object *objp, vector *point, float radius)
1960 {
1961         vector  vec_to_point;
1962         vector  perp_point;                             //      point radius away from *point on vector to objp->pos
1963         vector  up_vec, perp_vec;
1964
1965         vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1966         vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.v.fvec);
1967         vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1968         vm_vec_normalize(&perp_vec);
1969
1970         vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1971
1972         if (vm_vec_dot(&objp->orient.v.fvec, &perp_vec) > 0.0f) {
1973                 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, radius);
1974         } else {
1975                 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, -radius);
1976         }
1977 }
1978
1979 int     Player_attacking_enabled = 1;
1980
1981 // -----------------------------------------------------------------------------
1982 // Determine whether an object is targetable within a nebula
1983 int object_is_targetable(object *target, ship *viewer)
1984 {
1985         int stealth_ship = 0;
1986
1987         // if target is ship, check if visible by team
1988         if (target->type == OBJ_SHIP) {
1989                 stealth_ship = (Ship_info[Ships[target->instance].ship_info_index].flags & SIF_STEALTH);
1990                 if ( ship_is_visible_by_team(target->instance, viewer->team) == 1) {
1991                         return 1;
1992                 }
1993         }
1994
1995         // for AI partially targetable works as fully targetable, except for stealth ship
1996         if (stealth_ship) {
1997                 // if not team targetable, check if within frustrum
1998                 if ( ai_is_stealth_visible(&Objects[viewer->objnum], target) == STEALTH_VISIBLE ) {
1999                         return 1;
2000                 } else {
2001                         return 0;
2002                 }
2003         }
2004
2005         // if not fully targetable by team, check awacs level with viewer
2006         // allow targeting even if only only partially targetable to player
2007         float radar_return = awacs_get_level(target, viewer);
2008         if ( radar_return > 0.4 ) {
2009                 return 1;
2010         } else {
2011                 return 0;
2012         }
2013 }
2014
2015 //      Return number of enemies attacking object objnum
2016 //
2017 // AL 10.26.97: Also include turrets on large ships when couting enemies attacking
2018 int num_enemies_attacking(int objnum)
2019 {
2020         object          *objp;
2021         ship                    *sp;
2022         ship_subsys     *ssp;
2023         ship_obj                *so;
2024         int                     count;
2025
2026         count = 0;
2027
2028         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2029                 objp = &Objects[so->objnum];
2030                 Assert(objp->instance != -1);
2031                 sp = &Ships[objp->instance];
2032
2033                 if (Ai_info[sp->ai_index].target_objnum == objnum)
2034                         count++;
2035
2036                 // consider turrets that may be attacking objnum (but only turrets on SIF_BIG_SHIP ships)
2037                 if ( Ship_info[sp->ship_info_index].flags & SIF_BIG_SHIP ) {
2038
2039                         // loop through all the subsystems, check if turret has objnum as a target
2040                         ssp = GET_FIRST(&sp->subsys_list);
2041                         while ( ssp != END_OF_LIST( &sp->subsys_list ) ) {
2042
2043                                 if ( ssp->system_info->type == SUBSYSTEM_TURRET ) {
2044                                         if ( (ssp->turret_enemy_objnum == objnum) && (ssp->current_hits > 0) ) {
2045                                                 count++;
2046                                         }
2047                                 }
2048                                 ssp = GET_NEXT( ssp );
2049                         } // end while
2050                 }
2051         }
2052
2053         return count;
2054 }
2055
2056 //      Get the team to fire on given an object.
2057 int get_enemy_team_mask(int objnum)
2058 {
2059         int     my_team, enemy_team_mask;
2060
2061         my_team = Ships[Objects[objnum].instance].team;
2062
2063         if (Mission_all_attack) {
2064                 //      All teams attack all teams.
2065                 switch (my_team) {
2066                 case TEAM_FRIENDLY:
2067                         enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2068                         break;
2069                 case TEAM_HOSTILE:
2070                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2071                         break;
2072                 case TEAM_NEUTRAL:
2073                         enemy_team_mask = TEAM_FRIENDLY | TEAM_HOSTILE | TEAM_TRAITOR;
2074                         break;
2075                 case TEAM_UNKNOWN:
2076                         enemy_team_mask = TEAM_HOSTILE;
2077                         break;
2078                 case TEAM_TRAITOR:
2079                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2080                         break;
2081                 default:
2082                         enemy_team_mask = TEAM_HOSTILE;
2083                         Int3();                 //      Illegal value for team!
2084                         break;
2085                 }
2086         } else {
2087                 switch (my_team) {
2088                 case TEAM_FRIENDLY:
2089                         enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2090                         break;
2091                 case TEAM_HOSTILE:
2092                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2093                         break;
2094                 case TEAM_NEUTRAL:
2095                         enemy_team_mask = TEAM_FRIENDLY | TEAM_TRAITOR;
2096                         break;
2097                 case TEAM_UNKNOWN:
2098                         enemy_team_mask = TEAM_HOSTILE;
2099                         break;
2100                 case TEAM_TRAITOR:
2101                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2102                         break;
2103                 default:
2104                         enemy_team_mask = TEAM_HOSTILE;
2105                         Int3();                 //      Illegal value for team!
2106                         break;
2107                 }
2108         }
2109
2110         return enemy_team_mask;
2111 }
2112
2113 //      Scan all the ships in *objp's wing.
2114 //      Return the lowest maximum speed of a ship in the wing.
2115 //      Current maximum speed (based on energy settings) is shipp->current_max_speed
2116 float get_wing_lowest_max_speed(object *objp)
2117 {
2118         ship            *shipp;
2119         ai_info *aip;
2120         float           lowest_max_speed;
2121         int             wingnum;
2122         object  *o;
2123         ship_obj        *so;
2124
2125         Assert(objp->type == OBJ_SHIP);
2126         Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
2127         shipp = &Ships[objp->instance];
2128         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
2129         aip = &Ai_info[shipp->ai_index];
2130
2131         wingnum = aip->wing;
2132
2133         lowest_max_speed = shipp->current_max_speed;
2134
2135         if ( wingnum == -1 )
2136                 return lowest_max_speed;
2137
2138         Assert(wingnum >= 0);
2139
2140         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2141                 o = &Objects[so->objnum];
2142                 ship    *oshipp = &Ships[o->instance];
2143                 ai_info *oaip = &Ai_info[oshipp->ai_index];
2144
2145                 if ((oaip->mode == AIM_WAYPOINTS) && (oaip->wing == wingnum)) {
2146                         //      Note: If a ship in the wing has a super low max speed, probably its engines are disabled.  So, fly along and
2147                         //      ignore the poor guy.
2148                         float   cur_max = oshipp->current_max_speed;
2149
2150                         if (oaip->ai_flags & AIF_DOCKED) {
2151                                 if (oaip->dock_objnum > -1)
2152                                         if (Objects[oaip->dock_objnum].type == OBJ_SHIP) 
2153                                                 cur_max *= o->phys_info.mass/(o->phys_info.mass + Objects[oaip->dock_objnum].phys_info.mass);
2154                         }
2155                                                         
2156                         if ((oshipp->current_max_speed > 5.0f) && (cur_max < lowest_max_speed)) {
2157                                 lowest_max_speed = cur_max;
2158                         }
2159                 }
2160         }
2161
2162         return lowest_max_speed;
2163 }
2164
2165 /*
2166 //      Tell everyone to ignore object objnum.
2167 void set_global_ignore_object(int objnum)
2168 {
2169         int     i;
2170
2171         Assert(Objects[objnum].type == OBJ_SHIP);
2172
2173         nprintf(("AI", "Telling everyone to ignore object %s\n", Ships[Objects[objnum].instance].ship_name));
2174
2175         for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2176                 if (Ignore_objects[i].objnum == -1) {
2177                         Ignore_objects[i].objnum = objnum;
2178                         Ignore_objects[i].signature = Objects[objnum].signature;
2179                         break;
2180                 }
2181         }
2182
2183         if (i == MAX_IGNORE_OBJECTS) {
2184                 //      Couldn't find a free slot, but maybe one of these objects has died.
2185                 for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2186                         int     o = Ignore_objects[i].objnum;
2187                         if (Objects[o].type != OBJ_SHIP)
2188                                 break;          //      Not a ship, so use this slot.
2189                         if (Objects[o].signature != Ignore_objects[i].signature)
2190                                 break;          //      Signatures don't match, so use this slot.
2191                 }
2192
2193                 if (i != MAX_IGNORE_OBJECTS) {
2194                         Ignore_objects[i].objnum = objnum;
2195                         Ignore_objects[i].signature = Objects[objnum].signature;
2196                 } else {
2197                         nprintf(("Warning", "Ignore_objects buffer full.  Stealing a slot to ignore object #%i\n"));
2198                         Int3();
2199
2200                         int     r;
2201
2202                         r = objnum % MAX_IGNORE_OBJECTS;
2203
2204                         Ignore_objects[r].objnum = objnum;
2205                         Ignore_objects[r].signature = Objects[objnum].signature;
2206                 }
2207         }
2208 }
2209
2210 */
2211
2212 //      Determine if object objnum is supposed to be ignored by object with ai_info *aip.
2213 //      Return:
2214 //              TRUE    if objnum is aip->ignore_objnum (and signatures match)
2215 //                              or objnum is in ignore wing
2216 //              FALSE   otherwise
2217 int is_ignore_object(ai_info *aip, int objnum)
2218 {
2219
2220 /*      //      First, scan all objects in global array of objects to be ignored.
2221         for (int i=0; i<MAX_IGNORE_OBJECTS; i++)
2222                 if (Ignore_objects[i].objnum != -1)
2223                         if (objnum == Ignore_objects[i].objnum)
2224                                 if (Objects[Ignore_objects[i].objnum].signature == Ignore_objects[i].signature)
2225                                         return 1;
2226 */
2227
2228         //      Didn't find in global list.  Now check 
2229         if (aip->ignore_objnum == UNUSED_OBJNUM)
2230                 return 0;                                                                       //      Not ignoring anything.
2231         else if (aip->ignore_objnum >= 0) {             //      This means it's ignoring an object, not a wing.
2232                 if (aip->ignore_objnum == objnum) {
2233                         if (Objects[aip->ignore_objnum].signature == aip->ignore_signature) {
2234                                 return 1;
2235                         } else {
2236                                 aip->ignore_objnum = UNUSED_OBJNUM;
2237                                 return 0;
2238                         }
2239                 } else {
2240                         return 0;
2241                 }
2242         } else {                                                                                        //      Ignoring a wing.
2243                 Int3(); // Should never happen.  I thought I removed this behavior! -- MK, 5/17/98
2244                 return 0;
2245 /*              int     ignore_wingnum = -(aip->ignore_objnum + 1);
2246
2247                 Assert(ignore_wingnum < MAX_WINGS);
2248                 Assert(aip->shipnum >= 0);
2249                 return (Ships[Objects[objnum].instance].wingnum == ignore_wingnum);
2250 */      }
2251 }
2252
2253 // -----------------------------------------------------------------------------
2254
2255 // given a ship with bounding box and a point, find the closest point on the bbox
2256 int get_nearest_bbox_point(object *ship_obj, vector *start, vector *box_pt)
2257 {
2258         vector temp, rf_start;
2259         polymodel *pm;
2260         pm = model_get(Ship_info[Ships[ship_obj->instance].ship_info_index].modelnum);
2261
2262         // get start in ship rf
2263         vm_vec_sub(&temp, start, &ship_obj->pos);
2264         vm_vec_rotate(&rf_start, &temp, &ship_obj->orient);
2265
2266         // find box_pt
2267         int inside = project_point_onto_bbox(&pm->mins, &pm->maxs, &rf_start, &temp);
2268
2269         // get box_pt in world rf
2270         vm_vec_unrotate(box_pt, &temp, &ship_obj->orient);
2271         vm_vec_add2(box_pt, &ship_obj->pos);
2272
2273         return inside;
2274 }
2275
2276
2277 typedef struct eval_nearest_objnum {
2278         int     objnum;
2279         object *trial_objp;
2280         int     enemy_team_mask;
2281         int     enemy_wing;
2282         float   range;
2283         int     max_attackers;
2284         int     nearest_objnum;
2285         float   nearest_dist;
2286         int     check_danger_weapon_objnum;
2287 } eval_nearest_objnum;
2288
2289
2290 void evaluate_object_as_nearest_objnum(eval_nearest_objnum *eno)
2291 {
2292         ai_info *aip;
2293         ship_subsys     *attacking_subsystem;
2294
2295         aip = &Ai_info[Ships[Objects[eno->objnum].instance].ai_index];
2296
2297         attacking_subsystem = aip->targeted_subsys;
2298
2299         if ((attacking_subsystem != NULL) || !(eno->trial_objp->flags & OF_PROTECTED)) {
2300                 if ( OBJ_INDEX(eno->trial_objp) != eno->objnum ) {
2301 #ifndef NDEBUG
2302                         if (!Player_attacking_enabled && (eno->trial_objp == Player_obj))
2303                                 return;
2304 #endif
2305                         //      If only supposed to attack ship in a specific wing, don't attack other ships.
2306                         if ((eno->enemy_wing != -1) && (Ships[eno->trial_objp->instance].wingnum != eno->enemy_wing))
2307                                 return;
2308
2309                         //      Don't keep firing at a ship that is in its death throes.
2310                         if (Ships[eno->trial_objp->instance].flags & SF_DYING)
2311                                 return;
2312
2313                         if (is_ignore_object(aip, ((eno->trial_objp)-Objects)))
2314                                 return;
2315
2316                         if (eno->trial_objp->flags & OF_PROTECTED)
2317                                 return;
2318
2319                         if (Ships[eno->trial_objp->instance].flags & SF_ARRIVING)
2320                                 return;
2321
2322                         ship_info *sip = &Ship_info[Ships[eno->trial_objp->instance].ship_info_index];
2323
2324                         if (sip->flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2325                                 return;
2326
2327                         if (Ships[eno->trial_objp->instance].team & eno->enemy_team_mask) {
2328                                 float   dist;
2329                                 int     num_attacking;
2330
2331                                 // Allow targeting of stealth in nebula by his firing at me
2332                                 // This is done for a specific ship, not generally.
2333                                 if ( !eno->check_danger_weapon_objnum ) {
2334                                         // check if can be targeted if inside nebula
2335                                         if ( !object_is_targetable(eno->trial_objp, &Ships[Objects[eno->objnum].instance]) ) {
2336                                                 // check if stealth ship is visible, but not "targetable"
2337                                                 if ( !((sip->flags & SIF_STEALTH) && ai_is_stealth_visible(&Objects[eno->objnum], eno->trial_objp)) ) {
2338                                                         return;
2339                                                 }
2340                                         }
2341                                 }
2342
2343                                 // if objnum is BIG or HUGE, find distance to bbox
2344                                 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
2345                                         vector box_pt;
2346                                         // check if inside bbox
2347                                         int inside = get_nearest_bbox_point(eno->trial_objp, &Objects[eno->objnum].pos, &box_pt);
2348                                         if (inside) {
2349                                                 dist = 10.0f;
2350                                                 // on the box
2351                                         } else {
2352                                                 dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &box_pt);
2353                                         }
2354                                 } else {
2355                                         dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &eno->trial_objp->pos);
2356                                 }
2357                                 
2358                                 //      Make it more likely that fighters (or bombers) will be picked as an enemy by scaling up distance for other types.
2359                                 if ((Ship_info[Ships[eno->trial_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))) {
2360                                         dist = dist * 0.5f;
2361                                 }
2362
2363                                 num_attacking = num_enemies_attacking(eno->trial_objp-Objects);
2364                                 if ((sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) || (num_attacking < eno->max_attackers)) {
2365                                         if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))){
2366                                                 dist *= (float) (num_attacking+2)/2.0f;                         //      prevents lots of ships from attacking same target
2367                                         }
2368
2369                                         if (eno->trial_objp->flags & OF_PLAYER_SHIP){
2370                                                 dist *= 1.0f + (NUM_SKILL_LEVELS - Game_skill_level - 1)/NUM_SKILL_LEVELS;      //      Favor attacking non-players based on skill level.
2371                                         }
2372
2373                                         if (dist < eno->nearest_dist) {
2374                                                 eno->nearest_dist = dist;
2375                                                 eno->nearest_objnum = eno->trial_objp-Objects;
2376                                         }
2377                                 }
2378                         }
2379                 }
2380         }
2381
2382 }
2383
2384
2385 //      Given an object and an enemy team, return the index of the nearest enemy object.
2386 //      Unless aip->targeted_subsys != NULL, don't allow to attack objects
2387 //      with OF_PROTECTED bit set.
2388 //      Ship must be within range "range".
2389 //      Don't attack a ship that already has at least max_attackers attacking it.
2390 int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers)
2391 {
2392         object  *danger_weapon_objp;
2393         ai_info *aip;
2394         ship_obj        *so;
2395
2396         // initialize eno struct
2397         eval_nearest_objnum eno;
2398         eno.enemy_team_mask = enemy_team_mask;
2399         eno.enemy_wing = enemy_wing;
2400         eno.max_attackers = max_attackers;
2401         eno.objnum = objnum;
2402         eno.range = range;
2403         eno.nearest_dist = range;
2404         eno.nearest_objnum = -1;
2405         eno.check_danger_weapon_objnum = 0;
2406
2407         // go through the list of all ships and evaluate as potential targets
2408         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2409                 eno.trial_objp = &Objects[so->objnum];
2410                 evaluate_object_as_nearest_objnum(&eno);
2411
2412         }
2413
2414         // check if danger_weapon_objnum has will show a stealth ship
2415         aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2416         if (aip->danger_weapon_objnum >= 0) {
2417                 danger_weapon_objp = &Objects[aip->danger_weapon_objnum];
2418                 // validate weapon
2419                 if (danger_weapon_objp->signature == aip->danger_weapon_signature) {
2420                         Assert(danger_weapon_objp->type == OBJ_WEAPON);
2421                         // check if parent is a ship
2422                         if (danger_weapon_objp->parent >= 0) {
2423                                 if ( is_object_stealth_ship(&Objects[danger_weapon_objp->parent]) ) {
2424                                         // check if stealthy
2425                                         if ( ai_is_stealth_visible(&Objects[objnum], &Objects[danger_weapon_objp->parent]) != STEALTH_FULLY_TARGETABLE ) {
2426                                                 // check if weapon is laser
2427                                                 if (Weapon_info[Weapons[danger_weapon_objp->instance].weapon_info_index].subtype == WP_LASER) {
2428                                                         // check stealth ship by its laser fire
2429                                                         eno.check_danger_weapon_objnum = 1;
2430                                                         eno.trial_objp = &Objects[danger_weapon_objp->parent];
2431                                                         evaluate_object_as_nearest_objnum(&eno);
2432                                                 }
2433                                         }
2434                                 }
2435                         }
2436                 }
2437         }
2438
2439         //      If only looking for target in certain wing and couldn't find anything in
2440         //      that wing, look for any object.
2441         if ((eno.nearest_objnum == -1) && (enemy_wing != -1)) {
2442                 return get_nearest_objnum(objnum, enemy_team_mask, -1, range, max_attackers);
2443         }
2444
2445         return eno.nearest_objnum;
2446 }
2447
2448 //      Given an object and an enemy team, return the index of the nearest enemy object.
2449 //      Unlike find_enemy or find_nearest_objnum, this doesn't care about things like the protected flag or number
2450 //      of enemies attacking.
2451 //      It is used to find the nearest enemy to determine things like whether to rearm.
2452 int find_nearby_hostile(int objnum, int enemy_team_mask, float range, int *count)
2453 {
2454         int             nearest_objnum;
2455         float           nearest_dist;
2456         object  *objp;
2457         ai_info *aip;
2458         ship_obj        *so;
2459
2460         nearest_objnum = -1;
2461         nearest_dist = range;
2462
2463         aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2464
2465         *count = 0;
2466
2467         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2468                 objp = &Objects[so->objnum];
2469
2470                 if ( OBJ_INDEX(objp) != objnum ) {
2471                         if (Ships[objp->instance].flags & SF_DYING)
2472                                 continue;
2473
2474                         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2475                                 continue;
2476
2477                         if (Ships[objp->instance].team & enemy_team_mask) {
2478                                 float   dist;
2479
2480                                 dist = vm_vec_dist_quick(&Objects[objnum].pos, &objp->pos) - objp->radius*0.75f;
2481                                 
2482                                 if (dist < range) {
2483                                         (*count)++;
2484
2485                                         if (dist < nearest_dist) {
2486                                                 nearest_dist = dist;
2487                                                 nearest_objnum = objp-Objects;
2488                                         }
2489                                 }
2490                         }
2491                 }
2492         }
2493
2494         return nearest_objnum;
2495 }
2496
2497 // return !0 if objp can be considered for a turret target, 0 otherwise
2498 // input:       objp                            =>      object that turret is considering as an enemy
2499 //                              turret_parent   =>      object index for ship that turret sits on
2500 int valid_turret_enemy(object *objp, object *turret_parent)
2501 {
2502         if ( objp == turret_parent ) {
2503                 return 0;
2504         }
2505
2506         if ( objp->type == OBJ_ASTEROID ) {
2507                 return 1;
2508         }
2509
2510         if ( (objp->type == OBJ_SHIP) ) {
2511                 ship *shipp;
2512                 shipp = &Ships[objp->instance];
2513
2514                 // don't fire at ships with protected bit set!!!
2515                 if ( objp->flags & OF_PROTECTED ) {
2516                         return 0;
2517                 }
2518
2519                 if ( !(Ship_info[shipp->ship_info_index].flags & SIF_DO_COLLISION_CHECK)) {
2520                         return 0;
2521                 }
2522
2523                 if (shipp->flags & SF_ARRIVING) {
2524                         return 0;
2525                 }
2526
2527                 return 1;
2528         }
2529
2530         if ( objp->type == OBJ_WEAPON ) {
2531                 if ( Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_BOMB ) {
2532                         if ( obj_team(turret_parent) != Weapons[objp->instance].team ) {
2533                                 return 1;
2534                         }
2535                 }
2536         }
2537
2538         return 0;
2539 }
2540
2541 // return 1 if objp is in fov of the specified turret, tp.  Otherwise return 0.
2542 //      dist = distance from turret to center point of object
2543 int object_in_turret_fov(object *objp, model_subsystem *tp, vector *tvec, vector *tpos, float dist)
2544 {
2545         vector  v2e;
2546         float           dot;
2547         vm_vec_normalized_dir(&v2e, &objp->pos, tpos);
2548         dot = vm_vec_dot(&v2e, tvec);
2549
2550         dot += objp->radius / (dist + objp->radius);
2551
2552         if ( dot >= tp->turret_fov ) {
2553                 return 1;
2554         }
2555
2556         return 0;
2557 }
2558
2559 // return 1 if bomb_objp is headed towards ship_objp
2560 int bomb_headed_towards_ship(object *bomb_objp, object *ship_objp)
2561 {
2562         float           dot;
2563         vector  bomb_to_ship_vector;
2564
2565         vm_vec_normalized_dir(&bomb_to_ship_vector, &ship_objp->pos, &bomb_objp->pos);
2566         dot = vm_vec_dot(&bomb_objp->orient.v.fvec, &bomb_to_ship_vector);
2567
2568         if ( dot > 0 ) {
2569                 return 1;
2570         }
2571
2572         return 0;
2573 }
2574
2575 // nubmer of live turrets with target_objnum 
2576 int num_turrets_attacking(object *turret_parent, int target_objnum) 
2577 {
2578         ship_subsys *ss;
2579         ship *shipp;
2580         int count = 0;
2581         shipp = &Ships[turret_parent->instance];
2582
2583         Assert(turret_parent->type == OBJ_SHIP);
2584         Assert(Objects[target_objnum].type == OBJ_SHIP);
2585
2586         for (ss=GET_FIRST(&shipp->subsys_list); ss!=END_OF_LIST(&shipp->subsys_list); ss=GET_NEXT(ss)) {
2587                 // check if subsys is alive
2588                 if (ss->current_hits <= 0.0f) {
2589                         continue;
2590                 }
2591
2592                 // check if it's a turret
2593                 if (ss->system_info->type != SUBSYSTEM_TURRET) {
2594                         continue;
2595                 }
2596
2597                 // if the turret is locked
2598                 if(ss->weapons.flags & SW_FLAG_TURRET_LOCK){
2599                         continue;
2600                 }               
2601
2602                 // check if turret is targeting target_objnum
2603                 if (ss->turret_enemy_objnum == target_objnum) {
2604                         count++;
2605                 }
2606         }
2607
2608         return count;
2609 }
2610
2611 float Lethality_range_const = 2.0f;
2612 DCF(lethality_range, "N for modifying range: 1 / (1+N) at 100")
2613 {
2614         dc_get_arg(ARG_FLOAT);
2615         Lethality_range_const = Dc_arg_float;
2616 }
2617
2618 float Player_lethality_bump[NUM_SKILL_LEVELS] = {
2619         // 0.0f, 5.0f, 10.0f, 25.0f, 40.0f
2620         0.0f, 0.0f, 0.0f, 0.0f, 0.0f
2621 };
2622
2623 // evaluate obj as posssible target for turret
2624 void evaluate_obj_as_target(object *objp, eval_enemy_obj_struct *eeo)
2625 {
2626         object  *turret_parent_obj = &Objects[eeo->turret_parent_objnum];
2627         ship            *shipp;
2628         model_subsystem *tp = eeo->turret_subsys->system_info;
2629         float dist;
2630
2631         // Don't look for bombs when weapon system is not ok
2632         if (objp->type == OBJ_WEAPON && !eeo->weapon_system_ok) {
2633                 return;
2634         }
2635
2636         if ( !valid_turret_enemy(objp, turret_parent_obj) ) {
2637                 return;
2638         }
2639
2640 #ifndef NDEBUG
2641         if (!Player_attacking_enabled && (objp == Player_obj)) {
2642                 return;
2643         }
2644 #endif
2645
2646         if ( objp->type == OBJ_SHIP ) {
2647                 shipp = &Ships[objp->instance];
2648
2649                 // check on enemy team
2650                 if ( !(shipp->team & eeo->enemy_team_mask) ) {
2651                         return;
2652                 }
2653
2654                 // check if protected
2655                 if (objp->flags & OF_PROTECTED) {
2656                         return;
2657                 }
2658
2659                 // check if beam protected
2660                 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2661                         if (objp->flags & OF_BEAM_PROTECTED) {
2662                                 return;
2663                         }
2664                 }
2665
2666                 if (eeo->big_only_flag) {
2667                         if (!(Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2668                                 return;
2669                         }
2670                 }
2671
2672                 // check if     turret flagged to only target tagged ships
2673                 if ( (eeo->turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && !ship_is_tagged(objp) ) {
2674                         return;
2675                 }
2676
2677                 // check if valid target in nebula
2678                 if ( !object_is_targetable(objp, &Ships[Objects[eeo->turret_parent_objnum].instance]) ) {
2679                         // BYPASS ocassionally for stealth
2680                         int try_anyway = FALSE;
2681                         if ( is_object_stealth_ship(objp) ) {
2682                                 float turret_stealth_find_chance = 0.5f;
2683                                 float speed_mod = -0.1f + vm_vec_mag_quick(&objp->phys_info.vel) / 70.0f;
2684                                 if (frand() > (turret_stealth_find_chance + speed_mod)) {
2685                                         try_anyway = TRUE;
2686                                 }
2687                         }
2688
2689                         if (!try_anyway) {
2690                                 return;
2691                         }
2692                 }
2693
2694         } else {
2695                 shipp = NULL;
2696         }
2697
2698         // modify dist for BIG|HUGE, getting closest point on bbox, if not inside
2699         dist = vm_vec_dist_quick(eeo->tpos, &objp->pos) - objp->radius;
2700         if (dist < 0.0f) {
2701                 dist = 0.0f;
2702         }
2703
2704         // check if object is a bomb attacking the turret parent
2705         // check if bomb is homing on the turret parent ship
2706         if (objp->type == OBJ_WEAPON) {
2707                 if ( Weapons[objp->instance].homing_object == &Objects[eeo->turret_parent_objnum] ) {
2708                         if ( dist < eeo->nearest_homing_bomb_dist ) {
2709                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2710                                         eeo->nearest_homing_bomb_dist = dist;
2711                                         eeo->nearest_homing_bomb_objnum = OBJ_INDEX(objp);
2712                                 }
2713                         }
2714                 // if not homing, check if bomb is flying towards ship
2715                 } else if ( bomb_headed_towards_ship(objp, &Objects[eeo->turret_parent_objnum]) ) {
2716                         if ( dist < eeo->nearest_bomb_dist ) {
2717                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2718                                         eeo->nearest_bomb_dist = dist;
2719                                         eeo->nearest_bomb_objnum = OBJ_INDEX(objp);
2720                                 }
2721                         }
2722                 }
2723         } // end weapon section
2724
2725         // maybe recalculate dist for big or huge ship
2726 //      if (shipp && (Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2727 //              fvi_ray_boundingbox(min, max, start, direction, hit);
2728 //              dist = vm_vec_dist_quick(hit, tvec);
2729 //      }
2730
2731         // check for nearest attcker
2732         if ( (shipp) && (dist < eeo->weapon_travel_dist) ) {
2733                 ai_info *aip = &Ai_info[shipp->ai_index];
2734
2735                 // modify distance based on number of turrets from my ship attacking enemy (add 10% per turret)
2736                 // dist *= (num_enemies_attacking(OBJ_INDEX(objp))+2)/2;        //      prevents lots of ships from attacking same target
2737                 int num_att_turrets = num_turrets_attacking(turret_parent_obj, OBJ_INDEX(objp));
2738                 dist *= (1.0f + 0.1f*num_att_turrets);
2739
2740                 // return if we're over the cap
2741                 int max_turrets = 3 + Game_skill_level * Game_skill_level;
2742                 if (num_att_turrets > max_turrets) {
2743                         return;
2744                 }
2745
2746                 // modify distance based on lethality of objp to my ship
2747                 float active_lethality = aip->lethality;
2748                 if (objp->flags & OF_PLAYER_SHIP) {
2749                         active_lethality += Player_lethality_bump[Game_skill_level];
2750                 }
2751
2752                 dist /= (1.0f + 0.01f*Lethality_range_const*active_lethality);
2753
2754                 // Make level 2 tagged ships more likely to be targeted
2755                 if (shipp->level2_tag_left > 0.0f) {
2756                         dist *= 0.3f;
2757                 }
2758
2759                 // check if objp is targeting the turret's ship, or if objp has just hit the turret's ship
2760                 if ( aip->target_objnum == eeo->turret_parent_objnum || aip->last_objsig_hit == Objects[eeo->turret_parent_objnum].signature ) {
2761                         // A turret will always target a ship that is attacking itself... self-preservation!
2762                         if ( aip->targeted_subsys == eeo->turret_subsys ) {
2763                                 dist *= 0.5f;   // highest priority
2764                         }
2765                 }
2766
2767                 // maybe update nearest attacker
2768                 if ( dist < eeo->nearest_attacker_dist ) {
2769                         if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2770                                 // 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));
2771                                 eeo->nearest_attacker_dist = dist;
2772                                 eeo->nearest_attacker_objnum = OBJ_INDEX(objp);
2773                         }
2774                 }
2775         } // end ship section
2776 }
2777
2778 // return 0 only if objnum is beam protected and turret is beam turret
2779 int is_target_beam_valid(ship_subsys *turret_subsys, int objnum)
2780 {
2781         // check if turret has beam weapon
2782         model_subsystem *tp = turret_subsys->system_info;
2783
2784         if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2785                 if (Objects[objnum].flags & OF_BEAM_PROTECTED) {
2786                         return 0;
2787                 }
2788
2789                 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_HUGE) {
2790                         if (Objects[objnum].type == OBJ_SHIP && !(Ship_info[Ships[Objects[objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
2791                                 return 0;
2792                         }
2793                 }
2794         }
2795
2796         return 1;
2797 }
2798
2799
2800 //      Given an object and an enemy team, return the index of the nearest enemy object.
2801 //
2802 // input:
2803 //                              turret_parent_objnum    => parent objnum for the turret
2804 //                              turret_subsys                   => pointer to system_info for the turret subsystem
2805 //                              enemy_team_mask         => OR'ed TEAM_ flags for the enemy of the turret parent ship
2806 //                              tpos                                            => position of turret (world coords)
2807 //                              tvec                                            => forward vector of turret (world coords)
2808 //                              current_enemy                   =>      objnum of current turret target
2809 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)
2810 {
2811         float                                   weapon_travel_dist;
2812         int                                     weapon_system_ok;
2813         object                          *objp;
2814         model_subsystem *tp;
2815         eval_enemy_obj_struct eeo;
2816
2817         // list of stuff to go thru
2818         ship_obj                *so;
2819         missile_obj *mo;
2820
2821         tp = turret_subsys->system_info;
2822         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);
2823
2824         // Set flag based on strength of weapons subsystem.  If weapons subsystem is destroyed, don't let turrets fire at bombs
2825         weapon_system_ok = 0;
2826         if ( ship_get_subsystem_strength( &Ships[Objects[turret_parent_objnum].instance], SUBSYSTEM_WEAPONS ) > 0 ) {
2827                 weapon_system_ok = 1;
2828         }
2829
2830         // Initialize eeo struct.
2831         eeo.turret_parent_objnum = turret_parent_objnum;
2832         eeo.weapon_system_ok = weapon_system_ok;
2833         eeo.weapon_travel_dist = weapon_travel_dist;
2834         eeo.big_only_flag = big_only_flag;
2835         eeo.enemy_team_mask = enemy_team_mask;
2836         eeo.current_enemy = current_enemy;
2837         eeo.tpos = tpos;
2838         eeo.tvec = tvec;
2839         eeo.turret_subsys = turret_subsys;
2840
2841         eeo.nearest_attacker_dist = 99999.0f;
2842         eeo.nearest_attacker_objnum = -1;
2843
2844         eeo.nearest_homing_bomb_dist = 99999.0f;
2845         eeo.nearest_homing_bomb_objnum = -1;
2846
2847         eeo.nearest_bomb_dist = 99999.0f;
2848         eeo.nearest_bomb_objnum = -1;
2849
2850         eeo.nearest_dist = 99999.0f;
2851         eeo.nearest_objnum = -1;
2852
2853
2854         // Missile_obj_list
2855         for( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
2856                 objp = &Objects[mo->objnum];
2857                 evaluate_obj_as_target(objp, &eeo);
2858         }
2859         // highest priority
2860         if ( eeo.nearest_homing_bomb_objnum != -1 ) {                                   // highest priority is an incoming homing bomb
2861                 return eeo.nearest_homing_bomb_objnum;
2862         } else if ( eeo.nearest_bomb_objnum != -1 ) {                                   // next highest priority is an incoming dumbfire bomb
2863                 return eeo.nearest_bomb_objnum;
2864         }
2865
2866
2867         // Ship_used_list
2868         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2869                 objp = &Objects[so->objnum];
2870                 evaluate_obj_as_target(objp, &eeo);
2871         }
2872
2873         Assert(eeo.nearest_attacker_objnum < 0 || is_target_beam_valid(turret_subsys, eeo.nearest_attacker_objnum));
2874                 // next highest priority is attacking ship
2875         if ( eeo.nearest_attacker_objnum != -1 ) {                      // next highest priority is an attacking ship
2876                 return eeo.nearest_attacker_objnum;
2877          }
2878
2879
2880 #if !(defined(FS2_DEMO) || defined(FS1_DEMO))
2881                 asteroid_obj *ao;
2882         // Asteroid_obj_list
2883         for( ao = GET_FIRST(&Asteroid_obj_list); ao != END_OF_LIST(&Asteroid_obj_list); ao = GET_NEXT(ao) ) {
2884                 objp = &Objects[ao->objnum];
2885                 evaluate_obj_as_target(objp, &eeo);
2886         }
2887 #endif
2888
2889         return eeo.nearest_objnum;                                                                              // lowest priority is the closest enemy objnum
2890 }
2891
2892 //      Return timestamp until a ship can find an enemy.
2893 //      Yes, no parameters.  Based solely on skill level.
2894 int get_enemy_timestamp()
2895 {
2896         return (NUM_SKILL_LEVELS - Game_skill_level) * ( (myrand() % 500) + 500);
2897 }
2898
2899 // -------------------------------------------------------------------
2900 //      Return objnum if enemy found, else return -1;
2901 //      Don't attack a ship that already has at least max_attackers attacking it.
2902 int find_enemy(int objnum, float range, int max_attackers)
2903 {
2904         int     enemy_team_mask;
2905
2906         enemy_team_mask = get_enemy_team_mask(objnum);
2907
2908         //      if target_objnum != -1, use that as goal.
2909         ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2910         if (timestamp_elapsed(aip->choose_enemy_timestamp)) {
2911                 aip->choose_enemy_timestamp = timestamp(get_enemy_timestamp());
2912                 if (aip->target_objnum != -1) {
2913                         int     target_objnum = aip->target_objnum;
2914
2915                         // DKA don't undo object as target in nebula missions.
2916                         // This could cause attack on ship on fringe on nebula to stop if attackee moves our of nebula range.  (BAD)
2917                         if ( (Objects[target_objnum].signature == aip->target_signature) ) {
2918                                 if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
2919                                         if (!(Objects[target_objnum].flags & OF_PROTECTED)) {
2920                                                 // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
2921                                                 return target_objnum;
2922                                         }
2923                                 }
2924                         } else {
2925                                 aip->target_objnum = -1;
2926                                 aip->target_signature = -1;
2927                         }
2928                 }
2929                 return get_nearest_objnum(objnum, enemy_team_mask, aip->enemy_wing, range, max_attackers);
2930         } else {
2931                 aip->target_objnum = -1;
2932                 aip->target_signature = -1;
2933                 return -1;
2934         }
2935
2936 }
2937
2938 int Use_parent_target = 0;
2939 DCF_BOOL(use_parent_target, Use_parent_target)
2940
2941 // -------------------------------------------------------------------
2942 //      Return objnum if enemy found, else return -1;
2943 //
2944 // input:
2945 //                              turret_subsys   => pointer to turret subsystem
2946 //                              objnum                  => parent objnum for the turret
2947 //                              tpos                            => position of turret (world coords)
2948 //                              tvec                            => forward vector of turret (world coords)
2949 //                              current_enemy   =>      objnum of current turret target
2950 int find_turret_enemy(ship_subsys *turret_subsys, int objnum, vector *tpos, vector *tvec, int current_enemy, float fov, int big_only_flag = 0)
2951 {
2952         int                                     enemy_team_mask, enemy_objnum;
2953         model_subsystem *tp;
2954         ship_info                       *sip;
2955
2956         tp = turret_subsys->system_info;
2957         enemy_team_mask = get_enemy_team_mask(objnum);
2958
2959         //      If a small ship and target_objnum != -1, use that as goal.
2960         ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2961         sip = &Ship_info[Ships[Objects[objnum].instance].ship_info_index];
2962
2963         if ((sip->flags & SIF_SMALL_SHIP) && (aip->target_objnum != -1)) {
2964                 int target_objnum = aip->target_objnum;
2965
2966                 if (Objects[target_objnum].signature == aip->target_signature) {
2967                         if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
2968                                 if ( !(Objects[target_objnum].flags & OF_PROTECTED) ) {         // check this flag as well.
2969                                         // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
2970                                         return target_objnum;
2971                                 }
2972                         }
2973                 } else {
2974                         aip->target_objnum = -1;
2975                         aip->target_signature = -1;
2976                 }
2977         // Not small or small with target objnum
2978         } else {
2979                 // maybe use aip->target_objnum as next target
2980                 if ((frand() < 0.8f) && (aip->target_objnum != -1) && Use_parent_target) {
2981
2982                         //check if aip->target_objnum is valid target
2983                         int target_flags = Objects[aip->target_objnum].flags;
2984                         if ( target_flags & OF_PROTECTED ) {
2985                                 // AL 2-27-98: why is a protected ship being targeted?
2986                                 set_target_objnum(aip, -1);
2987                                 return -1;
2988                         }
2989
2990                         // maybe use ship target_objnum if valid for turret
2991                         // check for beam weapon and beam protected
2992                         if ( !((target_flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) ) {
2993                                 if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
2994                                         // check for huge weapon and huge ship
2995                                         if ( !big_only_flag || (Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
2996                                                 // check for tagged only and tagged ship
2997                                                 if ( (turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && ship_is_tagged(&Objects[aip->target_objnum]) ) {
2998                                                         // select new target if aip->target_objnum is out of field of view
2999                                                         vector v2e;
3000                                                         float dot, dist;
3001                                                         dist = vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, tpos);
3002                                                         dot = vm_vec_dot(&v2e, tvec);
3003                                                         //      MODIFY FOR ATTACKING BIG SHIP
3004                                                         // dot += (0.5f * Objects[aip->target_objnum].radius / dist);
3005                                                         if (dot > fov) {
3006                                                                 return aip->target_objnum;
3007                                                         }
3008                                                 }
3009                                         }
3010                                 }
3011                         }
3012                 }
3013         }
3014
3015         enemy_objnum = get_nearest_turret_objnum(objnum, turret_subsys, enemy_team_mask, tpos, tvec, current_enemy, big_only_flag);
3016         if ( enemy_objnum >= 0 ) {
3017                 Assert( !((Objects[enemy_objnum].flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) );
3018                 if ( Objects[enemy_objnum].flags & OF_PROTECTED ) {
3019                         Int3();
3020                         enemy_objnum = aip->target_objnum;
3021                 }
3022         }
3023
3024         return enemy_objnum;
3025 }
3026
3027 //      If issued an order to a ship that's awaiting repair, abort that process.
3028 //      However, do not abort process for an object that is currently being repaired -- let it finish.
3029 void ai_set_goal_maybe_abort_dock(object *objp, ai_info *aip)
3030 {
3031         if (aip->ai_flags & AIF_AWAITING_REPAIR) {
3032                 object  *repair_obj;
3033
3034                 if (aip->dock_objnum == -1) {
3035                         repair_obj = NULL;
3036                 } else {
3037                         repair_obj = &Objects[aip->dock_objnum];
3038                 }
3039                 ai_do_objects_repairing_stuff( objp, repair_obj, REPAIR_INFO_ABORT );
3040         }
3041         aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);    //      Might request again after 30 seconds.
3042 }
3043
3044 void force_avoid_player_check(object *objp, ai_info *aip)
3045 {
3046         if (Ships[objp->instance].team == Player_ship->team){
3047                 aip->avoid_check_timestamp = timestamp(0);              //      Force a check for collision next frame.
3048         }
3049 }
3050
3051 //      --------------------------------------------------------------------------
3052 //      Set *attacked as object to attack for object *attacker
3053 //      If attacked == NULL, then attack any enemy object.
3054 //      Attack point *rel_pos on object.  This is for supporting attacking subsystems.
3055 void ai_attack_object(object *attacker, object *attacked, int priority, ship_subsys *ssp)
3056 {
3057         ai_info *aip;
3058
3059         Assert(attacker != NULL);
3060         Assert(attacker->instance != -1);
3061         Assert(Ships[attacker->instance].ai_index != -1);
3062
3063         aip = &Ai_info[Ships[attacker->instance].ai_index];
3064         force_avoid_player_check(attacker, aip);
3065
3066         aip->ok_to_target_timestamp = timestamp(0);             //      Guarantee we can target.
3067
3068 //      if (!strnicmp(Ships[attacker->instance].ship_name, NOX("Kami"), 4)) {
3069 //              aip->ai_flags |= AIF_KAMIKAZE;
3070 //              aip->ai_flags |= AIF_NO_DYNAMIC;
3071 //      }
3072
3073         if (attacker == attacked) {
3074                 Int3();         //      Bogus!  Who tried to get me to attack myself!  Trace out and fix!
3075                 return;
3076         }
3077
3078         //      Only set to chase if a fighter or bomber, otherwise just return.
3079         if (!(Ship_info[Ships[attacker->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
3080 //              nprintf(("AI","Note: AI ship %s refusing to set AI mode to AIM_CHASE\n", Ships[attacker->instance].ship_name));
3081 //              return;
3082                 nprintf(("AI", "AI ship %s is large ship ordered to attack %s\n", Ships[attacker->instance].ship_name, Ships[attacked->instance].ship_name));
3083         }
3084
3085         //      This is how "engage enemy" gets processed
3086         if (attacked == NULL) {
3087                 aip->choose_enemy_timestamp = timestamp(0);
3088                 // nebula safe
3089                 set_target_objnum(aip, find_enemy(attacker-Objects, 99999.9f, 4));
3090         } else {
3091                 // check if we can see atacked in nebula
3092                 if (aip->target_objnum != attacked - Objects) {
3093                         aip->aspect_locked_time = 0.0f;
3094                 }
3095                 set_target_objnum(aip, attacked - Objects);
3096         }
3097
3098         ai_set_goal_maybe_abort_dock(attacker, aip);
3099         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);     //      No dynamic targeting for 7 seconds.
3100
3101         if (is_ignore_object(aip, aip->target_objnum)) {
3102                 aip->ignore_objnum = UNUSED_OBJNUM;
3103         }
3104
3105         aip->mode = AIM_CHASE;
3106         aip->submode = SM_ATTACK;       // AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
3107                                                                                 //                                       for AIM_CHASE... it may have been not set correctly here
3108         if (ssp == NULL) {
3109                 set_targeted_subsys(aip, NULL, -1);
3110                 if (aip->target_objnum != -1) {
3111                         //nprintf(("AI", "Unprotecting ship %s\n", Ships[Objects[aip->target_objnum].instance].ship_name));
3112                         Objects[aip->target_objnum].flags &= ~OF_PROTECTED;     //      If ship had been protected, unprotect it.
3113                 }
3114         } else {
3115                 Int3(); //      Not supported yet!
3116         }
3117 }
3118
3119 //      --------------------------------------------------------------------------
3120 //      Set *attacked as object to attack for object *attacker
3121 //      Attack point *rel_pos on object.  This is for supporting attacking subsystems.
3122 void ai_attack_wing(object *attacker, int wingnum, int priority)
3123 {
3124         ai_info *aip;
3125
3126         Assert(attacker != NULL);
3127         Assert(attacker->instance != -1);
3128         Assert(Ships[attacker->instance].ai_index != -1);
3129
3130         aip = &Ai_info[Ships[attacker->instance].ai_index];
3131
3132         aip->enemy_wing = wingnum;
3133         aip->mode = AIM_CHASE;
3134         aip->submode = SM_ATTACK;       // AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
3135                                                                                 //                                       for AIM_CHASE... it may have been not set correctly here
3136
3137         aip->ok_to_target_timestamp = timestamp(0);             //      Guarantee we can target.
3138
3139         int count = Wings[wingnum].current_count;
3140         if (count > 0) {
3141                 int     index;
3142
3143                 index = (int) (frand() * count);
3144
3145                 if (index >= count)
3146                         index = 0;
3147
3148                 set_target_objnum(aip, Ships[Wings[wingnum].ship_index[index]].objnum);
3149
3150                 ai_set_goal_maybe_abort_dock(attacker, aip);
3151                 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);     //      No dynamic targeting for 7 seconds.
3152         }
3153 }
3154
3155 //      --------------------------------------------------------------------------
3156 //      Set *evaded as object for *evader to evade.
3157 void ai_evade_object(object *evader, object *evaded, int priority)
3158 {
3159         ai_info *aip;
3160
3161         Assert(evader != NULL);
3162         Assert(evaded != NULL);
3163         Assert(evader->instance != -1);
3164         Assert(Ships[evader->instance].ai_index != -1);
3165
3166         if (evaded == evader) {
3167                 Int3(); //      Bogus!  Who tried to get me to evade myself!  Trace out and fix!
3168                 return;
3169         }
3170
3171         aip = &Ai_info[Ships[evader->instance].ai_index];
3172
3173         set_target_objnum(aip, evaded - Objects);
3174         aip->mode = AIM_EVADE;
3175
3176 }
3177
3178 //      Ignore some object without changing mode.
3179 void ai_ignore_object(object *ignorer, object *ignored, int priority)
3180 {
3181         ai_info *aip;
3182
3183         Assert(ignorer != NULL);
3184         Assert(ignored != NULL);
3185         Assert(ignorer->instance != -1);
3186         Assert(Ships[ignorer->instance].ai_index != -1);
3187         Assert(ignorer != ignored);
3188
3189         aip = &Ai_info[Ships[ignorer->instance].ai_index];
3190
3191         //      MK, 5/17/98, removing ignoring of wings.
3192         //      It's too confusing.  It often causes mysterious behavior in which fighters unexpectedly refuse to attack anything.
3193 /*      if (Ships[ignored->instance].wingnum > -1) {
3194                 int wingnum, i;
3195
3196                 wingnum = Ships[ignored->instance].wingnum;
3197                 aip->ignore_objnum = -(wingnum+1);
3198                 // set protected bit for each ship in a wing
3199                 //      MK, 4/23/98: Only set for fighters if they are the original "ignored" object
3200                 for (i = 0; i < Wings[wingnum].current_count; i++ ) {
3201                         object  *objp;
3202
3203                         objp = &Objects[Ships[Wings[wingnum].ship_index[i]].objnum];
3204                         if (objp != ignored) {
3205                                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))
3206                                         continue;
3207                         }
3208
3209                         Objects[Ships[Wings[wingnum].ship_index[i]].objnum].flags |= OF_PROTECTED;
3210                 }
3211
3212         } else {
3213         */ {
3214                 aip->ignore_objnum = ignored - Objects;
3215                 aip->ignore_signature = ignored->signature;
3216                 aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3217                 ignored->flags |= OF_PROTECTED;                                 // set protected bit of ignored ship.
3218         }
3219
3220 }
3221
3222 //      Ignore some object without changing mode.
3223 void ai_ignore_wing(object *ignorer, int wingnum, int priority)
3224 {
3225         ai_info *aip;
3226
3227         Assert(ignorer != NULL);
3228         Assert(ignorer->instance != -1);
3229         Assert(Ships[ignorer->instance].ai_index != -1);
3230         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
3231
3232         aip = &Ai_info[Ships[ignorer->instance].ai_index];
3233
3234         aip->ignore_objnum = -(wingnum +1);
3235         aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3236 }
3237
3238
3239 //      Add a path point in the global buffer Path_points.
3240 //      modify_index = index in Path_points at which to store path point.
3241 //      If modify_index == -1, then create a new point.
3242 //      If a new point is created (ie, modify_index == -1), then Ppfp is updated.
3243 void add_path_point(vector *pos, int path_num, int path_index, int modify_index)
3244 {
3245         pnode   *pnp;
3246
3247         if (modify_index == -1) {
3248                 Assert(Ppfp-Path_points < MAX_PATH_POINTS-1);
3249                 pnp = Ppfp;
3250                 Ppfp++;
3251         } else {
3252                 Assert((modify_index >= 0) && (modify_index < MAX_PATH_POINTS-1));
3253                 pnp = &Path_points[modify_index];
3254         }
3255
3256         pnp->pos = *pos;
3257         pnp->path_num = path_num;
3258         pnp->path_index = path_index;
3259 }
3260
3261 //      Given two points on a sphere, the center of the sphere and the radius, return a
3262 //      point on the vector through the midpoint of the chord on the sphere.
3263 void bisect_chord(vector *p0, vector *p1, vector *centerp, float radius)
3264 {
3265         vector  tvec;
3266         vector  new_pnt;
3267
3268         vm_vec_add(&tvec, p0, p1);
3269         vm_vec_sub2(&tvec, centerp);
3270         vm_vec_sub2(&tvec, centerp);
3271         if (vm_vec_mag_quick(&tvec) < 0.1f) {
3272                 vm_vec_sub(&tvec, p0, p1);
3273                 if (fl_abs(tvec.xyz.x) <= fl_abs(tvec.xyz.z)){
3274                         tvec.xyz.x = -tvec.xyz.z;
3275                 } else {
3276                         tvec.xyz.y = -tvec.xyz.x;
3277                 }
3278         }
3279
3280         vm_vec_normalize(&tvec);
3281         vm_vec_scale(&tvec, radius);
3282         vm_vec_add(&new_pnt, centerp, &tvec);
3283
3284         add_path_point(&new_pnt, -1, -1, -1);
3285 }
3286                         
3287 //      Create a path from the current position to a goal position.
3288 //      The current position is in the current object and the goal position is
3289 //      in the goal object.
3290 //      It is ok to intersect the current object, but not the goal object.
3291 //      This function is useful for creating a path to an initial point near a large
3292 //      object.
3293 //
3294 // input:       subsys_path:    optional param (default 0), indicates this is a path to a subsystem
3295 void create_path_to_point(vector *curpos, vector *goalpos, object *curobjp, object *goalobjp, int subsys_path)
3296 {
3297         //      If can't cast vector to goalpos, then create an intermediate point.
3298         if (pp_collide(curpos, goalpos, goalobjp, curobjp->radius)) {
3299                 vector  tan1;
3300                 float           radius;
3301
3302                 // If this is a path to a subsystem, use SUBSYS_PATH_DIST as the radius for the object you are
3303                 // trying to avoid.  This is needed since subsystem paths extend out to SUBSYS_PATH_DIST, and we
3304                 // want ships to reach their path destination without flying to points that sit on the radius of
3305                 // a small ship
3306                 radius = goalobjp->radius;
3307                 if (subsys_path) {
3308                         if ( SUBSYS_PATH_DIST > goalobjp->radius ) {
3309                                 radius = SUBSYS_PATH_DIST;
3310                         }
3311                 }
3312
3313                 //      The intermediate point is at the intersection of:
3314                 //              tangent to *goalobjp sphere at point *goalpos
3315                 //              tangent to *goalobjp sphere through point *curpos in plane defined by *curpos, *goalpos, goalobjp->pos
3316                 //      Note, there are two tangents through *curpos, unless *curpos is on the
3317                 //      sphere.  The tangent that causes the nearer intersection (to *goalpos) is chosen.
3318                 get_tangent_point(&tan1, curpos, &goalobjp->pos, goalpos, radius);
3319
3320                 //      If we can't reach tan1 from curpos, insert a new point.
3321                 if (pp_collide(&tan1, curpos, goalobjp, curobjp->radius))
3322                         bisect_chord(curpos, &tan1, &goalobjp->pos, radius);
3323
3324                 add_path_point(&tan1, -1, -1, -1);
3325
3326                 //      If we can't reach goalpos from tan1, insert a new point.
3327                 if (pp_collide(goalpos, &tan1, goalobjp, curobjp->radius))
3328                         bisect_chord(goalpos, &tan1, &goalobjp->pos, radius);
3329         }
3330
3331 }
3332
3333 //      Given an object and a model path, globalize the points on the model
3334 //      and copy into the global path list.
3335 //      If pnp != NULL, then modify, in place, the path points.  This is used to create new
3336 //      globalized points when the base object has moved.
3337 // input:       randomize_pnt   => optional parameter (default value -1), add random vector in sphere to this path point
3338 void copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt)
3339 {
3340         matrix  m;
3341         int             i;
3342         vector  v1;
3343         int             pp_index;               //      index in Path_points at which to store point, if this is a modify-in-place (pnp ! NULL)
3344         int             start_index, finish_index;
3345         
3346         // nprintf(("AI", "Creating path for object %s in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
3347         
3348         //      Initialize pp_index.
3349         //      If pnp == NULL, that means we're creating new points.  If not NULL, then modify in place.
3350         if (pnp == NULL)
3351                 pp_index = -1;                  //      This tells add_path_point to create a new point.
3352         else
3353                 pp_index = 0;                   //      pp_index will get assigned to index in Path_points to reuse.
3354
3355         vm_copy_transpose_matrix(&m, &objp->orient);
3356
3357         if (dir == 1) {
3358                 start_index = 0;
3359                 finish_index = min(count, mp->nverts);
3360         } else {
3361                 Assert(dir == -1);      //      direction must be up by 1 or down by 1 and it's neither!
3362                 start_index = mp->nverts-1;
3363                 finish_index = max(-1, mp->nverts-1-count);
3364         }
3365
3366         int offset = 0;
3367         for (i=start_index; i != finish_index; i += dir) {
3368                 //      Globalize the point.
3369                 vm_vec_rotate(&v1, &mp->verts[i].pos, &m);
3370                 vm_vec_add2(&v1, &objp->pos);
3371
3372                 if ( randomize_pnt == i ) {
3373                         vector v_rand;
3374                         static_randvec(OBJ_INDEX(objp), &v_rand);
3375                         vm_vec_scale(&v_rand, 30.0f);
3376                         vm_vec_add2(&v1, &v_rand);
3377                 }
3378
3379                 if (pp_index != -1)
3380                         pp_index = pnp-Path_points + offset;
3381
3382                 add_path_point(&v1, path_num, i, pp_index);
3383                 offset++;
3384         }
3385 }
3386
3387
3388 //      For pl_objp, create a path along path path_num into mobjp.
3389 //      The tricky part of this problem is creating the entry to the first point on the
3390 //      predefined path.  The points on this entry path are based on the location of Pl_objp
3391 //      relative to the start of the path.
3392 //
3393 // input:
3394 //                              subsys_path:    optional param (default 0), indicating this is a path to a subsystem
3395 void create_model_path(object *pl_objp, object *mobjp, int path_num, int subsys_path)
3396 {       
3397         ship                    *shipp = &Ships[pl_objp->instance];
3398         ai_info         *aip = &Ai_info[shipp->ai_index];
3399
3400         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3401         polymodel       *pm = model_get(osip->modelnum);
3402         int                     num_points;
3403         model_path      *mp;
3404         pnode                   *ppfp_start = Ppfp;
3405         matrix          m;
3406         vector          gp0;
3407
3408         Assert(path_num >= 0);
3409
3410         //      Do garbage collection if necessary.
3411         if (Ppfp-Path_points + 64 > MAX_PATH_POINTS) {
3412                 garbage_collect_path_points();
3413                 ppfp_start = Ppfp;
3414         }
3415
3416         aip->path_start = Ppfp - Path_points;
3417         Assert(path_num < pm->n_paths);
3418         
3419         mp = &pm->paths[path_num];
3420         num_points = mp->nverts;
3421
3422         Assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
3423
3424         vm_copy_transpose_matrix(&m, &mobjp->orient);
3425         vm_vec_rotate(&gp0, &mp->verts[0].pos, &m);
3426         vm_vec_add2(&gp0, &mobjp->pos);
3427
3428         if (pp_collide(&pl_objp->pos, &gp0, mobjp, pl_objp->radius)) {
3429                 vector  perim_point1;
3430                 vector  perim_point2;
3431
3432                 perim_point2 = pl_objp->pos;
3433                 
3434                 //      If object that wants to dock is inside bounding sphere of object it wants to dock with, make it fly out.
3435                 //      Assume it can fly "straight" out to the bounding sphere.
3436                 if (vm_vec_dist_quick(&pl_objp->pos, &mobjp->pos) < mobjp->radius) {
3437                         project_point_to_perimeter(&perim_point2, &mobjp->pos, mobjp->radius, &pl_objp->pos);
3438                         add_path_point(&perim_point2, path_num, -1, -1);
3439                 }
3440
3441                 //      If last point on pre-defined path is inside bounding sphere, create a new point on the surface of the sphere.
3442                 if (vm_vec_dist_quick(&mobjp->pos, &gp0) < mobjp->radius) {
3443                         project_point_to_perimeter(&perim_point1, &mobjp->pos, mobjp->radius, &gp0);
3444                         create_path_to_point(&perim_point2, &perim_point1, pl_objp, mobjp, subsys_path);
3445                         add_path_point(&perim_point1, path_num, -1, -1);
3446                 } else {                //      The predefined path extends outside the sphere.  Create path to that point.
3447                         create_path_to_point(&perim_point2, &gp0, pl_objp, mobjp, subsys_path);
3448                 }
3449         }
3450
3451         // AL 12-31-97: If following a subsystem path, add random vector to second last path point
3452         if ( subsys_path ) {
3453                 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL, mp->nverts-2);
3454         } else {
3455                 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL);
3456         }
3457
3458         aip->path_cur = aip->path_start;
3459         aip->path_dir = PD_FORWARD;
3460         aip->path_objnum = mobjp-Objects;
3461         aip->mp_index = path_num;
3462         aip->path_length = Ppfp - ppfp_start;
3463         aip->path_next_check_time = timestamp(1);
3464
3465         aip->path_goal_obj_hash = create_object_hash(&Objects[aip->path_objnum]);
3466
3467         aip->path_next_create_time = timestamp(1000);   //      OK to try to create one second later
3468         aip->path_create_pos = pl_objp->pos;
3469         aip->path_create_orient = pl_objp->orient;
3470
3471         aip->ai_flags &= ~AIF_USE_EXIT_PATH;                    // ensure this flag is cleared
3472 }
3473
3474 //      For pl_objp, create a path along path path_num into mobjp.
3475 //      The tricky part of this problem is creating the entry to the first point on the
3476 //      predefined path.  The points on this entry path are based on the location of pl_objp
3477 //      relative to the start of the path.
3478 void create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count)
3479 {       
3480         ship                    *shipp = &Ships[pl_objp->instance];
3481         ai_info         *aip = &Ai_info[shipp->ai_index];
3482
3483         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3484         polymodel       *pm = model_get(osip->modelnum);
3485         int                     num_points;
3486         model_path      *mp;
3487         pnode                   *ppfp_start = Ppfp;
3488
3489         aip->path_start = Ppfp - Path_points;
3490         Assert(path_num < pm->n_paths);
3491         
3492         mp = &pm->paths[path_num];
3493         num_points = mp->nverts;
3494
3495         Assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
3496
3497         copy_xlate_model_path_points(mobjp, mp, -1, count, path_num, NULL);
3498
3499         aip->path_cur = aip->path_start;
3500         aip->path_dir = PD_FORWARD;
3501         aip->path_objnum = mobjp-Objects;
3502         aip->mp_index = path_num;
3503         aip->path_length = Ppfp - ppfp_start;
3504         aip->path_next_check_time = timestamp(1);
3505
3506         aip->ai_flags |= AIF_USE_EXIT_PATH;             // mark as exit path, referenced in maybe
3507 }
3508
3509 //      Return true if the vector from curpos to goalpos intersects with any ship other than the ignore objects.
3510 //      Calls pp_collide
3511 int pp_collide_any(vector *curpos, vector *goalpos, float radius, object *ignore_objp1, object *ignore_objp2, int big_only_flag)
3512 {
3513         ship_obj        *so;    
3514
3515         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
3516                 object *objp = &Objects[so->objnum];
3517
3518                 if (big_only_flag) {
3519                         if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
3520                                 continue;
3521                 }
3522
3523                 if ((objp != ignore_objp1) && (objp != ignore_objp2)) {
3524                         if (pp_collide(curpos, goalpos, objp, radius))
3525                                 return OBJ_INDEX(objp);
3526                 }
3527         }
3528
3529         return -1;
3530 }
3531
3532 //      Used to create docking paths and other pre-defined paths through ships.
3533 //      Creates a path in absolute space.
3534 //      Create a path into the object objnum.
3535 //
3536 // input:
3537 //      pl_objp:                        object that will use the path
3538 //      objnum:                 Object to find path to.
3539 //      path_num:               model path index to use
3540 //      exit_flag:              true means this is an exit path in the model
3541 // subsys_path: optional param (default 0) that indicates this is a path to a subsystem
3542 //      Exit:
3543 //      ai_info struct in Pl_objp gets stuffed with information to enable Pl_objp to fly the path.
3544 void ai_find_path(object *pl_objp, int objnum, int path_num, int exit_flag, int subsys_path)
3545 {
3546         ai_info *aip = &Ai_info[Ships[pl_objp->instance].ai_index];
3547
3548         Assert(path_num >= 0);
3549
3550         //      This is test code, find an object with paths.
3551         if (objnum != -1) {
3552                 object  *objp = &Objects[objnum];
3553
3554                 if (objp->type == OBJ_SHIP) {
3555                         polymodel *pm;
3556
3557                         ship    *shipp = &Ships[objp->instance];
3558                         pm = model_get( shipp->modelnum );
3559                         Assert(pm->n_paths > path_num);
3560                         aip->goal_objnum = objp-Objects;
3561                         aip->goal_signature = objp->signature;
3562                         if (exit_flag)
3563                                 create_model_exit_path(pl_objp, objp, path_num);
3564                         else
3565                                 create_model_path(pl_objp, objp, path_num, subsys_path);
3566                         return;
3567                 }
3568
3569         }
3570 }
3571
3572 extern int vector_object_collision(vector *start_pos, vector *end_pos, object *objp, float radius_scale);
3573
3574 //      Maybe make *objp avoid a player object.
3575 //      For now, 4/6/98, only check Player_obj.
3576 //      If player collision would occur, set AIF_AVOIDING_SMALL_SHIP bit in ai_flags.
3577 //      Set aip->avoid_goal_point
3578 int maybe_avoid_player(object *objp, vector *goal_pos)
3579 {
3580         ai_info *aip;
3581         vector  cur_pos, new_goal_pos;
3582         object  *player_objp;
3583         vector  n_vec_to_goal, n_vec_to_player;
3584
3585         aip = &Ai_info[Ships[objp->instance].ai_index];
3586
3587         if (!timestamp_elapsed(aip->avoid_check_timestamp))
3588                 return 0;
3589
3590         player_objp = Player_obj;
3591
3592         float   speed_time;
3593
3594         //      How far two ships could be apart and still collide within one second.
3595         speed_time = player_objp->phys_info.speed + objp->phys_info.speed;
3596
3597         float   obj_obj_dist;
3598
3599         obj_obj_dist = vm_vec_dist_quick(&player_objp->pos, &objp->pos);
3600
3601         if (obj_obj_dist > speed_time*2.0f)
3602                 return 0;
3603
3604         cur_pos = objp->pos;
3605
3606         new_goal_pos = *goal_pos;
3607
3608         float dist = vm_vec_normalized_dir(&n_vec_to_goal, goal_pos, &objp->pos);
3609         vm_vec_normalized_dir(&n_vec_to_player, &player_objp->pos, &objp->pos);
3610
3611         if (dist > speed_time*2.0f) {
3612                 vm_vec_scale_add(&new_goal_pos, &objp->pos, &n_vec_to_goal, 200.0f);
3613         }
3614
3615         if (vector_object_collision(&objp->pos, &new_goal_pos, player_objp, 1.5f)) {
3616                 aip->ai_flags |= AIF_AVOIDING_SMALL_SHIP;
3617
3618                 vector  avoid_vec;
3619
3620                 vm_vec_sub(&avoid_vec, &n_vec_to_goal, &n_vec_to_player);
3621                 if (vm_vec_mag_quick(&avoid_vec) < 0.01f) {
3622                         vm_vec_copy_scale(&avoid_vec, &objp->orient.v.rvec, frand()-0.5f);
3623                         vm_vec_scale_add2(&avoid_vec, &objp->orient.v.uvec, frand()-0.5f);
3624                         vm_vec_normalize(&avoid_vec);
3625                 } else {
3626                         vector  tvec1;
3627                         vm_vec_normalize(&avoid_vec);
3628                         vm_vec_crossprod(&tvec1, &n_vec_to_goal, &avoid_vec);
3629                         vm_vec_crossprod(&avoid_vec, &tvec1, &n_vec_to_player);
3630                 }
3631
3632                 //      Now, avoid_vec is a vector perpendicular to the vector to the player and the direction *objp
3633                 //      should fly in to avoid the player while still approaching its goal.
3634                 vm_vec_scale_add(&aip->avoid_goal_point, &player_objp->pos, &avoid_vec, 400.0f);
3635
3636                 aip->avoid_check_timestamp = timestamp(1000);
3637
3638                 return 1;
3639         } else {
3640                 aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
3641                 aip->avoid_check_timestamp = timestamp((int) (obj_obj_dist/200.0f) + 500);
3642
3643                 return 0;
3644         }
3645 }
3646
3647 //      Make object *still_objp enter AIM_STILL mode.
3648 //      Make it point at view_pos.
3649 void ai_stay_still(object *still_objp, vector *view_pos)
3650 {
3651         ship    *shipp;
3652         ai_info *aip;
3653
3654         Assert(still_objp->type == OBJ_SHIP);
3655         Assert((still_objp->instance >= 0) && (still_objp->instance < MAX_OBJECTS));
3656
3657         shipp = &Ships[still_objp->instance];
3658         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
3659
3660         aip = &Ai_info[shipp->ai_index];
3661
3662         aip->mode = AIM_STILL;
3663
3664         //      If view_pos not NULL, point at that point.  Else, point at a point directly in front of ship.  Ie, don't turn.
3665         if (view_pos != NULL)
3666                 aip->goal_point = *view_pos;
3667         else
3668                 vm_vec_scale_add(&aip->goal_point, &still_objp->pos, &still_objp->orient.v.fvec, 100.0f);
3669 }
3670
3671 // code which is called from ai_dock_with_object and ai_dock to set flags and apprioriate variable
3672 // when two objects have completed docking.  used because we can dock object initially at misison load
3673 // time (meaning that ai_dock() might never get called).  docker has docked with dockee (i.e. docker
3674 // would be a freighter and dockee would be a cargo).
3675 void ai_do_objects_docked_stuff(object *docker, object *dockee)
3676 {
3677         ai_info *aip, *other_aip;
3678
3679         aip = &Ai_info[Ships[docker->instance].ai_index];
3680         other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3681
3682         // set the flags and dock_objnum for both objects
3683         aip->ai_flags |= AIF_DOCKED;
3684         aip->dock_objnum = OBJ_INDEX(dockee);
3685         other_aip->ai_flags |= AIF_DOCKED;
3686         other_aip->dock_objnum = OBJ_INDEX(docker);
3687         aip->dock_signature = dockee->signature;
3688         other_aip->dock_signature = docker->signature;
3689
3690         // add multiplayer hook here to deal with docked objects.  We need to only send information
3691         // about the object that is docking.  Both flags will get updated.
3692         if ( MULTIPLAYER_MASTER )
3693                 send_ai_info_update_packet( docker, AI_UPDATE_DOCK );
3694
3695 }
3696
3697 // code which is called when objects become undocked. Equivalent of above function.
3698 // dockee might not be valid since this code can get called to cleanup after a ship
3699 // has blown up!
3700 void ai_do_objects_undocked_stuff( object *docker, object *dockee )
3701 {
3702         ai_info *aip, *other_aip;
3703
3704         // add multiplayer hook here to deal with undocked objects.  Do it before we
3705         // do anything else.  We don't need to send info for both objects, since we can find
3706         // it be dock_objnum
3707         if ( MULTIPLAYER_MASTER )
3708                 send_ai_info_update_packet( docker, AI_UPDATE_UNDOCK );
3709
3710         aip = &Ai_info[Ships[docker->instance].ai_index];
3711
3712         // set the flags and dock_objnum for both objects
3713         aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3714         aip->dock_objnum = -1;
3715         
3716         if ( dockee != NULL ) {
3717                 other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3718                 other_aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3719                 other_aip->dock_objnum = -1;
3720         }
3721
3722 }
3723
3724
3725 //      --------------------------------------------------------------------------
3726 //      Interface from goals code to AI.
3727 //      Cause *docker to dock with *dockee.
3728 //      priority is priority of goal from goals code.
3729 //      dock_type is:
3730 //              AIDO_DOCK               set goal of docking
3731 //              AIDO_DOCK_NOW   immediately dock, used for ships that need to be docked at mission start
3732 //              AIDO_UNDOCK             set goal of undocking
3733 void ai_dock_with_object(object *docker, object *dockee, int priority, int dock_type, int docker_index, int dockee_index)
3734 {
3735         ai_info         *aip;
3736         polymodel       *pm;
3737         ai_info         *dockee_aip;
3738
3739         Assert(docker != NULL);
3740         Assert(dockee != NULL);
3741         Assert(docker->instance != -1);
3742         Assert(Ships[docker->instance].ai_index != -1);
3743         Assert(Ships[dockee->instance].ai_index != -1);
3744         Assert( docker_index != -1 );
3745         Assert( dockee_index != -1 );
3746
3747         aip = &Ai_info[Ships[docker->instance].ai_index];
3748
3749         if ((aip->ai_flags & AIF_DOCKED) && (dock_type == AIDO_DOCK)) {
3750                 object  *dockee2;
3751                 int             docker_index2, dockee_index2;
3752
3753                 Assert(aip->dock_objnum > -1);
3754                 dockee2 = &Objects[aip->dock_objnum];
3755                 docker_index2 = aip->dock_index;
3756                 dockee_index2 = aip->dockee_index;
3757                 // MWA -- 2/9/98.  use the goal code to undock the ships since goals might need to get removed
3758                 // and that code will do it properly.  I'd actually be surprised if we got into this code anymore
3759                 // since the outer layer goal code should deal with this issue....but who knows...
3760                 ai_add_goal_ship_internal( aip, AI_GOAL_UNDOCK, NULL, -1, -1, 0 );
3761
3762                 // old code below
3763                 //ai_dock_with_object(docker, dockee2, priority, AIDO_UNDOCK, docker_index2, dockee_index2);
3764                 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));
3765                 nprintf(("AI", "...so ship %s will now undock.\n", Ships[docker->instance].ship_name));
3766                 return;
3767         }
3768
3769         dockee_aip = &Ai_info[Ships[dockee->instance].ai_index];
3770
3771         aip->goal_objnum = dockee - Objects;
3772         aip->goal_signature = dockee->signature;
3773
3774         aip->mode = AIM_DOCK;
3775
3776         switch (dock_type) {
3777         case AIDO_DOCK:
3778                 aip->submode = AIS_DOCK_0;
3779                 break;
3780         case AIDO_DOCK_NOW:
3781                 aip->submode = AIS_DOCK_3A;
3782                 break;
3783         case AIDO_UNDOCK:
3784                 aip->submode = AIS_UNDOCK_0;
3785                 break;
3786         default:
3787                 Int3();         //      Bogus dock_type.
3788         }
3789
3790         aip->submode_start_time = Missiontime;
3791         aip->dock_index = docker_index;
3792         aip->dockee_index = dockee_index;
3793
3794         dockee_aip->dock_index = dockee_index;
3795         dockee_aip->dockee_index = docker_index;
3796
3797         // get the path number to the docking point on the dockee.  Each docking point contains a list
3798         // of paths that the point can be reached by.  Pick the first path in the path list for now.
3799         // We only want to do this stuff if we are docking!!!  Be sure to set the path index
3800         if ((dock_type == AIDO_DOCK) || (dock_type == AIDO_DOCK_NOW)) {
3801                 pm = model_get( Ships[dockee->instance].modelnum );
3802                 Assert( pm->docking_bays[dockee_index].num_spline_paths > 0 );
3803
3804                 // only set the dock path index if we are docking.  undocking will assume that dock_path_index
3805                 // already set from some other docking command
3806                 aip->dock_path_index = dockee_index;
3807                 dockee_aip->dock_path_index = docker_index;
3808         }
3809
3810         if (dock_type != AIDO_DOCK_NOW) {
3811                 int path_num;
3812                 //      Note: Second parameter is dock path index.  This should be specified as an
3813                 //      _input_ to this function and passed through.  The path index should be already
3814                 // set for the undock function
3815                 path_num = ai_return_path_num_from_dockbay(dockee, dockee_index);
3816                 ai_find_path(docker, dockee-Objects, path_num, 0);
3817 //              ai_find_path(dockee-Objects, dockee_index, 0);
3818         } else {
3819                 dock_orient_and_approach(docker, dockee, DOA_DOCK_STAY);
3820                 //aip->dock_objnum = OBJ_INDEX(dockee);
3821                 ai_do_objects_docked_stuff( docker, dockee );
3822         }
3823
3824 }
3825
3826 //      Cause a ship to fly its waypoints.
3827 //      flags tells:
3828 //              WPF_REPEAT      Set -> repeat waypoints.
3829 void ai_start_waypoints(object *objp, int waypoint_list_index, int wp_flags)
3830 {
3831         ai_info *aip;
3832
3833         Assert(waypoint_list_index < Num_waypoint_lists);
3834
3835         //nprintf(("AI", "Frame %i: Ship %s instructed to fly waypoint list #%i\n", AI_FrameCount, Ships[objp->instance].ship_name, waypoint_list_index));
3836         aip = &Ai_info[Ships[objp->instance].ai_index];
3837
3838         if ( (aip->mode == AIM_WAYPOINTS) && (aip->wp_index == waypoint_list_index) )
3839                 return;
3840
3841         aip->ai_flags |= AIF_FORMATION_WING;
3842         aip->ai_flags &= ~AIF_FORMATION_OBJECT;
3843         aip->wp_list = waypoint_list_index;
3844         aip->wp_index = 0;
3845         aip->wp_flags = wp_flags;
3846         aip->mode = AIM_WAYPOINTS;
3847
3848         Assert(aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC);
3849 }
3850
3851 //      Make *objp stay within dist units of *other_objp
3852 void ai_do_stay_near(object *objp, object *other_objp, float dist)
3853 {
3854         ai_info *aip;
3855
3856         Assert(objp != other_objp);             //      Bogus!  Told to stay near self.
3857         Assert(objp->type == OBJ_SHIP);
3858         Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
3859
3860         aip = &Ai_info[Ships[objp->instance].ai_index];
3861
3862         aip->mode = AIM_STAY_NEAR;
3863         aip->submode = -1;
3864         aip->stay_near_distance = dist;
3865         aip->goal_objnum = other_objp-Objects;
3866         aip->goal_signature = other_objp->signature;
3867
3868 }
3869
3870 //      Make object *objp form on wing of object *goal_objp
3871 void ai_form_on_wing(object *objp, object *goal_objp)
3872 {
3873         ai_info *aip;
3874         ship                    *shipp;
3875         ship_info       *sip;
3876
3877         // objp == goal_objp sometimes in multiplayer when someone leaves a game -- make a simple
3878         // out for this case.
3879         if ( Game_mode & GM_MULTIPLAYER ) {
3880                 if ( objp == goal_objp ) {
3881                         return;
3882                 }
3883         }
3884
3885         Assert(objp != goal_objp);              //      Bogus!  Told to form on own's wing!
3886
3887         shipp = &Ships[objp->instance];
3888         sip = &Ship_info[shipp->ship_info_index];
3889
3890         //      Only fighters or bombers allowed to form on wing.
3891         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
3892                 nprintf(("AI", "Warning: Ship %s tried to form on player's wing, but not fighter or bomber.\n", shipp->ship_name));
3893                 return;
3894         }
3895
3896         aip = &Ai_info[Ships[objp->instance].ai_index];
3897
3898         aip->ai_flags &= ~AIF_FORMATION_WING;
3899         aip->ai_flags |= AIF_FORMATION_OBJECT;
3900
3901         aip->goal_objnum = goal_objp-Objects;
3902         ai_set_goal_maybe_abort_dock(objp, aip);
3903         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME*4);           //      Super extra long time until can target another ship.
3904
3905 }
3906
3907 //      Given an object and an object on whose wing to form, return slot to use.
3908 //      Optimize:
3909 //              This function is called per object in formation per frame.  Should store slot in ai_info struct.
3910 int ai_formation_object_get_slotnum(int objnum, object *objp)
3911 {
3912         int     slotnum = 1;                    //      Note: Slot #0 means leader, which isn't someone who was told to form-on-wing.
3913         object *o;
3914
3915         for ( o = GET_FIRST(&obj_used_list); o != END_OF_LIST(&obj_used_list); o = GET_NEXT(o) ) {
3916                 if (objp == o)
3917                         break;
3918                 else if (o->type == OBJ_SHIP)
3919                         if (Ai_info[Ships[o->instance].ai_index].ai_flags & AIF_FORMATION_OBJECT)
3920                                 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == objnum)
3921                                         slotnum++;
3922         }
3923
3924         Assert(o != END_OF_LIST(&obj_used_list));       //      Didn't find objp in list of used ships.  Impossible!
3925
3926         return slotnum;
3927 }
3928
3929 #define BIGNUM  100000.0f
3930
3931 int Debug_k = 0;
3932
3933 //      Given an attacker's position and a target's position and velocity, compute the time of
3934 //      intersection of a weapon fired by the attacker with speed weapon_speed.
3935 //      Return this value.  Return value of 0.0f means no collision is possible.
3936 float compute_collision_time(vector *targpos, vector *targvel, vector *attackpos, float weapon_speed)
3937 {
3938         vector  vec_to_target;
3939         float           pos_dot_vel;
3940         float           vel_sqr;
3941         float           discrim;
3942
3943         vm_vec_sub(&vec_to_target, targpos, attackpos);
3944         pos_dot_vel = vm_vec_dot(&vec_to_target, targvel);
3945         vel_sqr = vm_vec_dot(targvel, targvel) - weapon_speed*weapon_speed;
3946         discrim = pos_dot_vel*pos_dot_vel - vel_sqr*vm_vec_dot(&vec_to_target, &vec_to_target);
3947
3948         if (discrim > 0.0f) {
3949                 float   t1, t2, t_solve;
3950
3951                 t1 = (-pos_dot_vel + fl_sqrt(discrim)) / vel_sqr;
3952                 t2 = (-pos_dot_vel - fl_sqrt(discrim)) / vel_sqr;
3953
3954                 t_solve = BIGNUM;
3955
3956                 if (t1 > 0.0f)
3957                         t_solve = t1;
3958                 if ((t2 > 0.0f) && (t2 < t_solve))
3959                         t_solve = t2;
3960
3961                 if (t_solve < BIGNUM-1.0f) {
3962                         return t_solve + Debug_k * flFrametime;
3963                 }
3964         }
3965
3966         return 0.0f;
3967 }
3968
3969
3970 //      --------------------------------------------------------------------------
3971 //      If far away, use player's speed.
3972 //      If in between, lerp between player and laser speed
3973 //      If close, use laser speed.
3974 // Want to know how much time it will take to get to the enemy.
3975 // This function doesn't account for the fact that by the time the player
3976 // (or his laser) gets to the current enemy position, the enemy will have moved.
3977 // This is dealt with in polish_predicted_enemy_pos.
3978 float compute_time_to_enemy(float dist_to_enemy, object *pobjp, object *eobjp)
3979 {
3980         float   time_to_enemy;
3981         float   pl_speed = pobjp->phys_info.speed;
3982         float   max_laser_distance, max_laser_speed;
3983         int     bank_num, weapon_num;
3984         ship    *shipp = &Ships[pobjp->instance];
3985
3986         bank_num = shipp->weapons.current_primary_bank;
3987         weapon_num = shipp->weapons.primary_bank_weapons[bank_num];
3988         max_laser_speed = Weapon_info[weapon_num].max_speed;
3989         max_laser_distance = max_laser_speed * Weapon_info[weapon_num].lifetime;
3990
3991         //      If pretty far away, use player's speed to predict position, else
3992         //      use laser's speed because when close, we care more about hitting
3993         //      with a laser than about causing ship:ship rendezvous.
3994         if (dist_to_enemy > 1.5 * max_laser_distance) {
3995                 if (pl_speed > 0.0f)
3996                         time_to_enemy = dist_to_enemy/pl_speed;
3997                 else
3998                         time_to_enemy = 1.0f;
3999         } else if (dist_to_enemy > 1.1*max_laser_distance) {
4000                 if (pl_speed > 0.1f) {
4001                         float   scale;
4002
4003                         scale = (float) ((dist_to_enemy - max_laser_distance) / max_laser_distance);
4004                 
4005                         time_to_enemy = (float) (dist_to_enemy/(pl_speed * scale + max_laser_speed * (1.0f - scale)));
4006                 } else
4007                         time_to_enemy = 2.0f;
4008         } else
4009                 time_to_enemy = (float) (dist_to_enemy/max_laser_speed);
4010
4011         // return time_to_enemy * (1.0f + Ai_info[Ships[pobjp->instance].ai_index].lead_scale);
4012         return time_to_enemy + flFrametime;
4013 }
4014
4015 //      Stuff *dot and *tts.
4016 //      *dot is always computed.  If dot is less than zero, the magnitude is
4017 //      incorrect, not having been divided by distance.
4018 //      If *dot is > 0.0f, then tts is computed.  This is the time it will take object
4019 //      *objp to get to *pos, assuming it moves right at it.
4020 void fds_aux(float *dot, float *tts, vector *pos, float dtime, object *objp)
4021 {
4022         vector  v2s;
4023
4024         vm_vec_sub(&v2s, pos, &objp->pos);
4025         *dot = vm_vec_dot(&v2s, &objp->orient.v.fvec);
4026
4027         if (*dot > 0.0f) {
4028                 float   dist;
4029
4030                 dist = vm_vec_dist(&objp->pos, pos);
4031
4032                 if (dist > 0.1f)
4033                         *dot /= dist;
4034                 else
4035                         *dot = 1.0f;
4036
4037                 if (objp->phys_info.speed > 0.1f)
4038                         *tts = dist / objp->phys_info.speed;
4039                 else
4040                         *tts = dist * 100.0f;
4041         }
4042 }
4043
4044 /*
4045 //      Return index of weapon that could hit object *sobjp within dtime seconds.
4046 //      Actual time until impact returned in *atime.
4047 int find_danger_weapon(object *sobjp, float dtime, float *atime, float dot_threshhold)
4048 {
4049         object  *objp, *best_objp = NULL;
4050         float           best_tts = 1000.0f;
4051
4052         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
4053                 if ((objp->type == OBJ_WEAPON) && (sobjp-Objects != objp->parent)) {
4054                         float           dot, tts;
4055                         // vector       psp;            //      Predicted ship position.
4056
4057                         //      Get dot and time to current ship position.
4058                         fds_aux(&dot, &tts, &sobjp->pos, dtime, objp);
4059
4060                         //      If dot and tts are in plausible range, do more expensive stuff.
4061                         if (dot > 0.98f) {
4062 //                              float   dot_from_sobjp;
4063                                 vector  v2e;
4064
4065                                 vm_vec_normalized_dir(&v2e, &objp->pos, &sobjp->pos);
4066 //                              dot_from_sobjp = vm_vec_dot(&sobjp->orient.v.fvec, &v2e);
4067 //                              if (dot_from_sobjp >= dot_threshhold)
4068                                         if (tts < dtime) {
4069                                                 if (tts < best_tts) {
4070                                                         best_tts = tts;
4071                                                         best_objp = objp;
4072                                                 }
4073                                         }
4074                         }
4075                 }
4076         }
4077
4078         *atime = best_tts;
4079
4080         if (best_objp != NULL)
4081                 return best_objp-Objects;
4082         else
4083                 return -1;
4084 }
4085 */
4086
4087 //      --------------------------------------------------------------------------
4088 void ai_set_positions(object *pl_objp, object *en_objp, ai_info *aip, vector *player_pos, vector *enemy_pos)
4089 {
4090         *player_pos = pl_objp->pos;
4091
4092         if (aip->next_predict_pos_time > Missiontime) {
4093                 *enemy_pos = aip->last_predicted_enemy_pos;
4094         } else {
4095                 *enemy_pos = en_objp->pos;
4096
4097                 aip->next_predict_pos_time = Missiontime + Skill_level_delay[Game_skill_level];
4098                 aip->last_predicted_enemy_pos = *enemy_pos;
4099         }
4100
4101
4102 }
4103
4104 //      --------------------------------------------------------------------------
4105 int find_nearest_waypoint(object *objp)
4106 {
4107         int     i;
4108         float   dist, min_dist, dot;
4109         int     min_ind;
4110         ship    *shipp;
4111         int     wp_listnum;
4112         waypoint_list   *wpl;
4113
4114         shipp = &Ships[objp->instance];
4115         wp_listnum = Ai_info[Ships[objp->instance].ai_index].wp_list;
4116         Assert(wp_listnum > 0);
4117         wpl = &Waypoint_lists[wp_listnum];
4118
4119         min_dist = 999999.0f;
4120         min_ind = -1;
4121
4122         for (i=0; i<wpl->count; i++) {
4123                 dist = vm_vec_dist_quick(&objp->pos, &wpl->waypoints[i]);
4124                 dot = vm_vec_dot_to_point(&objp->orient.v.fvec, &objp->pos, &wpl->waypoints[i]);
4125                 dist = (float) (dist * (1.25 - dot));
4126                 if (dist < min_dist) {
4127                         min_dist = dist;
4128                         min_ind = i;
4129                 }
4130         }
4131
4132         Assert(min_ind != -1);
4133
4134         return min_ind;
4135 }
4136
4137 //      Given an ai_info struct, by reading current goal and path information,
4138 //      extract base path information and return in pmp and pmpv.
4139 //      Return true if found, else return false.
4140 //      false means the current point is not on the original path.
4141 int get_base_path_info(int path_cur, int goal_objnum, model_path **pmp, mp_vert **pmpv)
4142 {
4143         pnode                   *pn = &Path_points[path_cur];
4144         ship_info       *sip = &Ship_info[Ships[Objects[goal_objnum].instance].ship_info_index];
4145         polymodel       *pm = model_get(sip->modelnum);
4146         //static        int     debug_last_index = -1;  // no longer used
4147         *pmpv = NULL;
4148         *pmp = NULL;
4149
4150         if (pn->path_num != -1) {
4151                 *pmp = &pm->paths[pn->path_num];
4152                 if (pn->path_index != -1)
4153                         *pmpv = &(*pmp)->verts[pn->path_index];
4154                 else
4155                         return 0;
4156         } else
4157                 return 0;
4158
4159 /*      if (debug_last_index != *pmpv-(*pmp)->verts) {
4160                 debug_last_index = *pmpv-(*pmp)->verts;
4161                 nprintf(("AI", "Point %i has %i turrets: ", *pmpv-(*pmp)->verts, (*pmpv)->nturrets));
4162                 for (int i=0; i<(*pmpv)->nturrets; i++) {
4163                         nprintf(("AI", "%i ", (*pmpv)->turret_ids[i]));
4164                 }
4165                 nprintf(("AI", "\n"));
4166         }
4167 */
4168         return 1;
4169 }
4170
4171 //      Modify, in place, the points in a global model path.
4172 //      Only modify those points that are defined in the model path.  Don't modify the
4173 //      leadin points, such as those that are necessary to get the model on the path.
4174 void modify_model_path_points(object *objp)
4175 {       
4176         ai_info         *aip = &Ai_info[Ships[objp->instance].ai_index];
4177         object          *mobjp = &Objects[aip->path_objnum];
4178         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
4179         polymodel       *pm = model_get(osip->modelnum);
4180         pnode                   *pnp;
4181         int                     path_num, dir;
4182
4183         Assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
4184
4185         pnp = &Path_points[aip->path_start];
4186         while ((pnp->path_index == -1) && (pnp-Path_points - aip->path_start < aip->path_length))
4187                 pnp++;
4188
4189         path_num = pnp->path_num;
4190         Assert((path_num >= 0) && (path_num < pm->n_paths));
4191         
4192         Assert(pnp->path_index != -1);  //      If this is -1, that means we never found the model path points
4193
4194         dir = 1;
4195         if ( aip->ai_flags & AIF_USE_EXIT_PATH ) {
4196                 dir = -1;
4197         }
4198
4199         copy_xlate_model_path_points(mobjp, &pm->paths[path_num], dir, pm->paths[path_num].nverts, path_num, pnp);
4200 }
4201
4202 //      Return an indication of the distance between two matrices.
4203 //      This is the sum of the distances of their dot products from 1.0f.
4204 float ai_matrix_dist(matrix *mat1, matrix *mat2)
4205 {
4206         float   t;
4207
4208         t =  1.0f - vm_vec_dot(&mat1->v.fvec, &mat2->v.fvec);
4209         t += 1.0f - vm_vec_dot(&mat1->v.uvec, &mat2->v.uvec);
4210         t += 1.0f - vm_vec_dot(&mat1->v.rvec, &mat2->v.rvec);
4211
4212         return t;
4213 }
4214
4215
4216 //      Paths are created in absolute space, so a moving object needs to have model paths within it recreated.
4217 //      This uses the hash functions which means the slightest movement will cause a recreate, though the timestamp
4218 //      prevents this from happening too often.
4219 //      force_recreate_flag TRUE means to recreate regardless of timestamp.
4220 //      Returns TRUE if path recreated.
4221 float maybe_recreate_path(object *objp, ai_info *aip, int force_recreate_flag)
4222 {
4223         int     hashval;
4224
4225         Assert(&Ai_info[Ships[objp->instance].ai_index] == aip);
4226
4227         if ((aip->mode == AIM_BAY_EMERGE) || (aip->mode == AIM_BAY_DEPART))
4228                 if ((OBJ_INDEX(objp) % 4) == (Framecount % 4))
4229                         force_recreate_flag = 1;
4230
4231         //      If no path, that means we don't need one.
4232         if (aip->path_start == -1)
4233                 return 0.0f;
4234
4235         // AL 11-12-97: If AIF_USE_STATIC_PATH is set, don't try to recreate.  This is needed when ships
4236         //                                  emerge from fighter bays.  We don't need to recreate the path.. and in case the 
4237         //              parent ship dies, we still want to be able to continue on the path
4238         if ( aip->ai_flags & AIF_USE_STATIC_PATH ) 
4239                 return 0.0f;
4240
4241         if (force_recreate_flag || timestamp_elapsed(aip->path_next_create_time)) {
4242                 object  *path_objp;
4243
4244                 path_objp = &Objects[aip->path_objnum];
4245
4246                 if ((hashval = create_object_hash(path_objp)) != aip->path_goal_obj_hash) {
4247                         float dist;
4248                         
4249                         dist = vm_vec_dist_quick(&path_objp->pos, &aip->path_create_pos);
4250                         dist += ai_matrix_dist(&path_objp->orient, &aip->path_create_orient) * 25.0f;
4251
4252                         if (force_recreate_flag || (dist > 2.0f)) {
4253                                 aip->path_next_create_time = timestamp(1000);   //      Update again in as little as 1000 milliseconds, ie 1 second.
4254                                 aip->path_goal_obj_hash = hashval;
4255                                 modify_model_path_points(objp);
4256
4257                                 aip->path_create_pos = path_objp->pos;
4258                                 aip->path_create_orient = path_objp->orient;
4259                                 
4260                                 return dist;
4261                         }
4262                 }
4263         }
4264
4265         return 0.0f;
4266 }
4267
4268 //      Set acceleration for ai_dock().
4269 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)
4270 {
4271         float prev_dot_to_goal = aip->prev_dot_to_goal;
4272         
4273         aip->prev_dot_to_goal = dot;
4274
4275         if (objp->phys_info.speed < 0.0f) {
4276                 accelerate_ship(aip, 1.0f/32.0f);
4277         } else if ((prev_dot_to_goal-dot) > 0.01) {
4278                 if (prev_dot_to_goal > dot + 0.05f) {
4279                         accelerate_ship(aip, 0.0f);
4280                 } else {
4281                         change_acceleration(aip, -1.0f);        //      -1.0f means subtract off flFrametime from acceleration value in 0.0..1.0
4282                 }
4283         } else {
4284                 if ((aip->mode == AIM_DOCK) && (dist_to_next < 150.0f) && (aip->path_start + aip->path_length - 2 == aip->path_cur)) {
4285                         set_accel_for_target_speed(objp, sip->max_speed * max(dist_to_next/500.0f, 1.0f));
4286                         //mprintf(("dist = %7.3f, speed = %7.3f\n", dist_to_next, objp->phys_info.speed));
4287                 } else if ((dot_to_next >= dot * .9) || (dist_to_next > 100.0f)) {
4288                         if (dist_to_goal > 200.0f)
4289                                 set_accel_for_target_speed(objp, sip->max_speed * (dot + 1.0f) / 2.0f);
4290                         else {
4291                                 float   xdot;
4292
4293                                 xdot = (dot_to_next + dot)/2.0f;
4294                                 if (xdot < 0.0f)
4295                                         xdot = 0.0f;
4296
4297                                 // AL: if following a path not in dock mode, move full speed
4298                                 if (( aip->mode != AIM_DOCK ) && (dot > 0.9f)) {
4299                                         set_accel_for_target_speed(objp, sip->max_speed*dot*dot*dot);
4300                                 } else {
4301                                         if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4302                                                 //nprintf(("AI", "Target speed = %7.3f\n", dist_to_goal/8.0f));
4303                                                 set_accel_for_target_speed(objp, dist_to_goal/8.0f + 2.0f);
4304                                         } else {
4305                                                 set_accel_for_target_speed(objp, sip->max_speed * (2*xdot + 0.25f)/4.0f);
4306                                         }
4307                                 }
4308                         }
4309                 } else {
4310                         float   xdot;
4311
4312                         xdot = max(dot_to_next, 0.1f);
4313                         if ( aip->mode != AIM_DOCK ) {
4314                                 set_accel_for_target_speed(objp, sip->max_speed);
4315                         } else {
4316                                 float   speed;
4317                                 if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4318                                         speed = dist_to_goal/8.0f + 2.0f;
4319                                 } else if (dist_to_goal < 4*objp->radius + 50.0f) {
4320                                         speed = dist_to_goal/4.0f + 4.0f;
4321                                 } else {
4322                                         speed = sip->max_speed * (3*xdot + 1.0f)/4.0f;
4323                                 }
4324                                 if (aip->mode == AIM_DOCK) {
4325                                         speed = speed * 2.0f + 1.0f;
4326                                         if (aip->goal_objnum != -1) {
4327                                                 speed += Objects[aip->goal_objnum].phys_info.speed;
4328                                         }
4329                                 }
4330
4331                                 set_accel_for_target_speed(objp, speed);
4332                         }
4333                 }
4334         }
4335 }
4336
4337 //      --------------------------------------------------------------------------
4338 //      Follow a path associated with a large object, such as a capital ship.
4339 //      The points defined on the path are in the object's reference frame.
4340 //      The object of interest is goal_objnum.
4341 //      The paths are defined in the model.  The path of interest is wp_list.
4342 //      The next goal point in the path is wp_index.
4343 //      wp_flags contain special information specific to the path.
4344
4345 // The path vertices are defined by model_path structs:
4346 //              typedef struct model_path {
4347 //                      char            name[MAX_NAME_LEN];                                     // name of the subsystem.  Probably displayed on HUD
4348 //                      int             nverts;
4349 //                      vector  *verts;
4350 //              } model_path;
4351
4352 //      The polymodel struct for the object contains the following:
4353 //              int                     n_paths;
4354 //              model_path      *paths;
4355
4356 //      Returns distance to goal point.
4357 float ai_path()
4358 {
4359         polymodel       *pm;
4360         int             num_paths, num_points;
4361         float           dot, dist_to_goal, dist_to_next, speed, dot_to_next;
4362         ship            *shipp = &Ships[Pl_objp->instance];
4363         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4364         ai_info *aip;
4365         vector  nvel_vec;
4366         float           mag, prev_dot_to_goal;
4367         vector  temp_vec, *slop_vec;
4368         object  *gobjp;
4369         ship            *gshipp;
4370         vector  *cvp, *nvp, next_vec, gcvp, gnvp;               //      current and next vertices in global coordinates.
4371
4372         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4373
4374         Assert(aip->goal_objnum != -1);
4375         Assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
4376
4377         gobjp = &Objects[aip->goal_objnum];
4378         gshipp = &Ships[gobjp->instance];
4379
4380         pm = model_get( gshipp->modelnum );
4381         num_paths = pm->n_paths;
4382         Assert(num_paths > 0);
4383
4384         if (aip->path_start == -1) {
4385                 int path_num;
4386                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
4387                 Assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
4388                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
4389         }
4390
4391         // nprintf(("AI", "Frame: %i, Path index = %i/%i\n", AI_FrameCount, aip->path_cur-aip->path_start, aip->path_length));
4392
4393         maybe_recreate_path(Pl_objp, aip, 0);
4394
4395         num_points = aip->path_length;
4396
4397         //      Set cvp and nvp as pointers to current and next vertices of interest on path.
4398         cvp = &Path_points[aip->path_cur].pos;
4399         if ((aip->path_cur + aip->path_dir - aip->path_start < num_points) || (aip->path_cur + aip->path_dir < aip->path_start))
4400                 nvp = &Path_points[aip->path_cur + aip->path_dir].pos;
4401         else {
4402                 //      If this is 0, then path length must be 1 which means we have no direction!
4403                 Assert((aip->path_cur - aip->path_dir >= aip->path_start) && (aip->path_cur - aip->path_dir - aip->path_start < num_points));
4404                 //      Cleanup for above Assert() which we hit too near release. -- MK, 5/24/98.
4405                 if (aip->path_cur - aip->path_dir - aip->path_start >= num_points) {
4406                         if (aip->path_dir == 1)
4407                                 aip->path_cur = aip->path_start;
4408                         else
4409                                 aip->path_cur = aip->path_start + num_points - 1;
4410                 }
4411
4412                 vector  delvec;
4413                 vm_vec_sub(&delvec, cvp, &Path_points[aip->path_cur - aip->path_dir].pos);
4414                 vm_vec_normalize(&delvec);
4415                 vm_vec_scale_add(&next_vec, cvp, &delvec, 10.0f);
4416                 nvp = &next_vec;
4417         }
4418
4419         //      Interrupt if can't get to current goal point.  Debug only.
4420 /*      if (pp_collide(&Pl_objp->pos, cvp, gobjp, Pl_objp->radius)) {
4421                 Int3();
4422         }
4423 */
4424         //      See if can reach next point (as opposed to current point)
4425         //      However, don't do this if docking and next point is last point.
4426         //      That is, we don't want to pursue the last point under control of the
4427         //      path code.  In docking, this is a special hack.
4428         if ((aip->mode != AIM_DOCK) || ((aip->path_cur-aip->path_start) < num_points - 2)) {
4429                 if ((aip->path_cur + aip->path_dir > aip->path_start) && (aip->path_cur + aip->path_dir < aip->path_start + num_points-2)) {
4430                         if ( timestamp_elapsed(aip->path_next_check_time)) {
4431                                 aip->path_next_check_time = timestamp( 3000 );
4432                                 if (!pp_collide(&Pl_objp->pos, nvp, gobjp, 1.1f * Pl_objp->radius)) {
4433                                         cvp = nvp;
4434                                         aip->path_cur += aip->path_dir;
4435                                         nvp = &Path_points[aip->path_cur].pos;
4436                                         //nprintf(("AI", "Reach: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4437                                 }
4438                         }
4439                 }
4440         }
4441
4442         gcvp = *cvp;
4443         gnvp = *nvp;
4444
4445         speed = Pl_objp->phys_info.speed;
4446
4447         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &gcvp);
4448         dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, &gnvp);
4449         //      Can't use fvec, need to use velocity vector because we aren't necessarily
4450         //      moving in the direction we're facing.
4451
4452 //      if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4453         if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4454                 mag = 0.0f;
4455                 vm_vec_zero(&nvel_vec);
4456         } else
4457                 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4458
4459         //      If moving not-very-slowly and sliding, then try to slide at goal, rather than
4460         //      point at goal.
4461         slop_vec = NULL;
4462         if (mag < 1.0f)
4463                 nvel_vec = Pl_objp->orient.v.fvec;
4464         else if (mag > 5.0f) {
4465                 float   nv_dot;
4466                 nv_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4467                 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4468                         slop_vec = &temp_vec;
4469                         vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.v.fvec);
4470                 }
4471         }
4472
4473         if (dist_to_goal > 0.1f)
4474                 ai_turn_towards_vector(&gcvp, Pl_objp, flFrametime, sip->srotation_time, slop_vec, NULL, 0.0f, 0);
4475
4476         //      Code to control speed is MUCH less forgiving in path following than in waypoint
4477         //      following.  Must be very close to path or might hit objects.
4478         prev_dot_to_goal = aip->prev_dot_to_goal;
4479         dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gcvp);
4480         dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gnvp);
4481
4482         set_accel_for_docking(Pl_objp, aip, dot, dot_to_next, dist_to_next, dist_to_goal, sip);
4483         aip->prev_dot_to_goal = dot;
4484
4485 //mprintf(("Goal index = %i, dist = %7.3f, dot = %7.3f\n", wp_index, dist_to_goal, dot));
4486
4487         //      If moving at a non-tiny velocity, detect attaining path point by its being close to
4488         //      line between previous and current object location.
4489         if ((dist_to_goal < MIN_DIST_TO_WAYPOINT_GOAL) || (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f)) {
4490                 vector  nearest_point;
4491                 float           r, min_dist_to_goal;
4492
4493                 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, &gcvp);
4494
4495                 //      Set min_dist_to_goal = how close must be to waypoint to pick next one.
4496                 //      If docking and this is the second last waypoint, must be very close.
4497                 if ((aip->mode == AIM_DOCK) && (aip->path_cur >= aip->path_length-2))
4498                         min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL;
4499                 else
4500                         min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius;
4501
4502                 if ( (vm_vec_dist_quick(&Pl_objp->pos, &gcvp) < min_dist_to_goal) ||
4503                         ((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, &gcvp) < (MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius))) {
4504                         aip->path_cur += aip->path_dir;
4505                         //nprintf(("AI", " Near: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4506                         if (((aip->path_cur - aip->path_start) > (num_points+1)) || (aip->path_cur < aip->path_start)) {
4507                                 Assert(aip->mode != AIM_DOCK);          //      If docking, should never get this far, getting to last point handled outside ai_path()
4508                                 aip->path_dir = -aip->path_dir;
4509 //                              aip->path_cur += aip->path_dir;
4510                         }
4511                 }
4512         }
4513
4514         return dist_to_goal;
4515 }
4516
4517 void update_min_max(float val, float *min, float *max)
4518 {
4519         if (val < *min)
4520                 *min = val;
4521         else if (val > *max)
4522                 *max = val;
4523 }
4524
4525 //      Stuff bounding box of all enemy objects within "range" units of object *my_objp.
4526 //      Stuff ni min_vec and max_vec.
4527 //      Return value: Number of enemy objects in bounding box.
4528 int get_enemy_team_range(object *my_objp, float range, int enemy_team_mask, vector *min_vec, vector *max_vec)
4529 {
4530         object  *objp;
4531         ship_obj        *so;
4532         int             count = 0;
4533
4534         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4535                 objp = &Objects[so->objnum];
4536                 if (Ships[objp->instance].team & enemy_team_mask) {
4537                         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))
4538                                 if (vm_vec_dist_quick(&my_objp->pos, &objp->pos) < range) {
4539                                         if (count == 0) {
4540                                                 *min_vec = objp->pos;
4541                                                 *max_vec = objp->pos;
4542                                                 count++;
4543                                         } else {
4544                                                 update_min_max(objp->pos.xyz.x, &min_vec->xyz.x, &max_vec->xyz.x);
4545                                                 update_min_max(objp->pos.xyz.y, &min_vec->xyz.y, &max_vec->xyz.y);
4546                                                 update_min_max(objp->pos.xyz.z, &min_vec->xyz.z, &max_vec->xyz.z);
4547                                         }
4548                                 }
4549
4550                 }
4551         }
4552
4553         return count;
4554 }
4555
4556 //      Pick a relatively safe spot for objp to fly to.
4557 //      Problem:
4558 //              Finds a spot away from any enemy within a bounding box.
4559 //              Doesn't verify that "safe spot" is not near some other enemy.
4560 void ai_safety_pick_spot(object *objp)
4561 {
4562         int             objnum;
4563         int             enemy_team_mask;
4564         vector  min_vec, max_vec;
4565         vector  vec_to_center, center;
4566         vector  goal_pos;
4567
4568         objnum = OBJ_INDEX(objp);
4569
4570         enemy_team_mask = get_enemy_team_mask(objnum);
4571
4572         if (get_enemy_team_range(objp, 1000.0f, enemy_team_mask, &min_vec, &max_vec)) {
4573                 vm_vec_avg(&center, &min_vec, &max_vec);
4574                 vm_vec_normalized_dir(&vec_to_center, &center, &objp->pos);
4575
4576                 vm_vec_scale_add(&goal_pos, &center, &vec_to_center, 2000.0f);
4577         } else
4578                 vm_vec_scale_add(&goal_pos, &objp->pos, &objp->orient.v.fvec, 100.0f);
4579
4580         Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pos;
4581 }
4582
4583 //      Fly to desired safe point.
4584 // Returns distance to that point.
4585 float ai_safety_goto_spot(object *objp)
4586 {
4587         float   dot, dist;
4588         ai_info *aip;
4589         vector  vec_to_goal;
4590         ship_info       *sip;
4591         float   dot_val;
4592
4593         sip = &Ship_info[Ships[objp->instance].ship_info_index];
4594
4595         aip = &Ai_info[Ships[objp->instance].ai_index];
4596         dist = vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
4597         dot = vm_vec_dot(&vec_to_goal, &objp->orient.v.fvec);
4598
4599         dot_val = (1.1f + dot) / 2.0f;
4600         if (dist > 200.0f) {
4601                 set_accel_for_target_speed(objp, sip->max_speed * dot_val);
4602         } else
4603                 set_accel_for_target_speed(objp, sip->max_speed * dot_val * (dist/200.0f + 0.2f));
4604
4605         return dist;
4606 }
4607
4608 void ai_safety_circle_spot(object *objp)
4609 {
4610         vector  goal_point;
4611         ship_info       *sip;
4612         float           dot;
4613
4614         sip = &Ship_info[Ships[objp->instance].ship_info_index];
4615
4616         goal_point = Ai_info[Ships[objp->instance].ai_index].goal_point;
4617         dot = turn_towards_tangent(objp, &goal_point, 250.0f);  //      Increased from 50 to 250 to make circling not look so wacky.
4618
4619         set_accel_for_target_speed(objp, 0.5f * (1.0f + dot) * sip->max_speed/4.0f);
4620
4621 //      float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
4622 //      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));
4623
4624 }
4625
4626 //      --------------------------------------------------------------------------
4627 void ai_safety()
4628 {
4629         ai_info *aip;
4630
4631         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4632
4633         switch (aip->submode) {
4634         case AISS_1:
4635                 ai_safety_pick_spot(Pl_objp);
4636                 aip->submode = AISS_2;
4637                 aip->submode_start_time = Missiontime;
4638                 break;
4639         case AISS_1a:   //      Pick a safe point because we just got whacked!
4640                 Int3();
4641                 break;
4642         case AISS_2:
4643                 if (ai_safety_goto_spot(Pl_objp) < 25.0f) {
4644                         aip->submode = AISS_3;
4645                         aip->submode_start_time = Missiontime;
4646                 }
4647                 break;
4648         case AISS_3:
4649                 ai_safety_circle_spot(Pl_objp);
4650                 break;
4651         default:
4652                 Int3();         //      Illegal submode for ai_safety();
4653                 break;
4654         }
4655 }
4656
4657 //      --------------------------------------------------------------------------
4658 //      make Pl_objp fly waypoints.
4659 void ai_waypoints()
4660 {
4661         int             wp_index;
4662         vector  *wp_cur, *wp_next;
4663         float           dot, dist_to_goal, dist_to_next, speed, dot_to_next;
4664         ship            *shipp = &Ships[Pl_objp->instance];
4665         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4666         waypoint_list   *wpl;
4667         ai_info *aip;
4668         vector  nvel_vec;
4669         float           mag;
4670         float           prev_dot_to_goal;
4671         vector  temp_vec;
4672         vector  *slop_vec;
4673
4674         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4675
4676         wp_index = aip->wp_index;
4677
4678         if (wp_index == -1) {
4679                 ai_start_waypoints(Pl_objp, 0, WPF_REPEAT);
4680                 wp_index = aip->wp_index;
4681                 aip->wp_dir = 1;
4682         }
4683
4684         wpl = &Waypoint_lists[Ai_info[Ships[Pl_objp->instance].ai_index].wp_list];
4685
4686         Assert(wpl->count);     // What? Is this zero? Probably wp_index never got initialized!
4687
4688         wp_cur = &wpl->waypoints[wp_index];
4689         wp_next = &wpl->waypoints[(wp_index+1) % wpl->count];
4690         speed = Pl_objp->phys_info.speed;
4691
4692         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, wp_cur);
4693         dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, wp_next);
4694
4695         //      Can't use fvec, need to use velocity vector because we aren't necessarily
4696         //      moving in the direction we're facing.
4697         // AL 23-3-98: Account for very small velocities by checking result of vm_vec_mag().
4698         //                                      If we don't vm_vec_copy_normalize() will think it is normalizing a null vector.
4699 //      if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4700         if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4701                 mag = 0.0f;
4702                 vm_vec_zero(&nvel_vec);
4703         } else {
4704                 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4705         }
4706
4707         //      If moving not-very-slowly and sliding, then try to slide at goal, rather than
4708         //      point at goal.
4709         slop_vec = NULL;
4710         if (mag < 1.0f) {
4711                 nvel_vec = Pl_objp->orient.v.fvec;
4712         } else if (mag > 5.0f) {
4713                 float   nv_dot;
4714                 nv_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4715                 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4716                         slop_vec = &temp_vec;
4717                         vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.v.fvec);
4718                 }
4719         }
4720
4721         //      If a wing leader, take turns more slowly, based on size of wing.
4722         int     scale;
4723
4724         if (Ai_info[Ships[Pl_objp->instance].ai_index].wing >= 0) {
4725                 scale = Wings[Ai_info[Ships[Pl_objp->instance].ai_index].wing].current_count;
4726                 scale = (int) ((scale+1)/2);
4727         } else {
4728                 scale = 1;
4729         }
4730
4731         if (dist_to_goal > 0.1f) {
4732                 ai_turn_towards_vector(wp_cur, Pl_objp, flFrametime, sip->srotation_time*3.0f*scale, slop_vec, NULL, 0.0f, 0);
4733         }
4734
4735         prev_dot_to_goal = aip->prev_dot_to_goal;
4736         dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_cur);
4737         dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_next);
4738         aip->prev_dot_to_goal = dot;
4739
4740         //      If there is no next point on the path, don't care about dot to next.
4741         if (wp_index + 1 >= wpl->count) {
4742                 dot_to_next = dot;
4743         }
4744
4745         // nprintf(("AI", "Wp #%i, dot = %6.3f, next dot = %6.3f, dist = %7.2f\n", wp_index, dot, dot_to_next, dist_to_goal));
4746
4747         if (Pl_objp->phys_info.speed < 0.0f) {
4748                 accelerate_ship(aip, 1.0f/32);
4749         } else if (prev_dot_to_goal > dot+0.01f) {
4750                 //      We are further from pointing at our goal this frame than last frame, so slow down.
4751                 set_accel_for_target_speed(Pl_objp, Pl_objp->phys_info.speed * 0.95f);
4752         } else if (dist_to_goal < 100.0f) {
4753                 float slew_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4754                 if (fl_abs(slew_dot) < 0.9f) {
4755                         accelerate_ship(aip, 0.0f);
4756                 } else if (dot < 0.88f + 0.1f*(100.0f - dist_to_goal)/100.0f) {
4757                         accelerate_ship(aip, 0.0f);
4758                 } else {
4759                         accelerate_ship(aip, 0.5f * dot * dot);
4760                 }
4761         } else {
4762                 float   dot1;
4763                 if (dist_to_goal < 250.0f) {
4764                         dot1 = dot*dot*dot;                             //      Very important to be pointing towards goal when nearby.  Note, cubing preserves sign.
4765                 } else {
4766                         if (dot > 0.0f) {
4767                                 dot1 = dot*dot;
4768                         } else {
4769                                 dot1 = dot;
4770                         }
4771                 }
4772
4773                 if (dist_to_goal > 100.0f + Pl_objp->radius * 2) {
4774                         if (dot < 0.2f) {
4775                                 dot1 = 0.2f;
4776                         }
4777                 }
4778
4779                 if (sip->flags & SIF_SMALL_SHIP) {
4780                         set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/5.0f);
4781                 } else {
4782                         set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/10.0f);
4783                 }
4784         }
4785
4786         //      Make sure not travelling too fast for someone to keep up.
4787         float   max_allowed_speed = 9999.9f;
4788
4789         if (shipp->wingnum != -1) {
4790                 max_allowed_speed = 0.9f * get_wing_lowest_max_speed(Pl_objp);
4791         }
4792
4793         // check if waypoint speed cap is set and adjust max speed
4794         if (aip->waypoint_speed_cap > 0) {
4795                 max_allowed_speed = (float) aip->waypoint_speed_cap;
4796         }
4797
4798         if (aip->prev_accel * shipp->current_max_speed > max_allowed_speed) {
4799                 accelerate_ship(aip, max_allowed_speed / shipp->current_max_speed);
4800         }
4801
4802         if (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f) {
4803                 vector  nearest_point;
4804                 float           r;
4805
4806                 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, wp_cur);
4807
4808                 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))) ||
4809                         ((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, wp_cur) < (MIN_DIST_TO_WAYPOINT_GOAL + fl_sqrt(Pl_objp->radius)))) {
4810                         wp_index++;
4811                         if (wp_index >= wpl->count)
4812                                 if (aip->wp_flags & WPF_REPEAT) {
4813                                         wp_index = 0;
4814                                 } else {
4815                                         int treat_as_ship;
4816
4817                                         // when not repeating waypoints -- mark the goal as done and put and entry into the mission log
4818                                         // we must be careful when dealing with wings.  A ship in a wing might be completing
4819                                         // a waypoint for for the entire wing, or it might be completing a goal for itself.  If
4820                                         // for itself and in a wing, treat the completion as we would a ship
4821                                         treat_as_ship = 1;
4822                                         if ( Ships[Pl_objp->instance].wingnum != -1 ) {
4823                                                 int type;
4824
4825                                                 // I don't think that you can fly waypoints as dynamic goals!!!
4826                                                 // -- This is legal, just stupid. -- Assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4827                                                 
4828                                                 //      Clean up from above Assert, just in case we ship without fixing it.  (Encountered by JimB on 2/9/98)
4829                                                 if ( (aip->active_goal == AI_GOAL_NONE) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC) ) {
4830                                                         aip->mode = AIM_NONE;
4831                                                         Int3(); //      Look at the ship, find out of it's supposed to be flying waypoints. -- MK.
4832                                                 }
4833
4834                                                 type = aip->goals[aip->active_goal].type;
4835                                                 if ( (type == AIG_TYPE_EVENT_WING) || (type == AIG_TYPE_PLAYER_WING) ) {
4836                                                         treat_as_ship = 0;
4837                                                 } else {
4838                                                         treat_as_ship = 1;
4839                                                 }
4840                                         }
4841
4842                                         // if the ship is not in a wing, remove the goal and continue on
4843                                         if ( treat_as_ship ) {
4844                                                 ai_mission_goal_complete( aip );                                        // this call should reset the AI mode
4845                                                 mission_log_add_entry(LOG_WAYPOINTS_DONE, Ships[Pl_objp->instance].ship_name, wpl->name, -1 );
4846                                         } else {
4847                                                 // this ship is in a wing.  We must mark the goal as being completed for all ships
4848                                                 // in the wing.  We will also mark an entry in the log that the wing completed the goal
4849                                                 // not the individual ship.
4850                                                 ai_mission_wing_goal_complete( Ships[Pl_objp->instance].wingnum, &(aip->goals[aip->active_goal]) );
4851                                                 mission_log_add_entry( LOG_WAYPOINTS_DONE, Wings[Ships[Pl_objp->instance].wingnum].name, wpl->name, -1 );
4852                                         }
4853                                         //wp_index = wpl->count-1;
4854                                 }
4855
4856                         aip->wp_index = wp_index;
4857                 }
4858         }
4859 }
4860
4861 //      Make Pl_objp avoid En_objp
4862 //      Not like evading.  This is for avoiding a collision!
4863 //      Note, use sliding if available.
4864 void avoid_ship()
4865 {
4866         //      To avoid an object, turn towards right or left vector until facing away from object.
4867         //      To choose right vs. left, pick one that is further from center of avoid object.
4868         //      Keep turning away from until pointing away from ship.
4869         //      Stay in avoid mode until at least 3 enemy ship radii away.
4870
4871         //      Speed setting:
4872         //      If inside sphere, zero speed and turn towards outside.
4873         //      If outside sphere, inside 2x sphere, set speed percent of max to:
4874         //              max(away_dot, (dist-rad)/rad)
4875         //      where away_dot is dot(Pl_objp->v.fvec, vec_En_objp_to_Pl_objp)
4876
4877         vector  vec_to_enemy;
4878         float           away_dot;
4879         float           dist;
4880         ship            *shipp = &Ships[Pl_objp->instance];
4881         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4882         ai_info *aip = &Ai_info[shipp->ai_index];
4883         vector  player_pos, enemy_pos;
4884
4885         // if we're avoiding a stealth ship, then we know where he is, update with no error
4886         if ( is_object_stealth_ship(En_objp) ) {
4887                 update_ai_stealth_info_with_error(aip/*, 1*/);
4888         }
4889
4890         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
4891         vm_vec_sub(&vec_to_enemy, &enemy_pos, &Pl_objp->pos);
4892
4893         dist = vm_vec_normalize(&vec_to_enemy);
4894         away_dot = -vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_to_enemy);
4895         
4896         if ((sip->max_vel.xyz.x > 0.0f) || (sip->max_vel.xyz.y > 0.0f)) {
4897                 if (vm_vec_dot(&Pl_objp->orient.v.rvec, &vec_to_enemy) > 0.0f) {
4898                         AI_ci.sideways = -1.0f;
4899                 } else {
4900                         AI_ci.sideways = 1.0f;
4901                 }
4902                 if (vm_vec_dot(&Pl_objp->orient.v.uvec, &vec_to_enemy) > 0.0f) {
4903                         AI_ci.vertical = -1.0f;
4904                 } else {
4905                         AI_ci.vertical = 1.0f;
4906                 }
4907         }               
4908
4909         //nprintf(("AI", "Frame %i: Sliding: %s %s\n", Framecount, AI_ci.sideways < 0 ? "left" : "right", AI_ci.vertical < 0 ? "down" : "up" ));
4910         // nprintf(("AI", "away_dot = %6.3f, dist = %7.2f, dist/radsum = %6.3f\n", away_dot, dist, dist/(Pl_objp->radius + En_objp->radius)));
4911
4912         //      If in front of enemy, turn away from it.
4913         //      If behind enemy, try to get fully behind it.
4914         if (away_dot < 0.0f) {
4915                 turn_away_from_point(Pl_objp, &enemy_pos, Pl_objp->phys_info.speed);
4916         } else {
4917                 vector  goal_pos;
4918
4919                 vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.fvec, -100.0f);
4920                 turn_towards_point(Pl_objp, &goal_pos, NULL, Pl_objp->phys_info.speed);
4921         }
4922
4923         //      Set speed.
4924         float   radsum = Pl_objp->radius + En_objp->radius;
4925
4926         if (dist < radsum)
4927                 accelerate_ship(aip, max(away_dot, 0.2f));
4928         else if (dist < 2*radsum)
4929                 accelerate_ship(aip, max(away_dot, (dist - radsum) / radsum)+0.2f);
4930         else
4931                 accelerate_ship(aip, 1.0f);
4932
4933 }
4934
4935 //      Maybe it's time to resume the previous AI mode in aip->previous_mode.
4936 //      Each type of previous_mode has its own criteria on when to resume.
4937 //      Return true if previous mode was resumed.
4938 int maybe_resume_previous_mode(object *objp, ai_info *aip)
4939 {
4940         //      Only (maybe) resume previous goal if current goal is dynamic.
4941         if (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC)
4942                 return 0;
4943
4944         if (aip->mode == AIM_EVADE_WEAPON) {
4945                 if (timestamp_elapsed(aip->mode_time) || (((aip->nearest_locked_object == -1) || (Objects[aip->nearest_locked_object].type != OBJ_WEAPON)) && (aip->danger_weapon_objnum == -1))) {
4946                         Assert(aip->previous_mode != AIM_EVADE_WEAPON);
4947                         aip->mode = aip->previous_mode;
4948                         aip->submode = aip->previous_submode;
4949                         aip->submode_start_time = Missiontime;
4950                         aip->active_goal = AI_GOAL_NONE;
4951                         aip->mode_time = -1;                    //      Means do forever.
4952                         return 1;
4953                 }
4954         } else if ( aip->previous_mode == AIM_GUARD) {
4955                 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
4956                         object  *guard_objp;
4957                         float   dist;
4958
4959                         guard_objp = &Objects[aip->guard_objnum];
4960                         dist = vm_vec_dist_quick(&guard_objp->pos, &objp->pos);
4961
4962                         //      If guarding ship is far away from guardee and enemy is far away from guardee,
4963                         //      then stop chasing and resume guarding.
4964                         if (dist > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4965                                 if ((En_objp != NULL) && (En_objp->type == OBJ_SHIP)) {
4966                                         if (vm_vec_dist_quick(&guard_objp->pos, &En_objp->pos) > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4967                                                 Assert(aip->previous_mode == AIM_GUARD);
4968                                                 aip->mode = aip->previous_mode;
4969                                                 aip->submode = AIS_GUARD_PATROL;
4970                                                 aip->active_goal = AI_GOAL_NONE;
4971                                                 return 1;
4972                                         }
4973                                 }
4974                         }
4975                 }
4976         }
4977
4978         return 0;
4979
4980 }
4981
4982 //      Call this function if you want something to happen on average every N quarters of a second.
4983 //      The truth value returned by this function will be the same for any given quarter second interval.
4984 //      The value "num" is only passed in to get asynchronous behavior for different objects.
4985 //      modulus == 1 will always return true.
4986 //      modulus == 2 will return true half the time.
4987 //      modulus == 16 will return true for one quarter second interval every four seconds.
4988 int static_rand_timed(int num, int modulus)
4989 {
4990         if (modulus < 2)
4991                 return 1;
4992         else {
4993                 int     t;
4994
4995                 t = Missiontime >> 18;          //      Get time in quarters of a second
4996                 t += num;
4997
4998                 return !(t % modulus);
4999         }
5000 }
5001
5002 //      Maybe fire afterburner based on AI class
5003 int ai_maybe_fire_afterburner(object *objp, ai_info *aip)
5004 {
5005         if (aip->ai_class == 0)
5006                 return 0;               //      Lowest level never aburners away
5007         else  {
5008                 //      Maybe don't afterburner because of a potential collision with the player.
5009                 //      If not multiplayer, near player and player in front, probably don't afterburner.
5010                 if (!(Game_mode & GM_MULTIPLAYER)) {
5011                         if (Ships[objp->instance].team == Player_ship->team) {
5012                                 float   dist;
5013
5014                                 dist = vm_vec_dist_quick(&objp->pos, &Player_obj->pos) - Player_obj->radius - objp->radius;
5015                                 if (dist < 150.0f) {
5016                                         vector  v2p;
5017                                         float           dot;
5018
5019                                         vm_vec_normalized_dir(&v2p, &Player_obj->pos, &objp->pos);
5020                                         dot = vm_vec_dot(&v2p, &objp->orient.v.fvec);
5021
5022                                         if (dot > 0.0f) {
5023                                                 if (dot * dist > 50.0f)
5024                                                         return 0;
5025                                         }
5026                                 }
5027                         }
5028                 }
5029
5030                 if (aip->ai_class >= Num_ai_classes-2)
5031                         return 1;               //      Highest two levels always aburner away.
5032                 else {
5033                         return static_rand_timed(objp-Objects, Num_ai_classes - aip->ai_class);
5034                 }
5035         }
5036 }
5037
5038 //      Maybe engage afterburner after being hit by an object.
5039 void maybe_afterburner_after_ship_hit(object *objp, ai_info *aip, object *en_objp)
5040 {
5041         //      Only do if facing a little away.
5042         if (en_objp != NULL) {
5043                 vector  v2e;
5044
5045                 vm_vec_normalized_dir(&v2e, &en_objp->pos, &objp->pos);
5046                 if (vm_vec_dot(&v2e, &objp->orient.v.fvec) > -0.5f)
5047                         return;
5048         }
5049
5050         if (!( objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5051                 if (ai_maybe_fire_afterburner(objp, aip)) {
5052                         afterburners_start(objp);
5053                         aip->afterburner_stop_time = Missiontime + F1_0/2;
5054                 }
5055         }
5056 }
5057
5058 //      Return true if object *objp is an instructor.
5059 //      Is an instructor if name begins INSTRUCTOR_SHIP_NAME else not.
5060 int is_instructor(object *objp)
5061 {
5062         return !strnicmp(Ships[objp->instance].ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME));
5063 }
5064
5065 //      Evade the weapon aip->danger_weapon_objnum
5066 //      If it's not valid, do a quick out.
5067 //      Evade by accelerating hard.
5068 //      If necessary, turn hard left or hard right.
5069 void evade_weapon()
5070 {
5071         object  *weapon_objp = NULL;
5072         object  *unlocked_weapon_objp = NULL, *locked_weapon_objp = NULL;
5073         vector  weapon_pos, player_pos, goal_point;
5074         vector  vec_from_enemy;
5075         float           dot_from_enemy, dot_to_enemy;
5076         float           dist;
5077         ship            *shipp = &Ships[Pl_objp->instance];
5078         ai_info *aip = &Ai_info[shipp->ai_index];
5079
5080         if (is_instructor(Pl_objp))
5081                 return;
5082
5083         //      Make sure we're actually being attacked.
5084         //      Favor locked objects.
5085         if (aip->nearest_locked_object != -1) {
5086                 if (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)
5087                         locked_weapon_objp = &Objects[aip->nearest_locked_object];
5088         }
5089         
5090         if (aip->danger_weapon_objnum != -1)
5091                 if (Objects[aip->danger_weapon_objnum].signature == aip->danger_weapon_signature)
5092                         unlocked_weapon_objp = &Objects[aip->danger_weapon_objnum];
5093                 else
5094                         aip->danger_weapon_objnum = -1;         //      Signatures don't match, so no longer endangered.
5095
5096         if (locked_weapon_objp != NULL) {
5097                 if (unlocked_weapon_objp != NULL) {
5098                         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))
5099                                 weapon_objp = locked_weapon_objp;
5100                         else
5101                                 weapon_objp = unlocked_weapon_objp;
5102                 } else
5103                         weapon_objp = locked_weapon_objp;
5104         } else if (unlocked_weapon_objp != NULL)
5105                 weapon_objp = unlocked_weapon_objp;
5106         else {
5107                 if (aip->mode == AIM_EVADE_WEAPON)
5108                         maybe_resume_previous_mode(Pl_objp, aip);
5109                 return;
5110         }
5111
5112         Assert(weapon_objp != NULL);
5113
5114         if (weapon_objp->type != OBJ_WEAPON) {
5115                 if (aip->mode == AIM_EVADE_WEAPON)
5116                         maybe_resume_previous_mode(Pl_objp, aip);
5117                 return;
5118         }
5119         
5120         weapon_pos = weapon_objp->pos;
5121         player_pos = Pl_objp->pos;
5122
5123         //      Make speed based on skill level, varying at highest skill level, which is harder to hit.
5124         accelerate_ship(aip, 1.0f);
5125
5126         dist = vm_vec_normalized_dir(&vec_from_enemy, &player_pos, &weapon_pos);
5127
5128         dot_to_enemy = -vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_from_enemy);
5129         dot_from_enemy = vm_vec_dot(&weapon_objp->orient.v.fvec, &vec_from_enemy);
5130         //nprintf(("AI", "dot from enemy = %7.3f\n", dot_from_enemy));
5131
5132         //      If shot is incoming...
5133         if (dot_from_enemy < 0.3f) {
5134                 if (weapon_objp == unlocked_weapon_objp)
5135                         aip->danger_weapon_objnum = -1;
5136                 return;
5137         } else if (dot_from_enemy > 0.7f) {
5138                 if (dist < 200.0f) {
5139                         if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5140                                 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
5141                                         //nprintf(("AI", "Frame %i, turning on afterburner.\n", AI_FrameCount));
5142                                         afterburners_start(Pl_objp);
5143                                         aip->afterburner_stop_time = Missiontime + F1_0/2;
5144                                 }
5145                         }
5146                 }
5147
5148                 //      If we're sort of pointing towards it...
5149                 if ((dot_to_enemy < -0.5f) || (dot_to_enemy > 0.5f)) {
5150                         float   rdot;
5151
5152                         //      Turn hard left or right, depending on which gets out of way quicker.
5153                         rdot = vm_vec_dot(&Pl_objp->orient.v.rvec, &vec_from_enemy);
5154
5155                         if ((rdot < -0.5f) || (rdot > 0.5f))
5156                                 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.v.rvec, -200.0f);
5157                         else
5158                                 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.v.rvec, 200.0f);
5159
5160                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
5161                 }
5162         }
5163
5164 }
5165
5166 //      Use sliding and backwards moving to face enemy.
5167 //      (Coded 2/20/98.  Works fine, but it's hard to see how to integrate it into the AI system.
5168 //       Typically ships are moving so fast that a little sliding isn't enough to gain an advantage.
5169 //       It's currently used to avoid collisions and could be used to evade weapon fire, but the latter
5170 //       would be frustrating, I think.
5171 //       This function is currently not called.)
5172 void slide_face_ship()
5173 {
5174         ship_info       *sip;
5175
5176         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
5177
5178         //      If can't slide, return.
5179         if ((sip->max_vel.xyz.x == 0.0f) && (sip->max_vel.xyz.y == 0.0f))
5180                 return;
5181
5182         vector  goal_pos;
5183         float           dot_from_enemy, dot_to_enemy;
5184         vector  vec_from_enemy, vec_to_goal;
5185         float           dist;
5186         float           up, right;
5187         ai_info         *aip;
5188
5189         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
5190
5191         dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
5192
5193         ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
5194
5195         dot_from_enemy = vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.fvec);
5196         dot_to_enemy = -vm_vec_dot(&vec_from_enemy, &Pl_objp->orient.v.fvec);
5197
5198         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.rvec) > 0.0f)
5199                 right = 1.0f;
5200         else
5201                 right = -1.0f;
5202
5203         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.uvec) > 0.0f)
5204                 up = 1.0f;
5205         else
5206                 up = -1.0f;
5207
5208         vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.rvec, right * 200.0f);
5209         vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.uvec, up * 200.0f);
5210
5211         vm_vec_normalized_dir(&vec_to_goal, &goal_pos, &Pl_objp->pos);
5212
5213         if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.v.rvec) > 0.0f)
5214                 AI_ci.sideways = 1.0f;
5215         else
5216                 AI_ci.sideways = -1.0f;
5217
5218         if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.v.uvec) > 0.0f)
5219                 AI_ci.vertical = 1.0f;
5220         else
5221                 AI_ci.vertical = -1.0f;
5222
5223         if (dist < 200.0f) {
5224                 if (dot_from_enemy < 0.7f)
5225                         accelerate_ship(aip, -1.0f);
5226                 else
5227                         accelerate_ship(aip, dot_from_enemy + 0.5f);
5228         } else {
5229                 if (dot_from_enemy < 0.7f) {
5230                         accelerate_ship(aip, 0.2f);
5231                 } else {
5232                         accelerate_ship(aip, 1.0f);
5233                 }
5234         }
5235 }
5236
5237 //      General code for handling one ship evading another.
5238 //      Problem: This code is also used for avoiding an impending collision.
5239 //      In such a case, it is not good to go to max speed, which is often good
5240 //      for a certain kind of evasion.
5241 void evade_ship()
5242 {
5243         vector  player_pos, enemy_pos, goal_point;
5244         vector  vec_from_enemy;
5245         float           dot_from_enemy;
5246         float           dist;
5247         ship            *shipp = &Ships[Pl_objp->instance];
5248         ship_info       *sip = &Ship_info[shipp->ship_info_index];
5249         ai_info *aip = &Ai_info[shipp->ai_index];
5250         float           bank_override = 0.0f;
5251
5252         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
5253
5254         //      Make speed based on skill level, varying at highest skill level, which is harder to hit.
5255         if (Game_skill_level == NUM_SKILL_LEVELS-1) {
5256                 int     rand_int;
5257                 float   accel_val;
5258
5259                 rand_int = static_rand(Pl_objp-Objects);
5260                 accel_val = (float) (((Missiontime^rand_int) >> 14) & 0x0f)/32.0f + 0.5f;
5261                 accelerate_ship(aip, accel_val);
5262                 //nprintf(("AI", "Accel value = %7.3f\n", accel_val));
5263         } else
5264                 accelerate_ship(aip, (float) (Game_skill_level+2) / (NUM_SKILL_LEVELS+1));
5265
5266         if ((Missiontime - aip->submode_start_time > F1_0/2) && (sip->afterburner_fuel_capacity > 0.0f)) {
5267                 float percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
5268                 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
5269                         afterburners_start(Pl_objp);
5270                         aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
5271                 }
5272         }
5273
5274         vm_vec_sub(&vec_from_enemy, &player_pos, &enemy_pos);
5275
5276         dist = vm_vec_normalize(&vec_from_enemy);
5277         dot_from_enemy = vm_vec_dot(&En_objp->orient.v.fvec, &vec_from_enemy);
5278
5279         if (dist > 250.0f) {
5280                 vector  gp1, gp2;
5281                 //      If far away from enemy, circle, going to nearer of point far off left or right wing
5282                 vm_vec_scale_add(&gp1, &enemy_pos, &En_objp->orient.v.rvec, 250.0f);
5283                 vm_vec_scale_add(&gp2, &enemy_pos, &En_objp->orient.v.rvec, -250.0f);
5284                 if (vm_vec_dist_quick(&gp1, &Pl_objp->pos) < vm_vec_dist_quick(&gp2, &Pl_objp->pos))
5285                         goal_point = gp1;
5286                 else
5287                         goal_point = gp2;
5288         } else if (dot_from_enemy < 0.1f) {
5289                 //      If already close to behind, goal is to get completely behind.
5290                 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.fvec, -1000.0f);
5291         } else if (dot_from_enemy > 0.9f) {
5292                 //      If enemy pointing almost right at self, and self pointing close to enemy, turn away from
5293                 vector  vec_to_enemy;
5294                 float           dot_to_enemy;
5295
5296                 vm_vec_sub(&vec_to_enemy, &enemy_pos, &player_pos);
5297
5298                 vm_vec_normalize(&vec_to_enemy);
5299                 dot_to_enemy = vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_to_enemy);
5300                 if (dot_to_enemy > 0.75f) {
5301                         //      Used to go to En_objp's right vector, but due to banking while turning, that
5302                         //      caused flying in an odd spiral.
5303                         vm_vec_scale_add(&goal_point, &enemy_pos, &Pl_objp->orient.v.rvec, 1000.0f);
5304                         if (dist < 100.0f)
5305                                 bank_override = Pl_objp->phys_info.speed; 
5306                 } else {
5307                         bank_override = Pl_objp->phys_info.speed;                       //      In enemy's sights, not pointing at him, twirl away.
5308                         // nprintf(("Mike", " Do sumpin' else."));
5309                         goto evade_ship_l1;
5310                 }
5311         } else {
5312 evade_ship_l1: ;
5313                 if (aip->ai_evasion > myrand()*100.0f/32767.0f) {
5314                         int     temp;
5315                         float   scale;
5316                         float   psrandval;      //      some value close to zero to choose whether to turn right or left.
5317
5318                         psrandval = (float) (((Missiontime >> 14) & 0x0f) - 8); //      Value between -8 and 7
5319                         psrandval = psrandval/16.0f;                                                    //      Value between -1/2 and 1/2 (approx)
5320
5321                         //      If not close to behind, turn towards his right or left vector, whichever won't cross his path.
5322                         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.rvec) > psrandval) {
5323                                 scale = 1000.0f;
5324                         } else {
5325                                 scale = -1000.0f;
5326                         }
5327
5328                         vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.rvec, scale);
5329
5330                         temp = ((Missiontime >> 16) & 0x07);
5331                         temp = ((temp * (temp+1)) % 16)/2 - 4;
5332                         if ((psrandval == 0) && (temp == 0))
5333                                 temp = 3;
5334
5335                         scale = 200.0f * temp;
5336
5337                         vm_vec_scale_add2(&goal_point, &En_objp->orient.v.uvec, scale);
5338                 } else {
5339                         //      No evasion this frame, but continue with previous turn.
5340                         //      Reason: If you don't, you lose rotational momentum.  Turning every other frame,
5341                         //      and not in between results in a very slow turn because of loss of momentum.
5342                         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))
5343                                 goal_point = aip->prev_goal_point;
5344                         else
5345                                 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.rvec, 100.0f);
5346                 }
5347         }
5348
5349         // nprintf(("Mike", "Goal point = %7.1f %7.1f %7.1f\n", goal_point.xyz.x, goal_point.xyz.y, goal_point.xyz.z));
5350         turn_towards_point(Pl_objp, &goal_point, NULL, bank_override);
5351
5352         aip->prev_goal_point = goal_point;
5353 }
5354
5355 //      --------------------------------------------------------------------------
5356 //      Fly in a manner making it difficult for opponent to attack.
5357 void ai_evade()
5358 {
5359         evade_ship();
5360 }
5361
5362 /*
5363 // -------------------------------------------------------------------
5364 //      Refine predicted enemy position because enemy will move while we move
5365 //      towards predicted enemy position.
5366 //      last_delta_vec is stuffed with size of polishing in last step.  This small amount
5367 //      can be used to perturb the predicted position to make firing not be exact.
5368 //      This function will almost always undershoot actual position, assuming both ships
5369 //      are moving at constant speed.  But with even one polishing step, the error should
5370 //      be under 1%. The number of polishing steps is specified in the parameter num_polish_steps.
5371 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)
5372 {
5373         int     iteration;
5374         vector  player_pos = pobjp->pos;
5375         vector  enemy_pos = *predicted_enemy_pos;
5376         physics_info    *en_physp = &eobjp->phys_info;
5377         float           time_to_enemy;
5378         vector  last_predicted_enemy_pos = *predicted_enemy_pos;
5379         
5380         vm_vec_zero(last_delta_vec);
5381
5382         for (iteration=0; iteration < num_polish_steps; iteration++) {
5383                 dist_to_enemy = vm_vec_dist_quick(predicted_enemy_pos, &player_pos);
5384                 time_to_enemy = compute_time_to_enemy(dist_to_enemy, pobjp, eobjp);
5385                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, time_to_enemy);
5386                 vm_vec_sub(last_delta_vec, predicted_enemy_pos, &last_predicted_enemy_pos);
5387                 last_predicted_enemy_pos= *predicted_enemy_pos;
5388         }
5389 }
5390 */
5391
5392 /*
5393 Relevant variables are:
5394         best_dot_to_enemy               best dot product to enemy in last BEST_DOT_TIME seconds
5395         best_dot_to_time                time at which best dot occurred
5396         best_dot_from_enemy     best dot product for enemy to player in last BEST_DOT_TIME seconds
5397         best_dot_from_time      time at which best dot occurred
5398         submode_start_time      time at which we entered the current submode
5399         previous_submode                previous submode, get it?
5400 Legal submodes are:
5401         CONTINUOUS_TURN vector_id {0..3 = right, -right, up, -up}
5402         ATTACK
5403         EVADE_SQUIGGLE
5404         EVADE_BRAKE
5405 */
5406
5407 float   G_collision_time;
5408 vector  G_predicted_pos, G_fire_pos;
5409
5410 /*
5411 void show_firing_diag()
5412 {
5413         float           dot;
5414         vector  v2t;
5415         vector  pos1, pos2;
5416         float           dist;
5417
5418         if (G_collision_time == 0.0f)
5419                 return;
5420
5421         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",
5422                 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));
5423         vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5424         dot = vm_vec_dot(&v2t, &Pl_objp->orient.v.fvec);
5425         mprintf(("Dot of v.fvec and vector to predicted position = %10.7f (%7.3f degrees)\n", dot, acos(dot)*180.0f/3.141592654f));
5426
5427         vm_vec_scale_add(&pos1, &En_objp->pos, &En_objp->phys_info.vel, G_collision_time);
5428         vm_vec_scale_add(&pos2, &G_fire_pos, &Pl_objp->orient.v.fvec, G_collision_time*300.0f);
5429         dist = vm_vec_dist(&pos1, &pos2);
5430
5431         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));
5432 }
5433 */
5434
5435 //      If:
5436 //              flags & WIF_PUNCTURE
5437 //      Then Select a Puncture weapon.
5438 //      Else
5439 //              Select Any ol' weapon.
5440 //      Returns primary_bank index.
5441 int ai_select_primary_weapon(object *objp, object *other_objp, int flags)
5442 {
5443         ship    *shipp = &Ships[objp->instance];
5444         ship_weapon *swp = &shipp->weapons;
5445         ship_info *sip;
5446
5447         //Assert( other_objp != NULL );
5448         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5449
5450         sip = &Ship_info[shipp->ship_info_index];
5451
5452         if (flags & WIF_PUNCTURE) {
5453                 if (swp->current_primary_bank >= 0) {
5454                         int     bank_index;
5455
5456                         bank_index = swp->current_primary_bank;
5457
5458                         if (Weapon_info[swp->primary_bank_weapons[bank_index]].wi_flags & WIF_PUNCTURE) {
5459                                 //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[bank_index]].name));
5460                                 return swp->current_primary_bank;
5461                         }
5462                 }
5463                 for (int i=0; i<swp->num_primary_banks; i++) {
5464                         int     weapon_info_index;
5465
5466                         weapon_info_index = swp->primary_bank_weapons[i];
5467
5468                         if (weapon_info_index > -1){
5469                                 if (Weapon_info[weapon_info_index].wi_flags & WIF_PUNCTURE) {
5470                                         swp->current_primary_bank = i;
5471                                         //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5472                                         return i;
5473                                 }
5474                         }
5475                 }
5476                 
5477                 // AL 26-3-98: If we couldn't find a puncture weapon, pick first available weapon if one isn't active
5478                 if ( swp->current_primary_bank < 0 ) {
5479                         if ( swp->num_primary_banks > 0 ) {
5480                                 swp->current_primary_bank = 0;
5481                         }
5482                 }
5483
5484         } else {                //      Don't need to be using a puncture weapon.
5485                 if (swp->current_primary_bank >= 0) {
5486                         if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)){
5487                                 return swp->current_primary_bank;
5488                         }
5489                 }
5490                 for (int i=0; i<swp->num_primary_banks; i++) {
5491                         if (swp->primary_bank_weapons[i] > -1) {
5492                                 if (!(Weapon_info[swp->primary_bank_weapons[i]].wi_flags & WIF_PUNCTURE)) {
5493                                         swp->current_primary_bank = i;
5494                                         nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5495                                         return i;
5496                                 }
5497                         }
5498                 }
5499                 //      Wasn't able to find a non-puncture weapon.  Stick with what we have.
5500         }
5501
5502         Assert( swp->current_primary_bank != -1 );              // get Alan or Allender
5503
5504         return swp->current_primary_bank;
5505 }
5506
5507 //      --------------------------------------------------------------------------
5508 //      Maybe link primary weapons.
5509 void set_primary_weapon_linkage(object *objp)
5510 {
5511         ship            *shipp;
5512         ai_info *aip;
5513
5514         shipp = &Ships[objp->instance];
5515         aip     = &Ai_info[shipp->ai_index];
5516
5517         shipp->flags &= ~SF_PRIMARY_LINKED;
5518
5519         if (Num_weapons > (int) (MAX_WEAPONS * 0.75f)) {
5520                 if (shipp->flags & SF_PRIMARY_LINKED)
5521                         nprintf(("AI", "Frame %i, ship %s: Unlinking primaries.\n", Framecount, shipp->ship_name));
5522                 shipp->flags &= ~SF_PRIMARY_LINKED;
5523                 return;         //      If low on slots, don't link.
5524         }
5525
5526         shipp->flags &= ~SF_PRIMARY_LINKED;
5527
5528         // AL: ensure target is a ship!
5529         if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
5530                 // If trying to destroy a big ship (i.e., not disable/disarm), always unleash all weapons
5531                 if ( ship_get_SIF(&Ships[Objects[aip->target_objnum].instance]) & SIF_BIG_SHIP) {
5532                         if ( aip->targeted_subsys == NULL ) {
5533                                 shipp->flags |= SF_PRIMARY_LINKED;
5534                                 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5535                                 return;
5536                         }
5537                 }
5538         }
5539
5540         // AL 2-11-98: If ship has a disarm or disable goal, don't link unless both weapons are
5541         //                                      puncture weapons
5542         if ( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) ) {
5543                 if ( aip->goals[aip->active_goal].ai_mode & (AI_GOAL_DISABLE_SHIP|AI_GOAL_DISARM_SHIP) ) {
5544                         ship_weapon     *swp;
5545                         swp = &shipp->weapons;
5546                         // only continue if both primaries are puncture weapons
5547                         if ( swp->num_primary_banks == 2 ) {
5548                                 if ( !(Weapon_info[swp->primary_bank_weapons[0]].wi_flags & WIF_PUNCTURE) ) 
5549                                         return;
5550                                 if ( !(Weapon_info[swp->primary_bank_weapons[1]].wi_flags & WIF_PUNCTURE) ) 
5551                                         return;
5552                         }
5553                 }
5554         }
5555
5556         //      Don't want all ships always linking weapons at start, so asynchronize.
5557         if (Missiontime < i2f(30))
5558                 return;
5559         else if (Missiontime < i2f(120)) {
5560                 int r = static_rand((Missiontime >> 17) ^ OBJ_INDEX(objp));
5561                 if ( (r&3) != 0)
5562                         return;
5563         }
5564
5565         if (shipp->weapon_energy > Link_energy_levels_always[Game_skill_level]) {
5566                 shipp->flags |= SF_PRIMARY_LINKED;
5567         } else if (shipp->weapon_energy > Link_energy_levels_maybe[Game_skill_level]) {
5568                 if (objp->hull_strength < Ship_info[shipp->ship_info_index].initial_hull_strength/3.0f)
5569                         shipp->flags |= SF_PRIMARY_LINKED;
5570         }
5571 }
5572
5573 //      --------------------------------------------------------------------------
5574 //      Fire the current primary weapon.
5575 //      *objp is the object to fire from.
5576 void ai_fire_primary_weapon(object *objp)
5577 {
5578         ship                    *shipp = &Ships[objp->instance];
5579         ship_weapon     *swp = &shipp->weapons;
5580         ship_info       *sip;
5581         ai_info         *aip;
5582         object          *enemy_objp;
5583
5584         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5585         sip = &Ship_info[shipp->ship_info_index];
5586
5587         aip = &Ai_info[shipp->ai_index];
5588
5589         //      If low on slots, fire a little less often.
5590         if (Num_weapons > (int) (0.9f * MAX_WEAPONS)) {
5591                 if (frand() > 0.5f) {
5592                         nprintf(("AI", "Frame %i, %s not fire.\n", Framecount, shipp->ship_name));
5593                         return;
5594                 }
5595         }
5596
5597         if (!Ai_firing_enabled){
5598                 return;
5599         }
5600
5601         if (aip->target_objnum != -1){
5602                 enemy_objp = &Objects[aip->target_objnum];
5603         } else {
5604                 enemy_objp = NULL;
5605         }
5606
5607         if ( (swp->current_primary_bank < 0) || (swp->current_primary_bank >= swp->num_primary_banks) || timestamp_elapsed(aip->primary_select_timestamp)) {
5608                 int     flags = 0;
5609                 // AL 2-11-98: If attacking any subsystem (not just engines), use disrupter weapon
5610 //              if ((aip->targeted_subsys != NULL) && (aip->targeted_subsys->system_info->type == SUBSYSTEM_ENGINE)) {
5611                 if ( aip->targeted_subsys != NULL ) {
5612                         flags = WIF_PUNCTURE;
5613                 }
5614                 ai_select_primary_weapon(objp, enemy_objp, flags);
5615                 ship_primary_changed(shipp);    // AL: maybe send multiplayer information when AI ship changes primaries
5616                 aip->primary_select_timestamp = timestamp(5 * 1000);    //      Maybe change primary weapon five seconds from now.
5617         }
5618
5619         //      If pointing nearly at predicted collision point of target, bash orientation to be perfectly pointing.
5620         float   dot;
5621         vector  v2t;
5622
5623 //      if (!IS_VEC_NULL(&G_predicted_pos)) {
5624         if (!( vm_vec_mag_quick(&G_predicted_pos) < AICODE_SMALL_MAGNITUDE )) {
5625                 if ( !vm_vec_cmp(&G_predicted_pos, &G_fire_pos) ) {
5626                         nprintf(("Warning", "Avoid NULL vector assert.. why are G_predicted_pos and G_fire_pos the same?\n"));
5627                 } else {
5628                         vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5629                         dot = vm_vec_dot(&v2t, &objp->orient.v.fvec);
5630                         if (dot > .998629534f){ //      if within 3.0 degrees of desired heading, bash
5631                                 vm_vector_2_matrix(&objp->orient, &v2t, &objp->orient.v.uvec, NULL);
5632                         }
5633                 }
5634         }
5635
5636         //      Make sure not firing at a protected ship unless firing at a live subsystem.
5637         //      Note: This happens every time the ship tries to fire, perhaps every frame.
5638         //      Should be wrapped in a timestamp, same one that enables it to fire, but that is complicated
5639         //      by multiple banks it can fire from.
5640         if (aip->target_objnum != -1) {
5641                 object  *tobjp = &Objects[aip->target_objnum];
5642                 if (tobjp->flags & OF_PROTECTED) {
5643                         if (aip->targeted_subsys != NULL) {
5644                                 int     type;
5645
5646                                 type = aip->targeted_subsys->system_info->type;
5647                                 if (ship_get_subsystem_strength(&Ships[tobjp->instance], type) == 0.0f) {
5648                                         aip->target_objnum = -1;
5649                                         return;
5650                                 }
5651                         } else {
5652                                 aip->target_objnum = -1;
5653                                 return;
5654                         }
5655                 }
5656         }
5657
5658         //      If enemy is protected, not firing a puncture weapon and enemy's hull is low, don't fire.
5659         if ((enemy_objp != NULL) && (enemy_objp->flags & OF_PROTECTED)) {
5660                 // AL: 3-6-98: Check if current_primary_bank is valid
5661                 if ((enemy_objp->hull_strength < 750.0f) && 
5662                         ((aip->targeted_subsys == NULL) || (enemy_objp->hull_strength < aip->targeted_subsys->current_hits + 50.0f)) &&
5663                         (swp->current_primary_bank >= 0) ) {
5664                         if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)) {
5665                                 //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));
5666                                 swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(1000);
5667                                 return;
5668                         }
5669
5670                         /*
5671                         int     num_attacking;
5672                         num_attacking = num_enemies_attacking(enemy_objp-Objects);
5673                         if (enemy_objp->hull_strength / num_attacking < 200.0f) {
5674                                 if (frand() < 0.75f) {
5675                                         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));
5676                                         swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(500);
5677                                         return;
5678                                 }
5679                         }
5680                         */
5681                 }
5682         }
5683
5684         set_primary_weapon_linkage(objp);
5685         
5686         // I think this will properly solve the problem
5687         // fire non-streaming weapons
5688         ship_fire_primary(objp, 0);
5689         
5690         // fire streaming weapons
5691         shipp->flags |= SF_TRIGGER_DOWN;
5692         ship_fire_primary(objp, 1);
5693         shipp->flags &= ~SF_TRIGGER_DOWN;
5694 }
5695
5696 //      --------------------------------------------------------------------------
5697 //      Return number of nearby enemy fighters.
5698 //      threshold is the distance within which a ship is considered near.
5699 //
5700 // input:       enemy_team_mask =>      teams that are considered as an enemy
5701 //                              pos                                     =>      world position to measure ship distances from
5702 //                              threshold                       =>      max distance from pos to be considered "near"
5703 //
5704 // exit:                number of ships within threshold units of pos
5705 int num_nearby_fighters(int enemy_team_mask, vector *pos, float threshold)
5706 {
5707         ship_obj        *so;
5708         object  *ship_objp;
5709         int             count = 0;
5710
5711         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5712
5713                 ship_objp = &Objects[so->objnum];
5714
5715                 if (Ships[ship_objp->instance].team & enemy_team_mask) {
5716                         if (Ship_info[Ships[ship_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) {
5717                                 if (vm_vec_dist_quick(pos, &ship_objp->pos) < threshold)
5718                                         count++;
5719                         }
5720                 }
5721         }
5722
5723         return count;
5724 }
5725
5726 //      --------------------------------------------------------------------------
5727 //      Select secondary weapon to fire.
5728 //      Currently, 1/16/98:
5729 //              If 0 secondary weapons available, return -1
5730 //              If 1 available, use it.
5731 //              If 2 or more, if the current weapon is one of them, stick with it, otherwise choose a random one.
5732 //      priority1 and priority2 are Weapon_info[] bitmasks such as WIF_HOMING_ASPECT.  If any weapon has any bit in priority1
5733 //      set, that weapon will be selected.  If not, apply to priority2.  If neither, return -1, meaning no weapon selected.
5734 //      Note, priorityX have default values of -1, meaning if not set, they will match any weapon.
5735 //      Return value:
5736 //              bank index
5737 //      Should do this:
5738 //              Favor aspect seekers when attacking small ships faraway.
5739 //              Favor rapid fire dumbfire when attacking a large ship.
5740 //              Ignore heat seekers because we're not sure how they'll work.
5741 void ai_select_secondary_weapon(object *objp, ship_weapon *swp, int priority1 = -1, int priority2 = -1)
5742 {
5743         int     num_weapon_types;
5744         int     weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
5745         int     i;
5746         int     ignore_mask;
5747         int     initial_bank;
5748
5749         initial_bank = swp->current_secondary_bank;
5750
5751         //      Ignore bombs unless one of the priorities asks for them to be selected.
5752         if (WIF_HUGE & (priority1 | priority2))
5753                 ignore_mask = 0;
5754         else
5755                 ignore_mask = WIF_HUGE;
5756
5757         if (!(WIF_BOMBER_PLUS & (priority1 | priority2)))
5758                 ignore_mask |= WIF_BOMBER_PLUS;
5759
5760 #ifndef NDEBUG
5761         for (i=0; i<MAX_WEAPON_TYPES; i++) {
5762                 weapon_id_list[i] = -1;
5763                 weapon_bank_list[i] = -1;
5764         }
5765 #endif
5766
5767         //      Stuff weapon_bank_list with bank index of available weapons.
5768         num_weapon_types = get_available_secondary_weapons(objp, weapon_id_list, weapon_bank_list);
5769
5770         int     priority2_index = -1;
5771
5772         for (i=0; i<num_weapon_types; i++) {
5773                 int     wi_flags;
5774
5775                 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5776                 if (!(wi_flags & ignore_mask)) {                                        //      Maybe bombs are illegal.
5777                         if (wi_flags & priority1) {
5778                                 swp->current_secondary_bank = weapon_bank_list[i];                              //      Found first priority, return it.
5779                                 break;
5780                         } else if (wi_flags & priority2)
5781                                 priority2_index = weapon_bank_list[i];  //      Found second priority, but might still find first priority.
5782                 }
5783         }
5784
5785         //      If didn't find anything above, then pick any secondary weapon.
5786         if (i == num_weapon_types) {
5787                 swp->current_secondary_bank = priority2_index;  //      Assume we won't find anything.
5788                 if (priority2_index == -1) {
5789                         for (i=0; i<num_weapon_types; i++) {
5790                                 int     wi_flags;
5791
5792                                 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5793                                 if (!(wi_flags & ignore_mask)) {                                        //      Maybe bombs are illegal.
5794                                         if (swp->secondary_bank_ammo[i] > 0) {
5795                                                 swp->current_secondary_bank = i;
5796                                                 break;
5797                                         }
5798                                 }
5799                         }
5800                 }
5801         }
5802
5803         //      If switched banks, force reacquisition of aspect lock.
5804         if (swp->current_secondary_bank != initial_bank) {
5805                 ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
5806                 
5807                 aip->aspect_locked_time = 0.0f;
5808                 aip->current_target_is_locked = 0;
5809         }
5810
5811
5812         ship_secondary_changed(&Ships[objp->instance]); // AL: let multiplayer know if secondary bank has changed
5813         // nprintf(("AI", "Ship %s selected weapon %s\n", Ships[objp->instance].ship_name, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
5814 }
5815
5816 //      Return number of objects homing on object *target_objp
5817 int compute_num_homing_objects(object *target_objp)
5818 {
5819         object  *objp;
5820         int             count = 0;
5821
5822         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
5823                 if (objp->type == OBJ_WEAPON) {
5824                         if (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_HOMING) {
5825                                 if (Weapons[objp->instance].homing_object == target_objp) {
5826                                         count++;
5827                                 }
5828                         }
5829                 }
5830         }
5831
5832         return count;
5833 }
5834
5835 //      Object *firing_objp just fired weapon weapon_index (index in Weapon_info).
5836 //      If it's a shockwave weapon, tell your team about it!
5837 void ai_maybe_announce_shockwave_weapon(object *firing_objp, int weapon_index)
5838 {
5839         if ((firing_objp->type == OBJ_SHIP) && (Weapon_info[weapon_index].shockwave_speed > 0.0f)) {
5840                 ship_obj        *so;
5841                 int             firing_ship_team;
5842
5843                 firing_ship_team = Ships[firing_objp->instance].team;
5844
5845                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5846                         object  *A = &Objects[so->objnum];
5847                         Assert(A->type == OBJ_SHIP);
5848
5849                         if (Ships[A->instance].team == firing_ship_team) {
5850                                 ai_info *aip = &Ai_info[Ships[A->instance].ai_index];
5851                                 // AL 1-5-98: only avoid shockwave if not docked or repairing
5852                                 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
5853                                         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_WEAPON;
5854                                 }
5855                         }
5856                 }
5857         }
5858 }
5859
5860 //      Return total payload of all incoming missiles.
5861 float compute_incoming_payload(object *target_objp)
5862 {
5863         missile_obj     *mo;
5864         float                   payload = 0.0f;
5865
5866         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
5867                 object  *objp;
5868
5869                 objp = &Objects[mo->objnum];
5870                 Assert(objp->type == OBJ_WEAPON);
5871                 if (Weapons[objp->instance].homing_object == target_objp) {
5872                         payload += Weapon_info[Weapons[objp->instance].weapon_info_index].damage;
5873                 }
5874         }
5875
5876         return payload;
5877 }
5878
5879 //      --------------------------------------------------------------------------
5880 //      Return true if OK for *aip to fire its current weapon at its current target.
5881 //      Only reason this function returns false is:
5882 //              weapon is a homer
5883 //              targeted at player
5884 //                      OR:     player has too many homers targeted at him
5885 //                                      Missiontime in that dead zone in which can't fire at this player
5886 //      Note: If player is attacking a ship, that ship is allowed to fire at player.  Otherwise, we get in a situation in which
5887 //      player is attacking a large ship, but that large ship is not defending itself with missiles.
5888 int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip)
5889 {
5890         int     num_homers = 0;
5891         object  *tobjp = &Objects[target_objnum];
5892
5893         if (target_objnum > -1) {
5894                 // AL 3-4-98: Ensure objp target is a ship first 
5895                 if ( tobjp->type == OBJ_SHIP ) {
5896
5897                         // should not get this far. check if ship is protected from beam and weapon is type beam
5898                         if ( (wip->wi_flags & WIF_BEAM) && (tobjp->flags & OF_BEAM_PROTECTED) ) {
5899                                 Int3();
5900                                 return 0;
5901                         }
5902                         if (Ship_info[Ships[tobjp->instance].ship_info_index].flags & SIF_SMALL_SHIP) {
5903                                 num_homers = compute_num_homing_objects(&Objects[target_objnum]);
5904                         }
5905                 }
5906
5907                 //      If player, maybe fire based on Skill_level and number of incoming weapons.
5908                 //      If non-player, maybe fire based on payload of incoming weapons.
5909                 if (wip->wi_flags & WIF_HOMING) {
5910                         if ((target_objnum > -1) && (tobjp->flags & OF_PLAYER_SHIP)) {
5911                                 if (Ai_info[Ships[tobjp->instance].ai_index].target_objnum != objnum) {
5912                                         //      Don't allow AI ships to fire at player for fixed periods of time based on skill level.
5913                                         //      With 5 skill levels, at Very Easy, they fire in 1/7 of every 10 second interval.
5914                                         //      At Easy, 2/7...at Expert, 5/7
5915                                         int t = ((Missiontime /(65536*10)) ^ target_objnum ^ 0x01) % (NUM_SKILL_LEVELS+2);
5916                                         if (t > Game_skill_level) {
5917                                                 //nprintf(("AI", "Not OK to fire homer at time thing %i\n", t));
5918                                                 return 0;
5919                                         }
5920                                 }
5921                                 //nprintf(("AI", " IS OK to fire homer at time thing %i ***\n", t));
5922                                 int     swarmers = 0;
5923                                 if (wip->wi_flags & WIF_SWARM)
5924                                         swarmers = 2;   //      Note, always want to be able to fire swarmers if no currently incident homers.
5925                                 if (Max_allowed_player_homers[Game_skill_level] < num_homers + swarmers) {
5926                                         return 0;
5927                                 }
5928                         } else if (num_homers > 3) {
5929                                 float   incoming_payload;
5930
5931                                 incoming_payload = compute_incoming_payload(&Objects[target_objnum]);
5932
5933                                 if (incoming_payload > tobjp->hull_strength) {
5934                                         return 0;
5935                                 }
5936                         }
5937                 }
5938         }
5939
5940         return 1;
5941 }
5942
5943 //      --------------------------------------------------------------------------
5944 //      Fire a secondary weapon.
5945 //      Maybe choose to fire a different one.
5946 //      priority1 and priority2 are optional parameters with defaults = -1
5947 int ai_fire_secondary_weapon(object *objp, int priority1, int priority2)
5948 {
5949         ship_weapon *swp;
5950         ship    *shipp;
5951         ship_info *sip;
5952         int             current_bank;
5953         int             rval = 0;
5954
5955 #ifndef NDEBUG
5956         if (!Ai_firing_enabled)
5957                 return rval;
5958 #endif
5959
5960         Assert( objp != NULL );
5961         Assert(objp->type == OBJ_SHIP);
5962         shipp = &Ships[objp->instance];
5963         swp = &shipp->weapons;
5964
5965         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5966         sip = &Ship_info[shipp->ship_info_index];
5967
5968         //      Select secondary weapon.
5969         current_bank = swp->current_secondary_bank; //ai_select_secondary_weapon(objp, swp, priority1, priority2);
5970
5971         //nprintf(("AI", "Frame %i: Current bank = %i, ammo remaining = %i\n", Framecount, current_bank, swp->secondary_bank_ammo[current_bank]));
5972         if (current_bank == -1) {
5973                 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5974                 return rval;
5975         }
5976
5977         Assert(current_bank < shipp->weapons.num_secondary_banks);
5978
5979         weapon_info     *wip = &Weapon_info[shipp->weapons.secondary_bank_weapons[current_bank]];
5980
5981         if ((wip->wi_flags & WIF_HOMING_ASPECT) && (!Ai_info[shipp->ai_index].current_target_is_locked)) {
5982                 //nprintf(("AI", "Not firing secondary weapon because not aspect locked.\n"));
5983                 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
5984         } else if ((wip->wi_flags & WIF_BOMB) || (vm_vec_dist_quick(&objp->pos, &En_objp->pos) > 50.0f)) {
5985                 //      This might look dumb, firing a bomb even if closer than 50 meters, but the reason is, if you're carrying
5986                 //      bombs, delivering them is probably more important than surviving.
5987                 ai_info *aip;
5988
5989                 aip = &Ai_info[shipp->ai_index];
5990                 
5991                 //      Note, maybe don't fire if firing at player and any homers yet fired.
5992                 //      Decreasing chance to fire the more homers are incoming on player.
5993                 if (check_ok_to_fire(OBJ_INDEX(objp), aip->target_objnum, wip)) {
5994                         if (ship_fire_secondary(objp)) {
5995                                 rval = 1;
5996                                 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5997                                 //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));
5998                         }
5999
6000                 } else {
6001                         swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
6002                 }
6003         }
6004
6005         return rval;
6006 }
6007
6008 //      Return true if it looks like obj1, if continuing to move along current vector, will
6009 //      collide with obj2.
6010 int might_collide_with_ship(object *obj1, object *obj2, float dot_to_enemy, float dist_to_enemy, float duration)
6011 {
6012         if (obj1->phys_info.speed * duration + 2*(obj1->radius + obj2->radius) > dist_to_enemy)
6013                 if (dot_to_enemy > 0.8f - 2*(obj1->radius + obj2->radius)/dist_to_enemy)
6014                         return objects_will_collide(obj1, obj2, duration, 2.0f);
6015
6016 //              BABY - 
6017 //              CONDITION 1, dist_to_enemy < o1_rad + o2_rad + (obj1.speed + obj2.speed) * time + 50
6018         
6019         return 0;
6020
6021 }
6022
6023 //      --------------------------------------------------------------------------
6024 //      Return true if ship *objp firing a laser believes it will hit a teammate.
6025 int might_hit_teammate(object *firing_objp)
6026 {
6027         int             team;
6028         object  *objp;
6029         ship_obj        *so;
6030
6031         team = Ships[firing_objp->instance].team;
6032
6033         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6034                 objp = &Objects[so->objnum];
6035                 if (Ships[objp->instance].team == team) {
6036                         float           dist, dot;
6037                         vector  vec_to_objp;
6038
6039                         vm_vec_sub(&vec_to_objp, &firing_objp->pos, &objp->pos);
6040                         dist = vm_vec_mag_quick(&vec_to_objp);
6041                         dot = vm_vec_dot(&firing_objp->orient.v.fvec, &vec_to_objp)/dist;
6042                         if (might_collide_with_ship(firing_objp, objp, dot, dist, 2.0f))
6043                                 return 1;
6044                 }
6045         }
6046
6047         return 0;
6048
6049 }
6050
6051 //int   Team_not_fire_count=0, Team_hit_count = 0;
6052
6053 void render_all_ship_bay_paths(object *objp)
6054 {
6055         int             i,j,color;
6056         ship            *sp = &Ships[objp->instance];
6057         polymodel       *pm;
6058         model_path      *mp;
6059
6060         pm = model_get(sp->modelnum);
6061         vector  global_path_point;
6062         vertex  v, prev_vertex;
6063
6064         if ( pm->ship_bay == NULL )
6065                 return;
6066
6067         for ( i = 0; i < pm->ship_bay->num_paths; i++ ) {
6068                 mp = &pm->paths[pm->ship_bay->paths[i]];
6069
6070                 for ( j = 0; j < mp->nverts; j++ ) {
6071                         vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6072                         vm_vec_add2(&global_path_point, &objp->pos);
6073                         g3_rotate_vertex(&v, &global_path_point);
6074                         color = 255 - j*50;
6075                         if ( color < 50 ) 
6076                                 color = 100;
6077                         gr_set_color(0, color, 0);
6078
6079                         if ( j == mp->nverts-1 ) {
6080                                 gr_set_color(255, 0, 0);
6081                         }
6082
6083                         g3_draw_sphere( &v, 1.5f);
6084
6085                         if ( j > 0 )
6086                                 g3_draw_line(&v, &prev_vertex);
6087
6088                         prev_vertex = v;
6089         
6090                 }
6091         }
6092 }
6093
6094 // debug function to show all path points associated with an object
6095 void render_all_subsys_paths(object *objp)
6096 {
6097         int             i,j,color;
6098         ship            *sp = &Ships[objp->instance];
6099         polymodel       *pm;
6100         model_path      *mp;
6101
6102         pm = model_get(sp->modelnum);
6103         vector  global_path_point;
6104         vertex  v, prev_vertex;
6105
6106         if ( pm->ship_bay == NULL )
6107                 return;
6108
6109         for ( i = 0; i < pm->n_paths; i++ ) {
6110                 mp = &pm->paths[i];
6111                 for ( j = 0; j < mp->nverts; j++ ) {
6112                         vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6113                         vm_vec_add2(&global_path_point, &objp->pos);
6114                         g3_rotate_vertex(&v, &global_path_point);
6115                         color = 255 - j*50;
6116                         if ( color < 50 ) 
6117                                 color = 100;
6118                         gr_set_color(0, color, 0);
6119
6120                         if ( j == mp->nverts-1 ) {
6121                                 gr_set_color(255, 0, 0);
6122                         }
6123
6124                         g3_draw_sphere( &v, 1.5f);
6125
6126                         if ( j > 0 )
6127                                 g3_draw_line(&v, &prev_vertex);
6128
6129                         prev_vertex = v;
6130                 }
6131         }
6132 }
6133
6134 void render_path_points(object *objp)
6135 {
6136         ship            *shipp = &Ships[objp->instance];
6137         ai_info *aip = &Ai_info[shipp->ai_index];
6138         object  *dobjp;
6139         polymodel       *pm;
6140
6141         render_all_subsys_paths(objp);
6142         render_all_ship_bay_paths(objp);
6143
6144         if (aip->goal_objnum < 0)
6145                 return;
6146
6147         dobjp = &Objects[aip->goal_objnum];
6148         pm = model_get(Ships[dobjp->instance].modelnum);
6149         vector  dock_point, global_dock_point;
6150         vertex  v;
6151
6152         ship_model_start(&Objects[aip->goal_objnum]);
6153         if (pm->n_docks) {
6154                 dock_point = pm->docking_bays[0].pnt[0];
6155                 model_find_world_point(&global_dock_point, &dock_point, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6156                 g3_rotate_vertex(&v, &global_dock_point);
6157                 gr_set_color(255, 255, 255);
6158                 g3_draw_sphere( &v, 1.5f);
6159         }
6160
6161         if (aip->path_start != -1) {
6162                 vertex          prev_vertex;
6163                 pnode                   *pp = &Path_points[aip->path_start];
6164                 int                     num_points = aip->path_length;
6165                 int                     i;
6166
6167                 for (i=0; i<num_points; i++) {
6168                         vertex  v0;
6169
6170                         g3_rotate_vertex( &v0, &pp->pos );
6171
6172                         gr_set_color(0, 128, 96);
6173                         if (i != 0)
6174                                 g3_draw_line(&v0, &prev_vertex);
6175
6176                         if (pp-Path_points == aip->path_cur)
6177                                 gr_set_color(255,255,0);
6178                         
6179                         g3_draw_sphere( &v0, 4.5f);
6180
6181                         //      Connect all the turrets that can fire upon this point to this point.
6182 /*                      if (0) { //pp->path_index != -1) {
6183                                 model_path      *pmp;
6184                                 mp_vert         *pmpv;
6185
6186                                 get_base_path_info(pp->path_index, aip->goal_objnum, &pmp, &pmpv);
6187
6188                                 if (pmpv->nturrets) {
6189                                         for (int j = 0; j<pmpv->nturrets; j++) {
6190                                                 vertex  v1;
6191                                                 vector  turret_pos;
6192                                                 ship_subsys     *ssp;
6193
6194                                                 ssp = ship_get_indexed_subsys(&Ships[Objects[aip->goal_objnum].instance], pmpv->turret_ids[j]);
6195
6196 model_find_world_point(&turret_pos, &ssp->system_info->pnt, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6197         
6198                                                 g3_rotate_vertex(&v1, &turret_pos);
6199                                                 gr_set_color(255, 255, 0);
6200                                                 g3_draw_line(&v0, &v1);
6201                                                 g3_draw_sphere( &v1, 1.5f);
6202                                         }
6203                                 }
6204                         } */
6205
6206                         prev_vertex = v0;
6207
6208                         pp++;
6209                 }
6210         }
6211
6212         ship_model_stop(&Objects[aip->goal_objnum]);
6213 }
6214
6215 // Return the distance that the current AI weapon will travel
6216 float ai_get_weapon_dist(ship_weapon *swp)
6217 {
6218         int     bank_num, weapon_num;
6219
6220         bank_num = swp->current_primary_bank;
6221         weapon_num = swp->primary_bank_weapons[bank_num];
6222
6223         //      If weapon_num is illegal, return a reasonable value.  A valid weapon
6224         //      will get selected when this ship tries to fire.
6225         if (weapon_num == -1) {
6226                 // Int3();
6227                 return 1000.0f;
6228         }
6229
6230         return Weapon_info[weapon_num].max_speed * Weapon_info[weapon_num].lifetime;
6231 }
6232
6233 float ai_get_weapon_speed(ship_weapon *swp)
6234 {
6235         int     bank_num, weapon_num;
6236
6237         bank_num = swp->current_primary_bank;
6238         if (bank_num < 0)
6239                 return 100.0f;
6240
6241         weapon_num = swp->primary_bank_weapons[bank_num];
6242
6243         if (weapon_num == -1) {
6244                 //Int3();
6245                 return 100.0f;
6246         }
6247
6248         return Weapon_info[weapon_num].max_speed;
6249 }
6250
6251 //      Compute the predicted position of a ship to be fired upon from a turret.
6252 //      This is based on position of firing gun, enemy object, weapon speed and skill level constraints.
6253 //      Return value in *predicted_enemy_pos.
6254 //      Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6255 //      *pobjp          object firing the weapon
6256 //      *eobjp          object being fired upon
6257 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)
6258 {
6259         ship    *shipp = &Ships[pobjp->instance];
6260         float   range_time;
6261
6262         //weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6263
6264         if (weapon_speed < 1.0f)
6265                 weapon_speed = 1.0f;
6266
6267         range_time = 2.0f;
6268
6269         //      Make it take longer for enemies to get player's allies in range based on skill level.
6270         if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team)
6271                 range_time += In_range_time[Game_skill_level];
6272
6273         //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6274
6275         if (time_enemy_in_range < range_time) {
6276                 float   dist;
6277
6278                 dist = vm_vec_dist_quick(&pobjp->pos, enemy_pos);
6279                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, time_enemy_in_range * dist/weapon_speed);
6280         } else {
6281                 float   collision_time, scale;
6282                 vector  rand_vec;
6283                 ai_info *aip = &Ai_info[shipp->ai_index];
6284
6285                 collision_time = compute_collision_time(enemy_pos, enemy_vel, gun_pos, weapon_speed);
6286
6287                 if (collision_time == 0.0f){
6288                         collision_time = 100.0f;
6289                 }
6290
6291                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, collision_time);
6292                 if (time_enemy_in_range > 2*range_time){
6293                         scale = (1.0f - aip->ai_accuracy) * 4.0f;
6294                 } else {
6295                         scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - time_enemy_in_range/(2*range_time)));
6296                 }               
6297
6298                 static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6299
6300                 vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6301                 G_collision_time = collision_time;
6302                 G_fire_pos = *gun_pos;
6303         }
6304
6305         G_predicted_pos = *predicted_enemy_pos;
6306 }
6307
6308 //      Compute the predicted position of a ship to be fired upon.
6309 //      This is based on current position of firing object, enemy object, relative position of gun on firing object,
6310 //      weapon speed and skill level constraints.
6311 //      Return value in *predicted_enemy_pos.
6312 //      Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6313 void set_predicted_enemy_pos(vector *predicted_enemy_pos, object *pobjp, object *eobjp, ai_info *aip)
6314 {
6315         float   weapon_speed, range_time;
6316         ship    *shipp = &Ships[pobjp->instance];
6317
6318         weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6319         weapon_speed = max(weapon_speed, 1.0f);         // set not less than 1
6320
6321         range_time = 2.0f;
6322
6323         //      Make it take longer for enemies to get player's allies in range based on skill level.
6324         // but don't bias team v. team missions
6325         if ( !((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) ) {
6326                 if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team) {
6327                         range_time += In_range_time[Game_skill_level];
6328                 }
6329         }
6330         //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6331
6332         if (aip->time_enemy_in_range < range_time) {
6333                 float   dist;
6334
6335                 dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6336                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, aip->time_enemy_in_range * dist/weapon_speed);
6337         } else {
6338                 float   collision_time;
6339                 vector  gun_pos, pnt;
6340                 polymodel *po = model_get( Ship_info[shipp->ship_info_index].modelnum );
6341
6342                 //      Compute position of gun in absolute space and use that as fire position.
6343                 if(po->gun_banks != NULL){
6344                         pnt = po->gun_banks[0].pnt[0];
6345                 } else {
6346                         pnt = Objects[shipp->objnum].pos;
6347                 }
6348                 vm_vec_unrotate(&gun_pos, &pnt, &pobjp->orient);
6349                 vm_vec_add2(&gun_pos, &pobjp->pos);
6350
6351                 collision_time = compute_collision_time(&eobjp->pos, &eobjp->phys_info.vel, &gun_pos, weapon_speed);
6352
6353                 if (collision_time == 0.0f) {
6354                         collision_time = 100.0f;
6355                 }
6356
6357                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, collision_time);
6358
6359                 // set globals
6360                 G_collision_time = collision_time;
6361                 G_fire_pos = gun_pos;
6362         }
6363
6364         // Now add error terms (1) regular aim (2) EMP (3) stealth
6365         float scale = 0.0f;
6366         vector rand_vec;
6367
6368         // regular skill level error in aim
6369         if (aip->time_enemy_in_range > 2*range_time) {
6370                 scale = (1.0f - aip->ai_accuracy) * 4.0f;
6371         } else {
6372                 scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - aip->time_enemy_in_range/(2*range_time)));
6373         }
6374
6375         // if this ship is under the effect of an EMP blast, throw his aim off a bit
6376         if (shipp->emp_intensity > 0.0f) {
6377                 // never go lower than 1/2 of the EMP effect max, otherwise things aren't noticeable
6378                 scale += (MAX_EMP_INACCURACY * (shipp->emp_intensity < 0.5f ? 0.5f : shipp->emp_intensity));
6379                 mprintf(("AI miss scale factor (EMP) %f\n",scale));
6380         }
6381
6382         // if stealthy ship, throw his aim off, more when farther away and when dot is small
6383         if ( aip->ai_flags & AIF_STEALTH_PURSIUT ) {
6384                 float dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6385                 vector temp;
6386                 vm_vec_sub(&temp, &eobjp->pos, &pobjp->pos);
6387                 vm_vec_normalize_quick(&temp);
6388                 float dot = vm_vec_dotprod(&temp, &pobjp->orient.v.fvec);
6389                 float st_err = 3.0f * (1.4f - dot) * (1.0f + dist / (get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST)) * (1 - aip->ai_accuracy);
6390                 scale += st_err;
6391                 // mprintf(("error term: %.1f, total %.1f, dot %.3f\n", st_err, scale, dot));
6392         }
6393
6394         // get a random vector that changes slowly over time (1x / sec)
6395         static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6396
6397         vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6398
6399         // set global
6400         G_predicted_pos = *predicted_enemy_pos;
6401 }
6402
6403 //      Handler of submode for Chase.  Go into a continuous turn for awhile.
6404 void ai_chase_ct()
6405 {
6406         vector          tvec;
6407         ship_info       *sip;
6408         ai_info         *aip;
6409
6410         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6411         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6412         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6413         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6414
6415         //      Make a continuous turn towards any combination of possibly negated
6416         // up and right vectors.
6417         tvec = Pl_objp->pos;
6418
6419         if (aip->submode_parm0 & 0x01)
6420                 vm_vec_add2(&tvec, &Pl_objp->orient.v.rvec);
6421         if (aip->submode_parm0 & 0x02)
6422                 vm_vec_sub2(&tvec, &Pl_objp->orient.v.rvec);
6423         if (aip->submode_parm0 & 0x04)
6424                 vm_vec_add2(&tvec, &Pl_objp->orient.v.uvec);
6425         if (aip->submode_parm0 & 0x08)
6426                 vm_vec_sub2(&tvec, &Pl_objp->orient.v.uvec);
6427
6428         //      Detect degenerate cases that cause tvec to be same as player pos.
6429         if (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
6430                 aip->submode_parm0 &= 0x05;
6431                 if (aip->submode_parm0 == 0)
6432                         aip->submode_parm0 = 1;
6433                 vm_vec_add2(&tvec, &Pl_objp->orient.v.rvec);
6434         }
6435
6436         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6437         accelerate_ship(aip, 1.0f);
6438 }
6439
6440 //      ATTACK submode handler for chase mode.
6441 void ai_chase_eb(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
6442 {
6443         vector  _pep;
6444         float           dot_to_enemy, dot_from_enemy;
6445
6446         compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
6447
6448         //      If we're trying to slow down to get behind, then point to turn towards is different.
6449         _pep = *predicted_enemy_pos;
6450         if ((dot_to_enemy > dot_from_enemy + 0.1f) || (dot_to_enemy > 0.9f))
6451                 vm_vec_scale_add(&_pep, &Pl_objp->pos, &En_objp->orient.v.fvec, 100.0f);
6452
6453         ai_turn_towards_vector(&_pep, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6454
6455         accelerate_ship(aip, 0.0f);
6456 }
6457
6458 //      Return time until weapon_objp might hit ship_objp.
6459 //      Assumes ship_objp is not moving.
6460 //      Returns negative time if not going to hit.
6461 //      This is a very approximate function, but is pretty fast.
6462 float ai_endangered_time(object *ship_objp, object *weapon_objp)
6463 {
6464         float           to_dot, from_dot, dist;
6465
6466         dist = compute_dots(ship_objp, weapon_objp, &to_dot, &from_dot);
6467
6468         //      Note, this is bogus.  It assumes only the weapon is moving.
6469         //      Only proceed if weapon sort of pointing at object and object pointing towards or away from weapon
6470         //      (Ie, if object moving at right angle to weapon, just continue for now...)
6471         if (weapon_objp->phys_info.speed < 1.0f)
6472                 return dist + 1.0f;
6473         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))
6474                 return dist / weapon_objp->phys_info.speed;
6475         else
6476                 return -1.0f;
6477 }
6478
6479 //      Return time until danger weapon could hit this ai object.
6480 //      Return negative time if not endangered.
6481 float ai_endangered_by_weapon(ai_info *aip)
6482 {
6483         object  *weapon_objp;
6484
6485         if (aip->danger_weapon_objnum == -1) {
6486                 return -1.0f;
6487         }
6488
6489         weapon_objp = &Objects[aip->danger_weapon_objnum];
6490
6491         if (weapon_objp->signature != aip->danger_weapon_signature) {
6492                 aip->danger_weapon_objnum = -1;
6493                 return -1.0f;
6494         }
6495
6496         return ai_endangered_time(&Objects[Ships[aip->shipnum].objnum], weapon_objp);
6497 }
6498
6499 //      Return true if this ship is near full strength.
6500 int ai_near_full_strength(object *objp, ship_info *sip)
6501 {
6502         return (objp->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp)/sip->shields > 0.8f);
6503 }
6504                                 
6505 //      Set acceleration while in attack mode.
6506 void attack_set_accel(ai_info *aip, float dist_to_enemy, float dot_to_enemy, float dot_from_enemy)
6507 {
6508         float   speed_ratio;
6509
6510         if (En_objp->phys_info.speed > 1.0f)
6511                 speed_ratio = Pl_objp->phys_info.speed/En_objp->phys_info.speed;
6512         else
6513                 speed_ratio = 5.0f;
6514
6515         //      Sometimes, told to attack slowly.  Allows to get in more hits.
6516         if (aip->ai_flags & AIF_ATTACK_SLOWLY) {
6517                 if ((dist_to_enemy > 200.0f) && (dist_to_enemy < 800.0f)) {
6518                         if ((dot_from_enemy < 0.9f) || ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
6519                                 //nprintf(("AI", " slowly "));
6520                                 accelerate_ship(aip, max(1.0f - (dist_to_enemy-200.0f)/600.0f, 0.1f));
6521                                 return;
6522                         }
6523                 } else
6524                         aip->ai_flags &= ~AIF_ATTACK_SLOWLY;
6525         }
6526
6527         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) {
6528                 //nprintf(("AI", "1"));
6529                 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6530                         if (dist_to_enemy > 800.0f) {
6531                                 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6532                                         float percent_left;
6533                                         ship    *shipp;
6534                                         ship_info *sip;
6535
6536                                         shipp = &Ships[Pl_objp->instance];
6537                                         sip = &Ship_info[shipp->ship_info_index];
6538
6539                                         if (sip->afterburner_fuel_capacity > 0.0f) {
6540                                                 percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
6541                                                 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
6542                                                         afterburners_start(Pl_objp);
6543                                                         aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
6544                                                 }
6545                                         }
6546                                 }
6547                         }
6548                 }
6549
6550                 accelerate_ship(aip, 1.0f);
6551         } else if ((Missiontime - aip->last_hit_time > F1_0*7)
6552                 && (En_objp->phys_info.speed < 10.0f) 
6553                 && (dist_to_enemy > 25.0f) 
6554                 && (dot_to_enemy > 0.8f)
6555                 && (dot_from_enemy < 0.8f)) {
6556                 accelerate_ship(aip, 0.0f);             //      No one attacking us, so don't need to move.
6557         } else if ((dot_from_enemy < 0.25f) && (dot_to_enemy > 0.5f)) {
6558                 set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed);
6559         } else if (Pl_objp->phys_info.speed < 15.0f) {
6560                 accelerate_ship(aip, 1.0f);
6561         } else if (Pl_objp->phys_info.speed > En_objp->phys_info.speed - 1.0f) {
6562                 if (dot_from_enemy > 0.75f)
6563                         accelerate_ship(aip, 1.0f);
6564                 else
6565                         set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed*0.75f + 3.0f);
6566         } else {
6567                 change_acceleration(aip, 0.5f);
6568         }
6569 }
6570
6571 //      Pl_objp (aip) tries to get behind En_objp.
6572 //      New on 2/21/98: If this ship can move backwards and slide, maybe do that to get behind.
6573 void get_behind_ship(ai_info *aip, ship_info *sip, float dist_to_enemy)
6574 {
6575         vector  new_pos;
6576         float           dot;
6577         vector  vec_from_enemy;
6578         float           dist;
6579
6580         dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
6581
6582         vm_vec_scale_add(&new_pos, &En_objp->pos, &En_objp->orient.v.fvec, -100.0f);            //      Pick point 100 units behind.
6583         ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6584
6585         dot = vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.fvec);
6586
6587         if (dot > 0.25f) {
6588                 accelerate_ship(aip, 1.0f);
6589         } else {
6590                 accelerate_ship(aip, (dot + 1.0f)/2.0f);
6591         }
6592 }
6593
6594 int avoid_player(object *objp, vector *goal_pos)
6595 {
6596         maybe_avoid_player(Pl_objp, goal_pos);
6597         ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
6598
6599         if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6600                 ship_info *sip = &Ship_info[Ships[objp->instance].ship_info_index];
6601
6602                 if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6603                         ai_turn_towards_vector(&aip->avoid_goal_point, objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6604                         accelerate_ship(aip, 0.5f);
6605                         return 1;
6606                 }
6607         }
6608
6609         return 0;
6610 }
6611
6612 //      Determine if a cylinder of width radius from p0 to p1 will collide with big_objp.
6613 //      If so, stuff *collision_point.
6614 int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6615 {
6616         mc_info mc;
6617
6618         mc.model_num = Ships[big_objp->instance].modelnum;              // Fill in the model to check
6619         mc.orient = &big_objp->orient;                  // The object's orient
6620         mc.pos = &big_objp->pos;                                        // The object's position
6621         mc.p0 = p0;                                                                             // Point 1 of ray to check
6622         mc.p1 = p1;
6623         mc.flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE | MC_SUBMODEL;                                  // flags
6624
6625         mc.radius = radius;
6626
6627         // Only check the 2nd lowest hull object
6628         polymodel *pm = model_get(Ships[big_objp->instance].modelnum);
6629         mc.submodel_num = pm->detail[0]; //pm->submodel->num_details-2];
6630         model_collide(&mc);
6631
6632         if (mc.num_hits)
6633                 *collision_point = mc.hit_point_world;
6634
6635         return mc.num_hits;
6636 }
6637
6638 //      Return true/false if *objp will collide with *big_objp
6639 //      Stuff distance in *distance to collision point if *objp will collide with *big_objp within delta_time seconds.
6640 //      Global collision point stuffed in *collision_point
6641 int will_collide_with_big_ship(object *objp, vector *goal_point, object *big_objp, vector *collision_point, float delta_time)
6642 {
6643         float           radius;
6644         vector  end_pos;
6645
6646         radius = big_objp->radius + delta_time * objp->phys_info.speed;
6647
6648         if (vm_vec_dist_quick(&big_objp->pos, &objp->pos) > radius) {
6649                 return 0;
6650         }
6651
6652         if (goal_point == NULL) {
6653                 vm_vec_scale_add(&end_pos, &objp->pos, &objp->phys_info.vel, delta_time);                                       // Point 2 of ray to check
6654         } else {
6655                 end_pos = *goal_point;
6656         }
6657
6658         return will_collide_pp(&objp->pos, &end_pos, objp->radius, big_objp, collision_point);
6659 }
6660
6661 //      Return true if *objp is expected to collide with a large ship.
6662 //      Stuff global collision point in *collision_point.
6663 //      If *goal_point is not NULL, use that as the point towards which *objp will be flying.  Don't use *objp velocity
6664 //      *ignore_objp will typically be the target this ship is pursuing, either to attack or guard.  We don't want to avoid it.
6665 int will_collide_with_big_ship_all(object *objp, object *ignore_objp, vector *goal_point, vector *collision_point, float *distance, float delta_time)
6666 {
6667         ship_obj        *so;
6668         object  *big_objp;
6669         int             collision_obj_index = -1;
6670         float           min_dist = 999999.9f;
6671         float           collision_time = -1.0f;
6672
6673         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6674                 float   time = 0.0f;
6675                 big_objp = &Objects[so->objnum];
6676
6677                 if (big_objp == ignore_objp)
6678                         continue;
6679
6680                 if (Ship_info[Ships[big_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
6681                         vector  cur_collision_point;
6682                         float           cur_dist;
6683
6684                         if (will_collide_with_big_ship(objp, goal_point, big_objp, &cur_collision_point, delta_time)) {
6685
6686                                 cur_dist = vm_vec_dist(&cur_collision_point, &objp->pos);
6687
6688                                 if (cur_dist < min_dist) {
6689                                         min_dist = cur_dist;
6690                                         *collision_point = cur_collision_point;
6691                                         collision_time = time;
6692                                         collision_obj_index = OBJ_INDEX(big_objp);
6693                                 }
6694                         }
6695                 }
6696         }
6697
6698         *distance = min_dist;
6699         return collision_obj_index;
6700
6701 }
6702
6703 typedef struct {
6704         float           dist;
6705         int             collide;
6706         vector  pos;
6707 } sgoal;
6708
6709 //int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6710 //      Pick a point for *objp to fly towards to avoid a collision with *big_objp at *collision_point
6711 //      Return result in *avoid_pos
6712 void mabs_pick_goal_point(object *objp, object *big_objp, vector *collision_point, vector *avoid_pos)
6713 {
6714         matrix  mat1;
6715         sgoal           goals[4];
6716         vector  v2b;
6717
6718         vm_vec_normalized_dir(&v2b, collision_point, &objp->pos);
6719         vm_vector_2_matrix(&mat1, &v2b, NULL, NULL);
6720
6721         int     found = 0;
6722
6723         //      Try various scales, in 0.5f, 0.75f, 1.0f, 1.25f.
6724         //      First try 0.5f to see if we can find a point that near the center of the target ship, which presumably
6725         //      means less of a turn.
6726         //      Try going as far as 1.25f * radius.
6727         float   s;
6728         for (s=0.5f; s<1.3f; s += 0.25f) {
6729                 int     i;
6730                 for (i=0; i<4; i++) {
6731                         vector p = big_objp->pos;
6732                         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
6733                         float kr = big_objp->radius*s + objp->radius * ((OBJ_INDEX(objp) % 4) ^ 2)/4;
6734                         if (i&1)
6735                                 ku = -ku;
6736                         if (i&2)
6737                                 kr = -kr;
6738                         vm_vec_scale_add2(&p, &mat1.v.uvec, ku);
6739                         vm_vec_scale_add2(&p, &mat1.v.rvec, kr);
6740                         goals[i].pos = p;
6741                         goals[i].dist = vm_vec_dist_quick(&objp->pos, &p);
6742                         goals[i].collide = will_collide_pp(&objp->pos, &p, objp->radius, big_objp, collision_point);
6743                         if (!goals[i].collide)
6744                                 found = 1;
6745                 }
6746
6747                 //      If we found a point that doesn't collide, find the nearest one and make that the *avoid_pos.
6748                 if (found) {
6749                         float   min_dist = 9999999.9f;
6750                         int     min_index = -1;
6751
6752                         for (i=0; i<4; i++) {
6753                                 if (!goals[i].collide && (goals[i].dist < min_dist)) {
6754                                         min_dist = goals[i].dist;
6755                                         min_index = i;
6756                                 }
6757                         }
6758
6759                         Assert(i != -1);
6760                         if (i != -1) {
6761                                 *avoid_pos = goals[min_index].pos;
6762                                 return;
6763                         }
6764                 }
6765         }
6766
6767         //      Drat.  We tried and tried and could not find a point that did not cause a collision.
6768         //      Get this dump pilot far away from the problem ship.
6769         vector  away_vec;
6770         vm_vec_normalized_dir(&away_vec, &objp->pos, collision_point);
6771         vm_vec_scale_add(avoid_pos, &objp->pos, &away_vec, big_objp->radius*1.5f);
6772
6773 }
6774
6775 //      Return true if a large ship is being ignored.
6776 int maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vector *goal_point, float delta_time)
6777 {
6778         if (timestamp_elapsed(aip->avoid_check_timestamp)) {
6779                 float           distance;
6780                 vector  collision_point;
6781                 int             ship_num;
6782                 if ((ship_num = will_collide_with_big_ship_all(Pl_objp, ignore_objp, goal_point, &collision_point, &distance, delta_time)) != -1) {
6783                         aip->ai_flags |= AIF_AVOIDING_BIG_SHIP;
6784                         mabs_pick_goal_point(objp, &Objects[ship_num], &collision_point, &aip->avoid_goal_point);
6785                         float dist = vm_vec_dist_quick(&aip->avoid_goal_point, &objp->pos);
6786                         aip->avoid_check_timestamp = timestamp(2000 + min(1000, (int) (dist * 2.0f)));  //      Delay until check again is based on distance to avoid point.
6787                         aip->avoid_ship_num = ship_num;
6788                 } else {
6789                         aip->ai_flags &= ~AIF_AVOIDING_BIG_SHIP;
6790                         aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
6791                         aip->avoid_ship_num = -1;
6792                         aip->avoid_check_timestamp = timestamp(1500);
6793                 }
6794         }
6795         
6796         if (aip->ai_flags & AIF_AVOIDING_BIG_SHIP) {
6797                 ship_info *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6798
6799                 vector  v2g;
6800
6801                 ai_turn_towards_vector(&aip->avoid_goal_point, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6802                 vm_vec_normalized_dir(&v2g, &aip->avoid_goal_point, &Pl_objp->pos);
6803                 float dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
6804                 float d2 = (1.0f + dot) * (1.0f + dot);
6805                 accelerate_ship(aip, d2/4.0f);
6806                 return 1;
6807         }
6808
6809         return 0;
6810 }
6811
6812 //      Set desired right vector for ships flying towards another ship.
6813 //      Since this is governed only by vector to target, it causes ships to align bank and look less chaotic.
6814 void compute_desired_rvec(vector *rvec, vector *goal_pos, vector *cur_pos)
6815 {
6816         vector  v2e;
6817
6818         vm_vec_normalized_dir(&v2e, goal_pos, cur_pos);
6819         rvec->xyz.x = v2e.xyz.z;
6820         rvec->xyz.y = 0.0f;
6821         rvec->xyz.z = -v2e.xyz.x;
6822         if (vm_vec_mag_squared(rvec) < 0.001f)
6823                 rvec->xyz.y = 1.0f;
6824 }
6825
6826 // Handler for stealth find submode of Chase.
6827 void ai_stealth_find()
6828 {
6829         ai_info         *aip;
6830         ship_info       *sip;
6831
6832         vector new_pos, vec_to_enemy;
6833         float dist_to_enemy, dot_to_enemy, dot_from_enemy;
6834
6835         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6836         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6837         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6838         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6839
6840         // get time since last seen
6841         int delta_time = (timestamp() - aip->stealth_last_visible_stamp);
6842
6843         // if delta_time is really big, i'm real confused, start sweep
6844         if (delta_time > 10000) {
6845                 aip->submode_parm0 = SM_SF_BAIL;
6846         }
6847
6848         // guestimate new position
6849         vm_vec_scale_add(&new_pos, &aip->stealth_last_pos, &aip->stealth_velocity, (delta_time * 0.001f));
6850
6851         // if I think he's behind me, go to the goal point
6852         if ( aip->submode_parm0 == SM_SF_BEHIND ) {
6853                 new_pos = aip->goal_point;
6854         }
6855
6856         // check for collision with big ships
6857         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &new_pos, 10.0f)) {
6858                 // reset ai submode to chase
6859                 return;
6860         }
6861
6862         // if dist is near max and dot is close to 1, accel, afterburn
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         // 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
6868         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) ) {
6869                 // do turn around)
6870                 vm_vec_scale_add(&aip->goal_point, &Pl_objp->pos, &Pl_objp->orient.v.fvec, -300.0f);
6871                 aip->submode_parm0 = SM_SF_BEHIND;
6872                 vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6873                 dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6874                 dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.v.fvec);
6875         }
6876
6877         if ( (dist_to_enemy > get_skill_stealth_dist_scaler()*STEALTH_MAX_VIEW_DIST) && (dot_to_enemy > 0.94f) ) {              // 20 degree half angle
6878                 // accelerate ship
6879                 accelerate_ship(aip, 1.0f);
6880
6881                 // engage afterburner
6882                 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6883                         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6884                                 afterburners_start(Pl_objp);
6885                                 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
6886                         }
6887                 }
6888
6889                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6890                 return;
6891         }
6892
6893         //      If enemy more than 500 meters away, all ships flying there will tend to match bank.
6894         //      They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
6895         //      to interpolate a matrix rather than just a vector.
6896         if (dist_to_enemy > 500.0f) {
6897                 vector  rvec;
6898                 compute_desired_rvec(&rvec, &new_pos, &Pl_objp->pos);
6899                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0, &rvec);
6900         } else {
6901                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6902         }
6903
6904         dot_from_enemy = -vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec);
6905
6906         attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
6907 }
6908
6909 // -----------------------------------------------------------------------------
6910 // try to find stealth ship by sweeping an area
6911 void ai_stealth_sweep()
6912 {
6913         ai_info         *aip;
6914         ship_info       *sip;
6915
6916         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6917         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6918         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6919         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6920
6921         vector goal_pt;
6922         vector forward, right, up;
6923         int lost_time;
6924
6925         // time since stealth last seen
6926         lost_time = (timestamp() - aip->stealth_last_visible_stamp);
6927
6928         // determine which pt to fly to in sweep by keeping track of parm0
6929         if (aip->submode_parm0 == SM_SS_SET_GOAL) {
6930
6931                 // don't make goal pt more than 2k from current pos
6932                 vm_vec_scale_add(&goal_pt, &aip->stealth_last_pos, &aip->stealth_velocity, (0.001f * lost_time));
6933
6934                 // make box size based on speed of stealth and expected time to intercept (keep box in range 200-500)
6935                 float box_size = vm_vec_mag_quick(&aip->stealth_velocity) * (0.001f * lost_time);
6936                 box_size = min(200.0f, box_size);
6937                 box_size = max(500.0f, box_size);
6938                 aip->stealth_sweep_box_size = box_size;
6939
6940                 aip->goal_point = goal_pt;
6941                 aip->submode_parm0 = SM_SS_BOX0;
6942         }
6943
6944         // GET UP, RIGHT, FORWARD FOR BOX based on stealth ship's velocity
6945         // if velocity changes in stealth mode, then ship is *seen*, and falls out of sweep mode
6946         // if stealth has no velocity make a velocity
6947         if ( vm_vec_mag_quick(&aip->stealth_velocity) < 1 ) {
6948                 vm_vec_rand_vec_quick(&aip->stealth_velocity);
6949         }
6950
6951         // get "right" vector for box
6952         vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_y_vector);
6953
6954         if ( vm_vec_mag_quick(&right) < 0.01 ) {
6955                 vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_z_vector);
6956         }
6957
6958         vm_vec_normalize_quick(&right);
6959
6960         // get forward for box
6961         vm_vec_copy_normalize_quick(&forward, &aip->stealth_velocity);
6962
6963         // get "up" for box
6964         vm_vec_crossprod(&up, &forward, &right);
6965         
6966         // lost far away ahead (do box)
6967         switch(aip->submode_parm0) {
6968         case SM_SS_BOX0:
6969                 goal_pt = aip->goal_point;
6970                 break;
6971
6972         // pt1 -U +R
6973         case SM_SS_LR:
6974                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
6975                 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
6976                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6977                 break;
6978
6979         // pt2 +U -R
6980         case SM_SS_UL:
6981                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
6982                 vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
6983                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6984                 break;
6985
6986         // pt3 back
6987         case SM_SS_BOX1:
6988                 goal_pt = aip->goal_point;
6989                 break;
6990
6991         // pt4 +U +R
6992         case SM_SS_UR:
6993                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
6994                 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
6995                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6996                 break;
6997
6998         // pt5 -U -R
6999         case SM_SS_LL:
7000                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
7001                 vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
7002                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
7003                 break;
7004
7005         // pt6 back
7006         case SM_SS_BOX2:
7007                 goal_pt = aip->goal_point;
7008                 break;
7009
7010         default:
7011                 Int3();
7012
7013         }
7014
7015         // when close to goal_pt, update next goal pt
7016         float dist_to_goal = vm_vec_dist(&goal_pt, &Pl_objp->pos);
7017         if (dist_to_goal < 15) {
7018                 aip->submode_parm0++;
7019         }
7020
7021         // check for collision with big ship
7022         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &goal_pt, 10.0f)) {
7023                 // skip to the next pt on box
7024                 aip->submode_parm0++;
7025                 return;
7026         }
7027
7028         ai_turn_towards_vector(&goal_pt, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
7029
7030         float dot = 1.0f;
7031         if (dist_to_goal < 100) {
7032                 vector vec_to_goal;
7033                 vm_vec_normalized_dir(&vec_to_goal, &goal_pt, &Pl_objp->pos);
7034                 dot = vm_vec_dotprod(&vec_to_goal, &Pl_objp->orient.v.fvec);
7035         }
7036
7037         accelerate_ship(aip, 0.8f*dot);
7038 }
7039
7040 //      ATTACK submode handler for chase mode.
7041 void ai_chase_attack(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
7042 {
7043         int             start_bank;
7044         float           dot_to_enemy, dot_from_enemy; //, time_to_hit;
7045         float           bank_override = 0.0f;
7046
7047         if (avoid_player(Pl_objp, predicted_enemy_pos))
7048                 return;
7049
7050         compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
7051
7052         polymodel *po = model_get( sip->modelnum );
7053
7054         vector  *rel_pos;
7055         float           scale;
7056         vector  randvec;
7057         vector  new_pos;
7058
7059         start_bank = Ships[aip->shipnum].weapons.current_primary_bank;
7060         if (po->n_guns && start_bank != -1 ) {
7061                 rel_pos = &po->gun_banks[start_bank].pnt[0];
7062         } else
7063                 rel_pos = NULL;
7064
7065         //      If ship moving slowly relative to its size, then don't attack its center point.
7066         //      How far from center we attack is based on speed, size and distance to enemy
7067         if (En_objp->radius > En_objp->phys_info.speed) {
7068                 static_randvec(Pl_objp-Objects, &randvec);
7069                 scale = dist_to_enemy/(dist_to_enemy + En_objp->radius) * En_objp->radius;
7070                 scale *= 0.5f * En_objp->radius/(En_objp->phys_info.speed + En_objp->radius);   // scale downward by 1/2 to 1/4
7071                 vm_vec_scale_add(&new_pos, predicted_enemy_pos, &randvec, scale);
7072         } else
7073                 new_pos = *predicted_enemy_pos;
7074
7075         if (dist_to_enemy < 250.0f) {
7076                 if (dot_from_enemy > 0.7f) {
7077                         bank_override = Pl_objp->phys_info.speed;
7078                 }
7079         }
7080
7081         //      If enemy more than 500 meters away, all ships flying there will tend to match bank.
7082         //      They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
7083         //      to interpolate a matrix rather than just a vector.
7084         if (dist_to_enemy > 500.0f) {
7085                 vector  rvec;
7086                 compute_desired_rvec(&rvec, predicted_enemy_pos, &Pl_objp->pos);
7087                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0, &rvec);
7088         } else {
7089                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0);
7090         }
7091
7092         attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
7093 }
7094
7095 //      EVADE_SQUIGGLE submode handler for chase mode.
7096 //      Changed by MK on 5/5/97.
7097 //      Used to evade towards a point off the right or up vector.
7098 //      Now, evade straight away to try to get far away.
7099 //      The squiggling should protect against laser fire.
7100 void ai_chase_es(ai_info *aip, ship_info *sip)
7101 {
7102         vector  tvec;
7103         fix             timeslice;
7104         fix             scale;
7105         float           bank_override = 0.0f;
7106
7107         tvec = Pl_objp->pos;
7108
7109         timeslice = (Missiontime >> 16) & 0x0f;
7110         scale = ((Missiontime >> 16) & 0x0f) << 14;
7111
7112         if (timeslice & 0x01)
7113                 vm_vec_scale_add2(&tvec, &Pl_objp->orient.v.rvec, f2fl(scale ^ 0x10000));
7114         if (timeslice & 0x02)
7115                 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.v.rvec, f2fl(scale));
7116         if (timeslice & 0x04)
7117                 vm_vec_scale_add2(&tvec, &Pl_objp->orient.v.uvec, f2fl(scale ^ 0x10000));
7118         if (timeslice & 0x08)
7119                 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.v.uvec, f2fl(scale));
7120
7121         while (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
7122                 tvec.xyz.x += frand();
7123                 tvec.xyz.y += frand();
7124         }
7125
7126         bank_override = Pl_objp->phys_info.speed;
7127
7128         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7129         accelerate_ship(aip, 1.0f);
7130 }
7131
7132 //      Trying to get away from opponent.
7133 void ai_chase_ga(ai_info *aip, ship_info *sip)
7134 {
7135         //      If not near end of this submode, evade squiggly.  If near end, just fly straight for a bit
7136         vector  tvec;
7137         float           bank_override;
7138         vector  vec_from_enemy;
7139
7140         if (En_objp != NULL) {
7141                 vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
7142         } else
7143                 vec_from_enemy = Pl_objp->orient.v.fvec;
7144
7145         static_randvec(Missiontime >> 15, &tvec);
7146         vm_vec_scale(&tvec, 100.0f);
7147         vm_vec_scale_add2(&tvec, &vec_from_enemy, 300.0f);
7148         vm_vec_add2(&tvec, &Pl_objp->pos);
7149
7150         bank_override = Pl_objp->phys_info.speed;
7151
7152         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7153
7154         accelerate_ship(aip, 2.0f);
7155
7156         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
7157                 if (!(Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
7158                         float percent_left = 100.0f * Ships[Pl_objp->instance].afterburner_fuel / sip->afterburner_fuel_capacity;
7159                         if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
7160                                 afterburners_start(Pl_objp);
7161                                 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7162                         }
7163                         afterburners_start(Pl_objp);
7164                         aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7165                 }
7166         }
7167
7168 }
7169
7170 //      Make object *objp attack subsystem with ID = subnum.
7171 //      Return true if found a subsystem to attack, else return false.
7172 //      Note, can fail if subsystem exists, but has no hits.
7173 int ai_set_attack_subsystem(object *objp, int subnum)
7174 {
7175         ship                    *shipp, *attacker_shipp;
7176         ai_info         *aip;
7177         ship_subsys     *ssp;
7178         object          *attacked_objp;
7179
7180         Assert(objp->type == OBJ_SHIP);
7181         Assert(objp->instance >= 0);
7182
7183         attacker_shipp = &Ships[objp->instance];
7184         Assert(attacker_shipp->ai_index >= 0);
7185
7186         aip = &Ai_info[attacker_shipp->ai_index];
7187
7188         // MWA -- 2/27/98.  Due to AL's changes, target_objnum is now not always valid (at least sometimes
7189         // in terms of goals).  So, bail if we don't have a valid target.
7190         if ( aip->target_objnum == -1 )
7191                 return 0;
7192
7193         attacked_objp = &Objects[aip->target_objnum];
7194         shipp = &Ships[attacked_objp->instance];                //  need to get our target's ship pointer!!!
7195
7196         ssp = ship_get_indexed_subsys(shipp, subnum, &objp->pos);
7197         if (ssp == NULL)
7198                 return 0;
7199
7200         set_targeted_subsys(aip, ssp, aip->target_objnum);
7201         
7202         if (aip->ignore_objnum == aip->target_objnum)
7203                 aip->ignore_objnum = UNUSED_OBJNUM;
7204
7205         // -- Done at caller in ai_process_mission_orders -- attacked_objp->flags |= OF_PROTECTED;
7206
7207         ai_set_goal_maybe_abort_dock(objp, aip);
7208         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7209
7210         return 1;
7211 }
7212
7213 void ai_set_guard_vec(object *objp, object *guard_objp)
7214 {
7215         ai_info *aip;
7216         float   radius;
7217
7218         aip = &Ai_info[Ships[objp->instance].ai_index];
7219
7220         //      Handle case of bogus call in which ship is told to guard self.
7221         Assert(objp != guard_objp);
7222         if (objp == guard_objp) {
7223                 vm_vec_rand_vec_quick(&aip->guard_vec);
7224                 vm_vec_scale(&aip->guard_vec, 100.0f);
7225                 return;
7226         }
7227
7228         // check if guard_objp is BIG
7229         radius = 5.0f * (objp->radius + guard_objp->radius) + 50.0f;
7230         if (radius > 300.0f) {
7231                 radius = guard_objp->radius * 1.25f;
7232         }
7233
7234         vm_vec_sub(&aip->guard_vec, &objp->pos, &guard_objp->pos);
7235
7236         if (vm_vec_mag(&aip->guard_vec) > 3.0f*radius) {
7237                 //      Far away, don't just use vector to object, causes clustering of guard ships.
7238                 vector  tvec, rvec;
7239                 float   mag;
7240                 mag = vm_vec_copy_normalize(&tvec, &aip->guard_vec);
7241                 vm_vec_rand_vec_quick(&rvec);                   
7242                 vm_vec_scale_add2(&tvec, &rvec, 0.5f);
7243                 vm_vec_copy_scale(&aip->guard_vec, &tvec, mag);
7244         }
7245
7246         vm_vec_normalize_quick(&aip->guard_vec);
7247         vm_vec_scale(&aip->guard_vec, radius);
7248 }
7249
7250 //      Make object *objp guard object *other_objp.
7251 //      To be called from the goals code.
7252 void ai_set_guard_wing(object *objp, int wingnum)
7253 {
7254         ship            *shipp;
7255         ai_info *aip;
7256         int             leader_objnum, leader_shipnum;
7257
7258         Assert(wingnum >= 0);
7259
7260         Assert(objp->type == OBJ_SHIP);
7261         Assert(objp->instance >= 0);
7262
7263         // shouldn't set the ai mode for the player
7264         if ( objp == Player_obj ) {
7265                 return;
7266         }
7267
7268         shipp = &Ships[objp->instance];
7269
7270         Assert(shipp->ai_index >= 0);
7271
7272         aip = &Ai_info[shipp->ai_index];
7273         force_avoid_player_check(objp, aip);
7274
7275         ai_set_goal_maybe_abort_dock(objp, aip);
7276         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7277
7278         //      This function is called whenever a guarded ship is destroyed, so this code
7279         //      prevents a ship from trying to guard a non-existent wing.
7280         if (Wings[wingnum].current_count < 1) {
7281                 aip->guard_objnum = -1;
7282                 aip->guard_wingnum = -1;
7283                 aip->mode = AIM_NONE;
7284         } else {
7285                 leader_shipnum = Wings[wingnum].ship_index[0];
7286                 leader_objnum = Ships[leader_shipnum].objnum;
7287
7288                 Assert((leader_objnum >= 0) && (leader_objnum < MAX_OBJECTS));
7289                 //Assert(leader_objnum != objp-Objects);        //      Don't allow ships to guard themselves.
7290                 if (leader_objnum == OBJ_INDEX(objp)) {
7291                         //Int3();       //      Seems illegal, but let's clean up.  Get MikeK.
7292                         return;
7293                 }
7294
7295                 aip->guard_wingnum = wingnum;
7296                 aip->guard_objnum = leader_objnum;
7297                 aip->guard_signature = Objects[leader_objnum].signature;
7298                 aip->mode = AIM_GUARD;
7299                 aip->submode = AIS_GUARD_STATIC;
7300
7301                 ai_set_guard_vec(objp, &Objects[leader_objnum]);
7302         }
7303 }
7304
7305 //      Make object *objp guard object *other_objp.
7306 //      To be called from the goals code.
7307 void ai_set_evade_object(object *objp, object *other_objp)
7308 {
7309         ship            *shipp;
7310         ai_info *aip;
7311         int             other_objnum;
7312
7313         Assert(objp->type == OBJ_SHIP);
7314         Assert(objp->instance >= 0);
7315
7316         shipp = &Ships[objp->instance];
7317
7318         Assert(shipp->ai_index >= 0);
7319
7320         aip = &Ai_info[shipp->ai_index];
7321
7322         other_objnum = OBJ_INDEX(other_objp);
7323         Assert(other_objnum >= 0);
7324
7325         Assert(other_objnum != Ships[aip->shipnum].objnum);     //      make sure not targeting self
7326         aip->target_objnum = other_objnum;
7327
7328         aip->mode = AIM_EVADE;
7329 }
7330
7331 //      Make objp guard other_objp
7332 //      If other_objp is a member of a wing, objp will guard that whole wing
7333 //      UNLESS objp is also a member of the wing!
7334 void ai_set_guard_object(object *objp, object *other_objp)
7335 {
7336         ship            *shipp;
7337         ai_info *aip;
7338         int             other_objnum;
7339
7340         Assert(objp->type == OBJ_SHIP);
7341         Assert(objp->instance >= 0);
7342         Assert(objp != other_objp);
7343
7344         shipp = &Ships[objp->instance];
7345
7346         Assert(shipp->ai_index >= 0);
7347
7348         aip = &Ai_info[shipp->ai_index];
7349         aip->avoid_check_timestamp = timestamp(1);
7350
7351         //      If ship to guard is in a wing, guard that whole wing.
7352         ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
7353         if ((other_aip->wing != -1) && (other_aip->wing != aip->wing)) {
7354                 ai_set_guard_wing(objp, Ai_info[Ships[other_objp->instance].ai_index].wing);
7355         } else {
7356
7357                 other_objnum = other_objp-Objects;
7358
7359                 aip->guard_objnum = other_objnum;
7360                 aip->guard_signature = other_objp->signature;
7361                 aip->guard_wingnum = -1;
7362
7363                 aip->mode = AIM_GUARD;
7364                 aip->submode = AIS_GUARD_STATIC;
7365
7366                 Assert(other_objnum >= 0);      //      Hmm, bogus object and we need its position for guard_vec.
7367
7368                 // vm_vec_sub(&aip->guard_vec, &objp->pos, &Objects[other_objnum].pos);
7369                 ai_set_guard_vec(objp, &Objects[other_objnum]);
7370
7371                 ai_set_goal_maybe_abort_dock(objp, aip);
7372                 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7373         }
7374 }
7375
7376 //      Update the aspect_locked_time field based on whether enemy is in view cone.
7377 //      Also set/clear AIF_SEEK_LOCK.
7378 void update_aspect_lock_information(ai_info *aip, vector *vec_to_enemy, float dist_to_enemy, float enemy_radius)
7379 {
7380         float   dot_to_enemy;
7381         int     num_weapon_types;
7382         int     weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
7383         ship    *shipp;
7384         ship_weapon     *swp;
7385         weapon_info     *wip;
7386
7387         shipp = &Ships[aip->shipnum];
7388         swp = &shipp->weapons;
7389
7390         // AL 3-7-98: This probably should never happen, but check to ensure that current_secondary_bank is valid
7391         if ( (swp->current_secondary_bank < 0) || (swp->current_secondary_bank > swp->num_secondary_banks) ) {
7392                 return;
7393         }
7394
7395         num_weapon_types = get_available_secondary_weapons(Pl_objp, weapon_id_list, weapon_bank_list);
7396
7397         wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
7398
7399         if (num_weapon_types && (wip->wi_flags & WIF_HOMING_ASPECT)) {
7400                 if (dist_to_enemy > 300.0f - min(enemy_radius, 100.0f))
7401                         aip->ai_flags |= AIF_SEEK_LOCK;
7402                 else
7403                         aip->ai_flags &= ~AIF_SEEK_LOCK;
7404
7405                 //      Update locking information for aspect seeking missiles.
7406                 aip->current_target_is_locked = 0;
7407                 dot_to_enemy = vm_vec_dot(vec_to_enemy, &Pl_objp->orient.v.fvec);
7408
7409                 float   needed_dot = 0.9f - 0.5f * enemy_radius/(dist_to_enemy + enemy_radius); //      Replaced MIN_TRACKABLE_DOT with 0.9f
7410                 if (dot_to_enemy > needed_dot) {
7411                         aip->aspect_locked_time += flFrametime;
7412                         // nprintf(("AI", "+ Lock time = %7.3f\n", aip->aspect_locked_time));
7413                         if (aip->aspect_locked_time >= wip->min_lock_time) {
7414                                 aip->aspect_locked_time = wip->min_lock_time;
7415                                 aip->current_target_is_locked = 1;
7416                         }
7417                 } else {
7418                         aip->aspect_locked_time -= flFrametime*2;
7419                         // nprintf(("AI", "- Lock time = %7.3f\n", aip->aspect_locked_time));
7420                         if (aip->aspect_locked_time < 0.0f)
7421                                 aip->aspect_locked_time = 0.0f;
7422                 }
7423                 //nprintf(("AI", "dot = %7.3f, time = %7.3f\n", dot_to_enemy, aip->aspect_locked_time));
7424         
7425         } else {
7426                 aip->current_target_is_locked = 0;
7427                 aip->aspect_locked_time = 0.0f; // Used to be this, why?: wip->min_lock_time;
7428                 aip->ai_flags &= ~AIF_SEEK_LOCK;
7429         }
7430
7431 }
7432
7433 //      We're in chase mode and we've recently collided with our target.
7434 //      Fly away from it!
7435 void ai_chase_fly_away(object *objp, ai_info *aip)
7436 {
7437         int     abort_flag = 0;
7438
7439         if (aip->ai_flags & AIF_TARGET_COLLISION) {
7440                 aip->ai_flags &= ~AIF_TARGET_COLLISION; //      Don't process this hit again next frame.
7441                 aip->submode = SM_FLY_AWAY;                                     //      Focus on avoiding target
7442                 aip->submode_start_time = Missiontime;
7443         }
7444
7445         if ((aip->target_objnum == -1) || (Objects[aip->target_objnum].signature != aip->target_signature)) {
7446                 abort_flag = 1;
7447         }
7448
7449         if (abort_flag || (Missiontime > aip->submode_start_time + F1_0)) {
7450                 aip->last_attack_time = Missiontime;
7451                 aip->submode = SM_ATTACK;
7452                 aip->submode_start_time = Missiontime;
7453         } else {
7454                 vector  v2e;
7455                 float           dot;
7456
7457                 vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, &objp->pos);
7458
7459                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2e);
7460                 if (dot < 0.0f)
7461                         accelerate_ship(aip, 1.0f);
7462                 else
7463                         accelerate_ship(aip, 1.0f - dot);
7464                 turn_away_from_point(objp, &Objects[aip->target_objnum].pos, 0.0f);
7465         }
7466 }
7467
7468 //      Return bank index of favored secondary weapon.
7469 //      Return -1 if nothing favored.
7470 //      "favored" means SEXPs have specified the weapon as being good to fire at en_objp.
7471 int has_preferred_secondary(object *objp, object *en_objp, ship_weapon *swp)
7472 {
7473 // int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
7474         int     i;
7475
7476         for (i=0; i<swp->num_secondary_banks; i++) {
7477                 if (swp->secondary_bank_capacity[i] > 0) {
7478                         if (swp->secondary_bank_ammo[i] > 0) {
7479                                 if (is_preferred_weapon(swp->secondary_bank_weapons[i], objp, en_objp) != -1){
7480                                         return i;
7481                                 }
7482                         }
7483                 }
7484         }
7485
7486         return -1;
7487 }
7488
7489 //      Choose which secondary weapon to fire.
7490 //      Note, this is not like ai_select_secondary_weapon().  "choose" means make a choice.
7491 //      "select" means execute an order.  Get it?
7492 //      This function calls ai_select_secondary_weapon() with the characteristics it should search for.
7493 void ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp)
7494 {
7495         float                   subsystem_strength = 0.0f;
7496         int                     is_big_ship, priority1, priority2;
7497         ship_weapon     *swp;
7498         ship_info       *esip;
7499
7500         if ( en_objp->type == OBJ_SHIP ) {
7501                 esip = &Ship_info[Ships[en_objp->instance].ship_info_index];
7502         } else {
7503                 esip = NULL;
7504         }
7505
7506         swp = &Ships[objp->instance].weapons;
7507
7508         // AL 3-5-98: do a quick out if the ship has no secondaries
7509         if ( swp->num_secondary_banks <= 0 ) {
7510                 swp->current_secondary_bank = -1;
7511                 return;
7512         }
7513
7514         int preferred_secondary = has_preferred_secondary(objp, en_objp, swp);
7515
7516         if (preferred_secondary != -1) {
7517                 if (swp->current_secondary_bank != preferred_secondary) {
7518                         aip->current_target_is_locked = 0;
7519                         aip->aspect_locked_time = 0.0f;
7520                         swp->current_secondary_bank = preferred_secondary;
7521                 }
7522                 //nprintf(("AI", "Favored secondary = %s\n", Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7523                 aip->ai_flags |= AIF_UNLOAD_SECONDARIES;
7524         } else {
7525                 aip->ai_flags &= ~AIF_UNLOAD_SECONDARIES;
7526                 if (aip->targeted_subsys) {
7527                         subsystem_strength = aip->targeted_subsys->current_hits;
7528                 }
7529
7530                 if ( esip ) {
7531                         is_big_ship = esip->flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP);
7532                 } else {
7533                         is_big_ship=0;
7534                 }
7535
7536                 if (is_big_ship) {
7537                         priority1 = WIF_HUGE;
7538                         priority2 = WIF_HOMING;
7539                 } else if ( (esip != NULL) && (esip->flags & SIF_BOMBER) ) {
7540                         priority1 = WIF_BOMBER_PLUS;
7541                         priority2 = WIF_HOMING;
7542                 } else if (subsystem_strength > 100.0f) {
7543                         priority1 = WIF_PUNCTURE;
7544                         priority2 = WIF_HOMING;
7545                 } else {
7546                         priority1 = WIF_HOMING;
7547                         priority2 = 0;
7548                 }
7549                 
7550                 ai_select_secondary_weapon(objp, swp, priority1, priority2);
7551         }
7552
7553         // nprintf(("AI", "Frame %i: Chose secondary %s\n", Framecount, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7554 }
7555
7556 //      Return time, in seconds, at which this ship can next fire its current secondary weapon.
7557 float set_secondary_fire_delay(ai_info *aip, ship *shipp, weapon_info *swip)
7558 {
7559         float t = swip->fire_wait;              //      Base delay for this weapon.
7560         if (shipp->team == Player_ship->team) {
7561                 //      On player's team, _lower_ skill level = faster firing
7562                 t = t * (Game_skill_level+2) / (NUM_SKILL_LEVELS);
7563         } else {                //      Not on player's team, higher skill level = faster firing
7564                 t = t * (NUM_SKILL_LEVELS - Game_skill_level+2) / (NUM_SKILL_LEVELS);
7565         }
7566
7567         t += (Num_ai_classes - aip->ai_class + 1) * 0.5f;
7568         t *= frand_range(0.8f, 1.2f);
7569
7570         //      For the missiles that fire fairly quickly, occasionally add an additional substantial delay.
7571         if (t < 5.0f)
7572                 if (frand() < 0.5f)
7573                         t = t * 2.0f + 2.0f;
7574
7575         return t;
7576 }
7577
7578
7579 void ai_chase_big_approach_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7580 {
7581         float dist_to_goal;
7582
7583         // head straight toward him and maybe circle later
7584         vm_vec_avg(goal_pos, &attack_objp->pos, &target_objp->pos);
7585
7586         // get distance to goal
7587         dist_to_goal = vm_vec_dist(goal_pos, &attack_objp->pos);
7588         
7589         // set accel
7590         if (dist_to_goal > 400.0f) {
7591                 *accel = 1.0f;
7592         } else {
7593                 *accel = dist_to_goal/400.0f;
7594         }
7595 }
7596
7597 void ai_chase_big_circle_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7598 {
7599         get_tangent_point(goal_pos, attack_objp, &target_objp->pos, attack_objp->radius + target_objp->radius + 100.0f);
7600
7601         *accel = 1.0f;
7602 }
7603
7604 // get the current and desired horizontal separations between target
7605 void ai_chase_big_get_separations(object *attack_objp, object *target_objp, vector *horz_vec_to_target, float *desired_separation, float *cur_separation)
7606 {
7607         float temp, r_target, r_attacker, h_attacker, h_target;
7608         float perp_dist;
7609         vector vec_to_target;
7610         polymodel *pm;
7611
7612         // get parameters of ships (as cylinders - radius and height)
7613         // get radius of attacker (for rotations about forward)
7614         pm = model_get(Ships[attack_objp->instance].modelnum);
7615         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7616         r_attacker = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7617         r_attacker = max(temp, r_attacker);
7618         h_attacker = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7619
7620         // get radius of target (for rotations about forward)
7621         pm = model_get(Ships[attack_objp->instance].modelnum);
7622         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7623         r_target = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7624         r_target = max(temp, r_target);
7625         h_target = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7626
7627         // find separation between cylinders [if parallel]
7628         vm_vec_sub(&vec_to_target, &attack_objp->pos, &target_objp->pos);
7629
7630         // find the distance between centers along forward direction of ships
7631         perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.v.fvec);
7632
7633         // subtract off perp component to get "horizontal" separation vector between cylinders [ASSUMING parallel]
7634         vm_vec_scale_add(horz_vec_to_target, &vec_to_target, &target_objp->orient.v.fvec, -perp_dist);
7635         *cur_separation = vm_vec_mag_quick(horz_vec_to_target);
7636
7637         // choose "optimal" separation of 1000 + r_target + r_attacker
7638         *desired_separation = 1000 + r_target + r_attacker;
7639 }
7640
7641 void ai_chase_big_parallel_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7642 {
7643         int opposing;
7644         float temp, r_target, r_attacker, h_attacker, h_target;
7645         float separation, optimal_separation;
7646         vector  horz_vec_to_target;
7647         polymodel *pm;
7648
7649         // get parameters of ships (as cylinders - radius and height)
7650         // get radius of attacker (for rotations about forward)
7651         pm = model_get(Ships[attack_objp->instance].modelnum);
7652         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7653         r_attacker = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7654         r_attacker = max(temp, r_attacker);
7655         h_attacker = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7656
7657         // get radius of target (for rotations about forward)
7658         pm = model_get(Ships[attack_objp->instance].modelnum);
7659         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7660         r_target = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7661         r_target = max(temp, r_target);
7662         h_target = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7663
7664         // are we opposing (only when other ship is not moving)
7665         opposing = ( vm_vec_dotprod(&attack_objp->orient.v.fvec, &target_objp->orient.v.fvec) < 0 );
7666
7667         ai_chase_big_get_separations(attack_objp, target_objp, &horz_vec_to_target, &optimal_separation, &separation);
7668
7669         // choose dist (2000) so that we don't bash
7670         float dist = 2000;
7671         if (opposing) {
7672                 dist = - dist;
7673         }
7674
7675         // set the goal pos as dist forward from target along target forward
7676         vm_vec_scale_add(goal_pos, &target_objp->pos, &target_objp->orient.v.fvec, dist);
7677         // then add horizontal separation
7678         vm_vec_scale_add2(goal_pos, &horz_vec_to_target, optimal_separation/separation);
7679
7680         // find the distance between centers along forward direction of ships
7681         vector vec_to_target;
7682         vm_vec_sub(&vec_to_target, &target_objp->pos, &attack_objp->pos);
7683         float perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.v.fvec);
7684
7685         float match_accel = target_objp->phys_info.vel.xyz.z / Ship_info[Ships[attack_objp->instance].ship_info_index].max_vel.xyz.z;
7686         float length_scale = attack_objp->radius;
7687
7688         // if we're heading toward enemy ship, we want to keep going if we're ahead
7689         if (opposing) {
7690                 perp_dist = -perp_dist;
7691         }
7692
7693         if (perp_dist > 0) {
7694                 // falling behind, so speed up
7695                 *accel = match_accel + (1.0f - match_accel) / length_scale * (perp_dist);
7696         } else {
7697                 // up in front, so slow down
7698                 *accel = match_accel  - match_accel / length_scale * -perp_dist;
7699                 *accel = max(0.0f, *accel);
7700         }
7701
7702 }
7703
7704
7705 //      Return *goal_pos for one cruiser to attack another (big ship).
7706 //      Choose point fairly nearby that is not occupied by another cruiser.
7707 void ai_cruiser_chase_set_goal_pos(vector *goal_pos, object *pl_objp, object *en_objp)
7708 {
7709         ai_info *aip;
7710
7711         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
7712         float accel;
7713
7714         switch (aip->submode) {
7715         case SM_BIG_APPROACH:
7716                 // do approach stuff;
7717                 ai_chase_big_approach_set_goal(goal_pos, pl_objp, en_objp, &accel);
7718                 break;
7719
7720         case SM_BIG_CIRCLE:
7721                 // do circle stuff
7722                 ai_chase_big_circle_set_goal(goal_pos, pl_objp, en_objp, &accel);
7723                 break;
7724
7725         case SM_BIG_PARALLEL:
7726                 // do parallel stuff
7727                 ai_chase_big_parallel_set_goal(goal_pos, pl_objp, en_objp, &accel);
7728                 break;
7729         }
7730 }
7731
7732 int maybe_hack_cruiser_chase_abort()
7733 {
7734         ship                    *shipp = &Ships[Pl_objp->instance];     
7735         ship                    *eshipp = &Ships[En_objp->instance];
7736         ai_info         *aip = &Ai_info[shipp->ai_index];
7737
7738         // mission sm3-08, sathanos chasing collosus
7739         if ( stricmp(Mission_filename, "sm3-08.fs2") == 0 ) {
7740                 if (( stricmp(eshipp->ship_name, "colossus") == 0 ) || ( stricmp(shipp->ship_name, "colossus") == 0 )) {
7741                         // Changed so all big ships attacking the Colossus will not do the chase code.
7742                         // Did this so Beast wouldn't swerve away from Colossus. -- MK, 9/14/99
7743                         //if ( stricmp(shipp->ship_name, "Sathanas") == 0 ) {
7744                                 // do cool hack stuff here
7745                                 ai_clear_ship_goals( aip );
7746                                 aip->mode = AIM_NONE;
7747                                 return 1;
7748                         //}
7749                 }
7750         }
7751
7752         return 0;
7753 }
7754
7755 //      Make a big ship pursue another big ship.
7756 //      (Note, called "ai_cruiser_chase" because we already have ai_chase_big() which means fighter chases big ship.
7757 void ai_cruiser_chase()
7758 {
7759         ship_info       *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7760         ship                    *shipp = &Ships[Pl_objp->instance];     
7761         ai_info         *aip = &Ai_info[shipp->ai_index];
7762
7763         if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7764                 Int3(); //      Hmm, not a very big ship, how did we get in this function?
7765                 aip->mode = AIM_NONE;
7766                 return;
7767         }
7768
7769         if (En_objp->type != OBJ_SHIP) {
7770                 Int3();
7771                 return;
7772         }
7773
7774         if (En_objp->instance < 0) {
7775                 Int3();
7776                 return;
7777         }
7778
7779         ship                    *eshipp;
7780         ship_info       *esip;
7781
7782         eshipp = &Ships[En_objp->instance];
7783         esip = &Ship_info[eshipp->ship_info_index];
7784
7785         if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7786                 // Int3();      //      Hmm, we're big and we're pursuing something other than a big ship?
7787                 aip->mode = AIM_NONE;
7788                 return;
7789         }
7790
7791         vector  goal_pos;
7792         float turn_time = Ship_info[Ships[Pl_objp->instance].ship_info_index].srotation_time;
7793
7794         // kamikaze - ram and explode
7795         if (aip->ai_flags & AIF_KAMIKAZE) {
7796                 ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
7797                 accelerate_ship(aip, 1.0f);
7798         } 
7799         
7800         // really track down and chase
7801         else {
7802                 // check valid submode
7803                 Assert( (aip->submode == SM_ATTACK) || (aip->submode == SM_BIG_APPROACH) || (aip->submode == SM_BIG_CIRCLE) || (aip->submode == SM_BIG_PARALLEL) );
7804
7805                 // just entering, approach enemy ship
7806                 if (aip->submode == SM_ATTACK) {
7807                         aip->submode = SM_BIG_APPROACH;
7808                 }
7809
7810                 // desired accel
7811                 float accel = 0.0f;
7812                 vector *rvecp = NULL;
7813
7814                 switch (aip->submode) {
7815                 case SM_BIG_APPROACH:
7816                         // do approach stuff;
7817                         ai_chase_big_approach_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7818                         // maybe set rvec
7819                         break;
7820
7821                 case SM_BIG_CIRCLE:
7822                         // do circle stuff
7823                         ai_chase_big_circle_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7824                         // maybe set rvec
7825                         break;
7826
7827                 case SM_BIG_PARALLEL:
7828                         // do parallel stuff
7829                         ai_chase_big_parallel_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7830                         //maybe set rvec
7831                         break;
7832                 }
7833
7834
7835                 // now move as desired
7836                 ai_turn_towards_vector(&goal_pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0, rvecp);
7837                 accelerate_ship(aip, accel);
7838
7839
7840                 // maybe switch to new mode
7841                 vector vec_to_enemy;
7842                 float dist_to_enemy;
7843                 int moving = (En_objp->phys_info.vel.xyz.z > 0.5f);
7844                 vm_vec_sub(&vec_to_enemy, &En_objp->pos, &Pl_objp->pos);
7845                 dist_to_enemy = vm_vec_mag_quick(&vec_to_enemy);
7846
7847                 switch (aip->submode) {
7848                 case SM_BIG_APPROACH:
7849                         if ( dist_to_enemy < (Pl_objp->radius + En_objp->radius)*1.25f + 200.0f ) {
7850                                 // moving
7851                                 if (moving) {
7852                                         // if within 90 degrees of en forward, go into parallel, otherwise circle
7853                                         if ( vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0 ) {
7854                                                 aip->submode = SM_BIG_PARALLEL;
7855                                         }
7856                                 }
7857
7858                                 // otherwise cirle
7859                                 if ( !maybe_hack_cruiser_chase_abort() ) {
7860                                         aip->submode = SM_BIG_CIRCLE;
7861                                 }
7862                         }
7863                         break;
7864
7865                 case SM_BIG_CIRCLE:
7866                         // moving
7867                         if (moving) {
7868                                 vector temp;
7869                                 float desired_sep, cur_sep;
7870                                 // we're behind the enemy ship
7871                                 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec) > 0) {
7872                                         // and we're turning toward the enemy
7873                                         if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0) {
7874                                                 // get separation
7875                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7876                                                 // and the separation is > 0.9 desired
7877                                                 if (cur_sep > 0.9 * desired_sep) {
7878                                                         aip->submode = SM_BIG_PARALLEL;
7879                                                 }
7880                                         }
7881                                 }
7882                         } else {
7883                                 // still
7884                                 vector temp;
7885                                 float desired_sep, cur_sep;
7886                                 // we're behind the enemy ship
7887                                 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec) > 0) {
7888                                         // and we're turning toward the enemy
7889                                         if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0) {
7890                                                 // get separation
7891                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7892                                                 //and the separation is [0.9 to 1.1] desired
7893                                                 if ( (cur_sep > 0.9f * desired_sep) ) {
7894                                                         aip->submode = SM_BIG_PARALLEL;
7895                                                 }
7896                                         }
7897                                 }
7898                                 // in front of ship
7899                                 else {
7900                                         // and we're turning toward the enemy
7901                                         if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) < 0) {
7902                                                 // get separation
7903                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7904                                                 //and the separation is [0.9 to 1.1] desired
7905                                                 if ( (cur_sep > 0.9f * desired_sep) ) {
7906                                                         aip->submode = SM_BIG_PARALLEL;
7907                                                 }
7908                                         }
7909                                 }
7910                         }
7911                         break;
7912
7913                 case SM_BIG_PARALLEL:
7914                         // we're opposing
7915                         if ( vm_vec_dotprod(&Pl_objp->orient.v.fvec, &En_objp->orient.v.fvec) < 0 ) {
7916                                 // and the other ship is moving
7917                                 if (moving) {
7918                                         // and we no longer overlap
7919                                         if ( dist_to_enemy > (0.75 * (En_objp->radius + Pl_objp->radius)) ) {
7920                                                 aip->submode = SM_BIG_APPROACH;
7921                                         }
7922                                 }
7923                         }
7924                         break;
7925                 }
7926         }
7927 }
7928
7929 // --------------------------------------------------------------------------
7930 // Make object Pl_objp chase object En_objp
7931 void ai_chase()
7932 {
7933         float                   dist_to_enemy, time_to_enemy;
7934         float                   dot_to_enemy, dot_from_enemy, real_dot_to_enemy;
7935         vector          player_pos, enemy_pos, predicted_enemy_pos, real_vec_to_enemy, predicted_vec_to_enemy;
7936         ship_info       *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7937         ship                    *shipp = &Ships[Pl_objp->instance];
7938         ship_weapon     *swp = &shipp->weapons;
7939         ai_info         *aip = &Ai_info[shipp->ai_index];
7940         int                     enemy_sip_flags;
7941
7942         if (aip->mode != AIM_CHASE) {
7943                 Int3();
7944         }
7945
7946         if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
7947                 ai_cruiser_chase();
7948                 return;
7949         }
7950
7951         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER | SIF_ESCAPEPOD))) {
7952                 Warning(LOCATION, "Ship %s is not 'small', but is in chase mode.\nSwitching to AI=none.\n", shipp->ship_name);
7953                 aip->mode = AIM_NONE;
7954                 return;
7955         }
7956
7957         //nprintf(("AI", "%7s ", Submode_text[aip->submode]));
7958
7959         if ( En_objp->type == OBJ_SHIP ) {
7960                 enemy_sip_flags = Ship_info[Ships[En_objp->instance].ship_info_index].flags;
7961         } else {
7962                 enemy_sip_flags = 0;
7963         }
7964
7965         if ( enemy_sip_flags > 0 ) {
7966                 if (enemy_sip_flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
7967                         ai_big_chase();
7968                         return;
7969                 }
7970         }
7971
7972         //      If collided with target_objnum last frame, avoid that ship.
7973         //      This should prevent the embarrassing behavior of ships getting stuck on each other
7974         //      as if they were magnetically attracted. -- MK, 11/13/97.
7975         if ((aip->ai_flags & AIF_TARGET_COLLISION) || (aip->submode == SM_FLY_AWAY)) {
7976                 ai_chase_fly_away(Pl_objp, aip);
7977                 return;
7978         }
7979
7980         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
7981         dist_to_enemy = vm_vec_dist_quick(&player_pos, &enemy_pos);
7982         time_to_enemy = compute_time_to_enemy(dist_to_enemy, Pl_objp, En_objp);
7983         vm_vec_sub(&real_vec_to_enemy, &enemy_pos, &player_pos);
7984
7985         vm_vec_normalize(&real_vec_to_enemy);
7986
7987         real_dot_to_enemy = vm_vec_dot(&real_vec_to_enemy, &Pl_objp->orient.v.fvec);
7988
7989         int is_stealthy_ship = 0;
7990         if ( (enemy_sip_flags > 0) && (enemy_sip_flags & SIF_STEALTH) ) {
7991                 if ( ai_is_stealth_visible(Pl_objp, En_objp) != STEALTH_FULLY_TARGETABLE ) {
7992                         is_stealthy_ship = 1;
7993                 }
7994         }
7995
7996         // Can only acquire lock on a target that isn't hidden from sensors
7997         if ( !(Ships[En_objp->instance].flags & SF_HIDDEN_FROM_SENSORS) && !is_stealthy_ship ) {
7998                 update_aspect_lock_information(aip, &real_vec_to_enemy, dist_to_enemy, En_objp->radius);
7999         } else {
8000                 aip->current_target_is_locked = 0;
8001                 aip->ai_flags &= ~AIF_SEEK_LOCK;
8002         }
8003
8004         //      If seeking lock, try to point directly at ship, else predict position so lasers can hit it.
8005         //      If just acquired target, or target is not in reasonable cone, don't refine believed enemy position.
8006         if ((real_dot_to_enemy < 0.25f) || (aip->target_time < 1.0f) || (aip->ai_flags & AIF_SEEK_LOCK)) {
8007                 predicted_enemy_pos = enemy_pos;
8008         } else {
8009                 //      Set predicted_enemy_pos.
8010                 //      See if attacking a subsystem.
8011                 if (aip->targeted_subsys != NULL) {
8012                         Assert(En_objp->type == OBJ_SHIP);
8013                         ship_info       *esip = &Ship_info[Ships[En_objp->instance].ship_info_index];
8014                         if (get_shield_strength(En_objp)/esip->shields < HULL_DAMAGE_THRESHOLD_PERCENT) {
8015                                 //int   rval;
8016
8017                                 if (aip->targeted_subsys != NULL) {
8018                                         get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
8019                                         predicted_enemy_pos = enemy_pos;
8020                                         predicted_vec_to_enemy = real_vec_to_enemy;
8021                                 } else {
8022                                         set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8023                                         set_target_objnum(aip, -1);
8024                                 }
8025                                 // 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));
8026
8027                         } else {
8028                                 set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8029                                 // 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));
8030                         }
8031                 } else {
8032                         set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8033                 }
8034         }
8035
8036         vm_vec_sub(&predicted_vec_to_enemy, &predicted_enemy_pos, &player_pos);
8037
8038         vm_vec_normalize(&predicted_vec_to_enemy);
8039
8040         dot_to_enemy = vm_vec_dot(&Pl_objp->orient.v.fvec, &predicted_vec_to_enemy);
8041         dot_from_enemy= - vm_vec_dot(&En_objp->orient.v.fvec, &real_vec_to_enemy);
8042
8043         //
8044         //      Set turn and acceleration based on submode.
8045         //
8046         switch (aip->submode) {
8047         case SM_CONTINUOUS_TURN:
8048                 ai_chase_ct();
8049                 break;
8050
8051         case SM_STEALTH_FIND:
8052                 ai_stealth_find();
8053                 break;
8054
8055         case SM_STEALTH_SWEEP:
8056                 ai_stealth_sweep();
8057                 break;
8058
8059         case SM_ATTACK:
8060         case SM_SUPER_ATTACK:
8061         case SM_ATTACK_FOREVER:
8062                 if (vm_vec_dist_quick(&Pl_objp->pos, &predicted_enemy_pos) > 100.0f + En_objp->radius * 2.0f) {
8063                         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &predicted_enemy_pos, 10.0f))
8064                                 return;
8065                 }
8066
8067                 ai_chase_attack(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8068                 break;
8069
8070         case SM_EVADE_SQUIGGLE:
8071                 ai_chase_es(aip, sip);
8072                 break;
8073
8074         case SM_EVADE_BRAKE:
8075                 ai_chase_eb(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8076                 break;
8077
8078         case SM_EVADE:
8079                 evade_ship();
8080                 break;
8081
8082         case SM_AVOID:
8083                 avoid_ship();
8084                 break;
8085
8086         case SM_GET_BEHIND:
8087                 get_behind_ship(aip, sip, dist_to_enemy);
8088                 break;
8089
8090         case SM_GET_AWAY:               //      Used to get away from opponent to prevent endless circling.
8091                 ai_chase_ga(aip, sip);
8092                 break;
8093
8094         case SM_EVADE_WEAPON:
8095                 evade_weapon();
8096                 break;
8097
8098         default:
8099                 // Int3();
8100                 aip->last_attack_time = Missiontime;
8101                 aip->submode = SM_ATTACK;
8102                 aip->submode_start_time = Missiontime;
8103         }
8104
8105         //
8106         //      Maybe choose a new submode.
8107         //
8108         if ( (aip->submode != SM_AVOID) && (aip->submode != SM_ATTACK_FOREVER) ) {
8109                 //      If a very long time since attacked, attack no matter what!
8110                 if ( (aip->submode != SM_SUPER_ATTACK) && (aip->submode != SM_GET_AWAY) && !(aip->ai_flags & AIF_STEALTH_PURSIUT) ) {
8111                         if (Missiontime - aip->last_attack_time > i2f(6)) {
8112                                 aip->submode = SM_SUPER_ATTACK;
8113                                 aip->submode_start_time = Missiontime;
8114                                 aip->last_attack_time = Missiontime;
8115                         }
8116                 }
8117
8118                 //      If a collision is expected, pull out!
8119                 //      If enemy is pointing away and moving a bit, don't worry about collision detection.
8120                 if ((dot_from_enemy > 0.5f) || (En_objp->phys_info.speed < 10.0f)) {
8121                         if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 4.0f)) {
8122                                 if ((Missiontime - aip->last_hit_time > F1_0*4) && (dist_to_enemy < Pl_objp->radius*2 + En_objp->radius*2)) {
8123                                         accelerate_ship(aip, -1.0f);
8124                                 } else {
8125                                         aip->submode = SM_AVOID;
8126                                         aip->submode_start_time = Missiontime;
8127                                 }
8128                         }
8129                 }
8130         }
8131
8132         switch (aip->submode) {
8133         case SM_CONTINUOUS_TURN:
8134                 if (Missiontime - aip->submode_start_time > i2f(3)) {
8135                         aip->last_attack_time = Missiontime;
8136                         aip->submode = SM_ATTACK;
8137                         aip->submode_start_time = Missiontime;
8138                 }
8139                 break;
8140
8141         case SM_ATTACK:
8142                 // if taraget is stealth and stealth not visible, then enter stealth find mode
8143                 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8144                         aip->submode = SM_STEALTH_FIND;
8145                         aip->submode_start_time = Missiontime;
8146                         aip->submode_parm0 = SM_SF_AHEAD;
8147                 } 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)) {
8148                         aip->submode = SM_SUPER_ATTACK;
8149                         aip->submode_start_time = Missiontime;
8150                         aip->last_attack_time = Missiontime;
8151                 } else if ((Missiontime - aip->last_hit_target_time > i2f(6)) &&
8152                         (dist_to_enemy < 500.0f) && (dot_to_enemy < 0.2f) &&
8153                         (frand() < (float) Game_skill_level/NUM_SKILL_LEVELS)) {
8154                         aip->submode = SM_GET_AWAY;
8155                         aip->submode_start_time = Missiontime;
8156                         aip->last_hit_target_time = Missiontime;
8157                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP)
8158                         && (dot_to_enemy < dot_from_enemy)
8159                         && (En_objp->phys_info.speed > 15.0f) 
8160                         && (dist_to_enemy < 200.0f) 
8161                         && (dist_to_enemy > 50.0f)
8162                         && (dot_to_enemy < 0.1f)
8163                         && (Missiontime - aip->submode_start_time > i2f(2))) {
8164                         aip->submode = SM_EVADE_BRAKE;
8165                         aip->submode_start_time = Missiontime;
8166                 } else if ((dot_to_enemy > 0.2f) && (dot_from_enemy > -0.2f) && (dot_from_enemy < 0.1f)) {
8167                         aip->submode = SM_GET_BEHIND;
8168                         aip->submode_start_time = Missiontime;
8169                 } 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)) {
8170                         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;
8171                                 aip->submode_start_time = Missiontime;
8172                                 aip->last_hit_target_time = Missiontime;
8173                         } else {
8174                                 aip->submode = SM_EVADE_SQUIGGLE;
8175                                 aip->submode_start_time = Missiontime;
8176                         }
8177                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP) && (Missiontime - aip->submode_start_time > F1_0*2)) {
8178                         if ((dot_to_enemy < 0.8f) && (dot_from_enemy > dot_to_enemy)) {
8179                                 if (frand() > 0.5f) {
8180                                         aip->submode = SM_CONTINUOUS_TURN;
8181                                         aip->submode_parm0 = myrand() & 0x0f;
8182                                         aip->submode_start_time = Missiontime;
8183                                 } else {
8184                                         aip->submode = SM_EVADE;
8185                                         aip->submode_start_time = Missiontime;
8186                                 }
8187                         } else {
8188                                 aip->submode_start_time = Missiontime;
8189                         }
8190                 }
8191
8192                 aip->last_attack_time = Missiontime;
8193
8194                 break;
8195                 
8196         case SM_EVADE_SQUIGGLE:
8197                 if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > 300.0f)) {
8198                         if ((dist_to_enemy < 100.0f) && (dot_to_enemy < 0.0f) && (dot_from_enemy > 0.5f)) {
8199                                 aip->submode = SM_EVADE_BRAKE;
8200                                 aip->submode_start_time = Missiontime;
8201                         } else {
8202                                 aip->last_attack_time = Missiontime;
8203                                 aip->submode = SM_ATTACK;
8204                                 aip->submode_start_time = Missiontime;
8205                         }
8206                 }
8207                 break;
8208         
8209         case SM_EVADE_BRAKE:
8210                 if ((dist_to_enemy < 15.0f) || (En_objp->phys_info.speed < 10.0f)) {
8211                         aip->submode = SM_AVOID;
8212                         aip->submode_start_time = Missiontime;
8213                 } else if ((dot_to_enemy > 0.9f) || ((dot_from_enemy > 0.9f) && (Missiontime - aip->submode_start_time > i2f(1)))) {
8214                         aip->last_attack_time = Missiontime;
8215                         aip->submode = SM_ATTACK;
8216                         aip->submode_start_time = Missiontime;
8217                 } else if (Missiontime - aip->submode_start_time > i2f(4)) {
8218                         aip->last_attack_time = Missiontime;
8219                         aip->submode = SM_ATTACK;
8220                         aip->submode_start_time = Missiontime;
8221                 }
8222                 break;
8223
8224         case SM_EVADE:
8225                 //      Modified by MK on 5/5/97 to keep trying to regain attack mode.  It's what a human would do.
8226                 if ((dot_to_enemy < 0.2f) && (dot_from_enemy < 0.8f) && (dist_to_enemy < 100.0f) && (En_objp->phys_info.speed > 15.0f)) {
8227                         aip->last_attack_time = Missiontime;
8228                         aip->submode = SM_EVADE_BRAKE;
8229                         aip->submode_start_time = Missiontime;
8230                 } else if (((dot_to_enemy > dot_from_enemy - 0.1f)
8231                         && (Missiontime > aip->submode_start_time + i2f(1)))
8232                         || (dist_to_enemy > 150.0f + 2*(Pl_objp->radius + En_objp->radius))) {
8233                         aip->last_attack_time = Missiontime;
8234                         aip->submode = SM_ATTACK;
8235                         aip->submode_start_time = Missiontime;
8236                 } else if (Missiontime - aip->submode_start_time > i2f(2))
8237                         if (dot_from_enemy > 0.8f) {
8238                                 aip->submode = SM_EVADE_SQUIGGLE;
8239                                 aip->submode_start_time = Missiontime;
8240                         }
8241
8242                 break;
8243
8244         case SM_SUPER_ATTACK:
8245                 // if stealth and invisible, enter stealth find mode
8246                 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8247                         aip->submode = SM_STEALTH_FIND;
8248                         aip->submode_start_time = Missiontime;
8249                         aip->submode_parm0 = SM_SF_AHEAD;
8250                 } 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) )) {
8251                         aip->ai_flags &= ~AIF_ATTACK_SLOWLY;    //      Just in case, clear here.
8252
8253                         switch (myrand() % 5) {
8254                         case 0:
8255                                 aip->submode = SM_CONTINUOUS_TURN;
8256                                 aip->submode_start_time = Missiontime;
8257                                 break;
8258                         case 1:
8259                                 aip->submode_start_time = Missiontime;  //      Stay in super attack mode
8260                                 break;
8261                         case 2:
8262                         case 3:
8263                                 if (frand() < (float) 0.5f * (aip->ai_class + Game_skill_level)/(Num_ai_classes + NUM_SKILL_LEVELS)) {
8264                                         aip->submode = SM_GET_AWAY;
8265                                         aip->submode_start_time = Missiontime;
8266                                 } else {
8267                                         aip->submode = SM_EVADE;
8268                                         aip->submode_start_time = Missiontime;
8269                                 }
8270                                 break;
8271                         case 4:
8272                                 if (dot_from_enemy + (NUM_SKILL_LEVELS - Game_skill_level) * 0.1f > dot_to_enemy) {     //      Less likely to GET_AWAY at lower skill levels.
8273                                         aip->submode = SM_EVADE;
8274                                         aip->submode_start_time = Missiontime;
8275                                 } else {
8276                                         aip->submode = SM_GET_AWAY;
8277                                         aip->submode_start_time = Missiontime;
8278                                 }
8279                                 break;
8280                         default:
8281                                 Int3(); //      Impossible!
8282                         }
8283                 }
8284
8285                 aip->last_attack_time = Missiontime;
8286
8287                 break;
8288
8289         case SM_AVOID:
8290                 if ((dot_to_enemy > -0.2f) && (dist_to_enemy / (dot_to_enemy + 0.3f) < 100.0f)) {
8291                         aip->submode_start_time = Missiontime;
8292                 } else if (Missiontime - aip->submode_start_time > i2f(1)/2)
8293                         if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 3.0f)) {
8294                                 aip->submode_start_time = Missiontime;
8295                         } else {
8296                                 aip->submode = SM_GET_BEHIND;
8297                                 aip->submode_start_time = Missiontime;
8298                         }
8299
8300                 break;
8301
8302         case SM_GET_BEHIND:
8303                 if ((dot_from_enemy < -0.7f) || (Missiontime - aip->submode_start_time > i2f(2))) {
8304                         aip->submode = SM_ATTACK;
8305                         aip->submode_start_time = Missiontime;
8306                         aip->last_attack_time = Missiontime;
8307                 }
8308                 break;
8309
8310         case SM_GET_AWAY:
8311                 if (Missiontime - aip->submode_start_time > i2f(2)) {
8312                         float   rand_dist;
8313
8314                         rand_dist = ((Missiontime >> 17) & 0x03) * 100.0f + 200.0f;     //      Some value in 200..500
8315                         if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > rand_dist) || (dot_from_enemy < 0.4f)) {
8316                                 aip->ai_flags |= AIF_ATTACK_SLOWLY;
8317                                 aip->submode = SM_ATTACK;
8318                                 aip->time_enemy_in_range = 2.0f;                //      Cheat.  Presumably if they were running away from you, they were monitoring you!
8319                                 aip->submode_start_time = Missiontime;
8320                                 aip->last_attack_time = Missiontime;
8321                         }
8322                 }
8323                 break;
8324
8325         case SM_EVADE_WEAPON:
8326                 if (aip->danger_weapon_objnum == -1) {
8327                         aip->submode = SM_ATTACK;
8328                         aip->submode_start_time = Missiontime;
8329                         aip->last_attack_time = Missiontime;
8330                 }
8331                 break;
8332
8333         // Either change to SM_ATTACK or AIM_FIND_STEALTH
8334         case SM_STEALTH_FIND:
8335                 // if time > 5 sec change mode to sweep
8336                 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8337                         aip->submode = SM_ATTACK;
8338                         aip->submode_start_time = Missiontime;
8339                         aip->last_attack_time = Missiontime;
8340                         // sweep if I can't find in 5 sec or bail from find
8341                 } else if ( ((Missiontime - aip->submode_start_time) > i2f(5)) || (aip->submode_parm0 == SM_SF_BAIL) ) {
8342                         // begin sweep mode
8343                         aip->submode = SM_STEALTH_SWEEP;
8344                         aip->submode_start_time = Missiontime;
8345                         aip->last_attack_time = Missiontime;
8346                         aip->submode_parm0 = SM_SS_SET_GOAL;
8347                 }
8348                 break;
8349
8350         case SM_STEALTH_SWEEP:
8351                 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8352                         aip->submode = SM_ATTACK;
8353                         aip->submode_start_time = Missiontime;
8354                         aip->last_attack_time = Missiontime;
8355                 } else if ( (timestamp() - aip->stealth_last_visible_stamp) < 5000 ) {
8356                         // go back to find mode
8357                         aip->submode = SM_STEALTH_FIND;
8358                         aip->submode_start_time = Missiontime;
8359                         aip->submode_parm0 = SM_SF_AHEAD;
8360                 } else if ( /*(Missiontime - aip->submode_start_time) > i2f(30) || */(aip->submode_parm0 == SM_SS_DONE) ) {
8361                         // set target objnum = -1
8362                         set_target_objnum(aip, -1);
8363
8364                         // set submode to attack
8365                         aip->submode = SM_ATTACK;
8366                         aip->submode_start_time = Missiontime;
8367                         aip->last_attack_time = Missiontime;
8368                 }
8369                 break;
8370
8371         case SM_ATTACK_FOREVER: //      Engines blown, just attack.
8372                 break;
8373
8374         default:
8375                 //Int3();
8376                 aip->submode = SM_ATTACK;
8377                 aip->last_attack_time = Missiontime;
8378
8379                 aip->submode_start_time = Missiontime;
8380         }
8381
8382         //
8383         //      Maybe fire primary weapon and update time_enemy_in_range
8384         //
8385         //nprintf(("AI", "time_enemy_in_range = %7.3f, dot = %7.3f\n", aip->time_enemy_in_range, dot_to_enemy));
8386
8387         if (aip->mode != AIM_EVADE) {
8388                 if (dot_to_enemy > 0.95f - 0.5f * En_objp->radius/max(1.0f, En_objp->radius + dist_to_enemy)) {
8389                         aip->time_enemy_in_range += flFrametime;
8390                         
8391                         //      Chance of hitting ship is based on dot product of firing ship's forward vector with vector to ship
8392                         //      and also the size of the target relative to distance to target.
8393                         if (dot_to_enemy > max(0.5f, 0.90f + aip->ai_accuracy/10.0f - En_objp->radius/max(1.0f,dist_to_enemy))) {
8394
8395                                 ship *temp_shipp;
8396                                 ship_weapon *tswp;
8397
8398                                 temp_shipp = &Ships[Pl_objp->instance];
8399                                 tswp = &temp_shipp->weapons;
8400                                 if ( tswp->num_primary_banks > 0 ) {
8401                                         float   scale;
8402                                         Assert(tswp->current_primary_bank < tswp->num_primary_banks);
8403                                         weapon_info     *pwip = &Weapon_info[tswp->primary_bank_weapons[tswp->current_primary_bank]];
8404
8405                                         //      Less likely to fire if far away and moving.
8406                                         scale = pwip->max_speed/(En_objp->phys_info.speed + pwip->max_speed);
8407                                         if (scale > 0.6f)
8408                                                 scale = (scale - 0.6f) * 1.5f;
8409                                         else
8410                                                 scale = 0.0f;
8411                                         if (dist_to_enemy < pwip->max_speed * (1.0f + scale)) {
8412                                                 ai_fire_primary_weapon(Pl_objp);
8413                                         }
8414
8415                                         //      Don't fire secondaries at a protected ship.
8416                                         if (!(En_objp->flags & OF_PROTECTED)) {
8417                                                 ai_choose_secondary_weapon(Pl_objp, aip, En_objp);
8418                                                 int current_bank = tswp->current_secondary_bank;
8419                                                 weapon_info     *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8420
8421                                                 if (current_bank > -1) {
8422                                                         if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8423                                                                 if (timestamp_until(swp->next_secondary_fire_stamp[current_bank]) > swip->fire_wait*1000.0f) {
8424                                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (swip->fire_wait*1000.0f));
8425                                                                 }
8426                                                         }
8427
8428                                                         if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
8429                                                                 if (tswp->current_secondary_bank >= 0) {
8430                                                                         weapon_info     *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8431                                                                         float firing_range;
8432                                                                         
8433                                                                         if (swip->wi_flags & WIF_BOMB)
8434                                                                                 firing_range = swip->max_speed * swip->lifetime * 0.75f;
8435                                                                         else
8436                                                                                 firing_range = swip->max_speed * swip->lifetime * (Game_skill_level + 1 + aip->ai_class/2)/NUM_SKILL_LEVELS;
8437
8438                                                                         // reduce firing range in nebula
8439                                                                         extern int Nebula_sec_range;
8440                                                                         if ((The_mission.flags & MISSION_FLAG_FULLNEB) && Nebula_sec_range) {
8441                                                                                 firing_range *= 0.8f;
8442                                                                         }
8443
8444                                                                         //      If firing a spawn weapon, distance doesn't matter.
8445                                                                         int     spawn_fire = 0;
8446
8447                                                                         if (swip->wi_flags & WIF_SPAWN) {
8448                                                                                 int     count;
8449
8450                                                                                 count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(Pl_objp)), &Pl_objp->pos, 1000.0f);
8451
8452                                                                                 if (count > 3)
8453                                                                                         spawn_fire = 1;
8454                                                                                 else if (count >= 1) {
8455                                                                                         float hull_percent = Pl_objp->hull_strength/sip->initial_hull_strength;
8456
8457                                                                                         if (hull_percent < 0.01f)
8458                                                                                                 hull_percent = 0.01f;
8459
8460                                                                                         if (frand() < 0.25f/(30.0f*hull_percent) * count)       //      With timestamp below, this means could fire in 30 seconds if one enemy.
8461                                                                                                 spawn_fire = 1;
8462                                                                                 }
8463                                                                         }
8464
8465                                                                         if (spawn_fire || (dist_to_enemy < firing_range)) {
8466                                                                                 if (ai_fire_secondary_weapon(Pl_objp)) {
8467                                                                                         //      Only if weapon was fired do we specify time until next fire.  If not fired, done in ai_fire_secondary...
8468                                                                                         float t;
8469                                                                                         
8470                                                                                         if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8471                                                                                                 t = swip->fire_wait;
8472                                                                                         } else {
8473                                                                                                 t = set_secondary_fire_delay(aip, temp_shipp, swip);
8474                                                                                         }
8475                                                                                         //nprintf(("AI", "Next secondary to be fired in %7.3f seconds.\n", t));
8476                                                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
8477                                                                                 }
8478                                                                         } else {
8479                                                                                 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
8480                                                                         }
8481                                                                 }
8482                                                         }
8483                                                 }
8484                                         }
8485                                 }
8486                         }
8487                 } else {
8488                         aip->time_enemy_in_range *= (1.0f - flFrametime);
8489                 }
8490         } else
8491                 aip->time_enemy_in_range *= (1.0f - flFrametime);
8492
8493 }
8494
8495 //      Make the object *objp move so that the point *dp on the object moves towards the point *vp
8496 //      Return distance.
8497 void dock_move_towards_point(object *objp, vector *dp, vector *vp, float speed_scale, float other_obj_speed = 0.0f)
8498 {
8499         physics_info    *pi = &objp->phys_info;
8500         float                           dist;                   //      dist to goal
8501         vector                  v2g;                    //      vector to goal
8502         vector                  abs_pnt;                //      location of dock point, ie objp->pos + db
8503
8504         if (dp == NULL)
8505                 abs_pnt = objp->pos;
8506         else
8507                 vm_vec_add(&abs_pnt, &objp->pos, dp);
8508
8509         dist = vm_vec_dist_quick(vp, &abs_pnt);
8510         if (dist > 0.0f) {
8511                 float   speed;
8512
8513                 dist = vm_vec_normalized_dir(&v2g, vp, &abs_pnt);
8514                 speed = fl_sqrt(dist) * speed_scale;
8515                 if (other_obj_speed < MAX_REPAIR_SPEED*0.75f)
8516                         speed += other_obj_speed;
8517                 else
8518                         speed += MAX_REPAIR_SPEED*0.75f;
8519
8520                 vm_vec_copy_scale(&pi->desired_vel, &v2g, speed);
8521         } else
8522                 vm_vec_zero(&pi->desired_vel);
8523 }
8524
8525 //      Set the orientation in the global reference frame for an object to attain
8526 //      to dock with another object.
8527 //      *dom            resultant global matrix
8528 //      *db_dest        pointer to destination docking bay information
8529 //      *db_src pointer to source docking bay information
8530 //      *dorient        pointer to global orientation of docking bay (ie, the dockee object's orient)
8531 //      *sorient        pointer to global orientation of docker
8532 void set_goal_dock_orient(matrix *dom, dock_bay *db_dest, dock_bay *db_src, matrix *dorient, matrix *sorient)
8533 {
8534         vector  fvec, uvec;
8535         matrix  m1, m2, m3;
8536
8537         //      Compute the global orientation of the docker's (dest) docking bay.
8538         fvec = db_dest->norm[0];
8539         vm_vec_negate(&fvec);
8540
8541         vm_vec_normalized_dir(&uvec, &db_dest->pnt[1], &db_dest->pnt[0]);
8542         vm_vector_2_matrix(&m1, &fvec, &uvec, NULL);
8543
8544         vm_matrix_x_matrix(&m3, dorient, &m1);
8545
8546         //      Compute the matrix given by the source docking bay.
8547         //      Pre-multiply the orientation of the source object (sorient) by the transpose
8548         //      of the docking bay's orientation, ie unrotate the source object's matrix.
8549         fvec = db_src->norm[0];
8550         vm_vec_normalized_dir(&uvec, &db_src->pnt[1], &db_src->pnt[0]);
8551         vm_vector_2_matrix(&m2, &fvec, &uvec, NULL);
8552         vm_transpose(&m2);
8553
8554         vm_matrix_x_matrix(dom, &m3, &m2);
8555 }
8556
8557 #define DOCK_BACKUP_RETURN_VAL  99999.9f
8558
8559 //      Make objp dock with dobjp
8560 //      Returns distance to goal, defined as distance between corresponding dock points, plus 10.0f * rotational velocity vector (DOA_DOCK only)
8561 //      DOA_APPROACH    means   approach point aip->path_cur
8562 //      DOA_DOCK                        means dock
8563 //      DOA_UNDOCK_1    means undock, moving to point nearest dock bay
8564 //      DOA_UNDOCK_2    means undock, moving to point nearest dock bay and facing away from ship
8565 //      DOA_DOCK_STAY   means rigidly maintain position in dock bay.
8566 float dock_orient_and_approach(object *objp, object *dobjp, int dock_mode)
8567 {
8568         ship_info       *sip0, *sip1;
8569         polymodel       *pm0, *pm1;
8570         ai_info         *aip;
8571         matrix          dom, nm;
8572         vector          goal_point, docker_point;
8573         float                   fdist = UNINITIALIZED_VALUE;
8574         int                     docker_index, dockee_index;             // index into docking_bays[] array for objects docking
8575                                                                                                                                 // docker is Pl_objp -- dockee is dobjp
8576         aip = &Ai_info[Ships[objp->instance].ai_index];
8577
8578         //      If dockee has moved much, then path will be recreated.
8579         //      Might need to change state if moved too far.
8580         if ((dock_mode != DOA_DOCK_STAY) && (dock_mode != DOA_DOCK)) {
8581                 if (maybe_recreate_path(objp, &Ai_info[Ships[objp->instance].ai_index], 0) > 5.0f) {
8582 /*                      if (dock_mode == DOA_APPROACH) {
8583                                 return DOCK_BACKUP_RETURN_VAL;
8584                         } else if (dock_mode == DOA_DOCK) {
8585                                 return DOCK_BACKUP_RETURN_VAL;          
8586                         }
8587 */              }
8588         }
8589
8590         objp->phys_info.forward_thrust = 0.0f;          //      Kill thrust so we don't have a sputtering thruster.
8591
8592         sip0 = &Ship_info[Ships[objp->instance].ship_info_index];
8593         sip1 = &Ship_info[Ships[dobjp->instance].ship_info_index];
8594         pm0 = model_get( sip0->modelnum );
8595         pm1 = model_get( sip1->modelnum );
8596
8597         docker_index = aip->dock_index;
8598         dockee_index = aip->dockee_index;
8599
8600         Assert( docker_index >= 0 );
8601         Assert( dockee_index >= 0 );
8602
8603         Assert(pm0->docking_bays[docker_index].num_slots == 2);
8604         Assert(pm1->docking_bays[dockee_index].num_slots == 2);
8605
8606         float speed_scale = 1.0f;
8607         if (sip0->flags & SIF_SUPPORT) {
8608                 speed_scale = 3.0f;
8609         }
8610
8611         switch (dock_mode) {
8612         case DOA_APPROACH:
8613                 {
8614                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8615                         return 9999.9f;
8616                 }
8617                 
8618                 //      Compute the desired global orientation matrix for the docker's station.
8619                 //      That is, the normal vector of the docking station must be the same as the
8620                 //      forward vector and the vector between its two points must be the uvec.
8621                 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8622
8623                 //      Compute new orientation matrix and update rotational velocity.
8624                 vector  w_in, w_out, vel_limit, acc_limit;
8625                 float           tdist, mdist, ss1;
8626
8627                 w_in = objp->phys_info.rotvel;
8628                 vel_limit = objp->phys_info.max_rotvel;
8629                 vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8630                 
8631                 if (sip0->flags & SIF_SUPPORT)
8632                         vm_vec_scale(&acc_limit, 2.0f);
8633
8634                 // 1 at end of line prevent overshoot
8635                 vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit, 1);
8636                 objp->phys_info.rotvel = w_out;
8637                 objp->orient = nm;
8638
8639                 //      Translate towards goal and note distance to goal.
8640                 goal_point = Path_points[aip->path_cur].pos;
8641                 mdist = ai_matrix_dist(&objp->orient, &dom);
8642                 tdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8643
8644                 //      If translation is badly lagging rotation, speed up translation.
8645                 if (mdist > 0.1f) {
8646                         ss1 = tdist/(10.0f * mdist);
8647                         if (ss1 > 2.0f)
8648                                 ss1 = 2.0f;
8649                 } else
8650                         ss1 = 2.0f;
8651
8652                 // nprintf(("AI", "speed scale = %7.3f\n", ss1));
8653                 speed_scale *= 1.0f + ss1;
8654
8655                 dock_move_towards_point(objp, NULL, &goal_point, speed_scale, dobjp->phys_info.speed);
8656
8657                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8658
8659                 //      Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8660                 // nprintf(("AI", "matrix dist = %7.3f, threshold = %7.3f\n", mdist, 2*flFrametime));
8661                 fdist += 2.0f * mdist;
8662
8663                 break;
8664         }
8665         case DOA_DOCK:
8666                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8667                         return 9999.9f;
8668                 }
8669         case DOA_DOCK_STAY:
8670                 //      Compute the desired global orientation matrix for the docker's station.
8671                 //      That is, the normal vector of the docking station must be the same as the
8672                 //      forward vector and the vector between its two points must be the uvec.
8673                 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8674
8675                 //      Compute distance between dock bay points.
8676                 vector  db0, db1, db2, db3;
8677
8678                 vm_vec_unrotate(&db0, &pm0->docking_bays[docker_index].pnt[0], &objp->orient);
8679                 vm_vec_add2(&db0, &objp->pos);
8680
8681                 vm_vec_unrotate(&db1, &pm0->docking_bays[docker_index].pnt[1], &objp->orient);
8682                 vm_vec_add2(&db1, &objp->pos);
8683
8684                 vm_vec_unrotate(&db2, &pm1->docking_bays[dockee_index].pnt[0], &dobjp->orient);
8685                 vm_vec_add2(&db2, &dobjp->pos);
8686
8687                 vm_vec_unrotate(&db3, &pm1->docking_bays[dockee_index].pnt[1], &dobjp->orient);
8688                 vm_vec_add2(&db3, &dobjp->pos);
8689
8690                 vm_vec_avg(&goal_point, &db2, &db3);
8691
8692                 vm_vec_avg(&docker_point, &db0, &db1);
8693                 vm_vec_sub2(&docker_point, &objp->pos);
8694
8695                 if (dock_mode == DOA_DOCK) {
8696                         vector  t1, t2;
8697                         vector  w_in, w_out, vel_limit, acc_limit;
8698
8699                         fdist = vm_vec_dist_quick(vm_vec_avg(&t1, &db0, &db1), vm_vec_avg(&t2, &db2, &db3));
8700
8701                         //      Compute new orientation matrix and update rotational velocity.
8702                         w_in = objp->phys_info.rotvel;
8703                         vel_limit = objp->phys_info.max_rotvel;
8704                         vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8705
8706                         if (sip0->flags & SIF_SUPPORT)
8707                                 vm_vec_scale(&acc_limit, 2.0f);
8708
8709                         vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit);
8710                         objp->phys_info.rotvel = w_out;
8711                         objp->orient = nm;
8712
8713                         //      Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8714                         fdist += 10.0f * vm_vec_mag_quick(&w_out);
8715
8716                         dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale, dobjp->phys_info.speed);
8717                 } else {
8718                         Assert(dock_mode == DOA_DOCK_STAY);
8719                         objp->orient = dom;
8720                         vector  temp;
8721                         vm_vec_sub(&temp, &goal_point, &docker_point);
8722                         vm_vec_sub(&objp->pos, &goal_point, &docker_point);
8723                 }
8724
8725                 break;
8726         case DOA_UNDOCK_1: {
8727                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8728                         return 9999.9f;
8729                 }
8730
8731                 //      Undocking.
8732                 //      Move to point on dock path nearest to dock station.
8733                 Assert(aip->path_length >= 2);
8734                 goal_point = Path_points[aip->path_start + aip->path_length-2].pos;
8735
8736                 vm_vec_zero(&docker_point);
8737                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8738
8739                 dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale);
8740
8741                 break;
8742                           }
8743
8744         case DOA_UNDOCK_2: {
8745                 //      Undocking.
8746                 //      Move to point on dock path nearest to dock station and orient away from big ship.
8747                 int             desired_index;
8748
8749                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8750                         return 9999.9f;
8751                 }
8752
8753                 Assert(aip->path_length >= 2);
8754 //              if (aip->path_length >= 3)
8755 //                      desired_index = aip->path_length-3;
8756 //              else
8757                         desired_index = aip->path_length-2;
8758
8759                 goal_point = Path_points[aip->path_start + desired_index].pos;
8760
8761                 dock_move_towards_point(objp, NULL, &goal_point, speed_scale);
8762
8763                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8764                 break;
8765                           }
8766         case DOA_UNDOCK_3: {
8767                 float           dist, goal_dist;
8768                 vector  away_vec;
8769
8770                 goal_dist = objp->radius + dobjp->radius + 25.0f;
8771
8772                 dist = vm_vec_normalized_dir(&away_vec, &objp->pos, &dobjp->pos);
8773                 vm_vec_scale_add(&goal_point, &dobjp->pos, &away_vec, goal_dist);
8774                 if (vm_vec_dist_quick(&goal_point, &dobjp->pos) < vm_vec_dist_quick(&objp->pos, &dobjp->pos))
8775                         fdist = 0.0f;
8776                 else {
8777                         float   dot, accel;
8778                         float turn_time = Ship_info[Ships[objp->instance].ship_info_index].srotation_time;
8779                         ai_turn_towards_vector(&goal_point, objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
8780
8781                         dot = vm_vec_dot(&objp->orient.v.fvec, &away_vec);
8782                         accel = 0.1f;
8783                         if (dot > accel)
8784                                 accel = dot;
8785                         if (dist > goal_dist/2)
8786                                 accel *= 1.2f - 0.5f*goal_dist/dist;
8787
8788                         accelerate_ship(aip, accel);
8789                         fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8790                 }
8791
8792                 break;
8793                                                          }
8794         }
8795
8796 #ifndef NDEBUG
8797         //      For debug purposes, compute global orientation of both dock vectors and show
8798         //      how close they are.
8799         vector  d0, d1;
8800
8801         vm_vec_unrotate(&d0, &pm0->docking_bays[docker_index].norm[0], &objp->orient);
8802         vm_vec_unrotate(&d1, &pm1->docking_bays[dockee_index].norm[0], &dobjp->orient);
8803
8804         //nprintf(("AI", "or/app: dist = %7.3f/%7.3f, dot = %7.3f, global dot = %7.3f\n", 
8805         //      vm_vec_dist_quick(&goal_point, &objp->pos), fdist,
8806         //      vm_vec_dot(&objp->orient.v.fvec, &dom.v.fvec), 
8807         //      vm_vec_dot(&d0, &d1)));
8808 #endif
8809
8810         // -- Note, A lot of callers don't care about fdist, so OK to return ERROR value: Assert(fdist != UNINITIALIZED_VALUE);
8811         return fdist;
8812
8813 }
8814
8815 void debug_find_guard_object()
8816 {
8817         ship                    *shipp = &Ships[Pl_objp->instance];     
8818         object          *objp;
8819
8820         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
8821                 if ((Pl_objp != objp) && (objp->type == OBJ_SHIP)) {
8822                         if (objp->instance != -1) {
8823                                 if (Ships[objp->instance].team == shipp->team)  {
8824                                         // nprintf(("AI", "Setting guard object for %s to %s\n", shipp->ship_name, Ships[objp->instance].ship_name));
8825                                         ai_set_guard_object(Pl_objp, objp);
8826                                 }
8827                         }
8828                 }
8829         }
8830
8831 }
8832
8833 //      Given an object number, return the number of ships attacking it.
8834 int num_ships_attacking(int objnum)
8835 {
8836         object  *objp;
8837         ship_obj        *so;
8838         int             count = 0;
8839
8840         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8841                 objp = &Objects[so->objnum];
8842                 if (objp->instance != -1) {
8843                         ai_info *aip;
8844                         aip = &Ai_info[Ships[objp->instance].ai_index];
8845
8846                         if ((aip->mode == AIM_CHASE) && (aip->target_objnum == objnum))
8847                                 if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team)
8848                                         count++;
8849                 }
8850         }
8851
8852         return count;
8853 }
8854
8855 //      For all objects attacking object #objnum, remove the one that is farthest away.
8856 //      Do this by resuming previous behavior, if any.  If not, set target_objnum to -1.
8857 void remove_farthest_attacker(int objnum)
8858 {
8859         object  *objp, *objp2, *farthest_objp;
8860         ship_obj        *so;
8861         float           farthest_dist;
8862
8863         objp2 = &Objects[objnum];
8864
8865         farthest_dist = 9999999.9f;
8866         farthest_objp = NULL;
8867
8868         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8869                 objp = &Objects[so->objnum];
8870                 if ( !(objp->flags & OF_PLAYER_SHIP)) {
8871                         if (objp->instance != -1) {
8872                                 ai_info *aip2;
8873
8874                                 aip2 = &Ai_info[Ships[objp->instance].ai_index];
8875
8876                                 if ((aip2->mode == AIM_CHASE) && (aip2->target_objnum == objnum)) {
8877                                         if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team) {
8878                                                 float   dist;
8879
8880                                                 dist = vm_vec_dist_quick(&objp->pos, &objp2->pos);
8881                                                 if (dist < farthest_dist) {
8882                                                         farthest_dist = dist;
8883                                                         farthest_objp = objp;
8884                                                 }
8885                                         }
8886                                 }
8887                         }
8888                 }
8889         }
8890
8891         if (farthest_objp != NULL) {
8892                 ai_info *aip;
8893                 Assert(farthest_objp->type == OBJ_SHIP);
8894                 Assert((farthest_objp->instance > -1) && (farthest_objp->instance < MAX_SHIPS));
8895                 Assert(Ships[farthest_objp->instance].ai_index > -1);
8896
8897                 aip = &Ai_info[Ships[farthest_objp->instance].ai_index];
8898
8899                 if (!maybe_resume_previous_mode(Pl_objp, aip)) {
8900                         //      If already ignoring something under player's orders, don't ignore current target.
8901                         if ((aip->ignore_objnum == UNUSED_OBJNUM) || (aip->ai_flags & AIF_TEMPORARY_IGNORE)) {
8902                                 aip->ignore_objnum = aip->target_objnum;
8903                                 aip->ignore_signature = Objects[aip->target_objnum].signature;
8904                                 aip->ai_flags |= AIF_TEMPORARY_IGNORE;
8905                                 aip->ignore_expire_timestamp = timestamp(((myrand() % 10) + 20) * 1000);        //      OK to attack again in 20 to 24 seconds.
8906                         }
8907                         aip->target_objnum = -1;
8908                         ai_do_default_behavior(farthest_objp);
8909                 }
8910         }
8911 }
8912
8913 // Maybe limit the number of attackers on attack_objnum.  For now, only limit attackers
8914 // in attacked_objnum is the player
8915 // input:       attacked_objnum =>              object index for ship we want to limit attacks on
8916 //
8917 //      exit:                   1       =>      num attackers exceeds maximum, abort
8918 //                                      0       =>      removed the farthest attacker
8919 //                                      -1      =>      nothing was done
8920 int ai_maybe_limit_attackers(int attacked_objnum)
8921 {
8922         int rval=-1;
8923
8924         // limit the number of ships attacking the _player_ only
8925 //      if ( attacked_objnum == OBJ_INDEX(Player_obj) ) {
8926         if ( Objects[attacked_objnum].flags & OF_PLAYER_SHIP) {
8927                 int num_attacking;
8928                 num_attacking = num_ships_attacking(attacked_objnum);
8929
8930                 if (num_attacking == Skill_level_max_attackers[Game_skill_level]) {
8931                         remove_farthest_attacker(attacked_objnum);
8932                         rval=0;
8933                 } else if (num_attacking > Skill_level_max_attackers[Game_skill_level]) {
8934                         rval=1;
8935                 }
8936                 //nprintf(("AI", "Num attacking player = %i\n", num_attacking));
8937         }
8938
8939         return rval;
8940 }
8941
8942 //      Object being guarded by object *guard_objp was hit by object *hitter_objp
8943 void guard_object_was_hit(object *guard_objp, object *hitter_objp)
8944 {
8945         int             hitter_objnum;
8946         ai_info *aip;
8947
8948         aip = &Ai_info[Ships[guard_objp->instance].ai_index];
8949
8950         if (guard_objp == hitter_objp) {
8951                 // Int3();      //      Bogus!  Who tried to get me to attack myself!  Trace out and fix!
8952                 return;
8953         }
8954
8955         if (guard_objp->type == OBJ_GHOST || hitter_objp->type == OBJ_GHOST)
8956                 return;
8957
8958         if (aip->ai_flags & AIF_NO_DYNAMIC)     //      Not allowed to pursue dynamic goals.  So, why are we guarding?
8959                 return;
8960
8961         Assert( (hitter_objp->type == OBJ_SHIP) || (hitter_objp->type == OBJ_ASTEROID) || (hitter_objp->type == OBJ_WEAPON) );
8962
8963         hitter_objnum = OBJ_INDEX(hitter_objp);
8964
8965         if ( hitter_objp->type == OBJ_SHIP ) {
8966                 //      If the hitter object is the ignore object, don't attack it.
8967                 if (is_ignore_object(aip, hitter_objp-Objects))
8968                         return;
8969
8970                 //      If hitter is on same team as me, don't attack him.
8971                 if (Ships[guard_objp->instance].team == Ships[hitter_objp->instance].team)
8972                         return;
8973
8974                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
8975                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
8976                         return;
8977                 }
8978
8979                 // dont attack if you can't see him
8980                 if ( awacs_get_level(hitter_objp, &Ships[aip->shipnum], 1) < 1 ) {
8981                         // if he's a stealth and visible, but not targetable, ok to attack.
8982                         if ( is_object_stealth_ship(hitter_objp) ) {
8983                                 if ( ai_is_stealth_visible(guard_objp, hitter_objp) != STEALTH_VISIBLE ) {
8984                                         return;
8985                                 }
8986                         }
8987                 }
8988         }
8989
8990         if (aip->target_objnum == -1) {
8991                 aip->ok_to_target_timestamp = timestamp(0);
8992         }
8993
8994         if ((aip->submode == AIS_GUARD_PATROL) || (aip->submode == AIS_GUARD_STATIC)) {
8995
8996                 if ( hitter_objp->type == OBJ_SHIP ) {
8997                         if (!(Ship_info[Ships[guard_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
8998                                 return;
8999                         }
9000
9001                         // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9002                         if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9003                                 return;
9004                         }
9005                 }
9006
9007                 if (aip->target_objnum != hitter_objnum) {
9008                         aip->aspect_locked_time = 0.0f;
9009                 }
9010
9011                 aip->ok_to_target_timestamp = timestamp(0);
9012
9013                 set_target_objnum(aip, hitter_objnum);
9014                 //if (aip->target_objnum == -1) nprintf(("AI", "Frame %i: Attacking NONE\n",Framecount)); else nprintf(("AI", "Frame %i: Attacking %s\n", Framecount, Ships[Objects[aip->target_objnum].instance].ship_name));
9015                 aip->previous_mode = AIM_GUARD;
9016                 aip->previous_submode = aip->submode;
9017                 aip->mode = AIM_CHASE;
9018                 aip->submode = SM_ATTACK;
9019                 aip->submode_start_time = Missiontime;
9020                 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9021         } else if (aip->previous_mode == AIM_GUARD) {
9022                 if (aip->target_objnum == -1) {
9023
9024                         if ( hitter_objp->type == OBJ_SHIP ) {
9025                                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9026                                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9027                                         return;
9028                                 }
9029                         }
9030
9031                         set_target_objnum(aip, hitter_objnum);
9032                 //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));
9033                         aip->mode = AIM_CHASE;
9034                         aip->submode = SM_ATTACK;
9035                         aip->submode_start_time = Missiontime;
9036                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9037                 } else {
9038                         int     num_attacking_cur, num_attacking_new;
9039
9040                         num_attacking_cur = num_ships_attacking(aip->target_objnum);
9041                         if (num_attacking_cur > 1) {
9042                                 num_attacking_new = num_ships_attacking(hitter_objnum);
9043
9044                                 if (num_attacking_new < num_attacking_cur) {
9045
9046                                         if ( hitter_objp->type == OBJ_SHIP ) {
9047                                                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9048                                                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9049                                                         return;
9050                                                 }
9051                                         }
9052                                         set_target_objnum(aip, hitter_objp-Objects);
9053                 //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));
9054                                         aip->mode = AIM_CHASE;
9055                                         aip->submode = SM_ATTACK;
9056                                         aip->submode_start_time = Missiontime;
9057                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9058                                 }
9059                         }
9060                 }
9061         }
9062 }
9063
9064 //      Ship object *hit_objp was hit by ship object *hitter_objp.
9065 //      See if anyone is guarding hit_objp and, if so, do something useful.
9066 void maybe_update_guard_object(object *hit_objp, object *hitter_objp)
9067 {
9068         object  *objp;
9069         ship_obj        *so;
9070
9071         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9072                 objp = &Objects[so->objnum];
9073                 if (objp->instance != -1) {
9074                         ai_info *aip;
9075                         aip = &Ai_info[Ships[objp->instance].ai_index];
9076
9077                         if ((aip->mode == AIM_GUARD) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
9078                                 if (aip->guard_objnum == hit_objp-Objects) {
9079                                         guard_object_was_hit(objp, hitter_objp);
9080                                 } else if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[hit_objp->instance].ai_index].wing)) {
9081                                         guard_object_was_hit(objp, hitter_objp);
9082                                 }
9083                         }
9084                 }
9085         }
9086 }
9087
9088 // Scan missile list looking for bombs homing on guarded_objp
9089 // return 1 if bomb is found (and targeted by guarding_objp), otherwise return 0
9090 int ai_guard_find_nearby_bomb(object *guarding_objp, object *guarded_objp)
9091 {       
9092         missile_obj     *mo;
9093         object          *bomb_objp, *closest_bomb_objp=NULL;
9094         float                   dist, dist_to_guarding_obj,closest_dist_to_guarding_obj=999999.0f;
9095         weapon          *wp;
9096         weapon_info     *wip;
9097
9098         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
9099                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
9100                 bomb_objp = &Objects[mo->objnum];
9101
9102                 wp = &Weapons[bomb_objp->instance];
9103                 wip = &Weapon_info[wp->weapon_info_index];
9104
9105                 if ( !(wip->wi_flags & WIF_BOMB) ) {
9106                         continue;
9107                 }
9108
9109                 if ( wp->homing_object != guarded_objp ) {
9110                         continue;
9111                 }
9112
9113                 dist = vm_vec_dist_quick(&bomb_objp->pos, &guarded_objp->pos);
9114
9115                 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9116                         dist_to_guarding_obj = vm_vec_dist_quick(&bomb_objp->pos, &guarding_objp->pos);
9117                         if ( dist_to_guarding_obj < closest_dist_to_guarding_obj ) {
9118                                 closest_dist_to_guarding_obj = dist_to_guarding_obj;
9119                                 closest_bomb_objp = bomb_objp;
9120                         }
9121                 }
9122         }
9123
9124         if ( closest_bomb_objp ) {
9125                 guard_object_was_hit(guarding_objp, closest_bomb_objp);
9126                 return 1;
9127         }
9128
9129         return 0;
9130 }
9131
9132 //      Scan enemy ships and see if one is near enough to guard object to be pursued.
9133 void ai_guard_find_nearby_ship(object *guarding_objp, object *guarded_objp)
9134 {
9135         ship            *guarding_shipp = &Ships[guarding_objp->instance];
9136         ai_info *guarding_aip = &Ai_info[guarding_shipp->ai_index];
9137         ship_obj        *so;
9138         object  *enemy_objp;
9139         float           dist;
9140
9141         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9142                 enemy_objp = &Objects[so->objnum];
9143
9144                 if (enemy_objp->instance < 0) {
9145                         continue;
9146                 }
9147
9148                 ship    *eshipp = &Ships[enemy_objp->instance];
9149
9150                 //      Don't attack a cargo container or other harmless ships
9151                 if (!(Ship_info[eshipp->ship_info_index].flags & SIF_HARMLESS)) {
9152                         if (guarding_shipp->team != eshipp->team)       {
9153                                 dist = vm_vec_dist_quick(&enemy_objp->pos, &guarded_objp->pos);
9154                                 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9155                                         guard_object_was_hit(guarding_objp, enemy_objp);
9156                                 } else if ((dist < 3000.0f) && (Ai_info[eshipp->ai_index].target_objnum == guarding_aip->guard_objnum)) {
9157                                         //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));
9158                                         guard_object_was_hit(guarding_objp, enemy_objp);
9159                                 }
9160                         }
9161                 }
9162         }
9163 }
9164
9165 // Scan for nearby asteroids.  Favor asteroids which have their collide_objnum set to that of the
9166 // guarded ship.  Also, favor asteroids that are closer to the guarding ship, since it looks cooler
9167 // when a ship blows up an asteroid then goes after the pieces that break off.
9168 void ai_guard_find_nearby_asteroid(object *guarding_objp, object *guarded_objp)
9169 {       
9170         float           dist;
9171
9172         object  *closest_asteroid_objp=NULL, *danger_asteroid_objp=NULL, *asteroid_objp;
9173         float           dist_to_self, closest_danger_asteroid_dist=999999.0f, closest_asteroid_dist=999999.0f;
9174
9175         for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp != END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) {
9176                 if ( asteroid_objp->type == OBJ_ASTEROID ) {
9177                         // Attack asteroid if near guarded ship
9178                         dist = vm_vec_dist_quick(&asteroid_objp->pos, &guarded_objp->pos);
9179                         if ( dist < (MAX_GUARD_DIST + guarded_objp->radius)*2) {
9180                                 dist_to_self = vm_vec_dist_quick(&asteroid_objp->pos, &guarding_objp->pos);
9181                                 if ( OBJ_INDEX(guarded_objp) == asteroid_collide_objnum(asteroid_objp) ) {
9182                                         if( dist_to_self < closest_danger_asteroid_dist ) {
9183                                                 danger_asteroid_objp=asteroid_objp;
9184                                                 closest_danger_asteroid_dist=dist_to_self;
9185                                         }
9186                                 } 
9187                                 if ( dist_to_self < closest_asteroid_dist ) {
9188                                         // only attack if moving slower than own max speed
9189                                         if ( vm_vec_mag_quick(&asteroid_objp->phys_info.vel) < guarding_objp->phys_info.max_vel.xyz.z ) {
9190                                                 closest_asteroid_dist = dist_to_self;
9191                                                 closest_asteroid_objp = asteroid_objp;
9192                                         }
9193                                 }
9194                         }
9195                 }
9196         }
9197
9198         if ( danger_asteroid_objp ) {
9199                 guard_object_was_hit(guarding_objp, danger_asteroid_objp);
9200         } else if ( closest_asteroid_objp ) {
9201                 guard_object_was_hit(guarding_objp, closest_asteroid_objp);
9202         }
9203 }
9204
9205 //      Scan potential harmful objects and see if one is near enough to guard object to be pursued.
9206 void ai_guard_find_nearby_object()
9207 {
9208         ship                    *shipp = &Ships[Pl_objp->instance];
9209         ai_info         *aip = &Ai_info[shipp->ai_index];
9210         object          *guardobjp;
9211         int                     bomb_found=0;
9212
9213         guardobjp = &Objects[aip->guard_objnum];
9214         
9215         // highest priority is a bomb fired on guarded ship
9216         bomb_found = ai_guard_find_nearby_bomb(Pl_objp, guardobjp);
9217
9218         if ( !bomb_found ) {
9219                 // check for ships if there are no bombs fired at guarded ship
9220                 ai_guard_find_nearby_ship(Pl_objp, guardobjp);
9221
9222                 // if not attacking anything, go for asteroid close to guarded ship
9223                 if ( (aip->target_objnum == -1) && asteroid_count() ) {
9224                         ai_guard_find_nearby_asteroid(Pl_objp, guardobjp);
9225                 }
9226         }
9227 }
9228
9229 // gets closest point on extended axis of cylinder, r_vec, and radius of cylinder
9230 // returns z of axis_point in cyl_objp reference frame
9231 float get_cylinder_points(object *other_objp, object *cyl_objp, vector *axis_pt, vector *r_vec, float *radius)
9232 {
9233         Assert(other_objp->type == OBJ_SHIP);
9234         Assert(cyl_objp->type == OBJ_SHIP);
9235
9236         // get radius of cylinder
9237         polymodel *pm = model_get(Ships[cyl_objp->instance].modelnum);
9238         float tempx, tempy;
9239         tempx = max(-pm->mins.xyz.x, pm->maxs.xyz.x);
9240         tempy = max(-pm->mins.xyz.y, pm->maxs.xyz.y);
9241         *radius = max(tempx, tempy);
9242
9243         // get vec from cylinder to other_obj
9244         vector r_sph;
9245         vm_vec_sub(&r_sph, &other_objp->pos, &cyl_objp->pos);
9246
9247         // get point on axis and on cylinder
9248         // extended_cylinder_z is along extended cylinder
9249         // cylinder_z is capped within cylinder
9250         float extended_cylinder_z = vm_vec_dotprod(&r_sph, &cyl_objp->orient.v.fvec);
9251
9252         // get pt on axis of extended cylinder
9253         vm_vec_scale_add(axis_pt, &cyl_objp->pos, &cyl_objp->orient.v.fvec, extended_cylinder_z);
9254
9255         // get r_vec (pos - axis_pt) normalized
9256         vm_vec_normalized_dir(r_vec, &other_objp->pos, axis_pt);
9257
9258         return extended_cylinder_z;
9259 }
9260
9261 // handler for guard behavior when guarding BIG ships
9262 //      When someone has attacked guarded ship, then attack that ship.
9263 // To attack another ship, switch out of guard mode into chase mode.
9264 void ai_big_guard()
9265 {
9266         
9267         ship                    *shipp = &Ships[Pl_objp->instance];
9268         ai_info         *aip = &Ai_info[shipp->ai_index];
9269         object          *guard_objp;
9270
9271         // sanity checks already done in ai_guard()
9272         guard_objp = &Objects[aip->guard_objnum];
9273
9274         switch (aip->submode) {
9275         case AIS_GUARD_STATIC:
9276         case AIS_GUARD_PATROL:
9277                 {
9278                 vector axis_pt, r_vec, theta_vec;
9279                 float radius, extended_z;
9280
9281                 // get random [0 to 1] based on OBJNUM
9282                 float objval = static_randf(Pl_objp-Objects);
9283
9284                 // get position relative to cylinder of guard_objp              
9285                 extended_z = get_cylinder_points(Pl_objp, guard_objp, &axis_pt, &r_vec, &radius);
9286                 vm_vec_crossprod(&theta_vec, &guard_objp->orient.v.fvec, &r_vec);
9287
9288                 // half ships circle each way
9289                 if (objval > 0.5f) {
9290                         vm_vec_negate(&theta_vec);
9291                 }
9292
9293                 float min_guard_dist = radius + Pl_objp->radius + 50.0f;
9294                 float desired_guard_dist = min_guard_dist + 0.5f * ((1.0f + objval) * MAX_GUARD_DIST);
9295                 float max_guard_dist =     min_guard_dist + 1.0f * ((1.0f + objval) * MAX_GUARD_DIST);
9296
9297                 // get z extents
9298                 float min_z, max_z, length;
9299                 polymodel *pm = model_get(Ships[guard_objp->instance].modelnum);
9300                 min_z = pm->mins.xyz.z;
9301                 max_z = pm->maxs.xyz.z;
9302                 length = max_z - min_z;
9303
9304                 // get desired z
9305                 // how often to choose new desired_z
9306                 // 1*(64) sec < 2000, 2*(64) < 2-4000 3*(64) > 4-8000, etc (Missiontime >> 22 is 64 sec intervals)
9307                 int time_choose = int(floor(log(length * 0.001) / log(2)));
9308                 float desired_z = min_z + length * static_randf( Pl_objp-Objects ^ (Missiontime >> (22 + time_choose)) );
9309
9310                 // get r from guard_ship
9311                 float cur_guard_rad = vm_vec_dist(&Pl_objp->pos, &axis_pt);
9312
9313                 // is ship within extents of cylinder of ship it is guarding
9314                 int inside = (extended_z > min_z) && (extended_z < min_z + length);
9315
9316                 vector goal_pt;
9317                 // maybe go into orbit mode
9318                 if (cur_guard_rad < max_guard_dist) {
9319                         if ( cur_guard_rad > min_guard_dist ) {
9320                                 if (inside) {
9321                                         // orbit
9322                                         vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, desired_guard_dist);
9323                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9324                                 } else {
9325                                         // move to where I can orbit
9326                                         if (extended_z < min_z) {
9327                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, min_z);
9328                                         } else {
9329                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, max_z);
9330                                         }
9331                                         vm_vec_scale_add2(&goal_pt, &r_vec, desired_guard_dist);
9332                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9333                                 }
9334                         } else {
9335                                 // too close for orbit mode
9336                                 if (inside) {
9337                                         // inside (fly straight out and return circle)
9338                                         vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, max_guard_dist);
9339                                 } else {
9340                                         // outside (fly to edge and circle)
9341                                         if (extended_z < min_z) {
9342                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, min_z);
9343                                         } else {
9344                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, max_z);
9345                                         }
9346                                         vm_vec_scale_add2(&goal_pt, &r_vec, max_guard_dist);
9347                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9348                                 }
9349                         }
9350
9351                         if (Pl_objp->phys_info.fspeed > 0) {
9352                                 // modify goal_pt to take account moving guard objp
9353                                 float dist = vm_vec_dist_quick(&Pl_objp->pos, &goal_pt);
9354                                 float time = dist / Pl_objp->phys_info.fspeed;
9355                                 vm_vec_scale_add2(&goal_pt, &guard_objp->phys_info.vel, time);
9356
9357                                 // now modify to move to desired z (at a max of 20 m/s)
9358                                 float delta_z = desired_z - extended_z;
9359                                 float v_z = delta_z * 0.2f;
9360                                 if (v_z < -20) {
9361                                         v_z = -20.0f;
9362                                 } else if (v_z > 20) {
9363                                         v_z = 20.0f;
9364                                 }
9365
9366                                 vm_vec_scale_add2(&goal_pt, &guard_objp->orient.v.fvec, v_z*time);
9367                         }
9368
9369                 } else {
9370                         // cast vector to center of guard_ship adjusted by desired_z
9371                         float delta_z = desired_z - extended_z;
9372                         vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, delta_z);
9373                 }
9374
9375                 // try not to bump into things along the way
9376                 if ( (cur_guard_rad > max_guard_dist) || (extended_z < min_z) || (extended_z > max_z) ) {
9377                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9378                                 return;
9379                         }
9380
9381                         if (avoid_player(Pl_objp, &goal_pt)) {
9382                                 return;
9383                         }
9384                 } else {
9385                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9386                                 return;
9387                         }
9388                 }
9389
9390                 // got the point, now let's go there
9391                 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);
9392 //              aip->goal_point = goal_pt;
9393                 accelerate_ship(aip, 1.0f);
9394
9395                 //      Periodically, scan for a nearby ship to attack.
9396                 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9397                         ai_guard_find_nearby_object();
9398                 }
9399                 }
9400                 break;
9401
9402         case AIS_GUARD_ATTACK:
9403                 //      The guarded ship has been attacked.  Do something useful!
9404                 ai_chase();
9405                 break;
9406
9407         default:
9408                 //Int3();       //      Illegal submode for Guard mode.
9409                 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9410                 aip->submode = AIS_GUARD_PATROL;
9411                 break;
9412         }
9413 }
9414
9415 //      Main handler for guard behavior.
9416 //      When someone has attacked guarded ship, then attack that ship.
9417 // To attack another ship, switch out of guard mode into chase mode.
9418 void ai_guard()
9419 {
9420         ship                    *shipp = &Ships[Pl_objp->instance];
9421         ai_info         *aip = &Ai_info[shipp->ai_index];
9422         object          *guard_objp;    
9423         ship                    *gshipp;
9424         float                   dist_to_guardobj, dot_to_guardobj;
9425         vector          vec_to_guardobj;
9426
9427         /*      //      Debug code, find an object to guard.
9428         int finding_guard_objnum = 0;   //      Debug code, to see if body of "if" below gets executed. 
9429         if (aip->guard_objnum == -1) {
9430                 finding_guard_objnum = 1;
9431                 debug_find_guard_object();
9432                 if (aip->guard_objnum == -1)
9433                         return;
9434         }
9435 */
9436         if (aip->guard_objnum == -1) {
9437                 aip->mode = AIM_NONE;
9438                 return;
9439         }
9440
9441         Assert(aip->guard_objnum != -1);
9442
9443         guard_objp = &Objects[aip->guard_objnum];
9444
9445         if (guard_objp == Pl_objp) {
9446                 Int3();         //      This seems illegal.  Why is a ship guarding itself?
9447                 aip->guard_objnum = -1;
9448                 return;
9449         }
9450
9451         // check that I have someone to guard
9452         if (guard_objp->instance == -1) {
9453                 return;
9454         }
9455
9456         //      Not sure whether this should be impossible, or a reasonable cleanup condition.
9457         //      For now (3/31/97), it's getting trapped by an Assert, so clean it up.
9458         if (guard_objp->type != OBJ_SHIP) {
9459                 aip->guard_objnum = -1;
9460                 return;
9461         }
9462
9463         // handler for gurad object with BIG radius
9464         if (guard_objp->radius > BIG_GUARD_RADIUS) {
9465                 ai_big_guard();
9466                 return;
9467         }
9468
9469         gshipp = &Ships[guard_objp->instance];
9470
9471         float                   objval;
9472         vector          goal_point;
9473         vector          rel_vec;
9474         float                   dist_to_goal_point, dot_to_goal_point, accel_scale;
9475         vector          v2g, rvec;
9476
9477         // get random [0 to 1] based on OBJNUM
9478         objval = static_randf(Pl_objp-Objects);
9479
9480         switch (aip->submode) {
9481         case AIS_GUARD_STATIC:
9482         case AIS_GUARD_PATROL:
9483                 //      Stay near ship
9484                 dist_to_guardobj = vm_vec_normalized_dir(&vec_to_guardobj, &guard_objp->pos, &Pl_objp->pos);
9485                 dot_to_guardobj = vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_to_guardobj);
9486
9487                 rel_vec = aip->guard_vec;
9488                 vm_vec_add(&goal_point, &guard_objp->pos, &rel_vec);
9489
9490                 vm_vec_normalized_dir(&v2g, &goal_point, &Pl_objp->pos);
9491                 dist_to_goal_point = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
9492                 dot_to_goal_point = vm_vec_dot(&v2g, &Pl_objp->orient.v.fvec);
9493                 accel_scale = (1.0f + dot_to_goal_point)/2.0f;
9494
9495                 //      If far away, get closer
9496                 if (dist_to_goal_point > MAX_GUARD_DIST + 1.5 * (Pl_objp->radius + guard_objp->radius)) {
9497                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 5.0f)) {
9498                                 return;
9499                         }
9500
9501                         if (avoid_player(Pl_objp, &goal_point)) {
9502                                 return;
9503                         }
9504
9505                         // quite far away, so try to go straight to 
9506                         compute_desired_rvec(&rvec, &goal_point, &Pl_objp->pos);
9507                         ai_turn_towards_vector(&goal_point, Pl_objp, flFrametime, Ship_info[shipp->ship_info_index].srotation_time, NULL, NULL, 0.0f, 0, &rvec);
9508
9509                         accelerate_ship(aip, accel_scale * (0.25f + dist_to_goal_point/700.0f));
9510                 } else {
9511                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 2.0f)) {
9512                                 return;
9513                         }
9514
9515                         // get max of guard_objp (1) normal speed (2) dock speed
9516                         float speed = guard_objp->phys_info.speed;
9517
9518                         if (guard_objp->type == OBJ_SHIP) {
9519                                 ai_info *guard_aip = &Ai_info[Ships[guard_objp->instance].ai_index];
9520
9521                                 if (guard_aip->dock_objnum != -1) {
9522                                         speed = max(speed, Objects[guard_aip->dock_objnum].phys_info.speed);
9523                                 }
9524                         }
9525                         
9526                         //      Deal with guarding a small object.
9527                         //      If going to guard_vec might cause a collision with guarded object, pick a new guard point.
9528                         if (vm_vec_dot(&v2g, &vec_to_guardobj) > 0.8f) {
9529                                 if (dist_to_guardobj < dist_to_goal_point) {
9530                                         ai_set_guard_vec(Pl_objp, guard_objp);  //      OK to return here.
9531                                         return;
9532                                 }
9533                         } 
9534
9535                         if (speed > 10.0f) {
9536                                 //      If goal ship is moving more than a tiny bit, don't orbit it, get near it.
9537                                 if (vm_vec_dist_quick(&goal_point, &Pl_objp->pos) > 40.0f) {
9538                                         if (vm_vec_dot(&Pl_objp->orient.v.fvec, &v2g) < 0.0f) {
9539                                                 //      Just slow down, don't turn.
9540                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point/10.0f);
9541                                         } else {
9542                                                 //      Goal point is in front.
9543
9544                                                 //      If close to goal point, don't change direction, just change speed.
9545                                                 if (dist_to_goal_point > Pl_objp->radius + 10.0f) {
9546                                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9547                                                 }
9548                                                 
9549                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + (dist_to_goal_point-40.0f)/20.0f);
9550                                         }
9551                                 } else {
9552                                         if (dot_to_goal_point > 0.8f) {
9553                                                 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9554                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + dist_to_goal_point*0.1f);
9555                                         } else {
9556                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point*0.1f - 1.0f);
9557                                         }
9558                                 }
9559                         // consider guard object STILL
9560                         } else if (guard_objp->radius < 50.0f) {
9561                                 if (dist_to_goal_point > 15.0f) {
9562                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9563                                         set_accel_for_target_speed(Pl_objp, (dist_to_goal_point-10.0f)/2.0f);
9564                                 } else if (Pl_objp->phys_info.speed < 1.0f) {
9565                                         turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9566                                 }
9567                                 //      It's a big ship
9568                         } else if (dist_to_guardobj > MAX_GUARD_DIST + Pl_objp->radius + guard_objp->radius) {
9569                                 //      Orbiting ship, too far away
9570                                 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.0f + objval/2) * guard_objp->radius);
9571                                 accelerate_ship(aip, (1.0f + dot)/2.0f);
9572                         } else if (dist_to_guardobj < Pl_objp->radius + guard_objp->radius) {
9573                                 //      Orbiting ship, got too close
9574                                 turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9575                                 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9576                                         change_acceleration(aip, 0.25f);
9577                                 else
9578                                         accelerate_ship(aip, 0.5f + objval/4.0f);
9579                         } else {
9580                                 //      Orbiting ship, about the right distance away.
9581                                 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.5f + objval/2.0f)*guard_objp->radius);
9582                                 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9583                                         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));
9584                                 else
9585                                         accelerate_ship(aip, 0.5f * (1.0f + dot) * (0.3f + objval/3.0f));
9586                         }
9587                 }
9588
9589                 //      Periodically, scan for a nearby ship to attack.
9590                 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9591                         ai_guard_find_nearby_object();
9592                 }
9593                 break;
9594
9595         case AIS_GUARD_ATTACK:
9596                 //      The guarded ship has been attacked.  Do something useful!
9597                 ai_chase();
9598
9599                 break;
9600         default:
9601                 //Int3();       //      Illegal submode for Guard mode.
9602                 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9603                 aip->submode = AIS_GUARD_PATROL;
9604                 break;
9605         }
9606
9607 }
9608
9609 // Return the object of the ship that the given object is docked
9610 // with.  Currently, we know a ship is docked when his ai_mode is AIM_DOCK,
9611 // and his submode is AIS_DOCK_3.  I suppose that this is likely to change though.
9612 // Also, the objnum that was is passed in may not be the object that actually
9613 // performed the docking maneuver.  This code will account for that case.
9614 object *ai_find_docked_object( object *docker )
9615 {
9616         ai_info *aip;
9617
9618         // we are trying to find the dockee of docker.  (Note that that these terms
9619         // are totally relative to what is passed in as a parameter.)
9620
9621         // first thing to attempt is to check and see if this object is docked with something.
9622         Assert( docker->type == OBJ_SHIP );             // this had probably better be a ship!!!
9623         aip = &Ai_info[Ships[docker->instance].ai_index];
9624         if ( !(aip->ai_flags & AIF_DOCKED) )            // flag not set if not docked with anything
9625                 return NULL;
9626
9627         if ( aip->dock_objnum == -1 ) {
9628                 Int3();                                                                                 // mwa says this is wrong wrong wrong
9629                 ai_do_objects_undocked_stuff( docker, NULL );
9630                 return NULL;
9631         }
9632
9633         return &Objects[aip->dock_objnum];
9634
9635 }
9636
9637
9638 // define for the points subtracted from score for a rearm started on a player.
9639 #define REPAIR_PENALTY          50
9640
9641
9642 // function to clean up ai flags, variables, and other interesting information
9643 // for a ship that was getting repaired.  The how parameter is useful for multiplayer
9644 // only in that it tells us why the repaired ship is being cleaned up.
9645 void ai_do_objects_repairing_stuff( object *repaired_objp, object *repair_objp, int how )
9646 {
9647         ai_info *aip, *repair_aip;
9648         int             stamp = -1;
9649
9650         Assert( repaired_objp->type == OBJ_SHIP);
9651         aip = &Ai_info[Ships[repaired_objp->instance].ai_index];
9652
9653         // multiplayer
9654         int p_index;
9655         p_index = -1;
9656         if(Game_mode & GM_MULTIPLAYER){
9657                 p_index = multi_find_player_by_object(repaired_objp);           
9658         }               
9659         else {          
9660                 if(repaired_objp == Player_obj){
9661                         p_index = Player_num;
9662                 }
9663         }
9664
9665         switch( how ) {
9666         case REPAIR_INFO_BEGIN:
9667                 aip->ai_flags |= AIF_BEING_REPAIRED;
9668                 aip->ai_flags &= ~AIF_AWAITING_REPAIR;
9669                 stamp = timestamp(-1);
9670
9671                 // if this is a player ship, then subtract the repair penalty from this player's score
9672                 if ( repaired_objp->flags & OF_PLAYER_SHIP ) {
9673                         if ( !(Game_mode & GM_MULTIPLAYER) ) {
9674                                 Player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());                    // subtract the penalty
9675                         } else {
9676                                 /*
9677                                 int pnum;
9678
9679                                 // multiplayer game -- find the player, then subtract the score
9680                                 pnum = multi_find_player_by_object( repaired_objp );
9681                                 if ( pnum != -1 ) {
9682                                         Net_players[pnum].player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());
9683
9684                                         // squad war
9685                                         multi_team_maybe_add_score(-(int)(REPAIR_PENALTY * scoring_get_scale_factor()), Net_players[pnum].p_info.team);
9686                                 } else {
9687                                         nprintf(("Network", "Couldn't find player for ship %s for repair penalty\n", Ships[repaired_objp->instance].ship_name));
9688                                 }
9689                                 */
9690                         }
9691                 }
9692                 break;
9693
9694         case REPAIR_INFO_BROKEN:
9695                 aip->ai_flags &= ~AIF_BEING_REPAIRED;
9696                 aip->ai_flags |= AIF_AWAITING_REPAIR;
9697                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9698                 break;
9699
9700         case REPAIR_INFO_END:
9701                 // when only awaiting repair, and the repair is ended, then set dock_objnum to -1.
9702                 if ( aip->ai_flags & AIF_AWAITING_REPAIR ){
9703                         aip->dock_objnum = -1;
9704                 }
9705                 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9706                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9707                 break;
9708
9709         case REPAIR_INFO_QUEUE:
9710                 aip->ai_flags |= AIF_AWAITING_REPAIR;
9711                 if ( aip == Player_ai ){
9712                         hud_support_view_start();
9713                 }
9714                 stamp = timestamp(-1);
9715                 break;
9716
9717         case REPAIR_INFO_ABORT:
9718         case REPAIR_INFO_KILLED:
9719                 // 5/4/98 -- MWA -- Need to set dock objnum to -1 to let code know this guy who was getting
9720                 // repaired (or queued for repair), isn't really going to be docked with anyone anymore.
9721                 aip->dock_objnum = -1;
9722                 aip->ai_flags &= ~AIF_DOCKED;
9723                 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9724                 if (repair_objp != NULL) {
9725                         repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9726                         repair_aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9727                 }               
9728
9729                 if ( p_index >= 0 ) {
9730                         hud_support_view_abort();
9731
9732                         // send appropriate message to player here
9733                         if ( how == REPAIR_INFO_KILLED ){
9734                                 message_send_builtin_to_player( MESSAGE_SUPPORT_KILLED, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9735                         } else {
9736                                 if ( repair_objp ){
9737                                         message_send_builtin_to_player( MESSAGE_REPAIR_ABORTED, &Ships[repair_objp->instance], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9738                                 }
9739                         }
9740                 }
9741
9742                 // add log entry if this is a player
9743                 if ( repaired_objp->flags & OF_PLAYER_SHIP ){
9744                         mission_log_add_entry(LOG_PLAYER_REARM_ABORT, Ships[repaired_objp->instance].ship_name, NULL);
9745                 }
9746
9747                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9748                 break;
9749
9750         case REPAIR_INFO_COMPLETE:
9751                 // clear the being repaired flag -- and 
9752                 if ( p_index >= 0 ) {
9753                         Assert( repair_objp );
9754                         
9755                         hud_support_view_stop();                        
9756
9757                         message_send_builtin_to_player(MESSAGE_REPAIR_DONE, &Ships[repair_objp->instance], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, p_index, -1);
9758                 }
9759                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9760                 break;
9761
9762         case REPAIR_INFO_ONWAY:
9763                 // need to set the dock_signature so that clients in multiplayer games rearm correctly
9764                 Assert( repair_objp );
9765                 aip->dock_signature = repair_objp->signature; 
9766                 aip->dock_objnum = OBJ_INDEX(repair_objp);
9767                 stamp = timestamp(-1);
9768                 break;
9769
9770         default:
9771                 Int3();                 // bogus type of repair info
9772         }
9773
9774         if (repair_objp){
9775                 Ai_info[Ships[repair_objp->instance].ai_index].warp_out_timestamp = stamp;
9776         }
9777
9778         // repair_objp might be NULL is we are cleaning up this mode because of the support ship
9779         // getting killed.
9780         if ( repair_objp ) {
9781                 aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9782                 switch ( how ) {
9783                 case REPAIR_INFO_ONWAY:
9784                         Assert( repaired_objp != NULL );
9785                         aip->goal_objnum = OBJ_INDEX(repaired_objp);
9786                         aip->ai_flags |= AIF_REPAIRING;
9787                         break;
9788
9789                 case REPAIR_INFO_BROKEN:
9790                         break;
9791
9792                 case REPAIR_INFO_END:
9793                 case REPAIR_INFO_ABORT:
9794                 case REPAIR_INFO_KILLED:
9795                         if ( how == REPAIR_INFO_ABORT )
9796                                 aip->goal_objnum = -1;
9797
9798                         aip->ai_flags &= ~AIF_REPAIRING;
9799                         break;
9800                         
9801                 case REPAIR_INFO_QUEUE:
9802                         ai_add_rearm_goal( repaired_objp, repair_objp );
9803                         break;
9804
9805                 case REPAIR_INFO_BEGIN:
9806                 case REPAIR_INFO_COMPLETE:
9807                         break;
9808
9809                 default:
9810                         Int3();         // bogus type of repair info
9811                 }
9812         }
9813
9814         multi_maybe_send_repair_info( repaired_objp, repair_objp, how );
9815 }
9816
9817 //      Cleanup AI stuff for when a ship was supposed to dock with another, but the ship
9818 //      it was supposed to dock with is no longer valid.
9819 void ai_cleanup_dock_mode(ai_info *aip, ship *shipp)
9820 {
9821         object *objp;
9822
9823         objp = &Objects[shipp->objnum];
9824         aip->mode = AIM_NONE;
9825
9826         if (aip->ai_flags & AIF_REPAIRING) {
9827                 Assert( aip->goal_objnum != -1 );
9828                 ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], &Objects[shipp->objnum], REPAIR_INFO_KILLED );
9829         } else if ( aip->ai_flags & AIF_BEING_REPAIRED ) {
9830                 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9831                 Assert( aip->dock_objnum != -1 );
9832                 ai_do_objects_repairing_stuff( &Objects[shipp->objnum], &Objects[aip->dock_objnum], REPAIR_INFO_KILLED );
9833         } else if ( aip->ai_flags & AIF_AWAITING_REPAIR ) {
9834                 // need to find the support ship that has me as a goal_objnum
9835                 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9836                 // MWA -- 3/38/98  Check to see if this guy is queued for a support ship, or there is already
9837                 // one in the mission
9838                 if ( mission_is_repair_scheduled(objp) ) {
9839                         mission_remove_scheduled_repair( objp );                        // this function will notify multiplayer clients.
9840                 } else {
9841                         if ( aip->dock_objnum != -1 )
9842                                 ai_do_objects_repairing_stuff( objp, &Objects[aip->dock_objnum], REPAIR_INFO_ABORT );
9843                         else
9844                                 ai_do_objects_repairing_stuff( objp, NULL, REPAIR_INFO_ABORT );
9845                 }
9846         }
9847
9848         if ( aip->ai_flags & AIF_DOCKED ) {
9849                 ai_info *other_aip;
9850
9851                 Assert( aip->dock_objnum != -1 );
9852
9853                 // if docked, and the dock_objnum is not undocking, force them to near last stage
9854                 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
9855                 if ( (other_aip->mode == AIM_DOCK) && (other_aip->submode < AIS_UNDOCK_3) )
9856                         other_aip->submode = AIS_UNDOCK_3;
9857                 ai_do_objects_undocked_stuff( objp, &Objects[aip->dock_objnum] );
9858         }
9859 }
9860
9861 /*
9862 //      Make dockee_objp shake a bit due to docking.
9863 void ai_dock_shake(object *docker_objp, object *dockee_objp)
9864 {
9865         vector  tangles;
9866         matrix  rotmat, tmp;
9867         float           scale;
9868         angles  *ap;
9869
9870         scale = 0.25f;          //      Compute this based on mass and speed at time of docking.
9871
9872         vm_vec_rand_vec_quick(&tangles);
9873         vm_vec_scale(&tangles, scale);
9874
9875         ap = (angles *) &tangles;
9876
9877         vm_angles_2_matrix(&rotmat, ap);
9878         vm_matrix_x_matrix( &tmp, &dockee_objp->orient, &rotmat );
9879         dockee_objp->orient = tmp;
9880
9881         vm_orthogonalize_matrix(&dockee_objp->orient);
9882
9883         dock_orient_and_approach(docker_objp, dockee_objp, DOA_DOCK_STAY);
9884
9885 }
9886 */
9887
9888 //      Make Pl_objp point at aip->goal_point.
9889 void ai_still()
9890 {
9891         ship    *shipp;
9892         ai_info *aip;
9893
9894         Assert(Pl_objp->type == OBJ_SHIP);
9895         Assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_OBJECTS));
9896
9897         shipp = &Ships[Pl_objp->instance];
9898         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
9899
9900         aip = &Ai_info[shipp->ai_index];
9901
9902         turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
9903 }
9904
9905 //      Make *Pl_objp stay near another ship.
9906 void ai_stay_near()
9907 {
9908         ai_info *aip;
9909         int             goal_objnum;
9910
9911         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
9912
9913         goal_objnum = aip->goal_objnum;
9914
9915         if ((goal_objnum < 0) || (Objects[goal_objnum].type != OBJ_SHIP) || (Objects[goal_objnum].signature != aip->goal_signature)) {
9916                 aip->mode = AIM_NONE;
9917         } else {
9918                 float           dist, max_dist, scale;
9919                 vector  rand_vec, goal_pos, vec_to_goal;
9920                 object  *goal_objp;
9921
9922                 goal_objp = &Objects[goal_objnum];
9923
9924                 //      Make not all ships pursue same point.
9925                 static_randvec(Pl_objp-Objects, &rand_vec);
9926
9927                 //      Make sure point is in front hemisphere (relative to Pl_objp's position.
9928                 vm_vec_sub(&vec_to_goal, &goal_objp->pos, &Pl_objp->pos);
9929                 if (vm_vec_dot(&rand_vec, &vec_to_goal) > 1.0f) {
9930                         vm_vec_negate(&rand_vec);
9931                 }
9932
9933                 //      Scale the random vector by an amount proportional to the distance from Pl_objp to the true goal.
9934                 dist = vm_vec_dist_quick(&goal_objp->pos, &Pl_objp->pos);
9935                 max_dist = aip->stay_near_distance;
9936                 scale = dist - max_dist/2;
9937                 if (scale < 0.0f)
9938                         scale = 0.0f;
9939
9940                 vm_vec_scale_add(&goal_pos, &goal_objp->pos, &rand_vec, scale);
9941
9942                 if (max_dist < Pl_objp->radius + goal_objp->radius + 25.0f)
9943                         max_dist = Pl_objp->radius + goal_objp->radius + 25.0f;
9944
9945                 if (dist > max_dist) {
9946                         turn_towards_point(Pl_objp, &goal_pos, NULL, 0.0f);
9947                         accelerate_ship(aip, dist / max_dist - 0.8f);
9948                 }
9949         
9950         }
9951
9952 }
9953
9954 //      Warn player if dock path is obstructed.
9955 int maybe_dock_obstructed(object *cur_objp, object *goal_objp, int big_only_flag)
9956 {
9957         vector  *goalpos, *curpos;
9958         float           radius;
9959         ai_info *aip;
9960         int             collide_objnum;
9961
9962         aip = &Ai_info[Ships[cur_objp->instance].ai_index];
9963
9964         Ai_info[Ships[goal_objp->instance].ai_index].ai_flags &= ~AIF_REPAIR_OBSTRUCTED;
9965
9966         if (goal_objp != Player_obj)
9967                 return -1;
9968
9969         curpos = &cur_objp->pos;
9970         radius = cur_objp->radius;
9971         goalpos = &Path_points[aip->path_cur].pos;
9972         collide_objnum = pp_collide_any(curpos, goalpos, radius, cur_objp, goal_objp, big_only_flag);
9973
9974         if (collide_objnum != -1)
9975                 Ai_info[Ships[goal_objp->instance].ai_index].ai_flags |= AIF_REPAIR_OBSTRUCTED;
9976
9977         return collide_objnum;
9978 }
9979
9980
9981 int Dock_path_warning_given = 0;
9982
9983 //      Docking behavior.
9984 //      Approach a ship, follow path to docking platform, approach platform, after awhile,
9985 //      undock.
9986 void ai_dock()
9987 {
9988         ship                    *shipp = &Ships[Pl_objp->instance];
9989         ai_info         *aip = &Ai_info[shipp->ai_index];
9990         object          *goal_objp;
9991         ship_info       *sip = &Ship_info[shipp->ship_info_index];
9992
9993         //      Make sure object we're supposed to dock with still exists.
9994         if ((aip->goal_objnum == -1) || (Objects[aip->goal_objnum].signature != aip->goal_signature)) {
9995                 ai_cleanup_dock_mode(aip, shipp);
9996                 return;
9997         }
9998
9999         goal_objp = &Objects[aip->goal_objnum];
10000
10001         //      For docking submodes (ie, not undocking), follow path.  Once at second last
10002         //      point on path (point just before point on dock platform), orient into position.
10003         // For undocking, first mode pushes docked ship straight back from docking point
10004         // second mode turns ship and moves to point on docking radius
10005         switch (aip->submode) {
10006
10007                 //      This mode means to find the path to the docking point.
10008         case AIS_DOCK_0:
10009                 //aip->path_start = -1;
10010                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10011                 ai_path();
10012                 if (!Dock_path_warning_given && (aip->path_length < 4)) {
10013                         Warning( LOCATION, "Ship '%s' has only %i points on dock path.  Docking will look strange.  Contact Adam.", shipp->ship_name, aip->path_length );
10014                         Dock_path_warning_given = 1;            //      This is on a mission-wide basis, but it's just a hack for now...
10015                 }
10016
10017                 aip->submode = AIS_DOCK_1;
10018                 aip->path_start = -1;
10019                 aip->submode_start_time = Missiontime;
10020                 break;
10021
10022                 //      This mode means to follow the path until just before the end.
10023         case AIS_DOCK_1: {
10024                 float   dist;
10025                 int     r;
10026
10027                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp, 1)) != -1) {
10028                         int     r1;
10029                         if ((r1 = maybe_avoid_big_ship(Pl_objp, goal_objp, aip, &goal_objp->pos, 7.0f)) != 0) {
10030                                 nprintf(("AI", "Support ship %s avoiding large ship %s\n", Ships[Pl_objp->instance].ship_name, Ships[Objects[r1].instance].ship_name));
10031                                 break;
10032                         } /*else {
10033                                 nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10034                                 accelerate_ship(aip, 0.0f);
10035                                 aip->submode = AIS_DOCK_0;
10036                         } */
10037                 } //else {
10038                 {
10039                         dist = ai_path();
10040                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10041                         //nprintf(("AI", "Dock 1: Frame: %i, goal point = %i, dist = %7.3f\n", Framecount, aip->path_cur-aip->path_start, dist));
10042
10043                         if (aip->path_cur-aip->path_start >= aip->path_length-1) {              //      If got this far, advance no matter what.
10044                                 aip->submode = AIS_DOCK_2;
10045                                 aip->submode_start_time = Missiontime;
10046                                 aip->path_cur--;
10047                                 Assert(aip->path_cur-aip->path_start >= 0);
10048                         } else if (aip->path_cur-aip->path_start >= aip->path_length-2) {
10049                                 if (Pl_objp->phys_info.speed > goal_objp->phys_info.speed + 1.5f) {
10050                                         set_accel_for_target_speed(Pl_objp, goal_objp->phys_info.speed);
10051                                 } else {
10052                                         aip->submode = AIS_DOCK_2;
10053                                         aip->submode_start_time = Missiontime;
10054                                 }
10055                         }
10056                 }
10057                 break;
10058                                           }
10059         //      This mode means to drag oneself right to the second last point on the path.
10060         //      Path code allows it to overshoot.
10061         case AIS_DOCK_2: {
10062                 float           dist;
10063                 int     r;
10064
10065                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10066                         nprintf(("AI", "Dock 2: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10067                         accelerate_ship(aip, 0.0f);
10068                         aip->submode = AIS_DOCK_1;
10069                 } else {
10070                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10071                         dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_APPROACH);
10072                         Assert(dist != UNINITIALIZED_VALUE);
10073
10074                         if (dist == DOCK_BACKUP_RETURN_VAL) {
10075                                 int path_num;
10076                                 aip->submode = AIS_DOCK_1;
10077                                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
10078                                 Assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
10079                                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
10080                                 break;
10081                         }
10082
10083                         //nprintf(("AI", "Dock 2: dist = %7.3f\n", vm_vec_dist_quick(&Pl_objp->pos, &goal_point)));
10084                         float   tolerance;
10085                         if (Objects[aip->goal_objnum].flags & OF_PLAYER_SHIP)
10086                                 tolerance = 6*flFrametime + 1.0f;
10087                         else
10088                                 tolerance = 4*flFrametime + 0.5f;
10089
10090                         if ( dist < tolerance) {
10091                                 aip->submode = AIS_DOCK_3;
10092                                 aip->submode_start_time = Missiontime;
10093                                 aip->path_cur++;
10094                         }
10095                 }
10096                 break;
10097                                                   }
10098
10099         case AIS_DOCK_3:
10100         case AIS_DOCK_3A:
10101                 {
10102                 Assert(aip->goal_objnum != -1);
10103                 int     r;
10104
10105                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10106                         nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10107                         accelerate_ship(aip, 0.0f);
10108                         aip->submode = AIS_DOCK_2;
10109                 } else {
10110
10111                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10112                         float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10113                         Assert(dist != UNINITIALIZED_VALUE);
10114
10115                         if (dist == DOCK_BACKUP_RETURN_VAL) {
10116                                 aip->submode = AIS_DOCK_2;
10117                                 break;
10118                         }
10119
10120                         //nprintf(("AI", "Dock 3: dist = %7.3f\n", dist));
10121
10122                         if (dist < 2*flFrametime * (1.0f + fl_sqrt(goal_objp->phys_info.speed))) {
10123                                 // - Removed by MK on 11/7/97, causes errors for ships docked at mission start: maybe_recreate_path(Pl_objp, aip, 1);
10124                                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10125                                 Assert(dist != UNINITIALIZED_VALUE);
10126
10127                                 physics_ship_init(Pl_objp);
10128
10129                                 ai_do_objects_docked_stuff( Pl_objp, goal_objp );
10130
10131                                 if (aip->submode == AIS_DOCK_3) {
10132                                         snd_play_3d( &Snds[SND_DOCK_ATTACH], &Pl_objp->pos, &View_position );
10133                                         hud_maybe_flash_docking_text(Pl_objp);
10134                                         // ai_dock_shake(Pl_objp, goal_objp);
10135
10136                                         if ((Pl_objp == Player_obj) || (goal_objp == Player_obj))
10137                                                 joy_ff_docked();  // shake player's joystick a little
10138                                 }
10139
10140                                 //      If this ship is repairing another ship...
10141                                 if (aip->ai_flags & AIF_REPAIRING) {
10142                                         aip->submode = AIS_DOCK_4;                      //      Special rearming only dock mode.
10143                                         aip->submode_start_time = Missiontime;
10144                                 } else {
10145                                         aip->submode = AIS_DOCK_4A;
10146                                         aip->submode_start_time = Missiontime;
10147                                 }
10148                         }
10149                 }
10150                 break;
10151                 }
10152
10153                 //      Yes, we just sit here.  We wait for further orders.  No, it's not a bug.
10154         case AIS_DOCK_4A:
10155                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10156                 //nprintf(("AI", "."));
10157                 if (aip->active_goal >= 0) {
10158                         mission_log_add_entry(LOG_SHIP_DOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10159
10160                         if (aip->goals[aip->active_goal].ai_mode == AI_GOAL_DOCK) {
10161                                 ai_mission_goal_complete( aip );                                        // Note, this calls ai_set_default_behavior().
10162                         } 
10163                 } else {        //      Can happen for initially docked ships.
10164                         ai_do_default_behavior( &Objects[Ships[aip->shipnum].objnum] );         // do the default behavior
10165                 }
10166                 
10167                 break;
10168
10169         case AIS_DOCK_4: {
10170                 //      This mode is only for rearming/repairing.
10171                 //      The ship that is performing the rearm enters this mode after it docks.
10172                 Assert((aip->goal_objnum >= -1) && (aip->goal_objnum < MAX_OBJECTS));
10173
10174                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10175                 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10176                 Assert(dist != UNINITIALIZED_VALUE);
10177
10178                 object  *goal_objp = &Objects[aip->goal_objnum];
10179                 Assert(goal_objp->type == OBJ_SHIP);
10180                 ship                    *goal_shipp = &Ships[goal_objp->instance];              
10181                 ai_info         *goal_aip = &Ai_info[goal_shipp->ai_index];
10182
10183                 //nprintf(("AI", "Dock 4: dist = %7.3f\n", dist));
10184
10185                 //      Make sure repair has not broken off.
10186                 if (dist > 5.0f) {      //      Oops, too far away!
10187                         if ( goal_aip->ai_flags & AIF_BEING_REPAIRED )
10188                                 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BROKEN);
10189
10190                         if (dist > Pl_objp->radius*2 + goal_objp->radius*2) {
10191                                 //      Got real far away from goal, so move back a couple modes and try again.
10192                                 aip->submode = AIS_DOCK_2;
10193                                 aip->submode_start_time = Missiontime;
10194                         }
10195                 } else {
10196                         if ( goal_aip->ai_flags & AIF_AWAITING_REPAIR )
10197                                 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BEGIN );
10198                 }
10199
10200                 break;
10201                                                   }
10202
10203         case AIS_UNDOCK_0: {
10204                 int path_num;
10205                 //      First stage of undocking.
10206
10207                 //nprintf(("AI", "Undock 0:\n"));
10208
10209                 aip->submode = AIS_UNDOCK_1;
10210                 aip->submode_start_time = Missiontime;
10211                 if (aip->dock_objnum == -1) {
10212                         aip->submode = AIS_UNDOCK_3;
10213                 } else {
10214
10215                         // set up the path points for the undocking procedure.  dock_path_index member should
10216                         // have gotten set in the docking code.
10217                         Assert( aip->dock_path_index != -1 );
10218                         path_num = ai_return_path_num_from_dockbay(goal_objp, aip->dock_path_index);
10219                         ai_find_path(Pl_objp, goal_objp-Objects, path_num, 0);
10220
10221                         // Play a ship docking detach sound
10222                         snd_play_3d( &Snds[SND_DOCK_DETACH], &Pl_objp->pos, &View_position );
10223                 }
10224                 break;
10225                                                          }
10226         case AIS_UNDOCK_1: {
10227                 //      Using thrusters, exit from dock station to nearest next dock path point.
10228                 float   dist;
10229                 
10230                 //nprintf(("AI", "Undock 1: time in this mode = %7.3f\n", f2fl(Missiontime - aip->submode_start_time)));
10231
10232                 if (Missiontime - aip->submode_start_time < REARM_BREAKOFF_DELAY) {
10233                         break;          //      Waiting for one second to elapse to let detach sound effect play out.
10234                 }
10235                 else {  // AL - added 05/16/97.  Hack to play depart sound.  Will probably take out.
10236                                         // Assumes that the submode_start_time is not used for AIS_UNDOCK_1 anymore
10237                         if ( aip->submode_start_time != 0 )
10238                                 snd_play_3d( &Snds[SND_DOCK_DEPART], &Pl_objp->pos, &View_position );
10239                         aip->submode_start_time = 0;
10240                 }
10241
10242                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_1);
10243                 Assert(dist != UNINITIALIZED_VALUE);
10244
10245                 float dist_to_dock_obj = vm_vec_dist_quick(&Pl_objp->pos, &Objects[aip->goal_objnum].pos);
10246
10247                 //      Move to within 0.1 units of second last point on path before orienting, or just plain far away from docked-to ship.
10248                 //      This allows undock to complete if first ship flies away.
10249                 if ((dist < 2*flFrametime) || (dist_to_dock_obj > 2*Pl_objp->radius)) {
10250                         aip->submode = AIS_UNDOCK_2;
10251                         aip->submode_start_time = Missiontime;
10252                 }
10253                 break;
10254                                                          }
10255         case AIS_UNDOCK_2: {
10256                 float dist;
10257                 ai_info *other_aip;
10258
10259                 // get pointer to docked object's aip to reset flags, etc
10260                 Assert( aip->dock_objnum != -1 );
10261                 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
10262
10263                 //      Second stage of undocking.
10264                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_2);
10265                 Assert(dist != UNINITIALIZED_VALUE);
10266
10267
10268                 //nprintf(("AI", "Undock 2: dist = %7.3f\n", dist));
10269                 
10270                 //      If at goal point, or quite far away from dock object
10271                 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) ) {
10272                         // reset the dock flags.  If rearm/repair, reset rearm repair flags for those ships as well.
10273                         if ( sip->flags & SIF_SUPPORT ) {
10274                                 ai_do_objects_repairing_stuff( &Objects[aip->dock_objnum], Pl_objp, REPAIR_INFO_END );
10275                         }
10276
10277                         // clear out flags for AIF_DOCKED for both objects.
10278                         ai_do_objects_undocked_stuff( Pl_objp, goal_objp );
10279                         physics_ship_init(Pl_objp);
10280                         aip->submode = AIS_UNDOCK_3;                            //      The do-nothing mode, until another order is issued
10281
10282                         //aip->ai_flags &= ~AIF_DOCKED;         //      @MK, 9/18/97
10283                         //other_aip->ai_flags &= ~AIF_DOCKED;
10284                         //aip->dock_objnum = -1;                                        // invalidate who obj is docked with
10285                         //other_aip->dock_objnum = -1;                  // MWA 10/07/97 invalide docked objects dock_objnum value as well
10286
10287                         // don't add undock log entries for support ships.
10288                         if ( !(sip->flags & SIF_SUPPORT) )
10289                                 mission_log_add_entry(LOG_SHIP_UNDOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10290
10291                 }
10292                 break;
10293                 }
10294         case AIS_UNDOCK_3: {
10295                 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_3);
10296                 Assert(dist != UNINITIALIZED_VALUE);
10297
10298                 if (dist < Pl_objp->radius/2 + 5.0f) {
10299                         aip->submode = AIS_UNDOCK_4;
10300                 }
10301
10302                 // possible that this flag hasn't been cleared yet.  When aborting a rearm, this submode might
10303                 // be entered directly.
10304                 if ( (sip->flags & SIF_SUPPORT) && (aip->ai_flags & AIF_REPAIRING) ) {
10305                         ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], Pl_objp, REPAIR_INFO_ABORT );
10306                 }
10307
10308                 break;
10309                                                  }
10310         case AIS_UNDOCK_4: {
10311                 ai_info *other_aip;
10312
10313                 // MWA 10/07/97  I'm slightly confused by the dual use of goal_objnum and dock_objnum.  Seems to me
10314                 // that goal_objnum and dock_objnum are the same through this whole docking/undocking process, although
10315                 // I could be wrong.  dock_objnum was reset in undock_2 submode so try to use goal_objnum here to
10316                 // get other ships ai_info pointer
10317                 Assert( aip->goal_objnum != -1 );
10318                 other_aip = &Ai_info[Ships[Objects[aip->goal_objnum].instance].ai_index];
10319
10320                 aip->mode = AIM_NONE;
10321                 aip->dock_path_index = -1;              // invalidate the docking path index
10322
10323                 // these flags should have been cleared long ago!
10324                 // Get Allender if you hit one of these!!!!!
10325                 // removed by allender on 2/16 since a ship may be docked with some other ship, but still be the
10326                 // goal_objnum of this ship ending it's undocking mode.
10327                 //Assert( !(aip->ai_flags & AIF_DOCKED) );
10328                 //Assert( !(other_aip->ai_flags & AIF_DOCKED) );
10329                 //Assert( !(aip->ai_flags & AIF_REPAIRING) );
10330                 //Assert( !(other_aip->ai_flags & AIF_BEING_REPAIRED) );
10331                 //Assert( !(other_aip->ai_flags & AIF_AWAITING_REPAIR) );
10332
10333                 // only call mission goal complete if this was indeed an undock goal
10334                 if ( aip->active_goal > -1 ) {
10335                         if ( aip->goals[aip->active_goal].ai_mode == AI_GOAL_UNDOCK )
10336                                 ai_mission_goal_complete( aip );                        // this call should reset the AI mode
10337                         //else
10338                         //      aip->active_goal = -1;                                          // this ensures that this ship might get new goal
10339                 }
10340
10341                 break;
10342                                                          }
10343         default:
10344                 Int3(); //      Error, bogus submode
10345         }
10346
10347 }
10348
10349 // TURRET BEGIN
10350
10351 //      Given an object and a turret on that object, return the global position and forward vector
10352 //      of the turret.   The gun normal is the unrotated gun normal, (the center of the FOV cone), not
10353 // the actual gun normal given using the current turret heading.  But it _is_ rotated into the model's orientation
10354 //      in global space.
10355 void ship_get_global_turret_info(object *objp, model_subsystem *tp, vector *gpos, vector *gvec)
10356 {
10357         matrix  m;
10358         vm_copy_transpose_matrix(&m, &objp->orient);
10359 //      vm_vec_rotate(gpos, &tp->turret_avg_firing_point, &m);
10360         vm_vec_rotate(gpos, &tp->pnt, &m);
10361         vm_vec_add2(gpos, &objp->pos);
10362         vm_vec_rotate(gvec, &tp->turret_norm, &m);      
10363 }
10364
10365 // Given an object and a turret on that object, return the actual firing point of the gun
10366 // and its normal.   This uses the current turret angles.  We are keeping track of which
10367 // gun to fire next in the ship specific info for this turret subobject.  Use this info
10368 // to determine which position to fire from next.
10369 //      Stuffs:
10370 //              *gpos: absolute position of gun firing point
10371 //              *gvec: vector fro *gpos to *targetp
10372 void ship_get_global_turret_gun_info(object *objp, ship_subsys *ssp, vector *gpos, vector *gvec, int use_angles, vector *targetp)
10373 {
10374         vector * gun_pos;
10375         model_subsystem *tp = ssp->system_info;
10376
10377         ship_model_start(objp);
10378
10379         gun_pos = &tp->turret_firing_point[ssp->turret_next_fire_pos % tp->turret_num_firing_points];
10380
10381         model_find_world_point(gpos, gun_pos, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10382
10383         if (use_angles)
10384                 model_find_world_dir(gvec, &tp->turret_norm, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10385         else {
10386                 //vector        gun_pos2;
10387                 //vm_vec_add(&gun_pos2, gpos, gun_pos);
10388                 vm_vec_normalized_dir(gvec, targetp, gpos);
10389         }
10390
10391         ship_model_stop(objp);  
10392 }
10393
10394 //      Rotate a turret towards an enemy.
10395 //      Return TRUE if caller should use angles in subsequent rotations.
10396 //      Some obscure model thing only John Slagel knows about.
10397 //      Sets predicted enemy position.
10398 //      If the turret (*ss) has a subsystem targeted, the subsystem is used as the predicted point.
10399 int aifft_rotate_turret(ship *shipp, int parent_objnum, ship_subsys *ss, object *objp, object *lep, vector *predicted_enemy_pos, vector *gvec)
10400 {
10401         if (ss->turret_enemy_objnum != -1)      {
10402                 model_subsystem *tp = ss->system_info;
10403                 vector  gun_pos, gun_vec;
10404                 float           weapon_speed;
10405                 float           weapon_system_strength;
10406
10407                 //      weapon_system_strength scales time enemy in range in 0..1.  So, the lower this is, the worse the aiming will be.
10408                 weapon_system_strength = ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS);
10409
10410                 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gun_pos, &gun_vec);
10411
10412                 weapon_speed = Weapon_info[tp->turret_weapon_type].max_speed;
10413                 float weapon_travel_dist = weapon_speed * Weapon_info[tp->turret_weapon_type].lifetime;
10414
10415                 vector  enemy_point;
10416                 if (ss->targeted_subsys != NULL) {
10417                         if (ss->turret_enemy_objnum != -1) {
10418                                 vm_vec_unrotate(&enemy_point, &ss->targeted_subsys->system_info->pnt, &Objects[ss->turret_enemy_objnum].orient);
10419                                 vm_vec_add2(&enemy_point, &Objects[ss->turret_enemy_objnum].pos);
10420                         }
10421                 } else {
10422                         if ((lep->type == OBJ_SHIP) && (Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
10423                                 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));
10424                         } else {
10425                                 enemy_point = lep->pos;
10426                         }
10427                 }
10428
10429                 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);
10430
10431                 if (weapon_system_strength < 0.7f) {
10432                         vector  rand_vec;
10433
10434                         static_randvec(Missiontime >> 18, &rand_vec);   //      Return same random number for two seconds.
10435                         //      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.
10436                         vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, (1.0f - weapon_system_strength)*1.5f * lep->radius);
10437                 }
10438
10439                 vector  v2e;
10440                 vm_vec_normalized_dir(&v2e, predicted_enemy_pos, &gun_pos);
10441                 if (vm_vec_dot(&v2e, gvec) > tp->turret_fov) {
10442                         int     rval;
10443
10444                         rval = model_rotate_gun(shipp->modelnum, ss->system_info, &Objects[parent_objnum].orient, 
10445                                 &ss->submodel_info_1.angs, &ss->submodel_info_2.angs,
10446                                 &Objects[parent_objnum].pos, predicted_enemy_pos);
10447                 }
10448         }
10449
10450         return 0;
10451 }
10452
10453 //      Determine if subsystem *enemy_subsysp is hittable from objp.
10454 //      If so, return dot product of vector from point abs_gunposp to *enemy_subsysp
10455 float   aifft_compute_turret_dot(object *objp, object *enemy_objp, vector *abs_gunposp, ship_subsys *turret_subsysp, ship_subsys *enemy_subsysp)
10456 {
10457         float   dot_out;
10458         vector  subobj_pos, vector_out;
10459
10460         vm_vec_unrotate(&subobj_pos, &enemy_subsysp->system_info->pnt, &enemy_objp->orient);
10461         vm_vec_add2(&subobj_pos, &enemy_objp->pos);
10462
10463         if (ship_subsystem_in_sight(enemy_objp, enemy_subsysp, abs_gunposp, &subobj_pos, 1, &dot_out, &vector_out)) {
10464                 vector  turret_norm;
10465
10466                 vm_vec_rotate(&turret_norm, &turret_subsysp->system_info->turret_norm, &objp->orient);
10467                 return vm_vec_dot(&turret_norm, &vector_out);
10468         } else
10469                 return -1.0f;
10470
10471 }
10472
10473 #define MAX_AIFFT_TURRETS                       60
10474 ship_subsys *aifft_list[MAX_AIFFT_TURRETS];
10475 float aifft_rank[MAX_AIFFT_TURRETS];
10476 int aifft_list_size = 0;
10477 int aifft_max_checks = 5;
10478 DCF(mf, "")
10479 {
10480         dc_get_arg(ARG_INT);
10481         aifft_max_checks = Dc_arg_int;
10482 }
10483
10484
10485 //      Pick a subsystem to attack on enemy_objp.
10486 //      Only pick one if enemy_objp is a big ship or a capital ship.
10487 //      Returns dot product from turret to subsystem in *dot_out
10488 ship_subsys *aifft_find_turret_subsys(object *objp, ship_subsys *ssp, object *enemy_objp, float *dot_out)
10489 {
10490         ship    *eshipp, *shipp;
10491         ship_info       *esip;
10492         ship_subsys     *best_subsysp = NULL;
10493         float dot;
10494
10495         Assert(enemy_objp->type == OBJ_SHIP);
10496
10497         eshipp = &Ships[enemy_objp->instance];
10498         esip = &Ship_info[eshipp->ship_info_index];
10499
10500         shipp = &Ships[objp->instance];
10501
10502         float   best_dot = 0.0f;
10503         *dot_out = best_dot;
10504
10505         //      Compute absolute gun position.
10506         vector  abs_gun_pos;
10507         vm_vec_unrotate(&abs_gun_pos, &ssp->system_info->pnt, &objp->orient);
10508         vm_vec_add2(&abs_gun_pos, &objp->pos);
10509
10510         //      Only pick a turret to attack on large ships.
10511         if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
10512                 return best_subsysp;
10513
10514         // Make sure big or huge ship *actually* has subsystems  (ie, knossos)
10515         if (esip->n_subsystems == 0) {
10516                 return best_subsysp;
10517         }
10518
10519         // first build up a list subsystems to traverse
10520         ship_subsys     *pss;
10521         aifft_list_size = 0;
10522         for ( pss = GET_FIRST(&eshipp->subsys_list); pss !=END_OF_LIST(&eshipp->subsys_list); pss = GET_NEXT(pss) ) {
10523                 model_subsystem *psub = pss->system_info;
10524
10525                 // if we've reached max turrets bail
10526                 if(aifft_list_size >= MAX_AIFFT_TURRETS){
10527                         break;
10528                 }
10529
10530                 // Don't process destroyed objects
10531                 if ( pss->current_hits <= 0.0f ){
10532                         continue;
10533                 }
10534                 
10535                 switch (psub->type) {
10536                 case SUBSYSTEM_WEAPONS:
10537                         aifft_list[aifft_list_size] = pss;
10538                         aifft_rank[aifft_list_size++] = 1.4f;
10539                         break;
10540
10541                 case SUBSYSTEM_TURRET:
10542                         aifft_list[aifft_list_size] = pss;
10543                         aifft_rank[aifft_list_size++] = 1.2f;
10544                         break;
10545
10546                 case SUBSYSTEM_SENSORS:
10547                 case SUBSYSTEM_ENGINE:
10548                         aifft_list[aifft_list_size] = pss;
10549                         aifft_rank[aifft_list_size++] = 1.0f;
10550                         break;
10551                 }
10552         }
10553
10554         // DKA:  6/28/99 all subsystems can be destroyed.
10555         //Assert(aifft_list_size > 0);
10556         if (aifft_list_size == 0) {
10557                 return best_subsysp;
10558         }
10559
10560         // determine a stride value so we're not checking too many turrets
10561         int stride = aifft_list_size > aifft_max_checks ? aifft_list_size / aifft_max_checks : 1;
10562         if(stride <= 0){
10563                 stride = 1;
10564         }
10565         int offset = (int)frand_range(0.0f, (float)(aifft_list_size % stride));
10566         int idx;
10567         for(idx=offset; idx<aifft_list_size; idx+=stride){
10568                 dot = aifft_compute_turret_dot(objp, enemy_objp, &abs_gun_pos, ssp, aifft_list[idx]);                   
10569
10570                 if (dot* aifft_rank[idx] > best_dot) {
10571                         best_dot = dot*aifft_rank[idx];
10572                         best_subsysp = aifft_list[idx];
10573                 }
10574         }
10575
10576         Assert(best_subsysp != &eshipp->subsys_list);
10577
10578         *dot_out = best_dot;
10579         return best_subsysp;
10580 }
10581
10582 // Set active weapon for turret
10583 void ai_turret_select_default_weapon(ship_subsys *turret)
10584 {
10585         ship_weapon *twp;
10586
10587         twp = &turret->weapons;
10588
10589         // If a primary weapon is available, select it
10590         if ( twp->num_primary_banks > 0 ) {
10591                 turret->system_info->turret_weapon_type = twp->primary_bank_weapons[0];
10592         } else if ( twp->num_secondary_banks > 0 ) {
10593                 turret->system_info->turret_weapon_type = twp->secondary_bank_weapons[0];
10594         }
10595 }
10596
10597 // return !0 if the specified target should scan for a new target, otherwise return 0
10598 int turret_should_pick_new_target(ship_subsys *turret)
10599 {
10600 //      int target_type;
10601
10602         if ( timestamp_elapsed(turret->turret_next_enemy_check_stamp) ) {
10603                 return 1;
10604         }
10605
10606         return 0;
10607
10608 /*
10609         if ( turret->turret_enemy_objnum == -1 ) {
10610                 return 1;
10611         }
10612                 
10613         target_type = Objects[turret->turret_enemy_objnum].type;
10614         if ( (target_type != OBJ_SHIP) && (target_type != OBJ_ASTEROID) ) {
10615                 return 1;
10616         }
10617
10618         return 0;
10619 */
10620 }
10621
10622 // Set the next fire timestamp for a turret, based on weapon type and ai class
10623 void turret_set_next_fire_timestamp(ship_subsys *turret, ai_info *aip)
10624 {
10625         float   wait;
10626         int     weapon_id;
10627
10628         weapon_id = turret->system_info->turret_weapon_type;
10629
10630         wait = Weapon_info[weapon_id].fire_wait * 1000.0f;
10631
10632         // make side even for team vs. team
10633         if ((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) {
10634                 // flak guns need to fire more rapidly
10635                 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10636                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10637                         wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10638                 } else {
10639                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10640                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10641                 }
10642         } else {
10643                 // flak guns need to fire more rapidly
10644                 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10645                         if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10646                                 wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10647                         } else {
10648                                 wait *= Ship_fire_delay_scale_hostile[Game_skill_level] * 0.5f;
10649                         }       
10650                         wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10651
10652                 } else if (Weapon_info[weapon_id].wi_flags & WIF_HUGE) {
10653                         // make huge weapons fire independently of team
10654                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10655                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10656                 } else {
10657                         // give team friendly an advantage
10658                         if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10659                                 wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10660                         } else {
10661                                 wait *= Ship_fire_delay_scale_hostile[Game_skill_level];
10662                         }       
10663                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10664                 }
10665         }
10666
10667         // vary wait time +/- 10%
10668         wait *= frand_range(0.9f, 1.1f);
10669         turret->turret_next_fire_stamp = timestamp((int) wait);
10670 }
10671
10672 // Decide  if a turret should launch an aspect seeking missile
10673 int turret_should_fire_aspect(ship_subsys *turret, float dot, int weapon_class)
10674 {
10675         weapon_info *wip;
10676
10677         wip = &Weapon_info[weapon_class];
10678
10679         if ( (dot > AICODE_TURRET_DUMBFIRE_ANGLE) && (turret->turret_time_enemy_in_range >= min(wip->min_lock_time,AICODE_TURRET_MAX_TIME_IN_RANGE)) ) {
10680                 return 1;
10681         }
10682
10683         return 0;
10684 }
10685
10686 // Update how long current target has been in this turrets range
10687 void turret_update_enemy_in_range(ship_subsys *turret, float seconds)
10688 {
10689         turret->turret_time_enemy_in_range += seconds;
10690
10691         if ( turret->turret_time_enemy_in_range < 0.0f ) {
10692                 turret->turret_time_enemy_in_range = 0.0f;
10693         }
10694
10695         if ( turret->turret_time_enemy_in_range > AICODE_TURRET_MAX_TIME_IN_RANGE ) {
10696                 turret->turret_time_enemy_in_range = AICODE_TURRET_MAX_TIME_IN_RANGE;
10697         }
10698 }
10699
10700
10701
10702 // Fire a weapon from a turret
10703 void turret_fire_weapon(ship_subsys *turret, int parent_objnum, vector *turret_pos, vector *turret_fvec, vector *predicted_pos = NULL)
10704 {
10705         matrix  turret_orient;
10706         int             turret_weapon_class, weapon_objnum;
10707         ai_info *parent_aip;
10708         ship            *parent_ship;
10709         beam_fire_info fire_info;
10710         float flak_range = 0.0f;
10711
10712         parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
10713         parent_ship = &Ships[Objects[parent_objnum].instance];
10714         turret_weapon_class = turret->system_info->turret_weapon_type;
10715
10716         if (check_ok_to_fire(parent_objnum, turret->turret_enemy_objnum, &Weapon_info[turret_weapon_class])) {
10717                 vm_vector_2_matrix(&turret_orient, turret_fvec, NULL, NULL);
10718                 turret->turret_last_fire_direction = *turret_fvec;
10719
10720                 // set next fire timestamp for the turret
10721                 turret_set_next_fire_timestamp(turret, parent_aip);
10722
10723                 // if this weapon is a beam weapon, handle it specially
10724                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM){
10725                         // if this beam isn't free to fire
10726                         if (!(turret->weapons.flags & SW_FLAG_BEAM_FREE)) {
10727                                 Int3(); // should never get this far
10728                                 return;
10729                         }
10730
10731                         // stuff beam firing info
10732                         memset(&fire_info, 0, sizeof(beam_fire_info));
10733                         fire_info.accuracy = 1.0f;
10734                         fire_info.beam_info_index = turret_weapon_class;
10735                         fire_info.beam_info_override = NULL;
10736                         fire_info.shooter = &Objects[parent_objnum];
10737                         fire_info.target = &Objects[turret->turret_enemy_objnum];
10738                         fire_info.target_subsys = NULL;
10739                         fire_info.turret = turret;
10740
10741                         // fire a beam weapon
10742                         beam_fire(&fire_info);
10743                 } else {
10744
10745                         // don't fire swarm, but set up swarm info
10746                         if (Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM) {
10747                                 turret_swarm_set_up_info(parent_objnum, turret, turret_weapon_class);
10748                                 return;
10749                         } else {
10750                                 weapon_objnum = weapon_create( turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10751                                 weapon_set_tracking_info(weapon_objnum, parent_objnum, turret->turret_enemy_objnum, 1, turret->targeted_subsys);                
10752                         }
10753
10754                         //nprintf(("AI", "Turret_time_enemy_in_range = %7.3f\n", ss->turret_time_enemy_in_range));              
10755                         if (weapon_objnum != -1) {
10756                                 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10757                                 // AL 1-6-97: Store pointer to turret subsystem
10758                                 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10759
10760                                 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10761                                         // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10762                                         if ( parent_objnum != OBJ_INDEX(Player_obj) ) {                                         
10763                                                 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], turret_pos, &View_position );                                          
10764                                         }
10765                                 }               
10766
10767                                 // if the gun is a flak gun
10768                                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){                       
10769                                         // show a muzzle flash
10770                                         flak_muzzle_flash(turret_pos, turret_fvec, turret_weapon_class);
10771
10772                                         // pick a firing range so that it detonates properly                    
10773                                         flak_pick_range(&Objects[weapon_objnum], predicted_pos, ship_get_subsystem_strength(parent_ship, SUBSYSTEM_WEAPONS));
10774
10775                                         // determine what that range was
10776                                         flak_range = flak_get_range(&Objects[weapon_objnum]);
10777                                 }
10778
10779                                 // in multiplayer (and the master), then send a turret fired packet.
10780                                 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10781                                         int subsys_index;
10782
10783                                         subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10784                                         Assert( subsys_index != -1 );
10785                                         if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){                       
10786                                                 send_flak_fired_packet( parent_objnum, subsys_index, weapon_objnum, flak_range );
10787                                         } else {
10788                                                 send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10789                                         }
10790                                 }
10791                         }
10792                 }
10793         } else {
10794                 float wait = 1000.0f * frand_range(0.9f, 1.1f);
10795                 turret->turret_next_fire_stamp = timestamp((int) wait);
10796         }
10797 }
10798
10799 void turret_swarm_fire_from_turret(ship_subsys *turret, int parent_objnum, int target_objnum, ship_subsys *target_subsys)
10800 {
10801         int turret_weapon_class, weapon_objnum;
10802         matrix turret_orient;
10803         vector turret_pos, turret_fvec;
10804
10805         // parent not alive, quick out.
10806         if (Objects[parent_objnum].type != OBJ_SHIP) {
10807                 return;
10808         }
10809
10810         //      change firing point
10811         ship_get_global_turret_gun_info(&Objects[parent_objnum], turret, &turret_pos, &turret_fvec, 1, NULL);
10812         turret->turret_next_fire_pos++;
10813
10814         // get class [index into Weapon_info array
10815         turret_weapon_class = turret->system_info->turret_weapon_type;
10816         Assert(Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM);
10817
10818         // make turret_orient from turret_fvec -- turret->turret_last_fire_direction
10819         vm_vector_2_matrix(&turret_orient, &turret_fvec, NULL, NULL);
10820
10821         // create weapon and homing info
10822         weapon_objnum = weapon_create(&turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10823         weapon_set_tracking_info(weapon_objnum, parent_objnum, target_objnum, 1, target_subsys);
10824
10825         // do other cool stuff if weapon is created.
10826         if (weapon_objnum > -1) {
10827                 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10828                 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10829
10830                 // maybe sound
10831                 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10832                         // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10833                         if ( parent_objnum != OBJ_INDEX(Player_obj) ) {
10834                                 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], &turret_pos, &View_position );
10835                         }
10836                 }
10837                 
10838                 // in multiplayer (and the master), then send a turret fired packet.
10839                 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10840                         int subsys_index;
10841
10842                         subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10843                         Assert( subsys_index != -1 );
10844                         send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10845                 }
10846         }
10847 }
10848
10849 int Num_ai_firing = 0;
10850 int Num_find_turret_enemy = 0;
10851 int Num_turrets_fired = 0;
10852 //      Given a turret tp and its parent parent_objnum, fire from the turret at its enemy.
10853 void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
10854 {
10855         float           weapon_firing_range;
10856         vector  v2e;
10857         object  *lep;           //      Last enemy pointer
10858         model_subsystem *tp = ss->system_info;
10859         int             use_angles, turret_weapon_class;
10860         vector  predicted_enemy_pos;
10861         object  *objp;
10862         ai_info *aip;
10863
10864         if (!Ai_firing_enabled) {
10865                 return;
10866         }
10867
10868         if (ss->current_hits < 0.0f) {
10869                 return;
10870         }
10871
10872         if ( ship_subsys_disrupted(ss) ){               // AL 1/19/98: Make sure turret isn't suffering disruption effects
10873                 return;
10874         }
10875
10876         // Check turret free
10877         if (ss->weapons.flags & SW_FLAG_TURRET_LOCK) {
10878                 return;
10879         }
10880
10881         // If beam weapon, check beam free
10882         if ( (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) && !(ss->weapons.flags & SW_FLAG_BEAM_FREE) ) {
10883                 return;
10884         }
10885
10886         Assert( shipp->objnum == parent_objnum );
10887
10888         if ( tp->turret_weapon_type < 0 ){
10889                 return;
10890         }
10891
10892         // Monitor number of calls to ai_fire_from_turret
10893         Num_ai_firing++;
10894
10895         turret_weapon_class = tp->turret_weapon_type;
10896
10897         // AL 09/14/97: ensure ss->turret_enemy_objnum != -1 before setting lep
10898         if ( (ss->turret_enemy_objnum >= 0 && ss->turret_enemy_objnum < MAX_OBJECTS) && (ss->turret_enemy_sig == Objects[ss->turret_enemy_objnum].signature)) {
10899                 lep = &Objects[ss->turret_enemy_objnum];
10900
10901                 // MK -- here is where turret is targeting a bomb.  I simply return for now.  We should force
10902                 // a target change -- or better yet, never pick a weapon when this turret has a "huge" weapon
10903                 // loaded.
10904
10905                 // we only care about targets which are ships.
10906                 //if ( lep->type != OBJ_SHIP )
10907                 //      return;
10908
10909                 //      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.
10910                 if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HUGE ) {
10911                         if ( lep->type != OBJ_SHIP ) {
10912                                 return;
10913                         }
10914                         if ( !(Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
10915                                 return;
10916                         }
10917                 }
10918
10919                 // If targeting protected or beam protected ship, don't fire.  Reset enemy objnum
10920                 if (lep->type == OBJ_SHIP) {
10921                         // Check if we're targeting a protected ship
10922                         if (lep->flags & OF_PROTECTED) {
10923                                 ss->turret_enemy_objnum = -1;
10924                                 ss->turret_time_enemy_in_range = 0.0f;
10925                                 return;
10926                         }
10927
10928                         // Check if we're targeting a beam protected ship with a beam weapon
10929                         if ( (lep->flags & OF_BEAM_PROTECTED) && (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) ) {
10930                                 ss->turret_enemy_objnum = -1;
10931                                 ss->turret_time_enemy_in_range = 0.0f;
10932                                 return;
10933                         }
10934                 }
10935         } else {
10936                 ss->turret_enemy_objnum = -1;
10937                 lep = NULL;
10938         }
10939         
10940         Assert((parent_objnum >= 0) && (parent_objnum < MAX_OBJECTS));
10941         objp = &Objects[parent_objnum];
10942         Assert(objp->type == OBJ_SHIP);
10943         aip = &Ai_info[Ships[objp->instance].ai_index];
10944
10945         // Use the turret info for all guns, not one gun in particular.
10946         vector   gvec, gpos;
10947         ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
10948
10949         // Rotate the turret even if time hasn't elapsed, since it needs to turn to face its target.
10950         use_angles = aifft_rotate_turret(shipp, parent_objnum, ss, objp, lep, &predicted_enemy_pos, &gvec);
10951
10952         if ( !timestamp_elapsed(ss->turret_next_fire_stamp)){
10953                 return;
10954         }
10955
10956         // Don't try to fire beyond weapon_limit_range
10957         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);
10958
10959         // if beam weapon in nebula and target not tagged, decrase firing range
10960         extern int Nebula_sec_range;
10961         if (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) {
10962                 if ( !((shipp->tag_left > 0) || (shipp->level2_tag_left > 0)) ) {
10963                         if (Nebula_sec_range) {
10964                                 weapon_firing_range *= float(BEAM_NEBULA_RANGE_REDUCE_FACTOR);
10965                         }
10966                 }
10967         }
10968
10969         if (ss->turret_enemy_objnum != -1) {
10970                 float dist_to_enemy = vm_vec_normalized_dir(&v2e, &predicted_enemy_pos, &gpos) - lep->radius;
10971                 if (dist_to_enemy > weapon_firing_range) {
10972                         ss->turret_enemy_objnum = -1;           //      Force picking of new enemy.
10973                 }
10974         }
10975
10976         // Turret spawn weapons are a special case.  They fire if there are enough enemies in the 
10977         // immediate area (not necessarily in the turret fov).
10978         if ( Weapon_info[turret_weapon_class].wi_flags & WIF_SPAWN ) {
10979                 int num_ships_nearby;
10980                 num_ships_nearby = num_nearby_fighters(get_enemy_team_mask(parent_objnum), &gpos, 1500.0f);
10981                 if (( num_ships_nearby >= 3 ) || ((num_ships_nearby >= 2) && (frand() < 0.1f))) {
10982                         turret_fire_weapon(ss, parent_objnum, &gpos, &ss->turret_last_fire_direction);
10983                 } else {
10984                         ss->turret_next_fire_stamp = timestamp(1000);   //      Regardless of firing rate, don't check whether should fire for awhile.
10985                 }
10986                 return;
10987         }
10988
10989         //      Maybe pick a new enemy.
10990         if ( turret_should_pick_new_target(ss) ) {
10991                 Num_find_turret_enemy++;
10992                 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);
10993                 Assert(objnum < 0 || is_target_beam_valid(ss, objnum));
10994
10995                 if (objnum != -1) {
10996                         if (ss->turret_enemy_objnum == -1) {
10997                                 ss->turret_enemy_objnum = objnum;
10998                                 ss->turret_enemy_sig = Objects[objnum].signature;
10999                                 // why return?
11000                                 return;
11001                         } else {
11002                                 ss->turret_enemy_objnum = objnum;
11003                                 ss->turret_enemy_sig = Objects[objnum].signature;
11004                         }
11005                 } else {
11006                         ss->turret_enemy_objnum = -1;
11007                 }
11008
11009                 if (ss->turret_enemy_objnum != -1) {
11010                         float   dot = 1.0f;
11011                         lep = &Objects[ss->turret_enemy_objnum];
11012                         if ( lep->type == OBJ_SHIP ) {
11013                                 ss->targeted_subsys = aifft_find_turret_subsys(objp, ss, lep, &dot);                            
11014                         }
11015                         ss->turret_next_enemy_check_stamp = timestamp((int) (max(dot, 0.5f)*2000.0f) + 1000);
11016                 } else {
11017                         ss->turret_next_enemy_check_stamp = timestamp((int) (2000.0f * frand_range(0.9f, 1.1f)));       //      Check every two seconds
11018                 }
11019         }
11020
11021         //      If still don't have an enemy, return.  Or, if enemy is protected, return.
11022         if (ss->turret_enemy_objnum != -1) {
11023                 //      Don't shoot at ship we're going to dock with.
11024                 if (ss->turret_enemy_objnum == aip->dock_objnum) {
11025                         ss->turret_enemy_objnum = -1;
11026                         return;
11027                 }
11028
11029                 if (Objects[ss->turret_enemy_objnum].flags & OF_PROTECTED) {
11030                         //      This can happen if the enemy was selected before it became protected.
11031                         ss->turret_enemy_objnum = -1;
11032                         return;
11033                 }
11034                 lep = &Objects[ss->turret_enemy_objnum];
11035         } else {
11036                 if (timestamp_until(ss->turret_next_fire_stamp) < 500) {
11037                         ss->turret_next_fire_stamp = timestamp(500);
11038                 }
11039                 return;
11040         }
11041
11042         if ( lep == NULL ){
11043                 return;
11044         }
11045
11046         Assert(ss->turret_enemy_objnum != -1);
11047
11048         float dot = vm_vec_dot(&v2e, &gvec);
11049
11050         if (dot > tp->turret_fov ) {
11051                 // Ok, the turret is lined up... now line up a particular gun.
11052                 int ok_to_fire = 0;
11053                 float dist_to_enemy;
11054
11055                 // We're ready to fire... now get down to specifics, like where is the
11056                 // actual gun point and normal, not just the one for whole turret.
11057                 ship_get_global_turret_gun_info(&Objects[parent_objnum], ss, &gpos, &gvec, use_angles, &predicted_enemy_pos);
11058                 ss->turret_next_fire_pos++;
11059
11060                 // Fire in the direction the turret is facing, not right at the target regardless of turret dir.
11061                 vm_vec_sub(&v2e, &predicted_enemy_pos, &gpos);
11062                 dist_to_enemy = vm_vec_normalize(&v2e);
11063                 dot = vm_vec_dot(&v2e, &gvec);
11064
11065                 // 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
11066                 // and make them less lethal
11067                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){
11068                         flak_jitter_aim(&v2e, dist_to_enemy, ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS));
11069                 }
11070
11071                 // Fire if:
11072                 //              dumbfire and nearly pointing at target.
11073                 //              heat seeking and target in a fairly wide cone.
11074                 //              aspect seeking and target is locked.
11075                 turret_weapon_class = tp->turret_weapon_type;
11076
11077                 // if dumbfire (lasers and non-homing missiles)
11078                 if ( !(Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING) ) {
11079                         if ((dist_to_enemy < 75.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11080                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11081                                 ok_to_fire = 1;
11082                         }
11083                 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_HEAT ) {     // if heat seekers
11084                         if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_HEATSEEK_ANGLE )) {
11085                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11086                                 ok_to_fire = 1;
11087                         }
11088                 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_ASPECT ) {   // if aspect seeker
11089                         if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11090                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11091                         }
11092                         if ( turret_should_fire_aspect(ss, dot, turret_weapon_class) ) {
11093                                 ok_to_fire = 1;
11094                         }
11095                 }
11096
11097                 if ( ok_to_fire ) {
11098                         Num_turrets_fired++;
11099                         
11100                         turret_fire_weapon(ss, parent_objnum, &gpos, &v2e, &predicted_enemy_pos);                                               
11101                 } else {
11102                         turret_update_enemy_in_range(ss, -4*Weapon_info[tp->turret_weapon_type].fire_wait);
11103                         ss->turret_next_fire_stamp = timestamp(500);
11104                 }
11105         } else {
11106                 // Lost him!
11107                 ss->turret_enemy_objnum = -1;           //      Reset enemy objnum, find a new one next frame.
11108                 ss->turret_time_enemy_in_range = 0.0f;
11109         }
11110 }
11111
11112 // TURRET END
11113
11114 #ifndef NDEBUG
11115 #define MAX_AI_DEBUG_RENDER_STUFF       100
11116 typedef struct ai_render_stuff {
11117         ship_subsys     *ss;
11118         int                     parent_objnum;
11119 } ai_render_stuff;
11120
11121 ai_render_stuff AI_debug_render_stuff[MAX_AI_DEBUG_RENDER_STUFF];
11122
11123 int     Num_AI_debug_render_stuff = 0;
11124
11125 void ai_debug_render_stuff()
11126 {
11127         vertex  vert1, vert2;
11128         vector  gpos2;
11129         int             i;
11130
11131         for (i=0; i<Num_AI_debug_render_stuff; i++) {
11132                 ship_subsys     *ss;
11133                 int     parent_objnum;
11134                 vector  gpos, gvec;
11135                 model_subsystem *tp;
11136
11137                 ss = AI_debug_render_stuff[i].ss;
11138                 tp = ss->system_info;
11139
11140                 parent_objnum = AI_debug_render_stuff[i].parent_objnum;
11141
11142                 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
11143                 g3_rotate_vertex(&vert1, &gpos);
11144                 vm_vec_scale_add(&gpos2, &gpos, &gvec, 20.0f);
11145                 g3_rotate_vertex(&vert2, &gpos2);
11146                 gr_set_color(0, 0, 255);
11147                 g3_draw_sphere(&vert1, 2.0f);
11148                 gr_set_color(255, 0, 255);
11149                 g3_draw_sphere(&vert2, 2.0f);
11150                 g3_draw_line(&vert1, &vert2);
11151         }
11152
11153         // draw from beta to its goal point
11154 /*      for (i=0; i<6; i++) {
11155                 ai_info *aip = &Ai_info[i];
11156                 gr_set_color(0, 0, 255);
11157                 g3_rotate_vertex(&vert1, &Objects[i].pos);
11158                 g3_rotate_vertex(&vert2, &aip->goal_point);
11159                 g3_draw_line(&vert1, &vert2);
11160         } */
11161         
11162
11163         Num_AI_debug_render_stuff = 0;
11164 }
11165
11166 #endif
11167
11168 #ifndef NDEBUG
11169 int     Msg_count_4996 = 0;
11170 #endif
11171
11172 //      --------------------------------------------------------------------------
11173 // Process subobjects of object objnum.
11174 //      Deal with engines disabled.
11175 void process_subobjects(int objnum)
11176 {
11177         model_subsystem *psub;
11178         ship_subsys     *pss;
11179         object  *objp = &Objects[objnum];
11180         ship            *shipp = &Ships[objp->instance];
11181         ai_info *aip = &Ai_info[shipp->ai_index];
11182         ship_info       *sip = &Ship_info[shipp->ship_info_index];
11183
11184         for ( pss = GET_FIRST(&shipp->subsys_list); pss !=END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
11185                 psub = pss->system_info;
11186
11187                 // Don't process destroyed objects
11188                 if ( pss->current_hits <= 0.0f ) 
11189                         continue;
11190
11191                 switch (psub->type) {
11192                 case SUBSYSTEM_TURRET:
11193                         if ( psub->turret_num_firing_points > 0 )       {
11194                                 ai_fire_from_turret(shipp, pss, objnum);
11195                         } else {
11196 #ifndef NDEBUG
11197                                 if (!Msg_count_4996) {
11198                                         Warning( LOCATION, "Ship '%s' has turrets with no guns!\nProbably a model problem, so get an artist!", shipp->ship_name );
11199                                         Msg_count_4996++;
11200                                 }
11201 #endif
11202                                 }
11203                         break;
11204
11205                 case SUBSYSTEM_ENGINE:
11206                 case SUBSYSTEM_NAVIGATION:
11207                 case SUBSYSTEM_COMMUNICATION:
11208                 case SUBSYSTEM_WEAPONS:
11209                 case SUBSYSTEM_SENSORS:
11210                 case SUBSYSTEM_UNKNOWN:
11211                         break;
11212
11213                 // next set of subsystems may rotation
11214                 case SUBSYSTEM_RADAR:
11215                 case SUBSYSTEM_SOLAR:
11216                 case SUBSYSTEM_GAS_COLLECT:
11217                 case SUBSYSTEM_ACTIVATION:
11218                         break;
11219                 default:
11220                         Error(LOCATION, "Illegal subsystem type.\n");
11221                 }
11222
11223                 // do solar/radar/gas/activator rotation here
11224                 if ( psub->flags & MSS_FLAG_ROTATES )   {
11225                         if (psub->flags & MSS_FLAG_STEPPED_ROTATE       ) {
11226                                 submodel_stepped_rotate(psub, &pss->submodel_info_1);
11227                         } else {
11228                                 submodel_rotate(psub, &pss->submodel_info_1 );
11229                         }
11230                 }
11231
11232         }
11233
11234         //      Deal with a ship with blown out engines.
11235         if (ship_get_subsystem_strength(shipp, SUBSYSTEM_ENGINE) == 0.0f) {
11236                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
11237                         // AL: Only attack forever if not trying to depart to a docking bay.  Need to have this in, since
11238                         //     a ship may get repaired... and it should still try to depart.  Since docking bay departures
11239                         //     are not handled as goals, we don't want to leave the AIM_BAY_DEPART mode.
11240                         if ( aip->mode != AIM_BAY_DEPART ) {
11241                                 ai_attack_object(objp, NULL, 99, NULL);         //      Regardless of current mode, enter attack mode.
11242                                 aip->submode = SM_ATTACK_FOREVER;                               //      Never leave attack submode, don't avoid, evade, etc.
11243                         }
11244                 }
11245         }
11246
11247
11248 }
11249
11250 //      Given an object and the wing it's in, return its index in the wing list.
11251 //      This defines its location in the wing formation.
11252 //      If the object can't be found in the wing, return -1.
11253 //      *objp           object of interest
11254 //      wingnum the wing *objp is in
11255 int get_wing_index(object *objp, int wingnum)
11256 {
11257         wing    *wingp;
11258         int     i;
11259
11260         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11261
11262         wingp = &Wings[wingnum];
11263
11264         for (i=wingp->current_count-1; i>=0; i--)
11265                 if ( objp->instance == wingp->ship_index[i] )
11266                         break;
11267
11268         return i;               //      Note, returns -1 if string not found.
11269 }
11270
11271 //      Given a wing, return a pointer to the object of its leader.
11272 //      Asserts if object not found.
11273 //      Currently, the wing leader is defined as the first object in the wing.
11274 //      wingnum         Wing number in Wings array.
11275 //      If wing leader is disabled, swap it with another ship.
11276 object * get_wing_leader(int wingnum)
11277 {
11278         wing            *wingp;
11279         int             ship_num;
11280
11281         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11282
11283         wingp = &Wings[wingnum];
11284
11285         Assert(wingp->current_count != 0);                      //      Make sure there is a leader
11286
11287         ship_num = wingp->ship_index[0];
11288
11289         //      If this ship is disabled, try another ship in the wing.
11290         int n = 0;
11291         while (ship_get_subsystem_strength(&Ships[ship_num], SUBSYSTEM_ENGINE) == 0.0f) {
11292                 n++;
11293                 if (n >= wingp->current_count)
11294                         break;  
11295                 ship_num = wingp->ship_index[n];
11296         }
11297
11298         if (( n != 0) && (n != wingp->current_count)) {
11299                 int t = wingp->ship_index[0];
11300                 wingp->ship_index[0] = wingp->ship_index[n];
11301                 wingp->ship_index[n] = t;
11302         }
11303
11304         return &Objects[Ships[ship_num].objnum];
11305 }
11306
11307 #define DEFAULT_WING_X_DELTA            1.0f
11308 #define DEFAULT_WING_Y_DELTA            0.25f
11309 #define DEFAULT_WING_Z_DELTA            0.75f
11310 #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))
11311 // next constant is higher that MAX_SHIPS_IN_WINGS to deal with forming on player's wing
11312 #define MAX_FORMATION_ROWS              4
11313
11314 //      Given a position in a wing, return the desired location of the ship relative to the leader
11315 //      *_delta_vec             OUTPUT.  delta vector based on wing_index
11316 //      wing_index              position in wing.
11317 void get_wing_delta(vector *_delta_vec, int wing_index)
11318 {
11319         int     wi0;
11320
11321         Assert(wing_index >= 0);
11322
11323         int     k, row, column;
11324
11325         int bank = wing_index / (MAX_FORMATION_ROWS*(MAX_FORMATION_ROWS+1)/2);
11326         wi0 = wing_index % (MAX_FORMATION_ROWS * (MAX_FORMATION_ROWS+1)/2);
11327
11328         k = 0;
11329         for (row=1; row<MAX_FORMATION_ROWS+1; row++) {
11330                 k += row;
11331                 if (wi0 < k)
11332                         break;
11333         }
11334
11335         row--;
11336         column = wi0 - k + row + 1;
11337
11338         _delta_vec->xyz.x = ((float) column - (float) row/2.0f) * DEFAULT_WING_X_DELTA/DEFAULT_WING_MAG;
11339         _delta_vec->xyz.y = ((float)row + (float)bank*2.25f) * DEFAULT_WING_Y_DELTA/DEFAULT_WING_MAG;
11340         _delta_vec->xyz.z = - ((float)row + 0.5f * (float) bank) * DEFAULT_WING_Z_DELTA/DEFAULT_WING_MAG;
11341 }
11342
11343 //      Compute the largest radius of a ship in a *objp's wing.
11344 float gwlr_1(object *objp, ai_info *aip)
11345 {
11346         int             wingnum = aip->wing;
11347         float           max_radius;
11348         object  *o;
11349         ship_obj        *so;
11350
11351         Assert(wingnum >= 0);
11352
11353         max_radius = objp->radius;
11354
11355         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11356                 o = &Objects[so->objnum];
11357                 if (Ai_info[Ships[o->instance].ai_index].wing == wingnum)
11358                         if (o->radius > max_radius)
11359                                 max_radius = o->radius;
11360         }
11361
11362         return max_radius;
11363 }
11364
11365 //      Compute the largest radius of a ship forming on *objp's wing.
11366 float gwlr_object_1(object *objp, ai_info *aip)
11367 {
11368         float           max_radius;
11369         object  *o;
11370         ship_obj        *so;
11371
11372         max_radius = objp->radius;
11373
11374         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11375                 o = &Objects[so->objnum];
11376                 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == OBJ_INDEX(objp))
11377                         if (o->radius > max_radius)
11378                                 max_radius = o->radius;
11379         }
11380
11381         return max_radius;
11382 }
11383
11384 //      For the wing that *objp is part of, return the largest ship radius in that wing.
11385 float get_wing_largest_radius(object *objp, int formation_object_flag)
11386 {
11387         ship            *shipp;
11388         ai_info *aip;
11389
11390         Assert(objp->type == OBJ_SHIP);
11391         Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
11392         shipp = &Ships[objp->instance];
11393         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11394         aip = &Ai_info[shipp->ai_index];
11395
11396         if (formation_object_flag) {
11397                 return gwlr_object_1(objp, aip);
11398         } else {
11399                 return gwlr_1(objp, aip);
11400         }
11401
11402 }
11403
11404 float Wing_y_scale = 2.0f;
11405 float Wing_scale = 1.0f;
11406 DCF(wing_y_scale, "")
11407 {
11408         dc_get_arg(ARG_FLOAT);
11409         Wing_y_scale = Dc_arg_float;
11410 }
11411
11412 DCF(wing_scale, "")
11413 {
11414         dc_get_arg(ARG_FLOAT);
11415         Wing_scale = Dc_arg_float;
11416 }
11417
11418 // Given a wing leader and a position in the wing formation, return the desired absolute location to fly to.
11419 //      Returns result in *result_pos.
11420 void get_absolute_wing_pos(vector *result_pos, object *leader_objp, int wing_index, int formation_object_flag)
11421 {
11422         vector  wing_delta, rotated_wing_delta;
11423         float           wing_spread_size;
11424
11425         get_wing_delta(&wing_delta, wing_index);                //      Desired location in leader's reference frame
11426
11427         wing_spread_size = max(50.0f, 3.0f * get_wing_largest_radius(leader_objp, formation_object_flag) + 15.0f);
11428
11429         // for player obj (1) move ships up 20% (2) scale formation up 20%
11430         if (leader_objp->flags & OF_PLAYER_SHIP) {
11431                 wing_delta.xyz.y *= Wing_y_scale;
11432                 wing_spread_size *= Wing_scale;
11433         }
11434
11435         vm_vec_scale(&wing_delta, wing_spread_size * (1.0f + leader_objp->phys_info.speed/70.0f));
11436
11437         vm_vec_unrotate(&rotated_wing_delta, &wing_delta, &leader_objp->orient);        //      Rotate into leader's reference.
11438
11439         vm_vec_add(result_pos, &leader_objp->pos, &rotated_wing_delta); //      goal_point is absolute 3-space point.
11440 }
11441
11442 #ifndef NDEBUG
11443 int Debug_render_wing_phantoms;
11444
11445 void render_wing_phantoms(object *objp)
11446 {
11447         int             i;
11448         ship            *shipp;
11449         ai_info *aip;
11450         int             wingnum;
11451         int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11452         vector  goal_point;
11453         
11454         Assert(objp->type == OBJ_SHIP);
11455         Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11456
11457         shipp = &Ships[objp->instance];
11458         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11459
11460         aip = &Ai_info[shipp->ai_index];
11461
11462         wingnum = aip->wing;
11463
11464         if (wingnum == -1)
11465                 return;
11466
11467         wing_index = get_wing_index(objp, wingnum);
11468
11469         //      If this ship is NOT the leader, abort.
11470         if (wing_index != 0)
11471                 return;
11472
11473         for (i=0; i<32; i++)
11474                 if (Debug_render_wing_phantoms & (1 << i)) {
11475                         get_absolute_wing_pos(&goal_point, objp, i, 0);
11476         
11477                         vertex  vert;
11478                         gr_set_color(255, 0, 128);
11479                         g3_rotate_vertex(&vert, &goal_point);
11480                         g3_draw_sphere(&vert, 2.0f);
11481                 }
11482
11483         Debug_render_wing_phantoms = 0;
11484
11485 }
11486
11487 void render_wing_phantoms_all()
11488 {
11489         object  *objp;
11490         ship_obj        *so;
11491
11492         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11493                 ship            *shipp;
11494                 ai_info *aip;
11495                 int             wingnum;
11496                 int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11497
11498                 objp = &Objects[so->objnum];
11499                 
11500                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11501                 shipp = &Ships[objp->instance];
11502                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11503
11504                 aip = &Ai_info[shipp->ai_index];
11505
11506                 wingnum = aip->wing;
11507
11508                 if (wingnum == -1)
11509                         continue;
11510
11511                 wing_index = get_wing_index(objp, wingnum);
11512
11513                 //      If this ship is NOT the leader, abort.
11514                 if (wing_index != 0)
11515                         continue;
11516                 
11517                 render_wing_phantoms(objp);
11518
11519                 return;
11520         }
11521 }
11522
11523 #endif
11524
11525 //      Hook from goals code to AI.
11526 //      Force a wing to fly in formation.
11527 //      Sets AIF_FORMATION bit in ai_flags.
11528 //      wingnum         Wing to force to fly in formation
11529 void ai_fly_in_formation(int wingnum)
11530 {
11531         object  *objp;
11532         ship            *shipp;
11533         ship_obj        *so;
11534
11535         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11536                 objp = &Objects[so->objnum];
11537                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11538
11539                 shipp = &Ships[objp->instance];
11540                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11541
11542                 if (Ai_info[shipp->ai_index].wing == wingnum) {
11543                         Ai_info[shipp->ai_index].ai_flags |= AIF_FORMATION_WING;
11544                         Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_OBJECT;
11545                 }
11546         }
11547 }
11548
11549 //      Hook from goals code to AI.
11550 //      Force a wing to abandon formation flying.
11551 //      Clears AIF_FORMATION bit in ai_flags.
11552 //      wingnum         Wing to force to fly in formation
11553 void ai_disband_formation(int wingnum)
11554 {
11555         object  *objp;
11556         ship            *shipp;
11557         ship_obj        *so;
11558
11559         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11560                 objp = &Objects[so->objnum];
11561                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11562
11563                 shipp = &Ships[objp->instance];
11564                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11565
11566                 if (Ai_info[shipp->ai_index].wing == wingnum) {
11567                         Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_WING;
11568                 }
11569         }
11570 }
11571
11572 float   Leader_chaos = 0.0f;
11573 int Chaos_frame = -1;
11574
11575 //      Return true if objp is flying in an erratic manner
11576 //      Only true if objp is a player
11577 int formation_is_leader_chaotic(object *objp)
11578 {
11579         if (Game_mode & GM_MULTIPLAYER)
11580                 return 0;
11581
11582         if (objp != Player_obj)
11583                 return 0;
11584
11585         if (Framecount != Chaos_frame) {
11586                 float   speed_scale;
11587                 float   fdot, udot;
11588
11589                 speed_scale = 3.0f + objp->phys_info.speed * 0.1f;
11590
11591                 fdot = 5.0f * (1.0f - vm_vec_dot(&objp->orient.v.fvec, &objp->last_orient.v.fvec)) * flFrametime;
11592                 udot = 8.0f * (1.0f - vm_vec_dot(&objp->orient.v.uvec, &objp->last_orient.v.uvec)) * flFrametime;
11593
11594                 Leader_chaos += fdot * speed_scale + udot * speed_scale;
11595
11596                 Leader_chaos *= (1.0f - flFrametime*0.2f);
11597
11598                 if (Leader_chaos < 0.0f)
11599                         Leader_chaos = 0.0f;
11600                 else if (Leader_chaos > 1.7f)
11601                         Leader_chaos = 1.7f;
11602
11603                 //nprintf(("AI", "Frame %i: chaos = %7.4f\n", Framecount, Leader_chaos));
11604
11605                 Chaos_frame = Framecount;
11606         }
11607
11608         return (Leader_chaos > 1.0f);
11609 }
11610
11611 // Fly in formation.
11612 //      Make Pl_objp assume its proper place in formation.
11613 //      If the leader of the wing is doing something stupid, like fighting a battle,
11614 //      then the poor sap wingmates will be in for a "world of hurt"
11615 //      Return TRUE if we need to process this object's normal mode
11616 int ai_formation()
11617 {
11618         object  *leader_objp;
11619         ship            *shipp;
11620         ai_info *aip, *laip;
11621         int             wingnum;
11622         int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11623         int             player_wing;    // index of the players wingnum
11624         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;
11625         float           dot_to_goal, dist_to_goal, leader_speed;
11626
11627         Assert(Pl_objp->type == OBJ_SHIP);
11628         Assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_SHIPS));
11629
11630         shipp = &Ships[Pl_objp->instance];
11631
11632         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11633
11634         aip = &Ai_info[shipp->ai_index];
11635
11636         Assert((aip->ai_flags & AIF_FORMATION) != AIF_FORMATION);       //      Make sure not both types of formation flying in effect.
11637
11638         //      Determine which kind of formation flying.
11639         //      If tracking an object, not in waypoint mode:
11640         if (aip->ai_flags & AIF_FORMATION_OBJECT) {
11641                 if ((aip->goal_objnum < 0) || (aip->goal_objnum >= MAX_OBJECTS)) {
11642                         aip->ai_flags &= ~AIF_FORMATION_OBJECT;
11643                         return 1;
11644                 }
11645                 
11646                 wing_index = ai_formation_object_get_slotnum(aip->goal_objnum, Pl_objp);
11647                 leader_objp = &Objects[aip->goal_objnum];
11648         } else {        //      Formation flying in waypoint mode.
11649                 Assert(aip->ai_flags & AIF_FORMATION_WING);
11650                 if (aip->mode != AIM_WAYPOINTS) {
11651                         aip->ai_flags &= ~AIF_FORMATION_WING;
11652                         return 1;
11653                 }
11654
11655                 wingnum = aip->wing;
11656
11657                 if (wingnum == -1)
11658                         return 1;
11659
11660                 // disable formation flying for any ship in the players wing
11661                 player_wing = Ships[Player_obj->instance].wingnum;
11662                 if ( (player_wing != -1) && (wingnum == player_wing) )
11663                         return 1;
11664
11665                 wing_index = get_wing_index(Pl_objp, wingnum);
11666
11667                 leader_objp = get_wing_leader(wingnum);
11668
11669         }
11670
11671         //      If docked with a ship in this wing, only the more massive one actually flies in formation.
11672         if (aip->dock_objnum != -1) {
11673                 object  *other_objp = &Objects[aip->dock_objnum];
11674                 ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
11675
11676                 if (aip->wing == other_aip->wing) {
11677                         if (Pl_objp->phys_info.mass < other_objp->phys_info.mass)
11678                                 return 0;
11679                         else if (Pl_objp->phys_info.mass == other_objp->phys_info.mass) {
11680                                 if (Pl_objp->signature < other_objp->signature)
11681                                         return 0;
11682                         }
11683                 }
11684         }
11685
11686         Assert(leader_objp != NULL);
11687         laip = &Ai_info[Ships[leader_objp->instance].ai_index];
11688
11689         //      Make sure we're really in this wing.
11690         if (wing_index == -1)
11691                 return 1;
11692
11693         //      If this ship is the leader, abort, as he doesn't have to follow anyone.
11694         if (wing_index == 0) {
11695                 // nprintf(("AI", "Hmm, wing leader %s in ai_formation for no good reason.\n", shipp->ship_name));
11696                 return 1;
11697         }
11698
11699         if (aip->mode == AIM_WAYPOINTS) {
11700                 aip->wp_list = laip->wp_list;
11701                 if (laip->wp_index < Waypoint_lists[laip->wp_list].count)
11702                         aip->wp_index = laip->wp_index;
11703                 else
11704                         aip->wp_index = Waypoint_lists[laip->wp_list].count - 1;
11705                 aip->wp_flags = laip->wp_flags;
11706                 aip->wp_dir = laip->wp_dir;
11707         }
11708
11709         #ifndef NDEBUG
11710         Debug_render_wing_phantoms |= (1 << wing_index);
11711         #endif
11712
11713         leader_speed = leader_objp->phys_info.speed;
11714         vector leader_vec = leader_objp->phys_info.vel;
11715
11716         get_absolute_wing_pos(&goal_point, leader_objp, wing_index, aip->ai_flags & AIF_FORMATION_OBJECT);
11717         vm_vec_scale_add(&future_goal_point_5, &goal_point, &leader_vec, 10.0f);
11718         vm_vec_scale_add(&future_goal_point_2, &goal_point, &leader_vec, 5.0f);
11719         vm_vec_scale_add(&future_goal_point_x, &goal_point, &leader_objp->orient.v.fvec, 10.0f);        //      used when very close to destination
11720         vm_vec_scale_add(&future_goal_point_1000x, &goal_point, &leader_objp->orient.v.fvec, 1000.0f);  //      used when very close to destination
11721
11722         //      Now, get information telling this object how to turn and accelerate to get to its
11723         //      desired location.
11724         vm_vec_sub(&vec_to_goal, &goal_point, &Pl_objp->pos);
11725         if ( vm_vec_mag_quick(&vec_to_goal) < AICODE_SMALL_MAGNITUDE )
11726                 vec_to_goal.xyz.x += 0.1f;
11727
11728         vm_vec_copy_normalize(&dir_to_goal, &vec_to_goal);
11729         //dot_to_goal = vm_vec_dot(&dir_to_goal, &leader_objp->orient.v.fvec);
11730         dot_to_goal = vm_vec_dot(&dir_to_goal, &Pl_objp->orient.v.fvec);
11731         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &goal_point);
11732         float   dist_to_goal_2 = vm_vec_dist_quick(&Pl_objp->pos, &future_goal_point_2);
11733
11734         // 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));
11735
11736         int     chaotic_leader = 0;
11737
11738         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.
11739
11740         if (dist_to_goal > 500.0f) {
11741                 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11742                 accelerate_ship(aip, 1.0f);
11743         } else if (dist_to_goal > 200.0f) {
11744                 if (dot_to_goal > -0.5f) {
11745                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11746                         float range_speed = shipp->current_max_speed - leader_speed;
11747                         if (range_speed > 0.0f)
11748                                 set_accel_for_target_speed(Pl_objp, leader_speed + range_speed * (dist_to_goal+100.0f)/500.0f);
11749                         else
11750                                 set_accel_for_target_speed(Pl_objp, shipp->current_max_speed);
11751                 } else {
11752                         turn_towards_point(Pl_objp, &future_goal_point_5, NULL, 0.0f);
11753                         if (leader_speed > 10.0f)
11754                                 set_accel_for_target_speed(Pl_objp, leader_speed *(1.0f + dot_to_goal));
11755                         else
11756                                 set_accel_for_target_speed(Pl_objp, 10.0f);
11757                 }
11758         } else {
11759                 vector  v2f2;
11760                 float   dot_to_f2;
11761                 float   dist_to_f2;
11762
11763                 dist_to_f2 = vm_vec_normalized_dir(&v2f2, &future_goal_point_2, &Pl_objp->pos);
11764                 dot_to_f2 = vm_vec_dot(&v2f2, &Pl_objp->orient.v.fvec);
11765
11766                 //      Leader flying like a maniac.  Don't try hard to form on wing.
11767                 if (chaotic_leader) {
11768                         turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11769                         set_accel_for_target_speed(Pl_objp, min(leader_speed*0.8f, 20.0f));
11770                 } else if (dist_to_goal > 75.0f) {
11771                         turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11772                         float   delta_speed;
11773                         float range_speed = shipp->current_max_speed - leader_speed;
11774                         if (range_speed > 0.0f)
11775                                 delta_speed = dist_to_goal_2/500.0f * range_speed;
11776                         else
11777                                 delta_speed = shipp->current_max_speed - leader_speed;
11778                         if (dot_to_goal < 0.0f) {
11779                                 delta_speed = -delta_speed;
11780                                 if (-delta_speed > leader_speed/2)
11781                                         delta_speed = -leader_speed/2;
11782                         }
11783
11784                         if (leader_speed < 5.0f)
11785                                 if (delta_speed < 5.0f)
11786                                         delta_speed = 5.0f;
11787
11788                         float scale = dot_to_f2;
11789                         if (scale < 0.1f)
11790                                 scale = 0.0f;
11791                         else
11792                                 scale *= scale;
11793
11794                         set_accel_for_target_speed(Pl_objp, scale * (leader_speed + delta_speed));
11795                 } else {
11796                         //nprintf(("AI", "Dot = %7.3f\n", dot_to_goal));
11797
11798                         if (leader_speed < 5.0f) {
11799                                 //      Leader very slow.  If not close to goal point, get very close.  Note, keep trying to get close unless
11800                                 //      moving very slowly, else momentum can carry far away from goal.
11801
11802                                 if ((dist_to_goal > 10.0f) || ((Pl_objp->phys_info.speed > leader_speed + 2.5f) && (dot_to_goal > 0.5f))) {
11803                                         //nprintf(("MK", "(1) "));
11804                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11805                                         set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/10.0f);
11806                                 } else {
11807                                         if (Pl_objp->phys_info.speed < 0.5f) {
11808                                                 //nprintf(("MK", "(2) "));
11809                                                 turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11810                                         } else {
11811                                                 //nprintf(("MK", "(3) "));
11812                                         }
11813                                         set_accel_for_target_speed(Pl_objp, leader_speed);
11814                                 }
11815                                 //nprintf(("MK", "dist: %7.3f, dot: %6.3f, speed: %7.3f\n", dist_to_goal, dot_to_goal, Pl_objp->phys_info.speed));
11816                         } else if (dist_to_goal > 10.0f) {
11817                                 float   dv;
11818
11819                                 //future_goal_point_2;
11820
11821                                 turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11822
11823                                 if (dist_to_goal > 25.0f) {
11824                                         if (dot_to_goal < 0.3f)
11825                                                 dv = -0.1f;
11826                                         else
11827                                                 dv = dot_to_goal - 0.2f;
11828
11829                                         set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/5.0f * dv);
11830                                 } else {
11831                                         set_accel_for_target_speed(Pl_objp, leader_speed + 1.5f * dot_to_goal - 1.0f);
11832                                 }
11833                         } else {
11834                                 if (Pl_objp->phys_info.speed < 0.1f)
11835                                         turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11836                                 else
11837                                         turn_towards_point(Pl_objp, &future_goal_point_x, NULL, 0.0f);
11838                                 set_accel_for_target_speed(Pl_objp, 0.0f);
11839                         }
11840                 }
11841
11842         }
11843
11844         //      See how different this ship's bank is relative to wing leader
11845         float   up_dot = vm_vec_dot(&leader_objp->orient.v.uvec, &Pl_objp->orient.v.uvec);
11846         if (up_dot < 0.996f) {
11847                 vector  w_out;
11848                 matrix  new_orient;
11849                 vector  angular_accel;
11850
11851                 vm_vec_copy_scale(&angular_accel, &Pl_objp->phys_info.max_rotvel, 0.2f);
11852                 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);
11853
11854         //      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)));
11855                 Pl_objp->orient = new_orient;
11856                 Pl_objp->phys_info.rotvel = w_out;
11857         //      Pl_objp->phys_info.desired_rotvel = w_out;
11858         } else {
11859                 Pl_objp->phys_info.rotvel.xyz.z = 0.0f;
11860         }
11861
11862         return 0;
11863 }
11864
11865 //      Return index of object repairing object objnum.
11866 int find_repairing_objnum(int objnum)
11867 {
11868         object          *objp;
11869         ship                    *shipp;
11870         ship_info       *sip;
11871         ship_obj                *so;
11872
11873         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11874                 objp = &Objects[so->objnum];
11875
11876                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11877
11878                 shipp = &Ships[objp->instance];
11879                 sip = &Ship_info[shipp->ship_info_index];
11880
11881                 if (sip->flags & SIF_SUPPORT) {
11882                         ai_info *aip;
11883
11884                         aip = &Ai_info[shipp->ai_index];
11885
11886                         if (aip->goal_objnum == objnum) {
11887                                 return objp-Objects;
11888                         }
11889                 }
11890         }
11891
11892         return -1;
11893 }
11894
11895 //      If object *objp is being repaired, deal with it!
11896 void ai_do_repair_frame(object *objp, ai_info *aip, float frametime)
11897 {
11898         if (Ships[objp->instance].team == TEAM_TRAITOR) {
11899                 ai_abort_rearm_request(objp);
11900                 return;
11901         }
11902
11903         if (aip->ai_flags & (AIF_BEING_REPAIRED | AIF_AWAITING_REPAIR)) {
11904                 int     dock_objnum;
11905                 ai_info *repair_aip;
11906
11907                 dock_objnum = aip->dock_objnum; // find_repairing_objnum(objp-Objects);
11908                 //Assert(dock_objnum != -1);
11909                 if (dock_objnum == -1)
11910                         return;
11911                 if (Objects[dock_objnum].signature != aip->dock_signature) {
11912                         Int3();         //      Curious -- object numbers match, but signatures do not.
11913                                                         //      Must mean original repair ship died and was replaced by current ship.
11914                         return;
11915                 }
11916         
11917                 repair_aip = &Ai_info[Ships[Objects[dock_objnum].instance].ai_index];
11918                 //Assert(repair_aip->mode == AIM_DOCK);
11919
11920                 if (aip->ai_flags & AIF_BEING_REPAIRED) {
11921                         // Assert(repair_aip->submode == AIS_DOCK_4);
11922
11923                         //      Wait awhile into the mode to synchronize with sound effect.
11924                         if (Missiontime - repair_aip->submode_start_time > REARM_SOUND_DELAY) {
11925                                 int repaired;
11926
11927                                 repaired = ship_do_rearm_frame( objp, frametime );              // hook to do missile rearming
11928
11929                                 //      See if fully repaired.  If so, cause process to stop.
11930                                 if ( repaired && (repair_aip->submode == AIS_DOCK_4)) {
11931
11932                                         repair_aip->submode = AIS_UNDOCK_0;
11933                                         repair_aip->submode_start_time = Missiontime;
11934
11935                                         // if repairing player object -- tell him done with repair
11936                                         if ( !MULTIPLAYER_CLIENT ){
11937                                                 ai_do_objects_repairing_stuff( objp, &Objects[dock_objnum], REPAIR_INFO_COMPLETE );
11938                                         }
11939                                 }
11940                         }
11941                 } else if (aip->ai_flags & AIF_AWAITING_REPAIR) {
11942                         //      If this ship has been awaiting repair for 90+ seconds, abort.
11943                         if ( !MULTIPLAYER_CLIENT ) {
11944                                 if ((Game_mode & GM_MULTIPLAYER) || (objp != Player_obj)) {
11945                                         if ((repair_aip->goal_objnum == OBJ_INDEX(objp)) && (timestamp_elapsed(aip->abort_rearm_timestamp))) {
11946                                                 ai_abort_rearm_request(objp);
11947                                                 aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);
11948                                         }
11949                                 }
11950                         }
11951                 }
11952         } else {
11953                 // AL 11-24-97: If this is the player ship, ensure the repair sound isn't playing.  We need to
11954                 //              do this check, since this is a looping sound, and may continue on if rearm/repair
11955                 //              finishes abnormally once sound begins looping.
11956                 if ( objp == Player_obj ) {
11957                         player_stop_repair_sound();
11958                 }
11959         }
11960 }
11961
11962 //      Shell around dock_orient_and_approach to detect whether dock process should be aborted.
11963 //      obj1 is the ship performing the repair.
11964 //      obj2 is the ship being repaired.
11965 void call_doa(object *obj1, object *obj2, ship_info *sip1)
11966 {
11967         if (sip1->flags & SIF_SUPPORT) {
11968                 if (obj2->phys_info.speed > MAX_REPAIR_SPEED) {
11969
11970                         // call the ai_abort rearm request code
11971                         ai_abort_rearm_request( obj2 );
11972                 } else
11973                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11974         } else {
11975                 if (Ship_info[Ships[obj1->instance].ship_info_index].flags & SIF_CARGO)
11976                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11977                 else if (Ship_info[Ships[obj2->instance].ship_info_index].flags & SIF_CARGO)
11978                         dock_orient_and_approach(obj2, obj1, DOA_DOCK_STAY);
11979                 else {
11980                         //mprintf(("Warning: Not sure, but making %s [%s] move to stay docked with %s [%s]\n",
11981                         //      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));
11982                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11983
11984                 }
11985         }
11986
11987 }
11988
11989 //      Maybe launch a countermeasure.
11990 //      Also, detect a supposed homing missile that no longer exists.
11991 void ai_maybe_launch_cmeasure(object *objp, ai_info *aip)
11992 {
11993         float                   dist;
11994         ship_info       *sip;
11995         ship                    *shipp;
11996
11997         shipp = &Ships[objp->instance];
11998         sip = &Ship_info[shipp->ship_info_index];
11999
12000         if (!(sip->flags & (SIF_SMALL_SHIP | SIF_TRANSPORT)))
12001                 return;
12002
12003         if (!shipp->cmeasure_count)
12004                 return;
12005
12006         if ( !timestamp_elapsed(shipp->cmeasure_fire_stamp) )
12007                 return;
12008
12009         //      If not on player's team and Skill_level + ai_class is low, never fire a countermeasure.  The ship is too dumb.
12010         if (shipp->team != Player_ship->team) {
12011                 if (Game_skill_level + aip->ai_class < 4){
12012                         return;
12013                 }
12014         }
12015
12016         if ((aip->nearest_locked_object != -1) && (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)) {
12017                 object  *weapon_objp;
12018                 weapon  *weaponp;
12019                 weapon_info     *wip;
12020
12021                 weapon_objp = &Objects[aip->nearest_locked_object];
12022                 weaponp = &Weapons[weapon_objp->instance];
12023                 wip = &Weapon_info[weaponp->weapon_info_index];
12024
12025                 if ((dist = vm_vec_dist_quick(&objp->pos, &weapon_objp->pos)) < weapon_objp->phys_info.speed*2.0f) {
12026         
12027                         aip->nearest_locked_distance = dist;
12028                         //      Verify that this object is really homing on us.
12029                         object  *weapon_objp;
12030
12031                         weapon_objp = &Objects[aip->nearest_locked_object];
12032
12033                         float   fire_chance;
12034
12035                         //      For ships on player's team, have constant, average chance to fire.
12036                         //      For enemies, increasing chance with higher skill level.
12037                         if (shipp->team == Player_ship->team)
12038                                 fire_chance = Cmeasure_fire_chance[NUM_SKILL_LEVELS/2];
12039                         else
12040                                 fire_chance = Cmeasure_fire_chance[Game_skill_level];
12041
12042                         //      Decrease chance to fire at lower ai class.
12043                         fire_chance *= (float) aip->ai_class/Num_ai_classes;
12044
12045                         float r = frand();
12046                         if (fire_chance < r) {
12047                                 //nprintf(("AI", "Not firing countermeasure due to skill level: %7.3f < %7.3f\n", fire_chance, r));
12048                                 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.
12049                                 return;
12050                         }
12051
12052                         if (weapon_objp->type == OBJ_WEAPON) {
12053                                 if (weapon_objp->instance >= 0) {
12054                                         //nprintf(("AI", "Firing countermeasure at time t=%7.3f\n", f2fl(Missiontime)));
12055                                         ship_launch_countermeasure(objp);
12056                                         shipp->cmeasure_fire_stamp = timestamp(2*CMEASURE_WAIT);
12057                                         return;
12058                                 }
12059                         }
12060         
12061                 }
12062         }
12063
12064         return;
12065 }
12066
12067 //      --------------------------------------------------------------------------
12068 void ai_preprocess_ignore_objnum(object *objp, ai_info *aip)
12069 {
12070 //      if (aip->ignore_objnum == UNUSED_OBJNUM)
12071 //              return;
12072
12073         if (aip->ai_flags & AIF_TEMPORARY_IGNORE) {
12074                 if (timestamp_elapsed(aip->ignore_expire_timestamp)) {
12075                         aip->ignore_objnum = UNUSED_OBJNUM;
12076                 }
12077         }
12078
12079         if (is_ignore_object(aip, aip->goal_objnum)) {
12080                 aip->goal_objnum = -1;
12081                 // AL 12-11-97: If in STRAFE mode, we need to ensure that target_objnum is also
12082                 //              set to -1
12083                 if ( aip->mode == AIM_STRAFE ) {
12084                         aip->target_objnum = -1;
12085                 }
12086         }
12087
12088         if (is_ignore_object(aip, aip->target_objnum))
12089                 aip->target_objnum = -1;
12090 }
12091
12092 /*
12093 void ai_safety_circle_spot()
12094 {
12095         vector  goal_point;
12096         ship_info       *sip;
12097
12098         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12099
12100         goal_point = Ai_info[Ships[Pl_objp->instance].ai_index].goal_point;
12101         turn_towards_tangent(Pl_objp, &goal_point, 50.0f);
12102
12103         set_accel_for_target_speed(Pl_objp, sip->max_speed/4.0f);
12104
12105 //      float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
12106 //      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));
12107
12108 }
12109 */
12110
12111 #define CHASE_CIRCLE_DIST               100.0f
12112
12113 void ai_chase_circle(object *objp)
12114 {
12115         float           dist_to_goal;
12116         float           target_speed;
12117         vector  goal_point;
12118         ship_info       *sip;
12119         ai_info         *aip;
12120
12121         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12122
12123         target_speed = sip->max_speed/4.0f;
12124         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12125
12126         Assert(vm_vec_mag(&aip->goal_point) >= 0.0f);           //      Supposedly detects bogus vector
12127
12128         goal_point = aip->goal_point;
12129
12130         if (aip->ignore_objnum == UNUSED_OBJNUM) {
12131                 dist_to_goal = vm_vec_dist_quick(&aip->goal_point, &objp->pos);
12132
12133                 if (dist_to_goal > 2*CHASE_CIRCLE_DIST) {
12134                         vector  vec_to_goal;
12135                         //      Too far from circle goal, create a new goal point.
12136                         vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
12137                         vm_vec_scale_add(&aip->goal_point, &objp->pos, &vec_to_goal, CHASE_CIRCLE_DIST);
12138                 }
12139
12140                 goal_point = aip->goal_point;
12141         } else if (is_ignore_object(aip, aip->ignore_objnum)) {
12142                 object  *ignore_objp = &Objects[aip->ignore_objnum];
12143
12144                 vector  tvec1;
12145                 float           dist;
12146
12147                 dist = vm_vec_normalized_dir(&tvec1, &Pl_objp->pos, &ignore_objp->pos);
12148
12149                 if (dist < ignore_objp->radius*2 + 1500.0f) {
12150                         vm_vec_scale_add(&goal_point, &Pl_objp->pos, &tvec1, ignore_objp->radius*2 + 1400.0f);
12151                         if (dist < ignore_objp->radius*2 + 1300.0f)
12152                                 target_speed = sip->max_speed * (1.25f - dist/(ignore_objp->radius*2 + 1500.0f));
12153                 }
12154         }
12155
12156         Assert(vm_vec_mag(&aip->goal_point) >= 0.0f);           //      Supposedly detects bogus vector
12157
12158         turn_towards_tangent(Pl_objp, &goal_point, 10*objp->radius + 200.0f);
12159
12160         set_accel_for_target_speed(Pl_objp, target_speed);
12161
12162 }
12163
12164 #define SHIELD_BALANCE_RATE     0.2f            //      0.1f -> takes 10 seconds to equalize shield.
12165
12166 //      Transfer shield energy to most recently hit section from others.
12167 void ai_transfer_shield(object *objp, int quadrant_num)
12168 {
12169         int     i;
12170         float   transfer_amount;
12171         float   transfer_delta;
12172         ship_info       *sip;
12173         float   max_quadrant_strength;
12174
12175         sip = &Ship_info[Ships[objp->instance].ship_info_index];
12176         max_quadrant_strength = sip->shields/MAX_SHIELD_SECTIONS;
12177
12178         transfer_amount = 0.0f;
12179         transfer_delta = (SHIELD_BALANCE_RATE/2) * max_quadrant_strength;
12180
12181         if (objp->shields[quadrant_num] + (MAX_SHIELD_SECTIONS-1)*transfer_delta > max_quadrant_strength)
12182                 transfer_delta = (max_quadrant_strength - objp->shields[quadrant_num])/(MAX_SHIELD_SECTIONS-1);
12183
12184         for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12185                 if (i != quadrant_num) {
12186                         if (objp->shields[i] >= transfer_delta) {
12187                                 objp->shields[i] -= transfer_delta;
12188                                 transfer_amount += transfer_delta;
12189                         } else {
12190                                 transfer_amount += objp->shields[i];
12191                                 objp->shields[i] = 0.0f;
12192                         }
12193                 }
12194
12195         objp->shields[quadrant_num] += transfer_amount;
12196 }
12197
12198 void ai_balance_shield(object *objp)
12199 {
12200         int     i;
12201         float   shield_strength_avg;
12202         float   delta;
12203
12204
12205         shield_strength_avg = get_shield_strength(objp)/MAX_SHIELD_SECTIONS;
12206
12207         delta = SHIELD_BALANCE_RATE * shield_strength_avg;
12208
12209         for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12210                 if (objp->shields[i] < shield_strength_avg) {
12211                         add_shield_strength(objp, delta);
12212                         if (objp->shields[i] > shield_strength_avg)
12213                                 objp->shields[i] = shield_strength_avg;
12214                 } else {
12215                         add_shield_strength(objp, -delta);
12216                         if (objp->shields[i] < shield_strength_avg)
12217                                 objp->shields[i] = shield_strength_avg;
12218                 }
12219 }
12220
12221 //      Manage the shield for this ship.
12222 //      Try to max out the side that was most recently hit.
12223 void ai_manage_shield(object *objp, ai_info *aip)
12224 {
12225         ship_info *sip;
12226
12227         sip = &Ship_info[Ships[objp->instance].ship_info_index];
12228
12229         if (timestamp_elapsed(aip->shield_manage_timestamp)) {
12230                 float           delay;
12231
12232                 //      Scale time until next manage shield based on Skill_level.
12233                 //      Ships on player's team are treated as if Skill_level is average.
12234                 if (Ships[objp->instance].team != Player_ship->team){
12235                         delay = Shield_manage_delays[Game_skill_level];
12236                 } else {
12237                         delay = Shield_manage_delays[NUM_SKILL_LEVELS/2];
12238                 }
12239
12240                 //      Scale between 1x and 3x based on ai_class
12241                 delay = delay + delay * (float) (3*(Num_ai_classes - aip->ai_class - 1) / (Num_ai_classes - 1));
12242                 aip->shield_manage_timestamp = timestamp((int) (delay * 1000.0f));
12243
12244                 if (sip->flags & SIF_SMALL_SHIP) {
12245                         if (Missiontime - aip->last_hit_time < F1_0*10)
12246                                 ai_transfer_shield(objp, aip->last_hit_quadrant);
12247                         else
12248                                 ai_balance_shield(objp);
12249                 }
12250
12251                 // 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]));
12252         }
12253 }
12254
12255 //      See if object *objp should evade an incoming missile.
12256 //      *aip is the ai_info pointer within *objp.
12257 void ai_maybe_evade_locked_missile(object *objp, ai_info *aip)
12258 {
12259         ship                    *shipp;
12260         ship_info       *sip;
12261
12262         shipp = &Ships[objp->instance];
12263         sip = &Ship_info[shipp->ship_info_index];
12264
12265         //      Only small ships evade an incoming missile.  Why would a capital ship try to swerve?
12266         if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12267                 return;
12268         }
12269
12270         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
12271                 return;
12272         }
12273
12274         if (aip->nearest_locked_object != -1) {
12275                 object  *missile_objp;
12276
12277                 missile_objp = &Objects[aip->nearest_locked_object];
12278
12279                 if (Weapons[missile_objp->instance].homing_object != objp) {
12280                         //nprintf(("AI", "\nMissile lost home!"));
12281                         aip->nearest_locked_object = -1;
12282                         return;
12283                 }
12284
12285                 if ((missile_objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[missile_objp->instance].weapon_info_index].wi_flags & WIF_HOMING)) {
12286                         float dist = vm_vec_dist_quick(&missile_objp->pos, &objp->pos);
12287                         float dist2 = 4.0f  * vm_vec_mag_quick(&missile_objp->phys_info.vel);                   
12288                         if (dist < dist2) {
12289                                 switch (aip->mode) {
12290                                 //      If in AIM_STRAFE mode, don't evade if parent of weapon is targeted ship.
12291                                 case AIM_STRAFE:
12292                                         if ((missile_objp->parent != -1) && (missile_objp->parent == aip->target_objnum)) {
12293                                                 ;
12294                                         } else {
12295                                                 ;               //      Alan -- If you want to handle incoming weapons from someone other than the ship
12296                                                                 //      the strafing ship is attacking, do it here.
12297                                         }
12298                                         break;
12299                                 case AIM_CHASE:
12300                                         //      Don't always go into evade weapon mode.  Usually, a countermeasure gets launched.
12301                                         // If low on countermeasures, more likely to try to evade.  If 8+, never evade due to low cmeasures.
12302                                         if (((((Missiontime >> 18) ^ OBJ_INDEX(objp)) & 3) == 0) || 
12303                                                 (objp->phys_info.speed < 40.0f) ||
12304                                                 (frand() < 1.0f - (float) shipp->cmeasure_count/8.0f)) {
12305                                                 if (aip->submode != SM_ATTACK_FOREVER) {        //      SM_ATTACK_FOREVER means engines blown.
12306                                                         aip->submode = SM_EVADE_WEAPON;
12307                                                         aip->submode_start_time = Missiontime;
12308                                                 }
12309                                         }
12310                                         break;
12311                                 case AIM_DOCK:  //      Ships in dock mode can evade iif they are not currently repairing or docked.
12312                                         if (aip->ai_flags & (AIF_REPAIRING | AIF_DOCKED))
12313                                                 break;
12314                                 case AIM_GUARD:
12315                                         //      If in guard mode and far away from guard object, don't pursue guy that hit me.
12316                                         if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12317                                                 if (vm_vec_dist_quick(&objp->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12318                                                         return;
12319                                                 }
12320                                         }
12321                                 case AIM_EVADE:
12322                                 case AIM_GET_BEHIND:
12323                                 case AIM_STAY_NEAR:
12324                                 case AIM_STILL:
12325                                 case AIM_AVOID:
12326                                 case AIM_WAYPOINTS:
12327                                 case AIM_NONE:
12328                                 case AIM_BIGSHIP:
12329                                 case AIM_PATH:
12330                                 case AIM_BE_REARMED:
12331                                 case AIM_SAFETY:
12332                                 case AIM_BAY_EMERGE:
12333                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12334                                         aip->previous_mode = aip->mode;
12335                                         aip->previous_submode = aip->submode;
12336                                         aip->mode = AIM_EVADE_WEAPON;
12337                                         aip->submode = -1;
12338                                         aip->submode_start_time = Missiontime;
12339                                         aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Max time to evade.
12340                                         //nprintf(("AI", "%s Evade weapon in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
12341                                         break;
12342                                 case AIM_EVADE_WEAPON:          //      Note: We don't want to change mode on another evasion, or previous_mode will get bashed.
12343                                 case AIM_PLAY_DEAD:
12344                                 case AIM_BAY_DEPART:
12345                                 case AIM_SENTRYGUN:
12346                                         break;
12347                                 case AIM_WARP_OUT:
12348                                         break;
12349                                 default:
12350                                         Int3();                 //      Hey, what mode is it?
12351                                         break;
12352                                 }
12353                         }
12354                 } else {
12355                         aip->nearest_locked_object = -1;
12356                 }
12357         }
12358 }
12359
12360 //      Maybe evade a dumbfire weapon that was fired when Pl_objp was targeted.
12361 //      Have an 80% chance of evading in a second
12362 void maybe_evade_dumbfire_weapon(ai_info *aip)
12363 {
12364         //      Only small ships evade an incoming missile.  Why would a capital ship try to swerve?
12365         if (!(Ship_info[Ships[Pl_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12366                 return;
12367         }
12368
12369         //      Make sure in a mode in which we evade dumbfire weapons.
12370         switch (aip->mode) {
12371         case AIM_CHASE:
12372                 if (aip->submode == SM_ATTACK_FOREVER) {
12373                         return;
12374                 }
12375         case AIM_GUARD:
12376                 //      If in guard mode and far away from guard object, don't pursue guy that hit me.
12377                 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12378                         if (vm_vec_dist_quick(&Objects[Ships[aip->shipnum].objnum].pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12379                                 return;
12380                         }
12381                 }
12382         case AIM_STILL:
12383         case AIM_STAY_NEAR:
12384         case AIM_EVADE:
12385         case AIM_GET_BEHIND:
12386         case AIM_AVOID:
12387         case AIM_PATH:
12388         case AIM_NONE:
12389         case AIM_WAYPOINTS:
12390         case AIM_SAFETY:
12391                 break;
12392         case AIM_STRAFE:
12393         case AIM_BIGSHIP:
12394         case AIM_DOCK:
12395         case AIM_PLAY_DEAD:
12396         case AIM_EVADE_WEAPON:
12397         case AIM_BAY_EMERGE:
12398         case AIM_BAY_DEPART:
12399         case AIM_SENTRYGUN:
12400         case AIM_WARP_OUT:
12401                 return;
12402         default:
12403                 Int3(); //      Bogus mode!
12404                 return;
12405         }
12406
12407         if (is_instructor(&Objects[Ships[aip->shipnum].objnum]))
12408                 return; //      Instructor doesn't evade.
12409
12410         float t = ai_endangered_by_weapon(aip);
12411         if ((t > 0.0f) && (t < 1.0f)) {
12412         // Check if this weapon is from a large ship Pl_objp is attacking... if so, enter strafe mode
12413                 if ( ai_big_maybe_enter_strafe_mode(Pl_objp, aip->danger_weapon_objnum) ) {
12414                         return;
12415                 }
12416
12417                 switch (aip->mode) {
12418                 case AIM_CHASE:
12419                         switch (aip->submode) {
12420                         case SM_EVADE:
12421                         case SM_ATTACK_FOREVER:
12422                         case SM_AVOID:
12423                         case SM_GET_AWAY:
12424                         case SM_EVADE_WEAPON:
12425                                 break;
12426                         default:
12427                                 if (ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
12428                                         //mprintf(("Ship %s entered super mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12429                                         aip->submode = SM_SUPER_ATTACK;
12430                                         aip->submode_start_time = Missiontime;
12431                                         aip->last_attack_time = Missiontime;
12432                                 } else {
12433                                         //mprintf(("Ship %s entered dumbfire evade mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12434                                         aip->submode = SM_EVADE_WEAPON;
12435                                         aip->submode_start_time = Missiontime;
12436                                 }
12437                                 break;
12438                         }
12439                         break;
12440                 case AIM_GUARD:
12441                 case AIM_STILL:
12442                 case AIM_STAY_NEAR:
12443                 case AIM_EVADE:
12444                 case AIM_GET_BEHIND:
12445                 case AIM_AVOID:
12446                 case AIM_PATH:
12447                 case AIM_NONE:
12448                 case AIM_WAYPOINTS:
12449                 case AIM_SAFETY:
12450                         if (!(aip->ai_flags & (AIF_NO_DYNAMIC | AIF_KAMIKAZE)) && (Ship_info[Ships[aip->shipnum].ship_info_index].flags & SIF_SMALL_SHIP)) {
12451                                 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12452                                 aip->previous_mode = aip->mode;
12453                                 aip->previous_submode = aip->submode;
12454                                 aip->mode = AIM_EVADE_WEAPON;
12455                                 aip->submode = -1;
12456                                 aip->submode_start_time = Missiontime;
12457                                 aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Evade for up to five seconds.
12458                         }
12459                         break;
12460                 case AIM_STRAFE:
12461                 case AIM_BIGSHIP:
12462                 case AIM_DOCK:
12463                 case AIM_PLAY_DEAD:
12464                 case AIM_EVADE_WEAPON:
12465                 case AIM_BAY_EMERGE:
12466                 case AIM_BAY_DEPART:
12467                 case AIM_SENTRYGUN:
12468                         break;
12469                 default:
12470                         Int3(); //      Bogus mode!
12471                 }
12472         }
12473 }
12474
12475 // determine what path to use when emerging from a fighter bay
12476 // input:       pl_objp =>      pointer to object for ship that is arriving
12477 //                              pos             =>      output parameter, it is the starting world pos for path choosen
12478 //                              v.fvec          =>      output parameter, this is the forward vector that ship has when arriving
12479 //
12480 // exit:                -1              =>      path could not be located
12481 //                               0              => success
12482 int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, vector *pos, vector *fvec)
12483 {
12484         int                     path_index, sb_path_index;
12485         ship                    *parent_sp = NULL;
12486         polymodel       *pm;
12487         ai_info         *aip;
12488         ship_bay                *sb;
12489         pnode                   *pnp;
12490         vector          *next_point;
12491
12492         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12493
12494         if ( parent_objnum == -1 ) {
12495                 Int3();
12496                 return -1;
12497         }
12498
12499         parent_sp = &Ships[Objects[parent_objnum].instance];
12500
12501         Assert(parent_sp != NULL);
12502         pm = model_get( parent_sp->modelnum );
12503         sb = pm->ship_bay;
12504
12505         if ( sb == NULL ) 
12506                 return -1;
12507
12508         if ( sb->num_paths <= 0 ) 
12509                 return -1;
12510
12511         // try to find a bay path that is not taken
12512         path_index = -1;
12513         sb_path_index = Ai_last_arrive_path++;
12514
12515         if ( sb_path_index >= sb->num_paths ) {
12516                 sb_path_index=0;
12517                 Ai_last_arrive_path=0;
12518         }
12519
12520         path_index = sb->paths[sb_path_index];
12521         if ( path_index == -1 ) 
12522                 return -1;
12523
12524         // create the path for pl_objp to follow
12525         create_model_exit_path(pl_objp, &Objects[parent_objnum], path_index, pm->paths[path_index].nverts);
12526         
12527         // Set this flag, so we don't bother recreating the path... we won't need to update the path
12528         // that has just been created.
12529 //      aip->ai_flags |= AIF_USE_STATIC_PATH;
12530
12531         // now return to the caller what the starting world pos and starting fvec for the ship will be
12532         Assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
12533         pnp = &Path_points[aip->path_start];
12534         *pos = pnp->pos;
12535
12536         // calc the forward vector using the starting two points of the path
12537         pnp = &Path_points[aip->path_start+1];
12538         next_point = &pnp->pos;
12539         vm_vec_normalized_dir(fvec, next_point, pos);
12540
12541         // record the parent objnum, since we'll need it once we're done with following the path
12542         aip->goal_objnum = parent_objnum;
12543         aip->goal_signature = Objects[parent_objnum].signature;
12544         aip->mode = AIM_BAY_EMERGE;
12545         aip->submode_start_time = Missiontime;
12546
12547         // set up starting vel
12548         vector vel;
12549         float speed;
12550         speed = Ship_info[Ships[pl_objp->instance].ship_info_index].max_speed;
12551         vel = *fvec;
12552         vm_vec_scale( &vel, speed );
12553         pl_objp->phys_info.vel = vel;
12554         pl_objp->phys_info.desired_vel = vel;
12555         pl_objp->phys_info.prev_ramp_vel.xyz.x = 0.0f;
12556         pl_objp->phys_info.prev_ramp_vel.xyz.y = 0.0f;
12557         pl_objp->phys_info.prev_ramp_vel.xyz.z = speed;
12558         pl_objp->phys_info.forward_thrust = 0.0f;               // How much the forward thruster is applied.  0-1.
12559
12560         return 0;       
12561 }
12562
12563 // clean up path data used for emerging from a fighter bay
12564 void ai_emerge_bay_path_cleanup(ai_info *aip)
12565 {
12566         aip->path_start = -1;
12567         aip->path_cur = -1;
12568         aip->path_length = 0;
12569         aip->mode = AIM_NONE;
12570 }
12571
12572 // handler for AIM_BAY_EMERGE
12573 void ai_bay_emerge()
12574 {
12575         ai_info *aip;
12576         int             parent_died=0;
12577
12578         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12579
12580         // if no path to follow, leave this mode
12581         if ( aip->path_start < 0 ) {
12582                 aip->mode = AIM_NONE;
12583                 return;
12584         }
12585
12586         // ensure parent ship is still alive
12587         if ( aip->goal_objnum < 0 ) {
12588                 parent_died=1;
12589         } 
12590         if ( !parent_died ) {
12591                 if ( Objects[aip->goal_objnum].signature != aip->goal_signature ) {
12592                         parent_died=1;
12593                 }
12594         }
12595
12596         if ( !parent_died ) {
12597                 Assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
12598                 if ( Ships[Objects[aip->goal_objnum].instance].flags & SF_DYING ) {
12599                         parent_died = 1;
12600                 }
12601         }
12602
12603         if ( parent_died ) {
12604                 ai_emerge_bay_path_cleanup(aip);
12605                 return;
12606         }
12607
12608         // follow the path to the final point
12609         ai_path();
12610
12611         // New test: must have been in AI_EMERGE mode for at least 10 seconds, and be a minimum distance from the start point
12612         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)) {
12613                 // erase path
12614                 ai_emerge_bay_path_cleanup(aip);
12615         }
12616
12617         // 2-25-99: Need this check to fix an assert for supercap ships... maybe we'll only do this check for supercaps 
12618         if (aip->path_cur > (aip->path_start+aip->path_length-1)) {
12619                 ai_emerge_bay_path_cleanup(aip);
12620         }       
12621 }
12622
12623 // Select the closest depart path
12624 //
12625 //      input:  aip     =>              ai info pointer to ship seeking to depart
12626 //                              pm              =>              pointer to polymodel for the ship contining the ship bay/depart paths
12627 //
12628 // exit:                >=0     =>              ship bay path index for depart path (ie index into sb->paths[])
12629 //                              -1              =>              no path could be found
12630 //
12631 // NOTE: this function should only be used for calculating closest depart paths for ai mode
12632 //                      AI_BAY_DEPART.  It tries to find the closest path that isn't already in use
12633 int ai_find_closest_depart_path(ai_info *aip, polymodel *pm)
12634 {
12635         int                     i, j, best_path, best_free_path;
12636         float                   dist, min_dist, min_free_dist;
12637         vector          *source;
12638         model_path      *mp;
12639         ship_bay                *sb;
12640
12641         sb = pm->ship_bay;
12642
12643         best_free_path = best_path = -1;
12644         min_free_dist = min_dist = 1e20f;
12645         Assert(aip->shipnum >= 0);
12646         source = &Objects[Ships[aip->shipnum].objnum].pos;
12647
12648         for ( i = 0; i < sb->num_paths; i++ ) {
12649
12650
12651                 mp = &pm->paths[sb->paths[i]];
12652                 for ( j = 0; j < mp->nverts; j++ ) {
12653                         dist = vm_vec_dist_squared(source, &mp->verts[j].pos);
12654
12655                         if ( dist < min_dist ) {
12656                                 min_dist = dist;
12657                                 best_path = i;
12658                         }
12659
12660                         // If this is a free path
12661                         if ( !(sb->depart_flags & (1<<i)) ) {
12662                                 if ( dist < min_free_dist ) {
12663                                         min_free_dist = dist;
12664                                         best_free_path = i;
12665                                 }
12666                         }
12667                 }
12668         }
12669
12670         if ( best_free_path >= 0 ) {
12671                 return best_free_path;          
12672         }
12673
12674         return best_path;
12675 }
12676
12677 // determine what path to use when trying to depart to a fighter bay
12678 // NOTE: this should be called when AIM_BAY_DEPART mode is set
12679 //
12680 // input:       pl_objp =>      pointer to object for ship that is departing
12681 //
12682 // exit:                -1      =>      could not find depart path
12683 //                              0       => found depart path
12684 int ai_acquire_depart_path(object *pl_objp, int parent_objnum)
12685 {
12686         int                     objnum, path_index;
12687         polymodel       *pm;
12688         ai_info         *aip;
12689         ship                    *sp;
12690         ship_bay                *sb;
12691
12692         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12693
12694         if ( parent_objnum == -1 ) {
12695                 ship_obj        *so;
12696
12697                 // for now just locate a captial ship on the same team:
12698                 so = GET_FIRST(&Ship_obj_list);
12699                 objnum = -1;
12700                 while(so != END_OF_LIST(&Ship_obj_list)){
12701                         sp = &Ships[Objects[so->objnum].instance];
12702                         if ( (Ship_info[sp->ship_info_index].flags & (SIF_HUGE_SHIP)) && (sp->team == Ships[pl_objp->instance].team) ) {
12703                                 objnum = so->objnum;
12704                                 break;
12705                         }
12706                         so = GET_NEXT(so);
12707                 } 
12708         } else {
12709                 objnum = parent_objnum;
12710         }
12711
12712         aip->path_start = -1;
12713
12714         if ( objnum == -1 )
12715                 return -1;
12716
12717         pm = model_get( Ships[Objects[objnum].instance].modelnum );
12718         sb = pm->ship_bay;
12719
12720         if ( sb == NULL ) 
12721                 return -1;
12722         if ( sb->num_paths <= 0 ) 
12723                 return -1;
12724
12725 /*
12726         
12727         path_index = -1;
12728         for ( i = 0; i < sb->num_paths; i++ ) {
12729                 if ( !(sb->depart_flags & (1<<i)) ) {
12730                         sb->depart_flags |= (1<<i);
12731                         path_index = sb->paths[i];
12732                         aip->submode_parm0 = i;                 // use mode-specific parameter to record ship bay path index
12733                         break;
12734                 }
12735         }
12736 */
12737         
12738         // take the closest path we can find
12739         int ship_bay_path;
12740         ship_bay_path = ai_find_closest_depart_path(aip, pm);
12741         path_index = sb->paths[ship_bay_path];
12742         aip->submode_parm0 = ship_bay_path;
12743         sb->depart_flags |= (1<<ship_bay_path);
12744
12745         if ( path_index == -1 ) {
12746                 return -1;
12747         }
12748
12749         Assert(pm->n_paths > path_index);
12750         ai_find_path(pl_objp, objnum, path_index, 0);
12751
12752         // Set this flag, so we don't bother recreating the path... we won't need to update the path
12753         // that has just been created.
12754         aip->ai_flags &= ~AIF_USE_STATIC_PATH;
12755
12756         aip->goal_objnum = objnum;
12757         aip->goal_signature = Objects[objnum].signature;
12758         aip->mode = AIM_BAY_DEPART;
12759
12760         Ships[pl_objp->instance].flags |= SF_DEPART_DOCKBAY;
12761         return 0;
12762 }
12763
12764 // handler for AIM_BAY_DEPART
12765 void ai_bay_depart()
12766 {
12767         ai_info *aip;
12768
12769         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12770
12771         // if no path to follow, leave this mode
12772         if ( aip->path_start < 0 ) {
12773                 aip->mode = AIM_NONE;
12774                 return;
12775         }
12776
12777         // check if parent ship still exists, if not abort depart 
12778         if ( aip->goal_signature != Objects[aip->goal_objnum].signature ) {
12779                 aip->mode = AIM_NONE;
12780                 return;
12781         }
12782
12783         // follow the path to the final point
12784         ai_path();
12785
12786         // if the final point is reached, let default AI take over
12787         if ( aip->path_cur >= (aip->path_start+aip->path_length) ) {
12788                 polymodel       *pm;
12789                 ship_bay                *sb;
12790
12791                 pm = model_get( Ships[Objects[aip->goal_objnum].instance].modelnum );
12792                 sb = pm->ship_bay;
12793                 if ( sb != NULL ) {
12794                         sb->depart_flags &= ~(1<<aip->submode_parm0);
12795                 }
12796
12797                 // make ship disappear
12798                 Pl_objp->flags |= OF_SHOULD_BE_DEAD;
12799                 ship_departed( Pl_objp->instance );
12800
12801                 // clean up path stuff
12802                 aip->path_start = -1;
12803                 aip->path_cur = -1;
12804                 aip->path_length = 0;
12805                 aip->mode = AIM_NONE;
12806         }
12807 }
12808
12809 // Handler for AIM_SENTRYGUN.  This AI mode is for sentry guns only (ie floating turrets).
12810 void ai_sentrygun()
12811 {
12812         // Nothing to do here.  Turret firing is handled via process_subobjects().
12813         // If you want the sentry guns to do anything beyond firing their turrets at enemies, add it here!
12814 }
12815
12816 //      --------------------------------------------------------------------------
12817 //      Execute behavior given by aip->mode.
12818 void ai_execute_behavior(ai_info *aip)
12819 {
12820         switch (aip->mode) {
12821         case AIM_CHASE:
12822                 if (En_objp) {
12823                         ai_chase();
12824                 } else if (aip->submode == SM_EVADE_WEAPON) {
12825                         evade_weapon();
12826                         // maybe reset submode
12827                         if (aip->danger_weapon_objnum == -1) {
12828                                 aip->submode = SM_ATTACK;
12829                                 aip->submode_start_time = Missiontime;
12830                                 aip->last_attack_time = Missiontime;
12831                         }
12832                 } else {
12833                         //      Don't circle if this is the instructor.
12834                         ship    *shipp = &Ships[aip->shipnum];
12835                         ship_info       *sip = &Ship_info[shipp->ship_info_index];
12836
12837                         if (strnicmp(shipp->ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME))) {
12838                                 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
12839                                         aip->mode = AIM_NONE;
12840                                 } else {
12841                                         ai_chase_circle(Pl_objp);
12842                                 }
12843                         }
12844                 }
12845                 break;
12846         case AIM_EVADE:
12847                 if (En_objp) {
12848                         ai_evade();
12849                 } else {
12850                         vector  tvec;
12851                         vm_vec_scale_add(&tvec, &Pl_objp->pos, &Pl_objp->orient.v.rvec, 100.0f);
12852                         turn_towards_point(Pl_objp, &tvec, NULL, 0.0f);
12853                         accelerate_ship(aip, 0.5f);
12854                 }
12855                 break;
12856         case AIM_STILL:
12857                 ai_still();
12858                 break;
12859         case AIM_STAY_NEAR:
12860                 ai_stay_near();
12861                 break;
12862         case AIM_GUARD:
12863                 ai_guard();
12864                 break;
12865         case AIM_WAYPOINTS:
12866                 ai_waypoints();
12867                 break;
12868         case AIM_DOCK:
12869                 ai_dock();
12870                 break;
12871         case AIM_NONE:
12872                 // ai_formation();
12873                 break;
12874         case AIM_BIGSHIP:
12875                 ai_big_ship(Pl_objp);
12876                 break;
12877         case AIM_PATH: {
12878                 int path_num;
12879                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], 0);
12880                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
12881                 ai_path();
12882                 break;
12883         }
12884         case AIM_SAFETY:
12885                 ai_safety();
12886                 break;
12887         case AIM_EVADE_WEAPON:
12888                 evade_weapon();
12889                 break;
12890         case AIM_STRAFE:
12891                 if (En_objp) {
12892                         Assert(En_objp->type == OBJ_SHIP);
12893                         ai_big_strafe();        // strafe a big ship
12894                 } else {
12895                         aip->mode = AIM_NONE;
12896                 }
12897                 break;
12898         case AIM_BAY_EMERGE:
12899                 ai_bay_emerge();
12900                 break;
12901         case AIM_BAY_DEPART:
12902                 ai_bay_depart();
12903                 break;
12904         case AIM_SENTRYGUN:
12905                 ai_sentrygun();
12906                 break;
12907         case AIM_WARP_OUT:
12908                 break;          //      Note, handled directly from ai_frame().
12909         default:
12910                 Int3();         //      This should never happen -- MK, 5/12/97 
12911                 break;
12912         }
12913
12914         if ( !(ship_get_SIF(aip->shipnum) & SIF_NOT_FLYABLE) ) {
12915                 maybe_evade_dumbfire_weapon(aip);
12916         }
12917 }
12918
12919 //      Auxiliary function for maybe_request_support.
12920 //      Return 1 if subsystem "type" is worthy of repair, else return 0.
12921 //      Since subsystems cannot be repaired if they are at 0 strength, don't return 1 if subsystem is dead.
12922 int mrs_subsystem(ship *shipp, int type)
12923 {
12924         float   t;
12925
12926         t = ship_get_subsystem_strength(shipp, type);
12927
12928         if (t > 0.0f) {
12929                 return (int) ((1.0f - t) * 3);
12930         } else {
12931                 return 3;
12932         }
12933 }
12934
12935 //      Return number of ships on *objp's team that are currently rearming.
12936 int num_allies_rearming(object *objp)
12937 {
12938         ship_obj        *so;
12939         int             team;
12940         int             count = 0;
12941
12942         team = Ships[objp->instance].team;
12943
12944         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
12945                 object  *A;
12946                 
12947                 Assert (so->objnum != -1);
12948                 A = &Objects[so->objnum];
12949
12950                 if (Ships[A->instance].team == team) {
12951                         if (Ai_info[Ships[A->instance].ai_index].ai_flags & (AIF_REPAIRING | AIF_AWAITING_REPAIR)) {
12952                                 count++;
12953                         }
12954                 }
12955         }
12956
12957         return count;
12958 }
12959
12960
12961 //      Maybe ship *objp should request support (rearm/repair).
12962 //      If it does, return TRUE, else return FALSE.
12963 int maybe_request_support(object *objp)
12964 {
12965         ship_info       *sip;
12966         ship                    *shipp;
12967         ai_info         *aip;
12968         int                     desire;
12969
12970         Assert(objp->type == OBJ_SHIP);
12971         shipp = &Ships[objp->instance];
12972         aip = &Ai_info[shipp->ai_index];
12973         sip = &Ship_info[shipp->ship_info_index];
12974
12975         if (!timestamp_elapsed(aip->next_rearm_request_timestamp))
12976                 return 0;
12977
12978         //      Only fighters and bombers request support.
12979         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER)))
12980                 return 0;
12981
12982         //      A ship that is currently awaiting does not need support!
12983         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
12984                 return 0;
12985
12986         if (!is_support_allowed(objp))
12987                 return 0;
12988
12989         //if (shipp->team != TEAM_FRIENDLY)
12990         //      return 0;
12991
12992         //      Compute a desire value.
12993         //      Desire of 0 means no reason to request support.
12994         //      1 is slight, 2 more, etc.  Maximum is around 20.  Anything larger than 3 is pretty strong.
12995         desire = 0;
12996
12997         //      Set desire based on hull strength.
12998         //      No: We no longer repair hull, so this would cause repeated repair requests.
12999         //desire += 6 - (int) ((objp->hull_strength/sip->initial_hull_strength) * 6.0f);
13000
13001         //      Set desire based on key subsystems.
13002         desire += 2*mrs_subsystem(shipp, SUBSYSTEM_ENGINE);     //      Note, disabled engine forces repair request, regardless of nearby enemies.
13003         desire += mrs_subsystem(shipp, SUBSYSTEM_COMMUNICATION);
13004         desire += mrs_subsystem(shipp, SUBSYSTEM_WEAPONS);
13005         desire += mrs_subsystem(shipp, SUBSYSTEM_SENSORS);
13006
13007         //      Set desire based on percentage of secondary weapons.
13008         ship_weapon *swp = &shipp->weapons;
13009
13010         for ( int i = 0; i < swp->num_secondary_banks; i++ ) {
13011                 if (swp->secondary_bank_start_ammo[i] > 0) {
13012 //                      float r = (float) swp->secondary_bank_ammo[i]*Weapon_info[swp->secondary_bank_weapons[i]].cargo_size/swp->secondary_bank_capacity[i];
13013                         float r = (float) swp->secondary_bank_ammo[i]/swp->secondary_bank_start_ammo[i];
13014                         desire += (int) ((1.0f - r) * 3.0f);
13015                 }
13016         }
13017
13018         //      If no reason to repair, don't bother to see if it's safe to repair.
13019         if (desire == 0){
13020                 return 0;
13021         }
13022
13023         //      Compute danger threshold.
13024         //      Balance this with desire and maybe request support.
13025         if (ai_good_time_to_rearm( objp )) {
13026                 ai_issue_rearm_request(objp);
13027                 return 1;
13028         } else if (num_allies_rearming(objp) < 2) {
13029                 if (desire >= 8) {      //      guarantees disabled will cause repair request
13030                         ai_issue_rearm_request(objp);
13031                 } else if (desire >= 3) {               //      >= 3 means having a single subsystem fully blown will cause repair.
13032                         int     count;
13033                         int objnum = find_nearby_hostile(OBJ_INDEX(objp), get_enemy_team_mask(OBJ_INDEX(objp)), 2000.0f, &count);
13034
13035                         if ((objnum == -1) || (count < 2) || (vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos) > 3000.0f*count/desire)) {
13036                                 ai_issue_rearm_request(objp);
13037                                 return 1;
13038                         } else {
13039                                 //nprintf(("AI", "Would like to rearm, but enemy only %7.3f units away.\n", vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos)));
13040                         }
13041                 }
13042         }
13043
13044         return 0;
13045
13046 }
13047
13048 void ai_set_mode_warp_out(object *objp, ai_info *aip)
13049 {
13050         ai_abort_rearm_request(objp);
13051         if (aip->mode != AIM_WARP_OUT) {
13052                 aip->mode = AIM_WARP_OUT;
13053                 aip->submode = AIS_WARP_1;
13054         }
13055 }
13056
13057 //      Maybe warp ship out.
13058 //      Shivan and HoL fighter/bomber warp out if their weapons subsystems have been destroyed.
13059 void ai_maybe_warp_out(object *objp)
13060 {
13061         ship    *shipp;
13062
13063         // don't do anything if in a training mission.
13064         if ( The_mission.game_type & MISSION_TYPE_TRAINING )
13065                 return;
13066
13067         Assert(objp->type == OBJ_SHIP);
13068
13069         shipp = &Ships[objp->instance];
13070         ai_info *aip = &Ai_info[shipp->ai_index];
13071
13072         if (aip->mode == AIM_WARP_OUT)
13073                 return;
13074
13075         //      If a support ship with no goals and low hull, warp out.  Be sure that there are no pending goals
13076         // in the support ships ai_goal array.  Just process this ships goals.
13077         ship_info       *sip = &Ship_info[shipp->ship_info_index];
13078         if (sip->flags & SIF_SUPPORT) {
13079                 if ( timestamp_elapsed(aip->warp_out_timestamp) ) {
13080                         ai_process_mission_orders( OBJ_INDEX(objp), aip );
13081                         if ( (aip->dock_objnum == -1) && (objp->hull_strength/sip->initial_hull_strength < 0.25f) ) {
13082                                 ai_set_mode_warp_out(objp, aip);
13083                         }
13084                 }
13085         }
13086
13087         //      Friendly don't warp out, they'll eventually request support.
13088         if (shipp->team == TEAM_FRIENDLY)
13089                 return;
13090
13091         if (!(shipp->flags & SF_DEPARTING)) {
13092                 ship_info       *sip;
13093
13094                 sip = &Ship_info[shipp->ship_info_index];
13095                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
13096                         if (aip->warp_out_timestamp == 0) {
13097                                 //if (ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS) == 0.0f) {
13098                                 //      aip->warp_out_timestamp = timestamp(((myrand() % 10) + 10) * 1000);
13099                                 //}
13100                         } else if (timestamp_elapsed(aip->warp_out_timestamp)) {
13101                                 ai_set_mode_warp_out(objp, aip);
13102                         }
13103                 }
13104         }
13105 }
13106
13107 //      Warp this ship out.
13108 void ai_warp_out(object *objp)
13109 {
13110         // if dying, don't warp out.
13111         if (Ships[objp->instance].flags & SF_DYING) {
13112                 return;
13113         }
13114
13115         ai_info *aip;
13116
13117         aip = &Ai_info[Ships[objp->instance].ai_index];
13118
13119         switch (aip->submode) {
13120         case AIS_WARP_1:
13121                 aip->force_warp_time = timestamp(10*1000);      //      Try to avoid a collision for up to ten seconds.
13122                 aip->submode = AIS_WARP_2;
13123                 break;
13124         case AIS_WARP_2:                        //      Make sure won't collide with any object.
13125                 if (timestamp_elapsed(aip->force_warp_time) || !collide_predict_large_ship(objp, objp->radius*2.0f + 100.0f)) {
13126                         aip->submode = AIS_WARP_3;
13127
13128                         // maybe recalculate collision pairs.
13129                         if (ship_get_warp_speed(objp) > ship_get_max_speed(&Ships[objp->instance])) {
13130                                 // recalculate collision pairs
13131                                 OBJ_RECALC_PAIRS(objp); 
13132                         }
13133
13134                         aip->force_warp_time = timestamp(4*1000);               //      Try to attain target speed for up to 4 seconds.
13135                 } else {
13136                         vector  goal_point;
13137                         vm_vec_scale_add(&goal_point, &objp->pos, &objp->orient.v.uvec, 100.0f);
13138                         turn_towards_point(objp, &goal_point, NULL, 0.0f);
13139                         accelerate_ship(aip, 0.0f);
13140                 }
13141                 break;
13142         case AIS_WARP_3:
13143                 //      Rampup desired_vel in here from current to desired velocity and set PF_USE_VEL. (not sure this is the right flag)
13144                 //      desired velocity is computed in shipfx_calculate_warp_time().  See shipfx#572 for sample code.
13145                 float   speed, goal_speed;
13146                 float shipfx_calculate_warp_speed(object*);
13147                 goal_speed = shipfx_calculate_warp_speed(objp);
13148
13149                 // HUGE ships go immediately to AIS_WARP_4
13150                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_HUGE_SHIP) {
13151                         aip->submode = AIS_WARP_4;
13152                         break;
13153                 }
13154                 //compute_warpout_stuff(objp, &goal_speed, &warp_time, &warp_pos);
13155                 //goal_speed = 80.0f;
13156                 //set_accel_for_target_speed(objp, 40.0f);
13157                 // 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
13158                 speed = goal_speed * flFrametime + objp->phys_info.speed * (1.0f - flFrametime);
13159                 vm_vec_copy_scale(&objp->phys_info.vel, &objp->orient.v.fvec, speed);
13160                 objp->phys_info.desired_vel = objp->phys_info.vel;
13161                 // nprintf(("AI", "Frame %i, speed = %7.3f, goal = %7.3f\n", Framecount, vm_vec_mag_quick(&objp->phys_info.vel), goal_speed));
13162                 if (timestamp_elapsed(aip->force_warp_time) || (fl_abs(objp->phys_info.speed - goal_speed) < 2.0f))
13163                         aip->submode = AIS_WARP_4;
13164                 break;
13165         case AIS_WARP_4: {
13166                 shipfx_warpout_start(objp);
13167                 aip->submode = AIS_WARP_5;
13168                 break;
13169         }
13170         case AIS_WARP_5:
13171                 break;
13172         default:
13173                 Int3();         //      Illegal submode for warping out.
13174         }
13175 }
13176
13177 //      Return object index of weapon that could produce a shockwave that should be known about to *objp.
13178 //      Return nearest one.
13179 int ai_find_shockwave_weapon(object *objp, ai_info *aip)
13180 {
13181         missile_obj     *mo;
13182         float   nearest_dist = 999999.9f;
13183         int     nearest_index = -1;
13184
13185         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
13186                 object          *A;
13187                 weapon          *wp;
13188                 weapon_info     *wip;
13189         
13190                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
13191                 A = &Objects[mo->objnum];
13192
13193                 Assert(A->type == OBJ_WEAPON);
13194                 Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
13195                 wp = &Weapons[A->instance];
13196                 wip = &Weapon_info[wp->weapon_info_index];
13197                 Assert( wip->subtype == WP_MISSILE );
13198
13199                 if (wip->shockwave_speed > 0.0f) {
13200                         float   dist;
13201
13202                         dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13203                         if (dist < nearest_dist) {
13204                                 nearest_dist = dist;
13205                                 nearest_index = mo->objnum;
13206                         }
13207                 }
13208         }
13209
13210         return nearest_index;
13211
13212 }
13213
13214 #define EVADE_SHOCKWAVE_DAMAGE_THRESHOLD                100.0f
13215
13216 //      Tell all ships to avoid a big ship that is blowing up.
13217 //      Only avoid if shockwave is fairly large.
13218 //      OK to tell everyone to avoid.  If they're too far away, that gets cleaned up in the frame interval.
13219 void ai_announce_ship_dying(object *dying_objp)
13220 {
13221         float damage = ship_get_exp_damage(dying_objp);
13222         if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {
13223                 ship_obj        *so;
13224
13225                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13226                         if (Ship_info[Ships[Objects[so->objnum].instance].ship_info_index].flags & (SIF_SMALL_SHIP | SIF_FREIGHTER)) {
13227                                 ai_info *aip;
13228
13229                                 aip = &Ai_info[Ships[Objects[so->objnum].instance].ai_index];
13230
13231                                 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
13232                                         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_SHIP;
13233                                 }
13234                         }
13235                 }
13236         }
13237 }
13238
13239
13240 //      Return object index of weapon that could produce a shockwave that should be known about to *objp.
13241 //      Return nearest one.
13242 int ai_find_shockwave_ship(object *objp, ai_info *aip)
13243 {
13244         ship_obj        *so;
13245         float   nearest_dist = 999999.9f;
13246         int     nearest_index = -1;
13247
13248         for ( so = GET_NEXT(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13249                 object          *A;
13250                 ship                    *shipp;
13251         
13252                 Assert(so->objnum >= 0 && so->objnum < MAX_OBJECTS);
13253                 A = &Objects[so->objnum];
13254
13255                 Assert(A->type == OBJ_SHIP);
13256                 Assert((A->instance >= 0) && (A->instance < MAX_SHIPS));
13257                 shipp = &Ships[A->instance];
13258                 //      Only look at objects in the process of dying.
13259                 if (shipp->flags & SF_DYING) {
13260                         float damage = ship_get_exp_damage(objp);
13261
13262                         if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {               //      Only evade quite large blasts
13263                                 float   dist;
13264
13265                                 dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13266                                 if (dist < nearest_dist) {
13267                                         nearest_dist = dist;
13268                                         nearest_index = so->objnum;
13269                                 }
13270                         }
13271                 }
13272         }
13273
13274         return nearest_index;
13275
13276 }
13277
13278 int aas_1(object *objp, ai_info *aip, vector *safe_pos)
13279 {
13280         // MAKE SURE safe_pos DOES NOT TAKE US TOWARD THE A SHIP WE'RE ATTACKING.
13281         if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_WEAPON) {
13282                 //      If we don't currently know of a weapon to avoid, try to find one.
13283                 //      If we can't find one, then clear the bit so we don't keep coming here.
13284                 if (aip->shockwave_object == -1) {
13285                         int shockwave_weapon = ai_find_shockwave_weapon(objp, aip);
13286                         if (shockwave_weapon == -1) {
13287                                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13288                                 return 0;
13289                         } else {
13290                                 aip->shockwave_object = shockwave_weapon;
13291                         }
13292                 }
13293
13294                 //      OK, we have reason to believe we should avoid aip->shockwave_object.
13295                 Assert(aip->shockwave_object > -1);
13296                 object  *weapon_objp = &Objects[aip->shockwave_object];
13297                 if (weapon_objp->type != OBJ_WEAPON) {
13298                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13299                         aip->shockwave_object = -1;
13300                         return 0;
13301                 }
13302
13303                 weapon  *weaponp = &Weapons[weapon_objp->instance];
13304                 weapon_info     *wip = &Weapon_info[weaponp->weapon_info_index];
13305                 object *target_ship_obj = NULL;
13306
13307                 if (wip->shockwave_speed == 0.0f) {
13308                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13309                         aip->shockwave_object = -1;
13310                         return 0;
13311                 }
13312
13313                 float   danger_dist;
13314                 vector  expected_pos;           //      Position at which we expect the weapon to detonate.
13315                 int             pos_set = 0;
13316
13317                 danger_dist = wip->outer_radius;
13318                 //      Set predicted position of detonation.
13319                 //      If an aspect locked missile, assume it will detonate at the homing position.
13320                 //      If not, which is not possible in a default FreeSpace weapon, then predict it will detonate at some
13321                 //      time in the future, this time based on max lifetime and life left.
13322                 if (wip->wi_flags & WIF_HOMING_ASPECT) {
13323                         expected_pos = weaponp->homing_pos;
13324                         if (weaponp->homing_object && weaponp->homing_object->type == OBJ_SHIP) {
13325                                 target_ship_obj = weaponp->homing_object;
13326                         }
13327                         pos_set = 1;
13328                         if (IS_VEC_NULL(&weaponp->homing_pos)) {
13329                                 pos_set = 0;
13330                                 if (weaponp->target_num != -1) {
13331                                         if (Objects[weaponp->target_num].type == OBJ_SHIP) {
13332                                                 target_ship_obj = &Objects[weaponp->target_num];
13333                                                 expected_pos = target_ship_obj->pos;
13334                                                 pos_set = 1;
13335                                         }
13336                                 }
13337                         }
13338                 }
13339
13340                 if (!pos_set) {
13341                         float   time_scale;
13342
13343                         if (wip->lifetime - weaponp->lifeleft > 5.0f) {
13344                                 time_scale = 1.0f;
13345                         } else {
13346                                 time_scale = weaponp->lifeleft/2.0f;
13347                         }
13348
13349                         vm_vec_scale_add(&expected_pos, &weapon_objp->pos, &weapon_objp->orient.v.fvec, time_scale);
13350                 }
13351
13352                 //      See if too far away to care about shockwave.
13353                 if (vm_vec_dist_quick(&objp->pos, &expected_pos) > danger_dist*2.0f) {
13354                         //aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13355                         return 0;
13356                 } else {
13357                         // try to find a safe position
13358                         vector vec_from_exp;
13359                         float dir = 1.0f;
13360                         vm_vec_sub(&vec_from_exp, &objp->pos, &expected_pos);
13361                         float dot = vm_vec_dotprod(&vec_from_exp, &weapon_objp->orient.v.fvec);
13362                         if (dot > -30) {
13363                                 // if we're already on the other side of the explosion, don't try to fly behind it
13364                                 dir = -1.0f;
13365                         }
13366
13367                         //      Fly towards a point behind the weapon.
13368                         vm_vec_scale_add(safe_pos, &weapon_objp->pos, &weapon_objp->orient.v.fvec, -50000.0f*dir);
13369
13370                         // verify safe_pos will not make us collide with our target objnum, else try 2 other vecs
13371                         // don't bang your head, else go
13372 //                      int go_safe = FALSE;
13373                         int go_safe = TRUE;
13374 /*                      if (target_ship_obj) {
13375                                 if (pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius)) {
13376                                         // try up to 2 other random directions
13377                                         vector dir_vec, rand_vec;
13378                                         int idx;
13379                                         for (idx=0; idx<2; idx++) {
13380                                                 vm_vec_rand_vec_quick(&rand_vec);
13381                                                 vm_vec_scale_add(&dir_vec, &weapon_objp->orient.v.fvec, &rand_vec, 0.5f);
13382                                                 vm_vec_scale_add(safe_pos, &weapon_objp->pos, &dir_vec, -50000.0f*dir);
13383                                                 if ( !pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius) ) {
13384                                                         go_safe = TRUE;
13385                                                         break;
13386                                                 }
13387                                         }
13388                                 } else { // direct path is safe
13389                                         go_safe = TRUE;
13390                                 }
13391                         } else { // no target_obj_ship
13392                                 go_safe = TRUE;
13393                         } */
13394
13395                         if (go_safe) {
13396                                 return 1;
13397                         } else {
13398                                 // can't figure out a good way to go
13399                                 return 0;
13400                         }
13401                 }
13402         } else if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_SHIP) {
13403                 if (aip->shockwave_object == -1) {
13404                         int shockwave_ship = ai_find_shockwave_ship(objp, aip);
13405                         if (shockwave_ship == -1) {
13406                                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13407                                 return 0;
13408                         } else {
13409                                 aip->shockwave_object = shockwave_ship;
13410                         }
13411                 }
13412
13413                 Assert(aip->shockwave_object > -1);
13414                 object  *ship_objp = &Objects[aip->shockwave_object];
13415                 if (ship_objp == objp) {
13416                         aip->shockwave_object = -1;
13417                         return 0;
13418                 }
13419
13420                 if (ship_objp->type != OBJ_SHIP) {
13421                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13422                         return 0;
13423                 }
13424
13425                 //      Optimize note! Don't really have to normalize.  We only need a point away from the blowing-up ship.
13426                 vector safe_vec;
13427
13428                 vm_vec_normalized_dir(&safe_vec, &objp->pos, &ship_objp->pos);
13429                 vm_vec_scale_add(safe_pos, &ship_objp->pos, &safe_vec, 50000.0f);       //      Fly away from the ship.
13430
13431                 float outer_rad = ship_get_exp_outer_rad(ship_objp);
13432
13433                 if (vm_vec_dist_quick(&objp->pos, &ship_objp->pos) > outer_rad*1.5f) {
13434                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13435                         return 0;
13436                 }
13437
13438                 return 1;
13439
13440         } else {
13441                 Int3(); //      Illegal -- supposedly avoiding a shockwave, but neither ship nor weapon.  What is it!?
13442         }
13443
13444         return 0;
13445 }
13446
13447 /*
13448 int rct_done = 0;
13449
13450 void rand_chance_test()
13451 {
13452         int     i;
13453         float   frametime;
13454
13455         if (rct_done)
13456                 return;
13457
13458         rct_done = 1;
13459
13460         for (frametime=0.02f; frametime<0.25f; frametime *= 1.25f) {
13461                 float   chance;
13462
13463                 nprintf(("AI", "%6.4f: ", frametime));
13464                 for (chance=0.25f; chance<2.5f; chance += 0.25f) {
13465                         int count = 0;
13466
13467                         for (i=0; i<100.0f/frametime; i++) {
13468                                 if (rand_chance(frametime, chance))
13469                                         count++;
13470                         }
13471                         nprintf(("AI", "%3i ", count));
13472                 }
13473                 nprintf(("AI", "\n"));
13474         }
13475 }
13476 */
13477
13478 //      --------------------------------------------------------------------------
13479 //      Make object *objp avoid the nearest dangerous shockwave-producing weapon.
13480 //      If it looks like there is no valid shockwave-producing weapon then clear the AIF_AVOID_SHOCKWAVE_WEAPON bit in ai_flags and return.
13481 //      Return 1 if avoiding a shockwave, else return 0.
13482 int ai_avoid_shockwave(object *objp, ai_info *aip)
13483 {
13484         vector  safe_pos;
13485
13486         //rand_chance_test();
13487         // BIG|HUGE do not respond to shockwaves
13488         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
13489                 // don't come here again
13490                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE;
13491                 return 0;
13492         }
13493
13494         //      Don't all react right away.
13495         if (!(aip->ai_flags & AIF_AVOID_SHOCKWAVE_STARTED))
13496                 if (!rand_chance(flFrametime, (float) aip->ai_class/4.0f + 0.25f))      //      Chance to avoid in 1 second is 0.25 + ai_class/4
13497                         return 0;
13498
13499         if (!aas_1(objp, aip, &safe_pos)) {
13500                 aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13501                 return 0;
13502         }
13503
13504         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13505
13506         //      OK, evade the shockwave!
13507         turn_towards_point(objp, &safe_pos, NULL, 0.0f);
13508         vector  vec_to_safe_pos;
13509         float           dot_to_goal;
13510
13511         vm_vec_normalized_dir(&vec_to_safe_pos, &safe_pos, &objp->pos);
13512
13513         dot_to_goal = vm_vec_dot(&objp->orient.v.fvec, &vec_to_safe_pos);
13514         if (dot_to_goal < -0.5f)
13515                 accelerate_ship(aip, 0.3f);
13516         else {
13517                 accelerate_ship(aip, 1.0f + dot_to_goal);
13518                 if (dot_to_goal > 0.2f) {
13519                         if (!(objp->phys_info.flags & PF_AFTERBURNER_ON )) {
13520                                 afterburners_start(objp);
13521                                 aip->afterburner_stop_time = Missiontime + 2*F1_0;
13522                         }
13523                 }
13524         }
13525
13526         return 1;
13527 }
13528
13529 //      Awaiting repair.  Be useful.
13530 //      Probably fly towards incoming repair ship.
13531 //      Return true if this ship is close to being repaired, else return false.
13532 int ai_await_repair_frame(object *objp, ai_info *aip)
13533 {
13534         if (!(aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)))
13535                 return 0;
13536
13537         if (aip->dock_objnum == -1)
13538                 return 0;
13539
13540         ship    *shipp;
13541         ship_info       *sip;
13542
13543         shipp = &Ships[Objects[aip->dock_objnum].instance];
13544         sip = &Ship_info[shipp->ship_info_index];
13545
13546         aip->ai_flags &= ~AIF_FORMATION_OBJECT; //      Prevents endless rotation.
13547
13548         if (!(sip->flags & SIF_SUPPORT))
13549                 return 0;
13550
13551         vector  goal_point;
13552         object  *repair_objp;
13553
13554         repair_objp = &Objects[aip->dock_objnum];
13555
13556         if (Ships[repair_objp->instance].team == TEAM_TRAITOR) {
13557                 ai_abort_rearm_request(repair_objp);
13558                 return 0;
13559         }
13560
13561         vm_vec_scale_add(&goal_point, &repair_objp->pos, &repair_objp->orient.v.uvec, -50.0f);  //      Fly towards point below repair ship.
13562
13563         vector  vtr;
13564         float dist = vm_vec_normalized_dir(&vtr, &goal_point, &objp->pos);
13565         float dot = vm_vec_dot(&vtr, &objp->orient.v.fvec);
13566
13567         if (dist > 200.0f) {
13568                 //nprintf(("AI", "%s flying towards %s for repair, dist = %7.3f\n", Ships[objp->instance].ship_name, &Ships[repair_objp->instance].ship_name, dist));
13569                 accelerate_ship(aip, (0.9f + dot) * dist/1500.0f);
13570                 turn_towards_point(objp, &goal_point, NULL, 0.0f);
13571         } else {
13572                 accelerate_ship(aip, 0.0f);
13573                 //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));
13574         }
13575
13576         return 1;
13577 }
13578
13579 //      Maybe cause this ship to self-destruct.
13580 //      Currently, any small ship (SIF_SMALL_SHIP) that has been disabled will self-destruct after awhile.
13581 //      Maybe should only do this if they are preventing their wing from re-entering.
13582 void ai_maybe_self_destruct(object *objp, ai_info *aip)
13583 {
13584         //      Friendly ships can be repaired, so no self-destruct.
13585         //      In multiplayer, just don't self-destruct.  I figured there would be a problem. -- MK, 3/19/98.
13586         if ((Ships[objp->instance].team == TEAM_FRIENDLY) || (Game_mode & GM_MULTIPLAYER))
13587                 return;
13588
13589         //      Small ships in a wing blow themselves up after awhile if engine or weapons system has been destroyed.
13590         //      Reason: Don't want them to prevent a re-emergence of the wing.
13591         //      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
13592         //      mission would be broken.
13593         if ((Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP) && (Ships[objp->instance].wingnum != -1)) {
13594                 if ((ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) ||
13595                         (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_WEAPONS) <= 0.0f)) {
13596                         if (aip->self_destruct_timestamp < 0)
13597                                 aip->self_destruct_timestamp = timestamp(90 * 1000);    //      seconds until self-destruct
13598                 } else {
13599                         aip->self_destruct_timestamp = -1;
13600                 }
13601
13602                 if (aip->self_destruct_timestamp < 0) {
13603                         return;
13604                 }
13605
13606                 if (timestamp_elapsed(aip->self_destruct_timestamp)) {
13607                         ship_apply_local_damage( objp, objp, &objp->pos, objp->hull_strength*flFrametime + 1.0f, MISS_SHIELDS);
13608                 }
13609         }
13610 }
13611
13612 // Determine if pl_objp needs a new target, called from ai_frame()
13613 int ai_need_new_target(object *pl_objp, int target_objnum)
13614 {
13615         object *objp;
13616
13617         if ( target_objnum < 0 ) {
13618                 return 1;
13619         }
13620
13621         objp = &Objects[target_objnum];
13622
13623         if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID) && (objp->type != OBJ_WEAPON) ) {
13624                 return 1;
13625         }
13626
13627         if ( objp->type == OBJ_SHIP ) {
13628                 if ( Ships[objp->instance].flags & SF_DYING ) {
13629                         return 1;
13630                 } else if (Ships[objp->instance].team == Ships[pl_objp->instance].team)
13631                         return 1;
13632         }
13633
13634         return 0;
13635 }
13636
13637 //      If *objp is recovering from a collision with a big ship, handle it.
13638 //      Return true if recovering.
13639 int maybe_big_ship_collide_recover_frame(object *objp, ai_info *aip)
13640 {
13641         float   dot, dist;
13642         vector  v2g;
13643         
13644         if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1) {
13645                 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);
13646                 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_1, &objp->pos);
13647                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
13648                 accelerate_ship(aip, dot);
13649
13650                 //      If close to desired point, or 15+ seconds since entered this mode, continue to next mode.
13651                 if ((timestamp_until(aip->big_recover_timestamp) < -15*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13652                         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_1;
13653                         aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13654                 }
13655
13656                 return 1;
13657
13658         } else if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_2) {
13659                 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);
13660                 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_2, &objp->pos);
13661                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
13662                 accelerate_ship(aip, dot);
13663
13664                 //      If close to desired point, or 30+ seconds since started avoiding collision, done avoiding.
13665                 if ((timestamp_until(aip->big_recover_timestamp) < -30*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13666                         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13667                         aip->ai_flags &= ~AIF_TARGET_COLLISION;
13668                 }
13669
13670                 return 1;
13671         }
13672
13673         if (aip->ai_flags & AIF_TARGET_COLLISION) {
13674                 aip->ai_flags &= ~AIF_TARGET_COLLISION;
13675         }
13676         return 0;
13677 }
13678
13679 void validate_mode_submode(ai_info *aip)
13680 {
13681         switch (aip->mode) {
13682         case AIM_CHASE:
13683                 // check valid submode
13684                 switch (aip->submode) {
13685                 case SM_CONTINUOUS_TURN:
13686                 case SM_ATTACK:
13687                 case SM_EVADE_SQUIGGLE:
13688                 case SM_EVADE_BRAKE:    
13689                 case SM_EVADE:          
13690                 case SM_SUPER_ATTACK:
13691                 case SM_AVOID:  
13692                 case SM_GET_BEHIND:
13693                 case SM_GET_AWAY:               
13694                 case SM_EVADE_WEAPON:
13695                 case SM_FLY_AWAY:       
13696                 case SM_ATTACK_FOREVER:
13697                         break;
13698                 default:
13699                         Int3();
13700                 }
13701                 break;
13702
13703         case AIM_STRAFE:
13704                 // check valid submode
13705                 switch(aip->submode) {
13706                 case AIS_STRAFE_ATTACK:
13707                 case AIS_STRAFE_AVOID:
13708                 case AIS_STRAFE_RETREAT1:
13709                 case AIS_STRAFE_RETREAT2:
13710                 case AIS_STRAFE_POSITION:
13711                         break;
13712                 default:
13713                         Int3();
13714                 }
13715                 break;
13716         }
13717 }
13718
13719 //      --------------------------------------------------------------------------
13720 // Process AI object "objnum".
13721 void ai_frame(int objnum)
13722 {
13723         ship            *shipp = &Ships[Objects[objnum].instance];
13724         ai_info *aip = &Ai_info[shipp->ai_index];
13725         int             target_objnum;
13726
13727 //      validate_mode_submode(aip);
13728
13729         Assert((aip->mode != AIM_WAYPOINTS) || (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC));
13730
13731         // Set globals defining the current object and its enemy object.
13732         Pl_objp = &Objects[objnum];
13733
13734         if (aip->mode == AIM_WARP_OUT) {
13735                 ai_warp_out(Pl_objp);
13736                 return;
13737         }
13738
13739 /*      //      HACK! TEST! REMOVE ME!
13740         if (Ship_info[shipp->ship_info_index].flags & SIF_BIG_SHIP)
13741                 if (shipp->team == Player_ship->team)
13742                         aip->mode = AIM_CHASE;
13743 */
13744
13745 //      if (!strnicmp(Ships[Pl_objp->instance].ship_name, "cancer", 6))
13746 //              nprintf(("AI", "Ship %s: mode = %s, submode = %i\n", Ships[Pl_objp->instance].ship_name, Mode_text[aip->mode], aip->submode));
13747
13748         ai_maybe_self_destruct(Pl_objp, aip);
13749
13750 //      if ( timestamp_elapsed(aip->goal_check_time) ) {
13751                 ai_process_mission_orders( objnum, aip );
13752 //              aip->goal_check_time = timestamp_rand(1000,2000);
13753 //      }
13754
13755         //      Avoid a shockwave, if necessary.  If a shockwave and rearming, stop rearming.
13756         if (aip->ai_flags & AIF_AVOID_SHOCKWAVE) {
13757                 if (ai_avoid_shockwave(Pl_objp, aip)) {
13758                         aip->ai_flags &= ~(AIF_BIG_SHIP_COLLIDE_RECOVER_1 | AIF_BIG_SHIP_COLLIDE_RECOVER_2);
13759                         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
13760                                 ai_abort_rearm_request(Pl_objp);
13761                         return;
13762                 }
13763         } else {
13764                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_STARTED;
13765         }
13766
13767         // moved call to ai_do_repair frame here from below because of the subsequent if statment returning
13768         // if the ship is getting repaired
13769         //      If waiting to be repaired, just stop and sit.
13770         ai_do_repair_frame(Pl_objp, aip, flFrametime);
13771         if ((aip->ai_flags & AIF_AWAITING_REPAIR) || (aip->ai_flags & AIF_BEING_REPAIRED)) {
13772                 if (ai_await_repair_frame(Pl_objp, aip))
13773                         return;
13774         }
13775
13776         if (aip->mode == AIM_PLAY_DEAD)
13777                 return;
13778
13779         //      If recovering from a collision with a big ship, don't continue.
13780         if (maybe_big_ship_collide_recover_frame(Pl_objp, aip))
13781                 return;
13782
13783         ai_preprocess_ignore_objnum(Pl_objp, aip);
13784         target_objnum = set_target_objnum(aip, aip->target_objnum);
13785
13786         // 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));
13787
13788         Assert(objnum != target_objnum);
13789
13790         ai_manage_shield(Pl_objp, aip);
13791         
13792         if ( maybe_request_support(Pl_objp) ) {
13793                 if ( Ships[Pl_objp->instance].flags & SF_FROM_PLAYER_WING ) {
13794                         ship_maybe_tell_about_rearm(shipp);
13795                 }
13796         }
13797
13798         ai_maybe_warp_out(Pl_objp);
13799
13800 /*
13801         //      If this ship is attacking an object's subsystems and someone else destroyed
13802         //      the subsystem, it could continue attacking the ship.  Need to invalidate the objnum.
13803         if (target_objnum >= 0)
13804                 if (Objects[target_objnum].flags & OF_PROTECTED) {
13805                         // if (aip->targeted_subsys != NULL)
13806                         //      ; //nprintf(("AI", "subsys hits = %7.3f\n", aip->targeted_subsys->current_hits));
13807
13808                         if ((aip->targeted_subsys == NULL) || (aip->targeted_subsys->current_hits <= 0.0f)) {
13809                                 target_objnum = -1;
13810                                 aip->target_objnum = -1;
13811                         }
13812                 }
13813 */
13814
13815
13816         //      Find an enemy if don't already have one.
13817         En_objp = NULL;
13818         if ( ai_need_new_target(Pl_objp, target_objnum) ) {
13819                 if ((aip->mode != AIM_EVADE_WEAPON) && (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
13820                         aip->resume_goal_time = -1;
13821                         aip->active_goal = AI_GOAL_NONE;
13822                 } else if (aip->resume_goal_time == -1) {
13823                         // AL 12-9-97: Don't allow cargo and navbuoys to set their aip->target_objnum
13824                         if ( !(Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS) ) {
13825                                 target_objnum = find_enemy(objnum, MAX_ENEMY_DISTANCE, Skill_level_max_attackers[Game_skill_level]);            //      Attack up to 25K units away.
13826                                 if (target_objnum != -1) {
13827                                         if (aip->target_objnum != target_objnum)
13828                                                 aip->aspect_locked_time = 0.0f;
13829                                         set_target_objnum(aip, target_objnum);
13830                                         En_objp = &Objects[target_objnum];
13831                                 }
13832                         }
13833                 }
13834         } else if (target_objnum >= 0) {
13835                 En_objp = &Objects[target_objnum];
13836         }
13837
13838         // set base stealth info each frame
13839         aip->ai_flags &= ~AIF_STEALTH_PURSIUT;
13840         if (En_objp && En_objp->type == OBJ_SHIP) {
13841                 if (Ship_info[Ships[En_objp->instance].ship_info_index].flags & SIF_STEALTH) {
13842                         int stealth_state = ai_is_stealth_visible(Pl_objp, En_objp);
13843                         float dist = vm_vec_dist_quick(&En_objp->pos, &Pl_objp->pos);
13844
13845                         if (stealth_state != STEALTH_FULLY_TARGETABLE) {
13846                                 aip->ai_flags |= AIF_STEALTH_PURSIUT;
13847                         }
13848
13849                         if ( (stealth_state == STEALTH_FULLY_TARGETABLE) || (stealth_state == STEALTH_VISIBLE) ) {
13850                                 aip->stealth_last_visible_stamp = timestamp();
13851                                 aip->stealth_last_cheat_visible_stamp = aip->stealth_last_visible_stamp;
13852                                 aip->stealth_last_pos = En_objp->pos;
13853                                 aip->stealth_velocity = En_objp->phys_info.vel;
13854                         } else if (dist < 100) {
13855                                 // get cheat timestamp
13856                                 aip->stealth_last_cheat_visible_stamp = timestamp();
13857
13858                                 // set approximate pos and vel, with increasing error as time from last_visible_stamp increases
13859                                 update_ai_stealth_info_with_error(aip/*, 0*/);
13860                         }
13861                 }
13862         }
13863
13864         /*      if ((Pl_objp != NULL) && (En_objp != NULL)) {
13865                 slide_face_ship();
13866                 return;
13867         }
13868 */
13869         // AL 12-10-97: ensure that cargo and navbuoys aip->target_objnum is always -1.
13870         if ( Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS ) {
13871                 aip->target_objnum = -1;
13872         }
13873
13874         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)) {
13875                 mprintf(("Warning: Object and its enemy have same position.  Object #%i\n", Pl_objp-Objects));
13876                 En_objp = NULL;
13877         }
13878
13879         if (aip->mode == AIM_CHASE) {
13880                 if (En_objp == NULL) {
13881                         aip->active_goal = -1;
13882                 }
13883         }
13884
13885         //      If there is a goal to resume and enough time has elapsed, resume the goal.
13886         if ((aip->resume_goal_time > 0) && (aip->resume_goal_time < Missiontime)) {
13887                 aip->active_goal = AI_GOAL_NONE;
13888                 aip->resume_goal_time = -1;
13889                 target_objnum = find_enemy(objnum, 2000.0f, Skill_level_max_attackers[Game_skill_level]);
13890                 if (target_objnum != -1) {
13891                         if (aip->target_objnum != target_objnum) {
13892                                 aip->aspect_locked_time = 0.0f;
13893                         }
13894                         set_target_objnum(aip, target_objnum);
13895                 }
13896         }
13897
13898         // check if targeted subsystem has been destroyed, if so, move onto another subsystem
13899         // if trying to disable or disarm the target
13900         if ((En_objp != NULL) && ( aip->targeted_subsys != NULL )) {
13901                 Assert(En_objp->type == OBJ_SHIP);
13902                 if ( aip->targeted_subsys->current_hits <= 0.0f ) {
13903                         int subsys_type;
13904
13905                         if ( aip->goals[0].ai_mode == AI_GOAL_DISABLE_SHIP ) {
13906                                 subsys_type = SUBSYSTEM_ENGINE;
13907                         } else if ( aip->goals[0].ai_mode == AI_GOAL_DISARM_SHIP ) {
13908                                 subsys_type = SUBSYSTEM_TURRET;
13909                         } else {
13910                                 subsys_type = -1;
13911                         }
13912
13913                         if ( subsys_type != -1 ) {
13914                                 ship_subsys *new_subsys;
13915                                 new_subsys = ship_return_next_subsys(&Ships[En_objp->instance], subsys_type, &Pl_objp->pos);
13916                                 if ( new_subsys != NULL ) {
13917                                         set_targeted_subsys(aip, new_subsys, aip->target_objnum);
13918                                 } else {
13919                                         // AL 12-16-97: no more subsystems to attack... reset targeting info
13920                                         aip->target_objnum = -1;
13921                                         set_targeted_subsys(aip, NULL, -1);
13922                                 }
13923                         } else {
13924                                 // targeted subsys is destroyed, so stop attacking it
13925                                 set_targeted_subsys(aip, NULL, -1);
13926                         }
13927                 }
13928         }
13929
13930         ai_maybe_launch_cmeasure(Pl_objp, aip);
13931         ai_maybe_evade_locked_missile(Pl_objp, aip);
13932
13933         aip->target_time += flFrametime;
13934
13935         int in_formation = 0;
13936         if (aip->ai_flags & AIF_FORMATION) {
13937                 in_formation = !ai_formation();
13938         }
13939
13940         if ( !in_formation ) {
13941                 ai_execute_behavior(aip);
13942         }
13943
13944         process_subobjects(objnum);
13945         maybe_resume_previous_mode(Pl_objp, aip);
13946         
13947         if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON ) {
13948                 if (Missiontime > aip->afterburner_stop_time) {
13949                         //nprintf(("AI", "Frame %i, turning off afterburner.\n", AI_FrameCount));
13950                         afterburners_stop(Pl_objp);
13951                 }
13952         }
13953 //      validate_mode_submode(aip);
13954 }
13955
13956 int Waypoints_created = 0;
13957
13958 //      Find the ship with the name *name in the Ship_info array.
13959 int find_ship_name(char *name)
13960 {
13961         int     i;
13962
13963         for (i=0; i<Num_ship_types; i++)
13964                 if (!strcmp(Ship_info[i].name, name))
13965                         return i;
13966
13967         return -1;
13968 }
13969
13970 void create_waypoints()
13971 {
13972         int     i, j, z;
13973
13974         // Waypoints_created = 1;
13975
13976         if (Waypoints_created)
13977                 return;
13978
13979         for (j=0; j<Num_waypoint_lists; j++)
13980                 for (i=0; i<Waypoint_lists[j].count; i++) {
13981                         z = obj_create(OBJ_WAYPOINT, 0, j * 65536 + i, NULL,
13982                                 &Waypoint_lists[j].waypoints[i], 0.0f, OF_RENDERS);
13983                 }
13984
13985         Waypoints_created = 1;
13986 }
13987
13988 int Last_ai_obj = -1;
13989
13990 void ai_process( object * obj, int ai_index, float frametime )
13991 {
13992 //      if (Ships[obj->instance].flags & SF_DYING)
13993 //              nprintf(("AI", "Frame: %i Ship %s is dying!\n", Framecount, Ships[obj->instance].ship_name));
13994
13995         if (obj->flags & OF_SHOULD_BE_DEAD)
13996                 return;
13997
13998         // 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.
13999         if ((Ships[obj->instance].flags & SF_DYING ) && !(Ship_info[Ships[obj->instance].ship_info_index].flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP))){
14000                 return;
14001         }
14002
14003         int rfc = 1;            //      Assume will be Reading Flying Controls.
14004
14005         Assert( obj->type == OBJ_SHIP );
14006         Assert( ai_index >= 0 );
14007
14008         init_ship_info();
14009
14010         create_waypoints();
14011
14012         AI_frametime = frametime;
14013         if (obj-Objects <= Last_ai_obj) {
14014                 AI_FrameCount++;
14015         }
14016
14017         memset( &AI_ci, 0, sizeof(AI_ci) );
14018
14019         ai_frame(obj-Objects);
14020
14021         AI_ci.pitch = 0.0f;
14022         AI_ci.bank = 0.0f;
14023         AI_ci.heading = 0.0f;
14024
14025         // the ships maximum velocity now depends on the energy flowing to engines
14026         obj->phys_info.max_vel.xyz.z = Ships[obj->instance].current_max_speed;
14027         ai_info *aip = &Ai_info[Ships[obj->instance].ai_index];
14028
14029         //      In certain circumstances, the AI says don't fly in the normal way.
14030         //      One circumstance is in docking and undocking, when the ship is moving
14031         //      under thruster control.
14032         switch (aip->mode) {
14033         case AIM_DOCK:
14034                 if ((aip->submode >= AIS_DOCK_2) && (aip->submode != AIS_UNDOCK_3))
14035                         rfc = 0;
14036                 break;
14037         case AIM_WARP_OUT:
14038                 if (aip->submode >= AIS_WARP_3)
14039                         rfc = 0;
14040                 break;
14041 //      case AIM_NONE:
14042 //              if (aip->submode == AIS_NONE_FORMATION)
14043 //                      rfc = 0;
14044 //              break;
14045         default:
14046                 break;
14047         }
14048
14049         if (rfc == 1) {
14050                 vector copy_desired_rotvel = obj->phys_info.rotvel;
14051                 physics_read_flying_controls( &obj->orient, &obj->phys_info, &AI_ci, frametime);
14052                 // if obj is in formation and not flight leader, don't update rotvel
14053                 if (aip->ai_flags & AIF_FORMATION) {
14054                         if (&Objects[aip->goal_objnum] != obj) {
14055                                 obj->phys_info.desired_rotvel = copy_desired_rotvel;
14056                                 obj->phys_info.rotvel = copy_desired_rotvel;
14057                         }
14058                 }
14059         }
14060
14061         Last_ai_obj = obj-Objects;
14062 }
14063
14064 //      Initialize ai_info struct of object objnum.
14065 void init_ai_object(int objnum)
14066 {
14067         int     ship_index, ai_index;
14068         ai_info *aip;
14069         int ship_type;
14070         object  *objp;
14071         vector  near_vec;                       //      A vector nearby and mainly in front of this object.
14072
14073         objp = &Objects[objnum];
14074         ship_index = objp->instance;
14075         ai_index = Ships[ship_index].ai_index;
14076         Assert((ai_index >= 0) && (ai_index < MAX_AI_INFO));
14077
14078         aip = &Ai_info[ai_index];
14079
14080         ship_type = Ships[ship_index].ship_info_index;
14081
14082         vm_vec_scale_add(&near_vec, &objp->pos, &objp->orient.v.fvec, 100.0f);
14083         vm_vec_scale_add2(&near_vec, &objp->orient.v.rvec, 10.0f);
14084
14085         // Things that shouldn't have to get initialized, but initialize them just in case!
14086         aip->ai_flags = 0;
14087         aip->previous_mode = AIM_NONE;
14088         aip->mode_time = -1;
14089         aip->target_objnum = -1;
14090         aip->target_signature = -1;
14091         aip->previous_target_objnum = -1;
14092         aip->target_time = 0.0f;
14093         aip->enemy_wing = -1;
14094         aip->attacker_objnum = -1;
14095         aip->goal_objnum = -1;
14096         aip->goal_signature = -1;
14097         aip->guard_objnum = -1;
14098         aip->guard_signature = -1;
14099         aip->guard_wingnum = -1;
14100         aip->dock_signature = -1;
14101         aip->submode = 0;
14102         aip->previous_submode = 0;
14103         aip->best_dot_to_enemy = -1.0f;
14104         aip->best_dot_from_enemy = -1.0f;
14105         aip->best_dot_to_time = 0;
14106         aip->best_dot_from_time = 0;
14107         aip->submode_start_time = 0;
14108         aip->submode_parm0 = 0;
14109         aip->active_goal = -1;
14110         aip->goal_check_time = timestamp(0);
14111         aip->last_predicted_enemy_pos = near_vec;
14112         aip->prev_goal_point = near_vec;
14113         aip->goal_point = near_vec;
14114         aip->time_enemy_in_range = 0.0f;
14115         aip->last_attack_time = 0;
14116         aip->last_hit_time = 0;
14117         aip->last_hit_quadrant = 0;
14118         aip->hitter_objnum = -1;
14119         aip->hitter_signature = -1;
14120         aip->resume_goal_time = -1;
14121         aip->prev_accel = 0.0f;
14122         aip->prev_dot_to_goal = 0.0f;
14123
14124         aip->ignore_objnum = UNUSED_OBJNUM;
14125         aip->ignore_signature = -1;
14126
14127         // aip->mode = AIM_NONE;
14128
14129         // End of Things that shouldn't have to get initialized, but initialize them just in case!
14130
14131         aip->ai_courage = Ai_classes[Ship_info[ship_type].ai_class].ai_courage[Game_skill_level];
14132         aip->ai_patience = Ai_classes[Ship_info[ship_type].ai_class].ai_patience[Game_skill_level];
14133         aip->ai_evasion = Ai_classes[Ship_info[ship_type].ai_class].ai_evasion[Game_skill_level];
14134         aip->ai_accuracy = Ai_classes[Ship_info[ship_type].ai_class].ai_accuracy[Game_skill_level];
14135
14136         if (Num_waypoint_lists > 0) {
14137                 aip->wp_index = -1;
14138                 aip->wp_list = -1;
14139         } else {
14140                 aip->wp_index = -1;
14141                 aip->wp_list = -1;
14142         }
14143
14144         aip->attacker_objnum = -1;
14145         aip->goal_signature = -1;
14146
14147         Objects[objnum].phys_info.prev_fvec = Objects[objnum].orient.v.fvec;
14148
14149         aip->last_predicted_enemy_pos.xyz.x = 0.0f;     //      Says this value needs to be recomputed!
14150         aip->time_enemy_in_range = 0.0f;
14151
14152         aip->resume_goal_time = -1;                                     //      Say there is no goal to resume.
14153
14154         aip->active_goal = -1;
14155         aip->path_start = -1;
14156         aip->path_goal_dist = -1;
14157         aip->path_length = 0;
14158         aip->path_subsystem_next_check = 1;
14159         aip->dock_path_index = -1;
14160         aip->dock_index = -1;
14161         aip->dock_objnum = -1;
14162
14163         aip->danger_weapon_objnum = -1;
14164         aip->danger_weapon_signature = -1;
14165
14166         aip->lead_scale = 0.0f;
14167         aip->last_hit_target_time = Missiontime;
14168
14169         aip->nearest_locked_object = -1;
14170         aip->nearest_locked_distance = 99999.0f;
14171
14172         aip->targeted_subsys = NULL;
14173         aip->last_subsys_target = NULL;
14174         aip->targeted_subsys_parent = -1;
14175
14176         // The next two fields are used to time the rearming to allow useful sound effects for missile rearming
14177         aip->rearm_first_missile = TRUE;                //      flag to indicate that next missile to load is the first missile
14178         aip->rearm_release_delay = 0;                   //      timestamp to delay the separation of docked ships after rearm
14179
14180         aip->next_predict_pos_time = 0;
14181
14182         aip->afterburner_stop_time = 0;
14183         aip->last_objsig_hit = -1;                              // object signature of the ship most recently hit by aip
14184
14185         aip->path_next_create_time = timestamp(1);
14186         aip->path_create_pos = Objects[objnum].pos;
14187         aip->path_create_orient = Objects[objnum].orient;
14188
14189         aip->ignore_expire_timestamp = timestamp(1);
14190         aip->warp_out_timestamp = 0;
14191         aip->next_rearm_request_timestamp = timestamp(1);
14192         aip->primary_select_timestamp = timestamp(1);
14193         aip->secondary_select_timestamp = timestamp(1);
14194         aip->scan_for_enemy_timestamp = timestamp(1);
14195
14196         aip->choose_enemy_timestamp = timestamp(3*(NUM_SKILL_LEVELS-Game_skill_level) * ((rand_alt() % 500) + 500));
14197
14198         aip->shockwave_object = -1;
14199         aip->shield_manage_timestamp = timestamp(1);
14200         aip->self_destruct_timestamp = -1;      //      This is a flag that we have not yet set this.
14201         aip->ok_to_target_timestamp = timestamp(1);
14202         aip->pick_big_attack_point_timestamp = timestamp(1);
14203         vm_vec_zero(&aip->big_attack_point);
14204
14205         aip->avoid_check_timestamp = timestamp(1);
14206
14207         aip->abort_rearm_timestamp = -1;
14208
14209         // artillery stuff
14210         aip->artillery_objnum = -1;
14211         aip->artillery_sig = -1;        
14212
14213         // waypoint speed cap
14214         aip->waypoint_speed_cap = -1;
14215
14216         // set lethality to enemy team
14217         aip->lethality = 0.0f;
14218 }
14219
14220 void init_ai_objects()
14221 {
14222         int     i;
14223
14224         for (i=0; i<num_objects; i++){
14225                 if (Objects[i].type == OBJ_SHIP){
14226                         init_ai_object(i);
14227                 }
14228         }
14229 }
14230
14231 void init_ai_system()
14232 {
14233         // MWA -- removed next line of code on 11/12/97.  When a ship is created
14234         // it calls init_ai_object() on it's objnum.  Doing this init at the point where
14235         // this function gets called messes things up.
14236         //init_ai_objects();
14237
14238         Ppfp = Path_points;
14239         Waypoints_created = 0;
14240
14241         Dock_path_warning_given = 0;
14242
14243 /*      for (int i=0; i<MAX_IGNORE_OBJECTS; i++) {
14244                 Ignore_objects[i].objnum = -1;
14245                 Ignore_objects[i].signature = -1;
14246         }
14247 */
14248
14249 }
14250
14251 void ai_set_default_behavior(object *obj, int classnum)
14252 {
14253         ai_info *aip;
14254
14255         Assert(obj != NULL);
14256         Assert(obj->instance != -1);
14257         Assert(Ships[obj->instance].ai_index != -1);
14258
14259         aip = &Ai_info[Ships[obj->instance].ai_index];
14260
14261         aip->behavior = classnum;
14262
14263 }
14264
14265 void ai_do_default_behavior(object *obj)
14266 {
14267         ai_info *aip;
14268         int             ship_flags;
14269
14270         Assert(obj != NULL);
14271         Assert(obj->instance != -1);
14272         Assert(Ships[obj->instance].ai_index != -1);
14273
14274         aip = &Ai_info[Ships[obj->instance].ai_index];
14275
14276         ship_flags = Ship_info[Ships[obj->instance].ship_info_index].flags;
14277         if (!is_instructor(obj) && (ship_flags & (SIF_FIGHTER | SIF_BOMBER))) {
14278                 int enemy_objnum = find_enemy(OBJ_INDEX(obj), 1000.0f, Skill_level_max_attackers[Game_skill_level]);
14279                 set_target_objnum(aip, enemy_objnum);
14280                 aip->mode = AIM_CHASE;
14281                 aip->submode = SM_ATTACK;
14282         } else if (ship_flags & (SIF_SUPPORT)) {
14283                 aip->mode = AIM_SAFETY;
14284                 aip->submode = AISS_1;
14285                 aip->ai_flags &= ~(AIF_REPAIRING);
14286         } else if ( ship_flags & SIF_SENTRYGUN ) {
14287                 aip->mode = AIM_SENTRYGUN;
14288         } else {
14289                 aip->mode = AIM_NONE;
14290         }
14291         
14292         aip->submode_start_time = Missiontime;
14293         aip->active_goal = AI_GOAL_NONE;
14294 }
14295
14296 #define FRIENDLY_DAMAGE_THRESHOLD       50.0f           //      Display a message at this threshold.  Note, this gets scaled by Skill_level
14297
14298 // send the given message from objp.  called from the maybe_process_friendly_hit
14299 // code below when a message must get send to the player when he fires on friendlies
14300 void process_friendly_hit_message( int message, object *objp )
14301 {
14302         int index;
14303
14304         // no traitor in multiplayer
14305         if(Game_mode & GM_MULTIPLAYER){
14306                 return;
14307         }
14308
14309         // don't send this message if a player ship was hit.
14310         if ( objp->flags & OF_PLAYER_SHIP ){
14311                 return;
14312         }
14313
14314         // check if objp is a cargo contianer -- if so, then find a new ship to send the message
14315         index = objp->instance;
14316         if ( !(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER|SIF_BOMBER)) ){
14317                 index = -1;
14318         }
14319
14320         // if the message is "oops" (the don't hit me message), always make come from Terran command
14321         if ( message == MESSAGE_OOPS ){
14322                 index = -1;
14323         }
14324
14325         if ( index >= 0){
14326                 message_send_builtin_to_player( message, &Ships[index], MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14327         } else {
14328                 message_send_builtin_to_player( message, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14329         }
14330 }
14331
14332 extern  void ship_set_subsystem_strength( ship *shipp, int type, float strength );
14333
14334 //      Object *objp_weapon, fired by *objp_hitter, hit object *objp_ship.
14335 void maybe_process_friendly_hit(object *objp_hitter, object *objp_hit, object *objp_weapon)
14336 {
14337         // no turning traitor in multiplayer
14338         if ( Game_mode & GM_MULTIPLAYER ) {
14339                 return;
14340         }
14341
14342         // ditto if mission says no traitors allowed
14343         if (The_mission.flags & MISSION_FLAG_NO_TRAITOR) {
14344                 return;
14345         }
14346
14347         if ((objp_hitter == Player_obj) && (Player_ship->team == TEAM_FRIENDLY)) {
14348
14349                 // AL 12-4-97: It is possible the Player is a OBJ_GHOST at this point.  If so, bail out.
14350                 if ( objp_hitter->type != OBJ_SHIP ) {
14351                         return;
14352                 }
14353
14354                 Assert(objp_hitter->type == OBJ_SHIP);
14355                 Assert(objp_hit->type == OBJ_SHIP);
14356                 Assert(objp_weapon->type == OBJ_WEAPON);
14357
14358                 ship    *shipp_hitter = &Ships[objp_hitter->instance];
14359                 ship    *shipp_hit = &Ships[objp_hit->instance];
14360
14361                 if (shipp_hitter->team != shipp_hit->team) {
14362                         return;
14363                 }
14364
14365                 // get the player
14366                 player *pp = &Players[Player_num];
14367
14368                 // wacky stuff here
14369                 if (pp->friendly_hits != 0) {
14370                         float   time_since_last_hit = f2fl(Missiontime - pp->friendly_last_hit_time);
14371                         if ((time_since_last_hit >= 0.0f) && (time_since_last_hit < 10000.0f)) {
14372                                 if (time_since_last_hit > 60.0f) {
14373                                         pp->friendly_hits = 0;
14374                                         pp->friendly_damage = 0.0f;
14375                                 } else if (time_since_last_hit > 2.0f) {
14376                                         pp->friendly_hits -= (int) time_since_last_hit/2;
14377                                         pp->friendly_damage -= time_since_last_hit;
14378                                 }
14379
14380                                 if (pp->friendly_damage < 0.0f) {
14381                                         pp->friendly_damage = 0.0f;
14382                                 }
14383
14384                                 if (pp->friendly_hits < 0) {
14385                                         pp->friendly_hits = 0;
14386                                 }
14387                         }
14388                 }
14389
14390                 float   damage;         //      Damage done by weapon.  Gets scaled down based on size of ship.
14391
14392                 damage = Weapon_info[Weapons[objp_weapon->instance].weapon_info_index].damage;
14393                 
14394                 // wacky stuff here
14395                 ship_info *sip = &Ship_info[Ships[objp_hit->instance].ship_info_index];
14396                 if (sip->initial_hull_strength > 1000.0f) {
14397                         float factor = sip->initial_hull_strength / 1000.0f;
14398                         factor = min(100.0f, factor);
14399                         damage /= factor;
14400                 }
14401
14402                 //      Don't penalize much at all for hitting cargo
14403                 if (sip->flags & (SIF_CARGO | SIF_SENTRYGUN)) {
14404                         damage /= 10.0f;
14405                 }
14406
14407                 //      Hit ship, but not targeting it, so it's not so heinous, maybe an accident.
14408                 if (Ai_info[shipp_hitter->ai_index].target_objnum != OBJ_INDEX(objp_hit)) {
14409                         damage /= 5.0f;
14410                 }
14411
14412                 pp->friendly_last_hit_time = Missiontime;
14413                 pp->friendly_hits++;
14414
14415                 // cap damage and number of hits done this frame
14416                 float accredited_damage = min(MAX_BURST_DAMAGE, pp->damage_this_burst + damage) - pp->damage_this_burst;
14417                 pp->friendly_damage += accredited_damage;
14418                 pp->damage_this_burst += accredited_damage;
14419
14420                 // Done with adjustments to damage.  Evaluate based on current friendly_damage
14421                 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 ));
14422                 
14423                 if (is_instructor(objp_hit)) {
14424                         // it's not nice to hit your instructor
14425                         if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD) {
14426                                 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_ATTACK, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14427                                 pp->last_warning_message_time = Missiontime;
14428                                 ship_set_subsystem_strength( Player_ship, SUBSYSTEM_WEAPONS, 0.0f);
14429
14430                                 training_fail();
14431
14432                                 //      Instructor warp out.
14433                                 ai_set_mode_warp_out(objp_hit, &Ai_info[Ships[objp_hit->instance].ai_index]);
14434                                 gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START_FORCED );     //      Force player to warp out.
14435
14436                                 //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) );
14437                                 //ship_apply_global_damage( objp_hitter, objp_hit, NULL, 1.0f );
14438                         } else if (Missiontime - pp->last_warning_message_time > F1_0*4) {
14439                                 // warning every 4 sec
14440                                 // use NULL as the message sender here since it is the Terran Command persona
14441                                 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_HIT, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14442                                 pp->last_warning_message_time = Missiontime;
14443                         }
14444
14445                 // not nice to hit your friends
14446                 } else if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f)) {
14447                         process_friendly_hit_message( MESSAGE_HAMMER_SWINE, objp_hit );
14448                         mission_goal_fail_all();
14449                         ai_abort_rearm_request( Player_obj );
14450
14451                         Player_ship->team = TEAM_TRAITOR;
14452
14453                 } else if ((damage > frand()) && (Missiontime - pp->last_warning_message_time > F1_0*4) && (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD)) {
14454                         // no closer than 4 sec intervals
14455                         //      Note: (damage > frand()) added on 12/9/97 by MK.  Since damage is now scaled down for big ships, we could get too
14456                         //      many warnings.  Kind of tedious.  frand() returns a value in 0..1, so this won't affect legit hits.
14457                         process_friendly_hit_message( MESSAGE_OOPS, objp_hit );
14458                         pp->last_warning_message_time = Missiontime;
14459                 }
14460         }
14461 }
14462
14463 //      Maybe make ship with ai_info *aip attack hitter_objnum as a dynamic goal
14464 void maybe_set_dynamic_chase(ai_info *aip, int hitter_objnum)
14465 {
14466         Assert(Ship_info[Ships[aip->shipnum].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER));
14467
14468         // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
14469         if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
14470                 return;
14471         }
14472
14473         // only set as target if can be targeted.
14474         if (awacs_get_level(&Objects[hitter_objnum], &Ships[aip->shipnum], 1) < 1) {
14475                 return;
14476         }
14477
14478         if (aip->target_objnum != hitter_objnum)
14479                 aip->aspect_locked_time = 0.0f;
14480         set_target_objnum(aip, hitter_objnum);
14481         aip->resume_goal_time = Missiontime + i2f(20);  //      Only chase up to 20 seconds.
14482         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14483
14484         set_targeted_subsys(aip, NULL, -1);             //      Say not attacking any particular subsystem.
14485
14486         aip->previous_submode = aip->mode;
14487         aip->mode = AIM_CHASE;
14488         aip->submode = SM_ATTACK;
14489 }
14490
14491
14492 //      Return true if *objp has armed an aspect seeking bomb.
14493 //      This function written so a ship with an important bomb to fire will willingly take hits in the face to fire its bomb.
14494 int firing_aspect_seeking_bomb(object *objp)
14495 {
14496         ship    *shipp;
14497         int     bank_index;
14498         ship_weapon     *swp;
14499
14500         shipp = &Ships[objp->instance];
14501
14502         swp = &shipp->weapons;
14503
14504         bank_index = swp->current_secondary_bank;
14505
14506         if (bank_index != -1)
14507                 if (swp->secondary_bank_ammo[bank_index] > 0) {
14508                         if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_BOMB) {
14509                                 if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_HOMING_ASPECT) {
14510                                         return 1;
14511                                 }
14512                         }
14513                 }
14514
14515         return 0;
14516 }
14517
14518 //      *objp collided with big ship *big_objp at global point *collide_pos
14519 //      Make it fly away from the collision point.
14520 // collision_normal is NULL, when a collision is imminent and we just want to bug out.
14521 void big_ship_collide_recover_start(object *objp, object *big_objp, vector *collide_pos, vector *collision_normal)
14522 {
14523         ai_info *aip;
14524
14525         Assert(objp->type == OBJ_SHIP);
14526
14527         aip = &Ai_info[Ships[objp->instance].ai_index];
14528
14529         if (!timestamp_elapsed(aip->big_recover_timestamp) && (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1))
14530                 return;
14531
14532         //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)));
14533         if (collision_normal) {
14534                 aip->big_recover_timestamp = timestamp(2000);
14535                 aip->big_collision_normal = *collision_normal;
14536         //      nprintf(("AI", " normal\n"));
14537         } else {
14538                 aip->big_recover_timestamp = timestamp(500);
14539         //      nprintf(("AI", " no normal\n"));
14540         }
14541
14542
14543         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
14544         aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_1;
14545
14546 //      vector  out_vec;
14547 //      vm_vec_normalized_dir(&out_vec, &objp->pos, collide_pos);
14548
14549         // big_recover_pos_1 is 100 m out along normal
14550         vector direction;
14551         if (collision_normal) {
14552                 direction = *collision_normal;
14553         } else {
14554                 vm_vec_copy_scale(&direction, &objp->orient.v.fvec, -1.0f);
14555         }
14556         vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &direction, 100.0f);
14557
14558         // go out 200 m from box closest box point
14559         get_world_closest_box_point_with_delta(&aip->big_recover_pos_2, big_objp, &aip->big_recover_pos_1, NULL, 300.0f);
14560
14561         accelerate_ship(aip, 0.0f);
14562 /*
14563         if (vm_vec_dot(collision_normal, &objp->orient.v.fvec) > 0.5f) {
14564 //              vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14565 //              vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.v.uvec, big_objp->radius/2.0f);
14566 //              vm_vec_scale_add(&aip->big_recover_pos_2, &objp->pos, &out_vec, big_objp->radius*2.0f);
14567                 accelerate_ship(aip, 2.0f);
14568         } else {
14569 //              vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14570 //              vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.v.uvec, big_objp->radius/2.0f);
14571                 accelerate_ship(aip, 0.0f);
14572         } */
14573 }
14574
14575 float max_lethality = 0.0f;
14576
14577 void ai_update_lethality(object *ship_obj, object *other_obj, float damage)
14578 {
14579         Assert(ship_obj->type == OBJ_SHIP);
14580         Assert(other_obj->type == OBJ_WEAPON || other_obj->type == OBJ_SHOCKWAVE);
14581         int dont_count = FALSE;
14582
14583         int parent = other_obj->parent;
14584         if (Objects[parent].type == OBJ_SHIP) {
14585                 if (Objects[parent].signature == other_obj->parent_sig) {
14586
14587                         // check damage done to enemy team
14588                         if (Ships[ship_obj->instance].team != Ships[Objects[parent].instance].team) {
14589
14590                                 // other is weapon
14591                                 if (other_obj->type == OBJ_WEAPON) {
14592                                         weapon *wp = &Weapons[other_obj->instance];
14593                                         weapon_info *wif = &Weapon_info[wp->weapon_info_index];
14594
14595                                         // if parent is BIG|HUGE, don't count beam
14596                                         if (Ship_info[Ships[Objects[parent].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
14597                                                 if (wif->wi_flags & WIF_BEAM) {
14598                                                         dont_count = TRUE;
14599                                                 }
14600                                         }
14601                                 }
14602
14603                                 if (!dont_count) {
14604                                         float lethality = 0.025f * damage;      // 2 cyclops (@2000) put you at 100 lethality
14605
14606                                         // increase lethality weapon's parent ship
14607                                         ai_info *aip = &Ai_info[Ships[Objects[parent].instance].ai_index];
14608                                         aip->lethality += lethality;
14609                                         aip->lethality = min(110.0f, aip->lethality);
14610                                         // if you hit, don;t be less than 0
14611                                         aip->lethality = max(0.0f, aip->lethality);
14612
14613 //                                      if (aip->lethality > max_lethality) {
14614 //                                              max_lethality = aip->lethality;
14615 //                                              mprintf(("new lethalilty high: %.1f\n", max_lethality));
14616 //                                      }
14617
14618                                         // if parent is player, show his lethality
14619 //                                      if (Objects[parent].flags & OF_PLAYER_SHIP) {
14620 //                                              mprintf(("Player lethality: %.1f\n", aip->lethality));
14621 //                                      }
14622                                 }
14623                         }
14624                 }
14625         }
14626 }
14627
14628
14629 //      Object *objp_ship was hit by either weapon *objp_weapon or collided into by ship hit_objp at point *hitpos.
14630 void ai_ship_hit(object *objp_ship, object *hit_objp, vector *hitpos, int shield_quadrant, vector *hit_normal)
14631 {
14632         int             hitter_objnum = -2;
14633         object  *objp_hitter = NULL;
14634         ship            *shipp;
14635         ai_info *aip, *hitter_aip;
14636
14637         shipp = &Ships[objp_ship->instance];
14638         aip = &Ai_info[shipp->ai_index];
14639
14640         if (objp_ship->flags & OF_PLAYER_SHIP)
14641                 return;
14642
14643         if ((aip->mode == AIM_WARP_OUT) || (aip->mode == AIM_PLAY_DEAD))
14644                 return;
14645
14646         if (hit_objp->type == OBJ_SHIP) {
14647                 //      If the object that this ship collided with is a big ship
14648                 if (Ship_info[Ships[hit_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
14649                         //      And the current object is _not_ a big ship
14650                         if (!(Ship_info[Ships[objp_ship->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
14651                                 //      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.
14652                                 big_ship_collide_recover_start(objp_ship, hit_objp, hitpos, hit_normal);
14653                         }
14654                 }
14655         }
14656
14657         if (hit_objp->type == OBJ_WEAPON) {
14658                 //      Make sure the object that fired this weapon is still alive.  If not, abort.
14659                 // Assert(hit_objp->parent >= 0);
14660                 if(hit_objp->parent < 0){
14661                         return;
14662                 }
14663                 if ( hit_objp->parent_sig != Objects[hit_objp->parent].signature ){
14664                         return;
14665                 }
14666
14667                 //      Hit by a protected ship, don't attack it.
14668                 if (Objects[hit_objp->parent].flags & OF_PROTECTED) {
14669                         if ((Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) && (aip->target_objnum == -1)) {
14670                                 if (aip->mode == AIM_CHASE) {
14671                                         if (aip->submode != SM_EVADE_WEAPON) {
14672                                                 aip->mode = AIM_CHASE;
14673                                                 aip->submode = SM_EVADE_WEAPON;
14674                                                 aip->submode_start_time = Missiontime;
14675                                         }
14676                                 } else if (aip->mode != AIM_EVADE_WEAPON) {
14677                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14678                                         aip->previous_mode = aip->mode;
14679                                         aip->previous_submode = aip->submode;
14680                                         aip->mode = AIM_EVADE_WEAPON;
14681                                         aip->submode = -1;
14682                                         aip->submode_start_time = Missiontime;
14683                                         aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Evade for up to five seconds.
14684                                 }
14685
14686                         }
14687                         return;
14688                 }
14689
14690                 hitter_objnum = hit_objp->parent;
14691                 Assert((hitter_objnum >= 0) && (hitter_objnum < MAX_OBJECTS));
14692                 objp_hitter = &Objects[hitter_objnum];
14693                 maybe_process_friendly_hit(objp_hitter, objp_ship, hit_objp);           //      Deal with player's friendly fire.
14694
14695                 if ( (shipp->team & TEAM_FRIENDLY) && !(Game_mode & GM_MULTIPLAYER) ) {
14696                         ship_maybe_ask_for_help(shipp);
14697                 }
14698         } else if (hit_objp->type == OBJ_SHIP) {
14699                 if (shipp->team == Ships[hit_objp->instance].team)              //      Don't have AI react to collisions between teammates.
14700                         return;
14701                 objp_hitter = hit_objp;
14702                 hitter_objnum = hit_objp-Objects;
14703         } else {
14704                 Int3(); //      Hmm, what kind of object hit this if not weapon or ship?  Get MikeK.
14705                 return;
14706         }
14707
14708         //      Collided into a protected ship, don't attack it.
14709         if (hit_objp->flags & OF_PROTECTED)
14710                 return;
14711
14712         Assert(objp_hitter != NULL);
14713         hitter_aip = &Ai_info[Ships[objp_hitter->instance].ai_index];
14714         hitter_aip->last_hit_target_time = Missiontime;
14715         
14716         // store the object signature of objp_ship into ai_info, since we want to track the last ship hit by 'hitter_objnum'
14717         hitter_aip->last_objsig_hit = objp_ship->signature; 
14718
14719         aip->last_hit_time = Missiontime;
14720
14721         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
14722                 return;
14723
14724         //      If this ship is awaiting repair, abort!
14725         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)) {
14726                 ship_info       *sip = &Ship_info[shipp->ship_info_index];
14727
14728                 if (objp_ship->hull_strength/sip->initial_hull_strength < 0.3f) {
14729                         //      No, only abort if hull below a certain level.
14730                         aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP/2);  //      Might request again after 15 seconds.
14731                         if ( !(objp_ship->flags & OF_PLAYER_SHIP) )                                             // mwa -- don't abort rearm for a player
14732                                 ai_abort_rearm_request(objp_ship);
14733                 }
14734         }
14735
14736         //      If firing a bomb, ignore enemy fire so we can gain lock drop the bomb.
14737         //      Only ignore fire if aspect_locked_time > 0.5f, as this means we're in range.
14738         if (firing_aspect_seeking_bomb(objp_ship)) {
14739                 if ((aip->ai_flags & AIF_SEEK_LOCK) && (aip->aspect_locked_time > 0.1f))
14740                         return;
14741         }
14742
14743         //      If in AIM_STRAFE mode and got hit by target, maybe attack turret if appropriate
14744         if (aip->mode == AIM_STRAFE) {
14745                 Assert(hitter_objnum != -2);
14746                 if (aip->target_objnum == hitter_objnum) {
14747                         if ( hit_objp->type == OBJ_WEAPON ) {
14748                                 ai_big_strafe_maybe_attack_turret(objp_ship, hit_objp);
14749                         }
14750                         return;
14751                 }
14752                 else {
14753                                 // AL 11-10-97:
14754                         ;       // do nothing here, we'll attack this hitter if it is a fighter or bomber (this is handled
14755                                 // in code later in this function
14756                 }
14757         }
14758
14759         if (objp_ship == Player_obj)
14760                 return;         //      We don't do AI for the player.
14761
14762         maybe_update_guard_object(objp_ship, objp_hitter);
14763
14764         //      Big ships don't go any further.
14765         if (!(Ship_info[shipp->ship_info_index].flags & SIF_SMALL_SHIP))
14766                 return;
14767
14768         //      If the hitter object is the ignore object, don't attack it.
14769         ship_info       *sip = &Ship_info[shipp->ship_info_index];
14770         if ((is_ignore_object(aip, objp_hitter-Objects)) && (sip->flags & (SIF_BOMBER | SIF_FIGHTER))) {
14771                 if (aip->mode == AIM_NONE) {
14772                         aip->mode = AIM_CHASE;  //      This will cause the ship to move, if not attack.
14773                         aip->submode = SM_EVADE;
14774                 }
14775                 return;
14776         }
14777
14778         //      Maybe abort based on mode.
14779         switch (aip->mode) {
14780         case AIM_CHASE:
14781                 if (aip->submode == SM_ATTACK_FOREVER)
14782                         return;
14783
14784                 if ( hit_objp->type == OBJ_WEAPON ) {
14785                         if ( ai_big_maybe_enter_strafe_mode(objp_ship, OBJ_INDEX(hit_objp), 1) )
14786                                 return;
14787                 }
14788
14789         case AIM_GUARD:
14790                 //      If in guard mode and far away from guard object, don't pursue guy that hit me.
14791                         if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
14792                                 if (vm_vec_dist_quick(&objp_ship->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
14793                                         return;
14794                                 }
14795                         }
14796         case AIM_STILL:
14797         case AIM_STAY_NEAR:
14798                 // Note: Dealt with above, at very top.  case AIM_PLAY_DEAD:
14799         case AIM_STRAFE:
14800                 break;
14801         case AIM_EVADE_WEAPON:
14802         case AIM_EVADE:
14803         case AIM_GET_BEHIND:
14804         case AIM_AVOID:
14805         case AIM_DOCK:
14806         case AIM_BIGSHIP:
14807         case AIM_PATH:
14808         case AIM_NONE:
14809         case AIM_BAY_DEPART:
14810         case AIM_SENTRYGUN:
14811                 return;
14812         case AIM_BAY_EMERGE:
14813                 // If just leaving the docking bay, don't react to enemy fire... just keep flying away from docking bay
14814                 if ( (Missiontime - aip->submode_start_time) < 5*F1_0 ) {
14815                         return;
14816                 }
14817                 break;
14818         case AIM_WAYPOINTS:
14819                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER))
14820                         break;
14821                 else
14822                         return;
14823                 break;
14824         case AIM_SAFETY:
14825                 if ((aip->submode != AISS_1) || (Missiontime - aip->submode_start_time > i2f(1))) {
14826                         aip->submode = AISS_1;
14827                         aip->submode_start_time = Missiontime;
14828                 }
14829                 return;
14830                 break;
14831         case AIM_WARP_OUT:
14832                 return;
14833                 break;
14834         default:
14835                 Int3(); //      Bogus mode!
14836         }
14837
14838         if (timestamp_elapsed(aip->ok_to_target_timestamp))
14839                 aip->ai_flags &= ~AIF_FORMATION;                        //      If flying in formation, bug out!
14840
14841         aip->hitter_objnum = hitter_objnum;
14842         aip->hitter_signature = Objects[hitter_objnum].signature;
14843
14844         //      If the hitter is not on the same team as the hittee, do some stuff.
14845         if (shipp->team != Ships[objp_hitter->instance].team) {
14846                 //nprintf(("AI", "Object %i attacking %i, who just hit him!\n", objp_ship-Objects, hitter_objnum));
14847
14848                 if ((hitter_objnum != aip->target_objnum) && (sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
14849                         maybe_set_dynamic_chase(aip, hitter_objnum);
14850                         maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14851                 } else {
14852                         if ((aip->mode == AIM_CHASE) && ((objp_ship->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp_ship)/sip->shields > 0.8f))) {
14853                                 switch (aip->submode) {
14854                                 case SM_ATTACK:
14855                                 case SM_SUPER_ATTACK:
14856                                 case SM_GET_AWAY:
14857                                         break;
14858                                 default:
14859                                         if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
14860                                                 maybe_set_dynamic_chase(aip, hitter_objnum);
14861                                         }
14862                                         maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14863                                         break;
14864                                 }
14865                         } else if (aip->mode == AIM_CHASE) {
14866                                 switch (aip->submode) {
14867                                 case SM_ATTACK:
14868                                         aip->submode = SM_EVADE;
14869                                         aip->submode_start_time = Missiontime;
14870                                         break;
14871                                 case SM_SUPER_ATTACK:
14872                                         if (Missiontime - aip->submode_start_time > i2f(1)) {
14873                                                 aip->submode = SM_EVADE;
14874                                                 aip->submode_start_time = Missiontime;
14875                                         }
14876                                         break;
14877                                 case SM_EVADE_BRAKE:
14878                                         break;
14879                                 case SM_EVADE_SQUIGGLE:
14880                                         aip->submode = SM_EVADE;
14881                                         aip->submode_start_time = Missiontime;
14882                                         break;
14883                                 default:
14884                                         if (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) {
14885                                                 maybe_set_dynamic_chase(aip, hitter_objnum);
14886                                                 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14887                                         }
14888
14889                                         break;
14890                                 }
14891                         } else {
14892                                 // AL 3-15-98: Prevent escape pods from entering chase mode
14893                                 if ( (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) ) {
14894                                         maybe_set_dynamic_chase(aip, hitter_objnum);
14895                                 }
14896                                 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14897                         }
14898                 }
14899         }
14900 }
14901
14902 //      Ship shipnum has been destroyed.
14903 //      Cleanup.
14904 // the parameter 'method' is used to tell is this ship was destroyed or it departed normally.
14905 // This function will get called in either case, and there are things that should be done if
14906 // the ship actually gets destroyed which shouldn't get done if it departed.
14907 void ai_ship_destroy(int shipnum, int method)
14908 {
14909         int             objnum;
14910         object  *other_objp;
14911         ship            *shipp;
14912         ship_obj        *so;
14913         ai_info *dead_aip;
14914
14915         Assert((shipnum >= 0) && (shipnum < MAX_SHIPS));
14916         objnum = Ships[shipnum].objnum;
14917         dead_aip = &Ai_info[Ships[shipnum].ai_index];
14918
14919         // if I was getting repaired, or awaiting repair, then cleanup the repair mode.  When awaiting repair, the dock_objnum
14920         // is -1.  When the support ship is on the way, the dock_objnum >= 0 (points to support ship).
14921         if ( dead_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14922                 if ( dead_aip->dock_objnum >= 0 )
14923                         ai_do_objects_repairing_stuff( &Objects[objnum], &Objects[dead_aip->dock_objnum], REPAIR_INFO_END);
14924                 else
14925                         ai_do_objects_repairing_stuff( &Objects[objnum], NULL, REPAIR_INFO_END );
14926         }
14927
14928         //      For all objects that had this ship as a target, wipe it out, forcing find of a new enemy.
14929         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
14930                 other_objp = &Objects[so->objnum];
14931                 Assert(other_objp->instance != -1);
14932
14933                 shipp = &Ships[other_objp->instance];
14934                 Assert(shipp->ai_index != -1);
14935
14936                 ai_info *aip = &Ai_info[shipp->ai_index];
14937
14938                 // MWA 2/11/98
14939                 // code commented out below is taken care of in ai_cleanup_dock_mode when gets called when the
14940                 // support ship starts it's death roll.
14941
14942                 //      If the destroyed ship was on its way to repair the current ship
14943                 if (aip->dock_objnum == objnum) {
14944
14945                         // clean up the flags for any kind of docking mode.  If aip was part of a goal of dock/undock
14946                         // then it will get cleaned up by the goal code.
14947                         ai_do_objects_undocked_stuff( other_objp, NULL );
14948
14949                         if ( aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14950                                 int abort_reason;
14951                                 if ( method == SEF_DEPARTED ) {
14952                                         abort_reason = REPAIR_INFO_ABORT;
14953                                 } else {
14954                                         abort_reason = REPAIR_INFO_KILLED;
14955                                 }
14956                                 ai_do_objects_repairing_stuff( other_objp, NULL, abort_reason );
14957                         }
14958                 }
14959
14960                 if (aip->target_objnum == objnum) {
14961                         set_target_objnum(aip, -1);
14962                         //      If this ship had a dynamic goal of chasing the dead ship, clear the dynamic goal.
14963                         if (aip->resume_goal_time != -1)
14964                                 aip->active_goal = AI_GOAL_NONE;
14965                 }
14966
14967                 if (aip->goal_objnum == objnum) {
14968                         aip->goal_objnum = -1;
14969                         aip->goal_signature = -1;
14970                 }
14971
14972                 if (aip->guard_objnum == objnum) {
14973                         aip->guard_objnum = -1;
14974                         aip->guard_signature = -1;
14975                 }
14976
14977                 if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[Objects[objnum].instance].ai_index].wing)) {
14978                         if (aip->guard_wingnum != aip->wing)
14979                                 ai_set_guard_wing(other_objp, aip->guard_wingnum);
14980                 }
14981
14982                 if (aip->hitter_objnum == objnum)
14983                         aip->hitter_objnum = -1;
14984
14985         }
14986
14987 }
14988
14989 /*
14990 //      Interface function to goals code.
14991 //      Make object *objp fly to point *vp and warp out.
14992 void ai_warp_out(object *objp, vector *vp)
14993 {
14994         ai_info *aip;
14995
14996         aip = &Ai_info[Ships[objp->instance].ai_index];
14997
14998         if (aip->mode != AIM_WARP_OUT) {
14999                 ai_set_mode_warp_out(objp, aip);
15000         }
15001         float   dist;
15002         float   dot;
15003         vector  v2v;
15004         ai_info *aip;
15005
15006         dist = vm_vec_normalized_dir(&v2v, vp, &objp->pos);
15007
15008         if (dist < objp->radius + 5.0f) {
15009
15010                 // Start the warp out effect 
15011                 shipfx_warpout_start(objp);
15012
15013         } else {
15014                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2v);
15015
15016                 aip = &Ai_info[Ships[objp->instance].ai_index];
15017
15018                 if (dist > 500.0f)
15019                         accelerate_ship(aip, 1.0f);
15020                 else
15021                         accelerate_ship(aip, (3*dot + 1.0f)/4.0f);
15022
15023                 turn_towards_point(objp, vp, NULL, 0.0f);
15024         }
15025 }
15026 */
15027
15028
15029 //      Do stuff at start of deathroll.
15030 void ai_deathroll_start(object *ship_obj)
15031 {
15032         ai_info *aip;
15033         ship            *shipp, *other_ship;
15034
15035         shipp = &Ships[ship_obj->instance];
15036         aip = &Ai_info[shipp->ai_index];
15037
15038         // mark object we are docked with so we can do damage and separate during deathroll
15039         // keep dock_objnum_when_dead from being changed if already set (only allow to be set when -1)
15040         if (Ships[ship_obj->instance].dock_objnum_when_dead == -1) {
15041                 Ships[ship_obj->instance].dock_objnum_when_dead = aip->dock_objnum;
15042                 // set other_ship dock_objnum_when_dead, if other_ship exits.
15043                 if (Ships[ship_obj->instance].dock_objnum_when_dead != -1) {
15044                         other_ship = &Ships[Objects[aip->dock_objnum].instance];
15045                         other_ship->dock_objnum_when_dead = shipp->objnum;
15046                 }
15047         }
15048
15049         ai_cleanup_dock_mode(aip, shipp);
15050
15051         aip->mode = AIM_NONE;
15052 }
15053
15054 //      Object *requester_objp tells rearm ship to abort rearm.
15055 //      Returns true if it succeeded, else false.
15056 //      To succeed means you were previously rearming.
15057 int ai_abort_rearm_request(object *requester_objp)
15058 {
15059         ship            *requester_shipp;
15060         ai_info *requester_aip;
15061
15062         Assert(requester_objp->type == OBJ_SHIP);
15063         if(requester_objp->type != OBJ_SHIP){
15064                 return 0;
15065         }
15066         Assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));      
15067         if((requester_objp->instance < 0) || (requester_objp->instance >= MAX_SHIPS)){
15068                 return 0;
15069         }
15070         requester_shipp = &Ships[requester_objp->instance];
15071         Assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));          
15072         if((requester_shipp->ai_index < 0) || (requester_shipp->ai_index >= MAX_AI_INFO)){
15073                 return 0;
15074         }       
15075         requester_aip = &Ai_info[requester_shipp->ai_index];
15076         
15077         if (requester_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)){
15078
15079                 // dock_objnum is always valid once a rearm repair has been requested.  It points to the
15080                 // ship that is coming to repair me.
15081                 if (requester_aip->dock_objnum != -1) {
15082                         object  *repair_objp;
15083                         ai_info *repair_aip;
15084
15085                         repair_objp = &Objects[requester_aip->dock_objnum];
15086                         repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
15087
15088                         //      Make sure signatures match.  This prevents nasty bugs in which an object
15089                         //      that was repairing another is destroyed and is replaced by another ship
15090                         //      before this code comes around.
15091                         if (repair_objp->signature == requester_aip->dock_signature) {
15092
15093                                 Assert( repair_objp->type == OBJ_SHIP );
15094
15095                                 // if support ship is in the process of undocking, don't do anything.
15096                                 if ( repair_aip->submode < AIS_UNDOCK_0 ) {
15097                                         ai_do_objects_repairing_stuff( requester_objp, repair_objp, REPAIR_INFO_ABORT );
15098
15099                                         if ( repair_aip->submode == AIS_DOCK_4 )
15100                                                 repair_aip->submode = AIS_UNDOCK_0;
15101                                         else
15102                                                 repair_aip->submode = AIS_UNDOCK_3;
15103
15104                                         repair_aip->submode_start_time = Missiontime;
15105                                 } else {
15106                                         nprintf(("AI", "Not aborting rearm since already undocking\n"));
15107                                 }
15108                         }
15109                 } else {
15110                         // setting these flags is the safe things to do.  There may not be a corresponding repair
15111                         // ship for this guys since a repair ship may be currently repairing someone else.
15112                         ai_do_objects_repairing_stuff( requester_objp, NULL, REPAIR_INFO_ABORT );
15113
15114                         // try and remove this guy from an arriving support ship.
15115                         mission_remove_scheduled_repair(requester_objp);
15116                 }
15117
15118                 return 1;
15119         } else if ( requester_aip->ai_flags & AIF_REPAIRING ) {
15120                 // a support ship can request to abort when he is told to do something else (like warp out).
15121                 // see if this support ships goal_objnum is valid.  If so, then issue this ai_abort comment
15122                 // for the ship that he is enroute to repair
15123                 if ( requester_aip->goal_objnum != -1 ) {
15124                         int val;
15125
15126                         val = ai_abort_rearm_request( &Objects[requester_aip->goal_objnum] );
15127                         return val;
15128                 }
15129         }
15130
15131         return 0;
15132 }
15133
15134 // function which gets called from ai-issue_rearm_request and from code in missionparse.cpp
15135 // to actually issue the rearm goal (support_obj to rearm requester_obj);
15136 void ai_add_rearm_goal( object *requester_objp, object *support_objp )
15137 {
15138         ship *support_shipp, *requester_shipp;
15139         ai_info *support_aip, *requester_aip;
15140
15141         support_shipp = &Ships[support_objp->instance];
15142         requester_shipp = &Ships[requester_objp->instance];
15143         requester_aip = &Ai_info[requester_shipp->ai_index];
15144
15145         Assert( support_shipp->ai_index != -1 );
15146         support_aip = &Ai_info[support_shipp->ai_index];
15147
15148         // if the requester is a player object, issue the order as the squadmate messaging code does.  Doing so
15149         // ensures that the player get a higher priority!
15150         requester_aip->ai_flags |= AIF_AWAITING_REPAIR; //      Tell that I'm awaiting repair.
15151         if ( requester_objp->flags & OF_PLAYER_SHIP )
15152                 ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_REARM_REPAIR, -1, requester_shipp->ship_name, support_aip );
15153         else
15154                 ai_add_goal_ship_internal( support_aip, AI_GOAL_REARM_REPAIR, requester_shipp->ship_name, -1, -1 );
15155
15156 }
15157
15158 //      Object *requester_objp requests rearming.
15159 //      Returns objnum of ship coming to repair requester on success
15160 //      Success means you found someone to rearm you and you weren't previously rearming.
15161 int ai_issue_rearm_request(object *requester_objp)
15162 {
15163         object  *objp;
15164         ship            *requester_shipp;
15165         ai_info *requester_aip;
15166
15167         Assert(requester_objp->type == OBJ_SHIP);
15168         Assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));
15169         requester_shipp = &Ships[requester_objp->instance];
15170         Assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));
15171         requester_aip = &Ai_info[requester_shipp->ai_index];
15172         
15173         //      Make sure not already awaiting repair.
15174         if (requester_aip->ai_flags & AIF_AWAITING_REPAIR) {
15175                 nprintf(("AI", "Ship %s already awaiting rearm by ship %s.\n", requester_shipp->ship_name, &Ships[Objects[requester_aip->dock_objnum].instance].ship_name));    
15176                 return -1;
15177         }
15178
15179         if ( !is_support_allowed(requester_objp) )
15180                 return -1;
15181
15182         //nprintf(("AI", "Ship %s requesting rearming.\n", requester_shipp->ship_name));
15183         requester_aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);  //      Might request again after this much time.
15184
15185         // call ship_find_repair_ship to get a support ship.  If none is found, then we will warp one in.  This
15186         // function will return the next available ship which can repair requester
15187         objp = ship_find_repair_ship( requester_objp );
15188         ai_do_objects_repairing_stuff( requester_objp, objp, REPAIR_INFO_QUEUE );
15189         if ( objp ) {
15190
15191                 // MWA 5/14/98 -- moved next item into the ai_do_objects_repairing_stuff function so that clients
15192                 // would properly update their hud support view
15193                 //ai_add_rearm_goal( requester_objp, objp );
15194                 return OBJ_INDEX(objp);
15195
15196         } else {
15197                 // call to warp in repair ship!!!!  for now, warp in any number of ships needed.  Should cap it to
15198                 // some reasonable max (or let support ships warp out).  We should assume here that ship_find_repair_ship()
15199                 // would have returned a valid object if there are too many support ships already in the mission
15200                 mission_warp_in_support_ship( requester_objp );
15201
15202                 return -1;
15203         }
15204
15205 }
15206
15207 // make objp rearm and repair goal_objp
15208 void ai_rearm_repair( object *objp, object  *goal_objp, int priority, int docker_index, int dockee_index )
15209 {
15210         ai_info *aip, *goal_aip;
15211
15212         aip = &Ai_info[Ships[objp->instance].ai_index];
15213         aip->goal_objnum = goal_objp-Objects;
15214
15215         // nprintf(("AI", "Ship %s preparing to rearm ship %s.\n", shipp->ship_name, requester_shipp->ship_name));
15216
15217         ai_dock_with_object(objp, goal_objp, priority, AIDO_DOCK, docker_index, dockee_index);
15218         aip->ai_flags |= AIF_REPAIRING;                                         //      Tell that repair guy is busy trying to repair someone.
15219
15220         goal_aip = &Ai_info[Ships[goal_objp->instance].ai_index];
15221         goal_aip->dock_objnum = objp-Objects;           //      Tell which object is coming to repair.
15222         goal_aip->dock_signature = objp->signature;
15223
15224         ai_do_objects_repairing_stuff( goal_objp, objp, REPAIR_INFO_ONWAY );
15225
15226         goal_aip->abort_rearm_timestamp = timestamp(NEXT_REARM_TIMESTAMP*3/2);
15227 }
15228
15229 // Given a dockee object and the index of the dockbay for that object (ie the dockbay index
15230 // into polymodel->dockbays[] for the model associated with the object), return the index
15231 // of a path_num associated with than dockbay (this is an index into polymodel->paths[])
15232 int ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index)
15233 {
15234         if ( dockbay_index < 0 || dockee_objp == NULL ) {
15235                 Int3();         // should never happen
15236                 return -1;
15237         }
15238
15239         if ( dockee_objp->type == OBJ_SHIP ) {
15240                 int                     path_num;
15241                 polymodel       *pm;
15242
15243                 pm = model_get( Ships[dockee_objp->instance].modelnum );
15244
15245                 // sanity checks
15246                 Assert(pm->n_docks > dockbay_index);
15247                 Assert(pm->docking_bays[dockbay_index].num_spline_paths > 0);
15248                 Assert(pm->docking_bays[dockbay_index].splines != NULL);
15249                 if(pm->n_docks <= dockbay_index){
15250                         return -1;
15251                 }
15252                 if(pm->docking_bays[dockbay_index].num_spline_paths <= 0){
15253                         return -1;
15254                 }
15255                 if(pm->docking_bays[dockbay_index].splines == NULL){
15256                         return -1;
15257                 }
15258
15259                 // We only need to return one path for the dockbay, so return the first
15260                 path_num = pm->docking_bays[dockbay_index].splines[0];
15261                 return path_num;
15262         } else {
15263                 return -1;
15264         }
15265 }
15266
15267 //      Actually go ahead and fire the synaptics.
15268 void cheat_fire_synaptic(object *objp, ship *shipp, ai_info *aip)
15269 {
15270         ship_weapon     *swp;
15271         swp = &shipp->weapons;
15272         int     current_bank = swp->current_secondary_bank;
15273
15274         ai_select_secondary_weapon(objp, swp, WIF_SPAWN, 0);
15275         if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
15276                 if (ship_fire_secondary(objp)) {
15277                         nprintf(("AI", "ship %s cheat fired synaptic!\n", shipp->ship_name));
15278                         swp->next_secondary_fire_stamp[current_bank] = timestamp(2500);
15279                 }
15280         }
15281 }
15282
15283 //      For the subspace mission (sm3-09a)
15284 //              for delta wing
15285 //                      if they're sufficiently far into the mission
15286 //                              if they're near one or more enemies
15287 //                                      every so often
15288 //                                              fire a synaptic if they have one.
15289 void maybe_cheat_fire_synaptic(object *objp, ai_info *aip)
15290 {
15291         //      Only do in subspace missions.
15292         if ( The_mission.flags & MISSION_FLAG_SUBSPACE )        {
15293                 ship    *shipp;
15294                 int     num, time;
15295
15296                 shipp = &Ships[objp->instance];
15297
15298                 if (!(strnicmp(shipp->ship_name, NOX("delta"), 5))) {
15299                         num = shipp->ship_name[6] - '1';
15300
15301                         if ((num >= 0) && (num <= 3)) {
15302                                 time = Missiontime >> 16;       //      Convert to seconds.
15303
15304                                 time -= 2*60;   //      Subtract off two minutes.
15305
15306                                 if (time > 0) {
15307                                         int modulus = 17 + num*3;
15308
15309                                         if ((time % modulus) < 2) {
15310                                                 int count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(objp)), &objp->pos, 1500.0f);
15311
15312                                                 if (count > 0) {
15313                                                         cheat_fire_synaptic(objp, shipp, aip);
15314                                                 }
15315                                         }
15316                                 }
15317                         }
15318                 }
15319         }
15320
15321 }
15322