]> icculus.org git repositories - taylor/freespace2.git/blob - src/ship/aicode.cpp
merge in updated FS1 code
[taylor/freespace2.git] / src / ship / aicode.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Ship/AiCode.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  * 
15  * AI code that does interesting stuff
16  *
17  * $Log$
18  * Revision 1.11  2006/04/26 19:45:22  taylor
19  * fix a FS1 docked speed issue, should keep it in line with the original now
20  *
21  * Revision 1.10  2005/03/29 02:18:47  taylor
22  * Various 64-bit platform fixes
23  * Fix compiler errors with MAKE_FS1 and fix gr_set_bitmap() too
24  * Make sure that turrets can fire at asteroids for FS1 (needed for a couple missions)
25  * Streaming audio support (big thanks to Pierre Willenbrock!!)
26  * Removed dependance on strings.tbl for FS1 since we don't actually need it now
27  *
28  * Revision 1.9  2004/09/20 01:31:44  theoddone33
29  * GCC 3.4 fixes.
30  *
31  * Revision 1.8  2003/08/03 16:10:30  taylor
32  * cleanup; compile warning fixes
33  *
34  * Revision 1.7  2003/05/25 02:30:43  taylor
35  * Freespace 1 support
36  *
37  * Revision 1.6  2002/07/13 19:47:02  theoddone33
38  * Fix some more warnings
39  *
40  * Change demo building, edit Makefile if you want the demo.
41  *
42  * Revision 1.5  2002/06/17 06:33:10  relnev
43  * ryan's struct patch for gcc 2.95
44  *
45  * Revision 1.4  2002/06/09 04:41:26  relnev
46  * added copyright header
47  *
48  * Revision 1.3  2002/06/01 07:12:34  relnev
49  * a few NDEBUG updates.
50  *
51  * removed a few warnings.
52  *
53  * Revision 1.2  2002/05/03 13:34:33  theoddone33
54  * More stuff compiles
55  *
56  * Revision 1.1.1.1  2002/05/03 03:28:10  root
57  * Initial import.
58  *
59  * 
60  * 107   9/15/99 4:42a Mikek
61  * Make any big ship attacking Colossus, or Colossus attacking any large
62  * ship not use big cruiser movement code.
63  * 
64  * 106   9/15/99 3:28a Jimb
65  * Make all big ships in sm3-08 not do cruiser chase code when attacking
66  * Colossus.  Added so Beast doesn't swerve away from Colossus.
67  * 
68  * 105   9/14/99 4:18p Andsager
69  * hack for mission sm3-08 to abort cruiser_chase as sathanas is about to
70  * begin circling colossus.
71  * 
72  * 104   9/08/99 10:44p Andsager
73  * Make HUGE ships not die when warping out, after warp effect started.
74  * 
75  * 103   9/03/99 11:40p Mikek
76  * Comment out an annoying nprintf().
77  * 
78  * 102   9/01/99 11:26p Dave
79  * Fixed release build warnings.
80  * 
81  * 101   9/01/99 9:12p Mikek
82  * Make it a boatload harder to become a traitor from hitting a large
83  * ship.
84  * 
85  * 100   9/01/99 4:01p Andsager
86  * Make sure BIG|HUGE ships do not respond to shockwaves
87  * 
88  * 99    9/01/99 10:09a Dave
89  * Pirate bob.
90  * 
91  * 98    8/31/99 4:24p Andsager
92  * Reduce collisions when attacking big ships.
93  * 
94  * 97    8/31/99 7:33a Mikek
95  * Improvements in formation flying, less silly behavior, especially when
96  * leader is moving very slowly.
97  * 
98  * 96    8/31/99 5:48a Mikek
99  * Making ships not overshoot so much in formation flying.  Intermediate
100  * checkin.
101  * 
102  * 95    8/30/99 12:03a Mikek
103  * Make guard behavior much less annoying.  Guarders don't get quite so
104  * close and they try to avoid striking the target they are guarding.
105  * 
106  * 94    8/29/99 4:18p Andsager
107  * New "burst" limit for friendly damage.  Also credit more damage done
108  * against large friendly ships.
109  * 
110  * 93    8/28/99 7:29p Dave
111  * Fixed wingmen persona messaging. Make sure locked turrets don't count
112  * towards the # attacking a player.
113  * 
114  * 92    8/26/99 10:46p Andsager
115  * Apply shockwave damage to lethality.
116  * 
117  * 91    8/26/99 8:52p Dave
118  * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
119  * 
120  * 90    8/26/99 5:14p Andsager
121  * 
122  * 89    8/24/99 8:55p Dave
123  * Make sure nondimming pixels work properly in tech menu.
124  * 
125  * 88    8/23/99 6:21p Jefff
126  * added "no traitor" option to missions (and fred)
127  * 
128  * 87    8/20/99 3:36p Andsager
129  * Make sure we don;t miss stealth sweep points.
130  * 
131  * 86    8/16/99 8:21a Andsager
132  * fix link error
133  * 
134  * 85    8/16/99 8:19a Andsager
135  * Add project_point_onto_bbox() to fvi and include in aicode
136  * 
137  * 84    8/15/99 1:30p Dave
138  * Removed some bounding box code because of link errors. Assuming needed
139  * function just needs to get checked in by DaveA.
140  * 
141  * 83    8/15/99 11:59a Andsager
142  * For targing big/huge ships, find nearest distance to bbox, not center.
143  * 
144  * 82    8/13/99 2:20p Andsager
145  * Add speed modification to chances turret will find stealth ship
146  * 
147  * 81    8/13/99 10:49a Andsager
148  * Knossos and HUGE ship warp out.  HUGE ship warp in.  Stealth search
149  * modes dont collide big ships.
150  * 
151  * 80    8/10/99 5:02p Andsager
152  * Fix bug where AI gets stuck in SM_EVADE_WEAPON with no target.
153  * 
154  * 79    8/10/99 11:58a Andsager
155  * Allow turrets to sometimes see stealth.
156  * 
157  * 78    7/31/99 2:57p Dave
158  * Scaled flak aim and jitter by weapon subsystem strength.
159  * 
160  * 77    7/27/99 10:33p Andsager
161  * improve ai for attacking stealth.  reduced jitter in aim.  reduced
162  * error in position when avoiding.  skill level support for attacking
163  * stealth.  Made target error same for team vs. team.
164  * 
165  * 76    7/27/99 10:49a Andsager
166  * Make turret fire rate independent of team for HUGE turrets, and also
167  * for mult team vs. team.
168  * 
169  * 75    7/26/99 12:14p Andsager
170  * Apply cap to how much slower a transport flies with cargo.  Remove
171  * limit on waypoint speed for training.  Enemy ai get stealth exact pos
172  * when stealth fires
173  * 
174  * 74    7/20/99 1:49p Dave
175  * Peter Drake build. Fixed some release build warnings.
176  * 
177  * 73    7/19/99 2:13p Dave
178  * Added some new strings for Heiko.
179  * 
180  * 72    7/19/99 12:02p Andsager
181  * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
182  * only blow up subsystem if its strength is > 0
183  * 
184  * 71    7/15/99 9:20a Andsager
185  * FS2_DEMO initial checkin
186  * 
187  * 70    7/14/99 1:44p Andsager
188  * modify ai_guard for BIG ships to circle around the long axis
189  * 
190  * 69    7/09/99 5:54p Dave
191  * Seperated cruiser types into individual types. Added tons of new
192  * briefing icons. Campaign screen.
193  * 
194  * 68    7/08/99 4:32p Andsager
195  * fix bug with turret-tagged-only
196  * 
197  * 67    7/08/99 12:06p Andsager
198  * Add turret-tagged-only and turret-tagged-clear sexp.
199  * 
200  * 66    7/02/99 3:49p Andsager
201  * Remove debug code.  Allow targeting of stealth from any weapon it
202  * fires.
203  * 
204  * 65    7/02/99 2:01p Andsager
205  * Fix bug where big ship tries to evade dumpfire weapon.
206  * 
207  * 64    7/02/99 10:58a Andsager
208  * Put in big ship - big ship attack mode.  Modify stealth sweep ai.
209  * 
210  * 63    6/30/99 5:53p Dave
211  * Put in new anti-camper code.
212  * 
213  * 62    6/28/99 3:22p Anoop
214  * Fix turret optimization, where ship may not have any valid subsystems
215  * (all blown off).
216  * 
217  * 61    6/25/99 5:56p Andsager
218  * First real pass on stealth ai.
219  * 
220  * 60    6/25/99 3:08p Dave
221  * Multiple flyby sounds.
222  * 
223  * 59    6/25/99 1:12p Danw
224  * DKA:  Make sure big ship has subsystems before trying to target them.
225  * 
226  * 58    6/25/99 10:56a Johnson
227  * Fixed dumb ai code.
228  * 
229  * 57    6/24/99 5:15p Dave
230  * Make sure stride is always at least one for checking turret subsystem
231  * targets.
232  * 
233  * 56    6/24/99 4:59p Dave
234  * Significant speedups to turret firing.
235  * 
236  * 55    6/23/99 5:51p Andsager
237  * Add waypoint-cap-speed.  Checkin stealth ai - inactive.
238  * 
239  * 54    6/16/99 10:21a Dave
240  * Added send-message-list sexpression.
241  * 
242  * 53    6/15/99 9:25a Andsager
243  * Make guard and dynamic chase (who hit you) work with stealth
244  * 
245  * 52    6/14/99 3:21p Andsager
246  * Allow collisions between ship and its debris.  Fix up collision pairs
247  * when large ship is warping out.
248  * 
249  * 51    6/14/99 10:45a Dave
250  * Made beam weapons specify accuracy by skill level in the weapons.tbl
251  * 
252  * 50    6/03/99 8:11a Andsager
253  * 
254  * 49    6/02/99 5:41p Andsager
255  * Reduce range of secondary weapons not fired from turrets in nebula.
256  * Reduce range of beams fired from turrrets in nebula
257  * 
258  * 48    6/02/99 3:23p Andsager
259  * Make AI aware of team visibility.  Allow player targeting with team
260  * visibility info.  Make stealth ships not targetable by AI in nebula
261  * unless tagged.
262  * 
263  * 47    6/02/99 12:52p Andsager
264  * Added team-wide ship visibility.  Implemented for player.
265  * 
266  * 46    6/01/99 8:35p Dave
267  * Finished lockarm weapons. Added proper supercap weapons/damage. Added
268  * awacs-set-radius sexpression.
269  * 
270  * 45    5/28/99 5:35p Andsager
271  * Make ai nebula aware
272  * 
273  * 44    5/24/99 9:55a Dave
274  * Fixed stream weapon ai firing problem. ick.
275  * 
276  * 43    5/20/99 7:00p Dave
277  * Added alternate type names for ships. Changed swarm missile table
278  * entries.
279  * 
280  * 42    5/18/99 1:30p Dave
281  * Added muzzle flash table stuff.
282  * 
283  * 41    5/12/99 2:55p Andsager
284  * Implemented level 2 tag as priority in turret object selection
285  * 
286  * 40    5/12/99 10:42a Andsager
287  * Fix turret bug allowing HUGE turrets to fire at fighters
288  * 
289  * 39    5/06/99 11:46a Andsager
290  * Bug fixes.  Don't get into illegal strafe submode.  Don't choose turret
291  * enemy objnum for beam protected.
292  * 
293  * 38    5/03/99 10:50p Andsager
294  * Make Asteroid_obj_list.  Change get_nearest_turret_objnum() to use
295  * Asteroid_obj_list, Ship_obj_list and Missile_obj_list vs.
296  * obj_used_list.
297  * 
298  * 37    4/29/99 2:29p Dave
299  * Made flak work much better in multiplayer.
300  * 
301  * 36    4/28/99 11:36p Dave
302  * Tweaked up subspace missile strike a bit,
303  * 
304  * 35    4/28/99 3:11p Andsager
305  * Stagger turret weapon fire times.  Make turrets smarter when target is
306  * protected or beam protected.  Add weaopn range to weapon info struct.
307  * 
308  * 34    4/26/99 10:58a Andsager
309  * Add OF_BEAM_PROTECTED flag to keep object from being targeted for zing.
310  * 
311  * 33    4/23/99 12:12p Andsager
312  * Modify wing positions when player is wing leader to prevent some
313  * collisions.
314  * 
315  * 32    4/23/99 12:01p Johnson
316  * Added SIF_HUGE_SHIP
317  * 
318  * 31    4/22/99 11:06p Dave
319  * Final pass at beam weapons. Solidified a lot of stuff. All that remains
320  * now is to tweak and fix bugs as they come up. No new beam weapon
321  * features.
322  * 
323  * 30    4/20/99 6:39p Dave
324  * Almost done with artillery targeting. Added support for downloading
325  * images on the PXO screen.
326  * 
327  * 29    4/20/99 3:40p Andsager
328  * Changes to big ship ai.  Uses bounding box as limit where to fly to
329  * when flying away.
330  * 
331  * 28    4/16/99 5:54p Dave
332  * Support for on/off style "stream" weapons. Real early support for
333  * target-painting lasers.
334  * 
335  * 27    4/02/99 9:55a Dave
336  * Added a few more options in the weapons.tbl for beam weapons. Attempt
337  * at putting "pain" packets into multiplayer.
338  * 
339  * 26    3/28/99 5:58p Dave
340  * Added early demo code. Make objects move. Nice and framerate
341  * independant, but not much else. Don't use yet unless you're me :)
342  * 
343  * 25    3/19/99 9:51a Dave
344  * Checkin to repair massive source safe crash. Also added support for
345  * pof-style nebulae, and some new weapons code.
346  * 
347  * 24    3/08/99 7:03p Dave
348  * First run of new object update system. Looks very promising.
349  * 
350  * 23    3/05/99 3:55p Anoop
351  * Handle some asserts properly.
352  * 
353  * 22    3/04/99 6:09p Dave
354  * Added in sexpressions for firing beams and checking for if a ship is
355  * tagged.
356  * 
357  * 21    3/02/99 9:25p Dave
358  * Added a bunch of model rendering debug code. Started work on fixing
359  * beam weapon wacky firing.
360  * 
361  * 20    2/25/99 2:32p Anoop
362  * (Alan). Fixed ai path following code for AI_BAY_EMERGE. Put in sanity
363  * check so that when the last point on the path is reached, it finishes.
364  * 
365  * 19    2/19/99 2:11p Anoop
366  * Put in some nice handling code for wacky support ship problems (like no
367  * docking paths)
368  * 
369  * 18    2/17/99 2:11p Dave
370  * First full run of squad war. All freespace and tracker side stuff
371  * works.
372  * 
373  * 17    2/11/99 5:22p Andsager
374  * Fixed bugs, generalized block Sexp_variables
375  * 
376  * 16    1/29/99 5:07p Dave
377  * Fixed multiplayer stuff. Put in multiplayer support for rapid fire
378  * missiles.
379  * 
380  * 15    1/29/99 2:25p Andsager
381  * Added turret_swarm_missiles
382  * 
383  * 14    1/27/99 9:56a Dave
384  * Temporary checkin of beam weapons for Dan to make cool sounds.
385  * 
386  * 13    1/24/99 11:37p Dave
387  * First full rev of beam weapons. Very customizable. Removed some bogus
388  * Int3()'s in low level net code.
389  * 
390  * 12    1/21/99 10:44a Dave
391  * More beam weapon stuff. Put in warmdown time.
392  * 
393  * 11    1/12/99 5:45p Dave
394  * Moved weapon pipeline in multiplayer to almost exclusively client side.
395  * Very good results. Bandwidth goes down, playability goes up for crappy
396  * connections. Fixed object update problem for ship subsystems.
397  * 
398  * 10    1/08/99 2:08p Dave
399  * Fixed software rendering for pofview. Super early support for AWACS and
400  * beam weapons.
401  * 
402  * 9     12/23/98 2:53p Andsager
403  * Added ship activation and gas collection subsystems, removed bridge
404  * 
405  * 8     11/12/98 12:13a Dave
406  * Tidied code up for multiplayer test. Put in network support for flak
407  * guns.
408  * 
409  * 7     11/05/98 5:55p Dave
410  * Big pass at reducing #includes
411  * 
412  * 6     10/26/98 9:42a Dave
413  * Early flak gun support.
414  * 
415  * 5     10/23/98 3:51p Dave
416  * Full support for tstrings.tbl and foreign languages. All that remains
417  * is to make it active in Fred.
418  * 
419  * 4     10/20/98 1:39p Andsager
420  * Make so sparks follow animated ship submodels.  Modify
421  * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
422  * submodel_num.  Add submodel_num to multiplayer hit packet.
423  * 
424  * 3     10/13/98 9:29a Dave
425  * Started neatening up freespace.h. Many variables renamed and
426  * reorganized. Added AlphaColors.[h,cpp]
427  * 
428  * 2     10/07/98 10:53a Dave
429  * Initial checkin.
430  * 
431  * 1     10/07/98 10:51a Dave
432  * 
433  * 
434  * $NoKeywords: $
435  */
436
437 // This module contains the actual AI code that does interesting stuff
438 // to objects.   The code in Ai.cpp is just for bookeeping, allocating
439 // ai slots and linking them to ships.
440
441 #include "pstypes.h"
442 #include "fix.h"
443 #include "linklist.h"
444 #include "object.h"
445 #include "physics.h"
446 #include "vecmat.h"
447 #include "ship.h"
448 #include "model.h"
449 #include "2d.h"
450 #include "3d.h"
451 #include "ai.h"
452 #include "floating.h"
453 #include "player.h"
454 #include "freespace.h"
455 #include "weapon.h"
456 #include "missiongoals.h"
457 #include "missionlog.h"
458 #include "timer.h"
459 #include "sound.h"
460 #include "aigoals.h"
461 #include "gamesnd.h"
462 #include "hudmessage.h"
463 #include "missionmessage.h"
464 #include "cmeasure.h"
465 #include "staticrand.h"
466 #include "multimsgs.h"
467 #include "afterburner.h"
468 #include "hudets.h"
469 #include "shipfx.h"
470 #include "shiphit.h"
471 #include "aibig.h"
472 #include "multiutil.h"
473 #include "hud.h"
474 #include "objcollide.h"
475 #include "asteroid.h"
476 #include "hudlock.h"
477 #include "missiontraining.h"
478 #include "gamesequence.h"
479 #include "joy_ff.h"
480 #include "localize.h"
481 #include "flak.h"
482 #include "beam.h"
483 #include "multi.h"
484 #include "swarm.h"
485 #include "multi_team.h"
486 #include "awacs.h"
487 #include "fvi.h"
488
489 #ifndef PLAT_UNIX
490 #pragma optimize("", off)
491 #pragma auto_inline(off)
492 #endif
493
494 #define UNINITIALIZED_VALUE     -99999.9f
495
496 #define INSTRUCTOR_SHIP_NAME NOX("instructor")
497
498 #define AICODE_SMALL_MAGNITUDE  0.001f          // cosider a vector NULL if mag is less than this
499
500 #define NEXT_REARM_TIMESTAMP (60*1000)                  //      Ships will re-request rearm, typically, after this long.
501
502 #define BEAM_NEBULA_RANGE_REDUCE_FACTOR         0.8
503
504 // AIM_CHASE submode defines
505 // SM_STEALTH_FIND
506 #define SM_SF_AHEAD             0
507 #define SM_SF_BEHIND    1
508 #define SM_SF_BAIL              2
509
510 // SM_STEALTH_SWEEP
511 #define SM_SS_SET_GOAL  -1
512 #define SM_SS_BOX0              0
513 #define SM_SS_LR                        1
514 #define SM_SS_UL                        2
515 #define SM_SS_BOX1              3
516 #define SM_SS_UR                        4
517 #define SM_SS_LL                        5
518 #define SM_SS_BOX2              6
519 #define SM_SS_DONE              7
520
521 //XSTR:OFF
522
523 const char *Mode_text[MAX_AI_BEHAVIORS] = {
524         "CHASE",
525         "EVADE",
526         "GET_BEHIND",
527         "CHASE_LONG",
528         "SQUIGGLE",
529         "GUARD",
530         "AVOID",
531         "WAYPOINTS",
532         "DOCK",
533         "NONE",
534         "BIGSHIP",
535         "PATH",
536         "BE_REARMED",
537         "SAFETY",
538         "EV_WEAPON",
539         "STRAFE",
540         "PLAY_DEAD",
541         "BAY_EMERGE",
542         "BAY_DEPART",
543         "SENTRYGUN",
544         "WARP_OUT",
545 };
546
547 //      Submode text is only valid for CHASE mode.
548 const char *Submode_text[] = {
549 "undefined",
550 "CONT_TURN",
551 "ATTACK   ",
552 "E_SQUIG  ",
553 "E_BRAKE  ",
554 "EVADE    ",
555 "SUP_ATTAK",
556 "AVOID    ",
557 "BEHIND   ",
558 "GET_AWAY ",
559 "E_WEAPON ",
560 "FLY_AWAY ",
561 "ATK_4EVER",
562 "STLTH_FND",
563 "STLTH_SWP",
564 "BIG_APPR",
565 "BIG_CIRC",
566 "BIG_PARL"
567 };
568
569 const char *Strafe_submode_text[5] = {
570 "ATTACK",
571 "AVOID",
572 "RETREAT1",
573 "RETREAT2",
574 "POSITION"
575 };
576 //XSTR:ON
577
578 /*
579 //      Used for global ignore of objects.  If an object appears in the Ignore_objects array,
580 //      no one will attack it.
581 #define MAX_IGNORE_OBJECTS      16
582 typedef struct {
583         int     objnum;
584         int     signature;
585 } ignore_object;
586
587 ignore_object   Ignore_objects[MAX_IGNORE_OBJECTS];
588 */
589
590 typedef struct eval_enemy_obj_struct {
591         int                     turret_parent_objnum;                   // parent of turret
592         float                   weapon_travel_dist;                             // max targeting range of turret weapon
593         int                     enemy_team_mask;
594         int                     weapon_system_ok;                                       // is the weapon subsystem of turret ship ok
595         int                     big_only_flag;                                          // turret fires only at big and huge ships
596         vector          *tpos;
597         vector          *tvec;
598         ship_subsys *turret_subsys;
599         int                     current_enemy;
600
601
602         float                   nearest_attacker_dist;                  // nearest ship 
603         int                     nearest_attacker_objnum;
604
605         float                   nearest_homing_bomb_dist;               // nearest homing bomb
606         int                     nearest_homing_bomb_objnum;
607
608         float                   nearest_bomb_dist;                              // nearest non-homing bomb
609         int                     nearest_bomb_objnum;
610
611         float                   nearest_dist;                                           // nearest ship attacking this turret
612         int                     nearest_objnum;
613 }       eval_enemy_obj_struct;
614
615
616 control_info    AI_ci;
617
618 object *Pl_objp;
619 object *En_objp;
620
621 waypoint_list Waypoint_lists[MAX_WAYPOINT_LISTS];
622
623 // How close a turret has to be point at its target before it
624 // can fire.  If the dot of the gun normal and the vector from gun
625 // to target is greater than this, the turret fires.  The smaller
626 // the sloppier the shooting.
627 #define AICODE_TURRET_DUMBFIRE_ANGLE            (0.8f)  
628 #define AICODE_TURRET_HEATSEEK_ANGLE            (0.7f)  
629 #define AICODE_TURRET_MAX_TIME_IN_RANGE (5.0f)
630
631 #define REARM_SOUND_DELAY               (3*F1_0)                //      Amount of time to delay rearm/repair after mode start
632 #define REARM_BREAKOFF_DELAY    (3*F1_0)                //      Amount of time to wait after fully rearmed to breakoff.
633
634 #define MIN_DIST_TO_WAYPOINT_GOAL       5.0f
635 #define MAX_GUARD_DIST                                  250.0f
636 #define BIG_GUARD_RADIUS                                500.0f
637
638 #define MAX_EVADE_TIME                  (15 * 1000)     //      Max time to evade a weapon.
639
640 // defines for repair ship stuff.
641 #define MAX_REPAIR_SPEED                        25.0f
642 #define MAX_UNDOCK_ABORT_SPEED  2.0f
643
644 // defines for EMP effect stuff
645 #define MAX_EMP_INACCURACY              50.0f
646
647 // defines for stealth
648 #define MAX_STEALTH_INACCURACY  50.0f           // at max view dist
649 #define STEALTH_MAX_VIEW_DIST   400             // dist at which 1) stealth no longer visible 2) firing inaccuracy is greatest
650 #define STEALTH_VIEW_CONE_DOT   0.707           // (half angle of 45 degrees)
651
652
653 ai_class        Ai_classes[MAX_AI_CLASSES];
654 int     Ai_firing_enabled = 1;
655 int     Num_ai_classes;
656
657 int     AI_FrameCount = 0;
658 int     Ship_info_inited = 0;
659 int     AI_watch_object = 0; // Debugging, object to spew debug info for.
660 int     Num_waypoint_lists = 0;
661 int     Mission_all_attack = 0;                                 //      !0 means all teams attack all teams.
662
663 const char *Skill_level_names(int level, int translate)
664 {
665         const char *str = NULL;
666
667         #if NUM_SKILL_LEVELS != 5
668         #error Number of skill levels is wrong!
669         #endif
670
671         if(translate){
672                 switch( level ) {
673                 case 0:
674                         str = XSTR("Very Easy", 469);
675                         break;
676                 case 1:
677                         str = XSTR("Easy", 470);
678                         break;
679                 case 2:
680                         str = XSTR("Medium", 471);
681                         break;
682                 case 3:
683                         str = XSTR("Hard", 472);
684                         break;
685                 case 4:
686                         str = XSTR("Insane", 473);
687                         break;
688                 default:        
689                         Int3();
690                 }
691         } else {
692                 switch( level ) {
693                 case 0:
694                         str = NOX("Very Easy");
695                         break;
696                 case 1:
697                         str = NOX("Easy");
698                         break;
699                 case 2:
700                         str = NOX("Medium");
701                         break;
702                 case 3:
703                         str = NOX("Hard");
704                         break;
705                 case 4:
706                         str = NOX("Insane");
707                         break;
708                 default:        
709                         Int3();
710                 }
711         }
712
713         return str;
714 }
715
716 #define DELAY_TARGET_TIME       (12*1000)               //      time in milliseconds until a ship can target a new enemy after an order.
717
718 //      Make enemy ships turn more slowly at lower skill levels.
719 float   Turn_time_skill_level_scale[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.6f, 1.3f, 1.0f};
720
721 //      Maximum number of simultaneous homing weapons on player based on skill level.
722 int     Max_allowed_player_homers[NUM_SKILL_LEVELS] = {2, 3, 4, 7, 99};
723
724 //      Number of ships that can attack another ship at a given skill level.
725 int     Skill_level_max_attackers[NUM_SKILL_LEVELS] = {2, 3, 4, 5, 99};
726
727 //      How long until next predict position.
728 fix Skill_level_delay[NUM_SKILL_LEVELS] = {2*F1_0, 3*F1_0/2, 4*F1_0/3, F1_0/2, 0};
729
730 //      AI ships link primary weapons if energy levels greater than the following amounts:
731 float   Link_energy_levels_always[NUM_SKILL_LEVELS] = {100.0f, 80.0f, 60.0f, 40.0f, 20.0f};     //      always link
732 float   Link_energy_levels_maybe[NUM_SKILL_LEVELS] = {90.0f, 60.0f, 40.0f, 20.0f, 10.0f};       //      link if hull strength low
733
734 //      Seconds to add to time it takes to get enemy in range.  Only for player's enemies.
735 float   In_range_time[NUM_SKILL_LEVELS] = {2.0f, 1.4f, 0.75f, 0.0f, -1.0f};
736
737 //      No matter what, a random unit vector gets scaled by this amount in firing at an enemy.
738 //      Note that for shorter in-range times, these values get scaled, so a value of 0.5f is meaningful.
739 float   Aiming_error[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.3f, 0.7f, 0.2f};
740
741 //      Chance a countermeasure will be fired based on skill level.
742 float Cmeasure_fire_chance[NUM_SKILL_LEVELS] = {0.2f, 0.3f, 0.5f, 0.9f, 1.1f};  //      Note, this gets scaled by ai_class
743
744 float Shield_manage_delays[NUM_SKILL_LEVELS] = {5.0f, 4.0f, 2.5f, 1.2f, 0.1f};
745
746 // accuracy we feed into the beam weapons based upon skill system
747 // float Beam_accuracy[NUM_SKILL_LEVELS] = {2.0f, 1.5f, 1.0f, 0.7f, 0.4f};
748
749 extern float Ship_fire_delay_scale_hostile[NUM_SKILL_LEVELS];
750 extern float Ship_fire_delay_scale_friendly[NUM_SKILL_LEVELS];
751
752 pnode           Path_points[MAX_PATH_POINTS];
753 pnode           *Ppfp;                  //      Free pointer in path points.
754
755 float   AI_frametime;
756
757 char *Ai_class_names[MAX_AI_CLASSES];
758
759 // global for rearm status for teams
760 int Ai_friendly_rearm_timestamp, Ai_hostile_rearm_timestamp, Ai_neutral_rearm_timestamp, Ai_traitor_rearm_timestamp, Ai_unknown_rearm_timestamp;
761
762 // globals for dealing with when to fire huge secondary weapons
763 #define MAX_HUGE_SECONDARY_INFO 10
764
765 typedef struct {
766         int team;
767         int weapon_index;
768         int max_fire_count;
769         char    *shipname;
770 } huge_fire_info;
771
772 huge_fire_info Ai_huge_fire_info[MAX_HUGE_SECONDARY_INFO];
773
774 int Ai_last_arrive_path;        // index of ship_bay path used by last arrival from a fighter bay
775
776 // forward declarations
777 int     ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index);
778 void    create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count=1);
779 void    copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt=-1);
780
781 // ai_set_rearm_status takes a team (friendly, hostile, neutral) and a time.  This function
782 // sets the timestamp used to tell is it is a good time for this team to rearm.  Once the timestamp
783 // is no longer valid, then rearming is not a "good time"
784 // not safe.  Called from sexpression code.
785 void ai_set_rearm_status( int team, int time )
786 {
787         SDL_assert( time >= 0 );
788
789         switch (team) {
790         case TEAM_FRIENDLY:
791                 Ai_friendly_rearm_timestamp = timestamp( time * 1000 );
792                 break;
793         case TEAM_HOSTILE:
794                 Ai_hostile_rearm_timestamp = timestamp( time * 1000 );
795                 break;
796         case TEAM_NEUTRAL:
797                 Ai_neutral_rearm_timestamp = timestamp( time * 1000 );
798                 break;
799         case TEAM_TRAITOR:
800                 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
801                 break;
802         case TEAM_UNKNOWN:
803                 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
804                 break;
805         default:
806                 Int3();
807                 break;
808         }
809 }
810
811 // int ai_good_time_to_rearm returns true(1) or false(0) if it is "safe" for the given
812 // object to rearm.  "safe" is currently defined by the mission designer using the good/bad
813 // time to rearm sexpressions.  This status is currently team based.  This function could
814 // be easily expended to further the definition of "safe"
815 int ai_good_time_to_rearm( object *objp )
816 {
817         int team, status;
818
819         SDL_assert(objp->type == OBJ_SHIP);
820         team = Ships[objp->instance].team;
821         status = 0;
822
823         switch(team) {
824         case TEAM_FRIENDLY:
825                 status = timestamp_valid(Ai_friendly_rearm_timestamp);
826                 break;
827         case TEAM_HOSTILE:
828                 status = timestamp_valid(Ai_hostile_rearm_timestamp);
829                 break;
830         case TEAM_NEUTRAL:
831                 status = timestamp_valid(Ai_neutral_rearm_timestamp);
832                 break;
833         case TEAM_TRAITOR:
834                 status = timestamp_valid(Ai_traitor_rearm_timestamp);
835                 break;
836         case TEAM_UNKNOWN:
837                 status = timestamp_valid(Ai_unknown_rearm_timestamp);
838                 break;
839         default:
840                 Int3();
841                 break;
842         }
843
844         return status;
845 }
846
847 // functions to deal with letting the ai know about good times to fire powerful secondary
848 // weapons.
849
850 // this function is entry point from sexpression code to set internal data for use by ai code.
851 void ai_good_secondary_time( int team, int weapon_index, int max_fire_count, char *shipname )
852 {
853         int i, index;
854
855         // find an open slot to put this data
856         for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
857                 if ( Ai_huge_fire_info[i].weapon_index == -1 )
858                         break;
859         }
860
861         SDL_assert( i < MAX_HUGE_SECONDARY_INFO );                      // we've run out of room
862
863         Ai_huge_fire_info[i].weapon_index = weapon_index;
864         Ai_huge_fire_info[i].team = team;
865         Ai_huge_fire_info[i].max_fire_count = max_fire_count;
866
867         Ai_huge_fire_info[i].shipname = ai_get_goal_ship_name( shipname, &index );
868 }
869
870 // function called internally to the ai code to tell whether or not weapon_num can be fired
871 // from firer_objp at target_objp.  This function will resolve the team for the firer.
872 // returns:
873 //              -1  -- when conditions don't allow firer to fire weapon_num on target_objp
874 //              >=0 -- when conditions allow firer to fire.  Return value is max number of weapon_nums
875 //           which can be fired on target_objp
876 int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
877 {
878         int i, firer_team, target_signature;
879         ship *firer_ship;
880         huge_fire_info *hfi = NULL;
881
882         SDL_assert( firer_objp->type == OBJ_SHIP );
883         firer_ship = &Ships[firer_objp->instance];
884         firer_team = firer_ship->team;
885
886         // get target object's signature and try to find it in the list.
887         target_signature = target_objp->signature;
888         for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
889                 int ship_index, signature;
890
891                 hfi = &Ai_huge_fire_info[i];
892                 if ( hfi->weapon_index == -1 )
893                         continue;
894
895                 ship_index = ship_name_lookup( hfi->shipname );
896                 if ( ship_index == -1 )
897                         continue;
898
899                 signature = Objects[Ships[ship_index].objnum].signature;
900
901                 // sigatures, weapon_index, and team must match
902                 if ( (signature == target_signature) && (hfi->weapon_index == weapon_num) && (hfi->team == firer_team) )
903                         break;
904         }
905
906         // return -1 if not found
907         if ( i == MAX_HUGE_SECONDARY_INFO )
908                 return -1;
909
910         // otherwise, we can return the max number of weapons we can fire against target_objps
911
912         return hfi->max_fire_count;
913 }
914
915 // function to clear out secondary firing infomration between levels
916 void ai_init_secondary_info()
917 {
918         int i;
919
920         // clear out the data for dealing with when ai ships can fire huge secondary weapons
921         for (i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
922                 Ai_huge_fire_info[i].weapon_index = -1;
923                 Ai_huge_fire_info[i].team = -1;
924                 Ai_huge_fire_info[i].max_fire_count = -1;
925                 Ai_huge_fire_info[i].shipname = NULL;
926         }
927 }
928
929
930 //      Garbage collect the Path_points buffer.
931 //      Scans all objects, looking for used Path_points records.
932 //      Compresses Path_points buffer, updating aip->path_start and aip->path_cur indices.
933 //      Updates Ppfp to point to first free record.
934 //      This function is fairly fast.  Its worst-case running time is proportional to
935 //      3*MAX_PATH_POINTS + MAX_OBJECTS
936 //      Things to do to optimize this function:
937 //              1. if (t != 0) xlt++; can be replaced by xlt += t; assuming t can only be 0 or 1.
938 //              2. When pp_xlate is getting stuffed the first time, note highest index and use that 
939 //                      instead of MAX_PATH_POINTS in following two for loops.
940 void garbage_collect_path_points()
941 {
942         int     i;
943         int     pp_xlate[MAX_PATH_POINTS];
944         object  *A;
945         ship_obj        *so;
946
947         //      Scan all objects and create Path_points xlate table.
948         for (i=0; i<MAX_PATH_POINTS; i++)
949                 pp_xlate[i] = 0;
950
951         //      in pp_xlate, mark all used Path_point records
952         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
953                 A = &Objects[so->objnum];
954                 ship    *shipp = &Ships[A->instance];
955                 if (shipp->ai_index != -1) {
956                         ai_info *aip = &Ai_info[shipp->ai_index];
957
958                         if ((aip->path_length > 0) && (aip->path_start > -1)) {
959
960                                 for (int i=aip->path_start; i<aip->path_start + aip->path_length; i++) {
961                                         SDL_assert(pp_xlate[i] == 0);   //      If this is not 0, then two paths use this point!
962                                         pp_xlate[i] = 1;
963                                 }
964                         }
965                 }
966         }
967
968         //      Now, stuff xlate index in pp_xlate.  This is the number to translate any path_start
969         //      or path_cur index to.
970         int     xlt = 0;
971         for (i=0; i<MAX_PATH_POINTS; i++) {
972                 int     t = pp_xlate[i];
973
974                 pp_xlate[i] = xlt;
975                 if (t != 0)
976                         xlt++;
977         }
978         
979         //      Update global Path_points free pointer.
980         Ppfp = &Path_points[xlt];
981
982         //      Now, using pp_xlate, fixup all aip->path_cur and aip->path_start indices
983         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
984                 A = &Objects[so->objnum];
985                 ship    *shipp = &Ships[A->instance];
986                 if (shipp->ai_index != -1) {
987                         ai_info *aip = &Ai_info[shipp->ai_index];
988
989                         if ((aip->path_length > 0) && (aip->path_start > -1)) {
990                                 SDL_assert(aip->path_start < MAX_PATH_POINTS);
991                                 aip->path_start = pp_xlate[aip->path_start];
992
993                                 SDL_assert((aip->path_cur >= 0) && (aip->path_cur < MAX_PATH_POINTS));
994                                 aip->path_cur = pp_xlate[aip->path_cur];
995                         }
996                 }
997         }
998
999         //      Now, compress the buffer.
1000         for (i=0; i<MAX_PATH_POINTS; i++)
1001                 if (i != pp_xlate[i])
1002                         Path_points[pp_xlate[i]] = Path_points[i];
1003
1004 }
1005
1006 //      Hash two values together, return result.
1007 //      Hash function: curval shifted right circular by one, newval xored in.
1008 int hash(unsigned int curval, int newval)
1009 {
1010         int     addval = curval & 1;
1011
1012         curval >>= 1;
1013         if (addval)
1014                 curval |= 0x80000000;
1015         curval ^= newval;
1016
1017         return curval;
1018 }
1019
1020 //      Hash some information in an object together.
1021 //      On 2/20/97, the information is position and orientation.
1022 int create_object_hash(object *objp)
1023 {
1024         int     *ip;
1025         unsigned int    hashval = 0;
1026         int     i;
1027
1028         ip = (int *) &objp->orient;
1029
1030         for (i=0; i<9; i++) {
1031                 hashval = hash(hashval, *ip);
1032                 ip++;
1033         }
1034
1035         ip = (int *) &objp->pos;
1036
1037         for (i=0; i<3; i++) {
1038                 hashval = hash(hashval, *ip);
1039                 ip++;
1040         }
1041
1042         return hashval;
1043 }
1044
1045 //      Stuff a list of NUM_SKILL_LEVELS floats at *plist.
1046 void parse_float_list(float *plist)
1047 {
1048         int     i;
1049
1050         for (i=0; i<NUM_SKILL_LEVELS; i++) {
1051                 stuff_float(&plist[i]);
1052         }
1053 }
1054
1055 void parse_ai_class()
1056 {
1057         ai_class        *aicp = &Ai_classes[Num_ai_classes];
1058
1059         required_string("$Name:");
1060         stuff_string(aicp->name, F_NAME, NULL);
1061
1062         Ai_class_names[Num_ai_classes] = aicp->name;
1063
1064         required_string("$accuracy:");
1065         parse_float_list(aicp->ai_accuracy);
1066
1067         required_string("$evasion:");
1068         parse_float_list(aicp->ai_evasion);
1069
1070         required_string("$courage:");
1071         parse_float_list(aicp->ai_courage);
1072
1073         required_string("$patience:");
1074         parse_float_list(aicp->ai_patience);
1075 }
1076
1077 void parse_aitbl()
1078 {
1079         // open localization
1080         lcl_ext_open();
1081
1082         read_file_text("ai.tbl");
1083
1084         reset_parse();
1085
1086         Num_ai_classes = 0;
1087
1088         required_string("#AI Classes");
1089
1090         while (required_string_either("#End", "$Name:")) {
1091                 SDL_assert( Num_ai_classes < MAX_AI_CLASSES);
1092
1093                 parse_ai_class();
1094
1095                 Num_ai_classes++;
1096         }
1097
1098         // close localization
1099         lcl_ext_close();
1100 }
1101
1102 static int ai_inited = 0;
1103
1104 //========================= BOOK-KEEPING FUNCTIONS =======================
1105
1106 // Called once at game start-up
1107 void ai_init()
1108 {
1109         if ( !ai_inited )       {
1110                 // Do the first time initialization stuff here
1111                 try {
1112                         parse_aitbl();                  
1113                 } catch (parse_error_t rval) {
1114                         Error(LOCATION, "Error parsing 'ai.tbl'\r\nError code = %i.\r\n", (int)rval);
1115                 }
1116
1117                 ai_inited = 1;
1118         }
1119
1120         init_semirand();
1121         
1122         ai_level_init();
1123 }
1124
1125 // this inits the ai.  You should be able to call this between
1126 // levels to reset everything.
1127 void ai_level_init()
1128 {
1129         int i;
1130  
1131         // Do the stuff to reset all ai stuff here
1132         for (i=0; i<MAX_AI_INFO ; i++) {
1133                 Ai_info[i].shipnum = -1;
1134         }
1135         Ai_goal_signature = 0;
1136         Ai_friendly_rearm_timestamp = timestamp(-1);
1137         Ai_hostile_rearm_timestamp = timestamp(-1);
1138         Ai_neutral_rearm_timestamp = timestamp(-1);
1139         Ai_traitor_rearm_timestamp = timestamp(-1);
1140
1141         // clear out the stuff needed for AI firing powerful secondary weapons
1142         ai_init_secondary_info();
1143
1144         Ai_last_arrive_path=0;
1145 }
1146
1147 // BEGIN STEALTH
1148 // -----------------------------------------------------------------------------
1149 // Check if object is a stealth ship
1150 int is_object_stealth_ship(object* objp)
1151 {
1152         if (objp->type == OBJ_SHIP) {
1153                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_STEALTH) {
1154                         return 1;
1155                 }
1156         }
1157
1158         // not stealth ship
1159         return 0;
1160 }
1161
1162 // -----------------------------------------------------------------------------
1163 // Init necessary ai info for new stealth target
1164 void init_ai_stealth_info(ai_info *aip, object *stealth_objp)
1165 {
1166         SDL_assert(is_object_stealth_ship(stealth_objp));
1167
1168         // set necessary ai info for new stealth target
1169         aip->stealth_last_pos = stealth_objp->pos;
1170         aip->stealth_velocity = stealth_objp->phys_info.vel;
1171         aip->stealth_last_visible_stamp = timestamp();
1172 }
1173
1174 // -----------------------------------------------------------------------------
1175 // Check whether Pl_objp can see a stealth ship object
1176 #define STEALTH_INVISIBLE                       0
1177 #define STEALTH_VISIBLE                         1
1178 #define STEALTH_FULLY_TARGETABLE        2
1179
1180 float get_skill_stealth_dist_scaler()
1181 {
1182         // return dist scaler based on skill level
1183         switch (Game_skill_level) {
1184         case 0: // very easy
1185                 return 0.65f;
1186
1187         case 1: // easy
1188                 return 0.9f;
1189
1190         case 2: // medium
1191                 return 1.0f;
1192
1193         case 3: // hard
1194                 return 1.1f;
1195
1196         case 4: // insane
1197                 return 1.3f;
1198
1199         default:
1200                 Int3();
1201         }
1202
1203         return 1.0f;
1204 }
1205
1206 float get_skill_stealth_dot_scaler()
1207 {
1208         // return multiplier on dot based on skill level
1209         switch (Game_skill_level) {
1210         case 0: // very easy
1211                 return 1.3f;
1212
1213         case 1: // easy
1214                 return 1.1f;
1215
1216         case 2: // medium
1217                 return 1.0f;
1218
1219         case 3: // hard
1220                 return 0.9f;
1221
1222         case 4: // insane
1223                 return 0.7f;
1224
1225         default:
1226                 Int3();
1227         }
1228
1229         return 1.0f;
1230 }
1231
1232 int ai_is_stealth_visible(object *viewer_objp, object *stealth_objp)
1233 {
1234         vector vec_to_stealth;
1235         float dot_to_stealth, dist_to_stealth, max_stealth_dist;
1236
1237         SDL_assert(stealth_objp->type == OBJ_SHIP);
1238         SDL_assert(viewer_objp->type == OBJ_SHIP);
1239
1240         // check if stealth ship
1241         SDL_assert(Ship_info[Ships[stealth_objp->instance].ship_info_index].flags & SIF_STEALTH);
1242
1243         // check if in neb and below awac level for visible
1244         if ( !ship_is_visible_by_team(stealth_objp->instance, Ships[viewer_objp->instance].team) ) {
1245                 vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &viewer_objp->pos);
1246                 dist_to_stealth = vm_vec_mag_quick(&vec_to_stealth);
1247                 dot_to_stealth = vm_vec_dotprod(&viewer_objp->orient.v.fvec, &vec_to_stealth) / dist_to_stealth;
1248
1249                 // get max dist at which stealth is visible
1250                 max_stealth_dist = get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST;
1251
1252                 // now check if within view frustrum
1253                 float needed_dot_to_stealth;
1254                 if (dist_to_stealth < 100) {
1255                         needed_dot_to_stealth = 0.0f;
1256                 } else {
1257                         needed_dot_to_stealth = get_skill_stealth_dot_scaler() * float(STEALTH_VIEW_CONE_DOT) * (dist_to_stealth / max_stealth_dist);
1258                 }
1259                 if (dot_to_stealth > needed_dot_to_stealth) {
1260                         if (dist_to_stealth < max_stealth_dist) {
1261                                 return STEALTH_VISIBLE;
1262                         }
1263                 }
1264
1265                 // not within frustrum
1266                 return STEALTH_INVISIBLE;
1267         }
1268
1269         // visible by awacs level
1270         return STEALTH_FULLY_TARGETABLE;
1271 }
1272
1273 // END STEALTH
1274
1275 //      Compute dot product of direction vector and forward vector.
1276 //      Direction vector is vector from one object to other object.
1277 //      Forward vector is the forward vector of the ship.
1278 //      If from_dot == NULL, don't fill it in.
1279 float compute_dots(object *objp, object *other_objp, float *to_dot, float *from_dot)
1280 {
1281         vector  v2o;
1282         float           dist;
1283
1284         dist = vm_vec_normalized_dir(&v2o, &other_objp->pos, &objp->pos);
1285
1286         *to_dot = vm_vec_dot(&objp->orient.v.fvec, &v2o);
1287
1288         if (from_dot != NULL)
1289                 *from_dot = - vm_vec_dot(&other_objp->orient.v.fvec, &v2o);
1290
1291         return dist;
1292 }
1293
1294 // -----------------------------------------------------------------------------
1295 // update estimated stealth info
1296 // this is a "cheat" update
1297 // error increases with time not seen, true distance away, dot to enemey
1298 // this is done only if we can not see the stealth target
1299 // need to infer its position either by weapon fire pos or last know pos
1300 void update_ai_stealth_info_with_error(ai_info *aip/*, int no_error*/)
1301 {
1302 //      object *ship;
1303         object *stealth_objp;
1304         /*
1305         float error_time_mult, error_dist_mult, error_dot_mult, error_mult;
1306         float pos_error, vel_error;
1307         vector error_vec, vec_to_stealth;
1308         float dist_to_stealth, dot_to_stealth;
1309         float delta_time, delta_capped;
1310         */
1311
1312         // make sure I am targeting a stealth ship
1313         SDL_assert( is_object_stealth_ship(&Objects[aip->target_objnum]) );
1314         stealth_objp = &Objects[aip->target_objnum];
1315
1316         // my_ship
1317 //      ship = &Objects[Ships[aip->shipnum].objnum];
1318
1319         // if update is due to weapon fire, get exact stealth position
1320 //      if (no_error) {
1321         aip->stealth_last_pos = stealth_objp->pos;
1322         aip->stealth_velocity = stealth_objp->phys_info.vel;
1323         aip->stealth_last_visible_stamp = timestamp();
1324 //              return;
1325 //      }
1326 /*
1327         // get time since last seen
1328         delta_time = 0.001f * (timestamp() - aip->stealth_last_visible_stamp);
1329
1330         // we don't want our "cheat" guess to more off than what we would get from extrapolating from last visible
1331         // only update if stealth info is "old"
1332         if ( (delta_time) < 0.5 ) {
1333                 return;
1334         }
1335
1336         // find vec_to_stealth and dist
1337         vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &ship->pos);
1338         dist_to_stealth = vm_vec_normalize_quick(&vec_to_stealth);
1339         dot_to_stealth = vm_vec_dotprod(&vec_to_stealth, &ship->orient.v.fvec);
1340
1341         // put cap on time
1342         delta_capped = delta_time;
1343         if (delta_time > 5.0) {
1344                 delta_capped = 5.0f;
1345         }
1346
1347         // erorr_time_mult (for 0-5) -> (1-6)
1348         error_time_mult = (1.0f + delta_capped);
1349
1350         // error_dot_mult (-1 to 1) -> (1-3)
1351         error_dot_mult = (2 - dot_to_stealth);
1352
1353         // error_dist_mult (0-1000+) -> (1-4)
1354         error_dist_mult = dist_to_stealth * 4.0f * 0.001f;
1355         if (error_dist_mult < 1) {
1356                 error_dist_mult = 1.0f;
1357         } else if (error_dist_mult > 4) {
1358                 error_dist_mult = 4.0f;
1359         }
1360
1361         // multiply error out
1362         error_mult = error_time_mult * error_dot_mult * error_dist_mult;
1363
1364         float base_pos_error = 10;
1365         float base_vel_error = 2;
1366
1367         // find the position and velocity error magnitude;
1368         pos_error = base_pos_error * error_mult;
1369         vel_error = base_vel_error * error_mult;
1370
1371         // get an error that changes slowly over time
1372         static_randvec( ((int)aip ^ (Missiontime >> 18)) & 7, &error_vec);
1373         vm_vec_zero(&error_vec);
1374
1375         // update pos and vel with error
1376         vm_vec_scale_add(&aip->stealth_velocity, &stealth_objp->phys_info.vel, &error_vec, vel_error);
1377
1378         // revise last "known" position to arrive at last pos with given error
1379         vm_vec_scale_add(&aip->stealth_last_pos, &stealth_objp->pos, &error_vec, pos_error);
1380         vm_vec_scale_add2(&aip->stealth_last_pos, &aip->stealth_velocity, -(0.001f * delta_time));
1381         */
1382 }
1383
1384 //      Update danger_weapon_objnum and signature in ai_info to say this weapon is to be avoided.
1385 void ai_update_danger_weapon(int attacked_objnum, int weapon_objnum)
1386 {
1387         object  *objp, *weapon_objp;
1388         ai_info *aip;
1389         float           old_dist, new_dist;
1390         float           old_dot, new_dot;
1391         object  *old_weapon_objp = NULL;
1392
1393         if ((attacked_objnum == -1) || (weapon_objnum == -1)) {
1394                 return;
1395         }
1396
1397         objp = &Objects[attacked_objnum];
1398
1399         // AL 2-24-98: If this isn't a ship, we don't need to worry about updating weapon_objnum (ie it would be
1400         //                                      an asteroid or bomb).
1401         if ( objp->type != OBJ_SHIP ) {
1402                 return;
1403         }
1404
1405         weapon_objp = &Objects[weapon_objnum];
1406
1407         aip = &Ai_info[Ships[objp->instance].ai_index];
1408
1409         // if my taraget is a stealth ship and is not visible
1410         if (aip->target_objnum >= 0) {
1411                 if ( is_object_stealth_ship(&Objects[aip->target_objnum]) ) {
1412                         if ( ai_is_stealth_visible(objp, &Objects[aip->target_objnum]) == STEALTH_INVISIBLE ) {
1413                                 // and the weapon is coming from that stealth ship
1414                                 if (weapon_objp->parent == aip->target_objnum) {
1415                                         // update my position estimate for stealth ship
1416                                         update_ai_stealth_info_with_error(aip/*, 1*/);
1417                                 }
1418                         }
1419                 }
1420         }
1421
1422         if (aip->danger_weapon_objnum != -1) {
1423                 old_weapon_objp = &Objects[aip->danger_weapon_objnum];
1424                 if ((old_weapon_objp->type == OBJ_WEAPON) && (old_weapon_objp->signature == aip->danger_weapon_signature)) {
1425                         ;
1426                 } else {
1427                         aip->danger_weapon_objnum = -1;
1428                 }
1429         }
1430
1431         new_dist = compute_dots(weapon_objp, objp, &new_dot, NULL);
1432
1433         if (aip->danger_weapon_objnum == -1) {
1434                 if (new_dist < 1500.0f) {
1435                         if (new_dot > 0.5f) {
1436                                 aip->danger_weapon_objnum = weapon_objnum;
1437                                 aip->danger_weapon_signature = weapon_objp->signature;
1438                         }
1439                 }
1440         } else {
1441                 SDL_assert(old_weapon_objp != NULL);
1442                 old_dist = compute_dots(old_weapon_objp, objp, &old_dot, NULL);
1443         
1444                 if (old_dot < 0.5f) {
1445                         aip->danger_weapon_objnum = -1;
1446                         old_dist = 9999.9f;
1447                 }
1448
1449                 if ((new_dot > 0.5f) && (new_dot > old_dot-0.01f)) {
1450                         if (new_dist < old_dist) {
1451                                 aip->danger_weapon_objnum = weapon_objnum;
1452                                 aip->danger_weapon_signature = weapon_objp->signature;
1453                         }
1454                 }
1455         }
1456 }
1457
1458 //      If rvec != NULL, use it to match bank by calling vm_matrix_interpolate.
1459 //      (rvec defaults to NULL)
1460 void ai_turn_towards_vector(vector *dest, object *objp, 
1461                                                                          float frametime, float turn_time, vector *slide_vec, vector *rel_pos, float bank_override, int flags, vector *rvec)
1462 {
1463         //matrix        goal_orient;
1464         matrix  curr_orient;
1465         vector  vel_in, vel_out, desired_fvec, src;
1466         float           delta_time;
1467         physics_info    *pip;
1468         vector  vel_limit, acc_limit;
1469         float           delta_bank;
1470
1471         //      Don't allow a ship to turn if it has no engine strength.
1472         // AL 3-12-98: objp may not always be a ship!
1473         if ( objp->type == OBJ_SHIP ) {
1474                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f)
1475                         return;
1476         }
1477                         
1478         //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));
1479         pip = &objp->phys_info;
1480
1481         vel_in = pip->rotvel;
1482         curr_orient = objp->orient;
1483         delta_time = flFrametime;
1484
1485         SDL_assert(turn_time > 0.0f);
1486         
1487         //      Scale turn_time based on skill level and team.
1488         if (!(flags & AITTV_FAST)){
1489                 if (objp->type == OBJ_SHIP){
1490                         if (Ships[objp->instance].team != Ships[Player_obj->instance].team){
1491                                 turn_time *= Turn_time_skill_level_scale[Game_skill_level];
1492                         }
1493                 }
1494         }
1495
1496         //      Set max turn rate.
1497         vel_limit.xyz.x = 2*PI/turn_time;
1498         vel_limit.xyz.y = 2*PI/turn_time;
1499         vel_limit.xyz.z = 2*PI/turn_time;
1500
1501         //      Set rate at which ship can accelerate to its rotational velocity.
1502         //      For now, weapons just go much faster.
1503         acc_limit = vel_limit;
1504         if (objp->type == OBJ_WEAPON)
1505                 vm_vec_scale(&acc_limit, 8.0f);
1506
1507         src = objp->pos;
1508
1509         if (rel_pos != NULL) {
1510                 vector  gun_point;
1511                 vm_vec_unrotate(&gun_point, rel_pos, &objp->orient);
1512                 vm_vec_add2(&src, &gun_point);
1513         }
1514
1515         vm_vec_normalized_dir(&desired_fvec, dest, &src);
1516
1517         //      Since ship isn't necessarily moving in the direction it's pointing, sometimes it's better
1518         //      to be moving towards goal rather than just pointing.  So, if slide_vec is !NULL, try to
1519         //      make ship move towards goal, not point at goal.
1520         if (slide_vec != NULL) {
1521                 vm_vec_add2(&desired_fvec, slide_vec);
1522                 vm_vec_normalize(&desired_fvec);
1523         }
1524
1525         //      Should be more general case here.  Currently, anything that is not a weapon will bank when it turns.
1526         if (objp->type == OBJ_WEAPON)
1527                 delta_bank = 0.0f;
1528         else if ((bank_override) && (Ships[objp->instance].team & opposing_team_mask(Player_ship->team))) {     //      Theoretically, this will only happen for Shivans.
1529                 delta_bank = bank_override;
1530                 //nprintf(("AI", "%i: %7.3f\n", Framecount, bank_override));
1531         } else {
1532                 delta_bank = vm_vec_dot(&curr_orient.v.rvec, &objp->last_orient.v.rvec);
1533                 delta_bank = 100.0f * (1.0f - delta_bank);
1534                 if (vm_vec_dot(&objp->last_orient.v.fvec, &objp->orient.v.rvec) < 0.0f)
1535                         delta_bank = -delta_bank;
1536
1537                 //nprintf(("AI", "%s: Frame %i: delta bank = %7.3f\n", Ships[objp->instance].ship_name, Framecount, delta_bank));
1538         }
1539
1540         //      Dave Andsager: The non-indented lines here are debug code to help you track down the problem in the physics
1541         //      that is causing ships to inexplicably rotate very far.  If you hit the Int3(), set the next statement to be
1542         //      the one marked "HERE".  (Do this clicking the cursor there, then right clicking.  Choose the right option.)
1543         //      This will allow you to rerun vm_forward_interpolate() with the values that caused the error.
1544         //      Note, you'll need to enable the Int3() about ten lines below.
1545 #ifndef NDEBUG
1546 vector tvec = objp->orient.v.fvec;
1547 vector  vel_in_copy;
1548 matrix  objp_orient_copy;
1549
1550 vel_in_copy = vel_in;
1551 objp_orient_copy = objp->orient;
1552
1553 vel_in = vel_in_copy;   //      HERE
1554 objp->orient = objp_orient_copy;
1555 #endif
1556         if (rvec != NULL) {
1557                 matrix  out_orient, goal_orient;
1558
1559                 vm_vector_2_matrix(&goal_orient, &desired_fvec, NULL, rvec);
1560                 vm_matrix_interpolate(&goal_orient, &curr_orient, &vel_in, delta_time, &out_orient, &vel_out, &vel_limit, &acc_limit);
1561                 objp->orient = out_orient;
1562         } else {
1563                 vm_forward_interpolate(&desired_fvec, &curr_orient, &vel_in, delta_time, delta_bank, &objp->orient, &vel_out, &vel_limit, &acc_limit);
1564         }
1565 #ifndef NDEBUG
1566 if (!((objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE))) {
1567         if (delta_time < 0.25f && vm_vec_dot(&objp->orient.v.fvec, &tvec) < 0.1f)
1568                 Int3(); //      Get Andsager.  A ship has turned too far in one frame.
1569 }
1570 #endif
1571
1572         pip->rotvel = vel_out;
1573 }
1574
1575 void init_ship_info()
1576 {
1577         int     i;
1578
1579         if (Ship_info_inited)
1580                 return;
1581
1582         for (i=0; i<MAX_SHIP_TYPES; i++) {
1583                 Ship_info[i].min_speed = - Ship_info[i].max_rear_vel;
1584                 Ship_info[i].max_accel = Ship_info[i].max_vel.xyz.z;
1585         }
1586
1587         Ship_info_inited = 1;
1588
1589 }
1590
1591 //      Set aip->target_objnum to objnum
1592 //      Update aip->previous_target_objnum.
1593 //      If new target (objnum) is different than old target, reset target_time.
1594 int set_target_objnum(ai_info *aip, int objnum)
1595 {
1596 /*
1597         char    old_name[32], new_name[32];
1598
1599         if (!timestamp_elapsed(aip->ok_to_target_timestamp))
1600                 return aip->target_objnum;
1601
1602         if (Player_ship && (Ships[aip->shipnum].team == Player_ship->team)) {
1603                 if (aip->target_objnum == -1)
1604                         strcpy(old_name, "none");
1605                 else
1606                         strcpy(old_name, Ships[Objects[aip->target_objnum].instance].ship_name);
1607
1608                 if (objnum == -1)
1609                         strcpy(new_name, "none");
1610                 else
1611                         strcpy(new_name, Ships[Objects[objnum].instance].ship_name);
1612
1613                 nprintf(("AI", "Ship %s changing target from %s to %s\n", Ships[aip->shipnum].ship_name, old_name, new_name));
1614         }
1615 */
1616
1617         // AL 2-25-97: Ensure that a protected ship isn't being set as a target (for non-players only)
1618         /*
1619         if ( objnum >= 0 ) {
1620                 if ( !(Objects[Ships[aip->shipnum].objnum].flags & OF_PLAYER_SHIP) ) {
1621                         if ( Objects[objnum].flags & OF_PROTECTED ) {
1622                                 // AL 2-26-97: removing Int3() until issue with setting OF_PROTECTED in ai_set_attack_subsystem()
1623                                 //Int3();                                                               // this should not happen
1624                                 return aip->target_objnum;              // don't change targets
1625                         }
1626                 }
1627         }
1628         */
1629
1630         if ((aip != Player_ai) && (!timestamp_elapsed(aip->ok_to_target_timestamp))) {
1631                 return aip->target_objnum;
1632         }
1633
1634         if (aip->target_objnum == objnum) {
1635                 aip->previous_target_objnum = aip->target_objnum;
1636         } else {
1637                 aip->previous_target_objnum = aip->target_objnum;
1638
1639                 // ignore this assert if a multiplayer observer
1640                 if((Game_mode & GM_MULTIPLAYER) && (aip == Player_ai) && (Player_obj->type == OBJ_OBSERVER)){
1641                 } else {
1642                         SDL_assert(objnum != Ships[aip->shipnum].objnum);       //      make sure not targeting self
1643                 }
1644
1645                 // if stealth target, init ai_info for stealth
1646                 if ( (objnum > 0) && is_object_stealth_ship(&Objects[objnum]) ) {
1647                         init_ai_stealth_info(aip, &Objects[objnum]);
1648                 }
1649
1650                 aip->target_objnum = objnum;
1651                 aip->target_time = 0.0f;
1652                 aip->target_signature = Objects[objnum].signature;
1653                 // clear targeted subsystem
1654                 set_targeted_subsys(aip, NULL, -1);
1655         }
1656         
1657         return aip->target_objnum;
1658 }
1659
1660 int ai_select_primary_weapon(object *objp, object *other_objp, int flags);
1661
1662 //      Make new_subsys the targeted subsystem of ship *aip.
1663 ship_subsys *set_targeted_subsys(ai_info *aip, ship_subsys *new_subsys, int parent_objnum)
1664 {
1665         SDL_assert(aip != NULL);
1666
1667         aip->last_subsys_target = aip->targeted_subsys;
1668         aip->targeted_subsys = new_subsys;
1669         aip->targeted_subsys_parent = parent_objnum;
1670
1671         if ( new_subsys ) {
1672                 // Make new_subsys target
1673                 if (new_subsys->system_info->type == SUBSYSTEM_ENGINE) {
1674                         if ( aip != Player_ai ) {
1675                                 ai_select_primary_weapon(&Objects[Ships[aip->shipnum].objnum], &Objects[parent_objnum], WIF_PUNCTURE);
1676                                 ship_primary_changed(&Ships[aip->shipnum]);     // AL: maybe send multiplayer information when AI ship changes primaries
1677                         }
1678                 }
1679
1680                 if ( aip == Player_ai ) {
1681                         hud_lock_reset(0.5f);
1682                 }
1683
1684         } else {
1685                 // Cleanup any subsys path information if it exists
1686                 ai_big_subsys_path_cleanup(aip);
1687         }
1688         
1689         return aip->targeted_subsys;
1690 }                                                                                         
1691
1692 // called to init the data for single ai object.  At this point,
1693 // the ship and the object and the ai_info are are correctly
1694 // linked together. Ai_info[ai_index].shipnum is the only valid field 
1695 // in ai_info.
1696 //      This is called right when the object is parsed, so you can't assume much
1697 //      has been initialized.  For example, wings, waypoints, goals are probably
1698 //      not yet loaded. --MK, 10/8/96
1699 void ai_object_init(object * obj, int ai_index)
1700 {
1701         ai_info *aip;
1702         SDL_assert(ai_index >= 0 && ai_index < MAX_AI_INFO);
1703
1704         aip = &Ai_info[ai_index];
1705
1706         aip->type = 0;          //      0 means not in use.
1707         aip->wing = -1;         //      Member of what wing? -1 means none.
1708         aip->ai_class = Ship_info[Ships[obj->instance].ship_info_index].ai_class;
1709         aip->behavior = AIM_NONE;
1710 }
1711
1712 //      If *aip is docked, set max acceleration to A->mass/(A->mass + B->mass) where A is *aip and B is dock object
1713 void adjust_accel_for_docking(ai_info *aip)
1714 {
1715         if (aip->dock_objnum != -1) {
1716                 object  *obj2p = &Objects[aip->dock_objnum];
1717                 object  *obj1p;
1718
1719                 obj1p = &Objects[Ships[aip->shipnum].objnum];
1720
1721                 if (obj2p->signature == aip->dock_signature) {
1722                         float   ratio;
1723
1724                         ratio = obj1p->phys_info.mass / (obj1p->phys_info.mass + obj2p->phys_info.mass);
1725
1726                         // put cap on how much ship can slow down
1727 #ifdef MAKE_FS1
1728                         // FS1 can go slower, perhaps down to 0, but I'll cap it at .25 just in case
1729                         if (ratio < 0.25f) {
1730                                 ratio = 0.25f;
1731                         }
1732 #else
1733                         if (ratio < 0.8) {
1734                                 ratio = 0.8f;
1735                         }
1736 #endif
1737
1738                         if (AI_ci.forward > ratio) {
1739                                 AI_ci.forward = ratio;
1740                         }
1741                 }
1742         }
1743 }
1744
1745 // -------------------------------------------------------------------
1746 void accelerate_ship(ai_info *aip, float accel)
1747 {
1748         aip->prev_accel = accel;
1749         AI_ci.forward = accel;
1750         adjust_accel_for_docking(aip);
1751 }
1752
1753 //      --------------------------------------------------------------------------
1754 void change_acceleration(ai_info *aip, float delta_accel)
1755 {
1756         float   new_accel;
1757
1758         if (delta_accel < 0.0f) {
1759                 if (aip->prev_accel > 0.0f)
1760                         aip->prev_accel = 0.0f;
1761         } else if (aip->prev_accel < 0.0f)
1762                 aip->prev_accel = 0.0f;
1763
1764         new_accel = aip->prev_accel + delta_accel * flFrametime;
1765
1766         if (new_accel > 1.0f)
1767                 new_accel = 1.0f;
1768         else if (new_accel < -1.0f)
1769                 new_accel = -1.0f;
1770         
1771         aip->prev_accel = new_accel;
1772
1773         AI_ci.forward = new_accel;
1774         adjust_accel_for_docking(aip);
1775 }
1776
1777 void set_accel_for_target_speed(object *objp, float tspeed)
1778 {
1779         float   max_speed;
1780         ai_info *aip;
1781
1782         aip = &Ai_info[Ships[objp->instance].ai_index];
1783
1784         max_speed = Ships[objp->instance].current_max_speed;
1785
1786         AI_ci.forward = tspeed/max_speed;
1787         aip->prev_accel = AI_ci.forward;
1788
1789         adjust_accel_for_docking(aip);
1790 }
1791
1792 //      Stuff perim_point with a point on the perimeter of the sphere defined by object *objp
1793 //      on the vector from the center of *objp through the point *vp.
1794 void project_point_to_perimeter(vector *perim_point, vector *pos, float radius, vector *vp)
1795 {
1796         vector  v1;
1797         float           mag;
1798
1799         vm_vec_sub(&v1, vp, pos);
1800         mag = vm_vec_mag(&v1);
1801
1802         if (mag == 0.0f) {
1803                 Warning(LOCATION, "projectable point is at center of sphere.");
1804                 (void) vm_vec_make(&v1, 0.0f, radius, 0.0f);
1805         } else {
1806                 vm_vec_normalize(&v1);
1807                 vm_vec_scale(&v1, 1.1f * radius + 10.0f);
1808         }
1809
1810         vm_vec_add2(&v1, pos);
1811         *perim_point = v1;
1812 }
1813
1814 //      Stuff tan1 with tangent point on sphere.  tan1 is point nearer to *p1
1815 //      *p0 is point through which tangents pass.
1816 //      *centerp is center of sphere.
1817 //      *p1 is another point in space to define the plane in which tan1, tan2 reside.
1818 //      radius is the radius of the sphere.
1819 //      Note, this is a very approximate function just for AI.
1820 //      Note also: On 12/26/96, p1 is used to define the plane perpendicular to that which
1821 //      contains the tangent point.
1822 void get_tangent_point(vector *tan1, vector *p0, vector *centerp, vector *p1, float radius)
1823 {
1824         vector  dest_vec, v2c, perp_vec, temp_vec, v2;
1825         float           dist, ratio;
1826
1827         //      Detect condition of point inside sphere.
1828         if (vm_vec_dist(p0, centerp) < radius)
1829                 project_point_to_perimeter(tan1, centerp, radius, p0);
1830         else {
1831                 vm_vec_normalized_dir(&v2c, centerp, p0);
1832
1833                 //      Compute perpendicular vector using p0, centerp, p1
1834                 vm_vec_normal(&temp_vec, p0, centerp, p1);
1835                 vm_vec_sub(&v2, centerp, p0);
1836                 vm_vec_cross(&perp_vec, &temp_vec, &v2);
1837
1838                 vm_vec_normalize(&perp_vec);
1839
1840                 dist = vm_vec_dist_quick(p0, centerp);
1841                 ratio = dist / radius;
1842
1843                 if (ratio < 2.0f)
1844                         vm_vec_scale_add(&dest_vec, &perp_vec, &v2c, ratio-1.0f);
1845                 else
1846                         vm_vec_scale_add(&dest_vec, &v2c, &perp_vec, (1.0f + 1.0f/ratio));
1847
1848                 vm_vec_scale_add(tan1, p0, &dest_vec, dist + radius);
1849         }
1850 }
1851
1852 //      --------------------------------------------------------------------------
1853 //      Given an object and a point, turn towards the point, resulting in
1854 // approach behavior.
1855 void turn_towards_point(object *objp, vector *point, vector *slide_vec, float bank_override)
1856 {
1857         ai_info *aip;
1858         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1859         
1860         // check if in formation and if not leader, don't change rotvel.xyz.z (bank to match leader elsewhere)
1861         if (aip->ai_flags & AIF_FORMATION) {
1862                 if (&Objects[aip->goal_objnum] != objp) {
1863                         float rotvel_z = objp->phys_info.rotvel.xyz.z;
1864                         ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1865                         objp->phys_info.rotvel.xyz.z = rotvel_z;
1866                 }
1867         } else {
1868                 // normal turn
1869                 ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1870         }
1871 }
1872
1873 //      --------------------------------------------------------------------------
1874 //      Given an object and a point, turn away from the point, resulting in avoidance behavior.
1875 //      Note: Turn away at full speed, not scaled down by skill level.
1876 void turn_away_from_point(object *objp, vector *point, float bank_override)
1877 {
1878         vector  opposite_point;
1879
1880         vm_vec_sub(&opposite_point, &objp->pos, point);
1881         vm_vec_add2(&opposite_point, &objp->pos);
1882
1883         ai_turn_towards_vector(&opposite_point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, NULL, NULL, bank_override, AITTV_FAST);
1884 }
1885
1886
1887 //      --------------------------------------------------------------------------
1888 //      Given an object and a point, turn tangent to the point, resulting in
1889 // a circling behavior.
1890 //      Make object *objp turn around the point *point with a radius of radius.
1891 //      Note that this isn't the same as following a circle of radius radius with
1892 //      center *point, but it should be adequate.
1893 //      Note that if you want to circle an object without hitting it, you should use
1894 //      about twice that object's radius for radius, else you'll certainly bump into it.
1895 //      Return dot product to goal point.
1896 float turn_towards_tangent(object *objp, vector *point, float radius)
1897 {
1898         vector  vec_to_point;
1899         vector  goal_point;
1900         vector  perp_point;                             //      point radius away from *point on vector to objp->pos
1901         vector  up_vec, perp_vec;
1902
1903         vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1904         vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.v.fvec);
1905         vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1906
1907         vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1908         if (vm_vec_dot(&objp->orient.v.fvec, &perp_vec) > 0.0f) {
1909                 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, radius);
1910         } else {
1911                 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, -radius);
1912         }
1913
1914 //      Ai_info[Ships[objp->instance].ai_index].goal_point = goal_point;
1915         turn_towards_point(objp, &goal_point, NULL, 0.0f);
1916
1917         vector  v2g;
1918
1919         vm_vec_normalized_dir(&v2g, &goal_point, &objp->pos);
1920         return vm_vec_dot(&objp->orient.v.fvec, &v2g);
1921 }
1922
1923 float turn_toward_tangent_with_axis(object *objp, object *center_objp, float radius)
1924 {
1925         vector r_vec, theta_vec;
1926         vector center_vec, vec_on_cylinder, sph_r_vec;
1927         float center_obj_z;
1928
1929         // find closest z of center objp
1930         vm_vec_sub(&sph_r_vec, &objp->pos, &center_objp->pos);
1931         center_obj_z = vm_vec_dotprod(&sph_r_vec, &center_objp->orient.v.fvec);
1932
1933         // find pt on axis with closest z
1934         vm_vec_scale_add(&center_vec, &center_objp->pos, &center_objp->orient.v.fvec, center_obj_z);
1935
1936         // get r_vec
1937         vm_vec_sub(&r_vec, &objp->pos, &center_vec);
1938 //      float r_mag = vm_vec_normalize_quick(&r_vec);
1939 //      mprintf(("cur_r: %.1f, desired_r: %.1f\n", r_mag, radius));
1940         SDL_assert( (vm_vec_dotprod(&r_vec, &center_objp->orient.v.fvec) < 0.0001));
1941
1942         // get theta vec - perp to r_vec and z_vec
1943         vm_vec_crossprod(&theta_vec, &center_objp->orient.v.fvec, &r_vec);
1944
1945 #ifndef NDEBUG
1946         float mag = vm_vec_normalize(&theta_vec);
1947         SDL_assert(mag > 0.9999 && mag < 1.0001);
1948 #endif
1949
1950         vector temp;
1951         vm_vec_crossprod(&temp, &r_vec, &theta_vec);
1952
1953 #ifndef NDEBUG
1954         float dot = vm_vec_dotprod(&temp, &center_objp->orient.v.fvec);
1955         SDL_assert( dot >0.9999 && dot < 1.0001);
1956 #endif
1957
1958         // find pt on clylinder with closest z
1959         vm_vec_scale_add(&vec_on_cylinder, &center_vec, &r_vec, radius);
1960
1961         vector goal_pt, v2g;
1962         vm_vec_scale_add(&goal_pt, &vec_on_cylinder, &theta_vec, radius);
1963
1964 //      Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pt;
1965         turn_towards_point(objp, &goal_pt, NULL, 0.0f);
1966
1967         vm_vec_normalized_dir(&v2g, &goal_pt, &objp->pos);
1968         return vm_vec_dot(&objp->orient.v.fvec, &v2g);
1969 }
1970
1971 //      Returns a point radius units away from *point that *objp should turn towards to orbit *point
1972 void get_tangent_point(vector *goal_point, object *objp, vector *point, float radius)
1973 {
1974         vector  vec_to_point;
1975         vector  perp_point;                             //      point radius away from *point on vector to objp->pos
1976         vector  up_vec, perp_vec;
1977
1978         vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1979         vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.v.fvec);
1980         vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1981         vm_vec_normalize(&perp_vec);
1982
1983         vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1984
1985         if (vm_vec_dot(&objp->orient.v.fvec, &perp_vec) > 0.0f) {
1986                 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, radius);
1987         } else {
1988                 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, -radius);
1989         }
1990 }
1991
1992 int     Player_attacking_enabled = 1;
1993
1994 // -----------------------------------------------------------------------------
1995 // Determine whether an object is targetable within a nebula
1996 int object_is_targetable(object *target, ship *viewer)
1997 {
1998         int stealth_ship = 0;
1999
2000         // if target is ship, check if visible by team
2001         if (target->type == OBJ_SHIP) {
2002                 stealth_ship = (Ship_info[Ships[target->instance].ship_info_index].flags & SIF_STEALTH);
2003                 if ( ship_is_visible_by_team(target->instance, viewer->team) == 1) {
2004                         return 1;
2005                 }
2006         }
2007
2008         // for AI partially targetable works as fully targetable, except for stealth ship
2009         if (stealth_ship) {
2010                 // if not team targetable, check if within frustrum
2011                 if ( ai_is_stealth_visible(&Objects[viewer->objnum], target) == STEALTH_VISIBLE ) {
2012                         return 1;
2013                 } else {
2014                         return 0;
2015                 }
2016         }
2017
2018         // if not fully targetable by team, check awacs level with viewer
2019         // allow targeting even if only only partially targetable to player
2020         float radar_return = awacs_get_level(target, viewer);
2021         if ( radar_return > 0.4 ) {
2022                 return 1;
2023         } else {
2024                 return 0;
2025         }
2026 }
2027
2028 //      Return number of enemies attacking object objnum
2029 //
2030 // AL 10.26.97: Also include turrets on large ships when couting enemies attacking
2031 int num_enemies_attacking(int objnum)
2032 {
2033         object          *objp;
2034         ship                    *sp;
2035         ship_subsys     *ssp;
2036         ship_obj                *so;
2037         int                     count;
2038
2039         count = 0;
2040
2041         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2042                 objp = &Objects[so->objnum];
2043                 SDL_assert(objp->instance != -1);
2044                 sp = &Ships[objp->instance];
2045
2046                 if (Ai_info[sp->ai_index].target_objnum == objnum)
2047                         count++;
2048
2049                 // consider turrets that may be attacking objnum (but only turrets on SIF_BIG_SHIP ships)
2050                 if ( Ship_info[sp->ship_info_index].flags & SIF_BIG_SHIP ) {
2051
2052                         // loop through all the subsystems, check if turret has objnum as a target
2053                         ssp = GET_FIRST(&sp->subsys_list);
2054                         while ( ssp != END_OF_LIST( &sp->subsys_list ) ) {
2055
2056                                 if ( ssp->system_info->type == SUBSYSTEM_TURRET ) {
2057                                         if ( (ssp->turret_enemy_objnum == objnum) && (ssp->current_hits > 0) ) {
2058                                                 count++;
2059                                         }
2060                                 }
2061                                 ssp = GET_NEXT( ssp );
2062                         } // end while
2063                 }
2064         }
2065
2066         return count;
2067 }
2068
2069 //      Get the team to fire on given an object.
2070 int get_enemy_team_mask(int objnum)
2071 {
2072         int     my_team, enemy_team_mask;
2073
2074         my_team = Ships[Objects[objnum].instance].team;
2075
2076         if (Mission_all_attack) {
2077                 //      All teams attack all teams.
2078                 switch (my_team) {
2079                 case TEAM_FRIENDLY:
2080                         enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2081                         break;
2082                 case TEAM_HOSTILE:
2083                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2084                         break;
2085                 case TEAM_NEUTRAL:
2086                         enemy_team_mask = TEAM_FRIENDLY | TEAM_HOSTILE | TEAM_TRAITOR;
2087                         break;
2088                 case TEAM_UNKNOWN:
2089                         enemy_team_mask = TEAM_HOSTILE;
2090                         break;
2091                 case TEAM_TRAITOR:
2092                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2093                         break;
2094                 default:
2095                         enemy_team_mask = TEAM_HOSTILE;
2096                         Int3();                 //      Illegal value for team!
2097                         break;
2098                 }
2099         } else {
2100                 switch (my_team) {
2101                 case TEAM_FRIENDLY:
2102                         enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2103                         break;
2104                 case TEAM_HOSTILE:
2105                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2106                         break;
2107                 case TEAM_NEUTRAL:
2108                         enemy_team_mask = TEAM_FRIENDLY | TEAM_TRAITOR;
2109                         break;
2110                 case TEAM_UNKNOWN:
2111                         enemy_team_mask = TEAM_HOSTILE;
2112                         break;
2113                 case TEAM_TRAITOR:
2114                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2115                         break;
2116                 default:
2117                         enemy_team_mask = TEAM_HOSTILE;
2118                         Int3();                 //      Illegal value for team!
2119                         break;
2120                 }
2121         }
2122
2123         return enemy_team_mask;
2124 }
2125
2126 //      Scan all the ships in *objp's wing.
2127 //      Return the lowest maximum speed of a ship in the wing.
2128 //      Current maximum speed (based on energy settings) is shipp->current_max_speed
2129 float get_wing_lowest_max_speed(object *objp)
2130 {
2131         ship            *shipp;
2132         ai_info *aip;
2133         float           lowest_max_speed;
2134         int             wingnum;
2135         object  *o;
2136         ship_obj        *so;
2137
2138         SDL_assert(objp->type == OBJ_SHIP);
2139         SDL_assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
2140         shipp = &Ships[objp->instance];
2141         SDL_assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
2142         aip = &Ai_info[shipp->ai_index];
2143
2144         wingnum = aip->wing;
2145
2146         lowest_max_speed = shipp->current_max_speed;
2147
2148         if ( wingnum == -1 )
2149                 return lowest_max_speed;
2150
2151         SDL_assert(wingnum >= 0);
2152
2153         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2154                 o = &Objects[so->objnum];
2155                 ship    *oshipp = &Ships[o->instance];
2156                 ai_info *oaip = &Ai_info[oshipp->ai_index];
2157
2158                 if ((oaip->mode == AIM_WAYPOINTS) && (oaip->wing == wingnum)) {
2159                         //      Note: If a ship in the wing has a super low max speed, probably its engines are disabled.  So, fly along and
2160                         //      ignore the poor guy.
2161                         float   cur_max = oshipp->current_max_speed;
2162
2163                         if (oaip->ai_flags & AIF_DOCKED) {
2164                                 if (oaip->dock_objnum > -1)
2165                                         if (Objects[oaip->dock_objnum].type == OBJ_SHIP) 
2166                                                 cur_max *= o->phys_info.mass/(o->phys_info.mass + Objects[oaip->dock_objnum].phys_info.mass);
2167                         }
2168                                                         
2169                         if ((oshipp->current_max_speed > 5.0f) && (cur_max < lowest_max_speed)) {
2170                                 lowest_max_speed = cur_max;
2171                         }
2172                 }
2173         }
2174
2175         return lowest_max_speed;
2176 }
2177
2178 /*
2179 //      Tell everyone to ignore object objnum.
2180 void set_global_ignore_object(int objnum)
2181 {
2182         int     i;
2183
2184         SDL_assert(Objects[objnum].type == OBJ_SHIP);
2185
2186         nprintf(("AI", "Telling everyone to ignore object %s\n", Ships[Objects[objnum].instance].ship_name));
2187
2188         for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2189                 if (Ignore_objects[i].objnum == -1) {
2190                         Ignore_objects[i].objnum = objnum;
2191                         Ignore_objects[i].signature = Objects[objnum].signature;
2192                         break;
2193                 }
2194         }
2195
2196         if (i == MAX_IGNORE_OBJECTS) {
2197                 //      Couldn't find a free slot, but maybe one of these objects has died.
2198                 for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2199                         int     o = Ignore_objects[i].objnum;
2200                         if (Objects[o].type != OBJ_SHIP)
2201                                 break;          //      Not a ship, so use this slot.
2202                         if (Objects[o].signature != Ignore_objects[i].signature)
2203                                 break;          //      Signatures don't match, so use this slot.
2204                 }
2205
2206                 if (i != MAX_IGNORE_OBJECTS) {
2207                         Ignore_objects[i].objnum = objnum;
2208                         Ignore_objects[i].signature = Objects[objnum].signature;
2209                 } else {
2210                         nprintf(("Warning", "Ignore_objects buffer full.  Stealing a slot to ignore object #%i\n"));
2211                         Int3();
2212
2213                         int     r;
2214
2215                         r = objnum % MAX_IGNORE_OBJECTS;
2216
2217                         Ignore_objects[r].objnum = objnum;
2218                         Ignore_objects[r].signature = Objects[objnum].signature;
2219                 }
2220         }
2221 }
2222
2223 */
2224
2225 //      Determine if object objnum is supposed to be ignored by object with ai_info *aip.
2226 //      Return:
2227 //              TRUE    if objnum is aip->ignore_objnum (and signatures match)
2228 //                              or objnum is in ignore wing
2229 //              FALSE   otherwise
2230 int is_ignore_object(ai_info *aip, int objnum)
2231 {
2232
2233 /*      //      First, scan all objects in global array of objects to be ignored.
2234         for (int i=0; i<MAX_IGNORE_OBJECTS; i++)
2235                 if (Ignore_objects[i].objnum != -1)
2236                         if (objnum == Ignore_objects[i].objnum)
2237                                 if (Objects[Ignore_objects[i].objnum].signature == Ignore_objects[i].signature)
2238                                         return 1;
2239 */
2240
2241         //      Didn't find in global list.  Now check 
2242         if (aip->ignore_objnum == UNUSED_OBJNUM)
2243                 return 0;                                                                       //      Not ignoring anything.
2244         else if (aip->ignore_objnum >= 0) {             //      This means it's ignoring an object, not a wing.
2245                 if (aip->ignore_objnum == objnum) {
2246                         if (Objects[aip->ignore_objnum].signature == aip->ignore_signature) {
2247                                 return 1;
2248                         } else {
2249                                 aip->ignore_objnum = UNUSED_OBJNUM;
2250                                 return 0;
2251                         }
2252                 } else {
2253                         return 0;
2254                 }
2255         } else {                                                                                        //      Ignoring a wing.
2256                 Int3(); // Should never happen.  I thought I removed this behavior! -- MK, 5/17/98
2257                 return 0;
2258 /*              int     ignore_wingnum = -(aip->ignore_objnum + 1);
2259
2260                 SDL_assert(ignore_wingnum < MAX_WINGS);
2261                 SDL_assert(aip->shipnum >= 0);
2262                 return (Ships[Objects[objnum].instance].wingnum == ignore_wingnum);
2263 */      }
2264 }
2265
2266 // -----------------------------------------------------------------------------
2267
2268 // given a ship with bounding box and a point, find the closest point on the bbox
2269 int get_nearest_bbox_point(object *ship_obj, vector *start, vector *box_pt)
2270 {
2271         vector temp, rf_start;
2272         polymodel *pm;
2273         pm = model_get(Ship_info[Ships[ship_obj->instance].ship_info_index].modelnum);
2274
2275         // get start in ship rf
2276         vm_vec_sub(&temp, start, &ship_obj->pos);
2277         vm_vec_rotate(&rf_start, &temp, &ship_obj->orient);
2278
2279         // find box_pt
2280         int inside = project_point_onto_bbox(&pm->mins, &pm->maxs, &rf_start, &temp);
2281
2282         // get box_pt in world rf
2283         vm_vec_unrotate(box_pt, &temp, &ship_obj->orient);
2284         vm_vec_add2(box_pt, &ship_obj->pos);
2285
2286         return inside;
2287 }
2288
2289
2290 typedef struct eval_nearest_objnum {
2291         int     objnum;
2292         object *trial_objp;
2293         int     enemy_team_mask;
2294         int     enemy_wing;
2295         float   range;
2296         int     max_attackers;
2297         int     nearest_objnum;
2298         float   nearest_dist;
2299         int     check_danger_weapon_objnum;
2300 } eval_nearest_objnum;
2301
2302
2303 void evaluate_object_as_nearest_objnum(eval_nearest_objnum *eno)
2304 {
2305         ai_info *aip;
2306         ship_subsys     *attacking_subsystem;
2307
2308         aip = &Ai_info[Ships[Objects[eno->objnum].instance].ai_index];
2309
2310         attacking_subsystem = aip->targeted_subsys;
2311
2312         if ((attacking_subsystem != NULL) || !(eno->trial_objp->flags & OF_PROTECTED)) {
2313                 if ( OBJ_INDEX(eno->trial_objp) != eno->objnum ) {
2314 #ifndef NDEBUG
2315                         if (!Player_attacking_enabled && (eno->trial_objp == Player_obj))
2316                                 return;
2317 #endif
2318                         //      If only supposed to attack ship in a specific wing, don't attack other ships.
2319                         if ((eno->enemy_wing != -1) && (Ships[eno->trial_objp->instance].wingnum != eno->enemy_wing))
2320                                 return;
2321
2322                         //      Don't keep firing at a ship that is in its death throes.
2323                         if (Ships[eno->trial_objp->instance].flags & SF_DYING)
2324                                 return;
2325
2326                         if (is_ignore_object(aip, ((eno->trial_objp)-Objects)))
2327                                 return;
2328
2329                         if (eno->trial_objp->flags & OF_PROTECTED)
2330                                 return;
2331
2332                         if (Ships[eno->trial_objp->instance].flags & SF_ARRIVING)
2333                                 return;
2334
2335                         ship_info *sip = &Ship_info[Ships[eno->trial_objp->instance].ship_info_index];
2336
2337                         if (sip->flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2338                                 return;
2339
2340                         if (Ships[eno->trial_objp->instance].team & eno->enemy_team_mask) {
2341                                 float   dist;
2342                                 int     num_attacking;
2343
2344                                 // Allow targeting of stealth in nebula by his firing at me
2345                                 // This is done for a specific ship, not generally.
2346                                 if ( !eno->check_danger_weapon_objnum ) {
2347                                         // check if can be targeted if inside nebula
2348                                         if ( !object_is_targetable(eno->trial_objp, &Ships[Objects[eno->objnum].instance]) ) {
2349                                                 // check if stealth ship is visible, but not "targetable"
2350                                                 if ( !((sip->flags & SIF_STEALTH) && ai_is_stealth_visible(&Objects[eno->objnum], eno->trial_objp)) ) {
2351                                                         return;
2352                                                 }
2353                                         }
2354                                 }
2355
2356                                 // if objnum is BIG or HUGE, find distance to bbox
2357                                 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
2358                                         vector box_pt;
2359                                         // check if inside bbox
2360                                         int inside = get_nearest_bbox_point(eno->trial_objp, &Objects[eno->objnum].pos, &box_pt);
2361                                         if (inside) {
2362                                                 dist = 10.0f;
2363                                                 // on the box
2364                                         } else {
2365                                                 dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &box_pt);
2366                                         }
2367                                 } else {
2368                                         dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &eno->trial_objp->pos);
2369                                 }
2370                                 
2371                                 //      Make it more likely that fighters (or bombers) will be picked as an enemy by scaling up distance for other types.
2372                                 if ((Ship_info[Ships[eno->trial_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))) {
2373                                         dist = dist * 0.5f;
2374                                 }
2375
2376                                 num_attacking = num_enemies_attacking(eno->trial_objp-Objects);
2377                                 if ((sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) || (num_attacking < eno->max_attackers)) {
2378                                         if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))){
2379                                                 dist *= (float) (num_attacking+2)/2.0f;                         //      prevents lots of ships from attacking same target
2380                                         }
2381
2382                                         if (eno->trial_objp->flags & OF_PLAYER_SHIP){
2383                                                 dist *= 1.0f + (NUM_SKILL_LEVELS - Game_skill_level - 1)/NUM_SKILL_LEVELS;      //      Favor attacking non-players based on skill level.
2384                                         }
2385
2386                                         if (dist < eno->nearest_dist) {
2387                                                 eno->nearest_dist = dist;
2388                                                 eno->nearest_objnum = eno->trial_objp-Objects;
2389                                         }
2390                                 }
2391                         }
2392                 }
2393         }
2394
2395 }
2396
2397
2398 //      Given an object and an enemy team, return the index of the nearest enemy object.
2399 //      Unless aip->targeted_subsys != NULL, don't allow to attack objects
2400 //      with OF_PROTECTED bit set.
2401 //      Ship must be within range "range".
2402 //      Don't attack a ship that already has at least max_attackers attacking it.
2403 int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers)
2404 {
2405         object  *danger_weapon_objp;
2406         ai_info *aip;
2407         ship_obj        *so;
2408
2409         // initialize eno struct
2410         eval_nearest_objnum eno;
2411         eno.enemy_team_mask = enemy_team_mask;
2412         eno.enemy_wing = enemy_wing;
2413         eno.max_attackers = max_attackers;
2414         eno.objnum = objnum;
2415         eno.range = range;
2416         eno.nearest_dist = range;
2417         eno.nearest_objnum = -1;
2418         eno.check_danger_weapon_objnum = 0;
2419
2420         // go through the list of all ships and evaluate as potential targets
2421         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2422                 eno.trial_objp = &Objects[so->objnum];
2423                 evaluate_object_as_nearest_objnum(&eno);
2424
2425         }
2426
2427         // check if danger_weapon_objnum has will show a stealth ship
2428         aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2429         if (aip->danger_weapon_objnum >= 0) {
2430                 danger_weapon_objp = &Objects[aip->danger_weapon_objnum];
2431                 // validate weapon
2432                 if (danger_weapon_objp->signature == aip->danger_weapon_signature) {
2433                         SDL_assert(danger_weapon_objp->type == OBJ_WEAPON);
2434                         // check if parent is a ship
2435                         if (danger_weapon_objp->parent >= 0) {
2436                                 if ( is_object_stealth_ship(&Objects[danger_weapon_objp->parent]) ) {
2437                                         // check if stealthy
2438                                         if ( ai_is_stealth_visible(&Objects[objnum], &Objects[danger_weapon_objp->parent]) != STEALTH_FULLY_TARGETABLE ) {
2439                                                 // check if weapon is laser
2440                                                 if (Weapon_info[Weapons[danger_weapon_objp->instance].weapon_info_index].subtype == WP_LASER) {
2441                                                         // check stealth ship by its laser fire
2442                                                         eno.check_danger_weapon_objnum = 1;
2443                                                         eno.trial_objp = &Objects[danger_weapon_objp->parent];
2444                                                         evaluate_object_as_nearest_objnum(&eno);
2445                                                 }
2446                                         }
2447                                 }
2448                         }
2449                 }
2450         }
2451
2452         //      If only looking for target in certain wing and couldn't find anything in
2453         //      that wing, look for any object.
2454         if ((eno.nearest_objnum == -1) && (enemy_wing != -1)) {
2455                 return get_nearest_objnum(objnum, enemy_team_mask, -1, range, max_attackers);
2456         }
2457
2458         return eno.nearest_objnum;
2459 }
2460
2461 //      Given an object and an enemy team, return the index of the nearest enemy object.
2462 //      Unlike find_enemy or find_nearest_objnum, this doesn't care about things like the protected flag or number
2463 //      of enemies attacking.
2464 //      It is used to find the nearest enemy to determine things like whether to rearm.
2465 int find_nearby_hostile(int objnum, int enemy_team_mask, float range, int *count)
2466 {
2467         int             nearest_objnum;
2468         float           nearest_dist;
2469         object  *objp;
2470         ship_obj        *so;
2471
2472         nearest_objnum = -1;
2473         nearest_dist = range;
2474
2475         *count = 0;
2476
2477         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2478                 objp = &Objects[so->objnum];
2479
2480                 if ( OBJ_INDEX(objp) != objnum ) {
2481                         if (Ships[objp->instance].flags & SF_DYING)
2482                                 continue;
2483
2484                         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2485                                 continue;
2486
2487                         if (Ships[objp->instance].team & enemy_team_mask) {
2488                                 float   dist;
2489
2490                                 dist = vm_vec_dist_quick(&Objects[objnum].pos, &objp->pos) - objp->radius*0.75f;
2491                                 
2492                                 if (dist < range) {
2493                                         (*count)++;
2494
2495                                         if (dist < nearest_dist) {
2496                                                 nearest_dist = dist;
2497                                                 nearest_objnum = objp-Objects;
2498                                         }
2499                                 }
2500                         }
2501                 }
2502         }
2503
2504         return nearest_objnum;
2505 }
2506
2507 // return !0 if objp can be considered for a turret target, 0 otherwise
2508 // input:       objp                            =>      object that turret is considering as an enemy
2509 //                              turret_parent   =>      object index for ship that turret sits on
2510 int valid_turret_enemy(object *objp, object *turret_parent)
2511 {
2512         if ( objp == turret_parent ) {
2513                 return 0;
2514         }
2515
2516         if ( objp->type == OBJ_ASTEROID ) {
2517                 return 1;
2518         }
2519
2520         if ( objp->type == OBJ_SHIP ) {
2521                 ship *shipp;
2522                 shipp = &Ships[objp->instance];
2523
2524                 // don't fire at ships with protected bit set!!!
2525                 if ( objp->flags & OF_PROTECTED ) {
2526                         return 0;
2527                 }
2528
2529                 if ( !(Ship_info[shipp->ship_info_index].flags & SIF_DO_COLLISION_CHECK)) {
2530                         return 0;
2531                 }
2532
2533                 if (shipp->flags & SF_ARRIVING) {
2534                         return 0;
2535                 }
2536
2537                 return 1;
2538         }
2539
2540         if ( objp->type == OBJ_WEAPON ) {
2541                 if ( Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_BOMB ) {
2542                         if ( obj_team(turret_parent) != Weapons[objp->instance].team ) {
2543                                 return 1;
2544                         }
2545                 }
2546         }
2547
2548         return 0;
2549 }
2550
2551 // return 1 if objp is in fov of the specified turret, tp.  Otherwise return 0.
2552 //      dist = distance from turret to center point of object
2553 int object_in_turret_fov(object *objp, model_subsystem *tp, vector *tvec, vector *tpos, float dist)
2554 {
2555         vector  v2e;
2556         float           dot;
2557         vm_vec_normalized_dir(&v2e, &objp->pos, tpos);
2558         dot = vm_vec_dot(&v2e, tvec);
2559
2560         dot += objp->radius / (dist + objp->radius);
2561
2562         if ( dot >= tp->turret_fov ) {
2563                 return 1;
2564         }
2565
2566         return 0;
2567 }
2568
2569 // return 1 if bomb_objp is headed towards ship_objp
2570 int bomb_headed_towards_ship(object *bomb_objp, object *ship_objp)
2571 {
2572         float           dot;
2573         vector  bomb_to_ship_vector;
2574
2575         vm_vec_normalized_dir(&bomb_to_ship_vector, &ship_objp->pos, &bomb_objp->pos);
2576         dot = vm_vec_dot(&bomb_objp->orient.v.fvec, &bomb_to_ship_vector);
2577
2578         if ( dot > 0 ) {
2579                 return 1;
2580         }
2581
2582         return 0;
2583 }
2584
2585 // nubmer of live turrets with target_objnum 
2586 int num_turrets_attacking(object *turret_parent, int target_objnum) 
2587 {
2588         ship_subsys *ss;
2589         ship *shipp;
2590         int count = 0;
2591         shipp = &Ships[turret_parent->instance];
2592
2593         SDL_assert(turret_parent->type == OBJ_SHIP);
2594         SDL_assert(Objects[target_objnum].type == OBJ_SHIP);
2595
2596         for (ss=GET_FIRST(&shipp->subsys_list); ss!=END_OF_LIST(&shipp->subsys_list); ss=GET_NEXT(ss)) {
2597                 // check if subsys is alive
2598                 if (ss->current_hits <= 0.0f) {
2599                         continue;
2600                 }
2601
2602                 // check if it's a turret
2603                 if (ss->system_info->type != SUBSYSTEM_TURRET) {
2604                         continue;
2605                 }
2606
2607                 // if the turret is locked
2608                 if(ss->weapons.flags & SW_FLAG_TURRET_LOCK){
2609                         continue;
2610                 }               
2611
2612                 // check if turret is targeting target_objnum
2613                 if (ss->turret_enemy_objnum == target_objnum) {
2614                         count++;
2615                 }
2616         }
2617
2618         return count;
2619 }
2620
2621 float Lethality_range_const = 2.0f;
2622 DCF(lethality_range, "N for modifying range: 1 / (1+N) at 100")
2623 {
2624         dc_get_arg(ARG_FLOAT);
2625         Lethality_range_const = Dc_arg_float;
2626 }
2627
2628 float Player_lethality_bump[NUM_SKILL_LEVELS] = {
2629         // 0.0f, 5.0f, 10.0f, 25.0f, 40.0f
2630         0.0f, 0.0f, 0.0f, 0.0f, 0.0f
2631 };
2632
2633 // evaluate obj as posssible target for turret
2634 void evaluate_obj_as_target(object *objp, eval_enemy_obj_struct *eeo)
2635 {
2636         object  *turret_parent_obj = &Objects[eeo->turret_parent_objnum];
2637         ship            *shipp;
2638         model_subsystem *tp = eeo->turret_subsys->system_info;
2639         float dist;
2640
2641         // Don't look for bombs when weapon system is not ok
2642         if (objp->type == OBJ_WEAPON && !eeo->weapon_system_ok) {
2643                 return;
2644         }
2645
2646         if ( !valid_turret_enemy(objp, turret_parent_obj) ) {
2647                 return;
2648         }
2649
2650 #ifndef NDEBUG
2651         if (!Player_attacking_enabled && (objp == Player_obj)) {
2652                 return;
2653         }
2654 #endif
2655
2656         if ( objp->type == OBJ_SHIP ) {
2657                 shipp = &Ships[objp->instance];
2658
2659                 // check on enemy team
2660                 if ( !(shipp->team & eeo->enemy_team_mask) ) {
2661                         return;
2662                 }
2663
2664                 // check if protected
2665                 if (objp->flags & OF_PROTECTED) {
2666                         return;
2667                 }
2668
2669                 // check if beam protected
2670                 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2671                         if (objp->flags & OF_BEAM_PROTECTED) {
2672                                 return;
2673                         }
2674                 }
2675
2676                 if (eeo->big_only_flag) {
2677                         if (!(Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2678                                 return;
2679                         }
2680                 }
2681
2682                 // check if     turret flagged to only target tagged ships
2683                 if ( (eeo->turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && !ship_is_tagged(objp) ) {
2684                         return;
2685                 }
2686
2687                 // check if valid target in nebula
2688                 if ( !object_is_targetable(objp, &Ships[Objects[eeo->turret_parent_objnum].instance]) ) {
2689                         // BYPASS ocassionally for stealth
2690                         int try_anyway = FALSE;
2691                         if ( is_object_stealth_ship(objp) ) {
2692                                 float turret_stealth_find_chance = 0.5f;
2693                                 float speed_mod = -0.1f + vm_vec_mag_quick(&objp->phys_info.vel) / 70.0f;
2694                                 if (frand() > (turret_stealth_find_chance + speed_mod)) {
2695                                         try_anyway = TRUE;
2696                                 }
2697                         }
2698
2699                         if (!try_anyway) {
2700                                 return;
2701                         }
2702                 }
2703
2704         } else {
2705                 shipp = NULL;
2706         }
2707
2708         // modify dist for BIG|HUGE, getting closest point on bbox, if not inside
2709         dist = vm_vec_dist_quick(eeo->tpos, &objp->pos) - objp->radius;
2710         if (dist < 0.0f) {
2711                 dist = 0.0f;
2712         }
2713
2714         // check if object is a bomb attacking the turret parent
2715         // check if bomb is homing on the turret parent ship
2716         if (objp->type == OBJ_WEAPON) {
2717                 if ( Weapons[objp->instance].homing_object == &Objects[eeo->turret_parent_objnum] ) {
2718                         if ( dist < eeo->nearest_homing_bomb_dist ) {
2719                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2720                                         eeo->nearest_homing_bomb_dist = dist;
2721                                         eeo->nearest_homing_bomb_objnum = OBJ_INDEX(objp);
2722                                 }
2723                         }
2724                 // if not homing, check if bomb is flying towards ship
2725                 } else if ( bomb_headed_towards_ship(objp, &Objects[eeo->turret_parent_objnum]) ) {
2726                         if ( dist < eeo->nearest_bomb_dist ) {
2727                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2728                                         eeo->nearest_bomb_dist = dist;
2729                                         eeo->nearest_bomb_objnum = OBJ_INDEX(objp);
2730                                 }
2731                         }
2732                 }
2733         } // end weapon section
2734
2735         // maybe recalculate dist for big or huge ship
2736 //      if (shipp && (Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2737 //              fvi_ray_boundingbox(min, max, start, direction, hit);
2738 //              dist = vm_vec_dist_quick(hit, tvec);
2739 //      }
2740
2741         // check for nearest attcker
2742         if ( (shipp) && (dist < eeo->weapon_travel_dist) ) {
2743                 ai_info *aip = &Ai_info[shipp->ai_index];
2744
2745                 // modify distance based on number of turrets from my ship attacking enemy (add 10% per turret)
2746                 // dist *= (num_enemies_attacking(OBJ_INDEX(objp))+2)/2;        //      prevents lots of ships from attacking same target
2747                 int num_att_turrets = num_turrets_attacking(turret_parent_obj, OBJ_INDEX(objp));
2748                 dist *= (1.0f + 0.1f*num_att_turrets);
2749
2750                 // return if we're over the cap
2751                 int max_turrets = 3 + Game_skill_level * Game_skill_level;
2752                 if (num_att_turrets > max_turrets) {
2753                         return;
2754                 }
2755
2756                 // modify distance based on lethality of objp to my ship
2757                 float active_lethality = aip->lethality;
2758                 if (objp->flags & OF_PLAYER_SHIP) {
2759                         active_lethality += Player_lethality_bump[Game_skill_level];
2760                 }
2761
2762                 dist /= (1.0f + 0.01f*Lethality_range_const*active_lethality);
2763
2764                 // Make level 2 tagged ships more likely to be targeted
2765                 if (shipp->level2_tag_left > 0.0f) {
2766                         dist *= 0.3f;
2767                 }
2768
2769                 // check if objp is targeting the turret's ship, or if objp has just hit the turret's ship
2770                 if ( aip->target_objnum == eeo->turret_parent_objnum || aip->last_objsig_hit == Objects[eeo->turret_parent_objnum].signature ) {
2771                         // A turret will always target a ship that is attacking itself... self-preservation!
2772                         if ( aip->targeted_subsys == eeo->turret_subsys ) {
2773                                 dist *= 0.5f;   // highest priority
2774                         }
2775                 }
2776
2777                 // maybe update nearest attacker
2778                 if ( dist < eeo->nearest_attacker_dist ) {
2779                         if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2780                                 // 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));
2781                                 eeo->nearest_attacker_dist = dist;
2782                                 eeo->nearest_attacker_objnum = OBJ_INDEX(objp);
2783                         }
2784                 }
2785         } // end ship section
2786
2787 #ifdef MAKE_FS1
2788         // check if object is an asteroid attacking the turret parent - taylor
2789         if (objp->type == OBJ_ASTEROID) {
2790                 if ( eeo->turret_parent_objnum == asteroid_collide_objnum(objp) ) {
2791                         // give priority to the closest asteroid *impact* (ms intervals)
2792                         dist *= 0.9f + (0.01f * asteroid_time_to_impact(objp));
2793
2794                         if (dist < eeo->nearest_dist ) {
2795                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2796                                         eeo->nearest_dist = dist;
2797                                         eeo->nearest_objnum = OBJ_INDEX(objp);
2798                                 }
2799                         }
2800                 }
2801         } // end asteroid selection
2802 #endif
2803 }
2804
2805 // return 0 only if objnum is beam protected and turret is beam turret
2806 int is_target_beam_valid(ship_subsys *turret_subsys, int objnum)
2807 {
2808         // check if turret has beam weapon
2809         model_subsystem *tp = turret_subsys->system_info;
2810
2811         if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2812                 if (Objects[objnum].flags & OF_BEAM_PROTECTED) {
2813                         return 0;
2814                 }
2815
2816                 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_HUGE) {
2817                         if (Objects[objnum].type == OBJ_SHIP && !(Ship_info[Ships[Objects[objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
2818                                 return 0;
2819                         }
2820                 }
2821         }
2822
2823         return 1;
2824 }
2825
2826
2827 //      Given an object and an enemy team, return the index of the nearest enemy object.
2828 //
2829 // input:
2830 //                              turret_parent_objnum    => parent objnum for the turret
2831 //                              turret_subsys                   => pointer to system_info for the turret subsystem
2832 //                              enemy_team_mask         => OR'ed TEAM_ flags for the enemy of the turret parent ship
2833 //                              tpos                                            => position of turret (world coords)
2834 //                              tvec                                            => forward vector of turret (world coords)
2835 //                              current_enemy                   =>      objnum of current turret target
2836 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)
2837 {
2838         float                                   weapon_travel_dist;
2839         int                                     weapon_system_ok;
2840         object                          *objp;
2841         model_subsystem *tp;
2842         eval_enemy_obj_struct eeo;
2843
2844         // list of stuff to go thru
2845         ship_obj                *so;
2846         missile_obj *mo;
2847
2848         tp = turret_subsys->system_info;
2849         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);
2850
2851         // Set flag based on strength of weapons subsystem.  If weapons subsystem is destroyed, don't let turrets fire at bombs
2852         weapon_system_ok = 0;
2853         if ( ship_get_subsystem_strength( &Ships[Objects[turret_parent_objnum].instance], SUBSYSTEM_WEAPONS ) > 0 ) {
2854                 weapon_system_ok = 1;
2855         }
2856
2857         // Initialize eeo struct.
2858         eeo.turret_parent_objnum = turret_parent_objnum;
2859         eeo.weapon_system_ok = weapon_system_ok;
2860         eeo.weapon_travel_dist = weapon_travel_dist;
2861         eeo.big_only_flag = big_only_flag;
2862         eeo.enemy_team_mask = enemy_team_mask;
2863         eeo.current_enemy = current_enemy;
2864         eeo.tpos = tpos;
2865         eeo.tvec = tvec;
2866         eeo.turret_subsys = turret_subsys;
2867
2868         eeo.nearest_attacker_dist = 99999.0f;
2869         eeo.nearest_attacker_objnum = -1;
2870
2871         eeo.nearest_homing_bomb_dist = 99999.0f;
2872         eeo.nearest_homing_bomb_objnum = -1;
2873
2874         eeo.nearest_bomb_dist = 99999.0f;
2875         eeo.nearest_bomb_objnum = -1;
2876
2877         eeo.nearest_dist = 99999.0f;
2878         eeo.nearest_objnum = -1;
2879
2880
2881         // Missile_obj_list
2882         for( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
2883                 objp = &Objects[mo->objnum];
2884                 evaluate_obj_as_target(objp, &eeo);
2885         }
2886         // highest priority
2887         if ( eeo.nearest_homing_bomb_objnum != -1 ) {                                   // highest priority is an incoming homing bomb
2888                 return eeo.nearest_homing_bomb_objnum;
2889         } else if ( eeo.nearest_bomb_objnum != -1 ) {                                   // next highest priority is an incoming dumbfire bomb
2890                 return eeo.nearest_bomb_objnum;
2891         }
2892
2893
2894         // Ship_used_list
2895         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2896                 objp = &Objects[so->objnum];
2897                 evaluate_obj_as_target(objp, &eeo);
2898         }
2899
2900         SDL_assert(eeo.nearest_attacker_objnum < 0 || is_target_beam_valid(turret_subsys, eeo.nearest_attacker_objnum));
2901                 // next highest priority is attacking ship
2902         if ( eeo.nearest_attacker_objnum != -1 ) {                      // next highest priority is an attacking ship
2903                 return eeo.nearest_attacker_objnum;
2904          }
2905
2906
2907 #if !(defined(FS2_DEMO) || defined(FS1_DEMO))
2908                 asteroid_obj *ao;
2909         // Asteroid_obj_list
2910         for( ao = GET_FIRST(&Asteroid_obj_list); ao != END_OF_LIST(&Asteroid_obj_list); ao = GET_NEXT(ao) ) {
2911                 objp = &Objects[ao->objnum];
2912                 evaluate_obj_as_target(objp, &eeo);
2913         }
2914 #endif
2915
2916         return eeo.nearest_objnum;                                                                              // lowest priority is the closest enemy objnum
2917 }
2918
2919 //      Return timestamp until a ship can find an enemy.
2920 //      Yes, no parameters.  Based solely on skill level.
2921 int get_enemy_timestamp()
2922 {
2923         return (NUM_SKILL_LEVELS - Game_skill_level) * ( (myrand() % 500) + 500);
2924 }
2925
2926 // -------------------------------------------------------------------
2927 //      Return objnum if enemy found, else return -1;
2928 //      Don't attack a ship that already has at least max_attackers attacking it.
2929 int find_enemy(int objnum, float range, int max_attackers)
2930 {
2931         int     enemy_team_mask;
2932
2933         enemy_team_mask = get_enemy_team_mask(objnum);
2934
2935         //      if target_objnum != -1, use that as goal.
2936         ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2937         if (timestamp_elapsed(aip->choose_enemy_timestamp)) {
2938                 aip->choose_enemy_timestamp = timestamp(get_enemy_timestamp());
2939                 if (aip->target_objnum != -1) {
2940                         int     target_objnum = aip->target_objnum;
2941
2942                         // DKA don't undo object as target in nebula missions.
2943                         // This could cause attack on ship on fringe on nebula to stop if attackee moves our of nebula range.  (BAD)
2944                         if (Objects[target_objnum].signature == aip->target_signature) {
2945                                 if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
2946                                         if (!(Objects[target_objnum].flags & OF_PROTECTED)) {
2947                                                 // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
2948                                                 return target_objnum;
2949                                         }
2950                                 }
2951                         } else {
2952                                 aip->target_objnum = -1;
2953                                 aip->target_signature = -1;
2954                         }
2955                 }
2956                 return get_nearest_objnum(objnum, enemy_team_mask, aip->enemy_wing, range, max_attackers);
2957         } else {
2958                 aip->target_objnum = -1;
2959                 aip->target_signature = -1;
2960                 return -1;
2961         }
2962
2963 }
2964
2965 int Use_parent_target = 0;
2966 DCF_BOOL(use_parent_target, Use_parent_target)
2967
2968 // -------------------------------------------------------------------
2969 //      Return objnum if enemy found, else return -1;
2970 //
2971 // input:
2972 //                              turret_subsys   => pointer to turret subsystem
2973 //                              objnum                  => parent objnum for the turret
2974 //                              tpos                            => position of turret (world coords)
2975 //                              tvec                            => forward vector of turret (world coords)
2976 //                              current_enemy   =>      objnum of current turret target
2977 int find_turret_enemy(ship_subsys *turret_subsys, int objnum, vector *tpos, vector *tvec, int current_enemy, float fov, int big_only_flag = 0)
2978 {
2979         int                                     enemy_team_mask, enemy_objnum;
2980         model_subsystem *tp;
2981         ship_info                       *sip;
2982
2983         tp = turret_subsys->system_info;
2984         enemy_team_mask = get_enemy_team_mask(objnum);
2985
2986         //      If a small ship and target_objnum != -1, use that as goal.
2987         ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2988         sip = &Ship_info[Ships[Objects[objnum].instance].ship_info_index];
2989
2990         if ((sip->flags & SIF_SMALL_SHIP) && (aip->target_objnum != -1)) {
2991                 int target_objnum = aip->target_objnum;
2992
2993                 if (Objects[target_objnum].signature == aip->target_signature) {
2994                         if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
2995                                 if ( !(Objects[target_objnum].flags & OF_PROTECTED) ) {         // check this flag as well.
2996                                         // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
2997                                         return target_objnum;
2998                                 }
2999                         }
3000                 } else {
3001                         aip->target_objnum = -1;
3002                         aip->target_signature = -1;
3003                 }
3004         // Not small or small with target objnum
3005         } else {
3006                 // maybe use aip->target_objnum as next target
3007                 if ((frand() < 0.8f) && (aip->target_objnum != -1) && Use_parent_target) {
3008
3009                         //check if aip->target_objnum is valid target
3010                         int target_flags = Objects[aip->target_objnum].flags;
3011                         if ( target_flags & OF_PROTECTED ) {
3012                                 // AL 2-27-98: why is a protected ship being targeted?
3013                                 set_target_objnum(aip, -1);
3014                                 return -1;
3015                         }
3016
3017                         // maybe use ship target_objnum if valid for turret
3018                         // check for beam weapon and beam protected
3019                         if ( !((target_flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) ) {
3020                                 if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
3021                                         // check for huge weapon and huge ship
3022                                         if ( !big_only_flag || (Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
3023                                                 // check for tagged only and tagged ship
3024                                                 if ( (turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && ship_is_tagged(&Objects[aip->target_objnum]) ) {
3025                                                         // select new target if aip->target_objnum is out of field of view
3026                                                         vector v2e;
3027                                                         float dot;//, dist;
3028                                                         /*dist =*/ vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, tpos);
3029                                                         dot = vm_vec_dot(&v2e, tvec);
3030                                                         //      MODIFY FOR ATTACKING BIG SHIP
3031                                                         // dot += (0.5f * Objects[aip->target_objnum].radius / dist);
3032                                                         if (dot > fov) {
3033                                                                 return aip->target_objnum;
3034                                                         }
3035                                                 }
3036                                         }
3037                                 }
3038                         }
3039                 }
3040         }
3041
3042         enemy_objnum = get_nearest_turret_objnum(objnum, turret_subsys, enemy_team_mask, tpos, tvec, current_enemy, big_only_flag);
3043         if ( enemy_objnum >= 0 ) {
3044                 SDL_assert( !((Objects[enemy_objnum].flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) );
3045                 if ( Objects[enemy_objnum].flags & OF_PROTECTED ) {
3046                         Int3();
3047                         enemy_objnum = aip->target_objnum;
3048                 }
3049         }
3050
3051         return enemy_objnum;
3052 }
3053
3054 //      If issued an order to a ship that's awaiting repair, abort that process.
3055 //      However, do not abort process for an object that is currently being repaired -- let it finish.
3056 void ai_set_goal_maybe_abort_dock(object *objp, ai_info *aip)
3057 {
3058         if (aip->ai_flags & AIF_AWAITING_REPAIR) {
3059                 object  *repair_obj;
3060
3061                 if (aip->dock_objnum == -1) {
3062                         repair_obj = NULL;
3063                 } else {
3064                         repair_obj = &Objects[aip->dock_objnum];
3065                 }
3066                 ai_do_objects_repairing_stuff( objp, repair_obj, REPAIR_INFO_ABORT );
3067         }
3068         aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);    //      Might request again after 30 seconds.
3069 }
3070
3071 void force_avoid_player_check(object *objp, ai_info *aip)
3072 {
3073         if (Ships[objp->instance].team == Player_ship->team){
3074                 aip->avoid_check_timestamp = timestamp(0);              //      Force a check for collision next frame.
3075         }
3076 }
3077
3078 //      --------------------------------------------------------------------------
3079 //      Set *attacked as object to attack for object *attacker
3080 //      If attacked == NULL, then attack any enemy object.
3081 //      Attack point *rel_pos on object.  This is for supporting attacking subsystems.
3082 void ai_attack_object(object *attacker, object *attacked, int priority, ship_subsys *ssp)
3083 {
3084         ai_info *aip;
3085
3086         SDL_assert(attacker != NULL);
3087         SDL_assert(attacker->instance != -1);
3088         SDL_assert(Ships[attacker->instance].ai_index != -1);
3089
3090         aip = &Ai_info[Ships[attacker->instance].ai_index];
3091         force_avoid_player_check(attacker, aip);
3092
3093         aip->ok_to_target_timestamp = timestamp(0);             //      Guarantee we can target.
3094
3095 //      if (!SDL_strncasecmp(Ships[attacker->instance].ship_name, NOX("Kami"), 4)) {
3096 //              aip->ai_flags |= AIF_KAMIKAZE;
3097 //              aip->ai_flags |= AIF_NO_DYNAMIC;
3098 //      }
3099
3100         if (attacker == attacked) {
3101                 Int3();         //      Bogus!  Who tried to get me to attack myself!  Trace out and fix!
3102                 return;
3103         }
3104
3105         //      Only set to chase if a fighter or bomber, otherwise just return.
3106         if (!(Ship_info[Ships[attacker->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
3107 //              nprintf(("AI","Note: AI ship %s refusing to set AI mode to AIM_CHASE\n", Ships[attacker->instance].ship_name));
3108 //              return;
3109                 nprintf(("AI", "AI ship %s is large ship ordered to attack %s\n", Ships[attacker->instance].ship_name, Ships[attacked->instance].ship_name));
3110         }
3111
3112         //      This is how "engage enemy" gets processed
3113         if (attacked == NULL) {
3114                 aip->choose_enemy_timestamp = timestamp(0);
3115                 // nebula safe
3116                 set_target_objnum(aip, find_enemy(attacker-Objects, 99999.9f, 4));
3117         } else {
3118                 // check if we can see atacked in nebula
3119                 if (aip->target_objnum != attacked - Objects) {
3120                         aip->aspect_locked_time = 0.0f;
3121                 }
3122                 set_target_objnum(aip, attacked - Objects);
3123         }
3124
3125         ai_set_goal_maybe_abort_dock(attacker, aip);
3126         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);     //      No dynamic targeting for 7 seconds.
3127
3128         if (is_ignore_object(aip, aip->target_objnum)) {
3129                 aip->ignore_objnum = UNUSED_OBJNUM;
3130         }
3131
3132         aip->mode = AIM_CHASE;
3133         aip->submode = SM_ATTACK;       // AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
3134                                                                                 //                                       for AIM_CHASE... it may have been not set correctly here
3135         if (ssp == NULL) {
3136                 set_targeted_subsys(aip, NULL, -1);
3137                 if (aip->target_objnum != -1) {
3138                         //nprintf(("AI", "Unprotecting ship %s\n", Ships[Objects[aip->target_objnum].instance].ship_name));
3139                         Objects[aip->target_objnum].flags &= ~OF_PROTECTED;     //      If ship had been protected, unprotect it.
3140                 }
3141         } else {
3142                 Int3(); //      Not supported yet!
3143         }
3144 }
3145
3146 //      --------------------------------------------------------------------------
3147 //      Set *attacked as object to attack for object *attacker
3148 //      Attack point *rel_pos on object.  This is for supporting attacking subsystems.
3149 void ai_attack_wing(object *attacker, int wingnum, int priority)
3150 {
3151         ai_info *aip;
3152
3153         SDL_assert(attacker != NULL);
3154         SDL_assert(attacker->instance != -1);
3155         SDL_assert(Ships[attacker->instance].ai_index != -1);
3156
3157         aip = &Ai_info[Ships[attacker->instance].ai_index];
3158
3159         aip->enemy_wing = wingnum;
3160         aip->mode = AIM_CHASE;
3161         aip->submode = SM_ATTACK;       // AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
3162                                                                                 //                                       for AIM_CHASE... it may have been not set correctly here
3163
3164         aip->ok_to_target_timestamp = timestamp(0);             //      Guarantee we can target.
3165
3166         int count = Wings[wingnum].current_count;
3167         if (count > 0) {
3168                 int     index;
3169
3170                 index = (int) (frand() * count);
3171
3172                 if (index >= count)
3173                         index = 0;
3174
3175                 set_target_objnum(aip, Ships[Wings[wingnum].ship_index[index]].objnum);
3176
3177                 ai_set_goal_maybe_abort_dock(attacker, aip);
3178                 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);     //      No dynamic targeting for 7 seconds.
3179         }
3180 }
3181
3182 //      --------------------------------------------------------------------------
3183 //      Set *evaded as object for *evader to evade.
3184 void ai_evade_object(object *evader, object *evaded, int priority)
3185 {
3186         ai_info *aip;
3187
3188         SDL_assert(evader != NULL);
3189         SDL_assert(evaded != NULL);
3190         SDL_assert(evader->instance != -1);
3191         SDL_assert(Ships[evader->instance].ai_index != -1);
3192
3193         if (evaded == evader) {
3194                 Int3(); //      Bogus!  Who tried to get me to evade myself!  Trace out and fix!
3195                 return;
3196         }
3197
3198         aip = &Ai_info[Ships[evader->instance].ai_index];
3199
3200         set_target_objnum(aip, evaded - Objects);
3201         aip->mode = AIM_EVADE;
3202
3203 }
3204
3205 //      Ignore some object without changing mode.
3206 void ai_ignore_object(object *ignorer, object *ignored, int priority)
3207 {
3208         ai_info *aip;
3209
3210         SDL_assert(ignorer != NULL);
3211         SDL_assert(ignored != NULL);
3212         SDL_assert(ignorer->instance != -1);
3213         SDL_assert(Ships[ignorer->instance].ai_index != -1);
3214         SDL_assert(ignorer != ignored);
3215
3216         aip = &Ai_info[Ships[ignorer->instance].ai_index];
3217
3218         //      MK, 5/17/98, removing ignoring of wings.
3219         //      It's too confusing.  It often causes mysterious behavior in which fighters unexpectedly refuse to attack anything.
3220 /*      if (Ships[ignored->instance].wingnum > -1) {
3221                 int wingnum, i;
3222
3223                 wingnum = Ships[ignored->instance].wingnum;
3224                 aip->ignore_objnum = -(wingnum+1);
3225                 // set protected bit for each ship in a wing
3226                 //      MK, 4/23/98: Only set for fighters if they are the original "ignored" object
3227                 for (i = 0; i < Wings[wingnum].current_count; i++ ) {
3228                         object  *objp;
3229
3230                         objp = &Objects[Ships[Wings[wingnum].ship_index[i]].objnum];
3231                         if (objp != ignored) {
3232                                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))
3233                                         continue;
3234                         }
3235
3236                         Objects[Ships[Wings[wingnum].ship_index[i]].objnum].flags |= OF_PROTECTED;
3237                 }
3238
3239         } else {
3240         */ {
3241                 aip->ignore_objnum = ignored - Objects;
3242                 aip->ignore_signature = ignored->signature;
3243                 aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3244                 ignored->flags |= OF_PROTECTED;                                 // set protected bit of ignored ship.
3245         }
3246
3247 }
3248
3249 //      Ignore some object without changing mode.
3250 void ai_ignore_wing(object *ignorer, int wingnum, int priority)
3251 {
3252         ai_info *aip;
3253
3254         SDL_assert(ignorer != NULL);
3255         SDL_assert(ignorer->instance != -1);
3256         SDL_assert(Ships[ignorer->instance].ai_index != -1);
3257         SDL_assert((wingnum >= 0) && (wingnum < MAX_WINGS));
3258
3259         aip = &Ai_info[Ships[ignorer->instance].ai_index];
3260
3261         aip->ignore_objnum = -(wingnum +1);
3262         aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3263 }
3264
3265
3266 //      Add a path point in the global buffer Path_points.
3267 //      modify_index = index in Path_points at which to store path point.
3268 //      If modify_index == -1, then create a new point.
3269 //      If a new point is created (ie, modify_index == -1), then Ppfp is updated.
3270 void add_path_point(vector *pos, int path_num, int path_index, int modify_index)
3271 {
3272         pnode   *pnp;
3273
3274         if (modify_index == -1) {
3275                 SDL_assert(Ppfp-Path_points < MAX_PATH_POINTS-1);
3276                 pnp = Ppfp;
3277                 Ppfp++;
3278         } else {
3279                 SDL_assert((modify_index >= 0) && (modify_index < MAX_PATH_POINTS-1));
3280                 pnp = &Path_points[modify_index];
3281         }
3282
3283         pnp->pos = *pos;
3284         pnp->path_num = path_num;
3285         pnp->path_index = path_index;
3286 }
3287
3288 //      Given two points on a sphere, the center of the sphere and the radius, return a
3289 //      point on the vector through the midpoint of the chord on the sphere.
3290 void bisect_chord(vector *p0, vector *p1, vector *centerp, float radius)
3291 {
3292         vector  tvec;
3293         vector  new_pnt;
3294
3295         vm_vec_add(&tvec, p0, p1);
3296         vm_vec_sub2(&tvec, centerp);
3297         vm_vec_sub2(&tvec, centerp);
3298         if (vm_vec_mag_quick(&tvec) < 0.1f) {
3299                 vm_vec_sub(&tvec, p0, p1);
3300                 if (fl_abs(tvec.xyz.x) <= fl_abs(tvec.xyz.z)){
3301                         tvec.xyz.x = -tvec.xyz.z;
3302                 } else {
3303                         tvec.xyz.y = -tvec.xyz.x;
3304                 }
3305         }
3306
3307         vm_vec_normalize(&tvec);
3308         vm_vec_scale(&tvec, radius);
3309         vm_vec_add(&new_pnt, centerp, &tvec);
3310
3311         add_path_point(&new_pnt, -1, -1, -1);
3312 }
3313                         
3314 //      Create a path from the current position to a goal position.
3315 //      The current position is in the current object and the goal position is
3316 //      in the goal object.
3317 //      It is ok to intersect the current object, but not the goal object.
3318 //      This function is useful for creating a path to an initial point near a large
3319 //      object.
3320 //
3321 // input:       subsys_path:    optional param (default 0), indicates this is a path to a subsystem
3322 void create_path_to_point(vector *curpos, vector *goalpos, object *curobjp, object *goalobjp, int subsys_path)
3323 {
3324         //      If can't cast vector to goalpos, then create an intermediate point.
3325         if (pp_collide(curpos, goalpos, goalobjp, curobjp->radius)) {
3326                 vector  tan1;
3327                 float           radius;
3328
3329                 // If this is a path to a subsystem, use SUBSYS_PATH_DIST as the radius for the object you are
3330                 // trying to avoid.  This is needed since subsystem paths extend out to SUBSYS_PATH_DIST, and we
3331                 // want ships to reach their path destination without flying to points that sit on the radius of
3332                 // a small ship
3333                 radius = goalobjp->radius;
3334                 if (subsys_path) {
3335                         if ( SUBSYS_PATH_DIST > goalobjp->radius ) {
3336                                 radius = SUBSYS_PATH_DIST;
3337                         }
3338                 }
3339
3340                 //      The intermediate point is at the intersection of:
3341                 //              tangent to *goalobjp sphere at point *goalpos
3342                 //              tangent to *goalobjp sphere through point *curpos in plane defined by *curpos, *goalpos, goalobjp->pos
3343                 //      Note, there are two tangents through *curpos, unless *curpos is on the
3344                 //      sphere.  The tangent that causes the nearer intersection (to *goalpos) is chosen.
3345                 get_tangent_point(&tan1, curpos, &goalobjp->pos, goalpos, radius);
3346
3347                 //      If we can't reach tan1 from curpos, insert a new point.
3348                 if (pp_collide(&tan1, curpos, goalobjp, curobjp->radius))
3349                         bisect_chord(curpos, &tan1, &goalobjp->pos, radius);
3350
3351                 add_path_point(&tan1, -1, -1, -1);
3352
3353                 //      If we can't reach goalpos from tan1, insert a new point.
3354                 if (pp_collide(goalpos, &tan1, goalobjp, curobjp->radius))
3355                         bisect_chord(goalpos, &tan1, &goalobjp->pos, radius);
3356         }
3357
3358 }
3359
3360 //      Given an object and a model path, globalize the points on the model
3361 //      and copy into the global path list.
3362 //      If pnp != NULL, then modify, in place, the path points.  This is used to create new
3363 //      globalized points when the base object has moved.
3364 // input:       randomize_pnt   => optional parameter (default value -1), add random vector in sphere to this path point
3365 void copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt)
3366 {
3367         matrix  m;
3368         int             i;
3369         vector  v1;
3370         int             pp_index;               //      index in Path_points at which to store point, if this is a modify-in-place (pnp ! NULL)
3371         int             start_index, finish_index;
3372         
3373         // nprintf(("AI", "Creating path for object %s in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
3374         
3375         //      Initialize pp_index.
3376         //      If pnp == NULL, that means we're creating new points.  If not NULL, then modify in place.
3377         if (pnp == NULL)
3378                 pp_index = -1;                  //      This tells add_path_point to create a new point.
3379         else
3380                 pp_index = 0;                   //      pp_index will get assigned to index in Path_points to reuse.
3381
3382         vm_copy_transpose_matrix(&m, &objp->orient);
3383
3384         if (dir == 1) {
3385                 start_index = 0;
3386                 finish_index = min(count, mp->nverts);
3387         } else {
3388                 SDL_assert(dir == -1);  //      direction must be up by 1 or down by 1 and it's neither!
3389                 start_index = mp->nverts-1;
3390                 finish_index = max(-1, mp->nverts-1-count);
3391         }
3392
3393         int offset = 0;
3394         for (i=start_index; i != finish_index; i += dir) {
3395                 //      Globalize the point.
3396                 vm_vec_rotate(&v1, &mp->verts[i].pos, &m);
3397                 vm_vec_add2(&v1, &objp->pos);
3398
3399                 if ( randomize_pnt == i ) {
3400                         vector v_rand;
3401                         static_randvec(OBJ_INDEX(objp), &v_rand);
3402                         vm_vec_scale(&v_rand, 30.0f);
3403                         vm_vec_add2(&v1, &v_rand);
3404                 }
3405
3406                 if (pp_index != -1)
3407                         pp_index = pnp-Path_points + offset;
3408
3409                 add_path_point(&v1, path_num, i, pp_index);
3410                 offset++;
3411         }
3412 }
3413
3414
3415 //      For pl_objp, create a path along path path_num into mobjp.
3416 //      The tricky part of this problem is creating the entry to the first point on the
3417 //      predefined path.  The points on this entry path are based on the location of Pl_objp
3418 //      relative to the start of the path.
3419 //
3420 // input:
3421 //                              subsys_path:    optional param (default 0), indicating this is a path to a subsystem
3422 void create_model_path(object *pl_objp, object *mobjp, int path_num, int subsys_path)
3423 {       
3424         ship                    *shipp = &Ships[pl_objp->instance];
3425         ai_info         *aip = &Ai_info[shipp->ai_index];
3426
3427         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3428         polymodel       *pm = model_get(osip->modelnum);
3429         model_path      *mp;
3430         pnode                   *ppfp_start = Ppfp;
3431         matrix          m;
3432         vector          gp0;
3433
3434         SDL_assert(path_num >= 0);
3435
3436         //      Do garbage collection if necessary.
3437         if (Ppfp-Path_points + 64 > MAX_PATH_POINTS) {
3438                 garbage_collect_path_points();
3439                 ppfp_start = Ppfp;
3440         }
3441
3442         aip->path_start = Ppfp - Path_points;
3443         SDL_assert(path_num < pm->n_paths);
3444         
3445         mp = &pm->paths[path_num];
3446
3447         SDL_assert(Ppfp-Path_points + mp->nverts + 4 < MAX_PATH_POINTS);
3448
3449         vm_copy_transpose_matrix(&m, &mobjp->orient);
3450         vm_vec_rotate(&gp0, &mp->verts[0].pos, &m);
3451         vm_vec_add2(&gp0, &mobjp->pos);
3452
3453         if (pp_collide(&pl_objp->pos, &gp0, mobjp, pl_objp->radius)) {
3454                 vector  perim_point1;
3455                 vector  perim_point2;
3456
3457                 perim_point2 = pl_objp->pos;
3458                 
3459                 //      If object that wants to dock is inside bounding sphere of object it wants to dock with, make it fly out.
3460                 //      Assume it can fly "straight" out to the bounding sphere.
3461                 if (vm_vec_dist_quick(&pl_objp->pos, &mobjp->pos) < mobjp->radius) {
3462                         project_point_to_perimeter(&perim_point2, &mobjp->pos, mobjp->radius, &pl_objp->pos);
3463                         add_path_point(&perim_point2, path_num, -1, -1);
3464                 }
3465
3466                 //      If last point on pre-defined path is inside bounding sphere, create a new point on the surface of the sphere.
3467                 if (vm_vec_dist_quick(&mobjp->pos, &gp0) < mobjp->radius) {
3468                         project_point_to_perimeter(&perim_point1, &mobjp->pos, mobjp->radius, &gp0);
3469                         create_path_to_point(&perim_point2, &perim_point1, pl_objp, mobjp, subsys_path);
3470                         add_path_point(&perim_point1, path_num, -1, -1);
3471                 } else {                //      The predefined path extends outside the sphere.  Create path to that point.
3472                         create_path_to_point(&perim_point2, &gp0, pl_objp, mobjp, subsys_path);
3473                 }
3474         }
3475
3476         // AL 12-31-97: If following a subsystem path, add random vector to second last path point
3477         if ( subsys_path ) {
3478                 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL, mp->nverts-2);
3479         } else {
3480                 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL);
3481         }
3482
3483         aip->path_cur = aip->path_start;
3484         aip->path_dir = PD_FORWARD;
3485         aip->path_objnum = mobjp-Objects;
3486         aip->mp_index = path_num;
3487         aip->path_length = Ppfp - ppfp_start;
3488         aip->path_next_check_time = timestamp(1);
3489
3490         aip->path_goal_obj_hash = create_object_hash(&Objects[aip->path_objnum]);
3491
3492         aip->path_next_create_time = timestamp(1000);   //      OK to try to create one second later
3493         aip->path_create_pos = pl_objp->pos;
3494         aip->path_create_orient = pl_objp->orient;
3495
3496         aip->ai_flags &= ~AIF_USE_EXIT_PATH;                    // ensure this flag is cleared
3497 }
3498
3499 //      For pl_objp, create a path along path path_num into mobjp.
3500 //      The tricky part of this problem is creating the entry to the first point on the
3501 //      predefined path.  The points on this entry path are based on the location of pl_objp
3502 //      relative to the start of the path.
3503 void create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count)
3504 {       
3505         ship                    *shipp = &Ships[pl_objp->instance];
3506         ai_info         *aip = &Ai_info[shipp->ai_index];
3507
3508         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3509         polymodel       *pm = model_get(osip->modelnum);
3510         model_path      *mp;
3511         pnode                   *ppfp_start = Ppfp;
3512
3513         aip->path_start = Ppfp - Path_points;
3514         SDL_assert(path_num < pm->n_paths);
3515         
3516         mp = &pm->paths[path_num];
3517
3518         SDL_assert(Ppfp-Path_points + mp->nverts + 4 < MAX_PATH_POINTS);
3519
3520         copy_xlate_model_path_points(mobjp, mp, -1, count, path_num, NULL);
3521
3522         aip->path_cur = aip->path_start;
3523         aip->path_dir = PD_FORWARD;
3524         aip->path_objnum = mobjp-Objects;
3525         aip->mp_index = path_num;
3526         aip->path_length = Ppfp - ppfp_start;
3527         aip->path_next_check_time = timestamp(1);
3528
3529         aip->ai_flags |= AIF_USE_EXIT_PATH;             // mark as exit path, referenced in maybe
3530 }
3531
3532 //      Return true if the vector from curpos to goalpos intersects with any ship other than the ignore objects.
3533 //      Calls pp_collide
3534 int pp_collide_any(vector *curpos, vector *goalpos, float radius, object *ignore_objp1, object *ignore_objp2, int big_only_flag)
3535 {
3536         ship_obj        *so;    
3537
3538         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
3539                 object *objp = &Objects[so->objnum];
3540
3541                 if (big_only_flag) {
3542                         if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
3543                                 continue;
3544                 }
3545
3546                 if ((objp != ignore_objp1) && (objp != ignore_objp2)) {
3547                         if (pp_collide(curpos, goalpos, objp, radius))
3548                                 return OBJ_INDEX(objp);
3549                 }
3550         }
3551
3552         return -1;
3553 }
3554
3555 //      Used to create docking paths and other pre-defined paths through ships.
3556 //      Creates a path in absolute space.
3557 //      Create a path into the object objnum.
3558 //
3559 // input:
3560 //      pl_objp:                        object that will use the path
3561 //      objnum:                 Object to find path to.
3562 //      path_num:               model path index to use
3563 //      exit_flag:              true means this is an exit path in the model
3564 // subsys_path: optional param (default 0) that indicates this is a path to a subsystem
3565 //      Exit:
3566 //      ai_info struct in Pl_objp gets stuffed with information to enable Pl_objp to fly the path.
3567 void ai_find_path(object *pl_objp, int objnum, int path_num, int exit_flag, int subsys_path)
3568 {
3569         ai_info *aip = &Ai_info[Ships[pl_objp->instance].ai_index];
3570
3571         SDL_assert(path_num >= 0);
3572
3573         //      This is test code, find an object with paths.
3574         if (objnum != -1) {
3575                 object  *objp = &Objects[objnum];
3576
3577                 if (objp->type == OBJ_SHIP) {
3578 #ifndef NDEBUG
3579                         polymodel *pm = model_get( Ships[objp->instance].modelnum );
3580                         SDL_assert(pm->n_paths > path_num);
3581 #endif
3582                         aip->goal_objnum = objp-Objects;
3583                         aip->goal_signature = objp->signature;
3584                         if (exit_flag)
3585                                 create_model_exit_path(pl_objp, objp, path_num);
3586                         else
3587                                 create_model_path(pl_objp, objp, path_num, subsys_path);
3588                         return;
3589                 }
3590
3591         }
3592 }
3593
3594 extern int vector_object_collision(vector *start_pos, vector *end_pos, object *objp, float radius_scale);
3595
3596 //      Maybe make *objp avoid a player object.
3597 //      For now, 4/6/98, only check Player_obj.
3598 //      If player collision would occur, set AIF_AVOIDING_SMALL_SHIP bit in ai_flags.
3599 //      Set aip->avoid_goal_point
3600 int maybe_avoid_player(object *objp, vector *goal_pos)
3601 {
3602         ai_info *aip;
3603         vector  cur_pos, new_goal_pos;
3604         object  *player_objp;
3605         vector  n_vec_to_goal, n_vec_to_player;
3606
3607         aip = &Ai_info[Ships[objp->instance].ai_index];
3608
3609         if (!timestamp_elapsed(aip->avoid_check_timestamp))
3610                 return 0;
3611
3612         player_objp = Player_obj;
3613
3614         float   speed_time;
3615
3616         //      How far two ships could be apart and still collide within one second.
3617         speed_time = player_objp->phys_info.speed + objp->phys_info.speed;
3618
3619         float   obj_obj_dist;
3620
3621         obj_obj_dist = vm_vec_dist_quick(&player_objp->pos, &objp->pos);
3622
3623         if (obj_obj_dist > speed_time*2.0f)
3624                 return 0;
3625
3626         cur_pos = objp->pos;
3627
3628         new_goal_pos = *goal_pos;
3629
3630         float dist = vm_vec_normalized_dir(&n_vec_to_goal, goal_pos, &objp->pos);
3631         vm_vec_normalized_dir(&n_vec_to_player, &player_objp->pos, &objp->pos);
3632
3633         if (dist > speed_time*2.0f) {
3634                 vm_vec_scale_add(&new_goal_pos, &objp->pos, &n_vec_to_goal, 200.0f);
3635         }
3636
3637         if (vector_object_collision(&objp->pos, &new_goal_pos, player_objp, 1.5f)) {
3638                 aip->ai_flags |= AIF_AVOIDING_SMALL_SHIP;
3639
3640                 vector  avoid_vec;
3641
3642                 vm_vec_sub(&avoid_vec, &n_vec_to_goal, &n_vec_to_player);
3643                 if (vm_vec_mag_quick(&avoid_vec) < 0.01f) {
3644                         vm_vec_copy_scale(&avoid_vec, &objp->orient.v.rvec, frand()-0.5f);
3645                         vm_vec_scale_add2(&avoid_vec, &objp->orient.v.uvec, frand()-0.5f);
3646                         vm_vec_normalize(&avoid_vec);
3647                 } else {
3648                         vector  tvec1;
3649                         vm_vec_normalize(&avoid_vec);
3650                         vm_vec_crossprod(&tvec1, &n_vec_to_goal, &avoid_vec);
3651                         vm_vec_crossprod(&avoid_vec, &tvec1, &n_vec_to_player);
3652                 }
3653
3654                 //      Now, avoid_vec is a vector perpendicular to the vector to the player and the direction *objp
3655                 //      should fly in to avoid the player while still approaching its goal.
3656                 vm_vec_scale_add(&aip->avoid_goal_point, &player_objp->pos, &avoid_vec, 400.0f);
3657
3658                 aip->avoid_check_timestamp = timestamp(1000);
3659
3660                 return 1;
3661         } else {
3662                 aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
3663                 aip->avoid_check_timestamp = timestamp((int) (obj_obj_dist/200.0f) + 500);
3664
3665                 return 0;
3666         }
3667 }
3668
3669 //      Make object *still_objp enter AIM_STILL mode.
3670 //      Make it point at view_pos.
3671 void ai_stay_still(object *still_objp, vector *view_pos)
3672 {
3673         ship    *shipp;
3674         ai_info *aip;
3675
3676         SDL_assert(still_objp->type == OBJ_SHIP);
3677         SDL_assert((still_objp->instance >= 0) && (still_objp->instance < MAX_OBJECTS));
3678
3679         shipp = &Ships[still_objp->instance];
3680         SDL_assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
3681
3682         aip = &Ai_info[shipp->ai_index];
3683
3684         aip->mode = AIM_STILL;
3685
3686         //      If view_pos not NULL, point at that point.  Else, point at a point directly in front of ship.  Ie, don't turn.
3687         if (view_pos != NULL)
3688                 aip->goal_point = *view_pos;
3689         else
3690                 vm_vec_scale_add(&aip->goal_point, &still_objp->pos, &still_objp->orient.v.fvec, 100.0f);
3691 }
3692
3693 // code which is called from ai_dock_with_object and ai_dock to set flags and apprioriate variable
3694 // when two objects have completed docking.  used because we can dock object initially at misison load
3695 // time (meaning that ai_dock() might never get called).  docker has docked with dockee (i.e. docker
3696 // would be a freighter and dockee would be a cargo).
3697 void ai_do_objects_docked_stuff(object *docker, object *dockee)
3698 {
3699         ai_info *aip, *other_aip;
3700
3701         aip = &Ai_info[Ships[docker->instance].ai_index];
3702         other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3703
3704         // set the flags and dock_objnum for both objects
3705         aip->ai_flags |= AIF_DOCKED;
3706         aip->dock_objnum = OBJ_INDEX(dockee);
3707         other_aip->ai_flags |= AIF_DOCKED;
3708         other_aip->dock_objnum = OBJ_INDEX(docker);
3709         aip->dock_signature = dockee->signature;
3710         other_aip->dock_signature = docker->signature;
3711
3712         // add multiplayer hook here to deal with docked objects.  We need to only send information
3713         // about the object that is docking.  Both flags will get updated.
3714         if ( MULTIPLAYER_MASTER )
3715                 send_ai_info_update_packet( docker, AI_UPDATE_DOCK );
3716
3717 }
3718
3719 // code which is called when objects become undocked. Equivalent of above function.
3720 // dockee might not be valid since this code can get called to cleanup after a ship
3721 // has blown up!
3722 void ai_do_objects_undocked_stuff( object *docker, object *dockee )
3723 {
3724         ai_info *aip, *other_aip;
3725
3726         // add multiplayer hook here to deal with undocked objects.  Do it before we
3727         // do anything else.  We don't need to send info for both objects, since we can find
3728         // it be dock_objnum
3729         if ( MULTIPLAYER_MASTER )
3730                 send_ai_info_update_packet( docker, AI_UPDATE_UNDOCK );
3731
3732         aip = &Ai_info[Ships[docker->instance].ai_index];
3733
3734         // set the flags and dock_objnum for both objects
3735         aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3736         aip->dock_objnum = -1;
3737         
3738         if ( dockee != NULL ) {
3739                 other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3740                 other_aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3741                 other_aip->dock_objnum = -1;
3742         }
3743
3744 }
3745
3746
3747 //      --------------------------------------------------------------------------
3748 //      Interface from goals code to AI.
3749 //      Cause *docker to dock with *dockee.
3750 //      priority is priority of goal from goals code.
3751 //      dock_type is:
3752 //              AIDO_DOCK               set goal of docking
3753 //              AIDO_DOCK_NOW   immediately dock, used for ships that need to be docked at mission start
3754 //              AIDO_UNDOCK             set goal of undocking
3755 void ai_dock_with_object(object *docker, object *dockee, int priority, int dock_type, int docker_index, int dockee_index)
3756 {
3757         ai_info         *aip;
3758         ai_info         *dockee_aip;
3759
3760         SDL_assert(docker != NULL);
3761         SDL_assert(dockee != NULL);
3762         SDL_assert(docker->instance != -1);
3763         SDL_assert(Ships[docker->instance].ai_index != -1);
3764         SDL_assert(Ships[dockee->instance].ai_index != -1);
3765         SDL_assert( docker_index != -1 );
3766         SDL_assert( dockee_index != -1 );
3767
3768         aip = &Ai_info[Ships[docker->instance].ai_index];
3769
3770         if ((aip->ai_flags & AIF_DOCKED) && (dock_type == AIDO_DOCK)) {
3771         //      object  *dockee2;
3772         //      int             docker_index2, dockee_index2;
3773
3774                 SDL_assert(aip->dock_objnum > -1);
3775         //      dockee2 = &Objects[aip->dock_objnum];
3776         //      docker_index2 = aip->dock_index;
3777         //      dockee_index2 = aip->dockee_index;
3778                 // MWA -- 2/9/98.  use the goal code to undock the ships since goals might need to get removed
3779                 // and that code will do it properly.  I'd actually be surprised if we got into this code anymore
3780                 // since the outer layer goal code should deal with this issue....but who knows...
3781                 ai_add_goal_ship_internal( aip, AI_GOAL_UNDOCK, NULL, -1, -1, 0 );
3782
3783                 // old code below
3784                 //ai_dock_with_object(docker, dockee2, priority, AIDO_UNDOCK, docker_index2, dockee_index2);
3785                 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));
3786                 nprintf(("AI", "...so ship %s will now undock.\n", Ships[docker->instance].ship_name));
3787                 return;
3788         }
3789
3790         dockee_aip = &Ai_info[Ships[dockee->instance].ai_index];
3791
3792         aip->goal_objnum = dockee - Objects;
3793         aip->goal_signature = dockee->signature;
3794
3795         aip->mode = AIM_DOCK;
3796
3797         switch (dock_type) {
3798         case AIDO_DOCK:
3799                 aip->submode = AIS_DOCK_0;
3800                 break;
3801         case AIDO_DOCK_NOW:
3802                 aip->submode = AIS_DOCK_3A;
3803                 break;
3804         case AIDO_UNDOCK:
3805                 aip->submode = AIS_UNDOCK_0;
3806                 break;
3807         default:
3808                 Int3();         //      Bogus dock_type.
3809         }
3810
3811         aip->submode_start_time = Missiontime;
3812         aip->dock_index = docker_index;
3813         aip->dockee_index = dockee_index;
3814
3815         dockee_aip->dock_index = dockee_index;
3816         dockee_aip->dockee_index = docker_index;
3817
3818         // get the path number to the docking point on the dockee.  Each docking point contains a list
3819         // of paths that the point can be reached by.  Pick the first path in the path list for now.
3820         // We only want to do this stuff if we are docking!!!  Be sure to set the path index
3821         if ((dock_type == AIDO_DOCK) || (dock_type == AIDO_DOCK_NOW)) {
3822 #ifndef NDEBUG
3823                 polymodel *pm = model_get( Ships[dockee->instance].modelnum );
3824                 SDL_assert( pm->docking_bays[dockee_index].num_spline_paths > 0 );
3825 #endif
3826
3827                 // only set the dock path index if we are docking.  undocking will assume that dock_path_index
3828                 // already set from some other docking command
3829                 aip->dock_path_index = dockee_index;
3830                 dockee_aip->dock_path_index = docker_index;
3831         }
3832
3833         if (dock_type != AIDO_DOCK_NOW) {
3834                 int path_num;
3835                 //      Note: Second parameter is dock path index.  This should be specified as an
3836                 //      _input_ to this function and passed through.  The path index should be already
3837                 // set for the undock function
3838                 path_num = ai_return_path_num_from_dockbay(dockee, dockee_index);
3839                 ai_find_path(docker, dockee-Objects, path_num, 0);
3840 //              ai_find_path(dockee-Objects, dockee_index, 0);
3841         } else {
3842                 dock_orient_and_approach(docker, dockee, DOA_DOCK_STAY);
3843                 //aip->dock_objnum = OBJ_INDEX(dockee);
3844                 ai_do_objects_docked_stuff( docker, dockee );
3845         }
3846
3847 }
3848
3849 //      Cause a ship to fly its waypoints.
3850 //      flags tells:
3851 //              WPF_REPEAT      Set -> repeat waypoints.
3852 void ai_start_waypoints(object *objp, int waypoint_list_index, int wp_flags)
3853 {
3854         ai_info *aip;
3855
3856         SDL_assert(waypoint_list_index < Num_waypoint_lists);
3857
3858         //nprintf(("AI", "Frame %i: Ship %s instructed to fly waypoint list #%i\n", AI_FrameCount, Ships[objp->instance].ship_name, waypoint_list_index));
3859         aip = &Ai_info[Ships[objp->instance].ai_index];
3860
3861         if ( (aip->mode == AIM_WAYPOINTS) && (aip->wp_index == waypoint_list_index) )
3862                 return;
3863
3864         aip->ai_flags |= AIF_FORMATION_WING;
3865         aip->ai_flags &= ~AIF_FORMATION_OBJECT;
3866         aip->wp_list = waypoint_list_index;
3867         aip->wp_index = 0;
3868         aip->wp_flags = wp_flags;
3869         aip->mode = AIM_WAYPOINTS;
3870
3871         SDL_assert(aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC);
3872 }
3873
3874 //      Make *objp stay within dist units of *other_objp
3875 void ai_do_stay_near(object *objp, object *other_objp, float dist)
3876 {
3877         ai_info *aip;
3878
3879         SDL_assert(objp != other_objp);         //      Bogus!  Told to stay near self.
3880         SDL_assert(objp->type == OBJ_SHIP);
3881         SDL_assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
3882
3883         aip = &Ai_info[Ships[objp->instance].ai_index];
3884
3885         aip->mode = AIM_STAY_NEAR;
3886         aip->submode = -1;
3887         aip->stay_near_distance = dist;
3888         aip->goal_objnum = other_objp-Objects;
3889         aip->goal_signature = other_objp->signature;
3890
3891 }
3892
3893 //      Make object *objp form on wing of object *goal_objp
3894 void ai_form_on_wing(object *objp, object *goal_objp)
3895 {
3896         ai_info *aip;
3897         ship                    *shipp;
3898         ship_info       *sip;
3899
3900         // objp == goal_objp sometimes in multiplayer when someone leaves a game -- make a simple
3901         // out for this case.
3902         if ( Game_mode & GM_MULTIPLAYER ) {
3903                 if ( objp == goal_objp ) {
3904                         return;
3905                 }
3906         }
3907
3908         SDL_assert(objp != goal_objp);          //      Bogus!  Told to form on own's wing!
3909
3910         shipp = &Ships[objp->instance];
3911         sip = &Ship_info[shipp->ship_info_index];
3912
3913         //      Only fighters or bombers allowed to form on wing.
3914         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
3915                 nprintf(("AI", "Warning: Ship %s tried to form on player's wing, but not fighter or bomber.\n", shipp->ship_name));
3916                 return;
3917         }
3918
3919         aip = &Ai_info[Ships[objp->instance].ai_index];
3920
3921         aip->ai_flags &= ~AIF_FORMATION_WING;
3922         aip->ai_flags |= AIF_FORMATION_OBJECT;
3923
3924         aip->goal_objnum = goal_objp-Objects;
3925         ai_set_goal_maybe_abort_dock(objp, aip);
3926         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME*4);           //      Super extra long time until can target another ship.
3927
3928 }
3929
3930 //      Given an object and an object on whose wing to form, return slot to use.
3931 //      Optimize:
3932 //              This function is called per object in formation per frame.  Should store slot in ai_info struct.
3933 int ai_formation_object_get_slotnum(int objnum, object *objp)
3934 {
3935         int     slotnum = 1;                    //      Note: Slot #0 means leader, which isn't someone who was told to form-on-wing.
3936         object *o;
3937
3938         for ( o = GET_FIRST(&obj_used_list); o != END_OF_LIST(&obj_used_list); o = GET_NEXT(o) ) {
3939                 if (objp == o)
3940                         break;
3941                 else if (o->type == OBJ_SHIP)
3942                         if (Ai_info[Ships[o->instance].ai_index].ai_flags & AIF_FORMATION_OBJECT)
3943                                 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == objnum)
3944                                         slotnum++;
3945         }
3946
3947         SDL_assert(o != END_OF_LIST(&obj_used_list));   //      Didn't find objp in list of used ships.  Impossible!
3948
3949         return slotnum;
3950 }
3951
3952 #define BIGNUM  100000.0f
3953
3954 int Debug_k = 0;
3955
3956 //      Given an attacker's position and a target's position and velocity, compute the time of
3957 //      intersection of a weapon fired by the attacker with speed weapon_speed.
3958 //      Return this value.  Return value of 0.0f means no collision is possible.
3959 float compute_collision_time(vector *targpos, vector *targvel, vector *attackpos, float weapon_speed)
3960 {
3961         vector  vec_to_target;
3962         float           pos_dot_vel;
3963         float           vel_sqr;
3964         float           discrim;
3965
3966         vm_vec_sub(&vec_to_target, targpos, attackpos);
3967         pos_dot_vel = vm_vec_dot(&vec_to_target, targvel);
3968         vel_sqr = vm_vec_dot(targvel, targvel) - weapon_speed*weapon_speed;
3969         discrim = pos_dot_vel*pos_dot_vel - vel_sqr*vm_vec_dot(&vec_to_target, &vec_to_target);
3970
3971         if (discrim > 0.0f) {
3972                 float   t1, t2, t_solve;
3973
3974                 t1 = (-pos_dot_vel + fl_sqrt(discrim)) / vel_sqr;
3975                 t2 = (-pos_dot_vel - fl_sqrt(discrim)) / vel_sqr;
3976
3977                 t_solve = BIGNUM;
3978
3979                 if (t1 > 0.0f)
3980                         t_solve = t1;
3981                 if ((t2 > 0.0f) && (t2 < t_solve))
3982                         t_solve = t2;
3983
3984                 if (t_solve < BIGNUM-1.0f) {
3985                         return t_solve + Debug_k * flFrametime;
3986                 }
3987         }
3988
3989         return 0.0f;
3990 }
3991
3992
3993 //      --------------------------------------------------------------------------
3994 //      If far away, use player's speed.
3995 //      If in between, lerp between player and laser speed
3996 //      If close, use laser speed.
3997 // Want to know how much time it will take to get to the enemy.
3998 // This function doesn't account for the fact that by the time the player
3999 // (or his laser) gets to the current enemy position, the enemy will have moved.
4000 // This is dealt with in polish_predicted_enemy_pos.
4001 float compute_time_to_enemy(float dist_to_enemy, object *pobjp, object *eobjp)
4002 {
4003         float   time_to_enemy;
4004         float   pl_speed = pobjp->phys_info.speed;
4005         float   max_laser_distance, max_laser_speed;
4006         int     bank_num, weapon_num;
4007         ship    *shipp = &Ships[pobjp->instance];
4008
4009         bank_num = shipp->weapons.current_primary_bank;
4010         weapon_num = shipp->weapons.primary_bank_weapons[bank_num];
4011         max_laser_speed = Weapon_info[weapon_num].max_speed;
4012         max_laser_distance = max_laser_speed * Weapon_info[weapon_num].lifetime;
4013
4014         //      If pretty far away, use player's speed to predict position, else
4015         //      use laser's speed because when close, we care more about hitting
4016         //      with a laser than about causing ship:ship rendezvous.
4017         if (dist_to_enemy > 1.5 * max_laser_distance) {
4018                 if (pl_speed > 0.0f)
4019                         time_to_enemy = dist_to_enemy/pl_speed;
4020                 else
4021                         time_to_enemy = 1.0f;
4022         } else if (dist_to_enemy > 1.1*max_laser_distance) {
4023                 if (pl_speed > 0.1f) {
4024                         float   scale;
4025
4026                         scale = (float) ((dist_to_enemy - max_laser_distance) / max_laser_distance);
4027                 
4028                         time_to_enemy = (float) (dist_to_enemy/(pl_speed * scale + max_laser_speed * (1.0f - scale)));
4029                 } else
4030                         time_to_enemy = 2.0f;
4031         } else
4032                 time_to_enemy = (float) (dist_to_enemy/max_laser_speed);
4033
4034         // return time_to_enemy * (1.0f + Ai_info[Ships[pobjp->instance].ai_index].lead_scale);
4035         return time_to_enemy + flFrametime;
4036 }
4037
4038 //      Stuff *dot and *tts.
4039 //      *dot is always computed.  If dot is less than zero, the magnitude is
4040 //      incorrect, not having been divided by distance.
4041 //      If *dot is > 0.0f, then tts is computed.  This is the time it will take object
4042 //      *objp to get to *pos, assuming it moves right at it.
4043 void fds_aux(float *dot, float *tts, vector *pos, float dtime, object *objp)
4044 {
4045         vector  v2s;
4046
4047         vm_vec_sub(&v2s, pos, &objp->pos);
4048         *dot = vm_vec_dot(&v2s, &objp->orient.v.fvec);
4049
4050         if (*dot > 0.0f) {
4051                 float   dist;
4052
4053                 dist = vm_vec_dist(&objp->pos, pos);
4054
4055                 if (dist > 0.1f)
4056                         *dot /= dist;
4057                 else
4058                         *dot = 1.0f;
4059
4060                 if (objp->phys_info.speed > 0.1f)
4061                         *tts = dist / objp->phys_info.speed;
4062                 else
4063                         *tts = dist * 100.0f;
4064         }
4065 }
4066
4067 /*
4068 //      Return index of weapon that could hit object *sobjp within dtime seconds.
4069 //      Actual time until impact returned in *atime.
4070 int find_danger_weapon(object *sobjp, float dtime, float *atime, float dot_threshhold)
4071 {
4072         object  *objp, *best_objp = NULL;
4073         float           best_tts = 1000.0f;
4074
4075         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
4076                 if ((objp->type == OBJ_WEAPON) && (sobjp-Objects != objp->parent)) {
4077                         float           dot, tts;
4078                         // vector       psp;            //      Predicted ship position.
4079
4080                         //      Get dot and time to current ship position.
4081                         fds_aux(&dot, &tts, &sobjp->pos, dtime, objp);
4082
4083                         //      If dot and tts are in plausible range, do more expensive stuff.
4084                         if (dot > 0.98f) {
4085 //                              float   dot_from_sobjp;
4086                                 vector  v2e;
4087
4088                                 vm_vec_normalized_dir(&v2e, &objp->pos, &sobjp->pos);
4089 //                              dot_from_sobjp = vm_vec_dot(&sobjp->orient.v.fvec, &v2e);
4090 //                              if (dot_from_sobjp >= dot_threshhold)
4091                                         if (tts < dtime) {
4092                                                 if (tts < best_tts) {
4093                                                         best_tts = tts;
4094                                                         best_objp = objp;
4095                                                 }
4096                                         }
4097                         }
4098                 }
4099         }
4100
4101         *atime = best_tts;
4102
4103         if (best_objp != NULL)
4104                 return best_objp-Objects;
4105         else
4106                 return -1;
4107 }
4108 */
4109
4110 //      --------------------------------------------------------------------------
4111 void ai_set_positions(object *pl_objp, object *en_objp, ai_info *aip, vector *player_pos, vector *enemy_pos)
4112 {
4113         *player_pos = pl_objp->pos;
4114
4115         if (aip->next_predict_pos_time > Missiontime) {
4116                 *enemy_pos = aip->last_predicted_enemy_pos;
4117         } else {
4118                 *enemy_pos = en_objp->pos;
4119
4120                 aip->next_predict_pos_time = Missiontime + Skill_level_delay[Game_skill_level];
4121                 aip->last_predicted_enemy_pos = *enemy_pos;
4122         }
4123
4124
4125 }
4126
4127 //      --------------------------------------------------------------------------
4128 int find_nearest_waypoint(object *objp)
4129 {
4130         int     i;
4131         float   dist, min_dist, dot;
4132         int     min_ind;
4133         int     wp_listnum;
4134         waypoint_list   *wpl;
4135
4136         wp_listnum = Ai_info[Ships[objp->instance].ai_index].wp_list;
4137         SDL_assert(wp_listnum > 0);
4138         wpl = &Waypoint_lists[wp_listnum];
4139
4140         min_dist = 999999.0f;
4141         min_ind = -1;
4142
4143         for (i=0; i<wpl->count; i++) {
4144                 dist = vm_vec_dist_quick(&objp->pos, &wpl->waypoints[i]);
4145                 dot = vm_vec_dot_to_point(&objp->orient.v.fvec, &objp->pos, &wpl->waypoints[i]);
4146                 dist = (float) (dist * (1.25 - dot));
4147                 if (dist < min_dist) {
4148                         min_dist = dist;
4149                         min_ind = i;
4150                 }
4151         }
4152
4153         SDL_assert(min_ind != -1);
4154
4155         return min_ind;
4156 }
4157
4158 //      Given an ai_info struct, by reading current goal and path information,
4159 //      extract base path information and return in pmp and pmpv.
4160 //      Return true if found, else return false.
4161 //      false means the current point is not on the original path.
4162 int get_base_path_info(int path_cur, int goal_objnum, model_path **pmp, mp_vert **pmpv)
4163 {
4164         pnode                   *pn = &Path_points[path_cur];
4165         ship_info       *sip = &Ship_info[Ships[Objects[goal_objnum].instance].ship_info_index];
4166         polymodel       *pm = model_get(sip->modelnum);
4167         //static        int     debug_last_index = -1;  // no longer used
4168         *pmpv = NULL;
4169         *pmp = NULL;
4170
4171         if (pn->path_num != -1) {
4172                 *pmp = &pm->paths[pn->path_num];
4173                 if (pn->path_index != -1)
4174                         *pmpv = &(*pmp)->verts[pn->path_index];
4175                 else
4176                         return 0;
4177         } else
4178                 return 0;
4179
4180 /*      if (debug_last_index != *pmpv-(*pmp)->verts) {
4181                 debug_last_index = *pmpv-(*pmp)->verts;
4182                 nprintf(("AI", "Point %i has %i turrets: ", *pmpv-(*pmp)->verts, (*pmpv)->nturrets));
4183                 for (int i=0; i<(*pmpv)->nturrets; i++) {
4184                         nprintf(("AI", "%i ", (*pmpv)->turret_ids[i]));
4185                 }
4186                 nprintf(("AI", "\n"));
4187         }
4188 */
4189         return 1;
4190 }
4191
4192 //      Modify, in place, the points in a global model path.
4193 //      Only modify those points that are defined in the model path.  Don't modify the
4194 //      leadin points, such as those that are necessary to get the model on the path.
4195 void modify_model_path_points(object *objp)
4196 {       
4197         ai_info         *aip = &Ai_info[Ships[objp->instance].ai_index];
4198         object          *mobjp = &Objects[aip->path_objnum];
4199         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
4200         polymodel       *pm = model_get(osip->modelnum);
4201         pnode                   *pnp;
4202         int                     path_num, dir;
4203
4204         SDL_assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
4205
4206         pnp = &Path_points[aip->path_start];
4207         while ((pnp->path_index == -1) && (pnp-Path_points - aip->path_start < aip->path_length))
4208                 pnp++;
4209
4210         path_num = pnp->path_num;
4211         SDL_assert((path_num >= 0) && (path_num < pm->n_paths));
4212         
4213         SDL_assert(pnp->path_index != -1);      //      If this is -1, that means we never found the model path points
4214
4215         dir = 1;
4216         if ( aip->ai_flags & AIF_USE_EXIT_PATH ) {
4217                 dir = -1;
4218         }
4219
4220         copy_xlate_model_path_points(mobjp, &pm->paths[path_num], dir, pm->paths[path_num].nverts, path_num, pnp);
4221 }
4222
4223 //      Return an indication of the distance between two matrices.
4224 //      This is the sum of the distances of their dot products from 1.0f.
4225 float ai_matrix_dist(matrix *mat1, matrix *mat2)
4226 {
4227         float   t;
4228
4229         t =  1.0f - vm_vec_dot(&mat1->v.fvec, &mat2->v.fvec);
4230         t += 1.0f - vm_vec_dot(&mat1->v.uvec, &mat2->v.uvec);
4231         t += 1.0f - vm_vec_dot(&mat1->v.rvec, &mat2->v.rvec);
4232
4233         return t;
4234 }
4235
4236
4237 //      Paths are created in absolute space, so a moving object needs to have model paths within it recreated.
4238 //      This uses the hash functions which means the slightest movement will cause a recreate, though the timestamp
4239 //      prevents this from happening too often.
4240 //      force_recreate_flag TRUE means to recreate regardless of timestamp.
4241 //      Returns TRUE if path recreated.
4242 float maybe_recreate_path(object *objp, ai_info *aip, int force_recreate_flag)
4243 {
4244         int     hashval;
4245
4246         SDL_assert(&Ai_info[Ships[objp->instance].ai_index] == aip);
4247
4248         if ((aip->mode == AIM_BAY_EMERGE) || (aip->mode == AIM_BAY_DEPART))
4249                 if ((OBJ_INDEX(objp) % 4) == (Framecount % 4))
4250                         force_recreate_flag = 1;
4251
4252         //      If no path, that means we don't need one.
4253         if (aip->path_start == -1)
4254                 return 0.0f;
4255
4256         // AL 11-12-97: If AIF_USE_STATIC_PATH is set, don't try to recreate.  This is needed when ships
4257         //                                  emerge from fighter bays.  We don't need to recreate the path.. and in case the 
4258         //              parent ship dies, we still want to be able to continue on the path
4259         if ( aip->ai_flags & AIF_USE_STATIC_PATH ) 
4260                 return 0.0f;
4261
4262         if (force_recreate_flag || timestamp_elapsed(aip->path_next_create_time)) {
4263                 object  *path_objp;
4264
4265                 path_objp = &Objects[aip->path_objnum];
4266
4267                 if ((hashval = create_object_hash(path_objp)) != aip->path_goal_obj_hash) {
4268                         float dist;
4269                         
4270                         dist = vm_vec_dist_quick(&path_objp->pos, &aip->path_create_pos);
4271                         dist += ai_matrix_dist(&path_objp->orient, &aip->path_create_orient) * 25.0f;
4272
4273                         if (force_recreate_flag || (dist > 2.0f)) {
4274                                 aip->path_next_create_time = timestamp(1000);   //      Update again in as little as 1000 milliseconds, ie 1 second.
4275                                 aip->path_goal_obj_hash = hashval;
4276                                 modify_model_path_points(objp);
4277
4278                                 aip->path_create_pos = path_objp->pos;
4279                                 aip->path_create_orient = path_objp->orient;
4280                                 
4281                                 return dist;
4282                         }
4283                 }
4284         }
4285
4286         return 0.0f;
4287 }
4288
4289 //      Set acceleration for ai_dock().
4290 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)
4291 {
4292         float prev_dot_to_goal = aip->prev_dot_to_goal;
4293         
4294         aip->prev_dot_to_goal = dot;
4295
4296         if (objp->phys_info.speed < 0.0f) {
4297                 accelerate_ship(aip, 1.0f/32.0f);
4298         } else if ((prev_dot_to_goal-dot) > 0.01) {
4299                 if (prev_dot_to_goal > dot + 0.05f) {
4300                         accelerate_ship(aip, 0.0f);
4301                 } else {
4302                         change_acceleration(aip, -1.0f);        //      -1.0f means subtract off flFrametime from acceleration value in 0.0..1.0
4303                 }
4304         } else {
4305                 if ((aip->mode == AIM_DOCK) && (dist_to_next < 150.0f) && (aip->path_start + aip->path_length - 2 == aip->path_cur)) {
4306                         set_accel_for_target_speed(objp, sip->max_speed * max(dist_to_next/500.0f, 1.0f));
4307                         //mprintf(("dist = %7.3f, speed = %7.3f\n", dist_to_next, objp->phys_info.speed));
4308                 } else if ((dot_to_next >= dot * .9) || (dist_to_next > 100.0f)) {
4309                         if (dist_to_goal > 200.0f)
4310                                 set_accel_for_target_speed(objp, sip->max_speed * (dot + 1.0f) / 2.0f);
4311                         else {
4312                                 float   xdot;
4313
4314                                 xdot = (dot_to_next + dot)/2.0f;
4315                                 if (xdot < 0.0f)
4316                                         xdot = 0.0f;
4317
4318                                 // AL: if following a path not in dock mode, move full speed
4319                                 if (( aip->mode != AIM_DOCK ) && (dot > 0.9f)) {
4320                                         set_accel_for_target_speed(objp, sip->max_speed*dot*dot*dot);
4321                                 } else {
4322                                         if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4323                                                 //nprintf(("AI", "Target speed = %7.3f\n", dist_to_goal/8.0f));
4324                                                 set_accel_for_target_speed(objp, dist_to_goal/8.0f + 2.0f);
4325                                         } else {
4326                                                 set_accel_for_target_speed(objp, sip->max_speed * (2*xdot + 0.25f)/4.0f);
4327                                         }
4328                                 }
4329                         }
4330                 } else {
4331                         float   xdot;
4332
4333                         xdot = max(dot_to_next, 0.1f);
4334                         if ( aip->mode != AIM_DOCK ) {
4335                                 set_accel_for_target_speed(objp, sip->max_speed);
4336                         } else {
4337                                 float   speed;
4338                                 if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4339                                         speed = dist_to_goal/8.0f + 2.0f;
4340                                 } else if (dist_to_goal < 4*objp->radius + 50.0f) {
4341                                         speed = dist_to_goal/4.0f + 4.0f;
4342                                 } else {
4343                                         speed = sip->max_speed * (3*xdot + 1.0f)/4.0f;
4344                                 }
4345                                 if (aip->mode == AIM_DOCK) {
4346                                         speed = speed * 2.0f + 1.0f;
4347                                         if (aip->goal_objnum != -1) {
4348                                                 speed += Objects[aip->goal_objnum].phys_info.speed;
4349                                         }
4350                                 }
4351
4352                                 set_accel_for_target_speed(objp, speed);
4353                         }
4354                 }
4355         }
4356 }
4357
4358 //      --------------------------------------------------------------------------
4359 //      Follow a path associated with a large object, such as a capital ship.
4360 //      The points defined on the path are in the object's reference frame.
4361 //      The object of interest is goal_objnum.
4362 //      The paths are defined in the model.  The path of interest is wp_list.
4363 //      The next goal point in the path is wp_index.
4364 //      wp_flags contain special information specific to the path.
4365
4366 // The path vertices are defined by model_path structs:
4367 //              typedef struct model_path {
4368 //                      char            name[MAX_NAME_LEN];                                     // name of the subsystem.  Probably displayed on HUD
4369 //                      int             nverts;
4370 //                      vector  *verts;
4371 //              } model_path;
4372
4373 //      The polymodel struct for the object contains the following:
4374 //              int                     n_paths;
4375 //              model_path      *paths;
4376
4377 //      Returns distance to goal point.
4378 float ai_path()
4379 {
4380         int             num_points;
4381         float           dot, dist_to_goal, dist_to_next, dot_to_next;
4382         ship            *shipp = &Ships[Pl_objp->instance];
4383         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4384         ai_info *aip;
4385         vector  nvel_vec;
4386         float           mag;//, prev_dot_to_goal;
4387         vector  temp_vec, *slop_vec;
4388         object  *gobjp;
4389         vector  *cvp, *nvp, next_vec, gcvp, gnvp;               //      current and next vertices in global coordinates.
4390
4391         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4392
4393         SDL_assert(aip->goal_objnum != -1);
4394         SDL_assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
4395
4396         gobjp = &Objects[aip->goal_objnum];
4397
4398 #ifndef NDEBUG
4399         polymodel *pm = model_get( Ships[gobjp->instance].modelnum );
4400         SDL_assert(pm->n_paths > 0);
4401 #endif
4402
4403         if (aip->path_start == -1) {
4404                 int path_num;
4405                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
4406                 SDL_assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
4407                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
4408         }
4409
4410         // nprintf(("AI", "Frame: %i, Path index = %i/%i\n", AI_FrameCount, aip->path_cur-aip->path_start, aip->path_length));
4411
4412         maybe_recreate_path(Pl_objp, aip, 0);
4413
4414         num_points = aip->path_length;
4415
4416         //      Set cvp and nvp as pointers to current and next vertices of interest on path.
4417         cvp = &Path_points[aip->path_cur].pos;
4418         if ((aip->path_cur + aip->path_dir - aip->path_start < num_points) || (aip->path_cur + aip->path_dir < aip->path_start))
4419                 nvp = &Path_points[aip->path_cur + aip->path_dir].pos;
4420         else {
4421                 //      If this is 0, then path length must be 1 which means we have no direction!
4422                 SDL_assert((aip->path_cur - aip->path_dir >= aip->path_start) && (aip->path_cur - aip->path_dir - aip->path_start < num_points));
4423                 //      Cleanup for above SDL_assert() which we hit too near release. -- MK, 5/24/98.
4424                 if (aip->path_cur - aip->path_dir - aip->path_start >= num_points) {
4425                         if (aip->path_dir == 1)
4426                                 aip->path_cur = aip->path_start;
4427                         else
4428                                 aip->path_cur = aip->path_start + num_points - 1;
4429                 }
4430
4431                 vector  delvec;
4432                 vm_vec_sub(&delvec, cvp, &Path_points[aip->path_cur - aip->path_dir].pos);
4433                 vm_vec_normalize(&delvec);
4434                 vm_vec_scale_add(&next_vec, cvp, &delvec, 10.0f);
4435                 nvp = &next_vec;
4436         }
4437
4438         //      Interrupt if can't get to current goal point.  Debug only.
4439 /*      if (pp_collide(&Pl_objp->pos, cvp, gobjp, Pl_objp->radius)) {
4440                 Int3();
4441         }
4442 */
4443         //      See if can reach next point (as opposed to current point)
4444         //      However, don't do this if docking and next point is last point.
4445         //      That is, we don't want to pursue the last point under control of the
4446         //      path code.  In docking, this is a special hack.
4447         if ((aip->mode != AIM_DOCK) || ((aip->path_cur-aip->path_start) < num_points - 2)) {
4448                 if ((aip->path_cur + aip->path_dir > aip->path_start) && (aip->path_cur + aip->path_dir < aip->path_start + num_points-2)) {
4449                         if ( timestamp_elapsed(aip->path_next_check_time)) {
4450                                 aip->path_next_check_time = timestamp( 3000 );
4451                                 if (!pp_collide(&Pl_objp->pos, nvp, gobjp, 1.1f * Pl_objp->radius)) {
4452                                         cvp = nvp;
4453                                         aip->path_cur += aip->path_dir;
4454                                         nvp = &Path_points[aip->path_cur].pos;
4455                                         //nprintf(("AI", "Reach: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4456                                 }
4457                         }
4458                 }
4459         }
4460
4461         gcvp = *cvp;
4462         gnvp = *nvp;
4463
4464         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &gcvp);
4465         dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, &gnvp);
4466         //      Can't use fvec, need to use velocity vector because we aren't necessarily
4467         //      moving in the direction we're facing.
4468
4469 //      if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4470         if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4471                 mag = 0.0f;
4472                 vm_vec_zero(&nvel_vec);
4473         } else
4474                 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4475
4476         //      If moving not-very-slowly and sliding, then try to slide at goal, rather than
4477         //      point at goal.
4478         slop_vec = NULL;
4479         if (mag < 1.0f)
4480                 nvel_vec = Pl_objp->orient.v.fvec;
4481         else if (mag > 5.0f) {
4482                 float   nv_dot;
4483                 nv_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4484                 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4485                         slop_vec = &temp_vec;
4486                         vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.v.fvec);
4487                 }
4488         }
4489
4490         if (dist_to_goal > 0.1f)
4491                 ai_turn_towards_vector(&gcvp, Pl_objp, flFrametime, sip->srotation_time, slop_vec, NULL, 0.0f, 0);
4492
4493         //      Code to control speed is MUCH less forgiving in path following than in waypoint
4494         //      following.  Must be very close to path or might hit objects.
4495 //      prev_dot_to_goal = aip->prev_dot_to_goal;
4496         dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gcvp);
4497         dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gnvp);
4498
4499         set_accel_for_docking(Pl_objp, aip, dot, dot_to_next, dist_to_next, dist_to_goal, sip);
4500         aip->prev_dot_to_goal = dot;
4501
4502 //mprintf(("Goal index = %i, dist = %7.3f, dot = %7.3f\n", wp_index, dist_to_goal, dot));
4503
4504         //      If moving at a non-tiny velocity, detect attaining path point by its being close to
4505         //      line between previous and current object location.
4506         if ((dist_to_goal < MIN_DIST_TO_WAYPOINT_GOAL) || (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f)) {
4507                 vector  nearest_point;
4508                 float           r, min_dist_to_goal;
4509
4510                 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, &gcvp);
4511
4512                 //      Set min_dist_to_goal = how close must be to waypoint to pick next one.
4513                 //      If docking and this is the second last waypoint, must be very close.
4514                 if ((aip->mode == AIM_DOCK) && (aip->path_cur >= aip->path_length-2))
4515                         min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL;
4516                 else
4517                         min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius;
4518
4519                 if ( (vm_vec_dist_quick(&Pl_objp->pos, &gcvp) < min_dist_to_goal) ||
4520                         (((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, &gcvp) < (MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius)))) {
4521                         aip->path_cur += aip->path_dir;
4522                         //nprintf(("AI", " Near: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4523                         if (((aip->path_cur - aip->path_start) > (num_points+1)) || (aip->path_cur < aip->path_start)) {
4524                                 SDL_assert(aip->mode != AIM_DOCK);              //      If docking, should never get this far, getting to last point handled outside ai_path()
4525                                 aip->path_dir = -aip->path_dir;
4526 //                              aip->path_cur += aip->path_dir;
4527                         }
4528                 }
4529         }
4530
4531         return dist_to_goal;
4532 }
4533
4534 void update_min_max(float val, float *min, float *max)
4535 {
4536         if (val < *min)
4537                 *min = val;
4538         else if (val > *max)
4539                 *max = val;
4540 }
4541
4542 //      Stuff bounding box of all enemy objects within "range" units of object *my_objp.
4543 //      Stuff ni min_vec and max_vec.
4544 //      Return value: Number of enemy objects in bounding box.
4545 int get_enemy_team_range(object *my_objp, float range, int enemy_team_mask, vector *min_vec, vector *max_vec)
4546 {
4547         object  *objp;
4548         ship_obj        *so;
4549         int             count = 0;
4550
4551         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4552                 objp = &Objects[so->objnum];
4553                 if (Ships[objp->instance].team & enemy_team_mask) {
4554                         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))
4555                                 if (vm_vec_dist_quick(&my_objp->pos, &objp->pos) < range) {
4556                                         if (count == 0) {
4557                                                 *min_vec = objp->pos;
4558                                                 *max_vec = objp->pos;
4559                                                 count++;
4560                                         } else {
4561                                                 update_min_max(objp->pos.xyz.x, &min_vec->xyz.x, &max_vec->xyz.x);
4562                                                 update_min_max(objp->pos.xyz.y, &min_vec->xyz.y, &max_vec->xyz.y);
4563                                                 update_min_max(objp->pos.xyz.z, &min_vec->xyz.z, &max_vec->xyz.z);
4564                                         }
4565                                 }
4566
4567                 }
4568         }
4569
4570         return count;
4571 }
4572
4573 //      Pick a relatively safe spot for objp to fly to.
4574 //      Problem:
4575 //              Finds a spot away from any enemy within a bounding box.
4576 //              Doesn't verify that "safe spot" is not near some other enemy.
4577 void ai_safety_pick_spot(object *objp)
4578 {
4579         int             objnum;
4580         int             enemy_team_mask;
4581         vector  min_vec, max_vec;
4582         vector  vec_to_center, center;
4583         vector  goal_pos;
4584
4585         objnum = OBJ_INDEX(objp);
4586
4587         enemy_team_mask = get_enemy_team_mask(objnum);
4588
4589         if (get_enemy_team_range(objp, 1000.0f, enemy_team_mask, &min_vec, &max_vec)) {
4590                 vm_vec_avg(&center, &min_vec, &max_vec);
4591                 vm_vec_normalized_dir(&vec_to_center, &center, &objp->pos);
4592
4593                 vm_vec_scale_add(&goal_pos, &center, &vec_to_center, 2000.0f);
4594         } else
4595                 vm_vec_scale_add(&goal_pos, &objp->pos, &objp->orient.v.fvec, 100.0f);
4596
4597         Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pos;
4598 }
4599
4600 //      Fly to desired safe point.
4601 // Returns distance to that point.
4602 float ai_safety_goto_spot(object *objp)
4603 {
4604         float   dot, dist;
4605         ai_info *aip;
4606         vector  vec_to_goal;
4607         ship_info       *sip;
4608         float   dot_val;
4609
4610         sip = &Ship_info[Ships[objp->instance].ship_info_index];
4611
4612         aip = &Ai_info[Ships[objp->instance].ai_index];
4613         dist = vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
4614         dot = vm_vec_dot(&vec_to_goal, &objp->orient.v.fvec);
4615
4616         dot_val = (1.1f + dot) / 2.0f;
4617         if (dist > 200.0f) {
4618                 set_accel_for_target_speed(objp, sip->max_speed * dot_val);
4619         } else
4620                 set_accel_for_target_speed(objp, sip->max_speed * dot_val * (dist/200.0f + 0.2f));
4621
4622         return dist;
4623 }
4624
4625 void ai_safety_circle_spot(object *objp)
4626 {
4627         vector  goal_point;
4628         ship_info       *sip;
4629         float           dot;
4630
4631         sip = &Ship_info[Ships[objp->instance].ship_info_index];
4632
4633         goal_point = Ai_info[Ships[objp->instance].ai_index].goal_point;
4634         dot = turn_towards_tangent(objp, &goal_point, 250.0f);  //      Increased from 50 to 250 to make circling not look so wacky.
4635
4636         set_accel_for_target_speed(objp, 0.5f * (1.0f + dot) * sip->max_speed/4.0f);
4637
4638 //      float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
4639 //      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));
4640
4641 }
4642
4643 //      --------------------------------------------------------------------------
4644 void ai_safety()
4645 {
4646         ai_info *aip;
4647
4648         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4649
4650         switch (aip->submode) {
4651         case AISS_1:
4652                 ai_safety_pick_spot(Pl_objp);
4653                 aip->submode = AISS_2;
4654                 aip->submode_start_time = Missiontime;
4655                 break;
4656         case AISS_1a:   //      Pick a safe point because we just got whacked!
4657                 Int3();
4658                 break;
4659         case AISS_2:
4660                 if (ai_safety_goto_spot(Pl_objp) < 25.0f) {
4661                         aip->submode = AISS_3;
4662                         aip->submode_start_time = Missiontime;
4663                 }
4664                 break;
4665         case AISS_3:
4666                 ai_safety_circle_spot(Pl_objp);
4667                 break;
4668         default:
4669                 Int3();         //      Illegal submode for ai_safety();
4670                 break;
4671         }
4672 }
4673
4674 //      --------------------------------------------------------------------------
4675 //      make Pl_objp fly waypoints.
4676 void ai_waypoints()
4677 {
4678         int             wp_index;
4679         vector  *wp_cur, *wp_next;
4680         float           dot, dist_to_goal;//, dot_to_next;
4681         ship            *shipp = &Ships[Pl_objp->instance];
4682         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4683         waypoint_list   *wpl;
4684         ai_info *aip;
4685         vector  nvel_vec;
4686         float           mag;
4687         float           prev_dot_to_goal;
4688         vector  temp_vec;
4689         vector  *slop_vec;
4690
4691         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4692
4693         wp_index = aip->wp_index;
4694
4695         if (wp_index == -1) {
4696                 ai_start_waypoints(Pl_objp, 0, WPF_REPEAT);
4697                 wp_index = aip->wp_index;
4698                 aip->wp_dir = 1;
4699         }
4700
4701         wpl = &Waypoint_lists[Ai_info[Ships[Pl_objp->instance].ai_index].wp_list];
4702
4703         SDL_assert(wpl->count); // What? Is this zero? Probably wp_index never got initialized!
4704
4705         wp_cur = &wpl->waypoints[wp_index];
4706         wp_next = &wpl->waypoints[(wp_index+1) % wpl->count];
4707
4708         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, wp_cur);
4709
4710         //      Can't use fvec, need to use velocity vector because we aren't necessarily
4711         //      moving in the direction we're facing.
4712         // AL 23-3-98: Account for very small velocities by checking result of vm_vec_mag().
4713         //                                      If we don't vm_vec_copy_normalize() will think it is normalizing a null vector.
4714 //      if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4715         if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4716                 mag = 0.0f;
4717                 vm_vec_zero(&nvel_vec);
4718         } else {
4719                 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4720         }
4721
4722         //      If moving not-very-slowly and sliding, then try to slide at goal, rather than
4723         //      point at goal.
4724         slop_vec = NULL;
4725         if (mag < 1.0f) {
4726                 nvel_vec = Pl_objp->orient.v.fvec;
4727         } else if (mag > 5.0f) {
4728                 float   nv_dot;
4729                 nv_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4730                 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4731                         slop_vec = &temp_vec;
4732                         vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.v.fvec);
4733                 }
4734         }
4735
4736         //      If a wing leader, take turns more slowly, based on size of wing.
4737         int     scale;
4738
4739         if (Ai_info[Ships[Pl_objp->instance].ai_index].wing >= 0) {
4740                 scale = Wings[Ai_info[Ships[Pl_objp->instance].ai_index].wing].current_count;
4741                 scale = (int) ((scale+1)/2);
4742         } else {
4743                 scale = 1;
4744         }
4745
4746         if (dist_to_goal > 0.1f) {
4747                 ai_turn_towards_vector(wp_cur, Pl_objp, flFrametime, sip->srotation_time*3.0f*scale, slop_vec, NULL, 0.0f, 0);
4748         }
4749
4750         prev_dot_to_goal = aip->prev_dot_to_goal;
4751         dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_cur);
4752         /*dot_to_next =*/ vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_next);
4753         aip->prev_dot_to_goal = dot;
4754
4755         //      If there is no next point on the path, don't care about dot to next.
4756 //      if (wp_index + 1 >= wpl->count) {
4757 //              dot_to_next = dot;
4758 //      }
4759
4760         // nprintf(("AI", "Wp #%i, dot = %6.3f, next dot = %6.3f, dist = %7.2f\n", wp_index, dot, dot_to_next, dist_to_goal));
4761
4762         if (Pl_objp->phys_info.speed < 0.0f) {
4763                 accelerate_ship(aip, 1.0f/32);
4764         } else if (prev_dot_to_goal > dot+0.01f) {
4765                 //      We are further from pointing at our goal this frame than last frame, so slow down.
4766                 set_accel_for_target_speed(Pl_objp, Pl_objp->phys_info.speed * 0.95f);
4767         } else if (dist_to_goal < 100.0f) {
4768                 float slew_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4769                 if (fl_abs(slew_dot) < 0.9f) {
4770                         accelerate_ship(aip, 0.0f);
4771                 } else if (dot < 0.88f + 0.1f*(100.0f - dist_to_goal)/100.0f) {
4772                         accelerate_ship(aip, 0.0f);
4773                 } else {
4774                         accelerate_ship(aip, 0.5f * dot * dot);
4775                 }
4776         } else {
4777                 float   dot1;
4778                 if (dist_to_goal < 250.0f) {
4779                         dot1 = dot*dot*dot;                             //      Very important to be pointing towards goal when nearby.  Note, cubing preserves sign.
4780                 } else {
4781                         if (dot > 0.0f) {
4782                                 dot1 = dot*dot;
4783                         } else {
4784                                 dot1 = dot;
4785                         }
4786                 }
4787
4788                 if (dist_to_goal > 100.0f + Pl_objp->radius * 2) {
4789                         if (dot < 0.2f) {
4790                                 dot1 = 0.2f;
4791                         }
4792                 }
4793
4794                 if (sip->flags & SIF_SMALL_SHIP) {
4795                         set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/5.0f);
4796                 } else {
4797                         set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/10.0f);
4798                 }
4799         }
4800
4801         //      Make sure not travelling too fast for someone to keep up.
4802         float   max_allowed_speed = 9999.9f;
4803
4804         if (shipp->wingnum != -1) {
4805                 max_allowed_speed = 0.9f * get_wing_lowest_max_speed(Pl_objp);
4806         }
4807
4808         // check if waypoint speed cap is set and adjust max speed
4809         if (aip->waypoint_speed_cap > 0) {
4810                 max_allowed_speed = (float) aip->waypoint_speed_cap;
4811         }
4812
4813         if (aip->prev_accel * shipp->current_max_speed > max_allowed_speed) {
4814                 accelerate_ship(aip, max_allowed_speed / shipp->current_max_speed);
4815         }
4816
4817         if (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f) {
4818                 vector  nearest_point;
4819                 float           r;
4820
4821                 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, wp_cur);
4822
4823                 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))) ||
4824                         (((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, wp_cur) < (MIN_DIST_TO_WAYPOINT_GOAL + fl_sqrt(Pl_objp->radius))))) {
4825                         wp_index++;
4826                         if (wp_index >= wpl->count) {
4827                                 if (aip->wp_flags & WPF_REPEAT) {
4828                                         wp_index = 0;
4829                                 } else {
4830                                         int treat_as_ship;
4831
4832                                         // when not repeating waypoints -- mark the goal as done and put and entry into the mission log
4833                                         // we must be careful when dealing with wings.  A ship in a wing might be completing
4834                                         // a waypoint for for the entire wing, or it might be completing a goal for itself.  If
4835                                         // for itself and in a wing, treat the completion as we would a ship
4836                                         treat_as_ship = 1;
4837                                         if ( Ships[Pl_objp->instance].wingnum != -1 ) {
4838                                                 int type;
4839
4840                                                 // I don't think that you can fly waypoints as dynamic goals!!!
4841                                                 // -- This is legal, just stupid. -- SDL_assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4842                                                 
4843                                                 //      Clean up from above SDL_assert, just in case we ship without fixing it.  (Encountered by JimB on 2/9/98)
4844                                                 if ( (aip->active_goal == AI_GOAL_NONE) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC) ) {
4845                                                         aip->mode = AIM_NONE;
4846                                                         Int3(); //      Look at the ship, find out of it's supposed to be flying waypoints. -- MK.
4847                                                 }
4848
4849                                                 type = aip->goals[aip->active_goal].type;
4850                                                 if ( (type == AIG_TYPE_EVENT_WING) || (type == AIG_TYPE_PLAYER_WING) ) {
4851                                                         treat_as_ship = 0;
4852                                                 } else {
4853                                                         treat_as_ship = 1;
4854                                                 }
4855                                         }
4856
4857                                         // if the ship is not in a wing, remove the goal and continue on
4858                                         if ( treat_as_ship ) {
4859                                                 ai_mission_goal_complete( aip );                                        // this call should reset the AI mode
4860                                                 mission_log_add_entry(LOG_WAYPOINTS_DONE, Ships[Pl_objp->instance].ship_name, wpl->name, -1 );
4861                                         } else {
4862                                                 // this ship is in a wing.  We must mark the goal as being completed for all ships
4863                                                 // in the wing.  We will also mark an entry in the log that the wing completed the goal
4864                                                 // not the individual ship.
4865                                                 ai_mission_wing_goal_complete( Ships[Pl_objp->instance].wingnum, &(aip->goals[aip->active_goal]) );
4866                                                 mission_log_add_entry( LOG_WAYPOINTS_DONE, Wings[Ships[Pl_objp->instance].wingnum].name, wpl->name, -1 );
4867                                         }
4868                                         //wp_index = wpl->count-1;
4869                                 }
4870                         }
4871
4872                         aip->wp_index = wp_index;
4873                 }
4874         }
4875 }
4876
4877 //      Make Pl_objp avoid En_objp
4878 //      Not like evading.  This is for avoiding a collision!
4879 //      Note, use sliding if available.
4880 void avoid_ship()
4881 {
4882         //      To avoid an object, turn towards right or left vector until facing away from object.
4883         //      To choose right vs. left, pick one that is further from center of avoid object.
4884         //      Keep turning away from until pointing away from ship.
4885         //      Stay in avoid mode until at least 3 enemy ship radii away.
4886
4887         //      Speed setting:
4888         //      If inside sphere, zero speed and turn towards outside.
4889         //      If outside sphere, inside 2x sphere, set speed percent of max to:
4890         //              max(away_dot, (dist-rad)/rad)
4891         //      where away_dot is dot(Pl_objp->v.fvec, vec_En_objp_to_Pl_objp)
4892
4893         vector  vec_to_enemy;
4894         float           away_dot;
4895         float           dist;
4896         ship            *shipp = &Ships[Pl_objp->instance];
4897         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4898         ai_info *aip = &Ai_info[shipp->ai_index];
4899         vector  player_pos, enemy_pos;
4900
4901         // if we're avoiding a stealth ship, then we know where he is, update with no error
4902         if ( is_object_stealth_ship(En_objp) ) {
4903                 update_ai_stealth_info_with_error(aip/*, 1*/);
4904         }
4905
4906         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
4907         vm_vec_sub(&vec_to_enemy, &enemy_pos, &Pl_objp->pos);
4908
4909         dist = vm_vec_normalize(&vec_to_enemy);
4910         away_dot = -vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_to_enemy);
4911         
4912         if ((sip->max_vel.xyz.x > 0.0f) || (sip->max_vel.xyz.y > 0.0f)) {
4913                 if (vm_vec_dot(&Pl_objp->orient.v.rvec, &vec_to_enemy) > 0.0f) {
4914                         AI_ci.sideways = -1.0f;
4915                 } else {
4916                         AI_ci.sideways = 1.0f;
4917                 }
4918                 if (vm_vec_dot(&Pl_objp->orient.v.uvec, &vec_to_enemy) > 0.0f) {
4919                         AI_ci.vertical = -1.0f;
4920                 } else {
4921                         AI_ci.vertical = 1.0f;
4922                 }
4923         }               
4924
4925         //nprintf(("AI", "Frame %i: Sliding: %s %s\n", Framecount, AI_ci.sideways < 0 ? "left" : "right", AI_ci.vertical < 0 ? "down" : "up" ));
4926         // nprintf(("AI", "away_dot = %6.3f, dist = %7.2f, dist/radsum = %6.3f\n", away_dot, dist, dist/(Pl_objp->radius + En_objp->radius)));
4927
4928         //      If in front of enemy, turn away from it.
4929         //      If behind enemy, try to get fully behind it.
4930         if (away_dot < 0.0f) {
4931                 turn_away_from_point(Pl_objp, &enemy_pos, Pl_objp->phys_info.speed);
4932         } else {
4933                 vector  goal_pos;
4934
4935                 vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.fvec, -100.0f);
4936                 turn_towards_point(Pl_objp, &goal_pos, NULL, Pl_objp->phys_info.speed);
4937         }
4938
4939         //      Set speed.
4940         float   radsum = Pl_objp->radius + En_objp->radius;
4941
4942         if (dist < radsum)
4943                 accelerate_ship(aip, max(away_dot, 0.2f));
4944         else if (dist < 2*radsum)
4945                 accelerate_ship(aip, max(away_dot, (dist - radsum) / radsum)+0.2f);
4946         else
4947                 accelerate_ship(aip, 1.0f);
4948
4949 }
4950
4951 //      Maybe it's time to resume the previous AI mode in aip->previous_mode.
4952 //      Each type of previous_mode has its own criteria on when to resume.
4953 //      Return true if previous mode was resumed.
4954 int maybe_resume_previous_mode(object *objp, ai_info *aip)
4955 {
4956         //      Only (maybe) resume previous goal if current goal is dynamic.
4957         if (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC)
4958                 return 0;
4959
4960         if (aip->mode == AIM_EVADE_WEAPON) {
4961                 if (timestamp_elapsed(aip->mode_time) || (((aip->nearest_locked_object == -1) || (Objects[aip->nearest_locked_object].type != OBJ_WEAPON)) && (aip->danger_weapon_objnum == -1))) {
4962                         SDL_assert(aip->previous_mode != AIM_EVADE_WEAPON);
4963                         aip->mode = aip->previous_mode;
4964                         aip->submode = aip->previous_submode;
4965                         aip->submode_start_time = Missiontime;
4966                         aip->active_goal = AI_GOAL_NONE;
4967                         aip->mode_time = -1;                    //      Means do forever.
4968                         return 1;
4969                 }
4970         } else if ( aip->previous_mode == AIM_GUARD) {
4971                 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
4972                         object  *guard_objp;
4973                         float   dist;
4974
4975                         guard_objp = &Objects[aip->guard_objnum];
4976                         dist = vm_vec_dist_quick(&guard_objp->pos, &objp->pos);
4977
4978                         //      If guarding ship is far away from guardee and enemy is far away from guardee,
4979                         //      then stop chasing and resume guarding.
4980                         if (dist > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4981                                 if ((En_objp != NULL) && (En_objp->type == OBJ_SHIP)) {
4982                                         if (vm_vec_dist_quick(&guard_objp->pos, &En_objp->pos) > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4983                                                 SDL_assert(aip->previous_mode == AIM_GUARD);
4984                                                 aip->mode = aip->previous_mode;
4985                                                 aip->submode = AIS_GUARD_PATROL;
4986                                                 aip->active_goal = AI_GOAL_NONE;
4987                                                 return 1;
4988                                         }
4989                                 }
4990                         }
4991                 }
4992         }
4993
4994         return 0;
4995
4996 }
4997
4998 //      Call this function if you want something to happen on average every N quarters of a second.
4999 //      The truth value returned by this function will be the same for any given quarter second interval.
5000 //      The value "num" is only passed in to get asynchronous behavior for different objects.
5001 //      modulus == 1 will always return true.
5002 //      modulus == 2 will return true half the time.
5003 //      modulus == 16 will return true for one quarter second interval every four seconds.
5004 int static_rand_timed(int num, int modulus)
5005 {
5006         if (modulus < 2)
5007                 return 1;
5008         else {
5009                 int     t;
5010
5011                 t = Missiontime >> 18;          //      Get time in quarters of a second
5012                 t += num;
5013
5014                 return !(t % modulus);
5015         }
5016 }
5017
5018 //      Maybe fire afterburner based on AI class
5019 int ai_maybe_fire_afterburner(object *objp, ai_info *aip)
5020 {
5021         if (aip->ai_class == 0)
5022                 return 0;               //      Lowest level never aburners away
5023         else  {
5024                 //      Maybe don't afterburner because of a potential collision with the player.
5025                 //      If not multiplayer, near player and player in front, probably don't afterburner.
5026                 if (!(Game_mode & GM_MULTIPLAYER)) {
5027                         if (Ships[objp->instance].team == Player_ship->team) {
5028                                 float   dist;
5029
5030                                 dist = vm_vec_dist_quick(&objp->pos, &Player_obj->pos) - Player_obj->radius - objp->radius;
5031                                 if (dist < 150.0f) {
5032                                         vector  v2p;
5033                                         float           dot;
5034
5035                                         vm_vec_normalized_dir(&v2p, &Player_obj->pos, &objp->pos);
5036                                         dot = vm_vec_dot(&v2p, &objp->orient.v.fvec);
5037
5038                                         if (dot > 0.0f) {
5039                                                 if (dot * dist > 50.0f)
5040                                                         return 0;
5041                                         }
5042                                 }
5043                         }
5044                 }
5045
5046                 if (aip->ai_class >= Num_ai_classes-2)
5047                         return 1;               //      Highest two levels always aburner away.
5048                 else {
5049                         return static_rand_timed(objp-Objects, Num_ai_classes - aip->ai_class);
5050                 }
5051         }
5052 }
5053
5054 //      Maybe engage afterburner after being hit by an object.
5055 void maybe_afterburner_after_ship_hit(object *objp, ai_info *aip, object *en_objp)
5056 {
5057         //      Only do if facing a little away.
5058         if (en_objp != NULL) {
5059                 vector  v2e;
5060
5061                 vm_vec_normalized_dir(&v2e, &en_objp->pos, &objp->pos);
5062                 if (vm_vec_dot(&v2e, &objp->orient.v.fvec) > -0.5f)
5063                         return;
5064         }
5065
5066         if (!( objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5067                 if (ai_maybe_fire_afterburner(objp, aip)) {
5068                         afterburners_start(objp);
5069                         aip->afterburner_stop_time = Missiontime + F1_0/2;
5070                 }
5071         }
5072 }
5073
5074 //      Return true if object *objp is an instructor.
5075 //      Is an instructor if name begins INSTRUCTOR_SHIP_NAME else not.
5076 int is_instructor(object *objp)
5077 {
5078         return !SDL_strncasecmp(Ships[objp->instance].ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME));
5079 }
5080
5081 //      Evade the weapon aip->danger_weapon_objnum
5082 //      If it's not valid, do a quick out.
5083 //      Evade by accelerating hard.
5084 //      If necessary, turn hard left or hard right.
5085 void evade_weapon()
5086 {
5087         object  *weapon_objp = NULL;
5088         object  *unlocked_weapon_objp = NULL, *locked_weapon_objp = NULL;
5089         vector  weapon_pos, player_pos, goal_point;
5090         vector  vec_from_enemy;
5091         float           dot_from_enemy, dot_to_enemy;
5092         float           dist;
5093         ship            *shipp = &Ships[Pl_objp->instance];
5094         ai_info *aip = &Ai_info[shipp->ai_index];
5095
5096         if (is_instructor(Pl_objp))
5097                 return;
5098
5099         //      Make sure we're actually being attacked.
5100         //      Favor locked objects.
5101         if (aip->nearest_locked_object != -1) {
5102                 if (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)
5103                         locked_weapon_objp = &Objects[aip->nearest_locked_object];
5104         }
5105         
5106         if (aip->danger_weapon_objnum != -1) {
5107                 if (Objects[aip->danger_weapon_objnum].signature == aip->danger_weapon_signature) {
5108                         unlocked_weapon_objp = &Objects[aip->danger_weapon_objnum];
5109                 } else {
5110                         aip->danger_weapon_objnum = -1;         //      Signatures don't match, so no longer endangered.
5111                 }
5112         }
5113
5114         if (locked_weapon_objp != NULL) {
5115                 if (unlocked_weapon_objp != NULL) {
5116                         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))
5117                                 weapon_objp = locked_weapon_objp;
5118                         else
5119                                 weapon_objp = unlocked_weapon_objp;
5120                 } else
5121                         weapon_objp = locked_weapon_objp;
5122         } else if (unlocked_weapon_objp != NULL)
5123                 weapon_objp = unlocked_weapon_objp;
5124         else {
5125                 if (aip->mode == AIM_EVADE_WEAPON)
5126                         maybe_resume_previous_mode(Pl_objp, aip);
5127                 return;
5128         }
5129
5130         SDL_assert(weapon_objp != NULL);
5131
5132         if (weapon_objp->type != OBJ_WEAPON) {
5133                 if (aip->mode == AIM_EVADE_WEAPON)
5134                         maybe_resume_previous_mode(Pl_objp, aip);
5135                 return;
5136         }
5137         
5138         weapon_pos = weapon_objp->pos;
5139         player_pos = Pl_objp->pos;
5140
5141         //      Make speed based on skill level, varying at highest skill level, which is harder to hit.
5142         accelerate_ship(aip, 1.0f);
5143
5144         dist = vm_vec_normalized_dir(&vec_from_enemy, &player_pos, &weapon_pos);
5145
5146         dot_to_enemy = -vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_from_enemy);
5147         dot_from_enemy = vm_vec_dot(&weapon_objp->orient.v.fvec, &vec_from_enemy);
5148         //nprintf(("AI", "dot from enemy = %7.3f\n", dot_from_enemy));
5149
5150         //      If shot is incoming...
5151         if (dot_from_enemy < 0.3f) {
5152                 if (weapon_objp == unlocked_weapon_objp)
5153                         aip->danger_weapon_objnum = -1;
5154                 return;
5155         } else if (dot_from_enemy > 0.7f) {
5156                 if (dist < 200.0f) {
5157                         if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5158                                 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
5159                                         //nprintf(("AI", "Frame %i, turning on afterburner.\n", AI_FrameCount));
5160                                         afterburners_start(Pl_objp);
5161                                         aip->afterburner_stop_time = Missiontime + F1_0/2;
5162                                 }
5163                         }
5164                 }
5165
5166                 //      If we're sort of pointing towards it...
5167                 if ((dot_to_enemy < -0.5f) || (dot_to_enemy > 0.5f)) {
5168                         float   rdot;
5169
5170                         //      Turn hard left or right, depending on which gets out of way quicker.
5171                         rdot = vm_vec_dot(&Pl_objp->orient.v.rvec, &vec_from_enemy);
5172
5173                         if ((rdot < -0.5f) || (rdot > 0.5f))
5174                                 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.v.rvec, -200.0f);
5175                         else
5176                                 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.v.rvec, 200.0f);
5177
5178                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
5179                 }
5180         }
5181
5182 }
5183
5184 //      Use sliding and backwards moving to face enemy.
5185 //      (Coded 2/20/98.  Works fine, but it's hard to see how to integrate it into the AI system.
5186 //       Typically ships are moving so fast that a little sliding isn't enough to gain an advantage.
5187 //       It's currently used to avoid collisions and could be used to evade weapon fire, but the latter
5188 //       would be frustrating, I think.
5189 //       This function is currently not called.)
5190 void slide_face_ship()
5191 {
5192         ship_info       *sip;
5193
5194         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
5195
5196         //      If can't slide, return.
5197         if ((sip->max_vel.xyz.x == 0.0f) && (sip->max_vel.xyz.y == 0.0f))
5198                 return;
5199
5200         vector  goal_pos;
5201         float           dot_from_enemy;
5202         vector  vec_from_enemy, vec_to_goal;
5203         float           dist;
5204         float           up, right;
5205         ai_info         *aip;
5206
5207         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
5208
5209         dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
5210
5211         ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
5212
5213         dot_from_enemy = vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.fvec);
5214
5215         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.rvec) > 0.0f)
5216                 right = 1.0f;
5217         else
5218                 right = -1.0f;
5219
5220         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.uvec) > 0.0f)
5221                 up = 1.0f;
5222         else
5223                 up = -1.0f;
5224
5225         vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.rvec, right * 200.0f);
5226         vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.uvec, up * 200.0f);
5227
5228         vm_vec_normalized_dir(&vec_to_goal, &goal_pos, &Pl_objp->pos);
5229
5230         if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.v.rvec) > 0.0f)
5231                 AI_ci.sideways = 1.0f;
5232         else
5233                 AI_ci.sideways = -1.0f;
5234
5235         if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.v.uvec) > 0.0f)
5236                 AI_ci.vertical = 1.0f;
5237         else
5238                 AI_ci.vertical = -1.0f;
5239
5240         if (dist < 200.0f) {
5241                 if (dot_from_enemy < 0.7f)
5242                         accelerate_ship(aip, -1.0f);
5243                 else
5244                         accelerate_ship(aip, dot_from_enemy + 0.5f);
5245         } else {
5246                 if (dot_from_enemy < 0.7f) {
5247                         accelerate_ship(aip, 0.2f);
5248                 } else {
5249                         accelerate_ship(aip, 1.0f);
5250                 }
5251         }
5252 }
5253
5254 //      General code for handling one ship evading another.
5255 //      Problem: This code is also used for avoiding an impending collision.
5256 //      In such a case, it is not good to go to max speed, which is often good
5257 //      for a certain kind of evasion.
5258 void evade_ship()
5259 {
5260         vector  player_pos, enemy_pos, goal_point;
5261         vector  vec_from_enemy;
5262         float           dot_from_enemy;
5263         float           dist;
5264         ship            *shipp = &Ships[Pl_objp->instance];
5265         ship_info       *sip = &Ship_info[shipp->ship_info_index];
5266         ai_info *aip = &Ai_info[shipp->ai_index];
5267         float           bank_override = 0.0f;
5268
5269         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
5270
5271         //      Make speed based on skill level, varying at highest skill level, which is harder to hit.
5272         if (Game_skill_level == NUM_SKILL_LEVELS-1) {
5273                 int     rand_int;
5274                 float   accel_val;
5275
5276                 rand_int = static_rand(Pl_objp-Objects);
5277                 accel_val = (float) (((Missiontime^rand_int) >> 14) & 0x0f)/32.0f + 0.5f;
5278                 accelerate_ship(aip, accel_val);
5279                 //nprintf(("AI", "Accel value = %7.3f\n", accel_val));
5280         } else
5281                 accelerate_ship(aip, (float) (Game_skill_level+2) / (NUM_SKILL_LEVELS+1));
5282
5283         if ((Missiontime - aip->submode_start_time > F1_0/2) && (sip->afterburner_fuel_capacity > 0.0f)) {
5284                 float percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
5285                 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
5286                         afterburners_start(Pl_objp);
5287                         aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
5288                 }
5289         }
5290
5291         vm_vec_sub(&vec_from_enemy, &player_pos, &enemy_pos);
5292
5293         dist = vm_vec_normalize(&vec_from_enemy);
5294         dot_from_enemy = vm_vec_dot(&En_objp->orient.v.fvec, &vec_from_enemy);
5295
5296         if (dist > 250.0f) {
5297                 vector  gp1, gp2;
5298                 //      If far away from enemy, circle, going to nearer of point far off left or right wing
5299                 vm_vec_scale_add(&gp1, &enemy_pos, &En_objp->orient.v.rvec, 250.0f);
5300                 vm_vec_scale_add(&gp2, &enemy_pos, &En_objp->orient.v.rvec, -250.0f);
5301                 if (vm_vec_dist_quick(&gp1, &Pl_objp->pos) < vm_vec_dist_quick(&gp2, &Pl_objp->pos))
5302                         goal_point = gp1;
5303                 else
5304                         goal_point = gp2;
5305         } else if (dot_from_enemy < 0.1f) {
5306                 //      If already close to behind, goal is to get completely behind.
5307                 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.fvec, -1000.0f);
5308         } else if (dot_from_enemy > 0.9f) {
5309                 //      If enemy pointing almost right at self, and self pointing close to enemy, turn away from
5310                 vector  vec_to_enemy;
5311                 float           dot_to_enemy;
5312
5313                 vm_vec_sub(&vec_to_enemy, &enemy_pos, &player_pos);
5314
5315                 vm_vec_normalize(&vec_to_enemy);
5316                 dot_to_enemy = vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_to_enemy);
5317                 if (dot_to_enemy > 0.75f) {
5318                         //      Used to go to En_objp's right vector, but due to banking while turning, that
5319                         //      caused flying in an odd spiral.
5320                         vm_vec_scale_add(&goal_point, &enemy_pos, &Pl_objp->orient.v.rvec, 1000.0f);
5321                         if (dist < 100.0f)
5322                                 bank_override = Pl_objp->phys_info.speed; 
5323                 } else {
5324                         bank_override = Pl_objp->phys_info.speed;                       //      In enemy's sights, not pointing at him, twirl away.
5325                         // nprintf(("Mike", " Do sumpin' else."));
5326                         goto evade_ship_l1;
5327                 }
5328         } else {
5329 evade_ship_l1: ;
5330                 if (aip->ai_evasion > myrand()*100.0f/32767.0f) {
5331                         int     temp;
5332                         float   scale;
5333                         float   psrandval;      //      some value close to zero to choose whether to turn right or left.
5334
5335                         psrandval = (float) (((Missiontime >> 14) & 0x0f) - 8); //      Value between -8 and 7
5336                         psrandval = psrandval/16.0f;                                                    //      Value between -1/2 and 1/2 (approx)
5337
5338                         //      If not close to behind, turn towards his right or left vector, whichever won't cross his path.
5339                         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.rvec) > psrandval) {
5340                                 scale = 1000.0f;
5341                         } else {
5342                                 scale = -1000.0f;
5343                         }
5344
5345                         vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.rvec, scale);
5346
5347                         temp = ((Missiontime >> 16) & 0x07);
5348                         temp = ((temp * (temp+1)) % 16)/2 - 4;
5349                         if ((psrandval == 0) && (temp == 0))
5350                                 temp = 3;
5351
5352                         scale = 200.0f * temp;
5353
5354                         vm_vec_scale_add2(&goal_point, &En_objp->orient.v.uvec, scale);
5355                 } else {
5356                         //      No evasion this frame, but continue with previous turn.
5357                         //      Reason: If you don't, you lose rotational momentum.  Turning every other frame,
5358                         //      and not in between results in a very slow turn because of loss of momentum.
5359                         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))
5360                                 goal_point = aip->prev_goal_point;
5361                         else
5362                                 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.rvec, 100.0f);
5363                 }
5364         }
5365
5366         // nprintf(("Mike", "Goal point = %7.1f %7.1f %7.1f\n", goal_point.xyz.x, goal_point.xyz.y, goal_point.xyz.z));
5367         turn_towards_point(Pl_objp, &goal_point, NULL, bank_override);
5368
5369         aip->prev_goal_point = goal_point;
5370 }
5371
5372 //      --------------------------------------------------------------------------
5373 //      Fly in a manner making it difficult for opponent to attack.
5374 void ai_evade()
5375 {
5376         evade_ship();
5377 }
5378
5379 /*
5380 // -------------------------------------------------------------------
5381 //      Refine predicted enemy position because enemy will move while we move
5382 //      towards predicted enemy position.
5383 //      last_delta_vec is stuffed with size of polishing in last step.  This small amount
5384 //      can be used to perturb the predicted position to make firing not be exact.
5385 //      This function will almost always undershoot actual position, assuming both ships
5386 //      are moving at constant speed.  But with even one polishing step, the error should
5387 //      be under 1%. The number of polishing steps is specified in the parameter num_polish_steps.
5388 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)
5389 {
5390         int     iteration;
5391         vector  player_pos = pobjp->pos;
5392         vector  enemy_pos = *predicted_enemy_pos;
5393         physics_info    *en_physp = &eobjp->phys_info;
5394         float           time_to_enemy;
5395         vector  last_predicted_enemy_pos = *predicted_enemy_pos;
5396         
5397         vm_vec_zero(last_delta_vec);
5398
5399         for (iteration=0; iteration < num_polish_steps; iteration++) {
5400                 dist_to_enemy = vm_vec_dist_quick(predicted_enemy_pos, &player_pos);
5401                 time_to_enemy = compute_time_to_enemy(dist_to_enemy, pobjp, eobjp);
5402                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, time_to_enemy);
5403                 vm_vec_sub(last_delta_vec, predicted_enemy_pos, &last_predicted_enemy_pos);
5404                 last_predicted_enemy_pos= *predicted_enemy_pos;
5405         }
5406 }
5407 */
5408
5409 /*
5410 Relevant variables are:
5411         best_dot_to_enemy               best dot product to enemy in last BEST_DOT_TIME seconds
5412         best_dot_to_time                time at which best dot occurred
5413         best_dot_from_enemy     best dot product for enemy to player in last BEST_DOT_TIME seconds
5414         best_dot_from_time      time at which best dot occurred
5415         submode_start_time      time at which we entered the current submode
5416         previous_submode                previous submode, get it?
5417 Legal submodes are:
5418         CONTINUOUS_TURN vector_id {0..3 = right, -right, up, -up}
5419         ATTACK
5420         EVADE_SQUIGGLE
5421         EVADE_BRAKE
5422 */
5423
5424 float   G_collision_time;
5425 vector  G_predicted_pos, G_fire_pos;
5426
5427 /*
5428 void show_firing_diag()
5429 {
5430         float           dot;
5431         vector  v2t;
5432         vector  pos1, pos2;
5433         float           dist;
5434
5435         if (G_collision_time == 0.0f)
5436                 return;
5437
5438         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",
5439                 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));
5440         vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5441         dot = vm_vec_dot(&v2t, &Pl_objp->orient.v.fvec);
5442         mprintf(("Dot of v.fvec and vector to predicted position = %10.7f (%7.3f degrees)\n", dot, acos(dot)*180.0f/3.141592654f));
5443
5444         vm_vec_scale_add(&pos1, &En_objp->pos, &En_objp->phys_info.vel, G_collision_time);
5445         vm_vec_scale_add(&pos2, &G_fire_pos, &Pl_objp->orient.v.fvec, G_collision_time*300.0f);
5446         dist = vm_vec_dist(&pos1, &pos2);
5447
5448         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));
5449 }
5450 */
5451
5452 //      If:
5453 //              flags & WIF_PUNCTURE
5454 //      Then Select a Puncture weapon.
5455 //      Else
5456 //              Select Any ol' weapon.
5457 //      Returns primary_bank index.
5458 int ai_select_primary_weapon(object *objp, object *other_objp, int flags)
5459 {
5460         ship    *shipp = &Ships[objp->instance];
5461         ship_weapon *swp = &shipp->weapons;
5462
5463         //SDL_assert( other_objp != NULL );
5464         SDL_assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5465
5466         if (flags & WIF_PUNCTURE) {
5467                 if (swp->current_primary_bank >= 0) {
5468                         int     bank_index;
5469
5470                         bank_index = swp->current_primary_bank;
5471
5472                         if (Weapon_info[swp->primary_bank_weapons[bank_index]].wi_flags & WIF_PUNCTURE) {
5473                                 //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[bank_index]].name));
5474                                 return swp->current_primary_bank;
5475                         }
5476                 }
5477                 for (int i=0; i<swp->num_primary_banks; i++) {
5478                         int     weapon_info_index;
5479
5480                         weapon_info_index = swp->primary_bank_weapons[i];
5481
5482                         if (weapon_info_index > -1){
5483                                 if (Weapon_info[weapon_info_index].wi_flags & WIF_PUNCTURE) {
5484                                         swp->current_primary_bank = i;
5485                                         //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5486                                         return i;
5487                                 }
5488                         }
5489                 }
5490                 
5491                 // AL 26-3-98: If we couldn't find a puncture weapon, pick first available weapon if one isn't active
5492                 if ( swp->current_primary_bank < 0 ) {
5493                         if ( swp->num_primary_banks > 0 ) {
5494                                 swp->current_primary_bank = 0;
5495                         }
5496                 }
5497
5498         } else {                //      Don't need to be using a puncture weapon.
5499                 if (swp->current_primary_bank >= 0) {
5500                         if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)){
5501                                 return swp->current_primary_bank;
5502                         }
5503                 }
5504                 for (int i=0; i<swp->num_primary_banks; i++) {
5505                         if (swp->primary_bank_weapons[i] > -1) {
5506                                 if (!(Weapon_info[swp->primary_bank_weapons[i]].wi_flags & WIF_PUNCTURE)) {
5507                                         swp->current_primary_bank = i;
5508                                         nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5509                                         return i;
5510                                 }
5511                         }
5512                 }
5513                 //      Wasn't able to find a non-puncture weapon.  Stick with what we have.
5514         }
5515
5516         SDL_assert( swp->current_primary_bank != -1 );          // get Alan or Allender
5517
5518         return swp->current_primary_bank;
5519 }
5520
5521 //      --------------------------------------------------------------------------
5522 //      Maybe link primary weapons.
5523 void set_primary_weapon_linkage(object *objp)
5524 {
5525         ship            *shipp;
5526         ai_info *aip;
5527
5528         shipp = &Ships[objp->instance];
5529         aip     = &Ai_info[shipp->ai_index];
5530
5531         shipp->flags &= ~SF_PRIMARY_LINKED;
5532
5533         if (Num_weapons > (int) (MAX_WEAPONS * 0.75f)) {
5534                 if (shipp->flags & SF_PRIMARY_LINKED) {
5535                         nprintf(("AI", "Frame %i, ship %s: Unlinking primaries.\n", Framecount, shipp->ship_name));
5536                 }
5537                 shipp->flags &= ~SF_PRIMARY_LINKED;
5538                 return;         //      If low on slots, don't link.
5539         }
5540
5541         shipp->flags &= ~SF_PRIMARY_LINKED;
5542
5543         // AL: ensure target is a ship!
5544         if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
5545                 // If trying to destroy a big ship (i.e., not disable/disarm), always unleash all weapons
5546                 if ( ship_get_SIF(&Ships[Objects[aip->target_objnum].instance]) & SIF_BIG_SHIP) {
5547                         if ( aip->targeted_subsys == NULL ) {
5548                                 shipp->flags |= SF_PRIMARY_LINKED;
5549                                 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5550                                 return;
5551                         }
5552                 }
5553         }
5554
5555         // AL 2-11-98: If ship has a disarm or disable goal, don't link unless both weapons are
5556         //                                      puncture weapons
5557         if ( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) ) {
5558                 if ( aip->goals[aip->active_goal].ai_mode & (AI_GOAL_DISABLE_SHIP|AI_GOAL_DISARM_SHIP) ) {
5559                         ship_weapon     *swp;
5560                         swp = &shipp->weapons;
5561                         // only continue if both primaries are puncture weapons
5562                         if ( swp->num_primary_banks == 2 ) {
5563                                 if ( !(Weapon_info[swp->primary_bank_weapons[0]].wi_flags & WIF_PUNCTURE) ) 
5564                                         return;
5565                                 if ( !(Weapon_info[swp->primary_bank_weapons[1]].wi_flags & WIF_PUNCTURE) ) 
5566                                         return;
5567                         }
5568                 }
5569         }
5570
5571         //      Don't want all ships always linking weapons at start, so asynchronize.
5572         if (Missiontime < i2f(30))
5573                 return;
5574         else if (Missiontime < i2f(120)) {
5575                 int r = static_rand((Missiontime >> 17) ^ OBJ_INDEX(objp));
5576                 if ( (r&3) != 0)
5577                         return;
5578         }
5579
5580         if (shipp->weapon_energy > Link_energy_levels_always[Game_skill_level]) {
5581                 shipp->flags |= SF_PRIMARY_LINKED;
5582         } else if (shipp->weapon_energy > Link_energy_levels_maybe[Game_skill_level]) {
5583                 if (objp->hull_strength < Ship_info[shipp->ship_info_index].initial_hull_strength/3.0f)
5584                         shipp->flags |= SF_PRIMARY_LINKED;
5585         }
5586 }
5587
5588 //      --------------------------------------------------------------------------
5589 //      Fire the current primary weapon.
5590 //      *objp is the object to fire from.
5591 void ai_fire_primary_weapon(object *objp)
5592 {
5593         ship                    *shipp = &Ships[objp->instance];
5594         ship_weapon     *swp = &shipp->weapons;
5595         ai_info         *aip;
5596         object          *enemy_objp;
5597
5598         SDL_assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5599
5600         aip = &Ai_info[shipp->ai_index];
5601
5602         //      If low on slots, fire a little less often.
5603         if (Num_weapons > (int) (0.9f * MAX_WEAPONS)) {
5604                 if (frand() > 0.5f) {
5605                         nprintf(("AI", "Frame %i, %s not fire.\n", Framecount, shipp->ship_name));
5606                         return;
5607                 }
5608         }
5609
5610         if (!Ai_firing_enabled){
5611                 return;
5612         }
5613
5614         if (aip->target_objnum != -1){
5615                 enemy_objp = &Objects[aip->target_objnum];
5616         } else {
5617                 enemy_objp = NULL;
5618         }
5619
5620         if ( (swp->current_primary_bank < 0) || (swp->current_primary_bank >= swp->num_primary_banks) || timestamp_elapsed(aip->primary_select_timestamp)) {
5621                 int     flags = 0;
5622                 // AL 2-11-98: If attacking any subsystem (not just engines), use disrupter weapon
5623 //              if ((aip->targeted_subsys != NULL) && (aip->targeted_subsys->system_info->type == SUBSYSTEM_ENGINE)) {
5624                 if ( aip->targeted_subsys != NULL ) {
5625                         flags = WIF_PUNCTURE;
5626                 }
5627                 ai_select_primary_weapon(objp, enemy_objp, flags);
5628                 ship_primary_changed(shipp);    // AL: maybe send multiplayer information when AI ship changes primaries
5629                 aip->primary_select_timestamp = timestamp(5 * 1000);    //      Maybe change primary weapon five seconds from now.
5630         }
5631
5632         //      If pointing nearly at predicted collision point of target, bash orientation to be perfectly pointing.
5633         float   dot;
5634         vector  v2t;
5635
5636 //      if (!IS_VEC_NULL(&G_predicted_pos)) {
5637         if (!( vm_vec_mag_quick(&G_predicted_pos) < AICODE_SMALL_MAGNITUDE )) {
5638                 if ( !vm_vec_cmp(&G_predicted_pos, &G_fire_pos) ) {
5639                         nprintf(("Warning", "Avoid NULL vector assert.. why are G_predicted_pos and G_fire_pos the same?\n"));
5640                 } else {
5641                         vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5642                         dot = vm_vec_dot(&v2t, &objp->orient.v.fvec);
5643                         if (dot > .998629534f){ //      if within 3.0 degrees of desired heading, bash
5644                                 vm_vector_2_matrix(&objp->orient, &v2t, &objp->orient.v.uvec, NULL);
5645                         }
5646                 }
5647         }
5648
5649         //      Make sure not firing at a protected ship unless firing at a live subsystem.
5650         //      Note: This happens every time the ship tries to fire, perhaps every frame.
5651         //      Should be wrapped in a timestamp, same one that enables it to fire, but that is complicated
5652         //      by multiple banks it can fire from.
5653         if (aip->target_objnum != -1) {
5654                 object  *tobjp = &Objects[aip->target_objnum];
5655                 if (tobjp->flags & OF_PROTECTED) {
5656                         if (aip->targeted_subsys != NULL) {
5657                                 int     type;
5658
5659                                 type = aip->targeted_subsys->system_info->type;
5660                                 if (ship_get_subsystem_strength(&Ships[tobjp->instance], type) == 0.0f) {
5661                                         aip->target_objnum = -1;
5662                                         return;
5663                                 }
5664                         } else {
5665                                 aip->target_objnum = -1;
5666                                 return;
5667                         }
5668                 }
5669         }
5670
5671         //      If enemy is protected, not firing a puncture weapon and enemy's hull is low, don't fire.
5672         if ((enemy_objp != NULL) && (enemy_objp->flags & OF_PROTECTED)) {
5673                 // AL: 3-6-98: Check if current_primary_bank is valid
5674                 if ((enemy_objp->hull_strength < 750.0f) && 
5675                         ((aip->targeted_subsys == NULL) || (enemy_objp->hull_strength < aip->targeted_subsys->current_hits + 50.0f)) &&
5676                         (swp->current_primary_bank >= 0) ) {
5677                         if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)) {
5678                                 //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));
5679                                 swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(1000);
5680                                 return;
5681                         }
5682
5683                         /*
5684                         int     num_attacking;
5685                         num_attacking = num_enemies_attacking(enemy_objp-Objects);
5686                         if (enemy_objp->hull_strength / num_attacking < 200.0f) {
5687                                 if (frand() < 0.75f) {
5688                                         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));
5689                                         swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(500);
5690                                         return;
5691                                 }
5692                         }
5693                         */
5694                 }
5695         }
5696
5697         set_primary_weapon_linkage(objp);
5698         
5699         // I think this will properly solve the problem
5700         // fire non-streaming weapons
5701         ship_fire_primary(objp, 0);
5702         
5703         // fire streaming weapons
5704         shipp->flags |= SF_TRIGGER_DOWN;
5705         ship_fire_primary(objp, 1);
5706         shipp->flags &= ~SF_TRIGGER_DOWN;
5707 }
5708
5709 //      --------------------------------------------------------------------------
5710 //      Return number of nearby enemy fighters.
5711 //      threshold is the distance within which a ship is considered near.
5712 //
5713 // input:       enemy_team_mask =>      teams that are considered as an enemy
5714 //                              pos                                     =>      world position to measure ship distances from
5715 //                              threshold                       =>      max distance from pos to be considered "near"
5716 //
5717 // exit:                number of ships within threshold units of pos
5718 int num_nearby_fighters(int enemy_team_mask, vector *pos, float threshold)
5719 {
5720         ship_obj        *so;
5721         object  *ship_objp;
5722         int             count = 0;
5723
5724         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5725
5726                 ship_objp = &Objects[so->objnum];
5727
5728                 if (Ships[ship_objp->instance].team & enemy_team_mask) {
5729                         if (Ship_info[Ships[ship_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) {
5730                                 if (vm_vec_dist_quick(pos, &ship_objp->pos) < threshold)
5731                                         count++;
5732                         }
5733                 }
5734         }
5735
5736         return count;
5737 }
5738
5739 //      --------------------------------------------------------------------------
5740 //      Select secondary weapon to fire.
5741 //      Currently, 1/16/98:
5742 //              If 0 secondary weapons available, return -1
5743 //              If 1 available, use it.
5744 //              If 2 or more, if the current weapon is one of them, stick with it, otherwise choose a random one.
5745 //      priority1 and priority2 are Weapon_info[] bitmasks such as WIF_HOMING_ASPECT.  If any weapon has any bit in priority1
5746 //      set, that weapon will be selected.  If not, apply to priority2.  If neither, return -1, meaning no weapon selected.
5747 //      Note, priorityX have default values of -1, meaning if not set, they will match any weapon.
5748 //      Return value:
5749 //              bank index
5750 //      Should do this:
5751 //              Favor aspect seekers when attacking small ships faraway.
5752 //              Favor rapid fire dumbfire when attacking a large ship.
5753 //              Ignore heat seekers because we're not sure how they'll work.
5754 void ai_select_secondary_weapon(object *objp, ship_weapon *swp, int priority1 = -1, int priority2 = -1)
5755 {
5756         int     num_weapon_types;
5757         int     weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
5758         int     i;
5759         int     ignore_mask;
5760         int     initial_bank;
5761
5762         initial_bank = swp->current_secondary_bank;
5763
5764         //      Ignore bombs unless one of the priorities asks for them to be selected.
5765         if (WIF_HUGE & (priority1 | priority2))
5766                 ignore_mask = 0;
5767         else
5768                 ignore_mask = WIF_HUGE;
5769
5770         if (!(WIF_BOMBER_PLUS & (priority1 | priority2)))
5771                 ignore_mask |= WIF_BOMBER_PLUS;
5772
5773 #ifndef NDEBUG
5774         for (i=0; i<MAX_WEAPON_TYPES; i++) {
5775                 weapon_id_list[i] = -1;
5776                 weapon_bank_list[i] = -1;
5777         }
5778 #endif
5779
5780         //      Stuff weapon_bank_list with bank index of available weapons.
5781         num_weapon_types = get_available_secondary_weapons(objp, weapon_id_list, weapon_bank_list);
5782
5783         int     priority2_index = -1;
5784
5785         for (i=0; i<num_weapon_types; i++) {
5786                 int     wi_flags;
5787
5788                 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5789                 if (!(wi_flags & ignore_mask)) {                                        //      Maybe bombs are illegal.
5790                         if (wi_flags & priority1) {
5791                                 swp->current_secondary_bank = weapon_bank_list[i];                              //      Found first priority, return it.
5792                                 break;
5793                         } else if (wi_flags & priority2)
5794                                 priority2_index = weapon_bank_list[i];  //      Found second priority, but might still find first priority.
5795                 }
5796         }
5797
5798         //      If didn't find anything above, then pick any secondary weapon.
5799         if (i == num_weapon_types) {
5800                 swp->current_secondary_bank = priority2_index;  //      Assume we won't find anything.
5801                 if (priority2_index == -1) {
5802                         for (i=0; i<num_weapon_types; i++) {
5803                                 int     wi_flags;
5804
5805                                 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5806                                 if (!(wi_flags & ignore_mask)) {                                        //      Maybe bombs are illegal.
5807                                         if (swp->secondary_bank_ammo[i] > 0) {
5808                                                 swp->current_secondary_bank = i;
5809                                                 break;
5810                                         }
5811                                 }
5812                         }
5813                 }
5814         }
5815
5816         //      If switched banks, force reacquisition of aspect lock.
5817         if (swp->current_secondary_bank != initial_bank) {
5818                 ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
5819                 
5820                 aip->aspect_locked_time = 0.0f;
5821                 aip->current_target_is_locked = 0;
5822         }
5823
5824
5825         ship_secondary_changed(&Ships[objp->instance]); // AL: let multiplayer know if secondary bank has changed
5826         // nprintf(("AI", "Ship %s selected weapon %s\n", Ships[objp->instance].ship_name, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
5827 }
5828
5829 //      Return number of objects homing on object *target_objp
5830 int compute_num_homing_objects(object *target_objp)
5831 {
5832         object  *objp;
5833         int             count = 0;
5834
5835         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
5836                 if (objp->type == OBJ_WEAPON) {
5837                         if (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_HOMING) {
5838                                 if (Weapons[objp->instance].homing_object == target_objp) {
5839                                         count++;
5840                                 }
5841                         }
5842                 }
5843         }
5844
5845         return count;
5846 }
5847
5848 //      Object *firing_objp just fired weapon weapon_index (index in Weapon_info).
5849 //      If it's a shockwave weapon, tell your team about it!
5850 void ai_maybe_announce_shockwave_weapon(object *firing_objp, int weapon_index)
5851 {
5852         if ((firing_objp->type == OBJ_SHIP) && (Weapon_info[weapon_index].shockwave_speed > 0.0f)) {
5853                 ship_obj        *so;
5854                 int             firing_ship_team;
5855
5856                 firing_ship_team = Ships[firing_objp->instance].team;
5857
5858                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5859                         object  *A = &Objects[so->objnum];
5860                         SDL_assert(A->type == OBJ_SHIP);
5861
5862                         if (Ships[A->instance].team == firing_ship_team) {
5863                                 ai_info *aip = &Ai_info[Ships[A->instance].ai_index];
5864                                 // AL 1-5-98: only avoid shockwave if not docked or repairing
5865                                 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
5866                                         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_WEAPON;
5867                                 }
5868                         }
5869                 }
5870         }
5871 }
5872
5873 //      Return total payload of all incoming missiles.
5874 float compute_incoming_payload(object *target_objp)
5875 {
5876         missile_obj     *mo;
5877         float                   payload = 0.0f;
5878
5879         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
5880                 object  *objp;
5881
5882                 objp = &Objects[mo->objnum];
5883                 SDL_assert(objp->type == OBJ_WEAPON);
5884                 if (Weapons[objp->instance].homing_object == target_objp) {
5885                         payload += Weapon_info[Weapons[objp->instance].weapon_info_index].damage;
5886                 }
5887         }
5888
5889         return payload;
5890 }
5891
5892 //      --------------------------------------------------------------------------
5893 //      Return true if OK for *aip to fire its current weapon at its current target.
5894 //      Only reason this function returns false is:
5895 //              weapon is a homer
5896 //              targeted at player
5897 //                      OR:     player has too many homers targeted at him
5898 //                                      Missiontime in that dead zone in which can't fire at this player
5899 //      Note: If player is attacking a ship, that ship is allowed to fire at player.  Otherwise, we get in a situation in which
5900 //      player is attacking a large ship, but that large ship is not defending itself with missiles.
5901 int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip)
5902 {
5903         int     num_homers = 0;
5904         object  *tobjp = &Objects[target_objnum];
5905
5906         if (target_objnum > -1) {
5907                 // AL 3-4-98: Ensure objp target is a ship first 
5908                 if ( tobjp->type == OBJ_SHIP ) {
5909
5910                         // should not get this far. check if ship is protected from beam and weapon is type beam
5911                         if ( (wip->wi_flags & WIF_BEAM) && (tobjp->flags & OF_BEAM_PROTECTED) ) {
5912                                 Int3();
5913                                 return 0;
5914                         }
5915                         if (Ship_info[Ships[tobjp->instance].ship_info_index].flags & SIF_SMALL_SHIP) {
5916                                 num_homers = compute_num_homing_objects(&Objects[target_objnum]);
5917                         }
5918                 }
5919
5920                 //      If player, maybe fire based on Skill_level and number of incoming weapons.
5921                 //      If non-player, maybe fire based on payload of incoming weapons.
5922                 if (wip->wi_flags & WIF_HOMING) {
5923                         if ((target_objnum > -1) && (tobjp->flags & OF_PLAYER_SHIP)) {
5924                                 if (Ai_info[Ships[tobjp->instance].ai_index].target_objnum != objnum) {
5925                                         //      Don't allow AI ships to fire at player for fixed periods of time based on skill level.
5926                                         //      With 5 skill levels, at Very Easy, they fire in 1/7 of every 10 second interval.
5927                                         //      At Easy, 2/7...at Expert, 5/7
5928                                         int t = ((Missiontime /(65536*10)) ^ target_objnum ^ 0x01) % (NUM_SKILL_LEVELS+2);
5929                                         if (t > Game_skill_level) {
5930                                                 //nprintf(("AI", "Not OK to fire homer at time thing %i\n", t));
5931                                                 return 0;
5932                                         }
5933                                 }
5934                                 //nprintf(("AI", " IS OK to fire homer at time thing %i ***\n", t));
5935                                 int     swarmers = 0;
5936                                 if (wip->wi_flags & WIF_SWARM)
5937                                         swarmers = 2;   //      Note, always want to be able to fire swarmers if no currently incident homers.
5938                                 if (Max_allowed_player_homers[Game_skill_level] < num_homers + swarmers) {
5939                                         return 0;
5940                                 }
5941                         } else if (num_homers > 3) {
5942                                 float   incoming_payload;
5943
5944                                 incoming_payload = compute_incoming_payload(&Objects[target_objnum]);
5945
5946                                 if (incoming_payload > tobjp->hull_strength) {
5947                                         return 0;
5948                                 }
5949                         }
5950                 }
5951         }
5952
5953         return 1;
5954 }
5955
5956 //      --------------------------------------------------------------------------
5957 //      Fire a secondary weapon.
5958 //      Maybe choose to fire a different one.
5959 //      priority1 and priority2 are optional parameters with defaults = -1
5960 int ai_fire_secondary_weapon(object *objp, int priority1, int priority2)
5961 {
5962         ship_weapon *swp;
5963         ship    *shipp;
5964         int             current_bank;
5965         int             rval = 0;
5966
5967 #ifndef NDEBUG
5968         if (!Ai_firing_enabled)
5969                 return rval;
5970 #endif
5971
5972         SDL_assert( objp != NULL );
5973         SDL_assert(objp->type == OBJ_SHIP);
5974         shipp = &Ships[objp->instance];
5975         swp = &shipp->weapons;
5976
5977         //      Select secondary weapon.
5978         current_bank = swp->current_secondary_bank; //ai_select_secondary_weapon(objp, swp, priority1, priority2);
5979
5980         //nprintf(("AI", "Frame %i: Current bank = %i, ammo remaining = %i\n", Framecount, current_bank, swp->secondary_bank_ammo[current_bank]));
5981         if (current_bank == -1) {
5982                 return rval;
5983         }
5984
5985         SDL_assert(current_bank < shipp->weapons.num_secondary_banks);
5986
5987         weapon_info     *wip = &Weapon_info[shipp->weapons.secondary_bank_weapons[current_bank]];
5988
5989         if ((wip->wi_flags & WIF_HOMING_ASPECT) && (!Ai_info[shipp->ai_index].current_target_is_locked)) {
5990                 //nprintf(("AI", "Not firing secondary weapon because not aspect locked.\n"));
5991                 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
5992         } else if ((wip->wi_flags & WIF_BOMB) || (vm_vec_dist_quick(&objp->pos, &En_objp->pos) > 50.0f)) {
5993                 //      This might look dumb, firing a bomb even if closer than 50 meters, but the reason is, if you're carrying
5994                 //      bombs, delivering them is probably more important than surviving.
5995                 ai_info *aip;
5996
5997                 aip = &Ai_info[shipp->ai_index];
5998                 
5999                 //      Note, maybe don't fire if firing at player and any homers yet fired.
6000                 //      Decreasing chance to fire the more homers are incoming on player.
6001                 if (check_ok_to_fire(OBJ_INDEX(objp), aip->target_objnum, wip)) {
6002                         if (ship_fire_secondary(objp)) {
6003                                 rval = 1;
6004                                 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
6005                                 //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));
6006                         }
6007
6008                 } else {
6009                         swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
6010                 }
6011         }
6012
6013         return rval;
6014 }
6015
6016 //      Return true if it looks like obj1, if continuing to move along current vector, will
6017 //      collide with obj2.
6018 int might_collide_with_ship(object *obj1, object *obj2, float dot_to_enemy, float dist_to_enemy, float duration)
6019 {
6020         if (obj1->phys_info.speed * duration + 2*(obj1->radius + obj2->radius) > dist_to_enemy)
6021                 if (dot_to_enemy > 0.8f - 2*(obj1->radius + obj2->radius)/dist_to_enemy)
6022                         return objects_will_collide(obj1, obj2, duration, 2.0f);
6023
6024 //              BABY - 
6025 //              CONDITION 1, dist_to_enemy < o1_rad + o2_rad + (obj1.speed + obj2.speed) * time + 50
6026         
6027         return 0;
6028
6029 }
6030
6031 //      --------------------------------------------------------------------------
6032 //      Return true if ship *objp firing a laser believes it will hit a teammate.
6033 int might_hit_teammate(object *firing_objp)
6034 {
6035         int             team;
6036         object  *objp;
6037         ship_obj        *so;
6038
6039         team = Ships[firing_objp->instance].team;
6040
6041         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6042                 objp = &Objects[so->objnum];
6043                 if (Ships[objp->instance].team == team) {
6044                         float           dist, dot;
6045                         vector  vec_to_objp;
6046
6047                         vm_vec_sub(&vec_to_objp, &firing_objp->pos, &objp->pos);
6048                         dist = vm_vec_mag_quick(&vec_to_objp);
6049                         dot = vm_vec_dot(&firing_objp->orient.v.fvec, &vec_to_objp)/dist;
6050                         if (might_collide_with_ship(firing_objp, objp, dot, dist, 2.0f))
6051                                 return 1;
6052                 }
6053         }
6054
6055         return 0;
6056
6057 }
6058
6059 //int   Team_not_fire_count=0, Team_hit_count = 0;
6060
6061 void render_all_ship_bay_paths(object *objp)
6062 {
6063         int             i,j,color;
6064         ship            *sp = &Ships[objp->instance];
6065         polymodel       *pm;
6066         model_path      *mp;
6067
6068         pm = model_get(sp->modelnum);
6069         vector  global_path_point;
6070         vertex  v, prev_vertex;
6071
6072         if ( pm->ship_bay == NULL )
6073                 return;
6074
6075         for ( i = 0; i < pm->ship_bay->num_paths; i++ ) {
6076                 mp = &pm->paths[pm->ship_bay->paths[i]];
6077
6078                 for ( j = 0; j < mp->nverts; j++ ) {
6079                         vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6080                         vm_vec_add2(&global_path_point, &objp->pos);
6081                         g3_rotate_vertex(&v, &global_path_point);
6082                         color = 255 - j*50;
6083                         if ( color < 50 ) 
6084                                 color = 100;
6085                         gr_set_color(0, color, 0);
6086
6087                         if ( j == mp->nverts-1 ) {
6088                                 gr_set_color(255, 0, 0);
6089                         }
6090
6091                         g3_draw_sphere( &v, 1.5f);
6092
6093                         if ( j > 0 )
6094                                 g3_draw_line(&v, &prev_vertex);
6095
6096                         prev_vertex = v;
6097         
6098                 }
6099         }
6100 }
6101
6102 // debug function to show all path points associated with an object
6103 void render_all_subsys_paths(object *objp)
6104 {
6105         int             i,j,color;
6106         ship            *sp = &Ships[objp->instance];
6107         polymodel       *pm;
6108         model_path      *mp;
6109
6110         pm = model_get(sp->modelnum);
6111         vector  global_path_point;
6112         vertex  v, prev_vertex;
6113
6114         if ( pm->ship_bay == NULL )
6115                 return;
6116
6117         for ( i = 0; i < pm->n_paths; i++ ) {
6118                 mp = &pm->paths[i];
6119                 for ( j = 0; j < mp->nverts; j++ ) {
6120                         vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6121                         vm_vec_add2(&global_path_point, &objp->pos);
6122                         g3_rotate_vertex(&v, &global_path_point);
6123                         color = 255 - j*50;
6124                         if ( color < 50 ) 
6125                                 color = 100;
6126                         gr_set_color(0, color, 0);
6127
6128                         if ( j == mp->nverts-1 ) {
6129                                 gr_set_color(255, 0, 0);
6130                         }
6131
6132                         g3_draw_sphere( &v, 1.5f);
6133
6134                         if ( j > 0 )
6135                                 g3_draw_line(&v, &prev_vertex);
6136
6137                         prev_vertex = v;
6138                 }
6139         }
6140 }
6141
6142 void render_path_points(object *objp)
6143 {
6144         ship            *shipp = &Ships[objp->instance];
6145         ai_info *aip = &Ai_info[shipp->ai_index];
6146         object  *dobjp;
6147         polymodel       *pm;
6148
6149         render_all_subsys_paths(objp);
6150         render_all_ship_bay_paths(objp);
6151
6152         if (aip->goal_objnum < 0)
6153                 return;
6154
6155         dobjp = &Objects[aip->goal_objnum];
6156         pm = model_get(Ships[dobjp->instance].modelnum);
6157         vector  dock_point, global_dock_point;
6158         vertex  v;
6159
6160         ship_model_start(&Objects[aip->goal_objnum]);
6161         if (pm->n_docks) {
6162                 dock_point = pm->docking_bays[0].pnt[0];
6163                 model_find_world_point(&global_dock_point, &dock_point, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6164                 g3_rotate_vertex(&v, &global_dock_point);
6165                 gr_set_color(255, 255, 255);
6166                 g3_draw_sphere( &v, 1.5f);
6167         }
6168
6169         if (aip->path_start != -1) {
6170                 vertex          prev_vertex;
6171                 pnode                   *pp = &Path_points[aip->path_start];
6172                 int                     num_points = aip->path_length;
6173                 int                     i;
6174
6175                 for (i=0; i<num_points; i++) {
6176                         vertex  v0;
6177
6178                         g3_rotate_vertex( &v0, &pp->pos );
6179
6180                         gr_set_color(0, 128, 96);
6181                         if (i != 0)
6182                                 g3_draw_line(&v0, &prev_vertex);
6183
6184                         if (pp-Path_points == aip->path_cur)
6185                                 gr_set_color(255,255,0);
6186                         
6187                         g3_draw_sphere( &v0, 4.5f);
6188
6189                         //      Connect all the turrets that can fire upon this point to this point.
6190 /*                      if (0) { //pp->path_index != -1) {
6191                                 model_path      *pmp;
6192                                 mp_vert         *pmpv;
6193
6194                                 get_base_path_info(pp->path_index, aip->goal_objnum, &pmp, &pmpv);
6195
6196                                 if (pmpv->nturrets) {
6197                                         for (int j = 0; j<pmpv->nturrets; j++) {
6198                                                 vertex  v1;
6199                                                 vector  turret_pos;
6200                                                 ship_subsys     *ssp;
6201
6202                                                 ssp = ship_get_indexed_subsys(&Ships[Objects[aip->goal_objnum].instance], pmpv->turret_ids[j]);
6203
6204 model_find_world_point(&turret_pos, &ssp->system_info->pnt, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6205         
6206                                                 g3_rotate_vertex(&v1, &turret_pos);
6207                                                 gr_set_color(255, 255, 0);
6208                                                 g3_draw_line(&v0, &v1);
6209                                                 g3_draw_sphere( &v1, 1.5f);
6210                                         }
6211                                 }
6212                         } */
6213
6214                         prev_vertex = v0;
6215
6216                         pp++;
6217                 }
6218         }
6219
6220         ship_model_stop(&Objects[aip->goal_objnum]);
6221 }
6222
6223 // Return the distance that the current AI weapon will travel
6224 float ai_get_weapon_dist(ship_weapon *swp)
6225 {
6226         int     bank_num, weapon_num;
6227
6228         bank_num = swp->current_primary_bank;
6229         weapon_num = swp->primary_bank_weapons[bank_num];
6230
6231         //      If weapon_num is illegal, return a reasonable value.  A valid weapon
6232         //      will get selected when this ship tries to fire.
6233         if (weapon_num == -1) {
6234                 // Int3();
6235                 return 1000.0f;
6236         }
6237
6238         return Weapon_info[weapon_num].max_speed * Weapon_info[weapon_num].lifetime;
6239 }
6240
6241 float ai_get_weapon_speed(ship_weapon *swp)
6242 {
6243         int     bank_num, weapon_num;
6244
6245         bank_num = swp->current_primary_bank;
6246         if (bank_num < 0)
6247                 return 100.0f;
6248
6249         weapon_num = swp->primary_bank_weapons[bank_num];
6250
6251         if (weapon_num == -1) {
6252                 //Int3();
6253                 return 100.0f;
6254         }
6255
6256         return Weapon_info[weapon_num].max_speed;
6257 }
6258
6259 //      Compute the predicted position of a ship to be fired upon from a turret.
6260 //      This is based on position of firing gun, enemy object, weapon speed and skill level constraints.
6261 //      Return value in *predicted_enemy_pos.
6262 //      Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6263 //      *pobjp          object firing the weapon
6264 //      *eobjp          object being fired upon
6265 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)
6266 {
6267         ship    *shipp = &Ships[pobjp->instance];
6268         float   range_time;
6269
6270         //weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6271
6272         if (weapon_speed < 1.0f)
6273                 weapon_speed = 1.0f;
6274
6275         range_time = 2.0f;
6276
6277         //      Make it take longer for enemies to get player's allies in range based on skill level.
6278         if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team)
6279                 range_time += In_range_time[Game_skill_level];
6280
6281         //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6282
6283         if (time_enemy_in_range < range_time) {
6284                 float   dist;
6285
6286                 dist = vm_vec_dist_quick(&pobjp->pos, enemy_pos);
6287                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, time_enemy_in_range * dist/weapon_speed);
6288         } else {
6289                 float   collision_time, scale;
6290                 vector  rand_vec;
6291                 ai_info *aip = &Ai_info[shipp->ai_index];
6292
6293                 collision_time = compute_collision_time(enemy_pos, enemy_vel, gun_pos, weapon_speed);
6294
6295                 if (collision_time == 0.0f){
6296                         collision_time = 100.0f;
6297                 }
6298
6299                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, collision_time);
6300                 if (time_enemy_in_range > 2*range_time){
6301                         scale = (1.0f - aip->ai_accuracy) * 4.0f;
6302                 } else {
6303                         scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - time_enemy_in_range/(2*range_time)));
6304                 }               
6305
6306                 static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6307
6308                 vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6309                 G_collision_time = collision_time;
6310                 G_fire_pos = *gun_pos;
6311         }
6312
6313         G_predicted_pos = *predicted_enemy_pos;
6314 }
6315
6316 //      Compute the predicted position of a ship to be fired upon.
6317 //      This is based on current position of firing object, enemy object, relative position of gun on firing object,
6318 //      weapon speed and skill level constraints.
6319 //      Return value in *predicted_enemy_pos.
6320 //      Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6321 void set_predicted_enemy_pos(vector *predicted_enemy_pos, object *pobjp, object *eobjp, ai_info *aip)
6322 {
6323         float   weapon_speed, range_time;
6324         ship    *shipp = &Ships[pobjp->instance];
6325
6326         weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6327         weapon_speed = max(weapon_speed, 1.0f);         // set not less than 1
6328
6329         range_time = 2.0f;
6330
6331         //      Make it take longer for enemies to get player's allies in range based on skill level.
6332         // but don't bias team v. team missions
6333         if ( !((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) ) {
6334                 if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team) {
6335                         range_time += In_range_time[Game_skill_level];
6336                 }
6337         }
6338         //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6339
6340         if (aip->time_enemy_in_range < range_time) {
6341                 float   dist;
6342
6343                 dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6344                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, aip->time_enemy_in_range * dist/weapon_speed);
6345         } else {
6346                 float   collision_time;
6347                 vector  gun_pos, pnt;
6348                 polymodel *po = model_get( Ship_info[shipp->ship_info_index].modelnum );
6349
6350                 //      Compute position of gun in absolute space and use that as fire position.
6351                 if(po->gun_banks != NULL){
6352                         pnt = po->gun_banks[0].pnt[0];
6353                 } else {
6354                         pnt = Objects[shipp->objnum].pos;
6355                 }
6356                 vm_vec_unrotate(&gun_pos, &pnt, &pobjp->orient);
6357                 vm_vec_add2(&gun_pos, &pobjp->pos);
6358
6359                 collision_time = compute_collision_time(&eobjp->pos, &eobjp->phys_info.vel, &gun_pos, weapon_speed);
6360
6361                 if (collision_time == 0.0f) {
6362                         collision_time = 100.0f;
6363                 }
6364
6365                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, collision_time);
6366
6367                 // set globals
6368                 G_collision_time = collision_time;
6369                 G_fire_pos = gun_pos;
6370         }
6371
6372         // Now add error terms (1) regular aim (2) EMP (3) stealth
6373         float scale = 0.0f;
6374         vector rand_vec;
6375
6376         // regular skill level error in aim
6377         if (aip->time_enemy_in_range > 2*range_time) {
6378                 scale = (1.0f - aip->ai_accuracy) * 4.0f;
6379         } else {
6380                 scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - aip->time_enemy_in_range/(2*range_time)));
6381         }
6382
6383         // if this ship is under the effect of an EMP blast, throw his aim off a bit
6384         if (shipp->emp_intensity > 0.0f) {
6385                 // never go lower than 1/2 of the EMP effect max, otherwise things aren't noticeable
6386                 scale += (MAX_EMP_INACCURACY * (shipp->emp_intensity < 0.5f ? 0.5f : shipp->emp_intensity));
6387                 mprintf(("AI miss scale factor (EMP) %f\n",scale));
6388         }
6389
6390         // if stealthy ship, throw his aim off, more when farther away and when dot is small
6391         if ( aip->ai_flags & AIF_STEALTH_PURSIUT ) {
6392                 float dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6393                 vector temp;
6394                 vm_vec_sub(&temp, &eobjp->pos, &pobjp->pos);
6395                 vm_vec_normalize_quick(&temp);
6396                 float dot = vm_vec_dotprod(&temp, &pobjp->orient.v.fvec);
6397                 float st_err = 3.0f * (1.4f - dot) * (1.0f + dist / (get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST)) * (1 - aip->ai_accuracy);
6398                 scale += st_err;
6399                 // mprintf(("error term: %.1f, total %.1f, dot %.3f\n", st_err, scale, dot));
6400         }
6401
6402         // get a random vector that changes slowly over time (1x / sec)
6403         static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6404
6405         vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6406
6407         // set global
6408         G_predicted_pos = *predicted_enemy_pos;
6409 }
6410
6411 //      Handler of submode for Chase.  Go into a continuous turn for awhile.
6412 void ai_chase_ct()
6413 {
6414         vector          tvec;
6415         ship_info       *sip;
6416         ai_info         *aip;
6417
6418         SDL_assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6419         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6420         SDL_assert(Ships[Pl_objp->instance].ai_index >= 0);
6421         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6422
6423         //      Make a continuous turn towards any combination of possibly negated
6424         // up and right vectors.
6425         tvec = Pl_objp->pos;
6426
6427         if (aip->submode_parm0 & 0x01)
6428                 vm_vec_add2(&tvec, &Pl_objp->orient.v.rvec);
6429         if (aip->submode_parm0 & 0x02)
6430                 vm_vec_sub2(&tvec, &Pl_objp->orient.v.rvec);
6431         if (aip->submode_parm0 & 0x04)
6432                 vm_vec_add2(&tvec, &Pl_objp->orient.v.uvec);
6433         if (aip->submode_parm0 & 0x08)
6434                 vm_vec_sub2(&tvec, &Pl_objp->orient.v.uvec);
6435
6436         //      Detect degenerate cases that cause tvec to be same as player pos.
6437         if (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
6438                 aip->submode_parm0 &= 0x05;
6439                 if (aip->submode_parm0 == 0)
6440                         aip->submode_parm0 = 1;
6441                 vm_vec_add2(&tvec, &Pl_objp->orient.v.rvec);
6442         }
6443
6444         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6445         accelerate_ship(aip, 1.0f);
6446 }
6447
6448 //      ATTACK submode handler for chase mode.
6449 void ai_chase_eb(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
6450 {
6451         vector  _pep;
6452         float           dot_to_enemy, dot_from_enemy;
6453
6454         compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
6455
6456         //      If we're trying to slow down to get behind, then point to turn towards is different.
6457         _pep = *predicted_enemy_pos;
6458         if ((dot_to_enemy > dot_from_enemy + 0.1f) || (dot_to_enemy > 0.9f))
6459                 vm_vec_scale_add(&_pep, &Pl_objp->pos, &En_objp->orient.v.fvec, 100.0f);
6460
6461         ai_turn_towards_vector(&_pep, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6462
6463         accelerate_ship(aip, 0.0f);
6464 }
6465
6466 //      Return time until weapon_objp might hit ship_objp.
6467 //      Assumes ship_objp is not moving.
6468 //      Returns negative time if not going to hit.
6469 //      This is a very approximate function, but is pretty fast.
6470 float ai_endangered_time(object *ship_objp, object *weapon_objp)
6471 {
6472         float           to_dot, from_dot, dist;
6473
6474         dist = compute_dots(ship_objp, weapon_objp, &to_dot, &from_dot);
6475
6476         //      Note, this is bogus.  It assumes only the weapon is moving.
6477         //      Only proceed if weapon sort of pointing at object and object pointing towards or away from weapon
6478         //      (Ie, if object moving at right angle to weapon, just continue for now...)
6479         if (weapon_objp->phys_info.speed < 1.0f)
6480                 return dist + 1.0f;
6481         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))
6482                 return dist / weapon_objp->phys_info.speed;
6483         else
6484                 return -1.0f;
6485 }
6486
6487 //      Return time until danger weapon could hit this ai object.
6488 //      Return negative time if not endangered.
6489 float ai_endangered_by_weapon(ai_info *aip)
6490 {
6491         object  *weapon_objp;
6492
6493         if (aip->danger_weapon_objnum == -1) {
6494                 return -1.0f;
6495         }
6496
6497         weapon_objp = &Objects[aip->danger_weapon_objnum];
6498
6499         if (weapon_objp->signature != aip->danger_weapon_signature) {
6500                 aip->danger_weapon_objnum = -1;
6501                 return -1.0f;
6502         }
6503
6504         return ai_endangered_time(&Objects[Ships[aip->shipnum].objnum], weapon_objp);
6505 }
6506
6507 //      Return true if this ship is near full strength.
6508 int ai_near_full_strength(object *objp, ship_info *sip)
6509 {
6510         return (objp->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp)/sip->shields > 0.8f);
6511 }
6512                                 
6513 //      Set acceleration while in attack mode.
6514 void attack_set_accel(ai_info *aip, float dist_to_enemy, float dot_to_enemy, float dot_from_enemy)
6515 {
6516         float   speed_ratio;
6517
6518         if (En_objp->phys_info.speed > 1.0f)
6519                 speed_ratio = Pl_objp->phys_info.speed/En_objp->phys_info.speed;
6520         else
6521                 speed_ratio = 5.0f;
6522
6523         //      Sometimes, told to attack slowly.  Allows to get in more hits.
6524         if (aip->ai_flags & AIF_ATTACK_SLOWLY) {
6525                 if ((dist_to_enemy > 200.0f) && (dist_to_enemy < 800.0f)) {
6526                         if ((dot_from_enemy < 0.9f) || ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
6527                                 //nprintf(("AI", " slowly "));
6528                                 accelerate_ship(aip, max(1.0f - (dist_to_enemy-200.0f)/600.0f, 0.1f));
6529                                 return;
6530                         }
6531                 } else
6532                         aip->ai_flags &= ~AIF_ATTACK_SLOWLY;
6533         }
6534
6535         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) {
6536                 //nprintf(("AI", "1"));
6537                 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6538                         if (dist_to_enemy > 800.0f) {
6539                                 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6540                                         float percent_left;
6541                                         ship    *shipp;
6542                                         ship_info *sip;
6543
6544                                         shipp = &Ships[Pl_objp->instance];
6545                                         sip = &Ship_info[shipp->ship_info_index];
6546
6547                                         if (sip->afterburner_fuel_capacity > 0.0f) {
6548                                                 percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
6549                                                 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
6550                                                         afterburners_start(Pl_objp);
6551                                                         aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
6552                                                 }
6553                                         }
6554                                 }
6555                         }
6556                 }
6557
6558                 accelerate_ship(aip, 1.0f);
6559         } else if ((Missiontime - aip->last_hit_time > F1_0*7)
6560                 && (En_objp->phys_info.speed < 10.0f) 
6561                 && (dist_to_enemy > 25.0f) 
6562                 && (dot_to_enemy > 0.8f)
6563                 && (dot_from_enemy < 0.8f)) {
6564                 accelerate_ship(aip, 0.0f);             //      No one attacking us, so don't need to move.
6565         } else if ((dot_from_enemy < 0.25f) && (dot_to_enemy > 0.5f)) {
6566                 set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed);
6567         } else if (Pl_objp->phys_info.speed < 15.0f) {
6568                 accelerate_ship(aip, 1.0f);
6569         } else if (Pl_objp->phys_info.speed > En_objp->phys_info.speed - 1.0f) {
6570                 if (dot_from_enemy > 0.75f)
6571                         accelerate_ship(aip, 1.0f);
6572                 else
6573                         set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed*0.75f + 3.0f);
6574         } else {
6575                 change_acceleration(aip, 0.5f);
6576         }
6577 }
6578
6579 //      Pl_objp (aip) tries to get behind En_objp.
6580 //      New on 2/21/98: If this ship can move backwards and slide, maybe do that to get behind.
6581 void get_behind_ship(ai_info *aip, ship_info *sip, float dist_to_enemy)
6582 {
6583         vector  new_pos;
6584         float           dot;
6585         vector  vec_from_enemy;
6586
6587         vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
6588
6589         vm_vec_scale_add(&new_pos, &En_objp->pos, &En_objp->orient.v.fvec, -100.0f);            //      Pick point 100 units behind.
6590         ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6591
6592         dot = vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.fvec);
6593
6594         if (dot > 0.25f) {
6595                 accelerate_ship(aip, 1.0f);
6596         } else {
6597                 accelerate_ship(aip, (dot + 1.0f)/2.0f);
6598         }
6599 }
6600
6601 int avoid_player(object *objp, vector *goal_pos)
6602 {
6603         maybe_avoid_player(Pl_objp, goal_pos);
6604         ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
6605
6606         if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6607                 ship_info *sip = &Ship_info[Ships[objp->instance].ship_info_index];
6608
6609                 if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6610                         ai_turn_towards_vector(&aip->avoid_goal_point, objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6611                         accelerate_ship(aip, 0.5f);
6612                         return 1;
6613                 }
6614         }
6615
6616         return 0;
6617 }
6618
6619 //      Determine if a cylinder of width radius from p0 to p1 will collide with big_objp.
6620 //      If so, stuff *collision_point.
6621 int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6622 {
6623         mc_info mc;
6624
6625         mc.model_num = Ships[big_objp->instance].modelnum;              // Fill in the model to check
6626         mc.orient = &big_objp->orient;                  // The object's orient
6627         mc.pos = &big_objp->pos;                                        // The object's position
6628         mc.p0 = p0;                                                                             // Point 1 of ray to check
6629         mc.p1 = p1;
6630         mc.flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE | MC_SUBMODEL;                                  // flags
6631
6632         mc.radius = radius;
6633
6634         // Only check the 2nd lowest hull object
6635         polymodel *pm = model_get(Ships[big_objp->instance].modelnum);
6636         mc.submodel_num = pm->detail[0]; //pm->submodel->num_details-2];
6637         model_collide(&mc);
6638
6639         if (mc.num_hits)
6640                 *collision_point = mc.hit_point_world;
6641
6642         return mc.num_hits;
6643 }
6644
6645 //      Return true/false if *objp will collide with *big_objp
6646 //      Stuff distance in *distance to collision point if *objp will collide with *big_objp within delta_time seconds.
6647 //      Global collision point stuffed in *collision_point
6648 int will_collide_with_big_ship(object *objp, vector *goal_point, object *big_objp, vector *collision_point, float delta_time)
6649 {
6650         float           radius;
6651         vector  end_pos;
6652
6653         radius = big_objp->radius + delta_time * objp->phys_info.speed;
6654
6655         if (vm_vec_dist_quick(&big_objp->pos, &objp->pos) > radius) {
6656                 return 0;
6657         }
6658
6659         if (goal_point == NULL) {
6660                 vm_vec_scale_add(&end_pos, &objp->pos, &objp->phys_info.vel, delta_time);                                       // Point 2 of ray to check
6661         } else {
6662                 end_pos = *goal_point;
6663         }
6664
6665         return will_collide_pp(&objp->pos, &end_pos, objp->radius, big_objp, collision_point);
6666 }
6667
6668 //      Return true if *objp is expected to collide with a large ship.
6669 //      Stuff global collision point in *collision_point.
6670 //      If *goal_point is not NULL, use that as the point towards which *objp will be flying.  Don't use *objp velocity
6671 //      *ignore_objp will typically be the target this ship is pursuing, either to attack or guard.  We don't want to avoid it.
6672 int will_collide_with_big_ship_all(object *objp, object *ignore_objp, vector *goal_point, vector *collision_point, float *distance, float delta_time)
6673 {
6674         ship_obj        *so;
6675         object  *big_objp;
6676         int             collision_obj_index = -1;
6677         float           min_dist = 999999.9f;
6678
6679         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6680                 big_objp = &Objects[so->objnum];
6681
6682                 if (big_objp == ignore_objp)
6683                         continue;
6684
6685                 if (Ship_info[Ships[big_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
6686                         vector  cur_collision_point;
6687                         float           cur_dist;
6688
6689                         if (will_collide_with_big_ship(objp, goal_point, big_objp, &cur_collision_point, delta_time)) {
6690
6691                                 cur_dist = vm_vec_dist(&cur_collision_point, &objp->pos);
6692
6693                                 if (cur_dist < min_dist) {
6694                                         min_dist = cur_dist;
6695                                         *collision_point = cur_collision_point;
6696                                         collision_obj_index = OBJ_INDEX(big_objp);
6697                                 }
6698                         }
6699                 }
6700         }
6701
6702         *distance = min_dist;
6703         return collision_obj_index;
6704
6705 }
6706
6707 typedef struct {
6708         float           dist;
6709         int             collide;
6710         vector  pos;
6711 } sgoal;
6712
6713 //int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6714 //      Pick a point for *objp to fly towards to avoid a collision with *big_objp at *collision_point
6715 //      Return result in *avoid_pos
6716 void mabs_pick_goal_point(object *objp, object *big_objp, vector *collision_point, vector *avoid_pos)
6717 {
6718         matrix  mat1;
6719         sgoal           goals[4];
6720         vector  v2b;
6721
6722         vm_vec_normalized_dir(&v2b, collision_point, &objp->pos);
6723         vm_vector_2_matrix(&mat1, &v2b, NULL, NULL);
6724
6725         int     found = 0;
6726
6727         //      Try various scales, in 0.5f, 0.75f, 1.0f, 1.25f.
6728         //      First try 0.5f to see if we can find a point that near the center of the target ship, which presumably
6729         //      means less of a turn.
6730         //      Try going as far as 1.25f * radius.
6731         float   s;
6732         for (s=0.5f; s<1.3f; s += 0.25f) {
6733                 int     i;
6734                 for (i=0; i<4; i++) {
6735                         vector p = big_objp->pos;
6736                         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
6737                         float kr = big_objp->radius*s + objp->radius * ((OBJ_INDEX(objp) % 4) ^ 2)/4;
6738                         if (i&1)
6739                                 ku = -ku;
6740                         if (i&2)
6741                                 kr = -kr;
6742                         vm_vec_scale_add2(&p, &mat1.v.uvec, ku);
6743                         vm_vec_scale_add2(&p, &mat1.v.rvec, kr);
6744                         goals[i].pos = p;
6745                         goals[i].dist = vm_vec_dist_quick(&objp->pos, &p);
6746                         goals[i].collide = will_collide_pp(&objp->pos, &p, objp->radius, big_objp, collision_point);
6747                         if (!goals[i].collide)
6748                                 found = 1;
6749                 }
6750
6751                 //      If we found a point that doesn't collide, find the nearest one and make that the *avoid_pos.
6752                 if (found) {
6753                         float   min_dist = 9999999.9f;
6754                         int     min_index = -1;
6755
6756                         for (i=0; i<4; i++) {
6757                                 if (!goals[i].collide && (goals[i].dist < min_dist)) {
6758                                         min_dist = goals[i].dist;
6759                                         min_index = i;
6760                                 }
6761                         }
6762
6763                         SDL_assert(i != -1);
6764                         if (i != -1) {
6765                                 *avoid_pos = goals[min_index].pos;
6766                                 return;
6767                         }
6768                 }
6769         }
6770
6771         //      Drat.  We tried and tried and could not find a point that did not cause a collision.
6772         //      Get this dump pilot far away from the problem ship.
6773         vector  away_vec;
6774         vm_vec_normalized_dir(&away_vec, &objp->pos, collision_point);
6775         vm_vec_scale_add(avoid_pos, &objp->pos, &away_vec, big_objp->radius*1.5f);
6776
6777 }
6778
6779 //      Return true if a large ship is being ignored.
6780 int maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vector *goal_point, float delta_time)
6781 {
6782         if (timestamp_elapsed(aip->avoid_check_timestamp)) {
6783                 float           distance;
6784                 vector  collision_point;
6785                 int             ship_num;
6786                 if ((ship_num = will_collide_with_big_ship_all(Pl_objp, ignore_objp, goal_point, &collision_point, &distance, delta_time)) != -1) {
6787                         aip->ai_flags |= AIF_AVOIDING_BIG_SHIP;
6788                         mabs_pick_goal_point(objp, &Objects[ship_num], &collision_point, &aip->avoid_goal_point);
6789                         float dist = vm_vec_dist_quick(&aip->avoid_goal_point, &objp->pos);
6790                         aip->avoid_check_timestamp = timestamp(2000 + min(1000, (int) (dist * 2.0f)));  //      Delay until check again is based on distance to avoid point.
6791                         aip->avoid_ship_num = ship_num;
6792                 } else {
6793                         aip->ai_flags &= ~AIF_AVOIDING_BIG_SHIP;
6794                         aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
6795                         aip->avoid_ship_num = -1;
6796                         aip->avoid_check_timestamp = timestamp(1500);
6797                 }
6798         }
6799         
6800         if (aip->ai_flags & AIF_AVOIDING_BIG_SHIP) {
6801                 ship_info *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6802
6803                 vector  v2g;
6804
6805                 ai_turn_towards_vector(&aip->avoid_goal_point, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6806                 vm_vec_normalized_dir(&v2g, &aip->avoid_goal_point, &Pl_objp->pos);
6807                 float dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
6808                 float d2 = (1.0f + dot) * (1.0f + dot);
6809                 accelerate_ship(aip, d2/4.0f);
6810                 return 1;
6811         }
6812
6813         return 0;
6814 }
6815
6816 //      Set desired right vector for ships flying towards another ship.
6817 //      Since this is governed only by vector to target, it causes ships to align bank and look less chaotic.
6818 void compute_desired_rvec(vector *rvec, vector *goal_pos, vector *cur_pos)
6819 {
6820         vector  v2e;
6821
6822         vm_vec_normalized_dir(&v2e, goal_pos, cur_pos);
6823         rvec->xyz.x = v2e.xyz.z;
6824         rvec->xyz.y = 0.0f;
6825         rvec->xyz.z = -v2e.xyz.x;
6826         if (vm_vec_mag_squared(rvec) < 0.001f)
6827                 rvec->xyz.y = 1.0f;
6828 }
6829
6830 // Handler for stealth find submode of Chase.
6831 void ai_stealth_find()
6832 {
6833         ai_info         *aip;
6834         ship_info       *sip;
6835
6836         vector new_pos, vec_to_enemy;
6837         float dist_to_enemy, dot_to_enemy, dot_from_enemy;
6838
6839         SDL_assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6840         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6841         SDL_assert(Ships[Pl_objp->instance].ai_index >= 0);
6842         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6843
6844         // get time since last seen
6845         int delta_time = (timestamp() - aip->stealth_last_visible_stamp);
6846
6847         // if delta_time is really big, i'm real confused, start sweep
6848         if (delta_time > 10000) {
6849                 aip->submode_parm0 = SM_SF_BAIL;
6850         }
6851
6852         // guestimate new position
6853         vm_vec_scale_add(&new_pos, &aip->stealth_last_pos, &aip->stealth_velocity, (delta_time * 0.001f));
6854
6855         // if I think he's behind me, go to the goal point
6856         if ( aip->submode_parm0 == SM_SF_BEHIND ) {
6857                 new_pos = aip->goal_point;
6858         }
6859
6860         // check for collision with big ships
6861         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &new_pos, 10.0f)) {
6862                 // reset ai submode to chase
6863                 return;
6864         }
6865
6866         // if dist is near max and dot is close to 1, accel, afterburn
6867         vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6868         dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6869         dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.v.fvec);
6870
6871         // 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
6872         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) ) {
6873                 // do turn around)
6874                 vm_vec_scale_add(&aip->goal_point, &Pl_objp->pos, &Pl_objp->orient.v.fvec, -300.0f);
6875                 aip->submode_parm0 = SM_SF_BEHIND;
6876                 vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6877                 dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6878                 dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.v.fvec);
6879         }
6880
6881         if ( (dist_to_enemy > get_skill_stealth_dist_scaler()*STEALTH_MAX_VIEW_DIST) && (dot_to_enemy > 0.94f) ) {              // 20 degree half angle
6882                 // accelerate ship
6883                 accelerate_ship(aip, 1.0f);
6884
6885                 // engage afterburner
6886                 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6887                         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6888                                 afterburners_start(Pl_objp);
6889                                 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
6890                         }
6891                 }
6892
6893                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6894                 return;
6895         }
6896
6897         //      If enemy more than 500 meters away, all ships flying there will tend to match bank.
6898         //      They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
6899         //      to interpolate a matrix rather than just a vector.
6900         if (dist_to_enemy > 500.0f) {
6901                 vector  rvec;
6902                 compute_desired_rvec(&rvec, &new_pos, &Pl_objp->pos);
6903                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0, &rvec);
6904         } else {
6905                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6906         }
6907
6908         dot_from_enemy = -vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec);
6909
6910         attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
6911 }
6912
6913 // -----------------------------------------------------------------------------
6914 // try to find stealth ship by sweeping an area
6915 void ai_stealth_sweep()
6916 {
6917         ai_info         *aip;
6918         ship_info       *sip;
6919
6920         SDL_assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6921         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6922         SDL_assert(Ships[Pl_objp->instance].ai_index >= 0);
6923         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6924
6925         vector goal_pt;
6926         vector forward, right, up;
6927         int lost_time;
6928
6929         // time since stealth last seen
6930         lost_time = (timestamp() - aip->stealth_last_visible_stamp);
6931
6932         // determine which pt to fly to in sweep by keeping track of parm0
6933         if (aip->submode_parm0 == SM_SS_SET_GOAL) {
6934
6935                 // don't make goal pt more than 2k from current pos
6936                 vm_vec_scale_add(&goal_pt, &aip->stealth_last_pos, &aip->stealth_velocity, (0.001f * lost_time));
6937
6938                 // make box size based on speed of stealth and expected time to intercept (keep box in range 200-500)
6939                 float box_size = vm_vec_mag_quick(&aip->stealth_velocity) * (0.001f * lost_time);
6940                 box_size = min(200.0f, box_size);
6941                 box_size = max(500.0f, box_size);
6942                 aip->stealth_sweep_box_size = box_size;
6943
6944                 aip->goal_point = goal_pt;
6945                 aip->submode_parm0 = SM_SS_BOX0;
6946         }
6947
6948         // GET UP, RIGHT, FORWARD FOR BOX based on stealth ship's velocity
6949         // if velocity changes in stealth mode, then ship is *seen*, and falls out of sweep mode
6950         // if stealth has no velocity make a velocity
6951         if ( vm_vec_mag_quick(&aip->stealth_velocity) < 1 ) {
6952                 vm_vec_rand_vec_quick(&aip->stealth_velocity);
6953         }
6954
6955         // get "right" vector for box
6956         vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_y_vector);
6957
6958         if ( vm_vec_mag_quick(&right) < 0.01 ) {
6959                 vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_z_vector);
6960         }
6961
6962         vm_vec_normalize_quick(&right);
6963
6964         // get forward for box
6965         vm_vec_copy_normalize_quick(&forward, &aip->stealth_velocity);
6966
6967         // get "up" for box
6968         vm_vec_crossprod(&up, &forward, &right);
6969         
6970         // lost far away ahead (do box)
6971         switch(aip->submode_parm0) {
6972         case SM_SS_BOX0:
6973                 goal_pt = aip->goal_point;
6974                 break;
6975
6976         // pt1 -U +R
6977         case SM_SS_LR:
6978                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
6979                 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
6980                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6981                 break;
6982
6983         // pt2 +U -R
6984         case SM_SS_UL:
6985                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
6986                 vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
6987                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6988                 break;
6989
6990         // pt3 back
6991         case SM_SS_BOX1:
6992                 goal_pt = aip->goal_point;
6993                 break;
6994
6995         // pt4 +U +R
6996         case SM_SS_UR:
6997                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
6998                 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
6999                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
7000                 break;
7001
7002         // pt5 -U -R
7003         case SM_SS_LL:
7004                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
7005                 vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
7006                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
7007                 break;
7008
7009         // pt6 back
7010         case SM_SS_BOX2:
7011                 goal_pt = aip->goal_point;
7012                 break;
7013
7014         default:
7015                 Int3();
7016
7017         }
7018
7019         // when close to goal_pt, update next goal pt
7020         float dist_to_goal = vm_vec_dist(&goal_pt, &Pl_objp->pos);
7021         if (dist_to_goal < 15) {
7022                 aip->submode_parm0++;
7023         }
7024
7025         // check for collision with big ship
7026         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &goal_pt, 10.0f)) {
7027                 // skip to the next pt on box
7028                 aip->submode_parm0++;
7029                 return;
7030         }
7031
7032         ai_turn_towards_vector(&goal_pt, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
7033
7034         float dot = 1.0f;
7035         if (dist_to_goal < 100) {
7036                 vector vec_to_goal;
7037                 vm_vec_normalized_dir(&vec_to_goal, &goal_pt, &Pl_objp->pos);
7038                 dot = vm_vec_dotprod(&vec_to_goal, &Pl_objp->orient.v.fvec);
7039         }
7040
7041         accelerate_ship(aip, 0.8f*dot);
7042 }
7043
7044 //      ATTACK submode handler for chase mode.
7045 void ai_chase_attack(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
7046 {
7047         int             start_bank;
7048         float           dot_to_enemy, dot_from_enemy; //, time_to_hit;
7049         float           bank_override = 0.0f;
7050
7051         if (avoid_player(Pl_objp, predicted_enemy_pos))
7052                 return;
7053
7054         compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
7055
7056         polymodel *po = model_get( sip->modelnum );
7057
7058         vector  *rel_pos;
7059         float           scale;
7060         vector  randvec;
7061         vector  new_pos;
7062
7063         start_bank = Ships[aip->shipnum].weapons.current_primary_bank;
7064         if (po->n_guns && start_bank != -1 ) {
7065                 rel_pos = &po->gun_banks[start_bank].pnt[0];
7066         } else
7067                 rel_pos = NULL;
7068
7069         //      If ship moving slowly relative to its size, then don't attack its center point.
7070         //      How far from center we attack is based on speed, size and distance to enemy
7071         if (En_objp->radius > En_objp->phys_info.speed) {
7072                 static_randvec(Pl_objp-Objects, &randvec);
7073                 scale = dist_to_enemy/(dist_to_enemy + En_objp->radius) * En_objp->radius;
7074                 scale *= 0.5f * En_objp->radius/(En_objp->phys_info.speed + En_objp->radius);   // scale downward by 1/2 to 1/4
7075                 vm_vec_scale_add(&new_pos, predicted_enemy_pos, &randvec, scale);
7076         } else
7077                 new_pos = *predicted_enemy_pos;
7078
7079         if (dist_to_enemy < 250.0f) {
7080                 if (dot_from_enemy > 0.7f) {
7081                         bank_override = Pl_objp->phys_info.speed;
7082                 }
7083         }
7084
7085         //      If enemy more than 500 meters away, all ships flying there will tend to match bank.
7086         //      They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
7087         //      to interpolate a matrix rather than just a vector.
7088         if (dist_to_enemy > 500.0f) {
7089                 vector  rvec;
7090                 compute_desired_rvec(&rvec, predicted_enemy_pos, &Pl_objp->pos);
7091                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0, &rvec);
7092         } else {
7093                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0);
7094         }
7095
7096         attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
7097 }
7098
7099 //      EVADE_SQUIGGLE submode handler for chase mode.
7100 //      Changed by MK on 5/5/97.
7101 //      Used to evade towards a point off the right or up vector.
7102 //      Now, evade straight away to try to get far away.
7103 //      The squiggling should protect against laser fire.
7104 void ai_chase_es(ai_info *aip, ship_info *sip)
7105 {
7106         vector  tvec;
7107         fix             timeslice;
7108         fix             scale;
7109         float           bank_override = 0.0f;
7110
7111         tvec = Pl_objp->pos;
7112
7113         timeslice = (Missiontime >> 16) & 0x0f;
7114         scale = ((Missiontime >> 16) & 0x0f) << 14;
7115
7116         if (timeslice & 0x01)
7117                 vm_vec_scale_add2(&tvec, &Pl_objp->orient.v.rvec, f2fl(scale ^ 0x10000));
7118         if (timeslice & 0x02)
7119                 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.v.rvec, f2fl(scale));
7120         if (timeslice & 0x04)
7121                 vm_vec_scale_add2(&tvec, &Pl_objp->orient.v.uvec, f2fl(scale ^ 0x10000));
7122         if (timeslice & 0x08)
7123                 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.v.uvec, f2fl(scale));
7124
7125         while (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
7126                 tvec.xyz.x += frand();
7127                 tvec.xyz.y += frand();
7128         }
7129
7130         bank_override = Pl_objp->phys_info.speed;
7131
7132         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7133         accelerate_ship(aip, 1.0f);
7134 }
7135
7136 //      Trying to get away from opponent.
7137 void ai_chase_ga(ai_info *aip, ship_info *sip)
7138 {
7139         //      If not near end of this submode, evade squiggly.  If near end, just fly straight for a bit
7140         vector  tvec;
7141         float           bank_override;
7142         vector  vec_from_enemy;
7143
7144         if (En_objp != NULL) {
7145                 vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
7146         } else
7147                 vec_from_enemy = Pl_objp->orient.v.fvec;
7148
7149         static_randvec(Missiontime >> 15, &tvec);
7150         vm_vec_scale(&tvec, 100.0f);
7151         vm_vec_scale_add2(&tvec, &vec_from_enemy, 300.0f);
7152         vm_vec_add2(&tvec, &Pl_objp->pos);
7153
7154         bank_override = Pl_objp->phys_info.speed;
7155
7156         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7157
7158         accelerate_ship(aip, 2.0f);
7159
7160         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
7161                 if (!(Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
7162                         float percent_left = 100.0f * Ships[Pl_objp->instance].afterburner_fuel / sip->afterburner_fuel_capacity;
7163                         if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
7164                                 afterburners_start(Pl_objp);
7165                                 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7166                         }
7167                         afterburners_start(Pl_objp);
7168                         aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7169                 }
7170         }
7171
7172 }
7173
7174 //      Make object *objp attack subsystem with ID = subnum.
7175 //      Return true if found a subsystem to attack, else return false.
7176 //      Note, can fail if subsystem exists, but has no hits.
7177 int ai_set_attack_subsystem(object *objp, int subnum)
7178 {
7179         ship                    *shipp, *attacker_shipp;
7180         ai_info         *aip;
7181         ship_subsys     *ssp;
7182         object          *attacked_objp;
7183
7184         SDL_assert(objp->type == OBJ_SHIP);
7185         SDL_assert(objp->instance >= 0);
7186
7187         attacker_shipp = &Ships[objp->instance];
7188         SDL_assert(attacker_shipp->ai_index >= 0);
7189
7190         aip = &Ai_info[attacker_shipp->ai_index];
7191
7192         // MWA -- 2/27/98.  Due to AL's changes, target_objnum is now not always valid (at least sometimes
7193         // in terms of goals).  So, bail if we don't have a valid target.
7194         if ( aip->target_objnum == -1 )
7195                 return 0;
7196
7197         attacked_objp = &Objects[aip->target_objnum];
7198         shipp = &Ships[attacked_objp->instance];                //  need to get our target's ship pointer!!!
7199
7200         ssp = ship_get_indexed_subsys(shipp, subnum, &objp->pos);
7201         if (ssp == NULL)
7202                 return 0;
7203
7204         set_targeted_subsys(aip, ssp, aip->target_objnum);
7205         
7206         if (aip->ignore_objnum == aip->target_objnum)
7207                 aip->ignore_objnum = UNUSED_OBJNUM;
7208
7209         // -- Done at caller in ai_process_mission_orders -- attacked_objp->flags |= OF_PROTECTED;
7210
7211         ai_set_goal_maybe_abort_dock(objp, aip);
7212         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7213
7214         return 1;
7215 }
7216
7217 void ai_set_guard_vec(object *objp, object *guard_objp)
7218 {
7219         ai_info *aip;
7220         float   radius;
7221
7222         aip = &Ai_info[Ships[objp->instance].ai_index];
7223
7224         //      Handle case of bogus call in which ship is told to guard self.
7225         SDL_assert(objp != guard_objp);
7226         if (objp == guard_objp) {
7227                 vm_vec_rand_vec_quick(&aip->guard_vec);
7228                 vm_vec_scale(&aip->guard_vec, 100.0f);
7229                 return;
7230         }
7231
7232         // check if guard_objp is BIG
7233         radius = 5.0f * (objp->radius + guard_objp->radius) + 50.0f;
7234         if (radius > 300.0f) {
7235                 radius = guard_objp->radius * 1.25f;
7236         }
7237
7238         vm_vec_sub(&aip->guard_vec, &objp->pos, &guard_objp->pos);
7239
7240         if (vm_vec_mag(&aip->guard_vec) > 3.0f*radius) {
7241                 //      Far away, don't just use vector to object, causes clustering of guard ships.
7242                 vector  tvec, rvec;
7243                 float   mag;
7244                 mag = vm_vec_copy_normalize(&tvec, &aip->guard_vec);
7245                 vm_vec_rand_vec_quick(&rvec);                   
7246                 vm_vec_scale_add2(&tvec, &rvec, 0.5f);
7247                 vm_vec_copy_scale(&aip->guard_vec, &tvec, mag);
7248         }
7249
7250         vm_vec_normalize_quick(&aip->guard_vec);
7251         vm_vec_scale(&aip->guard_vec, radius);
7252 }
7253
7254 //      Make object *objp guard object *other_objp.
7255 //      To be called from the goals code.
7256 void ai_set_guard_wing(object *objp, int wingnum)
7257 {
7258         ship            *shipp;
7259         ai_info *aip;
7260         int             leader_objnum, leader_shipnum;
7261
7262         SDL_assert(wingnum >= 0);
7263
7264         SDL_assert(objp->type == OBJ_SHIP);
7265         SDL_assert(objp->instance >= 0);
7266
7267         // shouldn't set the ai mode for the player
7268         if ( objp == Player_obj ) {
7269                 return;
7270         }
7271
7272         shipp = &Ships[objp->instance];
7273
7274         SDL_assert(shipp->ai_index >= 0);
7275
7276         aip = &Ai_info[shipp->ai_index];
7277         force_avoid_player_check(objp, aip);
7278
7279         ai_set_goal_maybe_abort_dock(objp, aip);
7280         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7281
7282         //      This function is called whenever a guarded ship is destroyed, so this code
7283         //      prevents a ship from trying to guard a non-existent wing.
7284         if (Wings[wingnum].current_count < 1) {
7285                 aip->guard_objnum = -1;
7286                 aip->guard_wingnum = -1;
7287                 aip->mode = AIM_NONE;
7288         } else {
7289                 leader_shipnum = Wings[wingnum].ship_index[0];
7290                 leader_objnum = Ships[leader_shipnum].objnum;
7291
7292                 SDL_assert((leader_objnum >= 0) && (leader_objnum < MAX_OBJECTS));
7293                 //SDL_assert(leader_objnum != objp-Objects);    //      Don't allow ships to guard themselves.
7294                 if (leader_objnum == OBJ_INDEX(objp)) {
7295                         //Int3();       //      Seems illegal, but let's clean up.  Get MikeK.
7296                         return;
7297                 }
7298
7299                 aip->guard_wingnum = wingnum;
7300                 aip->guard_objnum = leader_objnum;
7301                 aip->guard_signature = Objects[leader_objnum].signature;
7302                 aip->mode = AIM_GUARD;
7303                 aip->submode = AIS_GUARD_STATIC;
7304
7305                 ai_set_guard_vec(objp, &Objects[leader_objnum]);
7306         }
7307 }
7308
7309 //      Make object *objp guard object *other_objp.
7310 //      To be called from the goals code.
7311 void ai_set_evade_object(object *objp, object *other_objp)
7312 {
7313         ship            *shipp;
7314         ai_info *aip;
7315         int             other_objnum;
7316
7317         SDL_assert(objp->type == OBJ_SHIP);
7318         SDL_assert(objp->instance >= 0);
7319
7320         shipp = &Ships[objp->instance];
7321
7322         SDL_assert(shipp->ai_index >= 0);
7323
7324         aip = &Ai_info[shipp->ai_index];
7325
7326         other_objnum = OBJ_INDEX(other_objp);
7327         SDL_assert(other_objnum >= 0);
7328
7329         SDL_assert(other_objnum != Ships[aip->shipnum].objnum); //      make sure not targeting self
7330         aip->target_objnum = other_objnum;
7331
7332         aip->mode = AIM_EVADE;
7333 }
7334
7335 //      Make objp guard other_objp
7336 //      If other_objp is a member of a wing, objp will guard that whole wing
7337 //      UNLESS objp is also a member of the wing!
7338 void ai_set_guard_object(object *objp, object *other_objp)
7339 {
7340         ship            *shipp;
7341         ai_info *aip;
7342         int             other_objnum;
7343
7344         SDL_assert(objp->type == OBJ_SHIP);
7345         SDL_assert(objp->instance >= 0);
7346         SDL_assert(objp != other_objp);
7347
7348         shipp = &Ships[objp->instance];
7349
7350         SDL_assert(shipp->ai_index >= 0);
7351
7352         aip = &Ai_info[shipp->ai_index];
7353         aip->avoid_check_timestamp = timestamp(1);
7354
7355         //      If ship to guard is in a wing, guard that whole wing.
7356         ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
7357         if ((other_aip->wing != -1) && (other_aip->wing != aip->wing)) {
7358                 ai_set_guard_wing(objp, Ai_info[Ships[other_objp->instance].ai_index].wing);
7359         } else {
7360
7361                 other_objnum = other_objp-Objects;
7362
7363                 aip->guard_objnum = other_objnum;
7364                 aip->guard_signature = other_objp->signature;
7365                 aip->guard_wingnum = -1;
7366
7367                 aip->mode = AIM_GUARD;
7368                 aip->submode = AIS_GUARD_STATIC;
7369
7370                 SDL_assert(other_objnum >= 0);  //      Hmm, bogus object and we need its position for guard_vec.
7371
7372                 // vm_vec_sub(&aip->guard_vec, &objp->pos, &Objects[other_objnum].pos);
7373                 ai_set_guard_vec(objp, &Objects[other_objnum]);
7374
7375                 ai_set_goal_maybe_abort_dock(objp, aip);
7376                 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7377         }
7378 }
7379
7380 //      Update the aspect_locked_time field based on whether enemy is in view cone.
7381 //      Also set/clear AIF_SEEK_LOCK.
7382 void update_aspect_lock_information(ai_info *aip, vector *vec_to_enemy, float dist_to_enemy, float enemy_radius)
7383 {
7384         float   dot_to_enemy;
7385         int     num_weapon_types;
7386         int     weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
7387         ship    *shipp;
7388         ship_weapon     *swp;
7389         weapon_info     *wip;
7390
7391         shipp = &Ships[aip->shipnum];
7392         swp = &shipp->weapons;
7393
7394         // AL 3-7-98: This probably should never happen, but check to ensure that current_secondary_bank is valid
7395         if ( (swp->current_secondary_bank < 0) || (swp->current_secondary_bank > swp->num_secondary_banks) ) {
7396                 return;
7397         }
7398
7399         num_weapon_types = get_available_secondary_weapons(Pl_objp, weapon_id_list, weapon_bank_list);
7400
7401         wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
7402
7403         if (num_weapon_types && (wip->wi_flags & WIF_HOMING_ASPECT)) {
7404                 if (dist_to_enemy > 300.0f - min(enemy_radius, 100.0f))
7405                         aip->ai_flags |= AIF_SEEK_LOCK;
7406                 else
7407                         aip->ai_flags &= ~AIF_SEEK_LOCK;
7408
7409                 //      Update locking information for aspect seeking missiles.
7410                 aip->current_target_is_locked = 0;
7411                 dot_to_enemy = vm_vec_dot(vec_to_enemy, &Pl_objp->orient.v.fvec);
7412
7413                 float   needed_dot = 0.9f - 0.5f * enemy_radius/(dist_to_enemy + enemy_radius); //      Replaced MIN_TRACKABLE_DOT with 0.9f
7414                 if (dot_to_enemy > needed_dot) {
7415                         aip->aspect_locked_time += flFrametime;
7416                         // nprintf(("AI", "+ Lock time = %7.3f\n", aip->aspect_locked_time));
7417                         if (aip->aspect_locked_time >= wip->min_lock_time) {
7418                                 aip->aspect_locked_time = wip->min_lock_time;
7419                                 aip->current_target_is_locked = 1;
7420                         }
7421                 } else {
7422                         aip->aspect_locked_time -= flFrametime*2;
7423                         // nprintf(("AI", "- Lock time = %7.3f\n", aip->aspect_locked_time));
7424                         if (aip->aspect_locked_time < 0.0f)
7425                                 aip->aspect_locked_time = 0.0f;
7426                 }
7427                 //nprintf(("AI", "dot = %7.3f, time = %7.3f\n", dot_to_enemy, aip->aspect_locked_time));
7428         
7429         } else {
7430                 aip->current_target_is_locked = 0;
7431                 aip->aspect_locked_time = 0.0f; // Used to be this, why?: wip->min_lock_time;
7432                 aip->ai_flags &= ~AIF_SEEK_LOCK;
7433         }
7434
7435 }
7436
7437 //      We're in chase mode and we've recently collided with our target.
7438 //      Fly away from it!
7439 void ai_chase_fly_away(object *objp, ai_info *aip)
7440 {
7441         int     abort_flag = 0;
7442
7443         if (aip->ai_flags & AIF_TARGET_COLLISION) {
7444                 aip->ai_flags &= ~AIF_TARGET_COLLISION; //      Don't process this hit again next frame.
7445                 aip->submode = SM_FLY_AWAY;                                     //      Focus on avoiding target
7446                 aip->submode_start_time = Missiontime;
7447         }
7448
7449         if ((aip->target_objnum == -1) || (Objects[aip->target_objnum].signature != aip->target_signature)) {
7450                 abort_flag = 1;
7451         }
7452
7453         if (abort_flag || (Missiontime > aip->submode_start_time + F1_0)) {
7454                 aip->last_attack_time = Missiontime;
7455                 aip->submode = SM_ATTACK;
7456                 aip->submode_start_time = Missiontime;
7457         } else {
7458                 vector  v2e;
7459                 float           dot;
7460
7461                 vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, &objp->pos);
7462
7463                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2e);
7464                 if (dot < 0.0f)
7465                         accelerate_ship(aip, 1.0f);
7466                 else
7467                         accelerate_ship(aip, 1.0f - dot);
7468                 turn_away_from_point(objp, &Objects[aip->target_objnum].pos, 0.0f);
7469         }
7470 }
7471
7472 //      Return bank index of favored secondary weapon.
7473 //      Return -1 if nothing favored.
7474 //      "favored" means SEXPs have specified the weapon as being good to fire at en_objp.
7475 int has_preferred_secondary(object *objp, object *en_objp, ship_weapon *swp)
7476 {
7477 // int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
7478         int     i;
7479
7480         for (i=0; i<swp->num_secondary_banks; i++) {
7481                 if (swp->secondary_bank_capacity[i] > 0) {
7482                         if (swp->secondary_bank_ammo[i] > 0) {
7483                                 if (is_preferred_weapon(swp->secondary_bank_weapons[i], objp, en_objp) != -1){
7484                                         return i;
7485                                 }
7486                         }
7487                 }
7488         }
7489
7490         return -1;
7491 }
7492
7493 //      Choose which secondary weapon to fire.
7494 //      Note, this is not like ai_select_secondary_weapon().  "choose" means make a choice.
7495 //      "select" means execute an order.  Get it?
7496 //      This function calls ai_select_secondary_weapon() with the characteristics it should search for.
7497 void ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp)
7498 {
7499         float                   subsystem_strength = 0.0f;
7500         int                     is_big_ship, priority1, priority2;
7501         ship_weapon     *swp;
7502         ship_info       *esip;
7503
7504         if ( en_objp->type == OBJ_SHIP ) {
7505                 esip = &Ship_info[Ships[en_objp->instance].ship_info_index];
7506         } else {
7507                 esip = NULL;
7508         }
7509
7510         swp = &Ships[objp->instance].weapons;
7511
7512         // AL 3-5-98: do a quick out if the ship has no secondaries
7513         if ( swp->num_secondary_banks <= 0 ) {
7514                 swp->current_secondary_bank = -1;
7515                 return;
7516         }
7517
7518         int preferred_secondary = has_preferred_secondary(objp, en_objp, swp);
7519
7520         if (preferred_secondary != -1) {
7521                 if (swp->current_secondary_bank != preferred_secondary) {
7522                         aip->current_target_is_locked = 0;
7523                         aip->aspect_locked_time = 0.0f;
7524                         swp->current_secondary_bank = preferred_secondary;
7525                 }
7526                 //nprintf(("AI", "Favored secondary = %s\n", Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7527                 aip->ai_flags |= AIF_UNLOAD_SECONDARIES;
7528         } else {
7529                 aip->ai_flags &= ~AIF_UNLOAD_SECONDARIES;
7530                 if (aip->targeted_subsys) {
7531                         subsystem_strength = aip->targeted_subsys->current_hits;
7532                 }
7533
7534                 if ( esip ) {
7535                         is_big_ship = esip->flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP);
7536                 } else {
7537                         is_big_ship=0;
7538                 }
7539
7540                 if (is_big_ship) {
7541                         priority1 = WIF_HUGE;
7542                         priority2 = WIF_HOMING;
7543                 } else if ( (esip != NULL) && (esip->flags & SIF_BOMBER) ) {
7544                         priority1 = WIF_BOMBER_PLUS;
7545                         priority2 = WIF_HOMING;
7546                 } else if (subsystem_strength > 100.0f) {
7547                         priority1 = WIF_PUNCTURE;
7548                         priority2 = WIF_HOMING;
7549                 } else {
7550                         priority1 = WIF_HOMING;
7551                         priority2 = 0;
7552                 }
7553                 
7554                 ai_select_secondary_weapon(objp, swp, priority1, priority2);
7555         }
7556
7557         // nprintf(("AI", "Frame %i: Chose secondary %s\n", Framecount, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7558 }
7559
7560 //      Return time, in seconds, at which this ship can next fire its current secondary weapon.
7561 float set_secondary_fire_delay(ai_info *aip, ship *shipp, weapon_info *swip)
7562 {
7563         float t = swip->fire_wait;              //      Base delay for this weapon.
7564         if (shipp->team == Player_ship->team) {
7565                 //      On player's team, _lower_ skill level = faster firing
7566                 t = t * (Game_skill_level+2) / (NUM_SKILL_LEVELS);
7567         } else {                //      Not on player's team, higher skill level = faster firing
7568                 t = t * (NUM_SKILL_LEVELS - Game_skill_level+2) / (NUM_SKILL_LEVELS);
7569         }
7570
7571         t += (Num_ai_classes - aip->ai_class + 1) * 0.5f;
7572         t *= frand_range(0.8f, 1.2f);
7573
7574         //      For the missiles that fire fairly quickly, occasionally add an additional substantial delay.
7575         if (t < 5.0f)
7576                 if (frand() < 0.5f)
7577                         t = t * 2.0f + 2.0f;
7578
7579         return t;
7580 }
7581
7582
7583 void ai_chase_big_approach_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7584 {
7585         float dist_to_goal;
7586
7587         // head straight toward him and maybe circle later
7588         vm_vec_avg(goal_pos, &attack_objp->pos, &target_objp->pos);
7589
7590         // get distance to goal
7591         dist_to_goal = vm_vec_dist(goal_pos, &attack_objp->pos);
7592         
7593         // set accel
7594         if (dist_to_goal > 400.0f) {
7595                 *accel = 1.0f;
7596         } else {
7597                 *accel = dist_to_goal/400.0f;
7598         }
7599 }
7600
7601 void ai_chase_big_circle_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7602 {
7603         get_tangent_point(goal_pos, attack_objp, &target_objp->pos, attack_objp->radius + target_objp->radius + 100.0f);
7604
7605         *accel = 1.0f;
7606 }
7607
7608 // get the current and desired horizontal separations between target
7609 void ai_chase_big_get_separations(object *attack_objp, object *target_objp, vector *horz_vec_to_target, float *desired_separation, float *cur_separation)
7610 {
7611         float temp, r_target, r_attacker;
7612         float perp_dist;
7613         vector vec_to_target;
7614         polymodel *pm;
7615
7616         // get parameters of ships (as cylinders - radius and height)
7617         // get radius of attacker (for rotations about forward)
7618         pm = model_get(Ships[attack_objp->instance].modelnum);
7619         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7620         r_attacker = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7621         r_attacker = max(temp, r_attacker);
7622
7623         // get radius of target (for rotations about forward)
7624         pm = model_get(Ships[attack_objp->instance].modelnum);
7625         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7626         r_target = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7627         r_target = max(temp, r_target);
7628
7629         // find separation between cylinders [if parallel]
7630         vm_vec_sub(&vec_to_target, &attack_objp->pos, &target_objp->pos);
7631
7632         // find the distance between centers along forward direction of ships
7633         perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.v.fvec);
7634
7635         // subtract off perp component to get "horizontal" separation vector between cylinders [ASSUMING parallel]
7636         vm_vec_scale_add(horz_vec_to_target, &vec_to_target, &target_objp->orient.v.fvec, -perp_dist);
7637         *cur_separation = vm_vec_mag_quick(horz_vec_to_target);
7638
7639         // choose "optimal" separation of 1000 + r_target + r_attacker
7640         *desired_separation = 1000 + r_target + r_attacker;
7641 }
7642
7643 void ai_chase_big_parallel_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7644 {
7645         int opposing;
7646         float separation, optimal_separation;
7647         vector  horz_vec_to_target;
7648
7649         /*
7650         float temp, r_target, r_attacker;
7651         polymodel *pm;
7652
7653         // get parameters of ships (as cylinders - radius and height)
7654         // get radius of attacker (for rotations about forward)
7655         pm = model_get(Ships[attack_objp->instance].modelnum);
7656         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7657         r_attacker = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7658         r_attacker = max(temp, r_attacker);
7659
7660         // get radius of target (for rotations about forward)
7661         pm = model_get(Ships[attack_objp->instance].modelnum);
7662         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7663         r_target = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7664         r_target = max(temp, r_target);
7665         */
7666
7667         // are we opposing (only when other ship is not moving)
7668         opposing = ( vm_vec_dotprod(&attack_objp->orient.v.fvec, &target_objp->orient.v.fvec) < 0 );
7669
7670         ai_chase_big_get_separations(attack_objp, target_objp, &horz_vec_to_target, &optimal_separation, &separation);
7671
7672         // choose dist (2000) so that we don't bash
7673         float dist = 2000;
7674         if (opposing) {
7675                 dist = - dist;
7676         }
7677
7678         // set the goal pos as dist forward from target along target forward
7679         vm_vec_scale_add(goal_pos, &target_objp->pos, &target_objp->orient.v.fvec, dist);
7680         // then add horizontal separation
7681         vm_vec_scale_add2(goal_pos, &horz_vec_to_target, optimal_separation/separation);
7682
7683         // find the distance between centers along forward direction of ships
7684         vector vec_to_target;
7685         vm_vec_sub(&vec_to_target, &target_objp->pos, &attack_objp->pos);
7686         float perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.v.fvec);
7687
7688         float match_accel = target_objp->phys_info.vel.xyz.z / Ship_info[Ships[attack_objp->instance].ship_info_index].max_vel.xyz.z;
7689         float length_scale = attack_objp->radius;
7690
7691         // if we're heading toward enemy ship, we want to keep going if we're ahead
7692         if (opposing) {
7693                 perp_dist = -perp_dist;
7694         }
7695
7696         if (perp_dist > 0) {
7697                 // falling behind, so speed up
7698                 *accel = match_accel + (1.0f - match_accel) / length_scale * (perp_dist);
7699         } else {
7700                 // up in front, so slow down
7701                 *accel = match_accel  - match_accel / length_scale * -perp_dist;
7702                 *accel = max(0.0f, *accel);
7703         }
7704
7705 }
7706
7707
7708 //      Return *goal_pos for one cruiser to attack another (big ship).
7709 //      Choose point fairly nearby that is not occupied by another cruiser.
7710 void ai_cruiser_chase_set_goal_pos(vector *goal_pos, object *pl_objp, object *en_objp)
7711 {
7712         ai_info *aip;
7713
7714         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
7715         float accel;
7716
7717         switch (aip->submode) {
7718         case SM_BIG_APPROACH:
7719                 // do approach stuff;
7720                 ai_chase_big_approach_set_goal(goal_pos, pl_objp, en_objp, &accel);
7721                 break;
7722
7723         case SM_BIG_CIRCLE:
7724                 // do circle stuff
7725                 ai_chase_big_circle_set_goal(goal_pos, pl_objp, en_objp, &accel);
7726                 break;
7727
7728         case SM_BIG_PARALLEL:
7729                 // do parallel stuff
7730                 ai_chase_big_parallel_set_goal(goal_pos, pl_objp, en_objp, &accel);
7731                 break;
7732         }
7733 }
7734
7735 int maybe_hack_cruiser_chase_abort()
7736 {
7737         ship                    *shipp = &Ships[Pl_objp->instance];     
7738         ship                    *eshipp = &Ships[En_objp->instance];
7739         ai_info         *aip = &Ai_info[shipp->ai_index];
7740
7741         // mission sm3-08, sathanos chasing collosus
7742         if ( SDL_strcasecmp(Mission_filename, "sm3-08.fs2") == 0 ) {
7743                 if (( SDL_strcasecmp(eshipp->ship_name, "colossus") == 0 ) || ( SDL_strcasecmp(shipp->ship_name, "colossus") == 0 )) {
7744                         // Changed so all big ships attacking the Colossus will not do the chase code.
7745                         // Did this so Beast wouldn't swerve away from Colossus. -- MK, 9/14/99
7746                         //if ( SDL_strcasecmp(shipp->ship_name, "Sathanas") == 0 ) {
7747                                 // do cool hack stuff here
7748                                 ai_clear_ship_goals( aip );
7749                                 aip->mode = AIM_NONE;
7750                                 return 1;
7751                         //}
7752                 }
7753         }
7754
7755         return 0;
7756 }
7757
7758 //      Make a big ship pursue another big ship.
7759 //      (Note, called "ai_cruiser_chase" because we already have ai_chase_big() which means fighter chases big ship.
7760 void ai_cruiser_chase()
7761 {
7762         ship_info       *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7763         ship                    *shipp = &Ships[Pl_objp->instance];     
7764         ai_info         *aip = &Ai_info[shipp->ai_index];
7765
7766         if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7767                 Int3(); //      Hmm, not a very big ship, how did we get in this function?
7768                 aip->mode = AIM_NONE;
7769                 return;
7770         }
7771
7772         if (En_objp->type != OBJ_SHIP) {
7773                 Int3();
7774                 return;
7775         }
7776
7777         if (En_objp->instance < 0) {
7778                 Int3();
7779                 return;
7780         }
7781
7782         ship                    *eshipp;
7783         ship_info       *esip;
7784
7785         eshipp = &Ships[En_objp->instance];
7786         esip = &Ship_info[eshipp->ship_info_index];
7787
7788         if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7789                 // Int3();      //      Hmm, we're big and we're pursuing something other than a big ship?
7790                 aip->mode = AIM_NONE;
7791                 return;
7792         }
7793
7794         vector  goal_pos;
7795         float turn_time = Ship_info[Ships[Pl_objp->instance].ship_info_index].srotation_time;
7796
7797         // kamikaze - ram and explode
7798         if (aip->ai_flags & AIF_KAMIKAZE) {
7799                 ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
7800                 accelerate_ship(aip, 1.0f);
7801         } 
7802         
7803         // really track down and chase
7804         else {
7805                 // check valid submode
7806                 SDL_assert( (aip->submode == SM_ATTACK) || (aip->submode == SM_BIG_APPROACH) || (aip->submode == SM_BIG_CIRCLE) || (aip->submode == SM_BIG_PARALLEL) );
7807
7808                 // just entering, approach enemy ship
7809                 if (aip->submode == SM_ATTACK) {
7810                         aip->submode = SM_BIG_APPROACH;
7811                 }
7812
7813                 // desired accel
7814                 float accel = 0.0f;
7815                 vector *rvecp = NULL;
7816
7817                 switch (aip->submode) {
7818                 case SM_BIG_APPROACH:
7819                         // do approach stuff;
7820                         ai_chase_big_approach_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7821                         // maybe set rvec
7822                         break;
7823
7824                 case SM_BIG_CIRCLE:
7825                         // do circle stuff
7826                         ai_chase_big_circle_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7827                         // maybe set rvec
7828                         break;
7829
7830                 case SM_BIG_PARALLEL:
7831                         // do parallel stuff
7832                         ai_chase_big_parallel_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7833                         //maybe set rvec
7834                         break;
7835                 }
7836
7837
7838                 // now move as desired
7839                 ai_turn_towards_vector(&goal_pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0, rvecp);
7840                 accelerate_ship(aip, accel);
7841
7842
7843                 // maybe switch to new mode
7844                 vector vec_to_enemy;
7845                 float dist_to_enemy;
7846                 int moving = (En_objp->phys_info.vel.xyz.z > 0.5f);
7847                 vm_vec_sub(&vec_to_enemy, &En_objp->pos, &Pl_objp->pos);
7848                 dist_to_enemy = vm_vec_mag_quick(&vec_to_enemy);
7849
7850                 switch (aip->submode) {
7851                 case SM_BIG_APPROACH:
7852                         if ( dist_to_enemy < (Pl_objp->radius + En_objp->radius)*1.25f + 200.0f ) {
7853                                 // moving
7854                                 if (moving) {
7855                                         // if within 90 degrees of en forward, go into parallel, otherwise circle
7856                                         if ( vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0 ) {
7857                                                 aip->submode = SM_BIG_PARALLEL;
7858                                         }
7859                                 }
7860
7861                                 // otherwise cirle
7862                                 if ( !maybe_hack_cruiser_chase_abort() ) {
7863                                         aip->submode = SM_BIG_CIRCLE;
7864                                 }
7865                         }
7866                         break;
7867
7868                 case SM_BIG_CIRCLE:
7869                         // moving
7870                         if (moving) {
7871                                 vector temp;
7872                                 float desired_sep, cur_sep;
7873                                 // we're behind the enemy ship
7874                                 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec) > 0) {
7875                                         // and we're turning toward the enemy
7876                                         if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0) {
7877                                                 // get separation
7878                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7879                                                 // and the separation is > 0.9 desired
7880                                                 if (cur_sep > 0.9 * desired_sep) {
7881                                                         aip->submode = SM_BIG_PARALLEL;
7882                                                 }
7883                                         }
7884                                 }
7885                         } else {
7886                                 // still
7887                                 vector temp;
7888                                 float desired_sep, cur_sep;
7889                                 // we're behind the enemy ship
7890                                 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec) > 0) {
7891                                         // and we're turning toward the enemy
7892                                         if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0) {
7893                                                 // get separation
7894                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7895                                                 //and the separation is [0.9 to 1.1] desired
7896                                                 if ( (cur_sep > 0.9f * desired_sep) ) {
7897                                                         aip->submode = SM_BIG_PARALLEL;
7898                                                 }
7899                                         }
7900                                 }
7901                                 // in front of ship
7902                                 else {
7903                                         // and we're turning toward the enemy
7904                                         if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) < 0) {
7905                                                 // get separation
7906                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7907                                                 //and the separation is [0.9 to 1.1] desired
7908                                                 if ( (cur_sep > 0.9f * desired_sep) ) {
7909                                                         aip->submode = SM_BIG_PARALLEL;
7910                                                 }
7911                                         }
7912                                 }
7913                         }
7914                         break;
7915
7916                 case SM_BIG_PARALLEL:
7917                         // we're opposing
7918                         if ( vm_vec_dotprod(&Pl_objp->orient.v.fvec, &En_objp->orient.v.fvec) < 0 ) {
7919                                 // and the other ship is moving
7920                                 if (moving) {
7921                                         // and we no longer overlap
7922                                         if ( dist_to_enemy > (0.75 * (En_objp->radius + Pl_objp->radius)) ) {
7923                                                 aip->submode = SM_BIG_APPROACH;
7924                                         }
7925                                 }
7926                         }
7927                         break;
7928                 }
7929         }
7930 }
7931
7932 // --------------------------------------------------------------------------
7933 // Make object Pl_objp chase object En_objp
7934 void ai_chase()
7935 {
7936         float                   dist_to_enemy;
7937         float                   dot_to_enemy, dot_from_enemy, real_dot_to_enemy;
7938         vector          player_pos, enemy_pos, predicted_enemy_pos, real_vec_to_enemy, predicted_vec_to_enemy;
7939         ship_info       *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7940         ship                    *shipp = &Ships[Pl_objp->instance];
7941         ship_weapon     *swp = &shipp->weapons;
7942         ai_info         *aip = &Ai_info[shipp->ai_index];
7943         int                     enemy_sip_flags;
7944
7945         if (aip->mode != AIM_CHASE) {
7946                 Int3();
7947         }
7948
7949         if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
7950                 ai_cruiser_chase();
7951                 return;
7952         }
7953
7954         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER | SIF_ESCAPEPOD))) {
7955                 Warning(LOCATION, "Ship %s is not 'small', but is in chase mode.\nSwitching to AI=none.\n", shipp->ship_name);
7956                 aip->mode = AIM_NONE;
7957                 return;
7958         }
7959
7960         //nprintf(("AI", "%7s ", Submode_text[aip->submode]));
7961
7962         if ( En_objp->type == OBJ_SHIP ) {
7963                 enemy_sip_flags = Ship_info[Ships[En_objp->instance].ship_info_index].flags;
7964         } else {
7965                 enemy_sip_flags = 0;
7966         }
7967
7968         if ( enemy_sip_flags > 0 ) {
7969                 if (enemy_sip_flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
7970                         ai_big_chase();
7971                         return;
7972                 }
7973         }
7974
7975         //      If collided with target_objnum last frame, avoid that ship.
7976         //      This should prevent the embarrassing behavior of ships getting stuck on each other
7977         //      as if they were magnetically attracted. -- MK, 11/13/97.
7978         if ((aip->ai_flags & AIF_TARGET_COLLISION) || (aip->submode == SM_FLY_AWAY)) {
7979                 ai_chase_fly_away(Pl_objp, aip);
7980                 return;
7981         }
7982
7983         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
7984         dist_to_enemy = vm_vec_dist_quick(&player_pos, &enemy_pos);
7985         vm_vec_sub(&real_vec_to_enemy, &enemy_pos, &player_pos);
7986
7987         vm_vec_normalize(&real_vec_to_enemy);
7988
7989         real_dot_to_enemy = vm_vec_dot(&real_vec_to_enemy, &Pl_objp->orient.v.fvec);
7990
7991         int is_stealthy_ship = 0;
7992         if ( (enemy_sip_flags > 0) && (enemy_sip_flags & SIF_STEALTH) ) {
7993                 if ( ai_is_stealth_visible(Pl_objp, En_objp) != STEALTH_FULLY_TARGETABLE ) {
7994                         is_stealthy_ship = 1;
7995                 }
7996         }
7997
7998         // Can only acquire lock on a target that isn't hidden from sensors
7999         if ( !(Ships[En_objp->instance].flags & SF_HIDDEN_FROM_SENSORS) && !is_stealthy_ship ) {
8000                 update_aspect_lock_information(aip, &real_vec_to_enemy, dist_to_enemy, En_objp->radius);
8001         } else {
8002                 aip->current_target_is_locked = 0;
8003                 aip->ai_flags &= ~AIF_SEEK_LOCK;
8004         }
8005
8006         //      If seeking lock, try to point directly at ship, else predict position so lasers can hit it.
8007         //      If just acquired target, or target is not in reasonable cone, don't refine believed enemy position.
8008         if ((real_dot_to_enemy < 0.25f) || (aip->target_time < 1.0f) || (aip->ai_flags & AIF_SEEK_LOCK)) {
8009                 predicted_enemy_pos = enemy_pos;
8010         } else {
8011                 //      Set predicted_enemy_pos.
8012                 //      See if attacking a subsystem.
8013                 if (aip->targeted_subsys != NULL) {
8014                         SDL_assert(En_objp->type == OBJ_SHIP);
8015                         ship_info       *esip = &Ship_info[Ships[En_objp->instance].ship_info_index];
8016                         if (get_shield_strength(En_objp)/esip->shields < HULL_DAMAGE_THRESHOLD_PERCENT) {
8017                                 //int   rval;
8018
8019                                 if (aip->targeted_subsys != NULL) {
8020                                         get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
8021                                         predicted_enemy_pos = enemy_pos;
8022                                         predicted_vec_to_enemy = real_vec_to_enemy;
8023                                 } else {
8024                                         set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8025                                         set_target_objnum(aip, -1);
8026                                 }
8027                                 // 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));
8028
8029                         } else {
8030                                 set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8031                                 // 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));
8032                         }
8033                 } else {
8034                         set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8035                 }
8036         }
8037
8038         vm_vec_sub(&predicted_vec_to_enemy, &predicted_enemy_pos, &player_pos);
8039
8040         vm_vec_normalize(&predicted_vec_to_enemy);
8041
8042         dot_to_enemy = vm_vec_dot(&Pl_objp->orient.v.fvec, &predicted_vec_to_enemy);
8043         dot_from_enemy= - vm_vec_dot(&En_objp->orient.v.fvec, &real_vec_to_enemy);
8044
8045         //
8046         //      Set turn and acceleration based on submode.
8047         //
8048         switch (aip->submode) {
8049         case SM_CONTINUOUS_TURN:
8050                 ai_chase_ct();
8051                 break;
8052
8053         case SM_STEALTH_FIND:
8054                 ai_stealth_find();
8055                 break;
8056
8057         case SM_STEALTH_SWEEP:
8058                 ai_stealth_sweep();
8059                 break;
8060
8061         case SM_ATTACK:
8062         case SM_SUPER_ATTACK:
8063         case SM_ATTACK_FOREVER:
8064                 if (vm_vec_dist_quick(&Pl_objp->pos, &predicted_enemy_pos) > 100.0f + En_objp->radius * 2.0f) {
8065                         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &predicted_enemy_pos, 10.0f))
8066                                 return;
8067                 }
8068
8069                 ai_chase_attack(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8070                 break;
8071
8072         case SM_EVADE_SQUIGGLE:
8073                 ai_chase_es(aip, sip);
8074                 break;
8075
8076         case SM_EVADE_BRAKE:
8077                 ai_chase_eb(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8078                 break;
8079
8080         case SM_EVADE:
8081                 evade_ship();
8082                 break;
8083
8084         case SM_AVOID:
8085                 avoid_ship();
8086                 break;
8087
8088         case SM_GET_BEHIND:
8089                 get_behind_ship(aip, sip, dist_to_enemy);
8090                 break;
8091
8092         case SM_GET_AWAY:               //      Used to get away from opponent to prevent endless circling.
8093                 ai_chase_ga(aip, sip);
8094                 break;
8095
8096         case SM_EVADE_WEAPON:
8097                 evade_weapon();
8098                 break;
8099
8100         default:
8101                 // Int3();
8102                 aip->last_attack_time = Missiontime;
8103                 aip->submode = SM_ATTACK;
8104                 aip->submode_start_time = Missiontime;
8105         }
8106
8107         //
8108         //      Maybe choose a new submode.
8109         //
8110         if ( (aip->submode != SM_AVOID) && (aip->submode != SM_ATTACK_FOREVER) ) {
8111                 //      If a very long time since attacked, attack no matter what!
8112                 if ( (aip->submode != SM_SUPER_ATTACK) && (aip->submode != SM_GET_AWAY) && !(aip->ai_flags & AIF_STEALTH_PURSIUT) ) {
8113                         if (Missiontime - aip->last_attack_time > i2f(6)) {
8114                                 aip->submode = SM_SUPER_ATTACK;
8115                                 aip->submode_start_time = Missiontime;
8116                                 aip->last_attack_time = Missiontime;
8117                         }
8118                 }
8119
8120                 //      If a collision is expected, pull out!
8121                 //      If enemy is pointing away and moving a bit, don't worry about collision detection.
8122                 if ((dot_from_enemy > 0.5f) || (En_objp->phys_info.speed < 10.0f)) {
8123                         if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 4.0f)) {
8124                                 if ((Missiontime - aip->last_hit_time > F1_0*4) && (dist_to_enemy < Pl_objp->radius*2 + En_objp->radius*2)) {
8125                                         accelerate_ship(aip, -1.0f);
8126                                 } else {
8127                                         aip->submode = SM_AVOID;
8128                                         aip->submode_start_time = Missiontime;
8129                                 }
8130                         }
8131                 }
8132         }
8133
8134         switch (aip->submode) {
8135         case SM_CONTINUOUS_TURN:
8136                 if (Missiontime - aip->submode_start_time > i2f(3)) {
8137                         aip->last_attack_time = Missiontime;
8138                         aip->submode = SM_ATTACK;
8139                         aip->submode_start_time = Missiontime;
8140                 }
8141                 break;
8142
8143         case SM_ATTACK:
8144                 // if taraget is stealth and stealth not visible, then enter stealth find mode
8145                 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8146                         aip->submode = SM_STEALTH_FIND;
8147                         aip->submode_start_time = Missiontime;
8148                         aip->submode_parm0 = SM_SF_AHEAD;
8149                 } 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)) {
8150                         aip->submode = SM_SUPER_ATTACK;
8151                         aip->submode_start_time = Missiontime;
8152                         aip->last_attack_time = Missiontime;
8153                 } else if ((Missiontime - aip->last_hit_target_time > i2f(6)) &&
8154                         (dist_to_enemy < 500.0f) && (dot_to_enemy < 0.2f) &&
8155                         (frand() < (float) Game_skill_level/NUM_SKILL_LEVELS)) {
8156                         aip->submode = SM_GET_AWAY;
8157                         aip->submode_start_time = Missiontime;
8158                         aip->last_hit_target_time = Missiontime;
8159                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP)
8160                         && (dot_to_enemy < dot_from_enemy)
8161                         && (En_objp->phys_info.speed > 15.0f) 
8162                         && (dist_to_enemy < 200.0f) 
8163                         && (dist_to_enemy > 50.0f)
8164                         && (dot_to_enemy < 0.1f)
8165                         && (Missiontime - aip->submode_start_time > i2f(2))) {
8166                         aip->submode = SM_EVADE_BRAKE;
8167                         aip->submode_start_time = Missiontime;
8168                 } else if ((dot_to_enemy > 0.2f) && (dot_from_enemy > -0.2f) && (dot_from_enemy < 0.1f)) {
8169                         aip->submode = SM_GET_BEHIND;
8170                         aip->submode_start_time = Missiontime;
8171                 } 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)) {
8172                         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;
8173                                 aip->submode_start_time = Missiontime;
8174                                 aip->last_hit_target_time = Missiontime;
8175                         } else {
8176                                 aip->submode = SM_EVADE_SQUIGGLE;
8177                                 aip->submode_start_time = Missiontime;
8178                         }
8179                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP) && (Missiontime - aip->submode_start_time > F1_0*2)) {
8180                         if ((dot_to_enemy < 0.8f) && (dot_from_enemy > dot_to_enemy)) {
8181                                 if (frand() > 0.5f) {
8182                                         aip->submode = SM_CONTINUOUS_TURN;
8183                                         aip->submode_parm0 = myrand() & 0x0f;
8184                                         aip->submode_start_time = Missiontime;
8185                                 } else {
8186                                         aip->submode = SM_EVADE;
8187                                         aip->submode_start_time = Missiontime;
8188                                 }
8189                         } else {
8190                                 aip->submode_start_time = Missiontime;
8191                         }
8192                 }
8193
8194                 aip->last_attack_time = Missiontime;
8195
8196                 break;
8197                 
8198         case SM_EVADE_SQUIGGLE:
8199                 if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > 300.0f)) {
8200                         if ((dist_to_enemy < 100.0f) && (dot_to_enemy < 0.0f) && (dot_from_enemy > 0.5f)) {
8201                                 aip->submode = SM_EVADE_BRAKE;
8202                                 aip->submode_start_time = Missiontime;
8203                         } else {
8204                                 aip->last_attack_time = Missiontime;
8205                                 aip->submode = SM_ATTACK;
8206                                 aip->submode_start_time = Missiontime;
8207                         }
8208                 }
8209                 break;
8210         
8211         case SM_EVADE_BRAKE:
8212                 if ((dist_to_enemy < 15.0f) || (En_objp->phys_info.speed < 10.0f)) {
8213                         aip->submode = SM_AVOID;
8214                         aip->submode_start_time = Missiontime;
8215                 } else if ((dot_to_enemy > 0.9f) || ((dot_from_enemy > 0.9f) && (Missiontime - aip->submode_start_time > i2f(1)))) {
8216                         aip->last_attack_time = Missiontime;
8217                         aip->submode = SM_ATTACK;
8218                         aip->submode_start_time = Missiontime;
8219                 } else if (Missiontime - aip->submode_start_time > i2f(4)) {
8220                         aip->last_attack_time = Missiontime;
8221                         aip->submode = SM_ATTACK;
8222                         aip->submode_start_time = Missiontime;
8223                 }
8224                 break;
8225
8226         case SM_EVADE:
8227                 //      Modified by MK on 5/5/97 to keep trying to regain attack mode.  It's what a human would do.
8228                 if ((dot_to_enemy < 0.2f) && (dot_from_enemy < 0.8f) && (dist_to_enemy < 100.0f) && (En_objp->phys_info.speed > 15.0f)) {
8229                         aip->last_attack_time = Missiontime;
8230                         aip->submode = SM_EVADE_BRAKE;
8231                         aip->submode_start_time = Missiontime;
8232                 } else if (((dot_to_enemy > dot_from_enemy - 0.1f)
8233                         && (Missiontime > aip->submode_start_time + i2f(1)))
8234                         || (dist_to_enemy > 150.0f + 2*(Pl_objp->radius + En_objp->radius))) {
8235                         aip->last_attack_time = Missiontime;
8236                         aip->submode = SM_ATTACK;
8237                         aip->submode_start_time = Missiontime;
8238                 } else if (Missiontime - aip->submode_start_time > i2f(2))
8239                         if (dot_from_enemy > 0.8f) {
8240                                 aip->submode = SM_EVADE_SQUIGGLE;
8241                                 aip->submode_start_time = Missiontime;
8242                         }
8243
8244                 break;
8245
8246         case SM_SUPER_ATTACK:
8247                 // if stealth and invisible, enter stealth find mode
8248                 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8249                         aip->submode = SM_STEALTH_FIND;
8250                         aip->submode_start_time = Missiontime;
8251                         aip->submode_parm0 = SM_SF_AHEAD;
8252                 } 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) )) {
8253                         aip->ai_flags &= ~AIF_ATTACK_SLOWLY;    //      Just in case, clear here.
8254
8255                         switch (myrand() % 5) {
8256                         case 0:
8257                                 aip->submode = SM_CONTINUOUS_TURN;
8258                                 aip->submode_start_time = Missiontime;
8259                                 break;
8260                         case 1:
8261                                 aip->submode_start_time = Missiontime;  //      Stay in super attack mode
8262                                 break;
8263                         case 2:
8264                         case 3:
8265                                 if (frand() < (float) 0.5f * (aip->ai_class + Game_skill_level)/(Num_ai_classes + NUM_SKILL_LEVELS)) {
8266                                         aip->submode = SM_GET_AWAY;
8267                                         aip->submode_start_time = Missiontime;
8268                                 } else {
8269                                         aip->submode = SM_EVADE;
8270                                         aip->submode_start_time = Missiontime;
8271                                 }
8272                                 break;
8273                         case 4:
8274                                 if (dot_from_enemy + (NUM_SKILL_LEVELS - Game_skill_level) * 0.1f > dot_to_enemy) {     //      Less likely to GET_AWAY at lower skill levels.
8275                                         aip->submode = SM_EVADE;
8276                                         aip->submode_start_time = Missiontime;
8277                                 } else {
8278                                         aip->submode = SM_GET_AWAY;
8279                                         aip->submode_start_time = Missiontime;
8280                                 }
8281                                 break;
8282                         default:
8283                                 Int3(); //      Impossible!
8284                         }
8285                 }
8286
8287                 aip->last_attack_time = Missiontime;
8288
8289                 break;
8290
8291         case SM_AVOID:
8292                 if ((dot_to_enemy > -0.2f) && (dist_to_enemy / (dot_to_enemy + 0.3f) < 100.0f)) {
8293                         aip->submode_start_time = Missiontime;
8294                 } else if (Missiontime - aip->submode_start_time > i2f(1)/2) {
8295                         if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 3.0f)) {
8296                                 aip->submode_start_time = Missiontime;
8297                         } else {
8298                                 aip->submode = SM_GET_BEHIND;
8299                                 aip->submode_start_time = Missiontime;
8300                         }
8301                 }
8302
8303                 break;
8304
8305         case SM_GET_BEHIND:
8306                 if ((dot_from_enemy < -0.7f) || (Missiontime - aip->submode_start_time > i2f(2))) {
8307                         aip->submode = SM_ATTACK;
8308                         aip->submode_start_time = Missiontime;
8309                         aip->last_attack_time = Missiontime;
8310                 }
8311                 break;
8312
8313         case SM_GET_AWAY:
8314                 if (Missiontime - aip->submode_start_time > i2f(2)) {
8315                         float   rand_dist;
8316
8317                         rand_dist = ((Missiontime >> 17) & 0x03) * 100.0f + 200.0f;     //      Some value in 200..500
8318                         if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > rand_dist) || (dot_from_enemy < 0.4f)) {
8319                                 aip->ai_flags |= AIF_ATTACK_SLOWLY;
8320                                 aip->submode = SM_ATTACK;
8321                                 aip->time_enemy_in_range = 2.0f;                //      Cheat.  Presumably if they were running away from you, they were monitoring you!
8322                                 aip->submode_start_time = Missiontime;
8323                                 aip->last_attack_time = Missiontime;
8324                         }
8325                 }
8326                 break;
8327
8328         case SM_EVADE_WEAPON:
8329                 if (aip->danger_weapon_objnum == -1) {
8330                         aip->submode = SM_ATTACK;
8331                         aip->submode_start_time = Missiontime;
8332                         aip->last_attack_time = Missiontime;
8333                 }
8334                 break;
8335
8336         // Either change to SM_ATTACK or AIM_FIND_STEALTH
8337         case SM_STEALTH_FIND:
8338                 // if time > 5 sec change mode to sweep
8339                 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8340                         aip->submode = SM_ATTACK;
8341                         aip->submode_start_time = Missiontime;
8342                         aip->last_attack_time = Missiontime;
8343                         // sweep if I can't find in 5 sec or bail from find
8344                 } else if ( ((Missiontime - aip->submode_start_time) > i2f(5)) || (aip->submode_parm0 == SM_SF_BAIL) ) {
8345                         // begin sweep mode
8346                         aip->submode = SM_STEALTH_SWEEP;
8347                         aip->submode_start_time = Missiontime;
8348                         aip->last_attack_time = Missiontime;
8349                         aip->submode_parm0 = SM_SS_SET_GOAL;
8350                 }
8351                 break;
8352
8353         case SM_STEALTH_SWEEP:
8354                 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8355                         aip->submode = SM_ATTACK;
8356                         aip->submode_start_time = Missiontime;
8357                         aip->last_attack_time = Missiontime;
8358                 } else if ( (timestamp() - aip->stealth_last_visible_stamp) < 5000 ) {
8359                         // go back to find mode
8360                         aip->submode = SM_STEALTH_FIND;
8361                         aip->submode_start_time = Missiontime;
8362                         aip->submode_parm0 = SM_SF_AHEAD;
8363                 } else if ( /*(Missiontime - aip->submode_start_time) > i2f(30) || */ aip->submode_parm0 == SM_SS_DONE ) {
8364                         // set target objnum = -1
8365                         set_target_objnum(aip, -1);
8366
8367                         // set submode to attack
8368                         aip->submode = SM_ATTACK;
8369                         aip->submode_start_time = Missiontime;
8370                         aip->last_attack_time = Missiontime;
8371                 }
8372                 break;
8373
8374         case SM_ATTACK_FOREVER: //      Engines blown, just attack.
8375                 break;
8376
8377         default:
8378                 //Int3();
8379                 aip->submode = SM_ATTACK;
8380                 aip->last_attack_time = Missiontime;
8381
8382                 aip->submode_start_time = Missiontime;
8383         }
8384
8385         //
8386         //      Maybe fire primary weapon and update time_enemy_in_range
8387         //
8388         //nprintf(("AI", "time_enemy_in_range = %7.3f, dot = %7.3f\n", aip->time_enemy_in_range, dot_to_enemy));
8389
8390         if (aip->mode != AIM_EVADE) {
8391                 if (dot_to_enemy > 0.95f - 0.5f * En_objp->radius/max(1.0f, En_objp->radius + dist_to_enemy)) {
8392                         aip->time_enemy_in_range += flFrametime;
8393                         
8394                         //      Chance of hitting ship is based on dot product of firing ship's forward vector with vector to ship
8395                         //      and also the size of the target relative to distance to target.
8396                         if (dot_to_enemy > max(0.5f, 0.90f + aip->ai_accuracy/10.0f - En_objp->radius/max(1.0f,dist_to_enemy))) {
8397
8398                                 ship *temp_shipp;
8399                                 ship_weapon *tswp;
8400
8401                                 temp_shipp = &Ships[Pl_objp->instance];
8402                                 tswp = &temp_shipp->weapons;
8403                                 if ( tswp->num_primary_banks > 0 ) {
8404                                         float   scale;
8405                                         SDL_assert(tswp->current_primary_bank < tswp->num_primary_banks);
8406                                         weapon_info     *pwip = &Weapon_info[tswp->primary_bank_weapons[tswp->current_primary_bank]];
8407
8408                                         //      Less likely to fire if far away and moving.
8409                                         scale = pwip->max_speed/(En_objp->phys_info.speed + pwip->max_speed);
8410                                         if (scale > 0.6f)
8411                                                 scale = (scale - 0.6f) * 1.5f;
8412                                         else
8413                                                 scale = 0.0f;
8414                                         if (dist_to_enemy < pwip->max_speed * (1.0f + scale)) {
8415                                                 ai_fire_primary_weapon(Pl_objp);
8416                                         }
8417
8418                                         //      Don't fire secondaries at a protected ship.
8419                                         if (!(En_objp->flags & OF_PROTECTED)) {
8420                                                 ai_choose_secondary_weapon(Pl_objp, aip, En_objp);
8421                                                 int current_bank = tswp->current_secondary_bank;
8422                                                 weapon_info     *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8423
8424                                                 if (current_bank > -1) {
8425                                                         if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8426                                                                 if (timestamp_until(swp->next_secondary_fire_stamp[current_bank]) > swip->fire_wait*1000.0f) {
8427                                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (swip->fire_wait*1000.0f));
8428                                                                 }
8429                                                         }
8430
8431                                                         if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
8432                                                                 if (tswp->current_secondary_bank >= 0) {
8433                                                                         weapon_info     *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8434                                                                         float firing_range;
8435                                                                         
8436                                                                         if (swip->wi_flags & WIF_BOMB)
8437                                                                                 firing_range = swip->max_speed * swip->lifetime * 0.75f;
8438                                                                         else
8439                                                                                 firing_range = swip->max_speed * swip->lifetime * (Game_skill_level + 1 + aip->ai_class/2)/NUM_SKILL_LEVELS;
8440
8441                                                                         // reduce firing range in nebula
8442                                                                         extern int Nebula_sec_range;
8443                                                                         if ((The_mission.flags & MISSION_FLAG_FULLNEB) && Nebula_sec_range) {
8444                                                                                 firing_range *= 0.8f;
8445                                                                         }
8446
8447                                                                         //      If firing a spawn weapon, distance doesn't matter.
8448                                                                         int     spawn_fire = 0;
8449
8450                                                                         if (swip->wi_flags & WIF_SPAWN) {
8451                                                                                 int     count;
8452
8453                                                                                 count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(Pl_objp)), &Pl_objp->pos, 1000.0f);
8454
8455                                                                                 if (count > 3)
8456                                                                                         spawn_fire = 1;
8457                                                                                 else if (count >= 1) {
8458                                                                                         float hull_percent = Pl_objp->hull_strength/sip->initial_hull_strength;
8459
8460                                                                                         if (hull_percent < 0.01f)
8461                                                                                                 hull_percent = 0.01f;
8462
8463                                                                                         if (frand() < 0.25f/(30.0f*hull_percent) * count)       //      With timestamp below, this means could fire in 30 seconds if one enemy.
8464                                                                                                 spawn_fire = 1;
8465                                                                                 }
8466                                                                         }
8467
8468                                                                         if (spawn_fire || (dist_to_enemy < firing_range)) {
8469                                                                                 if (ai_fire_secondary_weapon(Pl_objp)) {
8470                                                                                         //      Only if weapon was fired do we specify time until next fire.  If not fired, done in ai_fire_secondary...
8471                                                                                         float t;
8472                                                                                         
8473                                                                                         if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8474                                                                                                 t = swip->fire_wait;
8475                                                                                         } else {
8476                                                                                                 t = set_secondary_fire_delay(aip, temp_shipp, swip);
8477                                                                                         }
8478                                                                                         //nprintf(("AI", "Next secondary to be fired in %7.3f seconds.\n", t));
8479                                                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
8480                                                                                 }
8481                                                                         } else {
8482                                                                                 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
8483                                                                         }
8484                                                                 }
8485                                                         }
8486                                                 }
8487                                         }
8488                                 }
8489                         }
8490                 } else {
8491                         aip->time_enemy_in_range *= (1.0f - flFrametime);
8492                 }
8493         } else
8494                 aip->time_enemy_in_range *= (1.0f - flFrametime);
8495
8496 }
8497
8498 //      Make the object *objp move so that the point *dp on the object moves towards the point *vp
8499 //      Return distance.
8500 void dock_move_towards_point(object *objp, vector *dp, vector *vp, float speed_scale, float other_obj_speed = 0.0f)
8501 {
8502         physics_info    *pi = &objp->phys_info;
8503         float                           dist;                   //      dist to goal
8504         vector                  v2g;                    //      vector to goal
8505         vector                  abs_pnt;                //      location of dock point, ie objp->pos + db
8506
8507         if (dp == NULL)
8508                 abs_pnt = objp->pos;
8509         else
8510                 vm_vec_add(&abs_pnt, &objp->pos, dp);
8511
8512         dist = vm_vec_dist_quick(vp, &abs_pnt);
8513         if (dist > 0.0f) {
8514                 float   speed;
8515
8516                 dist = vm_vec_normalized_dir(&v2g, vp, &abs_pnt);
8517                 speed = fl_sqrt(dist) * speed_scale;
8518                 if (other_obj_speed < MAX_REPAIR_SPEED*0.75f)
8519                         speed += other_obj_speed;
8520                 else
8521                         speed += MAX_REPAIR_SPEED*0.75f;
8522
8523                 vm_vec_copy_scale(&pi->desired_vel, &v2g, speed);
8524         } else
8525                 vm_vec_zero(&pi->desired_vel);
8526 }
8527
8528 //      Set the orientation in the global reference frame for an object to attain
8529 //      to dock with another object.
8530 //      *dom            resultant global matrix
8531 //      *db_dest        pointer to destination docking bay information
8532 //      *db_src pointer to source docking bay information
8533 //      *dorient        pointer to global orientation of docking bay (ie, the dockee object's orient)
8534 //      *sorient        pointer to global orientation of docker
8535 void set_goal_dock_orient(matrix *dom, dock_bay *db_dest, dock_bay *db_src, matrix *dorient, matrix *sorient)
8536 {
8537         vector  fvec, uvec;
8538         matrix  m1, m2, m3;
8539
8540         //      Compute the global orientation of the docker's (dest) docking bay.
8541         fvec = db_dest->norm[0];
8542         vm_vec_negate(&fvec);
8543
8544         vm_vec_normalized_dir(&uvec, &db_dest->pnt[1], &db_dest->pnt[0]);
8545         vm_vector_2_matrix(&m1, &fvec, &uvec, NULL);
8546
8547         vm_matrix_x_matrix(&m3, dorient, &m1);
8548
8549         //      Compute the matrix given by the source docking bay.
8550         //      Pre-multiply the orientation of the source object (sorient) by the transpose
8551         //      of the docking bay's orientation, ie unrotate the source object's matrix.
8552         fvec = db_src->norm[0];
8553         vm_vec_normalized_dir(&uvec, &db_src->pnt[1], &db_src->pnt[0]);
8554         vm_vector_2_matrix(&m2, &fvec, &uvec, NULL);
8555         vm_transpose(&m2);
8556
8557         vm_matrix_x_matrix(dom, &m3, &m2);
8558 }
8559
8560 #define DOCK_BACKUP_RETURN_VAL  99999.9f
8561
8562 //      Make objp dock with dobjp
8563 //      Returns distance to goal, defined as distance between corresponding dock points, plus 10.0f * rotational velocity vector (DOA_DOCK only)
8564 //      DOA_APPROACH    means   approach point aip->path_cur
8565 //      DOA_DOCK                        means dock
8566 //      DOA_UNDOCK_1    means undock, moving to point nearest dock bay
8567 //      DOA_UNDOCK_2    means undock, moving to point nearest dock bay and facing away from ship
8568 //      DOA_DOCK_STAY   means rigidly maintain position in dock bay.
8569 float dock_orient_and_approach(object *objp, object *dobjp, int dock_mode)
8570 {
8571         ship_info       *sip0, *sip1;
8572         polymodel       *pm0, *pm1;
8573         ai_info         *aip;
8574         matrix          dom, nm;
8575         vector          goal_point, docker_point;
8576         float                   fdist = UNINITIALIZED_VALUE;
8577         int                     docker_index, dockee_index;             // index into docking_bays[] array for objects docking
8578                                                                                                                                 // docker is Pl_objp -- dockee is dobjp
8579         aip = &Ai_info[Ships[objp->instance].ai_index];
8580
8581         //      If dockee has moved much, then path will be recreated.
8582         //      Might need to change state if moved too far.
8583         if ((dock_mode != DOA_DOCK_STAY) && (dock_mode != DOA_DOCK)) {
8584                 if (maybe_recreate_path(objp, &Ai_info[Ships[objp->instance].ai_index], 0) > 5.0f) {
8585 /*                      if (dock_mode == DOA_APPROACH) {
8586                                 return DOCK_BACKUP_RETURN_VAL;
8587                         } else if (dock_mode == DOA_DOCK) {
8588                                 return DOCK_BACKUP_RETURN_VAL;          
8589                         }
8590 */              }
8591         }
8592
8593         objp->phys_info.forward_thrust = 0.0f;          //      Kill thrust so we don't have a sputtering thruster.
8594
8595         sip0 = &Ship_info[Ships[objp->instance].ship_info_index];
8596         sip1 = &Ship_info[Ships[dobjp->instance].ship_info_index];
8597         pm0 = model_get( sip0->modelnum );
8598         pm1 = model_get( sip1->modelnum );
8599
8600         docker_index = aip->dock_index;
8601         dockee_index = aip->dockee_index;
8602
8603         SDL_assert( docker_index >= 0 );
8604         SDL_assert( dockee_index >= 0 );
8605
8606         SDL_assert(pm0->docking_bays[docker_index].num_slots == 2);
8607         SDL_assert(pm1->docking_bays[dockee_index].num_slots == 2);
8608
8609         float speed_scale = 1.0f;
8610         if (sip0->flags & SIF_SUPPORT) {
8611                 speed_scale = 3.0f;
8612         }
8613
8614         switch (dock_mode) {
8615         case DOA_APPROACH:
8616                 {
8617                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8618                         return 9999.9f;
8619                 }
8620                 
8621                 //      Compute the desired global orientation matrix for the docker's station.
8622                 //      That is, the normal vector of the docking station must be the same as the
8623                 //      forward vector and the vector between its two points must be the uvec.
8624                 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8625
8626                 //      Compute new orientation matrix and update rotational velocity.
8627                 vector  w_in, w_out, vel_limit, acc_limit;
8628                 float           tdist, mdist, ss1;
8629
8630                 w_in = objp->phys_info.rotvel;
8631                 vel_limit = objp->phys_info.max_rotvel;
8632                 vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8633                 
8634                 if (sip0->flags & SIF_SUPPORT)
8635                         vm_vec_scale(&acc_limit, 2.0f);
8636
8637                 // 1 at end of line prevent overshoot
8638                 vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit, 1);
8639                 objp->phys_info.rotvel = w_out;
8640                 objp->orient = nm;
8641
8642                 //      Translate towards goal and note distance to goal.
8643                 goal_point = Path_points[aip->path_cur].pos;
8644                 mdist = ai_matrix_dist(&objp->orient, &dom);
8645                 tdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8646
8647                 //      If translation is badly lagging rotation, speed up translation.
8648                 if (mdist > 0.1f) {
8649                         ss1 = tdist/(10.0f * mdist);
8650                         if (ss1 > 2.0f)
8651                                 ss1 = 2.0f;
8652                 } else
8653                         ss1 = 2.0f;
8654
8655                 // nprintf(("AI", "speed scale = %7.3f\n", ss1));
8656                 speed_scale *= 1.0f + ss1;
8657
8658                 dock_move_towards_point(objp, NULL, &goal_point, speed_scale, dobjp->phys_info.speed);
8659
8660                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8661
8662                 //      Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8663                 // nprintf(("AI", "matrix dist = %7.3f, threshold = %7.3f\n", mdist, 2*flFrametime));
8664                 fdist += 2.0f * mdist;
8665
8666                 break;
8667         }
8668         case DOA_DOCK:
8669                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8670                         return 9999.9f;
8671                 }
8672         case DOA_DOCK_STAY:
8673                 //      Compute the desired global orientation matrix for the docker's station.
8674                 //      That is, the normal vector of the docking station must be the same as the
8675                 //      forward vector and the vector between its two points must be the uvec.
8676                 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8677
8678                 //      Compute distance between dock bay points.
8679                 vector  db0, db1, db2, db3;
8680
8681                 vm_vec_unrotate(&db0, &pm0->docking_bays[docker_index].pnt[0], &objp->orient);
8682                 vm_vec_add2(&db0, &objp->pos);
8683
8684                 vm_vec_unrotate(&db1, &pm0->docking_bays[docker_index].pnt[1], &objp->orient);
8685                 vm_vec_add2(&db1, &objp->pos);
8686
8687                 vm_vec_unrotate(&db2, &pm1->docking_bays[dockee_index].pnt[0], &dobjp->orient);
8688                 vm_vec_add2(&db2, &dobjp->pos);
8689
8690                 vm_vec_unrotate(&db3, &pm1->docking_bays[dockee_index].pnt[1], &dobjp->orient);
8691                 vm_vec_add2(&db3, &dobjp->pos);
8692
8693                 vm_vec_avg(&goal_point, &db2, &db3);
8694
8695                 vm_vec_avg(&docker_point, &db0, &db1);
8696                 vm_vec_sub2(&docker_point, &objp->pos);
8697
8698                 if (dock_mode == DOA_DOCK) {
8699                         vector  t1, t2;
8700                         vector  w_in, w_out, vel_limit, acc_limit;
8701
8702                         fdist = vm_vec_dist_quick(vm_vec_avg(&t1, &db0, &db1), vm_vec_avg(&t2, &db2, &db3));
8703
8704                         //      Compute new orientation matrix and update rotational velocity.
8705                         w_in = objp->phys_info.rotvel;
8706                         vel_limit = objp->phys_info.max_rotvel;
8707                         vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8708
8709                         if (sip0->flags & SIF_SUPPORT)
8710                                 vm_vec_scale(&acc_limit, 2.0f);
8711
8712                         vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit);
8713                         objp->phys_info.rotvel = w_out;
8714                         objp->orient = nm;
8715
8716                         //      Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8717                         fdist += 10.0f * vm_vec_mag_quick(&w_out);
8718
8719                         dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale, dobjp->phys_info.speed);
8720                 } else {
8721                         SDL_assert(dock_mode == DOA_DOCK_STAY);
8722                         objp->orient = dom;
8723                         vector  temp;
8724                         vm_vec_sub(&temp, &goal_point, &docker_point);
8725                         vm_vec_sub(&objp->pos, &goal_point, &docker_point);
8726                 }
8727
8728                 break;
8729         case DOA_UNDOCK_1: {
8730                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8731                         return 9999.9f;
8732                 }
8733
8734                 //      Undocking.
8735                 //      Move to point on dock path nearest to dock station.
8736                 SDL_assert(aip->path_length >= 2);
8737                 goal_point = Path_points[aip->path_start + aip->path_length-2].pos;
8738
8739                 vm_vec_zero(&docker_point);
8740                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8741
8742                 dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale);
8743
8744                 break;
8745                           }
8746
8747         case DOA_UNDOCK_2: {
8748                 //      Undocking.
8749                 //      Move to point on dock path nearest to dock station and orient away from big ship.
8750                 int             desired_index;
8751
8752                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8753                         return 9999.9f;
8754                 }
8755
8756                 SDL_assert(aip->path_length >= 2);
8757 //              if (aip->path_length >= 3)
8758 //                      desired_index = aip->path_length-3;
8759 //              else
8760                         desired_index = aip->path_length-2;
8761
8762                 goal_point = Path_points[aip->path_start + desired_index].pos;
8763
8764                 dock_move_towards_point(objp, NULL, &goal_point, speed_scale);
8765
8766                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8767                 break;
8768                           }
8769         case DOA_UNDOCK_3: {
8770                 float           dist, goal_dist;
8771                 vector  away_vec;
8772
8773                 goal_dist = objp->radius + dobjp->radius + 25.0f;
8774
8775                 dist = vm_vec_normalized_dir(&away_vec, &objp->pos, &dobjp->pos);
8776                 vm_vec_scale_add(&goal_point, &dobjp->pos, &away_vec, goal_dist);
8777                 if (vm_vec_dist_quick(&goal_point, &dobjp->pos) < vm_vec_dist_quick(&objp->pos, &dobjp->pos))
8778                         fdist = 0.0f;
8779                 else {
8780                         float   dot, accel;
8781                         float turn_time = Ship_info[Ships[objp->instance].ship_info_index].srotation_time;
8782                         ai_turn_towards_vector(&goal_point, objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
8783
8784                         dot = vm_vec_dot(&objp->orient.v.fvec, &away_vec);
8785                         accel = 0.1f;
8786                         if (dot > accel)
8787                                 accel = dot;
8788                         if (dist > goal_dist/2)
8789                                 accel *= 1.2f - 0.5f*goal_dist/dist;
8790
8791                         accelerate_ship(aip, accel);
8792                         fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8793                 }
8794
8795                 break;
8796                                                          }
8797         }
8798
8799 #ifndef NDEBUG
8800         //      For debug purposes, compute global orientation of both dock vectors and show
8801         //      how close they are.
8802         vector  d0, d1;
8803
8804         vm_vec_unrotate(&d0, &pm0->docking_bays[docker_index].norm[0], &objp->orient);
8805         vm_vec_unrotate(&d1, &pm1->docking_bays[dockee_index].norm[0], &dobjp->orient);
8806
8807         //nprintf(("AI", "or/app: dist = %7.3f/%7.3f, dot = %7.3f, global dot = %7.3f\n", 
8808         //      vm_vec_dist_quick(&goal_point, &objp->pos), fdist,
8809         //      vm_vec_dot(&objp->orient.v.fvec, &dom.v.fvec), 
8810         //      vm_vec_dot(&d0, &d1)));
8811 #endif
8812
8813         // -- Note, A lot of callers don't care about fdist, so OK to return ERROR value: SDL_assert(fdist != UNINITIALIZED_VALUE);
8814         return fdist;
8815
8816 }
8817
8818 void debug_find_guard_object()
8819 {
8820         ship                    *shipp = &Ships[Pl_objp->instance];     
8821         object          *objp;
8822
8823         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
8824                 if ((Pl_objp != objp) && (objp->type == OBJ_SHIP)) {
8825                         if (objp->instance != -1) {
8826                                 if (Ships[objp->instance].team == shipp->team)  {
8827                                         // nprintf(("AI", "Setting guard object for %s to %s\n", shipp->ship_name, Ships[objp->instance].ship_name));
8828                                         ai_set_guard_object(Pl_objp, objp);
8829                                 }
8830                         }
8831                 }
8832         }
8833
8834 }
8835
8836 //      Given an object number, return the number of ships attacking it.
8837 int num_ships_attacking(int objnum)
8838 {
8839         object  *objp;
8840         ship_obj        *so;
8841         int             count = 0;
8842
8843         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8844                 objp = &Objects[so->objnum];
8845                 if (objp->instance != -1) {
8846                         ai_info *aip;
8847                         aip = &Ai_info[Ships[objp->instance].ai_index];
8848
8849                         if ((aip->mode == AIM_CHASE) && (aip->target_objnum == objnum))
8850                                 if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team)
8851                                         count++;
8852                 }
8853         }
8854
8855         return count;
8856 }
8857
8858 //      For all objects attacking object #objnum, remove the one that is farthest away.
8859 //      Do this by resuming previous behavior, if any.  If not, set target_objnum to -1.
8860 void remove_farthest_attacker(int objnum)
8861 {
8862         object  *objp, *objp2, *farthest_objp;
8863         ship_obj        *so;
8864         float           farthest_dist;
8865
8866         objp2 = &Objects[objnum];
8867
8868         farthest_dist = 9999999.9f;
8869         farthest_objp = NULL;
8870
8871         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8872                 objp = &Objects[so->objnum];
8873                 if ( !(objp->flags & OF_PLAYER_SHIP)) {
8874                         if (objp->instance != -1) {
8875                                 ai_info *aip2;
8876
8877                                 aip2 = &Ai_info[Ships[objp->instance].ai_index];
8878
8879                                 if ((aip2->mode == AIM_CHASE) && (aip2->target_objnum == objnum)) {
8880                                         if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team) {
8881                                                 float   dist;
8882
8883                                                 dist = vm_vec_dist_quick(&objp->pos, &objp2->pos);
8884                                                 if (dist < farthest_dist) {
8885                                                         farthest_dist = dist;
8886                                                         farthest_objp = objp;
8887                                                 }
8888                                         }
8889                                 }
8890                         }
8891                 }
8892         }
8893
8894         if (farthest_objp != NULL) {
8895                 ai_info *aip;
8896                 SDL_assert(farthest_objp->type == OBJ_SHIP);
8897                 SDL_assert((farthest_objp->instance > -1) && (farthest_objp->instance < MAX_SHIPS));
8898                 SDL_assert(Ships[farthest_objp->instance].ai_index > -1);
8899
8900                 aip = &Ai_info[Ships[farthest_objp->instance].ai_index];
8901
8902                 if (!maybe_resume_previous_mode(Pl_objp, aip)) {
8903                         //      If already ignoring something under player's orders, don't ignore current target.
8904                         if ((aip->ignore_objnum == UNUSED_OBJNUM) || (aip->ai_flags & AIF_TEMPORARY_IGNORE)) {
8905                                 aip->ignore_objnum = aip->target_objnum;
8906                                 aip->ignore_signature = Objects[aip->target_objnum].signature;
8907                                 aip->ai_flags |= AIF_TEMPORARY_IGNORE;
8908                                 aip->ignore_expire_timestamp = timestamp(((myrand() % 10) + 20) * 1000);        //      OK to attack again in 20 to 24 seconds.
8909                         }
8910                         aip->target_objnum = -1;
8911                         ai_do_default_behavior(farthest_objp);
8912                 }
8913         }
8914 }
8915
8916 // Maybe limit the number of attackers on attack_objnum.  For now, only limit attackers
8917 // in attacked_objnum is the player
8918 // input:       attacked_objnum =>              object index for ship we want to limit attacks on
8919 //
8920 //      exit:                   1       =>      num attackers exceeds maximum, abort
8921 //                                      0       =>      removed the farthest attacker
8922 //                                      -1      =>      nothing was done
8923 int ai_maybe_limit_attackers(int attacked_objnum)
8924 {
8925         int rval=-1;
8926
8927         // limit the number of ships attacking the _player_ only
8928 //      if ( attacked_objnum == OBJ_INDEX(Player_obj) ) {
8929         if ( Objects[attacked_objnum].flags & OF_PLAYER_SHIP) {
8930                 int num_attacking;
8931                 num_attacking = num_ships_attacking(attacked_objnum);
8932
8933                 if (num_attacking == Skill_level_max_attackers[Game_skill_level]) {
8934                         remove_farthest_attacker(attacked_objnum);
8935                         rval=0;
8936                 } else if (num_attacking > Skill_level_max_attackers[Game_skill_level]) {
8937                         rval=1;
8938                 }
8939                 //nprintf(("AI", "Num attacking player = %i\n", num_attacking));
8940         }
8941
8942         return rval;
8943 }
8944
8945 //      Object being guarded by object *guard_objp was hit by object *hitter_objp
8946 void guard_object_was_hit(object *guard_objp, object *hitter_objp)
8947 {
8948         int             hitter_objnum;
8949         ai_info *aip;
8950
8951         aip = &Ai_info[Ships[guard_objp->instance].ai_index];
8952
8953         if (guard_objp == hitter_objp) {
8954                 // Int3();      //      Bogus!  Who tried to get me to attack myself!  Trace out and fix!
8955                 return;
8956         }
8957
8958         if (guard_objp->type == OBJ_GHOST || hitter_objp->type == OBJ_GHOST)
8959                 return;
8960
8961         if (aip->ai_flags & AIF_NO_DYNAMIC)     //      Not allowed to pursue dynamic goals.  So, why are we guarding?
8962                 return;
8963
8964         SDL_assert( (hitter_objp->type == OBJ_SHIP) || (hitter_objp->type == OBJ_ASTEROID) || (hitter_objp->type == OBJ_WEAPON) );
8965
8966         hitter_objnum = OBJ_INDEX(hitter_objp);
8967
8968         if ( hitter_objp->type == OBJ_SHIP ) {
8969                 //      If the hitter object is the ignore object, don't attack it.
8970                 if (is_ignore_object(aip, hitter_objp-Objects))
8971                         return;
8972
8973                 //      If hitter is on same team as me, don't attack him.
8974                 if (Ships[guard_objp->instance].team == Ships[hitter_objp->instance].team)
8975                         return;
8976
8977                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
8978                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
8979                         return;
8980                 }
8981
8982                 // dont attack if you can't see him
8983                 if ( awacs_get_level(hitter_objp, &Ships[aip->shipnum], 1) < 1 ) {
8984                         // if he's a stealth and visible, but not targetable, ok to attack.
8985                         if ( is_object_stealth_ship(hitter_objp) ) {
8986                                 if ( ai_is_stealth_visible(guard_objp, hitter_objp) != STEALTH_VISIBLE ) {
8987                                         return;
8988                                 }
8989                         }
8990                 }
8991         }
8992
8993         if (aip->target_objnum == -1) {
8994                 aip->ok_to_target_timestamp = timestamp(0);
8995         }
8996
8997         if ((aip->submode == AIS_GUARD_PATROL) || (aip->submode == AIS_GUARD_STATIC)) {
8998
8999                 if ( hitter_objp->type == OBJ_SHIP ) {
9000                         if (!(Ship_info[Ships[guard_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
9001                                 return;
9002                         }
9003
9004                         // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9005                         if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9006                                 return;
9007                         }
9008                 }
9009
9010                 if (aip->target_objnum != hitter_objnum) {
9011                         aip->aspect_locked_time = 0.0f;
9012                 }
9013
9014                 aip->ok_to_target_timestamp = timestamp(0);
9015
9016                 set_target_objnum(aip, hitter_objnum);
9017                 //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));
9018                 aip->previous_mode = AIM_GUARD;
9019                 aip->previous_submode = aip->submode;
9020                 aip->mode = AIM_CHASE;
9021                 aip->submode = SM_ATTACK;
9022                 aip->submode_start_time = Missiontime;
9023                 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9024         } else if (aip->previous_mode == AIM_GUARD) {
9025                 if (aip->target_objnum == -1) {
9026
9027                         if ( hitter_objp->type == OBJ_SHIP ) {
9028                                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9029                                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9030                                         return;
9031                                 }
9032                         }
9033
9034                         set_target_objnum(aip, hitter_objnum);
9035                 //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));
9036                         aip->mode = AIM_CHASE;
9037                         aip->submode = SM_ATTACK;
9038                         aip->submode_start_time = Missiontime;
9039                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9040                 } else {
9041                         int     num_attacking_cur, num_attacking_new;
9042
9043                         num_attacking_cur = num_ships_attacking(aip->target_objnum);
9044                         if (num_attacking_cur > 1) {
9045                                 num_attacking_new = num_ships_attacking(hitter_objnum);
9046
9047                                 if (num_attacking_new < num_attacking_cur) {
9048
9049                                         if ( hitter_objp->type == OBJ_SHIP ) {
9050                                                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9051                                                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9052                                                         return;
9053                                                 }
9054                                         }
9055                                         set_target_objnum(aip, hitter_objp-Objects);
9056                 //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));
9057                                         aip->mode = AIM_CHASE;
9058                                         aip->submode = SM_ATTACK;
9059                                         aip->submode_start_time = Missiontime;
9060                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9061                                 }
9062                         }
9063                 }
9064         }
9065 }
9066
9067 //      Ship object *hit_objp was hit by ship object *hitter_objp.
9068 //      See if anyone is guarding hit_objp and, if so, do something useful.
9069 void maybe_update_guard_object(object *hit_objp, object *hitter_objp)
9070 {
9071         object  *objp;
9072         ship_obj        *so;
9073
9074         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9075                 objp = &Objects[so->objnum];
9076                 if (objp->instance != -1) {
9077                         ai_info *aip;
9078                         aip = &Ai_info[Ships[objp->instance].ai_index];
9079
9080                         if ((aip->mode == AIM_GUARD) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
9081                                 if (aip->guard_objnum == hit_objp-Objects) {
9082                                         guard_object_was_hit(objp, hitter_objp);
9083                                 } else if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[hit_objp->instance].ai_index].wing)) {
9084                                         guard_object_was_hit(objp, hitter_objp);
9085                                 }
9086                         }
9087                 }
9088         }
9089 }
9090
9091 // Scan missile list looking for bombs homing on guarded_objp
9092 // return 1 if bomb is found (and targeted by guarding_objp), otherwise return 0
9093 int ai_guard_find_nearby_bomb(object *guarding_objp, object *guarded_objp)
9094 {       
9095         missile_obj     *mo;
9096         object          *bomb_objp, *closest_bomb_objp=NULL;
9097         float                   dist, dist_to_guarding_obj,closest_dist_to_guarding_obj=999999.0f;
9098         weapon          *wp;
9099         weapon_info     *wip;
9100
9101         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
9102                 SDL_assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
9103                 bomb_objp = &Objects[mo->objnum];
9104
9105                 wp = &Weapons[bomb_objp->instance];
9106                 wip = &Weapon_info[wp->weapon_info_index];
9107
9108                 if ( !(wip->wi_flags & WIF_BOMB) ) {
9109                         continue;
9110                 }
9111
9112                 if ( wp->homing_object != guarded_objp ) {
9113                         continue;
9114                 }
9115
9116                 dist = vm_vec_dist_quick(&bomb_objp->pos, &guarded_objp->pos);
9117
9118                 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9119                         dist_to_guarding_obj = vm_vec_dist_quick(&bomb_objp->pos, &guarding_objp->pos);
9120                         if ( dist_to_guarding_obj < closest_dist_to_guarding_obj ) {
9121                                 closest_dist_to_guarding_obj = dist_to_guarding_obj;
9122                                 closest_bomb_objp = bomb_objp;
9123                         }
9124                 }
9125         }
9126
9127         if ( closest_bomb_objp ) {
9128                 guard_object_was_hit(guarding_objp, closest_bomb_objp);
9129                 return 1;
9130         }
9131
9132         return 0;
9133 }
9134
9135 //      Scan enemy ships and see if one is near enough to guard object to be pursued.
9136 void ai_guard_find_nearby_ship(object *guarding_objp, object *guarded_objp)
9137 {
9138         ship            *guarding_shipp = &Ships[guarding_objp->instance];
9139         ai_info *guarding_aip = &Ai_info[guarding_shipp->ai_index];
9140         ship_obj        *so;
9141         object  *enemy_objp;
9142         float           dist;
9143
9144         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9145                 enemy_objp = &Objects[so->objnum];
9146
9147                 if (enemy_objp->instance < 0) {
9148                         continue;
9149                 }
9150
9151                 ship    *eshipp = &Ships[enemy_objp->instance];
9152
9153                 //      Don't attack a cargo container or other harmless ships
9154                 if (!(Ship_info[eshipp->ship_info_index].flags & SIF_HARMLESS)) {
9155                         if (guarding_shipp->team != eshipp->team)       {
9156                                 dist = vm_vec_dist_quick(&enemy_objp->pos, &guarded_objp->pos);
9157                                 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9158                                         guard_object_was_hit(guarding_objp, enemy_objp);
9159                                 } else if ((dist < 3000.0f) && (Ai_info[eshipp->ai_index].target_objnum == guarding_aip->guard_objnum)) {
9160                                         //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));
9161                                         guard_object_was_hit(guarding_objp, enemy_objp);
9162                                 }
9163                         }
9164                 }
9165         }
9166 }
9167
9168 // Scan for nearby asteroids.  Favor asteroids which have their collide_objnum set to that of the
9169 // guarded ship.  Also, favor asteroids that are closer to the guarding ship, since it looks cooler
9170 // when a ship blows up an asteroid then goes after the pieces that break off.
9171 void ai_guard_find_nearby_asteroid(object *guarding_objp, object *guarded_objp)
9172 {       
9173         float           dist;
9174
9175         object  *closest_asteroid_objp=NULL, *danger_asteroid_objp=NULL, *asteroid_objp;
9176         float           dist_to_self, closest_danger_asteroid_dist=999999.0f, closest_asteroid_dist=999999.0f;
9177
9178         for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp != END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) {
9179                 if ( asteroid_objp->type == OBJ_ASTEROID ) {
9180                         // Attack asteroid if near guarded ship
9181                         dist = vm_vec_dist_quick(&asteroid_objp->pos, &guarded_objp->pos);
9182                         if ( dist < (MAX_GUARD_DIST + guarded_objp->radius)*2) {
9183                                 dist_to_self = vm_vec_dist_quick(&asteroid_objp->pos, &guarding_objp->pos);
9184                                 if ( OBJ_INDEX(guarded_objp) == asteroid_collide_objnum(asteroid_objp) ) {
9185                                         if( dist_to_self < closest_danger_asteroid_dist ) {
9186                                                 danger_asteroid_objp=asteroid_objp;
9187                                                 closest_danger_asteroid_dist=dist_to_self;
9188                                         }
9189                                 } 
9190                                 if ( dist_to_self < closest_asteroid_dist ) {
9191                                         // only attack if moving slower than own max speed
9192                                         if ( vm_vec_mag_quick(&asteroid_objp->phys_info.vel) < guarding_objp->phys_info.max_vel.xyz.z ) {
9193                                                 closest_asteroid_dist = dist_to_self;
9194                                                 closest_asteroid_objp = asteroid_objp;
9195                                         }
9196                                 }
9197                         }
9198                 }
9199         }
9200
9201         if ( danger_asteroid_objp ) {
9202                 guard_object_was_hit(guarding_objp, danger_asteroid_objp);
9203         } else if ( closest_asteroid_objp ) {
9204                 guard_object_was_hit(guarding_objp, closest_asteroid_objp);
9205         }
9206 }
9207
9208 //      Scan potential harmful objects and see if one is near enough to guard object to be pursued.
9209 void ai_guard_find_nearby_object()
9210 {
9211         ship                    *shipp = &Ships[Pl_objp->instance];
9212         ai_info         *aip = &Ai_info[shipp->ai_index];
9213         object          *guardobjp;
9214         int                     bomb_found=0;
9215
9216         guardobjp = &Objects[aip->guard_objnum];
9217         
9218         // highest priority is a bomb fired on guarded ship
9219         bomb_found = ai_guard_find_nearby_bomb(Pl_objp, guardobjp);
9220
9221         if ( !bomb_found ) {
9222                 // check for ships if there are no bombs fired at guarded ship
9223                 ai_guard_find_nearby_ship(Pl_objp, guardobjp);
9224
9225                 // if not attacking anything, go for asteroid close to guarded ship
9226                 if ( (aip->target_objnum == -1) && asteroid_count() ) {
9227                         ai_guard_find_nearby_asteroid(Pl_objp, guardobjp);
9228                 }
9229         }
9230 }
9231
9232 // gets closest point on extended axis of cylinder, r_vec, and radius of cylinder
9233 // returns z of axis_point in cyl_objp reference frame
9234 float get_cylinder_points(object *other_objp, object *cyl_objp, vector *axis_pt, vector *r_vec, float *radius)
9235 {
9236         SDL_assert(other_objp->type == OBJ_SHIP);
9237         SDL_assert(cyl_objp->type == OBJ_SHIP);
9238
9239         // get radius of cylinder
9240         polymodel *pm = model_get(Ships[cyl_objp->instance].modelnum);
9241         float tempx, tempy;
9242         tempx = max(-pm->mins.xyz.x, pm->maxs.xyz.x);
9243         tempy = max(-pm->mins.xyz.y, pm->maxs.xyz.y);
9244         *radius = max(tempx, tempy);
9245
9246         // get vec from cylinder to other_obj
9247         vector r_sph;
9248         vm_vec_sub(&r_sph, &other_objp->pos, &cyl_objp->pos);
9249
9250         // get point on axis and on cylinder
9251         // extended_cylinder_z is along extended cylinder
9252         // cylinder_z is capped within cylinder
9253         float extended_cylinder_z = vm_vec_dotprod(&r_sph, &cyl_objp->orient.v.fvec);
9254
9255         // get pt on axis of extended cylinder
9256         vm_vec_scale_add(axis_pt, &cyl_objp->pos, &cyl_objp->orient.v.fvec, extended_cylinder_z);
9257
9258         // get r_vec (pos - axis_pt) normalized
9259         vm_vec_normalized_dir(r_vec, &other_objp->pos, axis_pt);
9260
9261         return extended_cylinder_z;
9262 }
9263
9264 // handler for guard behavior when guarding BIG ships
9265 //      When someone has attacked guarded ship, then attack that ship.
9266 // To attack another ship, switch out of guard mode into chase mode.
9267 void ai_big_guard()
9268 {
9269         
9270         ship                    *shipp = &Ships[Pl_objp->instance];
9271         ai_info         *aip = &Ai_info[shipp->ai_index];
9272         object          *guard_objp;
9273
9274         // sanity checks already done in ai_guard()
9275         guard_objp = &Objects[aip->guard_objnum];
9276
9277         switch (aip->submode) {
9278         case AIS_GUARD_STATIC:
9279         case AIS_GUARD_PATROL:
9280                 {
9281                 vector axis_pt, r_vec, theta_vec;
9282                 float radius, extended_z;
9283
9284                 // get random [0 to 1] based on OBJNUM
9285                 float objval = static_randf(Pl_objp-Objects);
9286
9287                 // get position relative to cylinder of guard_objp              
9288                 extended_z = get_cylinder_points(Pl_objp, guard_objp, &axis_pt, &r_vec, &radius);
9289                 vm_vec_crossprod(&theta_vec, &guard_objp->orient.v.fvec, &r_vec);
9290
9291                 // half ships circle each way
9292                 if (objval > 0.5f) {
9293                         vm_vec_negate(&theta_vec);
9294                 }
9295
9296                 float min_guard_dist = radius + Pl_objp->radius + 50.0f;
9297                 float desired_guard_dist = min_guard_dist + 0.5f * ((1.0f + objval) * MAX_GUARD_DIST);
9298                 float max_guard_dist =     min_guard_dist + 1.0f * ((1.0f + objval) * MAX_GUARD_DIST);
9299
9300                 // get z extents
9301                 float min_z, max_z, length;
9302                 polymodel *pm = model_get(Ships[guard_objp->instance].modelnum);
9303                 min_z = pm->mins.xyz.z;
9304                 max_z = pm->maxs.xyz.z;
9305                 length = max_z - min_z;
9306
9307                 // get desired z
9308                 // how often to choose new desired_z
9309                 // 1*(64) sec < 2000, 2*(64) < 2-4000 3*(64) > 4-8000, etc (Missiontime >> 22 is 64 sec intervals)
9310                 int time_choose = int(floor(log(length * 0.001) / log(2.0)));
9311                 float desired_z = min_z + length * static_randf( (Pl_objp-Objects) ^ (Missiontime >> (22 + time_choose)) );
9312
9313                 // get r from guard_ship
9314                 float cur_guard_rad = vm_vec_dist(&Pl_objp->pos, &axis_pt);
9315
9316                 // is ship within extents of cylinder of ship it is guarding
9317                 int inside = (extended_z > min_z) && (extended_z < min_z + length);
9318
9319                 vector goal_pt;
9320                 // maybe go into orbit mode
9321                 if (cur_guard_rad < max_guard_dist) {
9322                         if ( cur_guard_rad > min_guard_dist ) {
9323                                 if (inside) {
9324                                         // orbit
9325                                         vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, desired_guard_dist);
9326                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9327                                 } else {
9328                                         // move to where I can orbit
9329                                         if (extended_z < min_z) {
9330                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, min_z);
9331                                         } else {
9332                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, max_z);
9333                                         }
9334                                         vm_vec_scale_add2(&goal_pt, &r_vec, desired_guard_dist);
9335                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9336                                 }
9337                         } else {
9338                                 // too close for orbit mode
9339                                 if (inside) {
9340                                         // inside (fly straight out and return circle)
9341                                         vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, max_guard_dist);
9342                                 } else {
9343                                         // outside (fly to edge and circle)
9344                                         if (extended_z < min_z) {
9345                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, min_z);
9346                                         } else {
9347                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, max_z);
9348                                         }
9349                                         vm_vec_scale_add2(&goal_pt, &r_vec, max_guard_dist);
9350                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9351                                 }
9352                         }
9353
9354                         if (Pl_objp->phys_info.fspeed > 0) {
9355                                 // modify goal_pt to take account moving guard objp
9356                                 float dist = vm_vec_dist_quick(&Pl_objp->pos, &goal_pt);
9357                                 float time = dist / Pl_objp->phys_info.fspeed;
9358                                 vm_vec_scale_add2(&goal_pt, &guard_objp->phys_info.vel, time);
9359
9360                                 // now modify to move to desired z (at a max of 20 m/s)
9361                                 float delta_z = desired_z - extended_z;
9362                                 float v_z = delta_z * 0.2f;
9363                                 if (v_z < -20) {
9364                                         v_z = -20.0f;
9365                                 } else if (v_z > 20) {
9366                                         v_z = 20.0f;
9367                                 }
9368
9369                                 vm_vec_scale_add2(&goal_pt, &guard_objp->orient.v.fvec, v_z*time);
9370                         }
9371
9372                 } else {
9373                         // cast vector to center of guard_ship adjusted by desired_z
9374                         float delta_z = desired_z - extended_z;
9375                         vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, delta_z);
9376                 }
9377
9378                 // try not to bump into things along the way
9379                 if ( (cur_guard_rad > max_guard_dist) || (extended_z < min_z) || (extended_z > max_z) ) {
9380                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9381                                 return;
9382                         }
9383
9384                         if (avoid_player(Pl_objp, &goal_pt)) {
9385                                 return;
9386                         }
9387                 } else {
9388                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9389                                 return;
9390                         }
9391                 }
9392
9393                 // got the point, now let's go there
9394                 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);
9395 //              aip->goal_point = goal_pt;
9396                 accelerate_ship(aip, 1.0f);
9397
9398                 //      Periodically, scan for a nearby ship to attack.
9399                 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9400                         ai_guard_find_nearby_object();
9401                 }
9402                 }
9403                 break;
9404
9405         case AIS_GUARD_ATTACK:
9406                 //      The guarded ship has been attacked.  Do something useful!
9407                 ai_chase();
9408                 break;
9409
9410         default:
9411                 //Int3();       //      Illegal submode for Guard mode.
9412                 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9413                 aip->submode = AIS_GUARD_PATROL;
9414                 break;
9415         }
9416 }
9417
9418 //      Main handler for guard behavior.
9419 //      When someone has attacked guarded ship, then attack that ship.
9420 // To attack another ship, switch out of guard mode into chase mode.
9421 void ai_guard()
9422 {
9423         ship                    *shipp = &Ships[Pl_objp->instance];
9424         ai_info         *aip = &Ai_info[shipp->ai_index];
9425         object          *guard_objp;    
9426         float                   dist_to_guardobj;
9427         vector          vec_to_guardobj;
9428
9429         /*      //      Debug code, find an object to guard.
9430         int finding_guard_objnum = 0;   //      Debug code, to see if body of "if" below gets executed. 
9431         if (aip->guard_objnum == -1) {
9432                 finding_guard_objnum = 1;
9433                 debug_find_guard_object();
9434                 if (aip->guard_objnum == -1)
9435                         return;
9436         }
9437 */
9438         if (aip->guard_objnum == -1) {
9439                 aip->mode = AIM_NONE;
9440                 return;
9441         }
9442
9443         SDL_assert(aip->guard_objnum != -1);
9444
9445         guard_objp = &Objects[aip->guard_objnum];
9446
9447         if (guard_objp == Pl_objp) {
9448                 Int3();         //      This seems illegal.  Why is a ship guarding itself?
9449                 aip->guard_objnum = -1;
9450                 return;
9451         }
9452
9453         // check that I have someone to guard
9454         if (guard_objp->instance == -1) {
9455                 return;
9456         }
9457
9458         //      Not sure whether this should be impossible, or a reasonable cleanup condition.
9459         //      For now (3/31/97), it's getting trapped by an SDL_assert, so clean it up.
9460         if (guard_objp->type != OBJ_SHIP) {
9461                 aip->guard_objnum = -1;
9462                 return;
9463         }
9464
9465         // handler for gurad object with BIG radius
9466         if (guard_objp->radius > BIG_GUARD_RADIUS) {
9467                 ai_big_guard();
9468                 return;
9469         }
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
9486                 rel_vec = aip->guard_vec;
9487                 vm_vec_add(&goal_point, &guard_objp->pos, &rel_vec);
9488
9489                 vm_vec_normalized_dir(&v2g, &goal_point, &Pl_objp->pos);
9490                 dist_to_goal_point = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
9491                 dot_to_goal_point = vm_vec_dot(&v2g, &Pl_objp->orient.v.fvec);
9492                 accel_scale = (1.0f + dot_to_goal_point)/2.0f;
9493
9494                 //      If far away, get closer
9495                 if (dist_to_goal_point > MAX_GUARD_DIST + 1.5 * (Pl_objp->radius + guard_objp->radius)) {
9496                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 5.0f)) {
9497                                 return;
9498                         }
9499
9500                         if (avoid_player(Pl_objp, &goal_point)) {
9501                                 return;
9502                         }
9503
9504                         // quite far away, so try to go straight to 
9505                         compute_desired_rvec(&rvec, &goal_point, &Pl_objp->pos);
9506                         ai_turn_towards_vector(&goal_point, Pl_objp, flFrametime, Ship_info[shipp->ship_info_index].srotation_time, NULL, NULL, 0.0f, 0, &rvec);
9507
9508                         accelerate_ship(aip, accel_scale * (0.25f + dist_to_goal_point/700.0f));
9509                 } else {
9510                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 2.0f)) {
9511                                 return;
9512                         }
9513
9514                         // get max of guard_objp (1) normal speed (2) dock speed
9515                         float speed = guard_objp->phys_info.speed;
9516
9517                         if (guard_objp->type == OBJ_SHIP) {
9518                                 ai_info *guard_aip = &Ai_info[Ships[guard_objp->instance].ai_index];
9519
9520                                 if (guard_aip->dock_objnum != -1) {
9521                                         speed = max(speed, Objects[guard_aip->dock_objnum].phys_info.speed);
9522                                 }
9523                         }
9524                         
9525                         //      Deal with guarding a small object.
9526                         //      If going to guard_vec might cause a collision with guarded object, pick a new guard point.
9527                         if (vm_vec_dot(&v2g, &vec_to_guardobj) > 0.8f) {
9528                                 if (dist_to_guardobj < dist_to_goal_point) {
9529                                         ai_set_guard_vec(Pl_objp, guard_objp);  //      OK to return here.
9530                                         return;
9531                                 }
9532                         } 
9533
9534                         if (speed > 10.0f) {
9535                                 //      If goal ship is moving more than a tiny bit, don't orbit it, get near it.
9536                                 if (vm_vec_dist_quick(&goal_point, &Pl_objp->pos) > 40.0f) {
9537                                         if (vm_vec_dot(&Pl_objp->orient.v.fvec, &v2g) < 0.0f) {
9538                                                 //      Just slow down, don't turn.
9539                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point/10.0f);
9540                                         } else {
9541                                                 //      Goal point is in front.
9542
9543                                                 //      If close to goal point, don't change direction, just change speed.
9544                                                 if (dist_to_goal_point > Pl_objp->radius + 10.0f) {
9545                                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9546                                                 }
9547                                                 
9548                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + (dist_to_goal_point-40.0f)/20.0f);
9549                                         }
9550                                 } else {
9551                                         if (dot_to_goal_point > 0.8f) {
9552                                                 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9553                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + dist_to_goal_point*0.1f);
9554                                         } else {
9555                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point*0.1f - 1.0f);
9556                                         }
9557                                 }
9558                         // consider guard object STILL
9559                         } else if (guard_objp->radius < 50.0f) {
9560                                 if (dist_to_goal_point > 15.0f) {
9561                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9562                                         set_accel_for_target_speed(Pl_objp, (dist_to_goal_point-10.0f)/2.0f);
9563                                 } else if (Pl_objp->phys_info.speed < 1.0f) {
9564                                         turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9565                                 }
9566                                 //      It's a big ship
9567                         } else if (dist_to_guardobj > MAX_GUARD_DIST + Pl_objp->radius + guard_objp->radius) {
9568                                 //      Orbiting ship, too far away
9569                                 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.0f + objval/2) * guard_objp->radius);
9570                                 accelerate_ship(aip, (1.0f + dot)/2.0f);
9571                         } else if (dist_to_guardobj < Pl_objp->radius + guard_objp->radius) {
9572                                 //      Orbiting ship, got too close
9573                                 turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9574                                 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9575                                         change_acceleration(aip, 0.25f);
9576                                 else
9577                                         accelerate_ship(aip, 0.5f + objval/4.0f);
9578                         } else {
9579                                 //      Orbiting ship, about the right distance away.
9580                                 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.5f + objval/2.0f)*guard_objp->radius);
9581                                 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9582                                         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));
9583                                 else
9584                                         accelerate_ship(aip, 0.5f * (1.0f + dot) * (0.3f + objval/3.0f));
9585                         }
9586                 }
9587
9588                 //      Periodically, scan for a nearby ship to attack.
9589                 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9590                         ai_guard_find_nearby_object();
9591                 }
9592                 break;
9593
9594         case AIS_GUARD_ATTACK:
9595                 //      The guarded ship has been attacked.  Do something useful!
9596                 ai_chase();
9597
9598                 break;
9599         default:
9600                 //Int3();       //      Illegal submode for Guard mode.
9601                 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9602                 aip->submode = AIS_GUARD_PATROL;
9603                 break;
9604         }
9605
9606 }
9607
9608 // Return the object of the ship that the given object is docked
9609 // with.  Currently, we know a ship is docked when his ai_mode is AIM_DOCK,
9610 // and his submode is AIS_DOCK_3.  I suppose that this is likely to change though.
9611 // Also, the objnum that was is passed in may not be the object that actually
9612 // performed the docking maneuver.  This code will account for that case.
9613 object *ai_find_docked_object( object *docker )
9614 {
9615         ai_info *aip;
9616
9617         // we are trying to find the dockee of docker.  (Note that that these terms
9618         // are totally relative to what is passed in as a parameter.)
9619
9620         // first thing to attempt is to check and see if this object is docked with something.
9621         SDL_assert( docker->type == OBJ_SHIP );         // this had probably better be a ship!!!
9622         aip = &Ai_info[Ships[docker->instance].ai_index];
9623         if ( !(aip->ai_flags & AIF_DOCKED) )            // flag not set if not docked with anything
9624                 return NULL;
9625
9626         if ( aip->dock_objnum == -1 ) {
9627                 Int3();                                                                                 // mwa says this is wrong wrong wrong
9628                 ai_do_objects_undocked_stuff( docker, NULL );
9629                 return NULL;
9630         }
9631
9632         return &Objects[aip->dock_objnum];
9633
9634 }
9635
9636
9637 // define for the points subtracted from score for a rearm started on a player.
9638 #define REPAIR_PENALTY          50
9639
9640
9641 // function to clean up ai flags, variables, and other interesting information
9642 // for a ship that was getting repaired.  The how parameter is useful for multiplayer
9643 // only in that it tells us why the repaired ship is being cleaned up.
9644 void ai_do_objects_repairing_stuff( object *repaired_objp, object *repair_objp, int how )
9645 {
9646         ai_info *aip, *repair_aip;
9647         int             stamp = -1;
9648
9649         SDL_assert( repaired_objp->type == OBJ_SHIP);
9650         aip = &Ai_info[Ships[repaired_objp->instance].ai_index];
9651
9652         // multiplayer
9653         int p_index;
9654         p_index = -1;
9655         if(Game_mode & GM_MULTIPLAYER){
9656                 p_index = multi_find_player_by_object(repaired_objp);           
9657         }               
9658         else {          
9659                 if(repaired_objp == Player_obj){
9660                         p_index = Player_num;
9661                 }
9662         }
9663
9664         switch( how ) {
9665         case REPAIR_INFO_BEGIN:
9666                 aip->ai_flags |= AIF_BEING_REPAIRED;
9667                 aip->ai_flags &= ~AIF_AWAITING_REPAIR;
9668                 stamp = timestamp(-1);
9669
9670                 // if this is a player ship, then subtract the repair penalty from this player's score
9671                 if ( repaired_objp->flags & OF_PLAYER_SHIP ) {
9672                         if ( !(Game_mode & GM_MULTIPLAYER) ) {
9673                                 Player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());                    // subtract the penalty
9674                         } else {
9675                                 /*
9676                                 int pnum;
9677
9678                                 // multiplayer game -- find the player, then subtract the score
9679                                 pnum = multi_find_player_by_object( repaired_objp );
9680                                 if ( pnum != -1 ) {
9681                                         Net_players[pnum].player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());
9682
9683                                         // squad war
9684                                         multi_team_maybe_add_score(-(int)(REPAIR_PENALTY * scoring_get_scale_factor()), Net_players[pnum].p_info.team);
9685                                 } else {
9686                                         nprintf(("Network", "Couldn't find player for ship %s for repair penalty\n", Ships[repaired_objp->instance].ship_name));
9687                                 }
9688                                 */
9689                         }
9690                 }
9691                 break;
9692
9693         case REPAIR_INFO_BROKEN:
9694                 aip->ai_flags &= ~AIF_BEING_REPAIRED;
9695                 aip->ai_flags |= AIF_AWAITING_REPAIR;
9696                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9697                 break;
9698
9699         case REPAIR_INFO_END:
9700                 // when only awaiting repair, and the repair is ended, then set dock_objnum to -1.
9701                 if ( aip->ai_flags & AIF_AWAITING_REPAIR ){
9702                         aip->dock_objnum = -1;
9703                 }
9704                 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9705                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9706                 break;
9707
9708         case REPAIR_INFO_QUEUE:
9709                 aip->ai_flags |= AIF_AWAITING_REPAIR;
9710                 if ( aip == Player_ai ){
9711                         hud_support_view_start();
9712                 }
9713                 stamp = timestamp(-1);
9714                 break;
9715
9716         case REPAIR_INFO_ABORT:
9717         case REPAIR_INFO_KILLED:
9718                 // 5/4/98 -- MWA -- Need to set dock objnum to -1 to let code know this guy who was getting
9719                 // repaired (or queued for repair), isn't really going to be docked with anyone anymore.
9720                 aip->dock_objnum = -1;
9721                 aip->ai_flags &= ~AIF_DOCKED;
9722                 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9723                 if (repair_objp != NULL) {
9724                         repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9725                         repair_aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9726                 }               
9727
9728                 if ( p_index >= 0 ) {
9729                         hud_support_view_abort();
9730
9731                         // send appropriate message to player here
9732                         if ( how == REPAIR_INFO_KILLED ){
9733                                 message_send_builtin_to_player( MESSAGE_SUPPORT_KILLED, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9734                         } else {
9735                                 if ( repair_objp ){
9736                                         message_send_builtin_to_player( MESSAGE_REPAIR_ABORTED, &Ships[repair_objp->instance], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9737                                 }
9738                         }
9739                 }
9740
9741                 // add log entry if this is a player
9742                 if ( repaired_objp->flags & OF_PLAYER_SHIP ){
9743                         mission_log_add_entry(LOG_PLAYER_REARM_ABORT, Ships[repaired_objp->instance].ship_name, NULL);
9744                 }
9745
9746                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9747                 break;
9748
9749         case REPAIR_INFO_COMPLETE:
9750                 // clear the being repaired flag -- and 
9751                 if ( p_index >= 0 ) {
9752                         SDL_assert( repair_objp );
9753                         
9754                         hud_support_view_stop();                        
9755
9756                         message_send_builtin_to_player(MESSAGE_REPAIR_DONE, &Ships[repair_objp->instance], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, p_index, -1);
9757                 }
9758                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9759                 break;
9760
9761         case REPAIR_INFO_ONWAY:
9762                 // need to set the dock_signature so that clients in multiplayer games rearm correctly
9763                 SDL_assert( repair_objp );
9764                 aip->dock_signature = repair_objp->signature; 
9765                 aip->dock_objnum = OBJ_INDEX(repair_objp);
9766                 stamp = timestamp(-1);
9767                 break;
9768
9769         default:
9770                 Int3();                 // bogus type of repair info
9771         }
9772
9773         if (repair_objp){
9774                 Ai_info[Ships[repair_objp->instance].ai_index].warp_out_timestamp = stamp;
9775         }
9776
9777         // repair_objp might be NULL is we are cleaning up this mode because of the support ship
9778         // getting killed.
9779         if ( repair_objp ) {
9780                 aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9781                 switch ( how ) {
9782                 case REPAIR_INFO_ONWAY:
9783                         SDL_assert( repaired_objp != NULL );
9784                         aip->goal_objnum = OBJ_INDEX(repaired_objp);
9785                         aip->ai_flags |= AIF_REPAIRING;
9786                         break;
9787
9788                 case REPAIR_INFO_BROKEN:
9789                         break;
9790
9791                 case REPAIR_INFO_END:
9792                 case REPAIR_INFO_ABORT:
9793                 case REPAIR_INFO_KILLED:
9794                         if ( how == REPAIR_INFO_ABORT )
9795                                 aip->goal_objnum = -1;
9796
9797                         aip->ai_flags &= ~AIF_REPAIRING;
9798                         break;
9799                         
9800                 case REPAIR_INFO_QUEUE:
9801                         ai_add_rearm_goal( repaired_objp, repair_objp );
9802                         break;
9803
9804                 case REPAIR_INFO_BEGIN:
9805                 case REPAIR_INFO_COMPLETE:
9806                         break;
9807
9808                 default:
9809                         Int3();         // bogus type of repair info
9810                 }
9811         }
9812
9813         multi_maybe_send_repair_info( repaired_objp, repair_objp, how );
9814 }
9815
9816 //      Cleanup AI stuff for when a ship was supposed to dock with another, but the ship
9817 //      it was supposed to dock with is no longer valid.
9818 void ai_cleanup_dock_mode(ai_info *aip, ship *shipp)
9819 {
9820         object *objp;
9821
9822         objp = &Objects[shipp->objnum];
9823         aip->mode = AIM_NONE;
9824
9825         if (aip->ai_flags & AIF_REPAIRING) {
9826                 SDL_assert( aip->goal_objnum != -1 );
9827                 ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], &Objects[shipp->objnum], REPAIR_INFO_KILLED );
9828         } else if ( aip->ai_flags & AIF_BEING_REPAIRED ) {
9829                 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9830                 SDL_assert( aip->dock_objnum != -1 );
9831                 ai_do_objects_repairing_stuff( &Objects[shipp->objnum], &Objects[aip->dock_objnum], REPAIR_INFO_KILLED );
9832         } else if ( aip->ai_flags & AIF_AWAITING_REPAIR ) {
9833                 // need to find the support ship that has me as a goal_objnum
9834                 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9835                 // MWA -- 3/38/98  Check to see if this guy is queued for a support ship, or there is already
9836                 // one in the mission
9837                 if ( mission_is_repair_scheduled(objp) ) {
9838                         mission_remove_scheduled_repair( objp );                        // this function will notify multiplayer clients.
9839                 } else {
9840                         if ( aip->dock_objnum != -1 )
9841                                 ai_do_objects_repairing_stuff( objp, &Objects[aip->dock_objnum], REPAIR_INFO_ABORT );
9842                         else
9843                                 ai_do_objects_repairing_stuff( objp, NULL, REPAIR_INFO_ABORT );
9844                 }
9845         }
9846
9847         if ( aip->ai_flags & AIF_DOCKED ) {
9848                 ai_info *other_aip;
9849
9850                 SDL_assert( aip->dock_objnum != -1 );
9851
9852                 // if docked, and the dock_objnum is not undocking, force them to near last stage
9853                 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
9854                 if ( (other_aip->mode == AIM_DOCK) && (other_aip->submode < AIS_UNDOCK_3) )
9855                         other_aip->submode = AIS_UNDOCK_3;
9856                 ai_do_objects_undocked_stuff( objp, &Objects[aip->dock_objnum] );
9857         }
9858 }
9859
9860 /*
9861 //      Make dockee_objp shake a bit due to docking.
9862 void ai_dock_shake(object *docker_objp, object *dockee_objp)
9863 {
9864         vector  tangles;
9865         matrix  rotmat, tmp;
9866         float           scale;
9867         angles  *ap;
9868
9869         scale = 0.25f;          //      Compute this based on mass and speed at time of docking.
9870
9871         vm_vec_rand_vec_quick(&tangles);
9872         vm_vec_scale(&tangles, scale);
9873
9874         ap = (angles *) &tangles;
9875
9876         vm_angles_2_matrix(&rotmat, ap);
9877         vm_matrix_x_matrix( &tmp, &dockee_objp->orient, &rotmat );
9878         dockee_objp->orient = tmp;
9879
9880         vm_orthogonalize_matrix(&dockee_objp->orient);
9881
9882         dock_orient_and_approach(docker_objp, dockee_objp, DOA_DOCK_STAY);
9883
9884 }
9885 */
9886
9887 //      Make Pl_objp point at aip->goal_point.
9888 void ai_still()
9889 {
9890         ship    *shipp;
9891         ai_info *aip;
9892
9893         SDL_assert(Pl_objp->type == OBJ_SHIP);
9894         SDL_assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_OBJECTS));
9895
9896         shipp = &Ships[Pl_objp->instance];
9897         SDL_assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
9898
9899         aip = &Ai_info[shipp->ai_index];
9900
9901         turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
9902 }
9903
9904 //      Make *Pl_objp stay near another ship.
9905 void ai_stay_near()
9906 {
9907         ai_info *aip;
9908         int             goal_objnum;
9909
9910         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
9911
9912         goal_objnum = aip->goal_objnum;
9913
9914         if ((goal_objnum < 0) || (Objects[goal_objnum].type != OBJ_SHIP) || (Objects[goal_objnum].signature != aip->goal_signature)) {
9915                 aip->mode = AIM_NONE;
9916         } else {
9917                 float           dist, max_dist, scale;
9918                 vector  rand_vec, goal_pos, vec_to_goal;
9919                 object  *goal_objp;
9920
9921                 goal_objp = &Objects[goal_objnum];
9922
9923                 //      Make not all ships pursue same point.
9924                 static_randvec(Pl_objp-Objects, &rand_vec);
9925
9926                 //      Make sure point is in front hemisphere (relative to Pl_objp's position.
9927                 vm_vec_sub(&vec_to_goal, &goal_objp->pos, &Pl_objp->pos);
9928                 if (vm_vec_dot(&rand_vec, &vec_to_goal) > 1.0f) {
9929                         vm_vec_negate(&rand_vec);
9930                 }
9931
9932                 //      Scale the random vector by an amount proportional to the distance from Pl_objp to the true goal.
9933                 dist = vm_vec_dist_quick(&goal_objp->pos, &Pl_objp->pos);
9934                 max_dist = aip->stay_near_distance;
9935                 scale = dist - max_dist/2;
9936                 if (scale < 0.0f)
9937                         scale = 0.0f;
9938
9939                 vm_vec_scale_add(&goal_pos, &goal_objp->pos, &rand_vec, scale);
9940
9941                 if (max_dist < Pl_objp->radius + goal_objp->radius + 25.0f)
9942                         max_dist = Pl_objp->radius + goal_objp->radius + 25.0f;
9943
9944                 if (dist > max_dist) {
9945                         turn_towards_point(Pl_objp, &goal_pos, NULL, 0.0f);
9946                         accelerate_ship(aip, dist / max_dist - 0.8f);
9947                 }
9948         
9949         }
9950
9951 }
9952
9953 //      Warn player if dock path is obstructed.
9954 int maybe_dock_obstructed(object *cur_objp, object *goal_objp, int big_only_flag)
9955 {
9956         vector  *goalpos, *curpos;
9957         float           radius;
9958         ai_info *aip;
9959         int             collide_objnum;
9960
9961         aip = &Ai_info[Ships[cur_objp->instance].ai_index];
9962
9963         Ai_info[Ships[goal_objp->instance].ai_index].ai_flags &= ~AIF_REPAIR_OBSTRUCTED;
9964
9965         if (goal_objp != Player_obj)
9966                 return -1;
9967
9968         curpos = &cur_objp->pos;
9969         radius = cur_objp->radius;
9970         goalpos = &Path_points[aip->path_cur].pos;
9971         collide_objnum = pp_collide_any(curpos, goalpos, radius, cur_objp, goal_objp, big_only_flag);
9972
9973         if (collide_objnum != -1)
9974                 Ai_info[Ships[goal_objp->instance].ai_index].ai_flags |= AIF_REPAIR_OBSTRUCTED;
9975
9976         return collide_objnum;
9977 }
9978
9979
9980 int Dock_path_warning_given = 0;
9981
9982 //      Docking behavior.
9983 //      Approach a ship, follow path to docking platform, approach platform, after awhile,
9984 //      undock.
9985 void ai_dock()
9986 {
9987         ship                    *shipp = &Ships[Pl_objp->instance];
9988         ai_info         *aip = &Ai_info[shipp->ai_index];
9989         object          *goal_objp;
9990         ship_info       *sip = &Ship_info[shipp->ship_info_index];
9991
9992         //      Make sure object we're supposed to dock with still exists.
9993         if ((aip->goal_objnum == -1) || (Objects[aip->goal_objnum].signature != aip->goal_signature)) {
9994                 ai_cleanup_dock_mode(aip, shipp);
9995                 return;
9996         }
9997
9998         goal_objp = &Objects[aip->goal_objnum];
9999
10000         //      For docking submodes (ie, not undocking), follow path.  Once at second last
10001         //      point on path (point just before point on dock platform), orient into position.
10002         // For undocking, first mode pushes docked ship straight back from docking point
10003         // second mode turns ship and moves to point on docking radius
10004         switch (aip->submode) {
10005
10006                 //      This mode means to find the path to the docking point.
10007         case AIS_DOCK_0:
10008                 //aip->path_start = -1;
10009                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10010                 ai_path();
10011                 if (!Dock_path_warning_given && (aip->path_length < 4)) {
10012                         Warning( LOCATION, "Ship '%s' has only %i points on dock path.  Docking will look strange.  Contact Adam.", shipp->ship_name, aip->path_length );
10013                         Dock_path_warning_given = 1;            //      This is on a mission-wide basis, but it's just a hack for now...
10014                 }
10015
10016                 aip->submode = AIS_DOCK_1;
10017                 aip->path_start = -1;
10018                 aip->submode_start_time = Missiontime;
10019                 break;
10020
10021                 //      This mode means to follow the path until just before the end.
10022         case AIS_DOCK_1: {
10023         //      float   dist;
10024                 int     r;
10025
10026                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp, 1)) != -1) {
10027                         int     r1;
10028                         if ((r1 = maybe_avoid_big_ship(Pl_objp, goal_objp, aip, &goal_objp->pos, 7.0f)) != 0) {
10029                                 nprintf(("AI", "Support ship %s avoiding large ship %s\n", Ships[Pl_objp->instance].ship_name, Ships[Objects[r1].instance].ship_name));
10030                                 break;
10031                         } /*else {
10032                                 nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10033                                 accelerate_ship(aip, 0.0f);
10034                                 aip->submode = AIS_DOCK_0;
10035                         } */
10036                 } //else {
10037                 {
10038                         /*dist =*/ ai_path();
10039                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10040                         //nprintf(("AI", "Dock 1: Frame: %i, goal point = %i, dist = %7.3f\n", Framecount, aip->path_cur-aip->path_start, dist));
10041
10042                         if (aip->path_cur-aip->path_start >= aip->path_length-1) {              //      If got this far, advance no matter what.
10043                                 aip->submode = AIS_DOCK_2;
10044                                 aip->submode_start_time = Missiontime;
10045                                 aip->path_cur--;
10046                                 SDL_assert(aip->path_cur-aip->path_start >= 0);
10047                         } else if (aip->path_cur-aip->path_start >= aip->path_length-2) {
10048                                 if (Pl_objp->phys_info.speed > goal_objp->phys_info.speed + 1.5f) {
10049                                         set_accel_for_target_speed(Pl_objp, goal_objp->phys_info.speed);
10050                                 } else {
10051                                         aip->submode = AIS_DOCK_2;
10052                                         aip->submode_start_time = Missiontime;
10053                                 }
10054                         }
10055                 }
10056                 break;
10057                                           }
10058         //      This mode means to drag oneself right to the second last point on the path.
10059         //      Path code allows it to overshoot.
10060         case AIS_DOCK_2: {
10061                 float           dist;
10062                 int     r;
10063
10064                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10065                         nprintf(("AI", "Dock 2: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10066                         accelerate_ship(aip, 0.0f);
10067                         aip->submode = AIS_DOCK_1;
10068                 } else {
10069                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10070                         dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_APPROACH);
10071                         SDL_assert(dist != UNINITIALIZED_VALUE);
10072
10073                         if (dist == DOCK_BACKUP_RETURN_VAL) {
10074                                 int path_num;
10075                                 aip->submode = AIS_DOCK_1;
10076                                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
10077                                 SDL_assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
10078                                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
10079                                 break;
10080                         }
10081
10082                         //nprintf(("AI", "Dock 2: dist = %7.3f\n", vm_vec_dist_quick(&Pl_objp->pos, &goal_point)));
10083                         float   tolerance;
10084                         if (Objects[aip->goal_objnum].flags & OF_PLAYER_SHIP)
10085                                 tolerance = 6*flFrametime + 1.0f;
10086                         else
10087                                 tolerance = 4*flFrametime + 0.5f;
10088
10089                         if ( dist < tolerance) {
10090                                 aip->submode = AIS_DOCK_3;
10091                                 aip->submode_start_time = Missiontime;
10092                                 aip->path_cur++;
10093                         }
10094                 }
10095                 break;
10096                                                   }
10097
10098         case AIS_DOCK_3:
10099         case AIS_DOCK_3A:
10100                 {
10101                 SDL_assert(aip->goal_objnum != -1);
10102                 int     r;
10103
10104                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10105                         nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10106                         accelerate_ship(aip, 0.0f);
10107                         aip->submode = AIS_DOCK_2;
10108                 } else {
10109
10110                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10111                         float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10112                         SDL_assert(dist != UNINITIALIZED_VALUE);
10113
10114                         if (dist == DOCK_BACKUP_RETURN_VAL) {
10115                                 aip->submode = AIS_DOCK_2;
10116                                 break;
10117                         }
10118
10119                         //nprintf(("AI", "Dock 3: dist = %7.3f\n", dist));
10120
10121                         if (dist < 2*flFrametime * (1.0f + fl_sqrt(goal_objp->phys_info.speed))) {
10122                                 // - Removed by MK on 11/7/97, causes errors for ships docked at mission start: maybe_recreate_path(Pl_objp, aip, 1);
10123                                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10124                                 SDL_assert(dist != UNINITIALIZED_VALUE);
10125
10126                                 physics_ship_init(Pl_objp);
10127
10128                                 ai_do_objects_docked_stuff( Pl_objp, goal_objp );
10129
10130                                 if (aip->submode == AIS_DOCK_3) {
10131                                         snd_play_3d( &Snds[SND_DOCK_ATTACH], &Pl_objp->pos, &View_position );
10132                                         hud_maybe_flash_docking_text(Pl_objp);
10133                                         // ai_dock_shake(Pl_objp, goal_objp);
10134
10135                                         if ((Pl_objp == Player_obj) || (goal_objp == Player_obj))
10136                                                 joy_ff_docked();  // shake player's joystick a little
10137                                 }
10138
10139                                 //      If this ship is repairing another ship...
10140                                 if (aip->ai_flags & AIF_REPAIRING) {
10141                                         aip->submode = AIS_DOCK_4;                      //      Special rearming only dock mode.
10142                                         aip->submode_start_time = Missiontime;
10143                                 } else {
10144                                         aip->submode = AIS_DOCK_4A;
10145                                         aip->submode_start_time = Missiontime;
10146                                 }
10147                         }
10148                 }
10149                 break;
10150                 }
10151
10152                 //      Yes, we just sit here.  We wait for further orders.  No, it's not a bug.
10153         case AIS_DOCK_4A:
10154                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10155                 //nprintf(("AI", "."));
10156                 if (aip->active_goal >= 0) {
10157                         mission_log_add_entry(LOG_SHIP_DOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10158
10159                         if (aip->goals[aip->active_goal].ai_mode == AI_GOAL_DOCK) {
10160                                 ai_mission_goal_complete( aip );                                        // Note, this calls ai_set_default_behavior().
10161                         } 
10162                 } else {        //      Can happen for initially docked ships.
10163                         ai_do_default_behavior( &Objects[Ships[aip->shipnum].objnum] );         // do the default behavior
10164                 }
10165                 
10166                 break;
10167
10168         case AIS_DOCK_4: {
10169                 //      This mode is only for rearming/repairing.
10170                 //      The ship that is performing the rearm enters this mode after it docks.
10171                 SDL_assert((aip->goal_objnum >= -1) && (aip->goal_objnum < MAX_OBJECTS));
10172
10173                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10174                 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10175                 SDL_assert(dist != UNINITIALIZED_VALUE);
10176
10177                 object  *goal_objp = &Objects[aip->goal_objnum];
10178                 SDL_assert(goal_objp->type == OBJ_SHIP);
10179                 ship                    *goal_shipp = &Ships[goal_objp->instance];              
10180                 ai_info         *goal_aip = &Ai_info[goal_shipp->ai_index];
10181
10182                 //nprintf(("AI", "Dock 4: dist = %7.3f\n", dist));
10183
10184                 //      Make sure repair has not broken off.
10185                 if (dist > 5.0f) {      //      Oops, too far away!
10186                         if ( goal_aip->ai_flags & AIF_BEING_REPAIRED )
10187                                 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BROKEN);
10188
10189                         if (dist > Pl_objp->radius*2 + goal_objp->radius*2) {
10190                                 //      Got real far away from goal, so move back a couple modes and try again.
10191                                 aip->submode = AIS_DOCK_2;
10192                                 aip->submode_start_time = Missiontime;
10193                         }
10194                 } else {
10195                         if ( goal_aip->ai_flags & AIF_AWAITING_REPAIR )
10196                                 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BEGIN );
10197                 }
10198
10199                 break;
10200                                                   }
10201
10202         case AIS_UNDOCK_0: {
10203                 int path_num;
10204                 //      First stage of undocking.
10205
10206                 //nprintf(("AI", "Undock 0:\n"));
10207
10208                 aip->submode = AIS_UNDOCK_1;
10209                 aip->submode_start_time = Missiontime;
10210                 if (aip->dock_objnum == -1) {
10211                         aip->submode = AIS_UNDOCK_3;
10212                 } else {
10213
10214                         // set up the path points for the undocking procedure.  dock_path_index member should
10215                         // have gotten set in the docking code.
10216                         SDL_assert( aip->dock_path_index != -1 );
10217                         path_num = ai_return_path_num_from_dockbay(goal_objp, aip->dock_path_index);
10218                         ai_find_path(Pl_objp, goal_objp-Objects, path_num, 0);
10219
10220                         // Play a ship docking detach sound
10221                         snd_play_3d( &Snds[SND_DOCK_DETACH], &Pl_objp->pos, &View_position );
10222                 }
10223                 break;
10224                                                          }
10225         case AIS_UNDOCK_1: {
10226                 //      Using thrusters, exit from dock station to nearest next dock path point.
10227                 float   dist;
10228                 
10229                 //nprintf(("AI", "Undock 1: time in this mode = %7.3f\n", f2fl(Missiontime - aip->submode_start_time)));
10230
10231                 if (Missiontime - aip->submode_start_time < REARM_BREAKOFF_DELAY) {
10232                         break;          //      Waiting for one second to elapse to let detach sound effect play out.
10233                 }
10234                 else {  // AL - added 05/16/97.  Hack to play depart sound.  Will probably take out.
10235                                         // Assumes that the submode_start_time is not used for AIS_UNDOCK_1 anymore
10236                         if ( aip->submode_start_time != 0 )
10237                                 snd_play_3d( &Snds[SND_DOCK_DEPART], &Pl_objp->pos, &View_position );
10238                         aip->submode_start_time = 0;
10239                 }
10240
10241                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_1);
10242                 SDL_assert(dist != UNINITIALIZED_VALUE);
10243
10244                 float dist_to_dock_obj = vm_vec_dist_quick(&Pl_objp->pos, &Objects[aip->goal_objnum].pos);
10245
10246                 //      Move to within 0.1 units of second last point on path before orienting, or just plain far away from docked-to ship.
10247                 //      This allows undock to complete if first ship flies away.
10248                 if ((dist < 2*flFrametime) || (dist_to_dock_obj > 2*Pl_objp->radius)) {
10249                         aip->submode = AIS_UNDOCK_2;
10250                         aip->submode_start_time = Missiontime;
10251                 }
10252                 break;
10253                                                          }
10254         case AIS_UNDOCK_2: {
10255                 float dist;
10256                 //ai_info *other_aip;
10257
10258                 // get pointer to docked object's aip to reset flags, etc
10259                 SDL_assert( aip->dock_objnum != -1 );
10260                 //other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
10261
10262                 //      Second stage of undocking.
10263                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_2);
10264                 SDL_assert(dist != UNINITIALIZED_VALUE);
10265
10266
10267                 //nprintf(("AI", "Undock 2: dist = %7.3f\n", dist));
10268                 
10269                 //      If at goal point, or quite far away from dock object
10270                 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) ) {
10271                         // reset the dock flags.  If rearm/repair, reset rearm repair flags for those ships as well.
10272                         if ( sip->flags & SIF_SUPPORT ) {
10273                                 ai_do_objects_repairing_stuff( &Objects[aip->dock_objnum], Pl_objp, REPAIR_INFO_END );
10274                         }
10275
10276                         // clear out flags for AIF_DOCKED for both objects.
10277                         ai_do_objects_undocked_stuff( Pl_objp, goal_objp );
10278                         physics_ship_init(Pl_objp);
10279                         aip->submode = AIS_UNDOCK_3;                            //      The do-nothing mode, until another order is issued
10280
10281                         //aip->ai_flags &= ~AIF_DOCKED;         //      @MK, 9/18/97
10282                         //other_aip->ai_flags &= ~AIF_DOCKED;
10283                         //aip->dock_objnum = -1;                                        // invalidate who obj is docked with
10284                         //other_aip->dock_objnum = -1;                  // MWA 10/07/97 invalide docked objects dock_objnum value as well
10285
10286                         // don't add undock log entries for support ships.
10287                         if ( !(sip->flags & SIF_SUPPORT) )
10288                                 mission_log_add_entry(LOG_SHIP_UNDOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10289
10290                 }
10291                 break;
10292                 }
10293         case AIS_UNDOCK_3: {
10294                 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_3);
10295                 SDL_assert(dist != UNINITIALIZED_VALUE);
10296
10297                 if (dist < Pl_objp->radius/2 + 5.0f) {
10298                         aip->submode = AIS_UNDOCK_4;
10299                 }
10300
10301                 // possible that this flag hasn't been cleared yet.  When aborting a rearm, this submode might
10302                 // be entered directly.
10303                 if ( (sip->flags & SIF_SUPPORT) && (aip->ai_flags & AIF_REPAIRING) ) {
10304                         ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], Pl_objp, REPAIR_INFO_ABORT );
10305                 }
10306
10307                 break;
10308                                                  }
10309         case AIS_UNDOCK_4: {
10310                 //ai_info *other_aip;
10311
10312                 // MWA 10/07/97  I'm slightly confused by the dual use of goal_objnum and dock_objnum.  Seems to me
10313                 // that goal_objnum and dock_objnum are the same through this whole docking/undocking process, although
10314                 // I could be wrong.  dock_objnum was reset in undock_2 submode so try to use goal_objnum here to
10315                 // get other ships ai_info pointer
10316                 //SDL_assert( aip->goal_objnum != -1 );
10317                 //other_aip = &Ai_info[Ships[Objects[aip->goal_objnum].instance].ai_index];
10318
10319                 aip->mode = AIM_NONE;
10320                 aip->dock_path_index = -1;              // invalidate the docking path index
10321
10322                 // these flags should have been cleared long ago!
10323                 // Get Allender if you hit one of these!!!!!
10324                 // removed by allender on 2/16 since a ship may be docked with some other ship, but still be the
10325                 // goal_objnum of this ship ending it's undocking mode.
10326                 //SDL_assert( !(aip->ai_flags & AIF_DOCKED) );
10327                 //SDL_assert( !(other_aip->ai_flags & AIF_DOCKED) );
10328                 //SDL_assert( !(aip->ai_flags & AIF_REPAIRING) );
10329                 //SDL_assert( !(other_aip->ai_flags & AIF_BEING_REPAIRED) );
10330                 //SDL_assert( !(other_aip->ai_flags & AIF_AWAITING_REPAIR) );
10331
10332                 // only call mission goal complete if this was indeed an undock goal
10333                 if ( aip->active_goal > -1 ) {
10334                         if ( aip->goals[aip->active_goal].ai_mode == AI_GOAL_UNDOCK )
10335                                 ai_mission_goal_complete( aip );                        // this call should reset the AI mode
10336                         //else
10337                         //      aip->active_goal = -1;                                          // this ensures that this ship might get new goal
10338                 }
10339
10340                 break;
10341                                                          }
10342         default:
10343                 Int3(); //      Error, bogus submode
10344         }
10345
10346 }
10347
10348 // TURRET BEGIN
10349
10350 //      Given an object and a turret on that object, return the global position and forward vector
10351 //      of the turret.   The gun normal is the unrotated gun normal, (the center of the FOV cone), not
10352 // the actual gun normal given using the current turret heading.  But it _is_ rotated into the model's orientation
10353 //      in global space.
10354 void ship_get_global_turret_info(object *objp, model_subsystem *tp, vector *gpos, vector *gvec)
10355 {
10356         matrix  m;
10357         vm_copy_transpose_matrix(&m, &objp->orient);
10358 //      vm_vec_rotate(gpos, &tp->turret_avg_firing_point, &m);
10359         vm_vec_rotate(gpos, &tp->pnt, &m);
10360         vm_vec_add2(gpos, &objp->pos);
10361         vm_vec_rotate(gvec, &tp->turret_norm, &m);      
10362 }
10363
10364 // Given an object and a turret on that object, return the actual firing point of the gun
10365 // and its normal.   This uses the current turret angles.  We are keeping track of which
10366 // gun to fire next in the ship specific info for this turret subobject.  Use this info
10367 // to determine which position to fire from next.
10368 //      Stuffs:
10369 //              *gpos: absolute position of gun firing point
10370 //              *gvec: vector fro *gpos to *targetp
10371 void ship_get_global_turret_gun_info(object *objp, ship_subsys *ssp, vector *gpos, vector *gvec, int use_angles, vector *targetp)
10372 {
10373         vector * gun_pos;
10374         model_subsystem *tp = ssp->system_info;
10375
10376         ship_model_start(objp);
10377
10378         gun_pos = &tp->turret_firing_point[ssp->turret_next_fire_pos % tp->turret_num_firing_points];
10379
10380         model_find_world_point(gpos, gun_pos, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10381
10382         if (use_angles)
10383                 model_find_world_dir(gvec, &tp->turret_norm, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10384         else {
10385                 //vector        gun_pos2;
10386                 //vm_vec_add(&gun_pos2, gpos, gun_pos);
10387                 vm_vec_normalized_dir(gvec, targetp, gpos);
10388         }
10389
10390         ship_model_stop(objp);  
10391 }
10392
10393 //      Rotate a turret towards an enemy.
10394 //      Return TRUE if caller should use angles in subsequent rotations.
10395 //      Some obscure model thing only John Slagel knows about.
10396 //      Sets predicted enemy position.
10397 //      If the turret (*ss) has a subsystem targeted, the subsystem is used as the predicted point.
10398 int aifft_rotate_turret(ship *shipp, int parent_objnum, ship_subsys *ss, object *objp, object *lep, vector *predicted_enemy_pos, vector *gvec)
10399 {
10400         if (ss->turret_enemy_objnum != -1)      {
10401                 model_subsystem *tp = ss->system_info;
10402                 vector  gun_pos, gun_vec;
10403                 float           weapon_speed;
10404                 float           weapon_system_strength;
10405
10406                 //      weapon_system_strength scales time enemy in range in 0..1.  So, the lower this is, the worse the aiming will be.
10407                 weapon_system_strength = ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS);
10408
10409                 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gun_pos, &gun_vec);
10410
10411                 weapon_speed = Weapon_info[tp->turret_weapon_type].max_speed;
10412                 float weapon_travel_dist = weapon_speed * Weapon_info[tp->turret_weapon_type].lifetime;
10413
10414                 vector  enemy_point;
10415                 if (ss->targeted_subsys != NULL) {
10416                         if (ss->turret_enemy_objnum != -1) {
10417                                 vm_vec_unrotate(&enemy_point, &ss->targeted_subsys->system_info->pnt, &Objects[ss->turret_enemy_objnum].orient);
10418                                 vm_vec_add2(&enemy_point, &Objects[ss->turret_enemy_objnum].pos);
10419                         }
10420                 } else {
10421                         if ((lep->type == OBJ_SHIP) && (Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
10422                                 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));
10423                         } else {
10424                                 enemy_point = lep->pos;
10425                         }
10426                 }
10427
10428                 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);
10429
10430                 if (weapon_system_strength < 0.7f) {
10431                         vector  rand_vec;
10432
10433                         static_randvec(Missiontime >> 18, &rand_vec);   //      Return same random number for two seconds.
10434                         //      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.
10435                         vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, (1.0f - weapon_system_strength)*1.5f * lep->radius);
10436                 }
10437
10438                 vector  v2e;
10439                 vm_vec_normalized_dir(&v2e, predicted_enemy_pos, &gun_pos);
10440                 if (vm_vec_dot(&v2e, gvec) > tp->turret_fov) {
10441                         model_rotate_gun(shipp->modelnum, ss->system_info, &Objects[parent_objnum].orient,
10442                                 &ss->submodel_info_1.angs, &ss->submodel_info_2.angs,
10443                                 &Objects[parent_objnum].pos, predicted_enemy_pos);
10444                 }
10445         }
10446
10447         return 0;
10448 }
10449
10450 //      Determine if subsystem *enemy_subsysp is hittable from objp.
10451 //      If so, return dot product of vector from point abs_gunposp to *enemy_subsysp
10452 float   aifft_compute_turret_dot(object *objp, object *enemy_objp, vector *abs_gunposp, ship_subsys *turret_subsysp, ship_subsys *enemy_subsysp)
10453 {
10454         float   dot_out;
10455         vector  subobj_pos, vector_out;
10456
10457         vm_vec_unrotate(&subobj_pos, &enemy_subsysp->system_info->pnt, &enemy_objp->orient);
10458         vm_vec_add2(&subobj_pos, &enemy_objp->pos);
10459
10460         if (ship_subsystem_in_sight(enemy_objp, enemy_subsysp, abs_gunposp, &subobj_pos, 1, &dot_out, &vector_out)) {
10461                 vector  turret_norm;
10462
10463                 vm_vec_rotate(&turret_norm, &turret_subsysp->system_info->turret_norm, &objp->orient);
10464                 return vm_vec_dot(&turret_norm, &vector_out);
10465         } else
10466                 return -1.0f;
10467
10468 }
10469
10470 #define MAX_AIFFT_TURRETS                       60
10471 ship_subsys *aifft_list[MAX_AIFFT_TURRETS];
10472 float aifft_rank[MAX_AIFFT_TURRETS];
10473 int aifft_list_size = 0;
10474 int aifft_max_checks = 5;
10475 DCF(mf, "")
10476 {
10477         dc_get_arg(ARG_INT);
10478         aifft_max_checks = Dc_arg_int;
10479 }
10480
10481
10482 //      Pick a subsystem to attack on enemy_objp.
10483 //      Only pick one if enemy_objp is a big ship or a capital ship.
10484 //      Returns dot product from turret to subsystem in *dot_out
10485 ship_subsys *aifft_find_turret_subsys(object *objp, ship_subsys *ssp, object *enemy_objp, float *dot_out)
10486 {
10487         ship    *eshipp;
10488         ship_info       *esip;
10489         ship_subsys     *best_subsysp = NULL;
10490         float dot;
10491
10492         SDL_assert(enemy_objp->type == OBJ_SHIP);
10493
10494         eshipp = &Ships[enemy_objp->instance];
10495         esip = &Ship_info[eshipp->ship_info_index];
10496
10497         float   best_dot = 0.0f;
10498         *dot_out = best_dot;
10499
10500         //      Compute absolute gun position.
10501         vector  abs_gun_pos;
10502         vm_vec_unrotate(&abs_gun_pos, &ssp->system_info->pnt, &objp->orient);
10503         vm_vec_add2(&abs_gun_pos, &objp->pos);
10504
10505         //      Only pick a turret to attack on large ships.
10506         if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
10507                 return best_subsysp;
10508
10509         // Make sure big or huge ship *actually* has subsystems  (ie, knossos)
10510         if (esip->n_subsystems == 0) {
10511                 return best_subsysp;
10512         }
10513
10514         // first build up a list subsystems to traverse
10515         ship_subsys     *pss;
10516         aifft_list_size = 0;
10517         for ( pss = GET_FIRST(&eshipp->subsys_list); pss !=END_OF_LIST(&eshipp->subsys_list); pss = GET_NEXT(pss) ) {
10518                 model_subsystem *psub = pss->system_info;
10519
10520                 // if we've reached max turrets bail
10521                 if(aifft_list_size >= MAX_AIFFT_TURRETS){
10522                         break;
10523                 }
10524
10525                 // Don't process destroyed objects
10526                 if ( pss->current_hits <= 0.0f ){
10527                         continue;
10528                 }
10529                 
10530                 switch (psub->type) {
10531                 case SUBSYSTEM_WEAPONS:
10532                         aifft_list[aifft_list_size] = pss;
10533                         aifft_rank[aifft_list_size++] = 1.4f;
10534                         break;
10535
10536                 case SUBSYSTEM_TURRET:
10537                         aifft_list[aifft_list_size] = pss;
10538                         aifft_rank[aifft_list_size++] = 1.2f;
10539                         break;
10540
10541                 case SUBSYSTEM_SENSORS:
10542                 case SUBSYSTEM_ENGINE:
10543                         aifft_list[aifft_list_size] = pss;
10544                         aifft_rank[aifft_list_size++] = 1.0f;
10545                         break;
10546                 }
10547         }
10548
10549         // DKA:  6/28/99 all subsystems can be destroyed.
10550         //SDL_assert(aifft_list_size > 0);
10551         if (aifft_list_size == 0) {
10552                 return best_subsysp;
10553         }
10554
10555         // determine a stride value so we're not checking too many turrets
10556         int stride = aifft_list_size > aifft_max_checks ? aifft_list_size / aifft_max_checks : 1;
10557         if(stride <= 0){
10558                 stride = 1;
10559         }
10560         int offset = (int)frand_range(0.0f, (float)(aifft_list_size % stride));
10561         int idx;
10562         for(idx=offset; idx<aifft_list_size; idx+=stride){
10563                 dot = aifft_compute_turret_dot(objp, enemy_objp, &abs_gun_pos, ssp, aifft_list[idx]);                   
10564
10565                 if (dot* aifft_rank[idx] > best_dot) {
10566                         best_dot = dot*aifft_rank[idx];
10567                         best_subsysp = aifft_list[idx];
10568                 }
10569         }
10570
10571         SDL_assert(best_subsysp != &eshipp->subsys_list);
10572
10573         *dot_out = best_dot;
10574         return best_subsysp;
10575 }
10576
10577 // Set active weapon for turret
10578 void ai_turret_select_default_weapon(ship_subsys *turret)
10579 {
10580         ship_weapon *twp;
10581
10582         twp = &turret->weapons;
10583
10584         // If a primary weapon is available, select it
10585         if ( twp->num_primary_banks > 0 ) {
10586                 turret->system_info->turret_weapon_type = twp->primary_bank_weapons[0];
10587         } else if ( twp->num_secondary_banks > 0 ) {
10588                 turret->system_info->turret_weapon_type = twp->secondary_bank_weapons[0];
10589         }
10590 }
10591
10592 // return !0 if the specified target should scan for a new target, otherwise return 0
10593 int turret_should_pick_new_target(ship_subsys *turret)
10594 {
10595 //      int target_type;
10596
10597         if ( timestamp_elapsed(turret->turret_next_enemy_check_stamp) ) {
10598                 return 1;
10599         }
10600
10601         return 0;
10602
10603 /*
10604         if ( turret->turret_enemy_objnum == -1 ) {
10605                 return 1;
10606         }
10607                 
10608         target_type = Objects[turret->turret_enemy_objnum].type;
10609         if ( (target_type != OBJ_SHIP) && (target_type != OBJ_ASTEROID) ) {
10610                 return 1;
10611         }
10612
10613         return 0;
10614 */
10615 }
10616
10617 // Set the next fire timestamp for a turret, based on weapon type and ai class
10618 void turret_set_next_fire_timestamp(ship_subsys *turret, ai_info *aip)
10619 {
10620         float   wait;
10621         int     weapon_id;
10622
10623         weapon_id = turret->system_info->turret_weapon_type;
10624
10625         wait = Weapon_info[weapon_id].fire_wait * 1000.0f;
10626
10627         // make side even for team vs. team
10628         if ((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) {
10629                 // flak guns need to fire more rapidly
10630                 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10631                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10632                         wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10633                 } else {
10634                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10635                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10636                 }
10637         } else {
10638                 // flak guns need to fire more rapidly
10639                 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10640                         if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10641                                 wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10642                         } else {
10643                                 wait *= Ship_fire_delay_scale_hostile[Game_skill_level] * 0.5f;
10644                         }       
10645                         wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10646
10647                 } else if (Weapon_info[weapon_id].wi_flags & WIF_HUGE) {
10648                         // make huge weapons fire independently of team
10649                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10650                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10651                 } else {
10652                         // give team friendly an advantage
10653                         if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10654                                 wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10655                         } else {
10656                                 wait *= Ship_fire_delay_scale_hostile[Game_skill_level];
10657                         }       
10658                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10659                 }
10660         }
10661
10662         // vary wait time +/- 10%
10663         wait *= frand_range(0.9f, 1.1f);
10664         turret->turret_next_fire_stamp = timestamp((int) wait);
10665 }
10666
10667 // Decide  if a turret should launch an aspect seeking missile
10668 int turret_should_fire_aspect(ship_subsys *turret, float dot, int weapon_class)
10669 {
10670         weapon_info *wip;
10671
10672         wip = &Weapon_info[weapon_class];
10673
10674         if ( (dot > AICODE_TURRET_DUMBFIRE_ANGLE) && (turret->turret_time_enemy_in_range >= min(wip->min_lock_time,AICODE_TURRET_MAX_TIME_IN_RANGE)) ) {
10675                 return 1;
10676         }
10677
10678         return 0;
10679 }
10680
10681 // Update how long current target has been in this turrets range
10682 void turret_update_enemy_in_range(ship_subsys *turret, float seconds)
10683 {
10684         turret->turret_time_enemy_in_range += seconds;
10685
10686         if ( turret->turret_time_enemy_in_range < 0.0f ) {
10687                 turret->turret_time_enemy_in_range = 0.0f;
10688         }
10689
10690         if ( turret->turret_time_enemy_in_range > AICODE_TURRET_MAX_TIME_IN_RANGE ) {
10691                 turret->turret_time_enemy_in_range = AICODE_TURRET_MAX_TIME_IN_RANGE;
10692         }
10693 }
10694
10695
10696
10697 // Fire a weapon from a turret
10698 void turret_fire_weapon(ship_subsys *turret, int parent_objnum, vector *turret_pos, vector *turret_fvec, vector *predicted_pos = NULL)
10699 {
10700         matrix  turret_orient;
10701         int             turret_weapon_class, weapon_objnum;
10702         ai_info *parent_aip;
10703         ship            *parent_ship;
10704         beam_fire_info fire_info;
10705         float flak_range = 0.0f;
10706
10707         parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
10708         parent_ship = &Ships[Objects[parent_objnum].instance];
10709         turret_weapon_class = turret->system_info->turret_weapon_type;
10710
10711         if (check_ok_to_fire(parent_objnum, turret->turret_enemy_objnum, &Weapon_info[turret_weapon_class])) {
10712                 vm_vector_2_matrix(&turret_orient, turret_fvec, NULL, NULL);
10713                 turret->turret_last_fire_direction = *turret_fvec;
10714
10715                 // set next fire timestamp for the turret
10716                 turret_set_next_fire_timestamp(turret, parent_aip);
10717
10718                 // if this weapon is a beam weapon, handle it specially
10719                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM){
10720                         // if this beam isn't free to fire
10721                         if (!(turret->weapons.flags & SW_FLAG_BEAM_FREE)) {
10722                                 Int3(); // should never get this far
10723                                 return;
10724                         }
10725
10726                         // stuff beam firing info
10727                         memset(&fire_info, 0, sizeof(beam_fire_info));
10728                         fire_info.accuracy = 1.0f;
10729                         fire_info.beam_info_index = turret_weapon_class;
10730                         fire_info.beam_info_override = NULL;
10731                         fire_info.shooter = &Objects[parent_objnum];
10732                         fire_info.target = &Objects[turret->turret_enemy_objnum];
10733                         fire_info.target_subsys = NULL;
10734                         fire_info.turret = turret;
10735
10736                         // fire a beam weapon
10737                         beam_fire(&fire_info);
10738                 } else {
10739
10740                         // don't fire swarm, but set up swarm info
10741                         if (Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM) {
10742                                 turret_swarm_set_up_info(parent_objnum, turret, turret_weapon_class);
10743                                 return;
10744                         } else {
10745                                 weapon_objnum = weapon_create( turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10746                                 weapon_set_tracking_info(weapon_objnum, parent_objnum, turret->turret_enemy_objnum, 1, turret->targeted_subsys);                
10747                         }
10748
10749                         //nprintf(("AI", "Turret_time_enemy_in_range = %7.3f\n", ss->turret_time_enemy_in_range));              
10750                         if (weapon_objnum != -1) {
10751                                 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10752                                 // AL 1-6-97: Store pointer to turret subsystem
10753                                 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10754
10755                                 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10756                                         // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10757                                         if ( parent_objnum != OBJ_INDEX(Player_obj) ) {                                         
10758                                                 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], turret_pos, &View_position );                                          
10759                                         }
10760                                 }               
10761
10762                                 // if the gun is a flak gun
10763                                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){                       
10764                                         // show a muzzle flash
10765                                         flak_muzzle_flash(turret_pos, turret_fvec, turret_weapon_class);
10766
10767                                         // pick a firing range so that it detonates properly                    
10768                                         flak_pick_range(&Objects[weapon_objnum], predicted_pos, ship_get_subsystem_strength(parent_ship, SUBSYSTEM_WEAPONS));
10769
10770                                         // determine what that range was
10771                                         flak_range = flak_get_range(&Objects[weapon_objnum]);
10772                                 }
10773
10774                                 // in multiplayer (and the master), then send a turret fired packet.
10775                                 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10776                                         int subsys_index;
10777
10778                                         subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10779                                         SDL_assert( subsys_index != -1 );
10780                                         if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){                       
10781                                                 send_flak_fired_packet( parent_objnum, subsys_index, weapon_objnum, flak_range );
10782                                         } else {
10783                                                 send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10784                                         }
10785                                 }
10786                         }
10787                 }
10788         } else {
10789                 float wait = 1000.0f * frand_range(0.9f, 1.1f);
10790                 turret->turret_next_fire_stamp = timestamp((int) wait);
10791         }
10792 }
10793
10794 void turret_swarm_fire_from_turret(ship_subsys *turret, int parent_objnum, int target_objnum, ship_subsys *target_subsys)
10795 {
10796         int turret_weapon_class, weapon_objnum;
10797         matrix turret_orient;
10798         vector turret_pos, turret_fvec;
10799
10800         // parent not alive, quick out.
10801         if (Objects[parent_objnum].type != OBJ_SHIP) {
10802                 return;
10803         }
10804
10805         //      change firing point
10806         ship_get_global_turret_gun_info(&Objects[parent_objnum], turret, &turret_pos, &turret_fvec, 1, NULL);
10807         turret->turret_next_fire_pos++;
10808
10809         // get class [index into Weapon_info array
10810         turret_weapon_class = turret->system_info->turret_weapon_type;
10811         SDL_assert(Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM);
10812
10813         // make turret_orient from turret_fvec -- turret->turret_last_fire_direction
10814         vm_vector_2_matrix(&turret_orient, &turret_fvec, NULL, NULL);
10815
10816         // create weapon and homing info
10817         weapon_objnum = weapon_create(&turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10818         weapon_set_tracking_info(weapon_objnum, parent_objnum, target_objnum, 1, target_subsys);
10819
10820         // do other cool stuff if weapon is created.
10821         if (weapon_objnum > -1) {
10822                 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10823                 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10824
10825                 // maybe sound
10826                 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10827                         // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10828                         if ( parent_objnum != OBJ_INDEX(Player_obj) ) {
10829                                 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], &turret_pos, &View_position );
10830                         }
10831                 }
10832                 
10833                 // in multiplayer (and the master), then send a turret fired packet.
10834                 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10835                         int subsys_index;
10836
10837                         subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10838                         SDL_assert( subsys_index != -1 );
10839                         send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10840                 }
10841         }
10842 }
10843
10844 int Num_ai_firing = 0;
10845 int Num_find_turret_enemy = 0;
10846 int Num_turrets_fired = 0;
10847 //      Given a turret tp and its parent parent_objnum, fire from the turret at its enemy.
10848 void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
10849 {
10850         float           weapon_firing_range;
10851         vector  v2e;
10852         object  *lep;           //      Last enemy pointer
10853         model_subsystem *tp = ss->system_info;
10854         int             use_angles, turret_weapon_class;
10855         vector  predicted_enemy_pos;
10856         object  *objp;
10857         ai_info *aip;
10858
10859         if (!Ai_firing_enabled) {
10860                 return;
10861         }
10862
10863         if (ss->current_hits < 0.0f) {
10864                 return;
10865         }
10866
10867         if ( ship_subsys_disrupted(ss) ){               // AL 1/19/98: Make sure turret isn't suffering disruption effects
10868                 return;
10869         }
10870
10871         // Check turret free
10872         if (ss->weapons.flags & SW_FLAG_TURRET_LOCK) {
10873                 return;
10874         }
10875
10876         // If beam weapon, check beam free
10877         if ( (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) && !(ss->weapons.flags & SW_FLAG_BEAM_FREE) ) {
10878                 return;
10879         }
10880
10881         SDL_assert( shipp->objnum == parent_objnum );
10882
10883         if ( tp->turret_weapon_type < 0 ){
10884                 return;
10885         }
10886
10887         // Monitor number of calls to ai_fire_from_turret
10888         Num_ai_firing++;
10889
10890         turret_weapon_class = tp->turret_weapon_type;
10891
10892         // AL 09/14/97: ensure ss->turret_enemy_objnum != -1 before setting lep
10893         if ( (ss->turret_enemy_objnum >= 0 && ss->turret_enemy_objnum < MAX_OBJECTS) && (ss->turret_enemy_sig == Objects[ss->turret_enemy_objnum].signature)) {
10894                 lep = &Objects[ss->turret_enemy_objnum];
10895
10896                 // MK -- here is where turret is targeting a bomb.  I simply return for now.  We should force
10897                 // a target change -- or better yet, never pick a weapon when this turret has a "huge" weapon
10898                 // loaded.
10899
10900                 // we only care about targets which are ships.
10901                 //if ( lep->type != OBJ_SHIP )
10902                 //      return;
10903
10904                 //      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.
10905                 if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HUGE ) {
10906                         if ( lep->type != OBJ_SHIP ) {
10907                                 return;
10908                         }
10909                         if ( !(Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
10910                                 return;
10911                         }
10912                 }
10913
10914                 // If targeting protected or beam protected ship, don't fire.  Reset enemy objnum
10915                 if (lep->type == OBJ_SHIP) {
10916                         // Check if we're targeting a protected ship
10917                         if (lep->flags & OF_PROTECTED) {
10918                                 ss->turret_enemy_objnum = -1;
10919                                 ss->turret_time_enemy_in_range = 0.0f;
10920                                 return;
10921                         }
10922
10923                         // Check if we're targeting a beam protected ship with a beam weapon
10924                         if ( (lep->flags & OF_BEAM_PROTECTED) && (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) ) {
10925                                 ss->turret_enemy_objnum = -1;
10926                                 ss->turret_time_enemy_in_range = 0.0f;
10927                                 return;
10928                         }
10929                 }
10930         } else {
10931                 ss->turret_enemy_objnum = -1;
10932                 lep = NULL;
10933         }
10934         
10935         SDL_assert((parent_objnum >= 0) && (parent_objnum < MAX_OBJECTS));
10936         objp = &Objects[parent_objnum];
10937         SDL_assert(objp->type == OBJ_SHIP);
10938         aip = &Ai_info[Ships[objp->instance].ai_index];
10939
10940         // Use the turret info for all guns, not one gun in particular.
10941         vector   gvec, gpos;
10942         ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
10943
10944         // Rotate the turret even if time hasn't elapsed, since it needs to turn to face its target.
10945         use_angles = aifft_rotate_turret(shipp, parent_objnum, ss, objp, lep, &predicted_enemy_pos, &gvec);
10946
10947         if ( !timestamp_elapsed(ss->turret_next_fire_stamp)){
10948                 return;
10949         }
10950
10951         // Don't try to fire beyond weapon_limit_range
10952         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);
10953
10954         // if beam weapon in nebula and target not tagged, decrase firing range
10955         extern int Nebula_sec_range;
10956         if (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) {
10957                 if ( !((shipp->tag_left > 0) || (shipp->level2_tag_left > 0)) ) {
10958                         if (Nebula_sec_range) {
10959                                 weapon_firing_range *= float(BEAM_NEBULA_RANGE_REDUCE_FACTOR);
10960                         }
10961                 }
10962         }
10963
10964         if (ss->turret_enemy_objnum != -1) {
10965                 float dist_to_enemy = vm_vec_normalized_dir(&v2e, &predicted_enemy_pos, &gpos) - lep->radius;
10966                 if (dist_to_enemy > weapon_firing_range) {
10967                         ss->turret_enemy_objnum = -1;           //      Force picking of new enemy.
10968                 }
10969         }
10970
10971         // Turret spawn weapons are a special case.  They fire if there are enough enemies in the 
10972         // immediate area (not necessarily in the turret fov).
10973         if ( Weapon_info[turret_weapon_class].wi_flags & WIF_SPAWN ) {
10974                 int num_ships_nearby;
10975                 num_ships_nearby = num_nearby_fighters(get_enemy_team_mask(parent_objnum), &gpos, 1500.0f);
10976                 if (( num_ships_nearby >= 3 ) || ((num_ships_nearby >= 2) && (frand() < 0.1f))) {
10977                         turret_fire_weapon(ss, parent_objnum, &gpos, &ss->turret_last_fire_direction);
10978                 } else {
10979                         ss->turret_next_fire_stamp = timestamp(1000);   //      Regardless of firing rate, don't check whether should fire for awhile.
10980                 }
10981                 return;
10982         }
10983
10984         //      Maybe pick a new enemy.
10985         if ( turret_should_pick_new_target(ss) ) {
10986                 Num_find_turret_enemy++;
10987                 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);
10988                 SDL_assert(objnum < 0 || is_target_beam_valid(ss, objnum));
10989
10990                 if (objnum != -1) {
10991                         if (ss->turret_enemy_objnum == -1) {
10992                                 ss->turret_enemy_objnum = objnum;
10993                                 ss->turret_enemy_sig = Objects[objnum].signature;
10994                                 // why return?
10995                                 return;
10996                         } else {
10997                                 ss->turret_enemy_objnum = objnum;
10998                                 ss->turret_enemy_sig = Objects[objnum].signature;
10999                         }
11000                 } else {
11001                         ss->turret_enemy_objnum = -1;
11002                 }
11003
11004                 if (ss->turret_enemy_objnum != -1) {
11005                         float   dot = 1.0f;
11006                         lep = &Objects[ss->turret_enemy_objnum];
11007                         if ( lep->type == OBJ_SHIP ) {
11008                                 ss->targeted_subsys = aifft_find_turret_subsys(objp, ss, lep, &dot);                            
11009                         }
11010                         ss->turret_next_enemy_check_stamp = timestamp((int) (max(dot, 0.5f)*2000.0f) + 1000);
11011                 } else {
11012                         ss->turret_next_enemy_check_stamp = timestamp((int) (2000.0f * frand_range(0.9f, 1.1f)));       //      Check every two seconds
11013                 }
11014         }
11015
11016         //      If still don't have an enemy, return.  Or, if enemy is protected, return.
11017         if (ss->turret_enemy_objnum != -1) {
11018                 //      Don't shoot at ship we're going to dock with.
11019                 if (ss->turret_enemy_objnum == aip->dock_objnum) {
11020                         ss->turret_enemy_objnum = -1;
11021                         return;
11022                 }
11023
11024                 if (Objects[ss->turret_enemy_objnum].flags & OF_PROTECTED) {
11025                         //      This can happen if the enemy was selected before it became protected.
11026                         ss->turret_enemy_objnum = -1;
11027                         return;
11028                 }
11029                 lep = &Objects[ss->turret_enemy_objnum];
11030         } else {
11031                 if (timestamp_until(ss->turret_next_fire_stamp) < 500) {
11032                         ss->turret_next_fire_stamp = timestamp(500);
11033                 }
11034                 return;
11035         }
11036
11037         if ( lep == NULL ){
11038                 return;
11039         }
11040
11041         SDL_assert(ss->turret_enemy_objnum != -1);
11042
11043         float dot = vm_vec_dot(&v2e, &gvec);
11044
11045         if (dot > tp->turret_fov ) {
11046                 // Ok, the turret is lined up... now line up a particular gun.
11047                 int ok_to_fire = 0;
11048                 float dist_to_enemy;
11049
11050                 // We're ready to fire... now get down to specifics, like where is the
11051                 // actual gun point and normal, not just the one for whole turret.
11052                 ship_get_global_turret_gun_info(&Objects[parent_objnum], ss, &gpos, &gvec, use_angles, &predicted_enemy_pos);
11053                 ss->turret_next_fire_pos++;
11054
11055                 // Fire in the direction the turret is facing, not right at the target regardless of turret dir.
11056                 vm_vec_sub(&v2e, &predicted_enemy_pos, &gpos);
11057                 dist_to_enemy = vm_vec_normalize(&v2e);
11058                 dot = vm_vec_dot(&v2e, &gvec);
11059
11060                 // 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
11061                 // and make them less lethal
11062                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){
11063                         flak_jitter_aim(&v2e, dist_to_enemy, ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS));
11064                 }
11065
11066                 // Fire if:
11067                 //              dumbfire and nearly pointing at target.
11068                 //              heat seeking and target in a fairly wide cone.
11069                 //              aspect seeking and target is locked.
11070                 turret_weapon_class = tp->turret_weapon_type;
11071
11072                 // if dumbfire (lasers and non-homing missiles)
11073                 if ( !(Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING) ) {
11074                         if ((dist_to_enemy < 75.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11075                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11076                                 ok_to_fire = 1;
11077                         }
11078                 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_HEAT ) {     // if heat seekers
11079                         if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_HEATSEEK_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_ASPECT ) {   // if aspect seeker
11084                         if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11085                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11086                         }
11087                         if ( turret_should_fire_aspect(ss, dot, turret_weapon_class) ) {
11088                                 ok_to_fire = 1;
11089                         }
11090                 }
11091
11092                 if ( ok_to_fire ) {
11093                         Num_turrets_fired++;
11094                         
11095                         turret_fire_weapon(ss, parent_objnum, &gpos, &v2e, &predicted_enemy_pos);                                               
11096                 } else {
11097                         turret_update_enemy_in_range(ss, -4*Weapon_info[tp->turret_weapon_type].fire_wait);
11098                         ss->turret_next_fire_stamp = timestamp(500);
11099                 }
11100         } else {
11101                 // Lost him!
11102                 ss->turret_enemy_objnum = -1;           //      Reset enemy objnum, find a new one next frame.
11103                 ss->turret_time_enemy_in_range = 0.0f;
11104         }
11105 }
11106
11107 // TURRET END
11108
11109 #ifndef NDEBUG
11110 #define MAX_AI_DEBUG_RENDER_STUFF       100
11111 typedef struct ai_render_stuff {
11112         ship_subsys     *ss;
11113         int                     parent_objnum;
11114 } ai_render_stuff;
11115
11116 ai_render_stuff AI_debug_render_stuff[MAX_AI_DEBUG_RENDER_STUFF];
11117
11118 int     Num_AI_debug_render_stuff = 0;
11119
11120 void ai_debug_render_stuff()
11121 {
11122         vertex  vert1, vert2;
11123         vector  gpos2;
11124         int             i;
11125
11126         for (i=0; i<Num_AI_debug_render_stuff; i++) {
11127                 ship_subsys     *ss;
11128                 int     parent_objnum;
11129                 vector  gpos, gvec;
11130                 model_subsystem *tp;
11131
11132                 ss = AI_debug_render_stuff[i].ss;
11133                 tp = ss->system_info;
11134
11135                 parent_objnum = AI_debug_render_stuff[i].parent_objnum;
11136
11137                 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
11138                 g3_rotate_vertex(&vert1, &gpos);
11139                 vm_vec_scale_add(&gpos2, &gpos, &gvec, 20.0f);
11140                 g3_rotate_vertex(&vert2, &gpos2);
11141                 gr_set_color(0, 0, 255);
11142                 g3_draw_sphere(&vert1, 2.0f);
11143                 gr_set_color(255, 0, 255);
11144                 g3_draw_sphere(&vert2, 2.0f);
11145                 g3_draw_line(&vert1, &vert2);
11146         }
11147
11148         // draw from beta to its goal point
11149 /*      for (i=0; i<6; i++) {
11150                 ai_info *aip = &Ai_info[i];
11151                 gr_set_color(0, 0, 255);
11152                 g3_rotate_vertex(&vert1, &Objects[i].pos);
11153                 g3_rotate_vertex(&vert2, &aip->goal_point);
11154                 g3_draw_line(&vert1, &vert2);
11155         } */
11156         
11157
11158         Num_AI_debug_render_stuff = 0;
11159 }
11160
11161 #endif
11162
11163 #ifndef NDEBUG
11164 int     Msg_count_4996 = 0;
11165 #endif
11166
11167 //      --------------------------------------------------------------------------
11168 // Process subobjects of object objnum.
11169 //      Deal with engines disabled.
11170 void process_subobjects(int objnum)
11171 {
11172         model_subsystem *psub;
11173         ship_subsys     *pss;
11174         object  *objp = &Objects[objnum];
11175         ship            *shipp = &Ships[objp->instance];
11176         ai_info *aip = &Ai_info[shipp->ai_index];
11177         ship_info       *sip = &Ship_info[shipp->ship_info_index];
11178
11179         for ( pss = GET_FIRST(&shipp->subsys_list); pss !=END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
11180                 psub = pss->system_info;
11181
11182                 // Don't process destroyed objects
11183                 if ( pss->current_hits <= 0.0f ) 
11184                         continue;
11185
11186                 switch (psub->type) {
11187                 case SUBSYSTEM_TURRET:
11188                         if ( psub->turret_num_firing_points > 0 )       {
11189                                 ai_fire_from_turret(shipp, pss, objnum);
11190                         } else {
11191 #ifndef NDEBUG
11192                                 if (!Msg_count_4996) {
11193                                         Warning( LOCATION, "Ship '%s' has turrets with no guns!\nProbably a model problem, so get an artist!", shipp->ship_name );
11194                                         Msg_count_4996++;
11195                                 }
11196 #endif
11197                                 }
11198                         break;
11199
11200                 case SUBSYSTEM_ENGINE:
11201                 case SUBSYSTEM_NAVIGATION:
11202                 case SUBSYSTEM_COMMUNICATION:
11203                 case SUBSYSTEM_WEAPONS:
11204                 case SUBSYSTEM_SENSORS:
11205                 case SUBSYSTEM_UNKNOWN:
11206                         break;
11207
11208                 // next set of subsystems may rotation
11209                 case SUBSYSTEM_RADAR:
11210                 case SUBSYSTEM_SOLAR:
11211 #ifndef MAKE_FS1
11212                 case SUBSYSTEM_GAS_COLLECT:
11213                 case SUBSYSTEM_ACTIVATION:
11214 #endif
11215                         break;
11216                 default:
11217                         Error(LOCATION, "Illegal subsystem type.\n");
11218                 }
11219
11220                 // do solar/radar/gas/activator rotation here
11221                 if ( psub->flags & MSS_FLAG_ROTATES )   {
11222                         if (psub->flags & MSS_FLAG_STEPPED_ROTATE       ) {
11223                                 submodel_stepped_rotate(psub, &pss->submodel_info_1);
11224                         } else {
11225                                 submodel_rotate(psub, &pss->submodel_info_1 );
11226                         }
11227                 }
11228
11229         }
11230
11231         //      Deal with a ship with blown out engines.
11232         if (ship_get_subsystem_strength(shipp, SUBSYSTEM_ENGINE) == 0.0f) {
11233                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
11234                         // AL: Only attack forever if not trying to depart to a docking bay.  Need to have this in, since
11235                         //     a ship may get repaired... and it should still try to depart.  Since docking bay departures
11236                         //     are not handled as goals, we don't want to leave the AIM_BAY_DEPART mode.
11237                         if ( aip->mode != AIM_BAY_DEPART ) {
11238                                 ai_attack_object(objp, NULL, 99, NULL);         //      Regardless of current mode, enter attack mode.
11239                                 aip->submode = SM_ATTACK_FOREVER;                               //      Never leave attack submode, don't avoid, evade, etc.
11240                         }
11241                 }
11242         }
11243
11244
11245 }
11246
11247 //      Given an object and the wing it's in, return its index in the wing list.
11248 //      This defines its location in the wing formation.
11249 //      If the object can't be found in the wing, return -1.
11250 //      *objp           object of interest
11251 //      wingnum the wing *objp is in
11252 int get_wing_index(object *objp, int wingnum)
11253 {
11254         wing    *wingp;
11255         int     i;
11256
11257         SDL_assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11258
11259         wingp = &Wings[wingnum];
11260
11261         for (i=wingp->current_count-1; i>=0; i--)
11262                 if ( objp->instance == wingp->ship_index[i] )
11263                         break;
11264
11265         return i;               //      Note, returns -1 if string not found.
11266 }
11267
11268 //      Given a wing, return a pointer to the object of its leader.
11269 //      Asserts if object not found.
11270 //      Currently, the wing leader is defined as the first object in the wing.
11271 //      wingnum         Wing number in Wings array.
11272 //      If wing leader is disabled, swap it with another ship.
11273 object * get_wing_leader(int wingnum)
11274 {
11275         wing            *wingp;
11276         int             ship_num;
11277
11278         SDL_assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11279
11280         wingp = &Wings[wingnum];
11281
11282         SDL_assert(wingp->current_count != 0);                  //      Make sure there is a leader
11283
11284         ship_num = wingp->ship_index[0];
11285
11286         //      If this ship is disabled, try another ship in the wing.
11287         int n = 0;
11288         while (ship_get_subsystem_strength(&Ships[ship_num], SUBSYSTEM_ENGINE) == 0.0f) {
11289                 n++;
11290                 if (n >= wingp->current_count)
11291                         break;  
11292                 ship_num = wingp->ship_index[n];
11293         }
11294
11295         if (( n != 0) && (n != wingp->current_count)) {
11296                 int t = wingp->ship_index[0];
11297                 wingp->ship_index[0] = wingp->ship_index[n];
11298                 wingp->ship_index[n] = t;
11299         }
11300
11301         return &Objects[Ships[ship_num].objnum];
11302 }
11303
11304 #define DEFAULT_WING_X_DELTA            1.0f
11305 #define DEFAULT_WING_Y_DELTA            0.25f
11306 #define DEFAULT_WING_Z_DELTA            0.75f
11307 #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))
11308 // next constant is higher that MAX_SHIPS_IN_WINGS to deal with forming on player's wing
11309 #define MAX_FORMATION_ROWS              4
11310
11311 //      Given a position in a wing, return the desired location of the ship relative to the leader
11312 //      *_delta_vec             OUTPUT.  delta vector based on wing_index
11313 //      wing_index              position in wing.
11314 void get_wing_delta(vector *_delta_vec, int wing_index)
11315 {
11316         int     wi0;
11317
11318         SDL_assert(wing_index >= 0);
11319
11320         int     k, row, column;
11321
11322         int bank = wing_index / (MAX_FORMATION_ROWS*(MAX_FORMATION_ROWS+1)/2);
11323         wi0 = wing_index % (MAX_FORMATION_ROWS * (MAX_FORMATION_ROWS+1)/2);
11324
11325         k = 0;
11326         for (row=1; row<MAX_FORMATION_ROWS+1; row++) {
11327                 k += row;
11328                 if (wi0 < k)
11329                         break;
11330         }
11331
11332         row--;
11333         column = wi0 - k + row + 1;
11334
11335         _delta_vec->xyz.x = ((float) column - (float) row/2.0f) * DEFAULT_WING_X_DELTA/DEFAULT_WING_MAG;
11336         _delta_vec->xyz.y = ((float)row + (float)bank*2.25f) * DEFAULT_WING_Y_DELTA/DEFAULT_WING_MAG;
11337         _delta_vec->xyz.z = - ((float)row + 0.5f * (float) bank) * DEFAULT_WING_Z_DELTA/DEFAULT_WING_MAG;
11338 }
11339
11340 //      Compute the largest radius of a ship in a *objp's wing.
11341 float gwlr_1(object *objp, ai_info *aip)
11342 {
11343         int             wingnum = aip->wing;
11344         float           max_radius;
11345         object  *o;
11346         ship_obj        *so;
11347
11348         SDL_assert(wingnum >= 0);
11349
11350         max_radius = objp->radius;
11351
11352         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11353                 o = &Objects[so->objnum];
11354                 if (Ai_info[Ships[o->instance].ai_index].wing == wingnum)
11355                         if (o->radius > max_radius)
11356                                 max_radius = o->radius;
11357         }
11358
11359         return max_radius;
11360 }
11361
11362 //      Compute the largest radius of a ship forming on *objp's wing.
11363 float gwlr_object_1(object *objp, ai_info *aip)
11364 {
11365         float           max_radius;
11366         object  *o;
11367         ship_obj        *so;
11368
11369         max_radius = objp->radius;
11370
11371         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11372                 o = &Objects[so->objnum];
11373                 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == OBJ_INDEX(objp))
11374                         if (o->radius > max_radius)
11375                                 max_radius = o->radius;
11376         }
11377
11378         return max_radius;
11379 }
11380
11381 //      For the wing that *objp is part of, return the largest ship radius in that wing.
11382 float get_wing_largest_radius(object *objp, int formation_object_flag)
11383 {
11384         ship            *shipp;
11385         ai_info *aip;
11386
11387         SDL_assert(objp->type == OBJ_SHIP);
11388         SDL_assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
11389         shipp = &Ships[objp->instance];
11390         SDL_assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11391         aip = &Ai_info[shipp->ai_index];
11392
11393         if (formation_object_flag) {
11394                 return gwlr_object_1(objp, aip);
11395         } else {
11396                 return gwlr_1(objp, aip);
11397         }
11398
11399 }
11400
11401 float Wing_y_scale = 2.0f;
11402 float Wing_scale = 1.0f;
11403 DCF(wing_y_scale, "")
11404 {
11405         dc_get_arg(ARG_FLOAT);
11406         Wing_y_scale = Dc_arg_float;
11407 }
11408
11409 DCF(wing_scale, "")
11410 {
11411         dc_get_arg(ARG_FLOAT);
11412         Wing_scale = Dc_arg_float;
11413 }
11414
11415 // Given a wing leader and a position in the wing formation, return the desired absolute location to fly to.
11416 //      Returns result in *result_pos.
11417 void get_absolute_wing_pos(vector *result_pos, object *leader_objp, int wing_index, int formation_object_flag)
11418 {
11419         vector  wing_delta, rotated_wing_delta;
11420         float           wing_spread_size;
11421
11422         get_wing_delta(&wing_delta, wing_index);                //      Desired location in leader's reference frame
11423
11424         wing_spread_size = max(50.0f, 3.0f * get_wing_largest_radius(leader_objp, formation_object_flag) + 15.0f);
11425
11426         // for player obj (1) move ships up 20% (2) scale formation up 20%
11427         if (leader_objp->flags & OF_PLAYER_SHIP) {
11428                 wing_delta.xyz.y *= Wing_y_scale;
11429                 wing_spread_size *= Wing_scale;
11430         }
11431
11432         vm_vec_scale(&wing_delta, wing_spread_size * (1.0f + leader_objp->phys_info.speed/70.0f));
11433
11434         vm_vec_unrotate(&rotated_wing_delta, &wing_delta, &leader_objp->orient);        //      Rotate into leader's reference.
11435
11436         vm_vec_add(result_pos, &leader_objp->pos, &rotated_wing_delta); //      goal_point is absolute 3-space point.
11437 }
11438
11439 #ifndef NDEBUG
11440 int Debug_render_wing_phantoms;
11441
11442 void render_wing_phantoms(object *objp)
11443 {
11444         int             i;
11445         ship            *shipp;
11446         ai_info *aip;
11447         int             wingnum;
11448         int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11449         vector  goal_point;
11450         
11451         SDL_assert(objp->type == OBJ_SHIP);
11452         SDL_assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11453
11454         shipp = &Ships[objp->instance];
11455         SDL_assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11456
11457         aip = &Ai_info[shipp->ai_index];
11458
11459         wingnum = aip->wing;
11460
11461         if (wingnum == -1)
11462                 return;
11463
11464         wing_index = get_wing_index(objp, wingnum);
11465
11466         //      If this ship is NOT the leader, abort.
11467         if (wing_index != 0)
11468                 return;
11469
11470         for (i=0; i<32; i++)
11471                 if (Debug_render_wing_phantoms & (1 << i)) {
11472                         get_absolute_wing_pos(&goal_point, objp, i, 0);
11473         
11474                         vertex  vert;
11475                         gr_set_color(255, 0, 128);
11476                         g3_rotate_vertex(&vert, &goal_point);
11477                         g3_draw_sphere(&vert, 2.0f);
11478                 }
11479
11480         Debug_render_wing_phantoms = 0;
11481
11482 }
11483
11484 void render_wing_phantoms_all()
11485 {
11486         object  *objp;
11487         ship_obj        *so;
11488
11489         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11490                 ship            *shipp;
11491                 ai_info *aip;
11492                 int             wingnum;
11493                 int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11494
11495                 objp = &Objects[so->objnum];
11496                 
11497                 SDL_assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11498                 shipp = &Ships[objp->instance];
11499                 SDL_assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11500
11501                 aip = &Ai_info[shipp->ai_index];
11502
11503                 wingnum = aip->wing;
11504
11505                 if (wingnum == -1)
11506                         continue;
11507
11508                 wing_index = get_wing_index(objp, wingnum);
11509
11510                 //      If this ship is NOT the leader, abort.
11511                 if (wing_index != 0)
11512                         continue;
11513                 
11514                 render_wing_phantoms(objp);
11515
11516                 return;
11517         }
11518 }
11519
11520 #endif
11521
11522 //      Hook from goals code to AI.
11523 //      Force a wing to fly in formation.
11524 //      Sets AIF_FORMATION bit in ai_flags.
11525 //      wingnum         Wing to force to fly in formation
11526 void ai_fly_in_formation(int wingnum)
11527 {
11528         object  *objp;
11529         ship            *shipp;
11530         ship_obj        *so;
11531
11532         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11533                 objp = &Objects[so->objnum];
11534                 SDL_assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11535
11536                 shipp = &Ships[objp->instance];
11537                 SDL_assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11538
11539                 if (Ai_info[shipp->ai_index].wing == wingnum) {
11540                         Ai_info[shipp->ai_index].ai_flags |= AIF_FORMATION_WING;
11541                         Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_OBJECT;
11542                 }
11543         }
11544 }
11545
11546 //      Hook from goals code to AI.
11547 //      Force a wing to abandon formation flying.
11548 //      Clears AIF_FORMATION bit in ai_flags.
11549 //      wingnum         Wing to force to fly in formation
11550 void ai_disband_formation(int wingnum)
11551 {
11552         object  *objp;
11553         ship            *shipp;
11554         ship_obj        *so;
11555
11556         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11557                 objp = &Objects[so->objnum];
11558                 SDL_assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11559
11560                 shipp = &Ships[objp->instance];
11561                 SDL_assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11562
11563                 if (Ai_info[shipp->ai_index].wing == wingnum) {
11564                         Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_WING;
11565                 }
11566         }
11567 }
11568
11569 float   Leader_chaos = 0.0f;
11570 int Chaos_frame = -1;
11571
11572 //      Return true if objp is flying in an erratic manner
11573 //      Only true if objp is a player
11574 int formation_is_leader_chaotic(object *objp)
11575 {
11576         if (Game_mode & GM_MULTIPLAYER)
11577                 return 0;
11578
11579         if (objp != Player_obj)
11580                 return 0;
11581
11582         if (Framecount != Chaos_frame) {
11583                 float   speed_scale;
11584                 float   fdot, udot;
11585
11586                 speed_scale = 3.0f + objp->phys_info.speed * 0.1f;
11587
11588                 fdot = 5.0f * (1.0f - vm_vec_dot(&objp->orient.v.fvec, &objp->last_orient.v.fvec)) * flFrametime;
11589                 udot = 8.0f * (1.0f - vm_vec_dot(&objp->orient.v.uvec, &objp->last_orient.v.uvec)) * flFrametime;
11590
11591                 Leader_chaos += fdot * speed_scale + udot * speed_scale;
11592
11593                 Leader_chaos *= (1.0f - flFrametime*0.2f);
11594
11595                 if (Leader_chaos < 0.0f)
11596                         Leader_chaos = 0.0f;
11597                 else if (Leader_chaos > 1.7f)
11598                         Leader_chaos = 1.7f;
11599
11600                 //nprintf(("AI", "Frame %i: chaos = %7.4f\n", Framecount, Leader_chaos));
11601
11602                 Chaos_frame = Framecount;
11603         }
11604
11605         return (Leader_chaos > 1.0f);
11606 }
11607
11608 // Fly in formation.
11609 //      Make Pl_objp assume its proper place in formation.
11610 //      If the leader of the wing is doing something stupid, like fighting a battle,
11611 //      then the poor sap wingmates will be in for a "world of hurt"
11612 //      Return TRUE if we need to process this object's normal mode
11613 int ai_formation()
11614 {
11615         object  *leader_objp;
11616         ship            *shipp;
11617         ai_info *aip, *laip;
11618         int             wingnum;
11619         int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11620         int             player_wing;    // index of the players wingnum
11621         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;
11622         float           dot_to_goal, dist_to_goal, leader_speed;
11623
11624         SDL_assert(Pl_objp->type == OBJ_SHIP);
11625         SDL_assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_SHIPS));
11626
11627         shipp = &Ships[Pl_objp->instance];
11628
11629         SDL_assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11630
11631         aip = &Ai_info[shipp->ai_index];
11632
11633         SDL_assert((aip->ai_flags & AIF_FORMATION) != AIF_FORMATION);   //      Make sure not both types of formation flying in effect.
11634
11635         //      Determine which kind of formation flying.
11636         //      If tracking an object, not in waypoint mode:
11637         if (aip->ai_flags & AIF_FORMATION_OBJECT) {
11638                 if ((aip->goal_objnum < 0) || (aip->goal_objnum >= MAX_OBJECTS)) {
11639                         aip->ai_flags &= ~AIF_FORMATION_OBJECT;
11640                         return 1;
11641                 }
11642                 
11643                 wing_index = ai_formation_object_get_slotnum(aip->goal_objnum, Pl_objp);
11644                 leader_objp = &Objects[aip->goal_objnum];
11645         } else {        //      Formation flying in waypoint mode.
11646                 SDL_assert(aip->ai_flags & AIF_FORMATION_WING);
11647                 if (aip->mode != AIM_WAYPOINTS) {
11648                         aip->ai_flags &= ~AIF_FORMATION_WING;
11649                         return 1;
11650                 }
11651
11652                 wingnum = aip->wing;
11653
11654                 if (wingnum == -1)
11655                         return 1;
11656
11657                 // disable formation flying for any ship in the players wing
11658                 player_wing = Ships[Player_obj->instance].wingnum;
11659                 if ( (player_wing != -1) && (wingnum == player_wing) )
11660                         return 1;
11661
11662                 wing_index = get_wing_index(Pl_objp, wingnum);
11663
11664                 leader_objp = get_wing_leader(wingnum);
11665
11666         }
11667
11668         //      If docked with a ship in this wing, only the more massive one actually flies in formation.
11669         if (aip->dock_objnum != -1) {
11670                 object  *other_objp = &Objects[aip->dock_objnum];
11671                 ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
11672
11673                 if (aip->wing == other_aip->wing) {
11674                         if (Pl_objp->phys_info.mass < other_objp->phys_info.mass)
11675                                 return 0;
11676                         else if (Pl_objp->phys_info.mass == other_objp->phys_info.mass) {
11677                                 if (Pl_objp->signature < other_objp->signature)
11678                                         return 0;
11679                         }
11680                 }
11681         }
11682
11683         SDL_assert(leader_objp != NULL);
11684         laip = &Ai_info[Ships[leader_objp->instance].ai_index];
11685
11686         //      Make sure we're really in this wing.
11687         if (wing_index == -1)
11688                 return 1;
11689
11690         //      If this ship is the leader, abort, as he doesn't have to follow anyone.
11691         if (wing_index == 0) {
11692                 // nprintf(("AI", "Hmm, wing leader %s in ai_formation for no good reason.\n", shipp->ship_name));
11693                 return 1;
11694         }
11695
11696         if (aip->mode == AIM_WAYPOINTS) {
11697                 aip->wp_list = laip->wp_list;
11698                 if (laip->wp_index < Waypoint_lists[laip->wp_list].count)
11699                         aip->wp_index = laip->wp_index;
11700                 else
11701                         aip->wp_index = Waypoint_lists[laip->wp_list].count - 1;
11702                 aip->wp_flags = laip->wp_flags;
11703                 aip->wp_dir = laip->wp_dir;
11704         }
11705
11706         #ifndef NDEBUG
11707         Debug_render_wing_phantoms |= (1 << wing_index);
11708         #endif
11709
11710         leader_speed = leader_objp->phys_info.speed;
11711         vector leader_vec = leader_objp->phys_info.vel;
11712
11713         get_absolute_wing_pos(&goal_point, leader_objp, wing_index, aip->ai_flags & AIF_FORMATION_OBJECT);
11714         vm_vec_scale_add(&future_goal_point_5, &goal_point, &leader_vec, 10.0f);
11715         vm_vec_scale_add(&future_goal_point_2, &goal_point, &leader_vec, 5.0f);
11716         vm_vec_scale_add(&future_goal_point_x, &goal_point, &leader_objp->orient.v.fvec, 10.0f);        //      used when very close to destination
11717         vm_vec_scale_add(&future_goal_point_1000x, &goal_point, &leader_objp->orient.v.fvec, 1000.0f);  //      used when very close to destination
11718
11719         //      Now, get information telling this object how to turn and accelerate to get to its
11720         //      desired location.
11721         vm_vec_sub(&vec_to_goal, &goal_point, &Pl_objp->pos);
11722         if ( vm_vec_mag_quick(&vec_to_goal) < AICODE_SMALL_MAGNITUDE )
11723                 vec_to_goal.xyz.x += 0.1f;
11724
11725         vm_vec_copy_normalize(&dir_to_goal, &vec_to_goal);
11726         //dot_to_goal = vm_vec_dot(&dir_to_goal, &leader_objp->orient.v.fvec);
11727         dot_to_goal = vm_vec_dot(&dir_to_goal, &Pl_objp->orient.v.fvec);
11728         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &goal_point);
11729         float   dist_to_goal_2 = vm_vec_dist_quick(&Pl_objp->pos, &future_goal_point_2);
11730
11731         // 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));
11732
11733         int     chaotic_leader = 0;
11734
11735         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.
11736
11737         if (dist_to_goal > 500.0f) {
11738                 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11739                 accelerate_ship(aip, 1.0f);
11740         } else if (dist_to_goal > 200.0f) {
11741                 if (dot_to_goal > -0.5f) {
11742                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11743                         float range_speed = shipp->current_max_speed - leader_speed;
11744                         if (range_speed > 0.0f)
11745                                 set_accel_for_target_speed(Pl_objp, leader_speed + range_speed * (dist_to_goal+100.0f)/500.0f);
11746                         else
11747                                 set_accel_for_target_speed(Pl_objp, shipp->current_max_speed);
11748                 } else {
11749                         turn_towards_point(Pl_objp, &future_goal_point_5, NULL, 0.0f);
11750                         if (leader_speed > 10.0f)
11751                                 set_accel_for_target_speed(Pl_objp, leader_speed *(1.0f + dot_to_goal));
11752                         else
11753                                 set_accel_for_target_speed(Pl_objp, 10.0f);
11754                 }
11755         } else {
11756                 vector  v2f2;
11757                 float   dot_to_f2;
11758
11759                 vm_vec_normalized_dir(&v2f2, &future_goal_point_2, &Pl_objp->pos);
11760                 dot_to_f2 = vm_vec_dot(&v2f2, &Pl_objp->orient.v.fvec);
11761
11762                 //      Leader flying like a maniac.  Don't try hard to form on wing.
11763                 if (chaotic_leader) {
11764                         turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11765                         set_accel_for_target_speed(Pl_objp, min(leader_speed*0.8f, 20.0f));
11766                 } else if (dist_to_goal > 75.0f) {
11767                         turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11768                         float   delta_speed;
11769                         float range_speed = shipp->current_max_speed - leader_speed;
11770                         if (range_speed > 0.0f)
11771                                 delta_speed = dist_to_goal_2/500.0f * range_speed;
11772                         else
11773                                 delta_speed = shipp->current_max_speed - leader_speed;
11774                         if (dot_to_goal < 0.0f) {
11775                                 delta_speed = -delta_speed;
11776                                 if (-delta_speed > leader_speed/2)
11777                                         delta_speed = -leader_speed/2;
11778                         }
11779
11780                         if (leader_speed < 5.0f)
11781                                 if (delta_speed < 5.0f)
11782                                         delta_speed = 5.0f;
11783
11784                         float scale = dot_to_f2;
11785                         if (scale < 0.1f)
11786                                 scale = 0.0f;
11787                         else
11788                                 scale *= scale;
11789
11790                         set_accel_for_target_speed(Pl_objp, scale * (leader_speed + delta_speed));
11791                 } else {
11792                         //nprintf(("AI", "Dot = %7.3f\n", dot_to_goal));
11793
11794                         if (leader_speed < 5.0f) {
11795                                 //      Leader very slow.  If not close to goal point, get very close.  Note, keep trying to get close unless
11796                                 //      moving very slowly, else momentum can carry far away from goal.
11797
11798                                 if ((dist_to_goal > 10.0f) || ((Pl_objp->phys_info.speed > leader_speed + 2.5f) && (dot_to_goal > 0.5f))) {
11799                                         //nprintf(("MK", "(1) "));
11800                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11801                                         set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/10.0f);
11802                                 } else {
11803                                         if (Pl_objp->phys_info.speed < 0.5f) {
11804                                                 //nprintf(("MK", "(2) "));
11805                                                 turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11806                                         } else {
11807                                                 //nprintf(("MK", "(3) "));
11808                                         }
11809                                         set_accel_for_target_speed(Pl_objp, leader_speed);
11810                                 }
11811                                 //nprintf(("MK", "dist: %7.3f, dot: %6.3f, speed: %7.3f\n", dist_to_goal, dot_to_goal, Pl_objp->phys_info.speed));
11812                         } else if (dist_to_goal > 10.0f) {
11813                                 float   dv;
11814
11815                                 //future_goal_point_2;
11816
11817                                 turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11818
11819                                 if (dist_to_goal > 25.0f) {
11820                                         if (dot_to_goal < 0.3f)
11821                                                 dv = -0.1f;
11822                                         else
11823                                                 dv = dot_to_goal - 0.2f;
11824
11825                                         set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/5.0f * dv);
11826                                 } else {
11827                                         set_accel_for_target_speed(Pl_objp, leader_speed + 1.5f * dot_to_goal - 1.0f);
11828                                 }
11829                         } else {
11830                                 if (Pl_objp->phys_info.speed < 0.1f)
11831                                         turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11832                                 else
11833                                         turn_towards_point(Pl_objp, &future_goal_point_x, NULL, 0.0f);
11834                                 set_accel_for_target_speed(Pl_objp, 0.0f);
11835                         }
11836                 }
11837
11838         }
11839
11840         //      See how different this ship's bank is relative to wing leader
11841         float   up_dot = vm_vec_dot(&leader_objp->orient.v.uvec, &Pl_objp->orient.v.uvec);
11842         if (up_dot < 0.996f) {
11843                 vector  w_out;
11844                 matrix  new_orient;
11845                 vector  angular_accel;
11846
11847                 vm_vec_copy_scale(&angular_accel, &Pl_objp->phys_info.max_rotvel, 0.2f);
11848                 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);
11849
11850         //      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)));
11851                 Pl_objp->orient = new_orient;
11852                 Pl_objp->phys_info.rotvel = w_out;
11853         //      Pl_objp->phys_info.desired_rotvel = w_out;
11854         } else {
11855                 Pl_objp->phys_info.rotvel.xyz.z = 0.0f;
11856         }
11857
11858         return 0;
11859 }
11860
11861 //      Return index of object repairing object objnum.
11862 int find_repairing_objnum(int objnum)
11863 {
11864         object          *objp;
11865         ship                    *shipp;
11866         ship_info       *sip;
11867         ship_obj                *so;
11868
11869         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11870                 objp = &Objects[so->objnum];
11871
11872                 SDL_assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11873
11874                 shipp = &Ships[objp->instance];
11875                 sip = &Ship_info[shipp->ship_info_index];
11876
11877                 if (sip->flags & SIF_SUPPORT) {
11878                         ai_info *aip;
11879
11880                         aip = &Ai_info[shipp->ai_index];
11881
11882                         if (aip->goal_objnum == objnum) {
11883                                 return objp-Objects;
11884                         }
11885                 }
11886         }
11887
11888         return -1;
11889 }
11890
11891 //      If object *objp is being repaired, deal with it!
11892 void ai_do_repair_frame(object *objp, ai_info *aip, float frametime)
11893 {
11894         if (Ships[objp->instance].team == TEAM_TRAITOR) {
11895                 ai_abort_rearm_request(objp);
11896                 return;
11897         }
11898
11899         if (aip->ai_flags & (AIF_BEING_REPAIRED | AIF_AWAITING_REPAIR)) {
11900                 int     dock_objnum;
11901                 ai_info *repair_aip;
11902
11903                 dock_objnum = aip->dock_objnum; // find_repairing_objnum(objp-Objects);
11904                 //SDL_assert(dock_objnum != -1);
11905                 if (dock_objnum == -1)
11906                         return;
11907                 if (Objects[dock_objnum].signature != aip->dock_signature) {
11908                         Int3();         //      Curious -- object numbers match, but signatures do not.
11909                                                         //      Must mean original repair ship died and was replaced by current ship.
11910                         return;
11911                 }
11912         
11913                 repair_aip = &Ai_info[Ships[Objects[dock_objnum].instance].ai_index];
11914                 //SDL_assert(repair_aip->mode == AIM_DOCK);
11915
11916                 if (aip->ai_flags & AIF_BEING_REPAIRED) {
11917                         // SDL_assert(repair_aip->submode == AIS_DOCK_4);
11918
11919                         //      Wait awhile into the mode to synchronize with sound effect.
11920                         if (Missiontime - repair_aip->submode_start_time > REARM_SOUND_DELAY) {
11921                                 int repaired;
11922
11923                                 repaired = ship_do_rearm_frame( objp, frametime );              // hook to do missile rearming
11924
11925                                 //      See if fully repaired.  If so, cause process to stop.
11926                                 if ( repaired && (repair_aip->submode == AIS_DOCK_4)) {
11927
11928                                         repair_aip->submode = AIS_UNDOCK_0;
11929                                         repair_aip->submode_start_time = Missiontime;
11930
11931                                         // if repairing player object -- tell him done with repair
11932                                         if ( !MULTIPLAYER_CLIENT ){
11933                                                 ai_do_objects_repairing_stuff( objp, &Objects[dock_objnum], REPAIR_INFO_COMPLETE );
11934                                         }
11935                                 }
11936                         }
11937                 } else if (aip->ai_flags & AIF_AWAITING_REPAIR) {
11938                         //      If this ship has been awaiting repair for 90+ seconds, abort.
11939                         if ( !MULTIPLAYER_CLIENT ) {
11940                                 if ((Game_mode & GM_MULTIPLAYER) || (objp != Player_obj)) {
11941                                         if ((repair_aip->goal_objnum == OBJ_INDEX(objp)) && (timestamp_elapsed(aip->abort_rearm_timestamp))) {
11942                                                 ai_abort_rearm_request(objp);
11943                                                 aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);
11944                                         }
11945                                 }
11946                         }
11947                 }
11948         } else {
11949                 // AL 11-24-97: If this is the player ship, ensure the repair sound isn't playing.  We need to
11950                 //              do this check, since this is a looping sound, and may continue on if rearm/repair
11951                 //              finishes abnormally once sound begins looping.
11952                 if ( objp == Player_obj ) {
11953                         player_stop_repair_sound();
11954                 }
11955         }
11956 }
11957
11958 //      Shell around dock_orient_and_approach to detect whether dock process should be aborted.
11959 //      obj1 is the ship performing the repair.
11960 //      obj2 is the ship being repaired.
11961 void call_doa(object *obj1, object *obj2, ship_info *sip1)
11962 {
11963         if (sip1->flags & SIF_SUPPORT) {
11964                 if (obj2->phys_info.speed > MAX_REPAIR_SPEED) {
11965
11966                         // call the ai_abort rearm request code
11967                         ai_abort_rearm_request( obj2 );
11968                 } else
11969                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11970         } else {
11971                 if (Ship_info[Ships[obj1->instance].ship_info_index].flags & SIF_CARGO)
11972                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11973                 else if (Ship_info[Ships[obj2->instance].ship_info_index].flags & SIF_CARGO)
11974                         dock_orient_and_approach(obj2, obj1, DOA_DOCK_STAY);
11975                 else {
11976                         //mprintf(("Warning: Not sure, but making %s [%s] move to stay docked with %s [%s]\n",
11977                         //      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));
11978                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11979
11980                 }
11981         }
11982
11983 }
11984
11985 //      Maybe launch a countermeasure.
11986 //      Also, detect a supposed homing missile that no longer exists.
11987 void ai_maybe_launch_cmeasure(object *objp, ai_info *aip)
11988 {
11989         float                   dist;
11990         ship_info       *sip;
11991         ship                    *shipp;
11992
11993         shipp = &Ships[objp->instance];
11994         sip = &Ship_info[shipp->ship_info_index];
11995
11996         if (!(sip->flags & (SIF_SMALL_SHIP | SIF_TRANSPORT)))
11997                 return;
11998
11999         if (!shipp->cmeasure_count)
12000                 return;
12001
12002         if ( !timestamp_elapsed(shipp->cmeasure_fire_stamp) )
12003                 return;
12004
12005         //      If not on player's team and Skill_level + ai_class is low, never fire a countermeasure.  The ship is too dumb.
12006         if (shipp->team != Player_ship->team) {
12007                 if (Game_skill_level + aip->ai_class < 4){
12008                         return;
12009                 }
12010         }
12011
12012         if ((aip->nearest_locked_object != -1) && (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)) {
12013                 object  *weapon_objp;
12014
12015                 weapon_objp = &Objects[aip->nearest_locked_object];
12016
12017                 if ((dist = vm_vec_dist_quick(&objp->pos, &weapon_objp->pos)) < weapon_objp->phys_info.speed*2.0f) {
12018         
12019                         aip->nearest_locked_distance = dist;
12020                         //      Verify that this object is really homing on us.
12021                         object  *weapon_objp;
12022
12023                         weapon_objp = &Objects[aip->nearest_locked_object];
12024
12025                         float   fire_chance;
12026
12027                         //      For ships on player's team, have constant, average chance to fire.
12028                         //      For enemies, increasing chance with higher skill level.
12029                         if (shipp->team == Player_ship->team)
12030                                 fire_chance = Cmeasure_fire_chance[NUM_SKILL_LEVELS/2];
12031                         else
12032                                 fire_chance = Cmeasure_fire_chance[Game_skill_level];
12033
12034                         //      Decrease chance to fire at lower ai class.
12035                         fire_chance *= (float) aip->ai_class/Num_ai_classes;
12036
12037                         float r = frand();
12038                         if (fire_chance < r) {
12039                                 //nprintf(("AI", "Not firing countermeasure due to skill level: %7.3f < %7.3f\n", fire_chance, r));
12040                                 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.
12041                                 return;
12042                         }
12043
12044                         if (weapon_objp->type == OBJ_WEAPON) {
12045                                 if (weapon_objp->instance >= 0) {
12046                                         //nprintf(("AI", "Firing countermeasure at time t=%7.3f\n", f2fl(Missiontime)));
12047                                         ship_launch_countermeasure(objp);
12048                                         shipp->cmeasure_fire_stamp = timestamp(2*CMEASURE_WAIT);
12049                                         return;
12050                                 }
12051                         }
12052         
12053                 }
12054         }
12055
12056         return;
12057 }
12058
12059 //      --------------------------------------------------------------------------
12060 void ai_preprocess_ignore_objnum(object *objp, ai_info *aip)
12061 {
12062 //      if (aip->ignore_objnum == UNUSED_OBJNUM)
12063 //              return;
12064
12065         if (aip->ai_flags & AIF_TEMPORARY_IGNORE) {
12066                 if (timestamp_elapsed(aip->ignore_expire_timestamp)) {
12067                         aip->ignore_objnum = UNUSED_OBJNUM;
12068                 }
12069         }
12070
12071         if (is_ignore_object(aip, aip->goal_objnum)) {
12072                 aip->goal_objnum = -1;
12073                 // AL 12-11-97: If in STRAFE mode, we need to ensure that target_objnum is also
12074                 //              set to -1
12075                 if ( aip->mode == AIM_STRAFE ) {
12076                         aip->target_objnum = -1;
12077                 }
12078         }
12079
12080         if (is_ignore_object(aip, aip->target_objnum))
12081                 aip->target_objnum = -1;
12082 }
12083
12084 /*
12085 void ai_safety_circle_spot()
12086 {
12087         vector  goal_point;
12088         ship_info       *sip;
12089
12090         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12091
12092         goal_point = Ai_info[Ships[Pl_objp->instance].ai_index].goal_point;
12093         turn_towards_tangent(Pl_objp, &goal_point, 50.0f);
12094
12095         set_accel_for_target_speed(Pl_objp, sip->max_speed/4.0f);
12096
12097 //      float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
12098 //      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));
12099
12100 }
12101 */
12102
12103 #define CHASE_CIRCLE_DIST               100.0f
12104
12105 void ai_chase_circle(object *objp)
12106 {
12107         float           dist_to_goal;
12108         float           target_speed;
12109         vector  goal_point;
12110         ship_info       *sip;
12111         ai_info         *aip;
12112
12113         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12114
12115         target_speed = sip->max_speed/4.0f;
12116         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12117
12118         SDL_assert(vm_vec_mag(&aip->goal_point) >= 0.0f);               //      Supposedly detects bogus vector
12119
12120         goal_point = aip->goal_point;
12121
12122         if (aip->ignore_objnum == UNUSED_OBJNUM) {
12123                 dist_to_goal = vm_vec_dist_quick(&aip->goal_point, &objp->pos);
12124
12125                 if (dist_to_goal > 2*CHASE_CIRCLE_DIST) {
12126                         vector  vec_to_goal;
12127                         //      Too far from circle goal, create a new goal point.
12128                         vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
12129                         vm_vec_scale_add(&aip->goal_point, &objp->pos, &vec_to_goal, CHASE_CIRCLE_DIST);
12130                 }
12131
12132                 goal_point = aip->goal_point;
12133         } else if (is_ignore_object(aip, aip->ignore_objnum)) {
12134                 object  *ignore_objp = &Objects[aip->ignore_objnum];
12135
12136                 vector  tvec1;
12137                 float           dist;
12138
12139                 dist = vm_vec_normalized_dir(&tvec1, &Pl_objp->pos, &ignore_objp->pos);
12140
12141                 if (dist < ignore_objp->radius*2 + 1500.0f) {
12142                         vm_vec_scale_add(&goal_point, &Pl_objp->pos, &tvec1, ignore_objp->radius*2 + 1400.0f);
12143                         if (dist < ignore_objp->radius*2 + 1300.0f)
12144                                 target_speed = sip->max_speed * (1.25f - dist/(ignore_objp->radius*2 + 1500.0f));
12145                 }
12146         }
12147
12148         SDL_assert(vm_vec_mag(&aip->goal_point) >= 0.0f);               //      Supposedly detects bogus vector
12149
12150         turn_towards_tangent(Pl_objp, &goal_point, 10*objp->radius + 200.0f);
12151
12152         set_accel_for_target_speed(Pl_objp, target_speed);
12153
12154 }
12155
12156 #define SHIELD_BALANCE_RATE     0.2f            //      0.1f -> takes 10 seconds to equalize shield.
12157
12158 //      Transfer shield energy to most recently hit section from others.
12159 void ai_transfer_shield(object *objp, int quadrant_num)
12160 {
12161         int     i;
12162         float   transfer_amount;
12163         float   transfer_delta;
12164         ship_info       *sip;
12165         float   max_quadrant_strength;
12166
12167         sip = &Ship_info[Ships[objp->instance].ship_info_index];
12168         max_quadrant_strength = sip->shields/MAX_SHIELD_SECTIONS;
12169
12170         transfer_amount = 0.0f;
12171         transfer_delta = (SHIELD_BALANCE_RATE/2) * max_quadrant_strength;
12172
12173         if (objp->shields[quadrant_num] + (MAX_SHIELD_SECTIONS-1)*transfer_delta > max_quadrant_strength)
12174                 transfer_delta = (max_quadrant_strength - objp->shields[quadrant_num])/(MAX_SHIELD_SECTIONS-1);
12175
12176         for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12177                 if (i != quadrant_num) {
12178                         if (objp->shields[i] >= transfer_delta) {
12179                                 objp->shields[i] -= transfer_delta;
12180                                 transfer_amount += transfer_delta;
12181                         } else {
12182                                 transfer_amount += objp->shields[i];
12183                                 objp->shields[i] = 0.0f;
12184                         }
12185                 }
12186
12187         objp->shields[quadrant_num] += transfer_amount;
12188 }
12189
12190 void ai_balance_shield(object *objp)
12191 {
12192         int     i;
12193         float   shield_strength_avg;
12194         float   delta;
12195
12196
12197         shield_strength_avg = get_shield_strength(objp)/MAX_SHIELD_SECTIONS;
12198
12199         delta = SHIELD_BALANCE_RATE * shield_strength_avg;
12200
12201         for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12202                 if (objp->shields[i] < shield_strength_avg) {
12203                         add_shield_strength(objp, delta);
12204                         if (objp->shields[i] > shield_strength_avg)
12205                                 objp->shields[i] = shield_strength_avg;
12206                 } else {
12207                         add_shield_strength(objp, -delta);
12208                         if (objp->shields[i] < shield_strength_avg)
12209                                 objp->shields[i] = shield_strength_avg;
12210                 }
12211 }
12212
12213 //      Manage the shield for this ship.
12214 //      Try to max out the side that was most recently hit.
12215 void ai_manage_shield(object *objp, ai_info *aip)
12216 {
12217         ship_info *sip;
12218
12219         sip = &Ship_info[Ships[objp->instance].ship_info_index];
12220
12221         if (timestamp_elapsed(aip->shield_manage_timestamp)) {
12222                 float           delay;
12223
12224                 //      Scale time until next manage shield based on Skill_level.
12225                 //      Ships on player's team are treated as if Skill_level is average.
12226                 if (Ships[objp->instance].team != Player_ship->team){
12227                         delay = Shield_manage_delays[Game_skill_level];
12228                 } else {
12229                         delay = Shield_manage_delays[NUM_SKILL_LEVELS/2];
12230                 }
12231
12232                 //      Scale between 1x and 3x based on ai_class
12233                 delay = delay + delay * (float) (3*(Num_ai_classes - aip->ai_class - 1) / (Num_ai_classes - 1));
12234                 aip->shield_manage_timestamp = timestamp((int) (delay * 1000.0f));
12235
12236                 if (sip->flags & SIF_SMALL_SHIP) {
12237                         if (Missiontime - aip->last_hit_time < F1_0*10)
12238                                 ai_transfer_shield(objp, aip->last_hit_quadrant);
12239                         else
12240                                 ai_balance_shield(objp);
12241                 }
12242
12243                 // 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]));
12244         }
12245 }
12246
12247 //      See if object *objp should evade an incoming missile.
12248 //      *aip is the ai_info pointer within *objp.
12249 void ai_maybe_evade_locked_missile(object *objp, ai_info *aip)
12250 {
12251         ship                    *shipp;
12252
12253         shipp = &Ships[objp->instance];
12254
12255         //      Only small ships evade an incoming missile.  Why would a capital ship try to swerve?
12256         if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12257                 return;
12258         }
12259
12260         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
12261                 return;
12262         }
12263
12264         if (aip->nearest_locked_object != -1) {
12265                 object  *missile_objp;
12266
12267                 missile_objp = &Objects[aip->nearest_locked_object];
12268
12269                 if (Weapons[missile_objp->instance].homing_object != objp) {
12270                         //nprintf(("AI", "\nMissile lost home!"));
12271                         aip->nearest_locked_object = -1;
12272                         return;
12273                 }
12274
12275                 if ((missile_objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[missile_objp->instance].weapon_info_index].wi_flags & WIF_HOMING)) {
12276                         float dist = vm_vec_dist_quick(&missile_objp->pos, &objp->pos);
12277                         float dist2 = 4.0f  * vm_vec_mag_quick(&missile_objp->phys_info.vel);                   
12278                         if (dist < dist2) {
12279                                 switch (aip->mode) {
12280                                 //      If in AIM_STRAFE mode, don't evade if parent of weapon is targeted ship.
12281                                 case AIM_STRAFE:
12282                                         if ((missile_objp->parent != -1) && (missile_objp->parent == aip->target_objnum)) {
12283                                                 ;
12284                                         } else {
12285                                                 ;               //      Alan -- If you want to handle incoming weapons from someone other than the ship
12286                                                                 //      the strafing ship is attacking, do it here.
12287                                         }
12288                                         break;
12289                                 case AIM_CHASE:
12290                                         //      Don't always go into evade weapon mode.  Usually, a countermeasure gets launched.
12291                                         // If low on countermeasures, more likely to try to evade.  If 8+, never evade due to low cmeasures.
12292                                         if (((((Missiontime >> 18) ^ OBJ_INDEX(objp)) & 3) == 0) || 
12293                                                 (objp->phys_info.speed < 40.0f) ||
12294                                                 (frand() < 1.0f - (float) shipp->cmeasure_count/8.0f)) {
12295                                                 if (aip->submode != SM_ATTACK_FOREVER) {        //      SM_ATTACK_FOREVER means engines blown.
12296                                                         aip->submode = SM_EVADE_WEAPON;
12297                                                         aip->submode_start_time = Missiontime;
12298                                                 }
12299                                         }
12300                                         break;
12301                                 case AIM_DOCK:  //      Ships in dock mode can evade iif they are not currently repairing or docked.
12302                                         if (aip->ai_flags & (AIF_REPAIRING | AIF_DOCKED))
12303                                                 break;
12304                                 case AIM_GUARD:
12305                                         //      If in guard mode and far away from guard object, don't pursue guy that hit me.
12306                                         if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12307                                                 if (vm_vec_dist_quick(&objp->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12308                                                         return;
12309                                                 }
12310                                         }
12311                                 case AIM_EVADE:
12312                                 case AIM_GET_BEHIND:
12313                                 case AIM_STAY_NEAR:
12314                                 case AIM_STILL:
12315                                 case AIM_AVOID:
12316                                 case AIM_WAYPOINTS:
12317                                 case AIM_NONE:
12318                                 case AIM_BIGSHIP:
12319                                 case AIM_PATH:
12320                                 case AIM_BE_REARMED:
12321                                 case AIM_SAFETY:
12322                                 case AIM_BAY_EMERGE:
12323                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12324                                         aip->previous_mode = aip->mode;
12325                                         aip->previous_submode = aip->submode;
12326                                         aip->mode = AIM_EVADE_WEAPON;
12327                                         aip->submode = -1;
12328                                         aip->submode_start_time = Missiontime;
12329                                         aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Max time to evade.
12330                                         //nprintf(("AI", "%s Evade weapon in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
12331                                         break;
12332                                 case AIM_EVADE_WEAPON:          //      Note: We don't want to change mode on another evasion, or previous_mode will get bashed.
12333                                 case AIM_PLAY_DEAD:
12334                                 case AIM_BAY_DEPART:
12335                                 case AIM_SENTRYGUN:
12336                                         break;
12337                                 case AIM_WARP_OUT:
12338                                         break;
12339                                 default:
12340                                         Int3();                 //      Hey, what mode is it?
12341                                         break;
12342                                 }
12343                         }
12344                 } else {
12345                         aip->nearest_locked_object = -1;
12346                 }
12347         }
12348 }
12349
12350 //      Maybe evade a dumbfire weapon that was fired when Pl_objp was targeted.
12351 //      Have an 80% chance of evading in a second
12352 void maybe_evade_dumbfire_weapon(ai_info *aip)
12353 {
12354         //      Only small ships evade an incoming missile.  Why would a capital ship try to swerve?
12355         if (!(Ship_info[Ships[Pl_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12356                 return;
12357         }
12358
12359         //      Make sure in a mode in which we evade dumbfire weapons.
12360         switch (aip->mode) {
12361         case AIM_CHASE:
12362                 if (aip->submode == SM_ATTACK_FOREVER) {
12363                         return;
12364                 }
12365         case AIM_GUARD:
12366                 //      If in guard mode and far away from guard object, don't pursue guy that hit me.
12367                 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12368                         if (vm_vec_dist_quick(&Objects[Ships[aip->shipnum].objnum].pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12369                                 return;
12370                         }
12371                 }
12372         case AIM_STILL:
12373         case AIM_STAY_NEAR:
12374         case AIM_EVADE:
12375         case AIM_GET_BEHIND:
12376         case AIM_AVOID:
12377         case AIM_PATH:
12378         case AIM_NONE:
12379         case AIM_WAYPOINTS:
12380         case AIM_SAFETY:
12381                 break;
12382         case AIM_STRAFE:
12383         case AIM_BIGSHIP:
12384         case AIM_DOCK:
12385         case AIM_PLAY_DEAD:
12386         case AIM_EVADE_WEAPON:
12387         case AIM_BAY_EMERGE:
12388         case AIM_BAY_DEPART:
12389         case AIM_SENTRYGUN:
12390         case AIM_WARP_OUT:
12391                 return;
12392         default:
12393                 Int3(); //      Bogus mode!
12394                 return;
12395         }
12396
12397         if (is_instructor(&Objects[Ships[aip->shipnum].objnum]))
12398                 return; //      Instructor doesn't evade.
12399
12400         float t = ai_endangered_by_weapon(aip);
12401         if ((t > 0.0f) && (t < 1.0f)) {
12402         // Check if this weapon is from a large ship Pl_objp is attacking... if so, enter strafe mode
12403                 if ( ai_big_maybe_enter_strafe_mode(Pl_objp, aip->danger_weapon_objnum) ) {
12404                         return;
12405                 }
12406
12407                 switch (aip->mode) {
12408                 case AIM_CHASE:
12409                         switch (aip->submode) {
12410                         case SM_EVADE:
12411                         case SM_ATTACK_FOREVER:
12412                         case SM_AVOID:
12413                         case SM_GET_AWAY:
12414                         case SM_EVADE_WEAPON:
12415                                 break;
12416                         default:
12417                                 if (ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
12418                                         //mprintf(("Ship %s entered super mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12419                                         aip->submode = SM_SUPER_ATTACK;
12420                                         aip->submode_start_time = Missiontime;
12421                                         aip->last_attack_time = Missiontime;
12422                                 } else {
12423                                         //mprintf(("Ship %s entered dumbfire evade mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12424                                         aip->submode = SM_EVADE_WEAPON;
12425                                         aip->submode_start_time = Missiontime;
12426                                 }
12427                                 break;
12428                         }
12429                         break;
12430                 case AIM_GUARD:
12431                 case AIM_STILL:
12432                 case AIM_STAY_NEAR:
12433                 case AIM_EVADE:
12434                 case AIM_GET_BEHIND:
12435                 case AIM_AVOID:
12436                 case AIM_PATH:
12437                 case AIM_NONE:
12438                 case AIM_WAYPOINTS:
12439                 case AIM_SAFETY:
12440                         if (!(aip->ai_flags & (AIF_NO_DYNAMIC | AIF_KAMIKAZE)) && (Ship_info[Ships[aip->shipnum].ship_info_index].flags & SIF_SMALL_SHIP)) {
12441                                 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12442                                 aip->previous_mode = aip->mode;
12443                                 aip->previous_submode = aip->submode;
12444                                 aip->mode = AIM_EVADE_WEAPON;
12445                                 aip->submode = -1;
12446                                 aip->submode_start_time = Missiontime;
12447                                 aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Evade for up to five seconds.
12448                         }
12449                         break;
12450                 case AIM_STRAFE:
12451                 case AIM_BIGSHIP:
12452                 case AIM_DOCK:
12453                 case AIM_PLAY_DEAD:
12454                 case AIM_EVADE_WEAPON:
12455                 case AIM_BAY_EMERGE:
12456                 case AIM_BAY_DEPART:
12457                 case AIM_SENTRYGUN:
12458                         break;
12459                 default:
12460                         Int3(); //      Bogus mode!
12461                 }
12462         }
12463 }
12464
12465 // determine what path to use when emerging from a fighter bay
12466 // input:       pl_objp =>      pointer to object for ship that is arriving
12467 //                              pos             =>      output parameter, it is the starting world pos for path choosen
12468 //                              v.fvec          =>      output parameter, this is the forward vector that ship has when arriving
12469 //
12470 // exit:                -1              =>      path could not be located
12471 //                               0              => success
12472 int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, vector *pos, vector *fvec)
12473 {
12474         int                     path_index, sb_path_index;
12475         ship                    *parent_sp = NULL;
12476         polymodel       *pm;
12477         ai_info         *aip;
12478         ship_bay                *sb;
12479         pnode                   *pnp;
12480         vector          *next_point;
12481
12482         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12483
12484         if ( parent_objnum == -1 ) {
12485                 Int3();
12486                 return -1;
12487         }
12488
12489         parent_sp = &Ships[Objects[parent_objnum].instance];
12490
12491         SDL_assert(parent_sp != NULL);
12492         pm = model_get( parent_sp->modelnum );
12493         sb = pm->ship_bay;
12494
12495         if ( sb == NULL ) 
12496                 return -1;
12497
12498         if ( sb->num_paths <= 0 ) 
12499                 return -1;
12500
12501         // try to find a bay path that is not taken
12502         sb_path_index = Ai_last_arrive_path++;
12503
12504         if ( sb_path_index >= sb->num_paths ) {
12505                 sb_path_index=0;
12506                 Ai_last_arrive_path=0;
12507         }
12508
12509         path_index = sb->paths[sb_path_index];
12510         if ( path_index == -1 ) 
12511                 return -1;
12512
12513         // create the path for pl_objp to follow
12514         create_model_exit_path(pl_objp, &Objects[parent_objnum], path_index, pm->paths[path_index].nverts);
12515         
12516         // Set this flag, so we don't bother recreating the path... we won't need to update the path
12517         // that has just been created.
12518 //      aip->ai_flags |= AIF_USE_STATIC_PATH;
12519
12520         // now return to the caller what the starting world pos and starting fvec for the ship will be
12521         SDL_assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
12522         pnp = &Path_points[aip->path_start];
12523         *pos = pnp->pos;
12524
12525         // calc the forward vector using the starting two points of the path
12526         pnp = &Path_points[aip->path_start+1];
12527         next_point = &pnp->pos;
12528         vm_vec_normalized_dir(fvec, next_point, pos);
12529
12530         // record the parent objnum, since we'll need it once we're done with following the path
12531         aip->goal_objnum = parent_objnum;
12532         aip->goal_signature = Objects[parent_objnum].signature;
12533         aip->mode = AIM_BAY_EMERGE;
12534         aip->submode_start_time = Missiontime;
12535
12536         // set up starting vel
12537         vector vel;
12538         float speed;
12539         speed = Ship_info[Ships[pl_objp->instance].ship_info_index].max_speed;
12540         vel = *fvec;
12541         vm_vec_scale( &vel, speed );
12542         pl_objp->phys_info.vel = vel;
12543         pl_objp->phys_info.desired_vel = vel;
12544         pl_objp->phys_info.prev_ramp_vel.xyz.x = 0.0f;
12545         pl_objp->phys_info.prev_ramp_vel.xyz.y = 0.0f;
12546         pl_objp->phys_info.prev_ramp_vel.xyz.z = speed;
12547         pl_objp->phys_info.forward_thrust = 0.0f;               // How much the forward thruster is applied.  0-1.
12548
12549         return 0;       
12550 }
12551
12552 // clean up path data used for emerging from a fighter bay
12553 void ai_emerge_bay_path_cleanup(ai_info *aip)
12554 {
12555         aip->path_start = -1;
12556         aip->path_cur = -1;
12557         aip->path_length = 0;
12558         aip->mode = AIM_NONE;
12559 }
12560
12561 // handler for AIM_BAY_EMERGE
12562 void ai_bay_emerge()
12563 {
12564         ai_info *aip;
12565         int             parent_died=0;
12566
12567         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12568
12569         // if no path to follow, leave this mode
12570         if ( aip->path_start < 0 ) {
12571                 aip->mode = AIM_NONE;
12572                 return;
12573         }
12574
12575         // ensure parent ship is still alive
12576         if ( aip->goal_objnum < 0 ) {
12577                 parent_died=1;
12578         } 
12579         if ( !parent_died ) {
12580                 if ( Objects[aip->goal_objnum].signature != aip->goal_signature ) {
12581                         parent_died=1;
12582                 }
12583         }
12584
12585         if ( !parent_died ) {
12586                 SDL_assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
12587                 if ( Ships[Objects[aip->goal_objnum].instance].flags & SF_DYING ) {
12588                         parent_died = 1;
12589                 }
12590         }
12591
12592         if ( parent_died ) {
12593                 ai_emerge_bay_path_cleanup(aip);
12594                 return;
12595         }
12596
12597         // follow the path to the final point
12598         ai_path();
12599
12600         // New test: must have been in AI_EMERGE mode for at least 10 seconds, and be a minimum distance from the start point
12601         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)) {
12602                 // erase path
12603                 ai_emerge_bay_path_cleanup(aip);
12604         }
12605
12606         // 2-25-99: Need this check to fix an assert for supercap ships... maybe we'll only do this check for supercaps 
12607         if (aip->path_cur > (aip->path_start+aip->path_length-1)) {
12608                 ai_emerge_bay_path_cleanup(aip);
12609         }       
12610 }
12611
12612 // Select the closest depart path
12613 //
12614 //      input:  aip     =>              ai info pointer to ship seeking to depart
12615 //                              pm              =>              pointer to polymodel for the ship contining the ship bay/depart paths
12616 //
12617 // exit:                >=0     =>              ship bay path index for depart path (ie index into sb->paths[])
12618 //                              -1              =>              no path could be found
12619 //
12620 // NOTE: this function should only be used for calculating closest depart paths for ai mode
12621 //                      AI_BAY_DEPART.  It tries to find the closest path that isn't already in use
12622 int ai_find_closest_depart_path(ai_info *aip, polymodel *pm)
12623 {
12624         int                     i, j, best_path, best_free_path;
12625         float                   dist, min_dist, min_free_dist;
12626         vector          *source;
12627         model_path      *mp;
12628         ship_bay                *sb;
12629
12630         sb = pm->ship_bay;
12631
12632         best_free_path = best_path = -1;
12633         min_free_dist = min_dist = 1e20f;
12634         SDL_assert(aip->shipnum >= 0);
12635         source = &Objects[Ships[aip->shipnum].objnum].pos;
12636
12637         for ( i = 0; i < sb->num_paths; i++ ) {
12638
12639
12640                 mp = &pm->paths[sb->paths[i]];
12641                 for ( j = 0; j < mp->nverts; j++ ) {
12642                         dist = vm_vec_dist_squared(source, &mp->verts[j].pos);
12643
12644                         if ( dist < min_dist ) {
12645                                 min_dist = dist;
12646                                 best_path = i;
12647                         }
12648
12649                         // If this is a free path
12650                         if ( !(sb->depart_flags & (1<<i)) ) {
12651                                 if ( dist < min_free_dist ) {
12652                                         min_free_dist = dist;
12653                                         best_free_path = i;
12654                                 }
12655                         }
12656                 }
12657         }
12658
12659         if ( best_free_path >= 0 ) {
12660                 return best_free_path;          
12661         }
12662
12663         return best_path;
12664 }
12665
12666 // determine what path to use when trying to depart to a fighter bay
12667 // NOTE: this should be called when AIM_BAY_DEPART mode is set
12668 //
12669 // input:       pl_objp =>      pointer to object for ship that is departing
12670 //
12671 // exit:                -1      =>      could not find depart path
12672 //                              0       => found depart path
12673 int ai_acquire_depart_path(object *pl_objp, int parent_objnum)
12674 {
12675         int                     objnum, path_index;
12676         polymodel       *pm;
12677         ai_info         *aip;
12678         ship                    *sp;
12679         ship_bay                *sb;
12680
12681         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12682
12683         if ( parent_objnum == -1 ) {
12684                 ship_obj        *so;
12685
12686                 // for now just locate a captial ship on the same team:
12687                 so = GET_FIRST(&Ship_obj_list);
12688                 objnum = -1;
12689                 while(so != END_OF_LIST(&Ship_obj_list)){
12690                         sp = &Ships[Objects[so->objnum].instance];
12691                         if ( (Ship_info[sp->ship_info_index].flags & (SIF_HUGE_SHIP)) && (sp->team == Ships[pl_objp->instance].team) ) {
12692                                 objnum = so->objnum;
12693                                 break;
12694                         }
12695                         so = GET_NEXT(so);
12696                 } 
12697         } else {
12698                 objnum = parent_objnum;
12699         }
12700
12701         aip->path_start = -1;
12702
12703         if ( objnum == -1 )
12704                 return -1;
12705
12706         pm = model_get( Ships[Objects[objnum].instance].modelnum );
12707         sb = pm->ship_bay;
12708
12709         if ( sb == NULL ) 
12710                 return -1;
12711         if ( sb->num_paths <= 0 ) 
12712                 return -1;
12713
12714 /*
12715         
12716         path_index = -1;
12717         for ( i = 0; i < sb->num_paths; i++ ) {
12718                 if ( !(sb->depart_flags & (1<<i)) ) {
12719                         sb->depart_flags |= (1<<i);
12720                         path_index = sb->paths[i];
12721                         aip->submode_parm0 = i;                 // use mode-specific parameter to record ship bay path index
12722                         break;
12723                 }
12724         }
12725 */
12726         
12727         // take the closest path we can find
12728         int ship_bay_path;
12729         ship_bay_path = ai_find_closest_depart_path(aip, pm);
12730
12731         if (ship_bay_path == -1) {
12732                 return -1;
12733         }
12734
12735         path_index = sb->paths[ship_bay_path];
12736         aip->submode_parm0 = ship_bay_path;
12737         sb->depart_flags |= (1<<ship_bay_path);
12738
12739         if ( path_index == -1 ) {
12740                 return -1;
12741         }
12742
12743         SDL_assert(pm->n_paths > path_index);
12744         ai_find_path(pl_objp, objnum, path_index, 0);
12745
12746         // Set this flag, so we don't bother recreating the path... we won't need to update the path
12747         // that has just been created.
12748         aip->ai_flags &= ~AIF_USE_STATIC_PATH;
12749
12750         aip->goal_objnum = objnum;
12751         aip->goal_signature = Objects[objnum].signature;
12752         aip->mode = AIM_BAY_DEPART;
12753
12754         Ships[pl_objp->instance].flags |= SF_DEPART_DOCKBAY;
12755         return 0;
12756 }
12757
12758 // handler for AIM_BAY_DEPART
12759 void ai_bay_depart()
12760 {
12761         ai_info *aip;
12762
12763         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12764
12765         // if no path to follow, leave this mode
12766         if ( aip->path_start < 0 ) {
12767                 aip->mode = AIM_NONE;
12768                 return;
12769         }
12770
12771         // check if parent ship still exists, if not abort depart 
12772         if ( aip->goal_signature != Objects[aip->goal_objnum].signature ) {
12773                 aip->mode = AIM_NONE;
12774                 return;
12775         }
12776
12777         // follow the path to the final point
12778         ai_path();
12779
12780         // if the final point is reached, let default AI take over
12781         if ( aip->path_cur >= (aip->path_start+aip->path_length) ) {
12782                 polymodel       *pm;
12783                 ship_bay                *sb;
12784
12785                 pm = model_get( Ships[Objects[aip->goal_objnum].instance].modelnum );
12786                 sb = pm->ship_bay;
12787                 if ( sb != NULL ) {
12788                         sb->depart_flags &= ~(1<<aip->submode_parm0);
12789                 }
12790
12791                 // make ship disappear
12792                 Pl_objp->flags |= OF_SHOULD_BE_DEAD;
12793                 ship_departed( Pl_objp->instance );
12794
12795                 // clean up path stuff
12796                 aip->path_start = -1;
12797                 aip->path_cur = -1;
12798                 aip->path_length = 0;
12799                 aip->mode = AIM_NONE;
12800         }
12801 }
12802
12803 // Handler for AIM_SENTRYGUN.  This AI mode is for sentry guns only (ie floating turrets).
12804 void ai_sentrygun()
12805 {
12806         // Nothing to do here.  Turret firing is handled via process_subobjects().
12807         // If you want the sentry guns to do anything beyond firing their turrets at enemies, add it here!
12808 }
12809
12810 //      --------------------------------------------------------------------------
12811 //      Execute behavior given by aip->mode.
12812 void ai_execute_behavior(ai_info *aip)
12813 {
12814         switch (aip->mode) {
12815         case AIM_CHASE:
12816                 if (En_objp) {
12817                         ai_chase();
12818                 } else if (aip->submode == SM_EVADE_WEAPON) {
12819                         evade_weapon();
12820                         // maybe reset submode
12821                         if (aip->danger_weapon_objnum == -1) {
12822                                 aip->submode = SM_ATTACK;
12823                                 aip->submode_start_time = Missiontime;
12824                                 aip->last_attack_time = Missiontime;
12825                         }
12826                 } else {
12827                         //      Don't circle if this is the instructor.
12828                         ship    *shipp = &Ships[aip->shipnum];
12829                         ship_info       *sip = &Ship_info[shipp->ship_info_index];
12830
12831                         if (SDL_strncasecmp(shipp->ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME))) {
12832                                 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
12833                                         aip->mode = AIM_NONE;
12834                                 } else {
12835                                         ai_chase_circle(Pl_objp);
12836                                 }
12837                         }
12838                 }
12839                 break;
12840         case AIM_EVADE:
12841                 if (En_objp) {
12842                         ai_evade();
12843                 } else {
12844                         vector  tvec;
12845                         vm_vec_scale_add(&tvec, &Pl_objp->pos, &Pl_objp->orient.v.rvec, 100.0f);
12846                         turn_towards_point(Pl_objp, &tvec, NULL, 0.0f);
12847                         accelerate_ship(aip, 0.5f);
12848                 }
12849                 break;
12850         case AIM_STILL:
12851                 ai_still();
12852                 break;
12853         case AIM_STAY_NEAR:
12854                 ai_stay_near();
12855                 break;
12856         case AIM_GUARD:
12857                 ai_guard();
12858                 break;
12859         case AIM_WAYPOINTS:
12860                 ai_waypoints();
12861                 break;
12862         case AIM_DOCK:
12863                 ai_dock();
12864                 break;
12865         case AIM_NONE:
12866                 // ai_formation();
12867                 break;
12868         case AIM_BIGSHIP:
12869                 ai_big_ship(Pl_objp);
12870                 break;
12871         case AIM_PATH: {
12872                 int path_num;
12873                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], 0);
12874                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
12875                 ai_path();
12876                 break;
12877         }
12878         case AIM_SAFETY:
12879                 ai_safety();
12880                 break;
12881         case AIM_EVADE_WEAPON:
12882                 evade_weapon();
12883                 break;
12884         case AIM_STRAFE:
12885                 if (En_objp) {
12886                         SDL_assert(En_objp->type == OBJ_SHIP);
12887                         ai_big_strafe();        // strafe a big ship
12888                 } else {
12889                         aip->mode = AIM_NONE;
12890                 }
12891                 break;
12892         case AIM_BAY_EMERGE:
12893                 ai_bay_emerge();
12894                 break;
12895         case AIM_BAY_DEPART:
12896                 ai_bay_depart();
12897                 break;
12898         case AIM_SENTRYGUN:
12899                 ai_sentrygun();
12900                 break;
12901         case AIM_WARP_OUT:
12902                 break;          //      Note, handled directly from ai_frame().
12903         default:
12904                 Int3();         //      This should never happen -- MK, 5/12/97 
12905                 break;
12906         }
12907
12908         if ( !(ship_get_SIF(aip->shipnum) & SIF_NOT_FLYABLE) ) {
12909                 maybe_evade_dumbfire_weapon(aip);
12910         }
12911 }
12912
12913 //      Auxiliary function for maybe_request_support.
12914 //      Return 1 if subsystem "type" is worthy of repair, else return 0.
12915 //      Since subsystems cannot be repaired if they are at 0 strength, don't return 1 if subsystem is dead.
12916 int mrs_subsystem(ship *shipp, int type)
12917 {
12918         float   t;
12919
12920         t = ship_get_subsystem_strength(shipp, type);
12921
12922         if (t > 0.0f) {
12923                 return (int) ((1.0f - t) * 3);
12924         } else {
12925                 return 3;
12926         }
12927 }
12928
12929 //      Return number of ships on *objp's team that are currently rearming.
12930 int num_allies_rearming(object *objp)
12931 {
12932         ship_obj        *so;
12933         int             team;
12934         int             count = 0;
12935
12936         team = Ships[objp->instance].team;
12937
12938         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
12939                 object  *A;
12940                 
12941                 SDL_assert (so->objnum != -1);
12942                 A = &Objects[so->objnum];
12943
12944                 if (Ships[A->instance].team == team) {
12945                         if (Ai_info[Ships[A->instance].ai_index].ai_flags & (AIF_REPAIRING | AIF_AWAITING_REPAIR)) {
12946                                 count++;
12947                         }
12948                 }
12949         }
12950
12951         return count;
12952 }
12953
12954
12955 //      Maybe ship *objp should request support (rearm/repair).
12956 //      If it does, return TRUE, else return FALSE.
12957 int maybe_request_support(object *objp)
12958 {
12959         ship_info       *sip;
12960         ship                    *shipp;
12961         ai_info         *aip;
12962         int                     desire;
12963
12964         SDL_assert(objp->type == OBJ_SHIP);
12965         shipp = &Ships[objp->instance];
12966         aip = &Ai_info[shipp->ai_index];
12967         sip = &Ship_info[shipp->ship_info_index];
12968
12969         if (!timestamp_elapsed(aip->next_rearm_request_timestamp))
12970                 return 0;
12971
12972         //      Only fighters and bombers request support.
12973         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER)))
12974                 return 0;
12975
12976         //      A ship that is currently awaiting does not need support!
12977         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
12978                 return 0;
12979
12980         if (!is_support_allowed(objp))
12981                 return 0;
12982
12983         //if (shipp->team != TEAM_FRIENDLY)
12984         //      return 0;
12985
12986         //      Compute a desire value.
12987         //      Desire of 0 means no reason to request support.
12988         //      1 is slight, 2 more, etc.  Maximum is around 20.  Anything larger than 3 is pretty strong.
12989         desire = 0;
12990
12991         //      Set desire based on hull strength.
12992         //      No: We no longer repair hull, so this would cause repeated repair requests.
12993         //desire += 6 - (int) ((objp->hull_strength/sip->initial_hull_strength) * 6.0f);
12994
12995         //      Set desire based on key subsystems.
12996         desire += 2*mrs_subsystem(shipp, SUBSYSTEM_ENGINE);     //      Note, disabled engine forces repair request, regardless of nearby enemies.
12997         desire += mrs_subsystem(shipp, SUBSYSTEM_COMMUNICATION);
12998         desire += mrs_subsystem(shipp, SUBSYSTEM_WEAPONS);
12999         desire += mrs_subsystem(shipp, SUBSYSTEM_SENSORS);
13000
13001         //      Set desire based on percentage of secondary weapons.
13002         ship_weapon *swp = &shipp->weapons;
13003
13004         for ( int i = 0; i < swp->num_secondary_banks; i++ ) {
13005                 if (swp->secondary_bank_start_ammo[i] > 0) {
13006 //                      float r = (float) swp->secondary_bank_ammo[i]*Weapon_info[swp->secondary_bank_weapons[i]].cargo_size/swp->secondary_bank_capacity[i];
13007                         float r = (float) swp->secondary_bank_ammo[i]/swp->secondary_bank_start_ammo[i];
13008                         desire += (int) ((1.0f - r) * 3.0f);
13009                 }
13010         }
13011
13012         //      If no reason to repair, don't bother to see if it's safe to repair.
13013         if (desire == 0){
13014                 return 0;
13015         }
13016
13017         //      Compute danger threshold.
13018         //      Balance this with desire and maybe request support.
13019         if (ai_good_time_to_rearm( objp )) {
13020                 ai_issue_rearm_request(objp);
13021                 return 1;
13022         } else if (num_allies_rearming(objp) < 2) {
13023                 if (desire >= 8) {      //      guarantees disabled will cause repair request
13024                         ai_issue_rearm_request(objp);
13025                 } else if (desire >= 3) {               //      >= 3 means having a single subsystem fully blown will cause repair.
13026                         int     count;
13027                         int objnum = find_nearby_hostile(OBJ_INDEX(objp), get_enemy_team_mask(OBJ_INDEX(objp)), 2000.0f, &count);
13028
13029                         if ((objnum == -1) || (count < 2) || (vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos) > 3000.0f*count/desire)) {
13030                                 ai_issue_rearm_request(objp);
13031                                 return 1;
13032                         } else {
13033                                 //nprintf(("AI", "Would like to rearm, but enemy only %7.3f units away.\n", vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos)));
13034                         }
13035                 }
13036         }
13037
13038         return 0;
13039
13040 }
13041
13042 void ai_set_mode_warp_out(object *objp, ai_info *aip)
13043 {
13044         ai_abort_rearm_request(objp);
13045         if (aip->mode != AIM_WARP_OUT) {
13046                 aip->mode = AIM_WARP_OUT;
13047                 aip->submode = AIS_WARP_1;
13048         }
13049 }
13050
13051 //      Maybe warp ship out.
13052 //      Shivan and HoL fighter/bomber warp out if their weapons subsystems have been destroyed.
13053 void ai_maybe_warp_out(object *objp)
13054 {
13055         ship    *shipp;
13056
13057         // don't do anything if in a training mission.
13058         if ( The_mission.game_type & MISSION_TYPE_TRAINING )
13059                 return;
13060
13061         SDL_assert(objp->type == OBJ_SHIP);
13062
13063         shipp = &Ships[objp->instance];
13064         ai_info *aip = &Ai_info[shipp->ai_index];
13065
13066         if (aip->mode == AIM_WARP_OUT)
13067                 return;
13068
13069         //      If a support ship with no goals and low hull, warp out.  Be sure that there are no pending goals
13070         // in the support ships ai_goal array.  Just process this ships goals.
13071         ship_info       *sip = &Ship_info[shipp->ship_info_index];
13072         if (sip->flags & SIF_SUPPORT) {
13073                 if ( timestamp_elapsed(aip->warp_out_timestamp) ) {
13074                         ai_process_mission_orders( OBJ_INDEX(objp), aip );
13075                         if ( (aip->dock_objnum == -1) && (objp->hull_strength/sip->initial_hull_strength < 0.25f) ) {
13076                                 ai_set_mode_warp_out(objp, aip);
13077                         }
13078                 }
13079         }
13080
13081         //      Friendly don't warp out, they'll eventually request support.
13082         if (shipp->team == TEAM_FRIENDLY)
13083                 return;
13084
13085         if (!(shipp->flags & SF_DEPARTING)) {
13086                 ship_info       *sip;
13087
13088                 sip = &Ship_info[shipp->ship_info_index];
13089                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
13090                         if (aip->warp_out_timestamp == 0) {
13091                                 //if (ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS) == 0.0f) {
13092                                 //      aip->warp_out_timestamp = timestamp(((myrand() % 10) + 10) * 1000);
13093                                 //}
13094                         } else if (timestamp_elapsed(aip->warp_out_timestamp)) {
13095                                 ai_set_mode_warp_out(objp, aip);
13096                         }
13097                 }
13098         }
13099 }
13100
13101 //      Warp this ship out.
13102 void ai_warp_out(object *objp)
13103 {
13104         // if dying, don't warp out.
13105         if (Ships[objp->instance].flags & SF_DYING) {
13106                 return;
13107         }
13108
13109         ai_info *aip;
13110
13111         aip = &Ai_info[Ships[objp->instance].ai_index];
13112
13113         switch (aip->submode) {
13114         case AIS_WARP_1:
13115                 aip->force_warp_time = timestamp(10*1000);      //      Try to avoid a collision for up to ten seconds.
13116                 aip->submode = AIS_WARP_2;
13117                 break;
13118         case AIS_WARP_2:                        //      Make sure won't collide with any object.
13119                 if (timestamp_elapsed(aip->force_warp_time) || !collide_predict_large_ship(objp, objp->radius*2.0f + 100.0f)) {
13120                         aip->submode = AIS_WARP_3;
13121
13122                         // maybe recalculate collision pairs.
13123                         if (ship_get_warp_speed(objp) > ship_get_max_speed(&Ships[objp->instance])) {
13124                                 // recalculate collision pairs
13125                                 OBJ_RECALC_PAIRS(objp); 
13126                         }
13127
13128                         aip->force_warp_time = timestamp(4*1000);               //      Try to attain target speed for up to 4 seconds.
13129                 } else {
13130                         vector  goal_point;
13131                         vm_vec_scale_add(&goal_point, &objp->pos, &objp->orient.v.uvec, 100.0f);
13132                         turn_towards_point(objp, &goal_point, NULL, 0.0f);
13133                         accelerate_ship(aip, 0.0f);
13134                 }
13135                 break;
13136         case AIS_WARP_3:
13137                 //      Rampup desired_vel in here from current to desired velocity and set PF_USE_VEL. (not sure this is the right flag)
13138                 //      desired velocity is computed in shipfx_calculate_warp_time().  See shipfx#572 for sample code.
13139                 float   speed, goal_speed;
13140                 float shipfx_calculate_warp_speed(object*);
13141                 goal_speed = shipfx_calculate_warp_speed(objp);
13142
13143                 // HUGE ships go immediately to AIS_WARP_4
13144                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_HUGE_SHIP) {
13145                         aip->submode = AIS_WARP_4;
13146                         break;
13147                 }
13148                 //compute_warpout_stuff(objp, &goal_speed, &warp_time, &warp_pos);
13149                 //goal_speed = 80.0f;
13150                 //set_accel_for_target_speed(objp, 40.0f);
13151                 // 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
13152                 speed = goal_speed * flFrametime + objp->phys_info.speed * (1.0f - flFrametime);
13153                 vm_vec_copy_scale(&objp->phys_info.vel, &objp->orient.v.fvec, speed);
13154                 objp->phys_info.desired_vel = objp->phys_info.vel;
13155                 // nprintf(("AI", "Frame %i, speed = %7.3f, goal = %7.3f\n", Framecount, vm_vec_mag_quick(&objp->phys_info.vel), goal_speed));
13156                 if (timestamp_elapsed(aip->force_warp_time) || (fl_abs(objp->phys_info.speed - goal_speed) < 2.0f))
13157                         aip->submode = AIS_WARP_4;
13158                 break;
13159         case AIS_WARP_4: {
13160                 shipfx_warpout_start(objp);
13161                 aip->submode = AIS_WARP_5;
13162                 break;
13163         }
13164         case AIS_WARP_5:
13165                 break;
13166         default:
13167                 Int3();         //      Illegal submode for warping out.
13168         }
13169 }
13170
13171 //      Return object index of weapon that could produce a shockwave that should be known about to *objp.
13172 //      Return nearest one.
13173 int ai_find_shockwave_weapon(object *objp, ai_info *aip)
13174 {
13175         missile_obj     *mo;
13176         float   nearest_dist = 999999.9f;
13177         int     nearest_index = -1;
13178
13179         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
13180                 object          *A;
13181                 weapon          *wp;
13182                 weapon_info     *wip;
13183         
13184                 SDL_assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
13185                 A = &Objects[mo->objnum];
13186
13187                 SDL_assert(A->type == OBJ_WEAPON);
13188                 SDL_assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
13189                 wp = &Weapons[A->instance];
13190                 wip = &Weapon_info[wp->weapon_info_index];
13191                 SDL_assert( wip->subtype == WP_MISSILE );
13192
13193                 if (wip->shockwave_speed > 0.0f) {
13194                         float   dist;
13195
13196                         dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13197                         if (dist < nearest_dist) {
13198                                 nearest_dist = dist;
13199                                 nearest_index = mo->objnum;
13200                         }
13201                 }
13202         }
13203
13204         return nearest_index;
13205
13206 }
13207
13208 #define EVADE_SHOCKWAVE_DAMAGE_THRESHOLD                100.0f
13209
13210 //      Tell all ships to avoid a big ship that is blowing up.
13211 //      Only avoid if shockwave is fairly large.
13212 //      OK to tell everyone to avoid.  If they're too far away, that gets cleaned up in the frame interval.
13213 void ai_announce_ship_dying(object *dying_objp)
13214 {
13215         float damage = ship_get_exp_damage(dying_objp);
13216         if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {
13217                 ship_obj        *so;
13218
13219                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13220                         if (Ship_info[Ships[Objects[so->objnum].instance].ship_info_index].flags & (SIF_SMALL_SHIP | SIF_FREIGHTER)) {
13221                                 ai_info *aip;
13222
13223                                 aip = &Ai_info[Ships[Objects[so->objnum].instance].ai_index];
13224
13225                                 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
13226                                         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_SHIP;
13227                                 }
13228                         }
13229                 }
13230         }
13231 }
13232
13233
13234 //      Return object index of weapon that could produce a shockwave that should be known about to *objp.
13235 //      Return nearest one.
13236 int ai_find_shockwave_ship(object *objp, ai_info *aip)
13237 {
13238         ship_obj        *so;
13239         float   nearest_dist = 999999.9f;
13240         int     nearest_index = -1;
13241
13242         for ( so = GET_NEXT(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13243                 object          *A;
13244                 ship                    *shipp;
13245         
13246                 SDL_assert(so->objnum >= 0 && so->objnum < MAX_OBJECTS);
13247                 A = &Objects[so->objnum];
13248
13249                 SDL_assert(A->type == OBJ_SHIP);
13250                 SDL_assert((A->instance >= 0) && (A->instance < MAX_SHIPS));
13251                 shipp = &Ships[A->instance];
13252                 //      Only look at objects in the process of dying.
13253                 if (shipp->flags & SF_DYING) {
13254                         float damage = ship_get_exp_damage(objp);
13255
13256                         if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {               //      Only evade quite large blasts
13257                                 float   dist;
13258
13259                                 dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13260                                 if (dist < nearest_dist) {
13261                                         nearest_dist = dist;
13262                                         nearest_index = so->objnum;
13263                                 }
13264                         }
13265                 }
13266         }
13267
13268         return nearest_index;
13269
13270 }
13271
13272 int aas_1(object *objp, ai_info *aip, vector *safe_pos)
13273 {
13274         // MAKE SURE safe_pos DOES NOT TAKE US TOWARD THE A SHIP WE'RE ATTACKING.
13275         if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_WEAPON) {
13276                 //      If we don't currently know of a weapon to avoid, try to find one.
13277                 //      If we can't find one, then clear the bit so we don't keep coming here.
13278                 if (aip->shockwave_object == -1) {
13279                         int shockwave_weapon = ai_find_shockwave_weapon(objp, aip);
13280                         if (shockwave_weapon == -1) {
13281                                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13282                                 return 0;
13283                         } else {
13284                                 aip->shockwave_object = shockwave_weapon;
13285                         }
13286                 }
13287
13288                 //      OK, we have reason to believe we should avoid aip->shockwave_object.
13289                 SDL_assert(aip->shockwave_object > -1);
13290                 object  *weapon_objp = &Objects[aip->shockwave_object];
13291                 if (weapon_objp->type != OBJ_WEAPON) {
13292                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13293                         aip->shockwave_object = -1;
13294                         return 0;
13295                 }
13296
13297                 weapon  *weaponp = &Weapons[weapon_objp->instance];
13298                 weapon_info     *wip = &Weapon_info[weaponp->weapon_info_index];
13299                 object *target_ship_obj = NULL;
13300
13301                 if (wip->shockwave_speed == 0.0f) {
13302                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13303                         aip->shockwave_object = -1;
13304                         return 0;
13305                 }
13306
13307                 float   danger_dist;
13308                 vector  expected_pos;           //      Position at which we expect the weapon to detonate.
13309                 int             pos_set = 0;
13310
13311                 danger_dist = wip->outer_radius;
13312                 //      Set predicted position of detonation.
13313                 //      If an aspect locked missile, assume it will detonate at the homing position.
13314                 //      If not, which is not possible in a default FreeSpace weapon, then predict it will detonate at some
13315                 //      time in the future, this time based on max lifetime and life left.
13316                 if (wip->wi_flags & WIF_HOMING_ASPECT) {
13317                         expected_pos = weaponp->homing_pos;
13318                 //      if (weaponp->homing_object && weaponp->homing_object->type == OBJ_SHIP) {
13319                 //              target_ship_obj = weaponp->homing_object;
13320                 //      }
13321                         pos_set = 1;
13322                         if (IS_VEC_NULL(&weaponp->homing_pos)) {
13323                                 pos_set = 0;
13324                                 if (weaponp->target_num != -1) {
13325                                         if (Objects[weaponp->target_num].type == OBJ_SHIP) {
13326                                                 target_ship_obj = &Objects[weaponp->target_num];
13327                                                 expected_pos = target_ship_obj->pos;
13328                                                 pos_set = 1;
13329                                         }
13330                                 }
13331                         }
13332                 }
13333
13334                 if (!pos_set) {
13335                         float   time_scale;
13336
13337                         if (wip->lifetime - weaponp->lifeleft > 5.0f) {
13338                                 time_scale = 1.0f;
13339                         } else {
13340                                 time_scale = weaponp->lifeleft/2.0f;
13341                         }
13342
13343                         vm_vec_scale_add(&expected_pos, &weapon_objp->pos, &weapon_objp->orient.v.fvec, time_scale);
13344                 }
13345
13346                 //      See if too far away to care about shockwave.
13347                 if (vm_vec_dist_quick(&objp->pos, &expected_pos) > danger_dist*2.0f) {
13348                         //aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13349                         return 0;
13350                 } else {
13351                         // try to find a safe position
13352                         vector vec_from_exp;
13353                         float dir = 1.0f;
13354                         vm_vec_sub(&vec_from_exp, &objp->pos, &expected_pos);
13355                         float dot = vm_vec_dotprod(&vec_from_exp, &weapon_objp->orient.v.fvec);
13356                         if (dot > -30) {
13357                                 // if we're already on the other side of the explosion, don't try to fly behind it
13358                                 dir = -1.0f;
13359                         }
13360
13361                         //      Fly towards a point behind the weapon.
13362                         vm_vec_scale_add(safe_pos, &weapon_objp->pos, &weapon_objp->orient.v.fvec, -50000.0f*dir);
13363
13364                         // verify safe_pos will not make us collide with our target objnum, else try 2 other vecs
13365                         // don't bang your head, else go
13366 //                      int go_safe = FALSE;
13367                         int go_safe = TRUE;
13368 /*                      if (target_ship_obj) {
13369                                 if (pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius)) {
13370                                         // try up to 2 other random directions
13371                                         vector dir_vec, rand_vec;
13372                                         int idx;
13373                                         for (idx=0; idx<2; idx++) {
13374                                                 vm_vec_rand_vec_quick(&rand_vec);
13375                                                 vm_vec_scale_add(&dir_vec, &weapon_objp->orient.v.fvec, &rand_vec, 0.5f);
13376                                                 vm_vec_scale_add(safe_pos, &weapon_objp->pos, &dir_vec, -50000.0f*dir);
13377                                                 if ( !pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius) ) {
13378                                                         go_safe = TRUE;
13379                                                         break;
13380                                                 }
13381                                         }
13382                                 } else { // direct path is safe
13383                                         go_safe = TRUE;
13384                                 }
13385                         } else { // no target_obj_ship
13386                                 go_safe = TRUE;
13387                         } */
13388
13389                         if (go_safe) {
13390                                 return 1;
13391                         } else {
13392                                 // can't figure out a good way to go
13393                                 return 0;
13394                         }
13395                 }
13396         } else if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_SHIP) {
13397                 if (aip->shockwave_object == -1) {
13398                         int shockwave_ship = ai_find_shockwave_ship(objp, aip);
13399                         if (shockwave_ship == -1) {
13400                                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13401                                 return 0;
13402                         } else {
13403                                 aip->shockwave_object = shockwave_ship;
13404                         }
13405                 }
13406
13407                 SDL_assert(aip->shockwave_object > -1);
13408                 object  *ship_objp = &Objects[aip->shockwave_object];
13409                 if (ship_objp == objp) {
13410                         aip->shockwave_object = -1;
13411                         return 0;
13412                 }
13413
13414                 if (ship_objp->type != OBJ_SHIP) {
13415                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13416                         return 0;
13417                 }
13418
13419                 //      Optimize note! Don't really have to normalize.  We only need a point away from the blowing-up ship.
13420                 vector safe_vec;
13421
13422                 vm_vec_normalized_dir(&safe_vec, &objp->pos, &ship_objp->pos);
13423                 vm_vec_scale_add(safe_pos, &ship_objp->pos, &safe_vec, 50000.0f);       //      Fly away from the ship.
13424
13425                 float outer_rad = ship_get_exp_outer_rad(ship_objp);
13426
13427                 if (vm_vec_dist_quick(&objp->pos, &ship_objp->pos) > outer_rad*1.5f) {
13428                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13429                         return 0;
13430                 }
13431
13432                 return 1;
13433
13434         } else {
13435                 Int3(); //      Illegal -- supposedly avoiding a shockwave, but neither ship nor weapon.  What is it!?
13436         }
13437
13438         return 0;
13439 }
13440
13441 /*
13442 int rct_done = 0;
13443
13444 void rand_chance_test()
13445 {
13446         int     i;
13447         float   frametime;
13448
13449         if (rct_done)
13450                 return;
13451
13452         rct_done = 1;
13453
13454         for (frametime=0.02f; frametime<0.25f; frametime *= 1.25f) {
13455                 float   chance;
13456
13457                 nprintf(("AI", "%6.4f: ", frametime));
13458                 for (chance=0.25f; chance<2.5f; chance += 0.25f) {
13459                         int count = 0;
13460
13461                         for (i=0; i<100.0f/frametime; i++) {
13462                                 if (rand_chance(frametime, chance))
13463                                         count++;
13464                         }
13465                         nprintf(("AI", "%3i ", count));
13466                 }
13467                 nprintf(("AI", "\n"));
13468         }
13469 }
13470 */
13471
13472 //      --------------------------------------------------------------------------
13473 //      Make object *objp avoid the nearest dangerous shockwave-producing weapon.
13474 //      If it looks like there is no valid shockwave-producing weapon then clear the AIF_AVOID_SHOCKWAVE_WEAPON bit in ai_flags and return.
13475 //      Return 1 if avoiding a shockwave, else return 0.
13476 int ai_avoid_shockwave(object *objp, ai_info *aip)
13477 {
13478         vector  safe_pos;
13479
13480         //rand_chance_test();
13481         // BIG|HUGE do not respond to shockwaves
13482         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
13483                 // don't come here again
13484                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE;
13485                 return 0;
13486         }
13487
13488         //      Don't all react right away.
13489         if (!(aip->ai_flags & AIF_AVOID_SHOCKWAVE_STARTED))
13490                 if (!rand_chance(flFrametime, (float) aip->ai_class/4.0f + 0.25f))      //      Chance to avoid in 1 second is 0.25 + ai_class/4
13491                         return 0;
13492
13493         if (!aas_1(objp, aip, &safe_pos)) {
13494                 aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13495                 return 0;
13496         }
13497
13498         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13499
13500         //      OK, evade the shockwave!
13501         turn_towards_point(objp, &safe_pos, NULL, 0.0f);
13502         vector  vec_to_safe_pos;
13503         float           dot_to_goal;
13504
13505         vm_vec_normalized_dir(&vec_to_safe_pos, &safe_pos, &objp->pos);
13506
13507         dot_to_goal = vm_vec_dot(&objp->orient.v.fvec, &vec_to_safe_pos);
13508         if (dot_to_goal < -0.5f)
13509                 accelerate_ship(aip, 0.3f);
13510         else {
13511                 accelerate_ship(aip, 1.0f + dot_to_goal);
13512                 if (dot_to_goal > 0.2f) {
13513                         if (!(objp->phys_info.flags & PF_AFTERBURNER_ON )) {
13514                                 afterburners_start(objp);
13515                                 aip->afterburner_stop_time = Missiontime + 2*F1_0;
13516                         }
13517                 }
13518         }
13519
13520         return 1;
13521 }
13522
13523 //      Awaiting repair.  Be useful.
13524 //      Probably fly towards incoming repair ship.
13525 //      Return true if this ship is close to being repaired, else return false.
13526 int ai_await_repair_frame(object *objp, ai_info *aip)
13527 {
13528         if (!(aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)))
13529                 return 0;
13530
13531         if (aip->dock_objnum == -1)
13532                 return 0;
13533
13534         ship    *shipp;
13535         ship_info       *sip;
13536
13537         shipp = &Ships[Objects[aip->dock_objnum].instance];
13538         sip = &Ship_info[shipp->ship_info_index];
13539
13540         aip->ai_flags &= ~AIF_FORMATION_OBJECT; //      Prevents endless rotation.
13541
13542         if (!(sip->flags & SIF_SUPPORT))
13543                 return 0;
13544
13545         vector  goal_point;
13546         object  *repair_objp;
13547
13548         repair_objp = &Objects[aip->dock_objnum];
13549
13550         if (Ships[repair_objp->instance].team == TEAM_TRAITOR) {
13551                 ai_abort_rearm_request(repair_objp);
13552                 return 0;
13553         }
13554
13555         vm_vec_scale_add(&goal_point, &repair_objp->pos, &repair_objp->orient.v.uvec, -50.0f);  //      Fly towards point below repair ship.
13556
13557         vector  vtr;
13558         float dist = vm_vec_normalized_dir(&vtr, &goal_point, &objp->pos);
13559         float dot = vm_vec_dot(&vtr, &objp->orient.v.fvec);
13560
13561         if (dist > 200.0f) {
13562                 //nprintf(("AI", "%s flying towards %s for repair, dist = %7.3f\n", Ships[objp->instance].ship_name, &Ships[repair_objp->instance].ship_name, dist));
13563                 accelerate_ship(aip, (0.9f + dot) * dist/1500.0f);
13564                 turn_towards_point(objp, &goal_point, NULL, 0.0f);
13565         } else {
13566                 accelerate_ship(aip, 0.0f);
13567                 //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));
13568         }
13569
13570         return 1;
13571 }
13572
13573 //      Maybe cause this ship to self-destruct.
13574 //      Currently, any small ship (SIF_SMALL_SHIP) that has been disabled will self-destruct after awhile.
13575 //      Maybe should only do this if they are preventing their wing from re-entering.
13576 void ai_maybe_self_destruct(object *objp, ai_info *aip)
13577 {
13578         //      Friendly ships can be repaired, so no self-destruct.
13579         //      In multiplayer, just don't self-destruct.  I figured there would be a problem. -- MK, 3/19/98.
13580         if ((Ships[objp->instance].team == TEAM_FRIENDLY) || (Game_mode & GM_MULTIPLAYER))
13581                 return;
13582
13583         //      Small ships in a wing blow themselves up after awhile if engine or weapons system has been destroyed.
13584         //      Reason: Don't want them to prevent a re-emergence of the wing.
13585         //      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
13586         //      mission would be broken.
13587         if ((Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP) && (Ships[objp->instance].wingnum != -1)) {
13588                 if ((ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) ||
13589                         (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_WEAPONS) <= 0.0f)) {
13590                         if (aip->self_destruct_timestamp < 0)
13591                                 aip->self_destruct_timestamp = timestamp(90 * 1000);    //      seconds until self-destruct
13592                 } else {
13593                         aip->self_destruct_timestamp = -1;
13594                 }
13595
13596                 if (aip->self_destruct_timestamp < 0) {
13597                         return;
13598                 }
13599
13600                 if (timestamp_elapsed(aip->self_destruct_timestamp)) {
13601                         ship_apply_local_damage( objp, objp, &objp->pos, objp->hull_strength*flFrametime + 1.0f, MISS_SHIELDS);
13602                 }
13603         }
13604 }
13605
13606 // Determine if pl_objp needs a new target, called from ai_frame()
13607 int ai_need_new_target(object *pl_objp, int target_objnum)
13608 {
13609         object *objp;
13610
13611         if ( target_objnum < 0 ) {
13612                 return 1;
13613         }
13614
13615         objp = &Objects[target_objnum];
13616
13617         if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID) && (objp->type != OBJ_WEAPON) ) {
13618                 return 1;
13619         }
13620
13621         if ( objp->type == OBJ_SHIP ) {
13622                 if ( Ships[objp->instance].flags & SF_DYING ) {
13623                         return 1;
13624                 } else if (Ships[objp->instance].team == Ships[pl_objp->instance].team)
13625                         return 1;
13626         }
13627
13628         return 0;
13629 }
13630
13631 //      If *objp is recovering from a collision with a big ship, handle it.
13632 //      Return true if recovering.
13633 int maybe_big_ship_collide_recover_frame(object *objp, ai_info *aip)
13634 {
13635         float   dot, dist;
13636         vector  v2g;
13637         
13638         if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1) {
13639                 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);
13640                 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_1, &objp->pos);
13641                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
13642                 accelerate_ship(aip, dot);
13643
13644                 //      If close to desired point, or 15+ seconds since entered this mode, continue to next mode.
13645                 if ((timestamp_until(aip->big_recover_timestamp) < -15*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13646                         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_1;
13647                         aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13648                 }
13649
13650                 return 1;
13651
13652         } else if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_2) {
13653                 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);
13654                 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_2, &objp->pos);
13655                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
13656                 accelerate_ship(aip, dot);
13657
13658                 //      If close to desired point, or 30+ seconds since started avoiding collision, done avoiding.
13659                 if ((timestamp_until(aip->big_recover_timestamp) < -30*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13660                         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13661                         aip->ai_flags &= ~AIF_TARGET_COLLISION;
13662                 }
13663
13664                 return 1;
13665         }
13666
13667         if (aip->ai_flags & AIF_TARGET_COLLISION) {
13668                 aip->ai_flags &= ~AIF_TARGET_COLLISION;
13669         }
13670         return 0;
13671 }
13672
13673 void validate_mode_submode(ai_info *aip)
13674 {
13675         switch (aip->mode) {
13676         case AIM_CHASE:
13677                 // check valid submode
13678                 switch (aip->submode) {
13679                 case SM_CONTINUOUS_TURN:
13680                 case SM_ATTACK:
13681                 case SM_EVADE_SQUIGGLE:
13682                 case SM_EVADE_BRAKE:    
13683                 case SM_EVADE:          
13684                 case SM_SUPER_ATTACK:
13685                 case SM_AVOID:  
13686                 case SM_GET_BEHIND:
13687                 case SM_GET_AWAY:               
13688                 case SM_EVADE_WEAPON:
13689                 case SM_FLY_AWAY:       
13690                 case SM_ATTACK_FOREVER:
13691                         break;
13692                 default:
13693                         Int3();
13694                 }
13695                 break;
13696
13697         case AIM_STRAFE:
13698                 // check valid submode
13699                 switch(aip->submode) {
13700                 case AIS_STRAFE_ATTACK:
13701                 case AIS_STRAFE_AVOID:
13702                 case AIS_STRAFE_RETREAT1:
13703                 case AIS_STRAFE_RETREAT2:
13704                 case AIS_STRAFE_POSITION:
13705                         break;
13706                 default:
13707                         Int3();
13708                 }
13709                 break;
13710         }
13711 }
13712
13713 //      --------------------------------------------------------------------------
13714 // Process AI object "objnum".
13715 void ai_frame(int objnum)
13716 {
13717         ship            *shipp = &Ships[Objects[objnum].instance];
13718         ai_info *aip = &Ai_info[shipp->ai_index];
13719         int             target_objnum;
13720
13721 //      validate_mode_submode(aip);
13722
13723         SDL_assert((aip->mode != AIM_WAYPOINTS) || (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC));
13724
13725         // Set globals defining the current object and its enemy object.
13726         Pl_objp = &Objects[objnum];
13727
13728         if (aip->mode == AIM_WARP_OUT) {
13729                 ai_warp_out(Pl_objp);
13730                 return;
13731         }
13732
13733 /*      //      HACK! TEST! REMOVE ME!
13734         if (Ship_info[shipp->ship_info_index].flags & SIF_BIG_SHIP)
13735                 if (shipp->team == Player_ship->team)
13736                         aip->mode = AIM_CHASE;
13737 */
13738
13739 //      if (!SDL_strncasecmp(Ships[Pl_objp->instance].ship_name, "cancer", 6))
13740 //              nprintf(("AI", "Ship %s: mode = %s, submode = %i\n", Ships[Pl_objp->instance].ship_name, Mode_text[aip->mode], aip->submode));
13741
13742         ai_maybe_self_destruct(Pl_objp, aip);
13743
13744 //      if ( timestamp_elapsed(aip->goal_check_time) ) {
13745                 ai_process_mission_orders( objnum, aip );
13746 //              aip->goal_check_time = timestamp_rand(1000,2000);
13747 //      }
13748
13749         //      Avoid a shockwave, if necessary.  If a shockwave and rearming, stop rearming.
13750         if (aip->ai_flags & AIF_AVOID_SHOCKWAVE) {
13751                 if (ai_avoid_shockwave(Pl_objp, aip)) {
13752                         aip->ai_flags &= ~(AIF_BIG_SHIP_COLLIDE_RECOVER_1 | AIF_BIG_SHIP_COLLIDE_RECOVER_2);
13753                         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
13754                                 ai_abort_rearm_request(Pl_objp);
13755                         return;
13756                 }
13757         } else {
13758                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_STARTED;
13759         }
13760
13761         // moved call to ai_do_repair frame here from below because of the subsequent if statment returning
13762         // if the ship is getting repaired
13763         //      If waiting to be repaired, just stop and sit.
13764         ai_do_repair_frame(Pl_objp, aip, flFrametime);
13765         if ((aip->ai_flags & AIF_AWAITING_REPAIR) || (aip->ai_flags & AIF_BEING_REPAIRED)) {
13766                 if (ai_await_repair_frame(Pl_objp, aip))
13767                         return;
13768         }
13769
13770         if (aip->mode == AIM_PLAY_DEAD)
13771                 return;
13772
13773         //      If recovering from a collision with a big ship, don't continue.
13774         if (maybe_big_ship_collide_recover_frame(Pl_objp, aip))
13775                 return;
13776
13777         ai_preprocess_ignore_objnum(Pl_objp, aip);
13778         target_objnum = set_target_objnum(aip, aip->target_objnum);
13779
13780         // 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));
13781
13782         SDL_assert(objnum != target_objnum);
13783
13784         ai_manage_shield(Pl_objp, aip);
13785         
13786         if ( maybe_request_support(Pl_objp) ) {
13787                 if ( Ships[Pl_objp->instance].flags & SF_FROM_PLAYER_WING ) {
13788                         ship_maybe_tell_about_rearm(shipp);
13789                 }
13790         }
13791
13792         ai_maybe_warp_out(Pl_objp);
13793
13794 /*
13795         //      If this ship is attacking an object's subsystems and someone else destroyed
13796         //      the subsystem, it could continue attacking the ship.  Need to invalidate the objnum.
13797         if (target_objnum >= 0)
13798                 if (Objects[target_objnum].flags & OF_PROTECTED) {
13799                         // if (aip->targeted_subsys != NULL)
13800                         //      ; //nprintf(("AI", "subsys hits = %7.3f\n", aip->targeted_subsys->current_hits));
13801
13802                         if ((aip->targeted_subsys == NULL) || (aip->targeted_subsys->current_hits <= 0.0f)) {
13803                                 target_objnum = -1;
13804                                 aip->target_objnum = -1;
13805                         }
13806                 }
13807 */
13808
13809
13810         //      Find an enemy if don't already have one.
13811         En_objp = NULL;
13812         if ( ai_need_new_target(Pl_objp, target_objnum) ) {
13813                 if ((aip->mode != AIM_EVADE_WEAPON) && (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
13814                         aip->resume_goal_time = -1;
13815                         aip->active_goal = AI_GOAL_NONE;
13816                 } else if (aip->resume_goal_time == -1) {
13817                         // AL 12-9-97: Don't allow cargo and navbuoys to set their aip->target_objnum
13818                         if ( !(Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS) ) {
13819                                 target_objnum = find_enemy(objnum, MAX_ENEMY_DISTANCE, Skill_level_max_attackers[Game_skill_level]);            //      Attack up to 25K units away.
13820                                 if (target_objnum != -1) {
13821                                         if (aip->target_objnum != target_objnum)
13822                                                 aip->aspect_locked_time = 0.0f;
13823                                         set_target_objnum(aip, target_objnum);
13824                                         En_objp = &Objects[target_objnum];
13825                                 }
13826                         }
13827                 }
13828         } else if (target_objnum >= 0) {
13829                 En_objp = &Objects[target_objnum];
13830         }
13831
13832         // set base stealth info each frame
13833         aip->ai_flags &= ~AIF_STEALTH_PURSIUT;
13834         if (En_objp && En_objp->type == OBJ_SHIP) {
13835                 if (Ship_info[Ships[En_objp->instance].ship_info_index].flags & SIF_STEALTH) {
13836                         int stealth_state = ai_is_stealth_visible(Pl_objp, En_objp);
13837                         float dist = vm_vec_dist_quick(&En_objp->pos, &Pl_objp->pos);
13838
13839                         if (stealth_state != STEALTH_FULLY_TARGETABLE) {
13840                                 aip->ai_flags |= AIF_STEALTH_PURSIUT;
13841                         }
13842
13843                         if ( (stealth_state == STEALTH_FULLY_TARGETABLE) || (stealth_state == STEALTH_VISIBLE) ) {
13844                                 aip->stealth_last_visible_stamp = timestamp();
13845                                 aip->stealth_last_cheat_visible_stamp = aip->stealth_last_visible_stamp;
13846                                 aip->stealth_last_pos = En_objp->pos;
13847                                 aip->stealth_velocity = En_objp->phys_info.vel;
13848                         } else if (dist < 100) {
13849                                 // get cheat timestamp
13850                                 aip->stealth_last_cheat_visible_stamp = timestamp();
13851
13852                                 // set approximate pos and vel, with increasing error as time from last_visible_stamp increases
13853                                 update_ai_stealth_info_with_error(aip/*, 0*/);
13854                         }
13855                 }
13856         }
13857
13858         /*      if ((Pl_objp != NULL) && (En_objp != NULL)) {
13859                 slide_face_ship();
13860                 return;
13861         }
13862 */
13863         // AL 12-10-97: ensure that cargo and navbuoys aip->target_objnum is always -1.
13864         if ( Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS ) {
13865                 aip->target_objnum = -1;
13866         }
13867
13868         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)) {
13869                 mprintf(("Warning: Object and its enemy have same position.  Object #%i\n", Pl_objp-Objects));
13870                 En_objp = NULL;
13871         }
13872
13873         if (aip->mode == AIM_CHASE) {
13874                 if (En_objp == NULL) {
13875                         aip->active_goal = -1;
13876                 }
13877         }
13878
13879         //      If there is a goal to resume and enough time has elapsed, resume the goal.
13880         if ((aip->resume_goal_time > 0) && (aip->resume_goal_time < Missiontime)) {
13881                 aip->active_goal = AI_GOAL_NONE;
13882                 aip->resume_goal_time = -1;
13883                 target_objnum = find_enemy(objnum, 2000.0f, Skill_level_max_attackers[Game_skill_level]);
13884                 if (target_objnum != -1) {
13885                         if (aip->target_objnum != target_objnum) {
13886                                 aip->aspect_locked_time = 0.0f;
13887                         }
13888                         set_target_objnum(aip, target_objnum);
13889                 }
13890         }
13891
13892         // check if targeted subsystem has been destroyed, if so, move onto another subsystem
13893         // if trying to disable or disarm the target
13894         if ((En_objp != NULL) && ( aip->targeted_subsys != NULL )) {
13895                 SDL_assert(En_objp->type == OBJ_SHIP);
13896                 if ( aip->targeted_subsys->current_hits <= 0.0f ) {
13897                         int subsys_type;
13898
13899                         if ( aip->goals[0].ai_mode == AI_GOAL_DISABLE_SHIP ) {
13900                                 subsys_type = SUBSYSTEM_ENGINE;
13901                         } else if ( aip->goals[0].ai_mode == AI_GOAL_DISARM_SHIP ) {
13902                                 subsys_type = SUBSYSTEM_TURRET;
13903                         } else {
13904                                 subsys_type = -1;
13905                         }
13906
13907                         if ( subsys_type != -1 ) {
13908                                 ship_subsys *new_subsys;
13909                                 new_subsys = ship_return_next_subsys(&Ships[En_objp->instance], subsys_type, &Pl_objp->pos);
13910                                 if ( new_subsys != NULL ) {
13911                                         set_targeted_subsys(aip, new_subsys, aip->target_objnum);
13912                                 } else {
13913                                         // AL 12-16-97: no more subsystems to attack... reset targeting info
13914                                         aip->target_objnum = -1;
13915                                         set_targeted_subsys(aip, NULL, -1);
13916                                 }
13917                         } else {
13918                                 // targeted subsys is destroyed, so stop attacking it
13919                                 set_targeted_subsys(aip, NULL, -1);
13920                         }
13921                 }
13922         }
13923
13924         ai_maybe_launch_cmeasure(Pl_objp, aip);
13925         ai_maybe_evade_locked_missile(Pl_objp, aip);
13926
13927         aip->target_time += flFrametime;
13928
13929         int in_formation = 0;
13930         if (aip->ai_flags & AIF_FORMATION) {
13931                 in_formation = !ai_formation();
13932         }
13933
13934         if ( !in_formation ) {
13935                 ai_execute_behavior(aip);
13936         }
13937
13938         process_subobjects(objnum);
13939         maybe_resume_previous_mode(Pl_objp, aip);
13940         
13941         if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON ) {
13942                 if (Missiontime > aip->afterburner_stop_time) {
13943                         //nprintf(("AI", "Frame %i, turning off afterburner.\n", AI_FrameCount));
13944                         afterburners_stop(Pl_objp);
13945                 }
13946         }
13947 //      validate_mode_submode(aip);
13948 }
13949
13950 int Waypoints_created = 0;
13951
13952 //      Find the ship with the name *name in the Ship_info array.
13953 int find_ship_name(char *name)
13954 {
13955         int     i;
13956
13957         for (i=0; i<Num_ship_types; i++)
13958                 if (!strcmp(Ship_info[i].name, name))
13959                         return i;
13960
13961         return -1;
13962 }
13963
13964 void create_waypoints()
13965 {
13966         int     i, j;
13967
13968         // Waypoints_created = 1;
13969
13970         if (Waypoints_created)
13971                 return;
13972
13973         for (j=0; j<Num_waypoint_lists; j++)
13974                 for (i=0; i<Waypoint_lists[j].count; i++) {
13975                         obj_create(OBJ_WAYPOINT, 0, j * 65536 + i, NULL,
13976                                 &Waypoint_lists[j].waypoints[i], 0.0f, OF_RENDERS);
13977                 }
13978
13979         Waypoints_created = 1;
13980 }
13981
13982 int Last_ai_obj = -1;
13983
13984 void ai_process( object * obj, int ai_index, float frametime )
13985 {
13986 //      if (Ships[obj->instance].flags & SF_DYING)
13987 //              nprintf(("AI", "Frame: %i Ship %s is dying!\n", Framecount, Ships[obj->instance].ship_name));
13988
13989         if (obj->flags & OF_SHOULD_BE_DEAD)
13990                 return;
13991
13992         // 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.
13993         if ((Ships[obj->instance].flags & SF_DYING ) && !(Ship_info[Ships[obj->instance].ship_info_index].flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP))){
13994                 return;
13995         }
13996
13997         int rfc = 1;            //      Assume will be Reading Flying Controls.
13998
13999         SDL_assert( obj->type == OBJ_SHIP );
14000         SDL_assert( ai_index >= 0 );
14001
14002         init_ship_info();
14003
14004         create_waypoints();
14005
14006         AI_frametime = frametime;
14007         if (obj-Objects <= Last_ai_obj) {
14008                 AI_FrameCount++;
14009         }
14010
14011         memset( &AI_ci, 0, sizeof(AI_ci) );
14012
14013         ai_frame(obj-Objects);
14014
14015         AI_ci.pitch = 0.0f;
14016         AI_ci.bank = 0.0f;
14017         AI_ci.heading = 0.0f;
14018
14019         // the ships maximum velocity now depends on the energy flowing to engines
14020         obj->phys_info.max_vel.xyz.z = Ships[obj->instance].current_max_speed;
14021         ai_info *aip = &Ai_info[Ships[obj->instance].ai_index];
14022
14023         //      In certain circumstances, the AI says don't fly in the normal way.
14024         //      One circumstance is in docking and undocking, when the ship is moving
14025         //      under thruster control.
14026         switch (aip->mode) {
14027         case AIM_DOCK:
14028                 if ((aip->submode >= AIS_DOCK_2) && (aip->submode != AIS_UNDOCK_3))
14029                         rfc = 0;
14030                 break;
14031         case AIM_WARP_OUT:
14032                 if (aip->submode >= AIS_WARP_3)
14033                         rfc = 0;
14034                 break;
14035 //      case AIM_NONE:
14036 //              if (aip->submode == AIS_NONE_FORMATION)
14037 //                      rfc = 0;
14038 //              break;
14039         default:
14040                 break;
14041         }
14042
14043         if (rfc == 1) {
14044                 vector copy_desired_rotvel = obj->phys_info.rotvel;
14045                 physics_read_flying_controls( &obj->orient, &obj->phys_info, &AI_ci, frametime);
14046                 // if obj is in formation and not flight leader, don't update rotvel
14047                 if (aip->ai_flags & AIF_FORMATION) {
14048                         if (&Objects[aip->goal_objnum] != obj) {
14049                                 obj->phys_info.desired_rotvel = copy_desired_rotvel;
14050                                 obj->phys_info.rotvel = copy_desired_rotvel;
14051                         }
14052                 }
14053         }
14054
14055         Last_ai_obj = obj-Objects;
14056 }
14057
14058 //      Initialize ai_info struct of object objnum.
14059 void init_ai_object(int objnum)
14060 {
14061         int     ship_index, ai_index;
14062         ai_info *aip;
14063         int ship_type;
14064         object  *objp;
14065         vector  near_vec;                       //      A vector nearby and mainly in front of this object.
14066
14067         objp = &Objects[objnum];
14068         ship_index = objp->instance;
14069         ai_index = Ships[ship_index].ai_index;
14070         SDL_assert((ai_index >= 0) && (ai_index < MAX_AI_INFO));
14071
14072         aip = &Ai_info[ai_index];
14073
14074         ship_type = Ships[ship_index].ship_info_index;
14075
14076         vm_vec_scale_add(&near_vec, &objp->pos, &objp->orient.v.fvec, 100.0f);
14077         vm_vec_scale_add2(&near_vec, &objp->orient.v.rvec, 10.0f);
14078
14079         // Things that shouldn't have to get initialized, but initialize them just in case!
14080         aip->ai_flags = 0;
14081         aip->previous_mode = AIM_NONE;
14082         aip->mode_time = -1;
14083         aip->target_objnum = -1;
14084         aip->target_signature = -1;
14085         aip->previous_target_objnum = -1;
14086         aip->target_time = 0.0f;
14087         aip->enemy_wing = -1;
14088         aip->attacker_objnum = -1;
14089         aip->goal_objnum = -1;
14090         aip->goal_signature = -1;
14091         aip->guard_objnum = -1;
14092         aip->guard_signature = -1;
14093         aip->guard_wingnum = -1;
14094         aip->dock_signature = -1;
14095         aip->submode = 0;
14096         aip->previous_submode = 0;
14097         aip->best_dot_to_enemy = -1.0f;
14098         aip->best_dot_from_enemy = -1.0f;
14099         aip->best_dot_to_time = 0;
14100         aip->best_dot_from_time = 0;
14101         aip->submode_start_time = 0;
14102         aip->submode_parm0 = 0;
14103         aip->active_goal = -1;
14104         aip->goal_check_time = timestamp(0);
14105         aip->last_predicted_enemy_pos = near_vec;
14106         aip->prev_goal_point = near_vec;
14107         aip->goal_point = near_vec;
14108         aip->time_enemy_in_range = 0.0f;
14109         aip->last_attack_time = 0;
14110         aip->last_hit_time = 0;
14111         aip->last_hit_quadrant = 0;
14112         aip->hitter_objnum = -1;
14113         aip->hitter_signature = -1;
14114         aip->resume_goal_time = -1;
14115         aip->prev_accel = 0.0f;
14116         aip->prev_dot_to_goal = 0.0f;
14117
14118         aip->ignore_objnum = UNUSED_OBJNUM;
14119         aip->ignore_signature = -1;
14120
14121         // aip->mode = AIM_NONE;
14122
14123         // End of Things that shouldn't have to get initialized, but initialize them just in case!
14124
14125         aip->ai_courage = Ai_classes[Ship_info[ship_type].ai_class].ai_courage[Game_skill_level];
14126         aip->ai_patience = Ai_classes[Ship_info[ship_type].ai_class].ai_patience[Game_skill_level];
14127         aip->ai_evasion = Ai_classes[Ship_info[ship_type].ai_class].ai_evasion[Game_skill_level];
14128         aip->ai_accuracy = Ai_classes[Ship_info[ship_type].ai_class].ai_accuracy[Game_skill_level];
14129
14130         if (Num_waypoint_lists > 0) {
14131                 aip->wp_index = -1;
14132                 aip->wp_list = -1;
14133         } else {
14134                 aip->wp_index = -1;
14135                 aip->wp_list = -1;
14136         }
14137
14138         aip->attacker_objnum = -1;
14139         aip->goal_signature = -1;
14140
14141         Objects[objnum].phys_info.prev_fvec = Objects[objnum].orient.v.fvec;
14142
14143         aip->last_predicted_enemy_pos.xyz.x = 0.0f;     //      Says this value needs to be recomputed!
14144         aip->time_enemy_in_range = 0.0f;
14145
14146         aip->resume_goal_time = -1;                                     //      Say there is no goal to resume.
14147
14148         aip->active_goal = -1;
14149         aip->path_start = -1;
14150         aip->path_goal_dist = -1;
14151         aip->path_length = 0;
14152         aip->path_subsystem_next_check = 1;
14153         aip->dock_path_index = -1;
14154         aip->dock_index = -1;
14155         aip->dock_objnum = -1;
14156
14157         aip->danger_weapon_objnum = -1;
14158         aip->danger_weapon_signature = -1;
14159
14160         aip->lead_scale = 0.0f;
14161         aip->last_hit_target_time = Missiontime;
14162
14163         aip->nearest_locked_object = -1;
14164         aip->nearest_locked_distance = 99999.0f;
14165
14166         aip->targeted_subsys = NULL;
14167         aip->last_subsys_target = NULL;
14168         aip->targeted_subsys_parent = -1;
14169
14170         // The next two fields are used to time the rearming to allow useful sound effects for missile rearming
14171         aip->rearm_first_missile = TRUE;                //      flag to indicate that next missile to load is the first missile
14172         aip->rearm_release_delay = 0;                   //      timestamp to delay the separation of docked ships after rearm
14173
14174         aip->next_predict_pos_time = 0;
14175
14176         aip->afterburner_stop_time = 0;
14177         aip->last_objsig_hit = -1;                              // object signature of the ship most recently hit by aip
14178
14179         aip->path_next_create_time = timestamp(1);
14180         aip->path_create_pos = Objects[objnum].pos;
14181         aip->path_create_orient = Objects[objnum].orient;
14182
14183         aip->ignore_expire_timestamp = timestamp(1);
14184         aip->warp_out_timestamp = 0;
14185         aip->next_rearm_request_timestamp = timestamp(1);
14186         aip->primary_select_timestamp = timestamp(1);
14187         aip->secondary_select_timestamp = timestamp(1);
14188         aip->scan_for_enemy_timestamp = timestamp(1);
14189
14190         aip->choose_enemy_timestamp = timestamp(3*(NUM_SKILL_LEVELS-Game_skill_level) * ((rand_alt() % 500) + 500));
14191
14192         aip->shockwave_object = -1;
14193         aip->shield_manage_timestamp = timestamp(1);
14194         aip->self_destruct_timestamp = -1;      //      This is a flag that we have not yet set this.
14195         aip->ok_to_target_timestamp = timestamp(1);
14196         aip->pick_big_attack_point_timestamp = timestamp(1);
14197         vm_vec_zero(&aip->big_attack_point);
14198
14199         aip->avoid_check_timestamp = timestamp(1);
14200
14201         aip->abort_rearm_timestamp = -1;
14202
14203         // artillery stuff
14204         aip->artillery_objnum = -1;
14205         aip->artillery_sig = -1;        
14206
14207         // waypoint speed cap
14208         aip->waypoint_speed_cap = -1;
14209
14210         // set lethality to enemy team
14211         aip->lethality = 0.0f;
14212 }
14213
14214 void init_ai_objects()
14215 {
14216         int     i;
14217
14218         for (i=0; i<num_objects; i++){
14219                 if (Objects[i].type == OBJ_SHIP){
14220                         init_ai_object(i);
14221                 }
14222         }
14223 }
14224
14225 void init_ai_system()
14226 {
14227         // MWA -- removed next line of code on 11/12/97.  When a ship is created
14228         // it calls init_ai_object() on it's objnum.  Doing this init at the point where
14229         // this function gets called messes things up.
14230         //init_ai_objects();
14231
14232         Ppfp = Path_points;
14233         Waypoints_created = 0;
14234
14235         Dock_path_warning_given = 0;
14236
14237 /*      for (int i=0; i<MAX_IGNORE_OBJECTS; i++) {
14238                 Ignore_objects[i].objnum = -1;
14239                 Ignore_objects[i].signature = -1;
14240         }
14241 */
14242
14243 }
14244
14245 void ai_set_default_behavior(object *obj, int classnum)
14246 {
14247         ai_info *aip;
14248
14249         SDL_assert(obj != NULL);
14250         SDL_assert(obj->instance != -1);
14251         SDL_assert(Ships[obj->instance].ai_index != -1);
14252
14253         aip = &Ai_info[Ships[obj->instance].ai_index];
14254
14255         aip->behavior = classnum;
14256
14257 }
14258
14259 void ai_do_default_behavior(object *obj)
14260 {
14261         ai_info *aip;
14262         int             ship_flags;
14263
14264         SDL_assert(obj != NULL);
14265         SDL_assert(obj->instance != -1);
14266         SDL_assert(Ships[obj->instance].ai_index != -1);
14267
14268         aip = &Ai_info[Ships[obj->instance].ai_index];
14269
14270         ship_flags = Ship_info[Ships[obj->instance].ship_info_index].flags;
14271         if (!is_instructor(obj) && (ship_flags & (SIF_FIGHTER | SIF_BOMBER))) {
14272                 int enemy_objnum = find_enemy(OBJ_INDEX(obj), 1000.0f, Skill_level_max_attackers[Game_skill_level]);
14273                 set_target_objnum(aip, enemy_objnum);
14274                 aip->mode = AIM_CHASE;
14275                 aip->submode = SM_ATTACK;
14276         } else if (ship_flags & (SIF_SUPPORT)) {
14277                 aip->mode = AIM_SAFETY;
14278                 aip->submode = AISS_1;
14279                 aip->ai_flags &= ~(AIF_REPAIRING);
14280         } else if ( ship_flags & SIF_SENTRYGUN ) {
14281                 aip->mode = AIM_SENTRYGUN;
14282         } else {
14283                 aip->mode = AIM_NONE;
14284         }
14285         
14286         aip->submode_start_time = Missiontime;
14287         aip->active_goal = AI_GOAL_NONE;
14288 }
14289
14290 #define FRIENDLY_DAMAGE_THRESHOLD       50.0f           //      Display a message at this threshold.  Note, this gets scaled by Skill_level
14291
14292 // send the given message from objp.  called from the maybe_process_friendly_hit
14293 // code below when a message must get send to the player when he fires on friendlies
14294 void process_friendly_hit_message( int message, object *objp )
14295 {
14296         int index;
14297
14298         // no traitor in multiplayer
14299         if(Game_mode & GM_MULTIPLAYER){
14300                 return;
14301         }
14302
14303         // don't send this message if a player ship was hit.
14304         if ( objp->flags & OF_PLAYER_SHIP ){
14305                 return;
14306         }
14307
14308         // check if objp is a cargo contianer -- if so, then find a new ship to send the message
14309         index = objp->instance;
14310         if ( !(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER|SIF_BOMBER)) ){
14311                 index = -1;
14312         }
14313
14314         // if the message is "oops" (the don't hit me message), always make come from Terran command
14315         if ( message == MESSAGE_OOPS ){
14316                 index = -1;
14317         }
14318
14319         if ( index >= 0){
14320                 message_send_builtin_to_player( message, &Ships[index], MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14321         } else {
14322                 message_send_builtin_to_player( message, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14323         }
14324 }
14325
14326 extern  void ship_set_subsystem_strength( ship *shipp, int type, float strength );
14327
14328 //      Object *objp_weapon, fired by *objp_hitter, hit object *objp_ship.
14329 void maybe_process_friendly_hit(object *objp_hitter, object *objp_hit, object *objp_weapon)
14330 {
14331         // no turning traitor in multiplayer
14332         if ( Game_mode & GM_MULTIPLAYER ) {
14333                 return;
14334         }
14335
14336         // ditto if mission says no traitors allowed
14337         if (The_mission.flags & MISSION_FLAG_NO_TRAITOR) {
14338                 return;
14339         }
14340
14341         if ((objp_hitter == Player_obj) && (Player_ship->team == TEAM_FRIENDLY)) {
14342
14343                 // AL 12-4-97: It is possible the Player is a OBJ_GHOST at this point.  If so, bail out.
14344                 if ( objp_hitter->type != OBJ_SHIP ) {
14345                         return;
14346                 }
14347
14348                 SDL_assert(objp_hitter->type == OBJ_SHIP);
14349                 SDL_assert(objp_hit->type == OBJ_SHIP);
14350                 SDL_assert(objp_weapon->type == OBJ_WEAPON);
14351
14352                 ship    *shipp_hitter = &Ships[objp_hitter->instance];
14353                 ship    *shipp_hit = &Ships[objp_hit->instance];
14354
14355                 if (shipp_hitter->team != shipp_hit->team) {
14356                         return;
14357                 }
14358
14359                 // get the player
14360                 player *pp = &Players[Player_num];
14361
14362                 // wacky stuff here
14363                 if (pp->friendly_hits != 0) {
14364                         float   time_since_last_hit = f2fl(Missiontime - pp->friendly_last_hit_time);
14365                         if ((time_since_last_hit >= 0.0f) && (time_since_last_hit < 10000.0f)) {
14366                                 if (time_since_last_hit > 60.0f) {
14367                                         pp->friendly_hits = 0;
14368                                         pp->friendly_damage = 0.0f;
14369                                 } else if (time_since_last_hit > 2.0f) {
14370                                         pp->friendly_hits -= (int) time_since_last_hit/2;
14371                                         pp->friendly_damage -= time_since_last_hit;
14372                                 }
14373
14374                                 if (pp->friendly_damage < 0.0f) {
14375                                         pp->friendly_damage = 0.0f;
14376                                 }
14377
14378                                 if (pp->friendly_hits < 0) {
14379                                         pp->friendly_hits = 0;
14380                                 }
14381                         }
14382                 }
14383
14384                 float   damage;         //      Damage done by weapon.  Gets scaled down based on size of ship.
14385
14386                 damage = Weapon_info[Weapons[objp_weapon->instance].weapon_info_index].damage;
14387                 
14388                 // wacky stuff here
14389                 ship_info *sip = &Ship_info[Ships[objp_hit->instance].ship_info_index];
14390                 if (sip->initial_hull_strength > 1000.0f) {
14391                         float factor = sip->initial_hull_strength / 1000.0f;
14392                         factor = min(100.0f, factor);
14393                         damage /= factor;
14394                 }
14395
14396                 //      Don't penalize much at all for hitting cargo
14397                 if (sip->flags & (SIF_CARGO | SIF_SENTRYGUN)) {
14398                         damage /= 10.0f;
14399                 }
14400
14401                 //      Hit ship, but not targeting it, so it's not so heinous, maybe an accident.
14402                 if (Ai_info[shipp_hitter->ai_index].target_objnum != OBJ_INDEX(objp_hit)) {
14403                         damage /= 5.0f;
14404                 }
14405
14406                 pp->friendly_last_hit_time = Missiontime;
14407                 pp->friendly_hits++;
14408
14409                 // cap damage and number of hits done this frame
14410                 float accredited_damage = min(MAX_BURST_DAMAGE, pp->damage_this_burst + damage) - pp->damage_this_burst;
14411                 pp->friendly_damage += accredited_damage;
14412                 pp->damage_this_burst += accredited_damage;
14413
14414                 // Done with adjustments to damage.  Evaluate based on current friendly_damage
14415                 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 ));
14416                 
14417                 if (is_instructor(objp_hit)) {
14418                         // it's not nice to hit your instructor
14419                         if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD) {
14420                                 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_ATTACK, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14421                                 pp->last_warning_message_time = Missiontime;
14422                                 ship_set_subsystem_strength( Player_ship, SUBSYSTEM_WEAPONS, 0.0f);
14423
14424                                 training_fail();
14425
14426                                 //      Instructor warp out.
14427                                 ai_set_mode_warp_out(objp_hit, &Ai_info[Ships[objp_hit->instance].ai_index]);
14428                                 gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START_FORCED );     //      Force player to warp out.
14429
14430                                 //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) );
14431                                 //ship_apply_global_damage( objp_hitter, objp_hit, NULL, 1.0f );
14432                         } else if (Missiontime - pp->last_warning_message_time > F1_0*4) {
14433                                 // warning every 4 sec
14434                                 // use NULL as the message sender here since it is the Terran Command persona
14435                                 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_HIT, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14436                                 pp->last_warning_message_time = Missiontime;
14437                         }
14438
14439                 // not nice to hit your friends
14440                 } else if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f)) {
14441                         process_friendly_hit_message( MESSAGE_HAMMER_SWINE, objp_hit );
14442                         mission_goal_fail_all();
14443                         ai_abort_rearm_request( Player_obj );
14444
14445                         Player_ship->team = TEAM_TRAITOR;
14446
14447                 } else if ((damage > frand()) && (Missiontime - pp->last_warning_message_time > F1_0*4) && (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD)) {
14448                         // no closer than 4 sec intervals
14449                         //      Note: (damage > frand()) added on 12/9/97 by MK.  Since damage is now scaled down for big ships, we could get too
14450                         //      many warnings.  Kind of tedious.  frand() returns a value in 0..1, so this won't affect legit hits.
14451                         process_friendly_hit_message( MESSAGE_OOPS, objp_hit );
14452                         pp->last_warning_message_time = Missiontime;
14453                 }
14454         }
14455 }
14456
14457 //      Maybe make ship with ai_info *aip attack hitter_objnum as a dynamic goal
14458 void maybe_set_dynamic_chase(ai_info *aip, int hitter_objnum)
14459 {
14460         SDL_assert(Ship_info[Ships[aip->shipnum].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER));
14461
14462         // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
14463         if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
14464                 return;
14465         }
14466
14467         // only set as target if can be targeted.
14468         if (awacs_get_level(&Objects[hitter_objnum], &Ships[aip->shipnum], 1) < 1) {
14469                 return;
14470         }
14471
14472         if (aip->target_objnum != hitter_objnum)
14473                 aip->aspect_locked_time = 0.0f;
14474         set_target_objnum(aip, hitter_objnum);
14475         aip->resume_goal_time = Missiontime + i2f(20);  //      Only chase up to 20 seconds.
14476         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14477
14478         set_targeted_subsys(aip, NULL, -1);             //      Say not attacking any particular subsystem.
14479
14480         aip->previous_submode = aip->mode;
14481         aip->mode = AIM_CHASE;
14482         aip->submode = SM_ATTACK;
14483 }
14484
14485
14486 //      Return true if *objp has armed an aspect seeking bomb.
14487 //      This function written so a ship with an important bomb to fire will willingly take hits in the face to fire its bomb.
14488 int firing_aspect_seeking_bomb(object *objp)
14489 {
14490         ship    *shipp;
14491         int     bank_index;
14492         ship_weapon     *swp;
14493
14494         shipp = &Ships[objp->instance];
14495
14496         swp = &shipp->weapons;
14497
14498         bank_index = swp->current_secondary_bank;
14499
14500         if (bank_index != -1)
14501                 if (swp->secondary_bank_ammo[bank_index] > 0) {
14502                         if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_BOMB) {
14503                                 if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_HOMING_ASPECT) {
14504                                         return 1;
14505                                 }
14506                         }
14507                 }
14508
14509         return 0;
14510 }
14511
14512 //      *objp collided with big ship *big_objp at global point *collide_pos
14513 //      Make it fly away from the collision point.
14514 // collision_normal is NULL, when a collision is imminent and we just want to bug out.
14515 void big_ship_collide_recover_start(object *objp, object *big_objp, vector *collide_pos, vector *collision_normal)
14516 {
14517         ai_info *aip;
14518
14519         SDL_assert(objp->type == OBJ_SHIP);
14520
14521         aip = &Ai_info[Ships[objp->instance].ai_index];
14522
14523         if (!timestamp_elapsed(aip->big_recover_timestamp) && (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1))
14524                 return;
14525
14526         //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)));
14527         if (collision_normal) {
14528                 aip->big_recover_timestamp = timestamp(2000);
14529                 aip->big_collision_normal = *collision_normal;
14530         //      nprintf(("AI", " normal\n"));
14531         } else {
14532                 aip->big_recover_timestamp = timestamp(500);
14533         //      nprintf(("AI", " no normal\n"));
14534         }
14535
14536
14537         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
14538         aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_1;
14539
14540 //      vector  out_vec;
14541 //      vm_vec_normalized_dir(&out_vec, &objp->pos, collide_pos);
14542
14543         // big_recover_pos_1 is 100 m out along normal
14544         vector direction;
14545         if (collision_normal) {
14546                 direction = *collision_normal;
14547         } else {
14548                 vm_vec_copy_scale(&direction, &objp->orient.v.fvec, -1.0f);
14549         }
14550         vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &direction, 100.0f);
14551
14552         // go out 200 m from box closest box point
14553         get_world_closest_box_point_with_delta(&aip->big_recover_pos_2, big_objp, &aip->big_recover_pos_1, NULL, 300.0f);
14554
14555         accelerate_ship(aip, 0.0f);
14556 /*
14557         if (vm_vec_dot(collision_normal, &objp->orient.v.fvec) > 0.5f) {
14558 //              vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14559 //              vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.v.uvec, big_objp->radius/2.0f);
14560 //              vm_vec_scale_add(&aip->big_recover_pos_2, &objp->pos, &out_vec, big_objp->radius*2.0f);
14561                 accelerate_ship(aip, 2.0f);
14562         } else {
14563 //              vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14564 //              vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.v.uvec, big_objp->radius/2.0f);
14565                 accelerate_ship(aip, 0.0f);
14566         } */
14567 }
14568
14569 float max_lethality = 0.0f;
14570
14571 void ai_update_lethality(object *ship_obj, object *other_obj, float damage)
14572 {
14573         SDL_assert(ship_obj->type == OBJ_SHIP);
14574         SDL_assert(other_obj->type == OBJ_WEAPON || other_obj->type == OBJ_SHOCKWAVE);
14575         int dont_count = FALSE;
14576
14577         int parent = other_obj->parent;
14578         if (Objects[parent].type == OBJ_SHIP) {
14579                 if (Objects[parent].signature == other_obj->parent_sig) {
14580
14581                         // check damage done to enemy team
14582                         if (Ships[ship_obj->instance].team != Ships[Objects[parent].instance].team) {
14583
14584                                 // other is weapon
14585                                 if (other_obj->type == OBJ_WEAPON) {
14586                                         weapon *wp = &Weapons[other_obj->instance];
14587                                         weapon_info *wif = &Weapon_info[wp->weapon_info_index];
14588
14589                                         // if parent is BIG|HUGE, don't count beam
14590                                         if (Ship_info[Ships[Objects[parent].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
14591                                                 if (wif->wi_flags & WIF_BEAM) {
14592                                                         dont_count = TRUE;
14593                                                 }
14594                                         }
14595                                 }
14596
14597                                 if (!dont_count) {
14598                                         float lethality = 0.025f * damage;      // 2 cyclops (@2000) put you at 100 lethality
14599
14600                                         // increase lethality weapon's parent ship
14601                                         ai_info *aip = &Ai_info[Ships[Objects[parent].instance].ai_index];
14602                                         aip->lethality += lethality;
14603                                         aip->lethality = min(110.0f, aip->lethality);
14604                                         // if you hit, don;t be less than 0
14605                                         aip->lethality = max(0.0f, aip->lethality);
14606
14607 //                                      if (aip->lethality > max_lethality) {
14608 //                                              max_lethality = aip->lethality;
14609 //                                              mprintf(("new lethalilty high: %.1f\n", max_lethality));
14610 //                                      }
14611
14612                                         // if parent is player, show his lethality
14613 //                                      if (Objects[parent].flags & OF_PLAYER_SHIP) {
14614 //                                              mprintf(("Player lethality: %.1f\n", aip->lethality));
14615 //                                      }
14616                                 }
14617                         }
14618                 }
14619         }
14620 }
14621
14622
14623 //      Object *objp_ship was hit by either weapon *objp_weapon or collided into by ship hit_objp at point *hitpos.
14624 void ai_ship_hit(object *objp_ship, object *hit_objp, vector *hitpos, int shield_quadrant, vector *hit_normal)
14625 {
14626         int             hitter_objnum = -2;
14627         object  *objp_hitter = NULL;
14628         ship            *shipp;
14629         ai_info *aip, *hitter_aip;
14630
14631         shipp = &Ships[objp_ship->instance];
14632         aip = &Ai_info[shipp->ai_index];
14633
14634         if (objp_ship->flags & OF_PLAYER_SHIP)
14635                 return;
14636
14637         if ((aip->mode == AIM_WARP_OUT) || (aip->mode == AIM_PLAY_DEAD))
14638                 return;
14639
14640         if (hit_objp->type == OBJ_SHIP) {
14641                 //      If the object that this ship collided with is a big ship
14642                 if (Ship_info[Ships[hit_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
14643                         //      And the current object is _not_ a big ship
14644                         if (!(Ship_info[Ships[objp_ship->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
14645                                 //      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.
14646                                 big_ship_collide_recover_start(objp_ship, hit_objp, hitpos, hit_normal);
14647                         }
14648                 }
14649         }
14650
14651         if (hit_objp->type == OBJ_WEAPON) {
14652                 //      Make sure the object that fired this weapon is still alive.  If not, abort.
14653                 // SDL_assert(hit_objp->parent >= 0);
14654                 if(hit_objp->parent < 0){
14655                         return;
14656                 }
14657                 if ( hit_objp->parent_sig != Objects[hit_objp->parent].signature ){
14658                         return;
14659                 }
14660
14661                 //      Hit by a protected ship, don't attack it.
14662                 if (Objects[hit_objp->parent].flags & OF_PROTECTED) {
14663                         if ((Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) && (aip->target_objnum == -1)) {
14664                                 if (aip->mode == AIM_CHASE) {
14665                                         if (aip->submode != SM_EVADE_WEAPON) {
14666                                                 aip->mode = AIM_CHASE;
14667                                                 aip->submode = SM_EVADE_WEAPON;
14668                                                 aip->submode_start_time = Missiontime;
14669                                         }
14670                                 } else if (aip->mode != AIM_EVADE_WEAPON) {
14671                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14672                                         aip->previous_mode = aip->mode;
14673                                         aip->previous_submode = aip->submode;
14674                                         aip->mode = AIM_EVADE_WEAPON;
14675                                         aip->submode = -1;
14676                                         aip->submode_start_time = Missiontime;
14677                                         aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Evade for up to five seconds.
14678                                 }
14679
14680                         }
14681                         return;
14682                 }
14683
14684                 hitter_objnum = hit_objp->parent;
14685                 SDL_assert((hitter_objnum >= 0) && (hitter_objnum < MAX_OBJECTS));
14686                 objp_hitter = &Objects[hitter_objnum];
14687                 maybe_process_friendly_hit(objp_hitter, objp_ship, hit_objp);           //      Deal with player's friendly fire.
14688
14689                 if ( (shipp->team & TEAM_FRIENDLY) && !(Game_mode & GM_MULTIPLAYER) ) {
14690                         ship_maybe_ask_for_help(shipp);
14691                 }
14692         } else if (hit_objp->type == OBJ_SHIP) {
14693                 if (shipp->team == Ships[hit_objp->instance].team)              //      Don't have AI react to collisions between teammates.
14694                         return;
14695                 objp_hitter = hit_objp;
14696                 hitter_objnum = hit_objp-Objects;
14697         } else {
14698                 Int3(); //      Hmm, what kind of object hit this if not weapon or ship?  Get MikeK.
14699                 return;
14700         }
14701
14702         //      Collided into a protected ship, don't attack it.
14703         if (hit_objp->flags & OF_PROTECTED)
14704                 return;
14705
14706         SDL_assert(objp_hitter != NULL);
14707         hitter_aip = &Ai_info[Ships[objp_hitter->instance].ai_index];
14708         hitter_aip->last_hit_target_time = Missiontime;
14709         
14710         // store the object signature of objp_ship into ai_info, since we want to track the last ship hit by 'hitter_objnum'
14711         hitter_aip->last_objsig_hit = objp_ship->signature; 
14712
14713         aip->last_hit_time = Missiontime;
14714
14715         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
14716                 return;
14717
14718         //      If this ship is awaiting repair, abort!
14719         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)) {
14720                 ship_info       *sip = &Ship_info[shipp->ship_info_index];
14721
14722                 if (objp_ship->hull_strength/sip->initial_hull_strength < 0.3f) {
14723                         //      No, only abort if hull below a certain level.
14724                         aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP/2);  //      Might request again after 15 seconds.
14725                         if ( !(objp_ship->flags & OF_PLAYER_SHIP) )                                             // mwa -- don't abort rearm for a player
14726                                 ai_abort_rearm_request(objp_ship);
14727                 }
14728         }
14729
14730         //      If firing a bomb, ignore enemy fire so we can gain lock drop the bomb.
14731         //      Only ignore fire if aspect_locked_time > 0.5f, as this means we're in range.
14732         if (firing_aspect_seeking_bomb(objp_ship)) {
14733                 if ((aip->ai_flags & AIF_SEEK_LOCK) && (aip->aspect_locked_time > 0.1f))
14734                         return;
14735         }
14736
14737         //      If in AIM_STRAFE mode and got hit by target, maybe attack turret if appropriate
14738         if (aip->mode == AIM_STRAFE) {
14739                 SDL_assert(hitter_objnum != -2);
14740                 if (aip->target_objnum == hitter_objnum) {
14741                         if ( hit_objp->type == OBJ_WEAPON ) {
14742                                 ai_big_strafe_maybe_attack_turret(objp_ship, hit_objp);
14743                         }
14744                         return;
14745                 }
14746                 else {
14747                                 // AL 11-10-97:
14748                         ;       // do nothing here, we'll attack this hitter if it is a fighter or bomber (this is handled
14749                                 // in code later in this function
14750                 }
14751         }
14752
14753         if (objp_ship == Player_obj)
14754                 return;         //      We don't do AI for the player.
14755
14756         maybe_update_guard_object(objp_ship, objp_hitter);
14757
14758         //      Big ships don't go any further.
14759         if (!(Ship_info[shipp->ship_info_index].flags & SIF_SMALL_SHIP))
14760                 return;
14761
14762         //      If the hitter object is the ignore object, don't attack it.
14763         ship_info       *sip = &Ship_info[shipp->ship_info_index];
14764         if ((is_ignore_object(aip, objp_hitter-Objects)) && (sip->flags & (SIF_BOMBER | SIF_FIGHTER))) {
14765                 if (aip->mode == AIM_NONE) {
14766                         aip->mode = AIM_CHASE;  //      This will cause the ship to move, if not attack.
14767                         aip->submode = SM_EVADE;
14768                 }
14769                 return;
14770         }
14771
14772         //      Maybe abort based on mode.
14773         switch (aip->mode) {
14774         case AIM_CHASE:
14775                 if (aip->submode == SM_ATTACK_FOREVER)
14776                         return;
14777
14778                 if ( hit_objp->type == OBJ_WEAPON ) {
14779                         if ( ai_big_maybe_enter_strafe_mode(objp_ship, OBJ_INDEX(hit_objp), 1) )
14780                                 return;
14781                 }
14782
14783         case AIM_GUARD:
14784                 //      If in guard mode and far away from guard object, don't pursue guy that hit me.
14785                         if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
14786                                 if (vm_vec_dist_quick(&objp_ship->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
14787                                         return;
14788                                 }
14789                         }
14790         case AIM_STILL:
14791         case AIM_STAY_NEAR:
14792                 // Note: Dealt with above, at very top.  case AIM_PLAY_DEAD:
14793         case AIM_STRAFE:
14794                 break;
14795         case AIM_EVADE_WEAPON:
14796         case AIM_EVADE:
14797         case AIM_GET_BEHIND:
14798         case AIM_AVOID:
14799         case AIM_DOCK:
14800         case AIM_BIGSHIP:
14801         case AIM_PATH:
14802         case AIM_NONE:
14803         case AIM_BAY_DEPART:
14804         case AIM_SENTRYGUN:
14805                 return;
14806         case AIM_BAY_EMERGE:
14807                 // If just leaving the docking bay, don't react to enemy fire... just keep flying away from docking bay
14808                 if ( (Missiontime - aip->submode_start_time) < 5*F1_0 ) {
14809                         return;
14810                 }
14811                 break;
14812         case AIM_WAYPOINTS:
14813                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER))
14814                         break;
14815                 else
14816                         return;
14817                 break;
14818         case AIM_SAFETY:
14819                 if ((aip->submode != AISS_1) || (Missiontime - aip->submode_start_time > i2f(1))) {
14820                         aip->submode = AISS_1;
14821                         aip->submode_start_time = Missiontime;
14822                 }
14823                 return;
14824                 break;
14825         case AIM_WARP_OUT:
14826                 return;
14827                 break;
14828         default:
14829                 Int3(); //      Bogus mode!
14830         }
14831
14832         if (timestamp_elapsed(aip->ok_to_target_timestamp))
14833                 aip->ai_flags &= ~AIF_FORMATION;                        //      If flying in formation, bug out!
14834
14835         aip->hitter_objnum = hitter_objnum;
14836         aip->hitter_signature = Objects[hitter_objnum].signature;
14837
14838         //      If the hitter is not on the same team as the hittee, do some stuff.
14839         if (shipp->team != Ships[objp_hitter->instance].team) {
14840                 //nprintf(("AI", "Object %i attacking %i, who just hit him!\n", objp_ship-Objects, hitter_objnum));
14841
14842                 if ((hitter_objnum != aip->target_objnum) && (sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
14843                         maybe_set_dynamic_chase(aip, hitter_objnum);
14844                         maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14845                 } else {
14846                         if ((aip->mode == AIM_CHASE) && ((objp_ship->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp_ship)/sip->shields > 0.8f))) {
14847                                 switch (aip->submode) {
14848                                 case SM_ATTACK:
14849                                 case SM_SUPER_ATTACK:
14850                                 case SM_GET_AWAY:
14851                                         break;
14852                                 default:
14853                                         if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
14854                                                 maybe_set_dynamic_chase(aip, hitter_objnum);
14855                                         }
14856                                         maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14857                                         break;
14858                                 }
14859                         } else if (aip->mode == AIM_CHASE) {
14860                                 switch (aip->submode) {
14861                                 case SM_ATTACK:
14862                                         aip->submode = SM_EVADE;
14863                                         aip->submode_start_time = Missiontime;
14864                                         break;
14865                                 case SM_SUPER_ATTACK:
14866                                         if (Missiontime - aip->submode_start_time > i2f(1)) {
14867                                                 aip->submode = SM_EVADE;
14868                                                 aip->submode_start_time = Missiontime;
14869                                         }
14870                                         break;
14871                                 case SM_EVADE_BRAKE:
14872                                         break;
14873                                 case SM_EVADE_SQUIGGLE:
14874                                         aip->submode = SM_EVADE;
14875                                         aip->submode_start_time = Missiontime;
14876                                         break;
14877                                 default:
14878                                         if (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) {
14879                                                 maybe_set_dynamic_chase(aip, hitter_objnum);
14880                                                 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14881                                         }
14882
14883                                         break;
14884                                 }
14885                         } else {
14886                                 // AL 3-15-98: Prevent escape pods from entering chase mode
14887                                 if ( (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) ) {
14888                                         maybe_set_dynamic_chase(aip, hitter_objnum);
14889                                 }
14890                                 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14891                         }
14892                 }
14893         }
14894 }
14895
14896 //      Ship shipnum has been destroyed.
14897 //      Cleanup.
14898 // the parameter 'method' is used to tell is this ship was destroyed or it departed normally.
14899 // This function will get called in either case, and there are things that should be done if
14900 // the ship actually gets destroyed which shouldn't get done if it departed.
14901 void ai_ship_destroy(int shipnum, int method)
14902 {
14903         int             objnum;
14904         object  *other_objp;
14905         ship            *shipp;
14906         ship_obj        *so;
14907         ai_info *dead_aip;
14908
14909         SDL_assert((shipnum >= 0) && (shipnum < MAX_SHIPS));
14910         objnum = Ships[shipnum].objnum;
14911         dead_aip = &Ai_info[Ships[shipnum].ai_index];
14912
14913         // if I was getting repaired, or awaiting repair, then cleanup the repair mode.  When awaiting repair, the dock_objnum
14914         // is -1.  When the support ship is on the way, the dock_objnum >= 0 (points to support ship).
14915         if ( dead_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14916                 if ( dead_aip->dock_objnum >= 0 )
14917                         ai_do_objects_repairing_stuff( &Objects[objnum], &Objects[dead_aip->dock_objnum], REPAIR_INFO_END);
14918                 else
14919                         ai_do_objects_repairing_stuff( &Objects[objnum], NULL, REPAIR_INFO_END );
14920         }
14921
14922         //      For all objects that had this ship as a target, wipe it out, forcing find of a new enemy.
14923         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
14924                 other_objp = &Objects[so->objnum];
14925                 SDL_assert(other_objp->instance != -1);
14926
14927                 shipp = &Ships[other_objp->instance];
14928                 SDL_assert(shipp->ai_index != -1);
14929
14930                 ai_info *aip = &Ai_info[shipp->ai_index];
14931
14932                 // MWA 2/11/98
14933                 // code commented out below is taken care of in ai_cleanup_dock_mode when gets called when the
14934                 // support ship starts it's death roll.
14935
14936                 //      If the destroyed ship was on its way to repair the current ship
14937                 if (aip->dock_objnum == objnum) {
14938
14939                         // clean up the flags for any kind of docking mode.  If aip was part of a goal of dock/undock
14940                         // then it will get cleaned up by the goal code.
14941                         ai_do_objects_undocked_stuff( other_objp, NULL );
14942
14943                         if ( aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14944                                 int abort_reason;
14945                                 if ( method == SEF_DEPARTED ) {
14946                                         abort_reason = REPAIR_INFO_ABORT;
14947                                 } else {
14948                                         abort_reason = REPAIR_INFO_KILLED;
14949                                 }
14950                                 ai_do_objects_repairing_stuff( other_objp, NULL, abort_reason );
14951                         }
14952                 }
14953
14954                 if (aip->target_objnum == objnum) {
14955                         set_target_objnum(aip, -1);
14956                         //      If this ship had a dynamic goal of chasing the dead ship, clear the dynamic goal.
14957                         if (aip->resume_goal_time != -1)
14958                                 aip->active_goal = AI_GOAL_NONE;
14959                 }
14960
14961                 if (aip->goal_objnum == objnum) {
14962                         aip->goal_objnum = -1;
14963                         aip->goal_signature = -1;
14964                 }
14965
14966                 if (aip->guard_objnum == objnum) {
14967                         aip->guard_objnum = -1;
14968                         aip->guard_signature = -1;
14969                 }
14970
14971                 if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[Objects[objnum].instance].ai_index].wing)) {
14972                         if (aip->guard_wingnum != aip->wing)
14973                                 ai_set_guard_wing(other_objp, aip->guard_wingnum);
14974                 }
14975
14976                 if (aip->hitter_objnum == objnum)
14977                         aip->hitter_objnum = -1;
14978
14979         }
14980
14981 }
14982
14983 /*
14984 //      Interface function to goals code.
14985 //      Make object *objp fly to point *vp and warp out.
14986 void ai_warp_out(object *objp, vector *vp)
14987 {
14988         ai_info *aip;
14989
14990         aip = &Ai_info[Ships[objp->instance].ai_index];
14991
14992         if (aip->mode != AIM_WARP_OUT) {
14993                 ai_set_mode_warp_out(objp, aip);
14994         }
14995         float   dist;
14996         float   dot;
14997         vector  v2v;
14998         ai_info *aip;
14999
15000         dist = vm_vec_normalized_dir(&v2v, vp, &objp->pos);
15001
15002         if (dist < objp->radius + 5.0f) {
15003
15004                 // Start the warp out effect 
15005                 shipfx_warpout_start(objp);
15006
15007         } else {
15008                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2v);
15009
15010                 aip = &Ai_info[Ships[objp->instance].ai_index];
15011
15012                 if (dist > 500.0f)
15013                         accelerate_ship(aip, 1.0f);
15014                 else
15015                         accelerate_ship(aip, (3*dot + 1.0f)/4.0f);
15016
15017                 turn_towards_point(objp, vp, NULL, 0.0f);
15018         }
15019 }
15020 */
15021
15022
15023 //      Do stuff at start of deathroll.
15024 void ai_deathroll_start(object *ship_obj)
15025 {
15026         ai_info *aip;
15027         ship            *shipp, *other_ship;
15028
15029         shipp = &Ships[ship_obj->instance];
15030         aip = &Ai_info[shipp->ai_index];
15031
15032         // mark object we are docked with so we can do damage and separate during deathroll
15033         // keep dock_objnum_when_dead from being changed if already set (only allow to be set when -1)
15034         if (Ships[ship_obj->instance].dock_objnum_when_dead == -1) {
15035                 Ships[ship_obj->instance].dock_objnum_when_dead = aip->dock_objnum;
15036                 // set other_ship dock_objnum_when_dead, if other_ship exits.
15037                 if (Ships[ship_obj->instance].dock_objnum_when_dead != -1) {
15038                         other_ship = &Ships[Objects[aip->dock_objnum].instance];
15039                         other_ship->dock_objnum_when_dead = shipp->objnum;
15040                 }
15041         }
15042
15043         ai_cleanup_dock_mode(aip, shipp);
15044
15045         aip->mode = AIM_NONE;
15046 }
15047
15048 //      Object *requester_objp tells rearm ship to abort rearm.
15049 //      Returns true if it succeeded, else false.
15050 //      To succeed means you were previously rearming.
15051 int ai_abort_rearm_request(object *requester_objp)
15052 {
15053         ship            *requester_shipp;
15054         ai_info *requester_aip;
15055
15056         SDL_assert(requester_objp->type == OBJ_SHIP);
15057         if(requester_objp->type != OBJ_SHIP){
15058                 return 0;
15059         }
15060         SDL_assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));  
15061         if((requester_objp->instance < 0) || (requester_objp->instance >= MAX_SHIPS)){
15062                 return 0;
15063         }
15064         requester_shipp = &Ships[requester_objp->instance];
15065         SDL_assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));              
15066         if((requester_shipp->ai_index < 0) || (requester_shipp->ai_index >= MAX_AI_INFO)){
15067                 return 0;
15068         }       
15069         requester_aip = &Ai_info[requester_shipp->ai_index];
15070         
15071         if (requester_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)){
15072
15073                 // dock_objnum is always valid once a rearm repair has been requested.  It points to the
15074                 // ship that is coming to repair me.
15075                 if (requester_aip->dock_objnum != -1) {
15076                         object  *repair_objp;
15077                         ai_info *repair_aip;
15078
15079                         repair_objp = &Objects[requester_aip->dock_objnum];
15080                         repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
15081
15082                         //      Make sure signatures match.  This prevents nasty bugs in which an object
15083                         //      that was repairing another is destroyed and is replaced by another ship
15084                         //      before this code comes around.
15085                         if (repair_objp->signature == requester_aip->dock_signature) {
15086
15087                                 SDL_assert( repair_objp->type == OBJ_SHIP );
15088
15089                                 // if support ship is in the process of undocking, don't do anything.
15090                                 if ( repair_aip->submode < AIS_UNDOCK_0 ) {
15091                                         ai_do_objects_repairing_stuff( requester_objp, repair_objp, REPAIR_INFO_ABORT );
15092
15093                                         if ( repair_aip->submode == AIS_DOCK_4 )
15094                                                 repair_aip->submode = AIS_UNDOCK_0;
15095                                         else
15096                                                 repair_aip->submode = AIS_UNDOCK_3;
15097
15098                                         repair_aip->submode_start_time = Missiontime;
15099                                 } else {
15100                                         nprintf(("AI", "Not aborting rearm since already undocking\n"));
15101                                 }
15102                         }
15103                 } else {
15104                         // setting these flags is the safe things to do.  There may not be a corresponding repair
15105                         // ship for this guys since a repair ship may be currently repairing someone else.
15106                         ai_do_objects_repairing_stuff( requester_objp, NULL, REPAIR_INFO_ABORT );
15107
15108                         // try and remove this guy from an arriving support ship.
15109                         mission_remove_scheduled_repair(requester_objp);
15110                 }
15111
15112                 return 1;
15113         } else if ( requester_aip->ai_flags & AIF_REPAIRING ) {
15114                 // a support ship can request to abort when he is told to do something else (like warp out).
15115                 // see if this support ships goal_objnum is valid.  If so, then issue this ai_abort comment
15116                 // for the ship that he is enroute to repair
15117                 if ( requester_aip->goal_objnum != -1 ) {
15118                         int val;
15119
15120                         val = ai_abort_rearm_request( &Objects[requester_aip->goal_objnum] );
15121                         return val;
15122                 }
15123         }
15124
15125         return 0;
15126 }
15127
15128 // function which gets called from ai-issue_rearm_request and from code in missionparse.cpp
15129 // to actually issue the rearm goal (support_obj to rearm requester_obj);
15130 void ai_add_rearm_goal( object *requester_objp, object *support_objp )
15131 {
15132         ship *support_shipp, *requester_shipp;
15133         ai_info *support_aip, *requester_aip;
15134
15135         support_shipp = &Ships[support_objp->instance];
15136         requester_shipp = &Ships[requester_objp->instance];
15137         requester_aip = &Ai_info[requester_shipp->ai_index];
15138
15139         SDL_assert( support_shipp->ai_index != -1 );
15140         support_aip = &Ai_info[support_shipp->ai_index];
15141
15142         // if the requester is a player object, issue the order as the squadmate messaging code does.  Doing so
15143         // ensures that the player get a higher priority!
15144         requester_aip->ai_flags |= AIF_AWAITING_REPAIR; //      Tell that I'm awaiting repair.
15145         if ( requester_objp->flags & OF_PLAYER_SHIP )
15146                 ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_REARM_REPAIR, -1, requester_shipp->ship_name, support_aip );
15147         else
15148                 ai_add_goal_ship_internal( support_aip, AI_GOAL_REARM_REPAIR, requester_shipp->ship_name, -1, -1 );
15149
15150 }
15151
15152 //      Object *requester_objp requests rearming.
15153 //      Returns objnum of ship coming to repair requester on success
15154 //      Success means you found someone to rearm you and you weren't previously rearming.
15155 int ai_issue_rearm_request(object *requester_objp)
15156 {
15157         object  *objp;
15158         ship            *requester_shipp;
15159         ai_info *requester_aip;
15160
15161         SDL_assert(requester_objp->type == OBJ_SHIP);
15162         SDL_assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));
15163         requester_shipp = &Ships[requester_objp->instance];
15164         SDL_assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));
15165         requester_aip = &Ai_info[requester_shipp->ai_index];
15166         
15167         //      Make sure not already awaiting repair.
15168         if (requester_aip->ai_flags & AIF_AWAITING_REPAIR) {
15169                 nprintf(("AI", "Ship %s already awaiting rearm by ship %s.\n", requester_shipp->ship_name, &Ships[Objects[requester_aip->dock_objnum].instance].ship_name));    
15170                 return -1;
15171         }
15172
15173         if ( !is_support_allowed(requester_objp) )
15174                 return -1;
15175
15176         //nprintf(("AI", "Ship %s requesting rearming.\n", requester_shipp->ship_name));
15177         requester_aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);  //      Might request again after this much time.
15178
15179         // call ship_find_repair_ship to get a support ship.  If none is found, then we will warp one in.  This
15180         // function will return the next available ship which can repair requester
15181         objp = ship_find_repair_ship( requester_objp );
15182         ai_do_objects_repairing_stuff( requester_objp, objp, REPAIR_INFO_QUEUE );
15183         if ( objp ) {
15184
15185                 // MWA 5/14/98 -- moved next item into the ai_do_objects_repairing_stuff function so that clients
15186                 // would properly update their hud support view
15187                 //ai_add_rearm_goal( requester_objp, objp );
15188                 return OBJ_INDEX(objp);
15189
15190         } else {
15191                 // call to warp in repair ship!!!!  for now, warp in any number of ships needed.  Should cap it to
15192                 // some reasonable max (or let support ships warp out).  We should assume here that ship_find_repair_ship()
15193                 // would have returned a valid object if there are too many support ships already in the mission
15194                 mission_warp_in_support_ship( requester_objp );
15195
15196                 return -1;
15197         }
15198
15199 }
15200
15201 // make objp rearm and repair goal_objp
15202 void ai_rearm_repair( object *objp, object  *goal_objp, int priority, int docker_index, int dockee_index )
15203 {
15204         ai_info *aip, *goal_aip;
15205
15206         aip = &Ai_info[Ships[objp->instance].ai_index];
15207         aip->goal_objnum = goal_objp-Objects;
15208
15209         // nprintf(("AI", "Ship %s preparing to rearm ship %s.\n", shipp->ship_name, requester_shipp->ship_name));
15210
15211         ai_dock_with_object(objp, goal_objp, priority, AIDO_DOCK, docker_index, dockee_index);
15212         aip->ai_flags |= AIF_REPAIRING;                                         //      Tell that repair guy is busy trying to repair someone.
15213
15214         goal_aip = &Ai_info[Ships[goal_objp->instance].ai_index];
15215         goal_aip->dock_objnum = objp-Objects;           //      Tell which object is coming to repair.
15216         goal_aip->dock_signature = objp->signature;
15217
15218         ai_do_objects_repairing_stuff( goal_objp, objp, REPAIR_INFO_ONWAY );
15219
15220         goal_aip->abort_rearm_timestamp = timestamp(NEXT_REARM_TIMESTAMP*3/2);
15221 }
15222
15223 // Given a dockee object and the index of the dockbay for that object (ie the dockbay index
15224 // into polymodel->dockbays[] for the model associated with the object), return the index
15225 // of a path_num associated with than dockbay (this is an index into polymodel->paths[])
15226 int ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index)
15227 {
15228         if ( dockbay_index < 0 || dockee_objp == NULL ) {
15229                 Int3();         // should never happen
15230                 return -1;
15231         }
15232
15233         if ( dockee_objp->type == OBJ_SHIP ) {
15234                 int                     path_num;
15235                 polymodel       *pm;
15236
15237                 pm = model_get( Ships[dockee_objp->instance].modelnum );
15238
15239                 // sanity checks
15240                 SDL_assert(pm->n_docks > dockbay_index);
15241                 SDL_assert(pm->docking_bays[dockbay_index].num_spline_paths > 0);
15242                 SDL_assert(pm->docking_bays[dockbay_index].splines != NULL);
15243                 if(pm->n_docks <= dockbay_index){
15244                         return -1;
15245                 }
15246                 if(pm->docking_bays[dockbay_index].num_spline_paths <= 0){
15247                         return -1;
15248                 }
15249                 if(pm->docking_bays[dockbay_index].splines == NULL){
15250                         return -1;
15251                 }
15252
15253                 // We only need to return one path for the dockbay, so return the first
15254                 path_num = pm->docking_bays[dockbay_index].splines[0];
15255                 return path_num;
15256         } else {
15257                 return -1;
15258         }
15259 }
15260
15261 //      Actually go ahead and fire the synaptics.
15262 void cheat_fire_synaptic(object *objp, ship *shipp, ai_info *aip)
15263 {
15264         ship_weapon     *swp;
15265         swp = &shipp->weapons;
15266         int     current_bank = swp->current_secondary_bank;
15267
15268         ai_select_secondary_weapon(objp, swp, WIF_SPAWN, 0);
15269         if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
15270                 if (ship_fire_secondary(objp)) {
15271                         nprintf(("AI", "ship %s cheat fired synaptic!\n", shipp->ship_name));
15272                         swp->next_secondary_fire_stamp[current_bank] = timestamp(2500);
15273                 }
15274         }
15275 }
15276
15277 //      For the subspace mission (sm3-09a)
15278 //              for delta wing
15279 //                      if they're sufficiently far into the mission
15280 //                              if they're near one or more enemies
15281 //                                      every so often
15282 //                                              fire a synaptic if they have one.
15283 void maybe_cheat_fire_synaptic(object *objp, ai_info *aip)
15284 {
15285         //      Only do in subspace missions.
15286         if ( The_mission.flags & MISSION_FLAG_SUBSPACE )        {
15287                 ship    *shipp;
15288                 int     num, time;
15289
15290                 shipp = &Ships[objp->instance];
15291
15292                 if (!(SDL_strncasecmp(shipp->ship_name, NOX("delta"), 5))) {
15293                         num = shipp->ship_name[6] - '1';
15294
15295                         if ((num >= 0) && (num <= 3)) {
15296                                 time = Missiontime >> 16;       //      Convert to seconds.
15297
15298                                 time -= 2*60;   //      Subtract off two minutes.
15299
15300                                 if (time > 0) {
15301                                         int modulus = 17 + num*3;
15302
15303                                         if ((time % modulus) < 2) {
15304                                                 int count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(objp)), &objp->pos, 1500.0f);
15305
15306                                                 if (count > 0) {
15307                                                         cheat_fire_synaptic(objp, shipp, aip);
15308                                                 }
15309                                         }
15310                                 }
15311                         }
15312                 }
15313         }
15314
15315 }
15316