]> icculus.org git repositories - taylor/freespace2.git/blob - src/ship/aicode.cpp
Various 64-bit platform fixes
[taylor/freespace2.git] / src / ship / aicode.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Ship/AiCode.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  * 
15  * AI code that does interesting stuff
16  *
17  * $Log$
18  * Revision 1.10  2005/03/29 02:18:47  taylor
19  * Various 64-bit platform fixes
20  * Fix compiler errors with MAKE_FS1 and fix gr_set_bitmap() too
21  * Make sure that turrets can fire at asteroids for FS1 (needed for a couple missions)
22  * Streaming audio support (big thanks to Pierre Willenbrock!!)
23  * Removed dependance on strings.tbl for FS1 since we don't actually need it now
24  *
25  * Revision 1.9  2004/09/20 01:31:44  theoddone33
26  * GCC 3.4 fixes.
27  *
28  * Revision 1.8  2003/08/03 16:10:30  taylor
29  * cleanup; compile warning fixes
30  *
31  * Revision 1.7  2003/05/25 02:30:43  taylor
32  * Freespace 1 support
33  *
34  * Revision 1.6  2002/07/13 19:47:02  theoddone33
35  * Fix some more warnings
36  *
37  * Change demo building, edit Makefile if you want the demo.
38  *
39  * Revision 1.5  2002/06/17 06:33:10  relnev
40  * ryan's struct patch for gcc 2.95
41  *
42  * Revision 1.4  2002/06/09 04:41:26  relnev
43  * added copyright header
44  *
45  * Revision 1.3  2002/06/01 07:12:34  relnev
46  * a few NDEBUG updates.
47  *
48  * removed a few warnings.
49  *
50  * Revision 1.2  2002/05/03 13:34:33  theoddone33
51  * More stuff compiles
52  *
53  * Revision 1.1.1.1  2002/05/03 03:28:10  root
54  * Initial import.
55  *
56  * 
57  * 107   9/15/99 4:42a Mikek
58  * Make any big ship attacking Colossus, or Colossus attacking any large
59  * ship not use big cruiser movement code.
60  * 
61  * 106   9/15/99 3:28a Jimb
62  * Make all big ships in sm3-08 not do cruiser chase code when attacking
63  * Colossus.  Added so Beast doesn't swerve away from Colossus.
64  * 
65  * 105   9/14/99 4:18p Andsager
66  * hack for mission sm3-08 to abort cruiser_chase as sathanas is about to
67  * begin circling colossus.
68  * 
69  * 104   9/08/99 10:44p Andsager
70  * Make HUGE ships not die when warping out, after warp effect started.
71  * 
72  * 103   9/03/99 11:40p Mikek
73  * Comment out an annoying nprintf().
74  * 
75  * 102   9/01/99 11:26p Dave
76  * Fixed release build warnings.
77  * 
78  * 101   9/01/99 9:12p Mikek
79  * Make it a boatload harder to become a traitor from hitting a large
80  * ship.
81  * 
82  * 100   9/01/99 4:01p Andsager
83  * Make sure BIG|HUGE ships do not respond to shockwaves
84  * 
85  * 99    9/01/99 10:09a Dave
86  * Pirate bob.
87  * 
88  * 98    8/31/99 4:24p Andsager
89  * Reduce collisions when attacking big ships.
90  * 
91  * 97    8/31/99 7:33a Mikek
92  * Improvements in formation flying, less silly behavior, especially when
93  * leader is moving very slowly.
94  * 
95  * 96    8/31/99 5:48a Mikek
96  * Making ships not overshoot so much in formation flying.  Intermediate
97  * checkin.
98  * 
99  * 95    8/30/99 12:03a Mikek
100  * Make guard behavior much less annoying.  Guarders don't get quite so
101  * close and they try to avoid striking the target they are guarding.
102  * 
103  * 94    8/29/99 4:18p Andsager
104  * New "burst" limit for friendly damage.  Also credit more damage done
105  * against large friendly ships.
106  * 
107  * 93    8/28/99 7:29p Dave
108  * Fixed wingmen persona messaging. Make sure locked turrets don't count
109  * towards the # attacking a player.
110  * 
111  * 92    8/26/99 10:46p Andsager
112  * Apply shockwave damage to lethality.
113  * 
114  * 91    8/26/99 8:52p Dave
115  * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
116  * 
117  * 90    8/26/99 5:14p Andsager
118  * 
119  * 89    8/24/99 8:55p Dave
120  * Make sure nondimming pixels work properly in tech menu.
121  * 
122  * 88    8/23/99 6:21p Jefff
123  * added "no traitor" option to missions (and fred)
124  * 
125  * 87    8/20/99 3:36p Andsager
126  * Make sure we don;t miss stealth sweep points.
127  * 
128  * 86    8/16/99 8:21a Andsager
129  * fix link error
130  * 
131  * 85    8/16/99 8:19a Andsager
132  * Add project_point_onto_bbox() to fvi and include in aicode
133  * 
134  * 84    8/15/99 1:30p Dave
135  * Removed some bounding box code because of link errors. Assuming needed
136  * function just needs to get checked in by DaveA.
137  * 
138  * 83    8/15/99 11:59a Andsager
139  * For targing big/huge ships, find nearest distance to bbox, not center.
140  * 
141  * 82    8/13/99 2:20p Andsager
142  * Add speed modification to chances turret will find stealth ship
143  * 
144  * 81    8/13/99 10:49a Andsager
145  * Knossos and HUGE ship warp out.  HUGE ship warp in.  Stealth search
146  * modes dont collide big ships.
147  * 
148  * 80    8/10/99 5:02p Andsager
149  * Fix bug where AI gets stuck in SM_EVADE_WEAPON with no target.
150  * 
151  * 79    8/10/99 11:58a Andsager
152  * Allow turrets to sometimes see stealth.
153  * 
154  * 78    7/31/99 2:57p Dave
155  * Scaled flak aim and jitter by weapon subsystem strength.
156  * 
157  * 77    7/27/99 10:33p Andsager
158  * improve ai for attacking stealth.  reduced jitter in aim.  reduced
159  * error in position when avoiding.  skill level support for attacking
160  * stealth.  Made target error same for team vs. team.
161  * 
162  * 76    7/27/99 10:49a Andsager
163  * Make turret fire rate independent of team for HUGE turrets, and also
164  * for mult team vs. team.
165  * 
166  * 75    7/26/99 12:14p Andsager
167  * Apply cap to how much slower a transport flies with cargo.  Remove
168  * limit on waypoint speed for training.  Enemy ai get stealth exact pos
169  * when stealth fires
170  * 
171  * 74    7/20/99 1:49p Dave
172  * Peter Drake build. Fixed some release build warnings.
173  * 
174  * 73    7/19/99 2:13p Dave
175  * Added some new strings for Heiko.
176  * 
177  * 72    7/19/99 12:02p Andsager
178  * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
179  * only blow up subsystem if its strength is > 0
180  * 
181  * 71    7/15/99 9:20a Andsager
182  * FS2_DEMO initial checkin
183  * 
184  * 70    7/14/99 1:44p Andsager
185  * modify ai_guard for BIG ships to circle around the long axis
186  * 
187  * 69    7/09/99 5:54p Dave
188  * Seperated cruiser types into individual types. Added tons of new
189  * briefing icons. Campaign screen.
190  * 
191  * 68    7/08/99 4:32p Andsager
192  * fix bug with turret-tagged-only
193  * 
194  * 67    7/08/99 12:06p Andsager
195  * Add turret-tagged-only and turret-tagged-clear sexp.
196  * 
197  * 66    7/02/99 3:49p Andsager
198  * Remove debug code.  Allow targeting of stealth from any weapon it
199  * fires.
200  * 
201  * 65    7/02/99 2:01p Andsager
202  * Fix bug where big ship tries to evade dumpfire weapon.
203  * 
204  * 64    7/02/99 10:58a Andsager
205  * Put in big ship - big ship attack mode.  Modify stealth sweep ai.
206  * 
207  * 63    6/30/99 5:53p Dave
208  * Put in new anti-camper code.
209  * 
210  * 62    6/28/99 3:22p Anoop
211  * Fix turret optimization, where ship may not have any valid subsystems
212  * (all blown off).
213  * 
214  * 61    6/25/99 5:56p Andsager
215  * First real pass on stealth ai.
216  * 
217  * 60    6/25/99 3:08p Dave
218  * Multiple flyby sounds.
219  * 
220  * 59    6/25/99 1:12p Danw
221  * DKA:  Make sure big ship has subsystems before trying to target them.
222  * 
223  * 58    6/25/99 10:56a Johnson
224  * Fixed dumb ai code.
225  * 
226  * 57    6/24/99 5:15p Dave
227  * Make sure stride is always at least one for checking turret subsystem
228  * targets.
229  * 
230  * 56    6/24/99 4:59p Dave
231  * Significant speedups to turret firing.
232  * 
233  * 55    6/23/99 5:51p Andsager
234  * Add waypoint-cap-speed.  Checkin stealth ai - inactive.
235  * 
236  * 54    6/16/99 10:21a Dave
237  * Added send-message-list sexpression.
238  * 
239  * 53    6/15/99 9:25a Andsager
240  * Make guard and dynamic chase (who hit you) work with stealth
241  * 
242  * 52    6/14/99 3:21p Andsager
243  * Allow collisions between ship and its debris.  Fix up collision pairs
244  * when large ship is warping out.
245  * 
246  * 51    6/14/99 10:45a Dave
247  * Made beam weapons specify accuracy by skill level in the weapons.tbl
248  * 
249  * 50    6/03/99 8:11a Andsager
250  * 
251  * 49    6/02/99 5:41p Andsager
252  * Reduce range of secondary weapons not fired from turrets in nebula.
253  * Reduce range of beams fired from turrrets in nebula
254  * 
255  * 48    6/02/99 3:23p Andsager
256  * Make AI aware of team visibility.  Allow player targeting with team
257  * visibility info.  Make stealth ships not targetable by AI in nebula
258  * unless tagged.
259  * 
260  * 47    6/02/99 12:52p Andsager
261  * Added team-wide ship visibility.  Implemented for player.
262  * 
263  * 46    6/01/99 8:35p Dave
264  * Finished lockarm weapons. Added proper supercap weapons/damage. Added
265  * awacs-set-radius sexpression.
266  * 
267  * 45    5/28/99 5:35p Andsager
268  * Make ai nebula aware
269  * 
270  * 44    5/24/99 9:55a Dave
271  * Fixed stream weapon ai firing problem. ick.
272  * 
273  * 43    5/20/99 7:00p Dave
274  * Added alternate type names for ships. Changed swarm missile table
275  * entries.
276  * 
277  * 42    5/18/99 1:30p Dave
278  * Added muzzle flash table stuff.
279  * 
280  * 41    5/12/99 2:55p Andsager
281  * Implemented level 2 tag as priority in turret object selection
282  * 
283  * 40    5/12/99 10:42a Andsager
284  * Fix turret bug allowing HUGE turrets to fire at fighters
285  * 
286  * 39    5/06/99 11:46a Andsager
287  * Bug fixes.  Don't get into illegal strafe submode.  Don't choose turret
288  * enemy objnum for beam protected.
289  * 
290  * 38    5/03/99 10:50p Andsager
291  * Make Asteroid_obj_list.  Change get_nearest_turret_objnum() to use
292  * Asteroid_obj_list, Ship_obj_list and Missile_obj_list vs.
293  * obj_used_list.
294  * 
295  * 37    4/29/99 2:29p Dave
296  * Made flak work much better in multiplayer.
297  * 
298  * 36    4/28/99 11:36p Dave
299  * Tweaked up subspace missile strike a bit,
300  * 
301  * 35    4/28/99 3:11p Andsager
302  * Stagger turret weapon fire times.  Make turrets smarter when target is
303  * protected or beam protected.  Add weaopn range to weapon info struct.
304  * 
305  * 34    4/26/99 10:58a Andsager
306  * Add OF_BEAM_PROTECTED flag to keep object from being targeted for zing.
307  * 
308  * 33    4/23/99 12:12p Andsager
309  * Modify wing positions when player is wing leader to prevent some
310  * collisions.
311  * 
312  * 32    4/23/99 12:01p Johnson
313  * Added SIF_HUGE_SHIP
314  * 
315  * 31    4/22/99 11:06p Dave
316  * Final pass at beam weapons. Solidified a lot of stuff. All that remains
317  * now is to tweak and fix bugs as they come up. No new beam weapon
318  * features.
319  * 
320  * 30    4/20/99 6:39p Dave
321  * Almost done with artillery targeting. Added support for downloading
322  * images on the PXO screen.
323  * 
324  * 29    4/20/99 3:40p Andsager
325  * Changes to big ship ai.  Uses bounding box as limit where to fly to
326  * when flying away.
327  * 
328  * 28    4/16/99 5:54p Dave
329  * Support for on/off style "stream" weapons. Real early support for
330  * target-painting lasers.
331  * 
332  * 27    4/02/99 9:55a Dave
333  * Added a few more options in the weapons.tbl for beam weapons. Attempt
334  * at putting "pain" packets into multiplayer.
335  * 
336  * 26    3/28/99 5:58p Dave
337  * Added early demo code. Make objects move. Nice and framerate
338  * independant, but not much else. Don't use yet unless you're me :)
339  * 
340  * 25    3/19/99 9:51a Dave
341  * Checkin to repair massive source safe crash. Also added support for
342  * pof-style nebulae, and some new weapons code.
343  * 
344  * 24    3/08/99 7:03p Dave
345  * First run of new object update system. Looks very promising.
346  * 
347  * 23    3/05/99 3:55p Anoop
348  * Handle some asserts properly.
349  * 
350  * 22    3/04/99 6:09p Dave
351  * Added in sexpressions for firing beams and checking for if a ship is
352  * tagged.
353  * 
354  * 21    3/02/99 9:25p Dave
355  * Added a bunch of model rendering debug code. Started work on fixing
356  * beam weapon wacky firing.
357  * 
358  * 20    2/25/99 2:32p Anoop
359  * (Alan). Fixed ai path following code for AI_BAY_EMERGE. Put in sanity
360  * check so that when the last point on the path is reached, it finishes.
361  * 
362  * 19    2/19/99 2:11p Anoop
363  * Put in some nice handling code for wacky support ship problems (like no
364  * docking paths)
365  * 
366  * 18    2/17/99 2:11p Dave
367  * First full run of squad war. All freespace and tracker side stuff
368  * works.
369  * 
370  * 17    2/11/99 5:22p Andsager
371  * Fixed bugs, generalized block Sexp_variables
372  * 
373  * 16    1/29/99 5:07p Dave
374  * Fixed multiplayer stuff. Put in multiplayer support for rapid fire
375  * missiles.
376  * 
377  * 15    1/29/99 2:25p Andsager
378  * Added turret_swarm_missiles
379  * 
380  * 14    1/27/99 9:56a Dave
381  * Temporary checkin of beam weapons for Dan to make cool sounds.
382  * 
383  * 13    1/24/99 11:37p Dave
384  * First full rev of beam weapons. Very customizable. Removed some bogus
385  * Int3()'s in low level net code.
386  * 
387  * 12    1/21/99 10:44a Dave
388  * More beam weapon stuff. Put in warmdown time.
389  * 
390  * 11    1/12/99 5:45p Dave
391  * Moved weapon pipeline in multiplayer to almost exclusively client side.
392  * Very good results. Bandwidth goes down, playability goes up for crappy
393  * connections. Fixed object update problem for ship subsystems.
394  * 
395  * 10    1/08/99 2:08p Dave
396  * Fixed software rendering for pofview. Super early support for AWACS and
397  * beam weapons.
398  * 
399  * 9     12/23/98 2:53p Andsager
400  * Added ship activation and gas collection subsystems, removed bridge
401  * 
402  * 8     11/12/98 12:13a Dave
403  * Tidied code up for multiplayer test. Put in network support for flak
404  * guns.
405  * 
406  * 7     11/05/98 5:55p Dave
407  * Big pass at reducing #includes
408  * 
409  * 6     10/26/98 9:42a Dave
410  * Early flak gun support.
411  * 
412  * 5     10/23/98 3:51p Dave
413  * Full support for tstrings.tbl and foreign languages. All that remains
414  * is to make it active in Fred.
415  * 
416  * 4     10/20/98 1:39p Andsager
417  * Make so sparks follow animated ship submodels.  Modify
418  * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
419  * submodel_num.  Add submodel_num to multiplayer hit packet.
420  * 
421  * 3     10/13/98 9:29a Dave
422  * Started neatening up freespace.h. Many variables renamed and
423  * reorganized. Added AlphaColors.[h,cpp]
424  * 
425  * 2     10/07/98 10:53a Dave
426  * Initial checkin.
427  * 
428  * 1     10/07/98 10:51a Dave
429  * 
430  * 
431  * $NoKeywords: $
432  */
433
434 // This module contains the actual AI code that does interesting stuff
435 // to objects.   The code in Ai.cpp is just for bookeeping, allocating
436 // ai slots and linking them to ships.
437
438 #include "pstypes.h"
439 #include "fix.h"
440 #include "linklist.h"
441 #include "object.h"
442 #include "physics.h"
443 #include "vecmat.h"
444 #include "ship.h"
445 #include "model.h"
446 #include "2d.h"
447 #include "3d.h"
448 #include "ai.h"
449 #include "floating.h"
450 #include "player.h"
451 #include "freespace.h"
452 #include "weapon.h"
453 #include "missiongoals.h"
454 #include "missionlog.h"
455 #include "timer.h"
456 #include "sound.h"
457 #include "aigoals.h"
458 #include "gamesnd.h"
459 #include "hudmessage.h"
460 #include "missionmessage.h"
461 #include "cmeasure.h"
462 #include "staticrand.h"
463 #include "multimsgs.h"
464 #include "afterburner.h"
465 #include "hudets.h"
466 #include "shipfx.h"
467 #include "shiphit.h"
468 #include "aibig.h"
469 #include "multiutil.h"
470 #include "hud.h"
471 #include "objcollide.h"
472 #include "asteroid.h"
473 #include "hudlock.h"
474 #include "missiontraining.h"
475 #include "gamesequence.h"
476 #include "joy_ff.h"
477 #include "localize.h"
478 #include "flak.h"
479 #include "beam.h"
480 #include "multi.h"
481 #include "swarm.h"
482 #include "multi_team.h"
483 #include "awacs.h"
484 #include "fvi.h"
485
486 #ifndef PLAT_UNIX
487 #pragma optimize("", off)
488 #pragma auto_inline(off)
489 #endif
490
491 #define UNINITIALIZED_VALUE     -99999.9f
492
493 #define INSTRUCTOR_SHIP_NAME NOX("instructor")
494
495 #define AICODE_SMALL_MAGNITUDE  0.001f          // cosider a vector NULL if mag is less than this
496
497 #define NEXT_REARM_TIMESTAMP (60*1000)                  //      Ships will re-request rearm, typically, after this long.
498
499 #define BEAM_NEBULA_RANGE_REDUCE_FACTOR         0.8
500
501 // AIM_CHASE submode defines
502 // SM_STEALTH_FIND
503 #define SM_SF_AHEAD             0
504 #define SM_SF_BEHIND    1
505 #define SM_SF_BAIL              2
506
507 // SM_STEALTH_SWEEP
508 #define SM_SS_SET_GOAL  -1
509 #define SM_SS_BOX0              0
510 #define SM_SS_LR                        1
511 #define SM_SS_UL                        2
512 #define SM_SS_BOX1              3
513 #define SM_SS_UR                        4
514 #define SM_SS_LL                        5
515 #define SM_SS_BOX2              6
516 #define SM_SS_DONE              7
517
518 //XSTR:OFF
519
520 char *Mode_text[MAX_AI_BEHAVIORS] = {
521         "CHASE",
522         "EVADE",
523         "GET_BEHIND",
524         "CHASE_LONG",
525         "SQUIGGLE",
526         "GUARD",
527         "AVOID",
528         "WAYPOINTS",
529         "DOCK",
530         "NONE",
531         "BIGSHIP",
532         "PATH",
533         "BE_REARMED",
534         "SAFETY",
535         "EV_WEAPON",
536         "STRAFE",
537         "PLAY_DEAD",
538         "BAY_EMERGE",
539         "BAY_DEPART",
540         "SENTRYGUN",
541         "WARP_OUT",
542 };
543
544 //      Submode text is only valid for CHASE mode.
545 char *Submode_text[] = {
546 "undefined",
547 "CONT_TURN",
548 "ATTACK   ",
549 "E_SQUIG  ",
550 "E_BRAKE  ",
551 "EVADE    ",
552 "SUP_ATTAK",
553 "AVOID    ",
554 "BEHIND   ",
555 "GET_AWAY ",
556 "E_WEAPON ",
557 "FLY_AWAY ",
558 "ATK_4EVER",
559 "STLTH_FND",
560 "STLTH_SWP",
561 "BIG_APPR",
562 "BIG_CIRC",
563 "BIG_PARL"
564 };
565
566 char *Strafe_submode_text[5] = {
567 "ATTACK",
568 "AVOID",
569 "RETREAT1",
570 "RETREAT2",
571 "POSITION"
572 };
573 //XSTR:ON
574
575 /*
576 //      Used for global ignore of objects.  If an object appears in the Ignore_objects array,
577 //      no one will attack it.
578 #define MAX_IGNORE_OBJECTS      16
579 typedef struct {
580         int     objnum;
581         int     signature;
582 } ignore_object;
583
584 ignore_object   Ignore_objects[MAX_IGNORE_OBJECTS];
585 */
586
587 typedef struct eval_enemy_obj_struct {
588         int                     turret_parent_objnum;                   // parent of turret
589         float                   weapon_travel_dist;                             // max targeting range of turret weapon
590         int                     enemy_team_mask;
591         int                     weapon_system_ok;                                       // is the weapon subsystem of turret ship ok
592         int                     big_only_flag;                                          // turret fires only at big and huge ships
593         vector          *tpos;
594         vector          *tvec;
595         ship_subsys *turret_subsys;
596         int                     current_enemy;
597
598
599         float                   nearest_attacker_dist;                  // nearest ship 
600         int                     nearest_attacker_objnum;
601
602         float                   nearest_homing_bomb_dist;               // nearest homing bomb
603         int                     nearest_homing_bomb_objnum;
604
605         float                   nearest_bomb_dist;                              // nearest non-homing bomb
606         int                     nearest_bomb_objnum;
607
608         float                   nearest_dist;                                           // nearest ship attacking this turret
609         int                     nearest_objnum;
610 }       eval_enemy_obj_struct;
611
612
613 control_info    AI_ci;
614
615 object *Pl_objp;
616 object *En_objp;
617
618 waypoint_list Waypoint_lists[MAX_WAYPOINT_LISTS];
619
620 // How close a turret has to be point at its target before it
621 // can fire.  If the dot of the gun normal and the vector from gun
622 // to target is greater than this, the turret fires.  The smaller
623 // the sloppier the shooting.
624 #define AICODE_TURRET_DUMBFIRE_ANGLE            (0.8f)  
625 #define AICODE_TURRET_HEATSEEK_ANGLE            (0.7f)  
626 #define AICODE_TURRET_MAX_TIME_IN_RANGE (5.0f)
627
628 #define REARM_SOUND_DELAY               (3*F1_0)                //      Amount of time to delay rearm/repair after mode start
629 #define REARM_BREAKOFF_DELAY    (3*F1_0)                //      Amount of time to wait after fully rearmed to breakoff.
630
631 #define MIN_DIST_TO_WAYPOINT_GOAL       5.0f
632 #define MAX_GUARD_DIST                                  250.0f
633 #define BIG_GUARD_RADIUS                                500.0f
634
635 #define MAX_EVADE_TIME                  (15 * 1000)     //      Max time to evade a weapon.
636
637 // defines for repair ship stuff.
638 #define MAX_REPAIR_SPEED                        25.0f
639 #define MAX_UNDOCK_ABORT_SPEED  2.0f
640
641 // defines for EMP effect stuff
642 #define MAX_EMP_INACCURACY              50.0f
643
644 // defines for stealth
645 #define MAX_STEALTH_INACCURACY  50.0f           // at max view dist
646 #define STEALTH_MAX_VIEW_DIST   400             // dist at which 1) stealth no longer visible 2) firing inaccuracy is greatest
647 #define STEALTH_VIEW_CONE_DOT   0.707           // (half angle of 45 degrees)
648
649
650 ai_class        Ai_classes[MAX_AI_CLASSES];
651 int     Ai_firing_enabled = 1;
652 int     Num_ai_classes;
653
654 int     AI_FrameCount = 0;
655 int     Ship_info_inited = 0;
656 int     AI_watch_object = 0; // Debugging, object to spew debug info for.
657 int     Num_waypoint_lists = 0;
658 int     Mission_all_attack = 0;                                 //      !0 means all teams attack all teams.
659
660 char *Skill_level_names(int level, int translate)
661 {
662         char *str = NULL;
663
664         #if NUM_SKILL_LEVELS != 5
665         #error Number of skill levels is wrong!
666         #endif
667
668         if(translate){
669                 switch( level ) {
670                 case 0:
671                         str = XSTR("Very Easy", 469);
672                         break;
673                 case 1:
674                         str = XSTR("Easy", 470);
675                         break;
676                 case 2:
677                         str = XSTR("Medium", 471);
678                         break;
679                 case 3:
680                         str = XSTR("Hard", 472);
681                         break;
682                 case 4:
683                         str = XSTR("Insane", 473);
684                         break;
685                 default:        
686                         Int3();
687                 }
688         } else {
689                 switch( level ) {
690                 case 0:
691                         str = NOX("Very Easy");
692                         break;
693                 case 1:
694                         str = NOX("Easy");
695                         break;
696                 case 2:
697                         str = NOX("Medium");
698                         break;
699                 case 3:
700                         str = NOX("Hard");
701                         break;
702                 case 4:
703                         str = NOX("Insane");
704                         break;
705                 default:        
706                         Int3();
707                 }
708         }
709
710         return str;
711 }
712
713 #define DELAY_TARGET_TIME       (12*1000)               //      time in milliseconds until a ship can target a new enemy after an order.
714
715 //      Make enemy ships turn more slowly at lower skill levels.
716 float   Turn_time_skill_level_scale[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.6f, 1.3f, 1.0f};
717
718 //      Maximum number of simultaneous homing weapons on player based on skill level.
719 int     Max_allowed_player_homers[NUM_SKILL_LEVELS] = {2, 3, 4, 7, 99};
720
721 //      Number of ships that can attack another ship at a given skill level.
722 int     Skill_level_max_attackers[NUM_SKILL_LEVELS] = {2, 3, 4, 5, 99};
723
724 //      How long until next predict position.
725 fix Skill_level_delay[NUM_SKILL_LEVELS] = {2*F1_0, 3*F1_0/2, 4*F1_0/3, F1_0/2, 0};
726
727 //      AI ships link primary weapons if energy levels greater than the following amounts:
728 float   Link_energy_levels_always[NUM_SKILL_LEVELS] = {100.0f, 80.0f, 60.0f, 40.0f, 20.0f};     //      always link
729 float   Link_energy_levels_maybe[NUM_SKILL_LEVELS] = {90.0f, 60.0f, 40.0f, 20.0f, 10.0f};       //      link if hull strength low
730
731 //      Seconds to add to time it takes to get enemy in range.  Only for player's enemies.
732 float   In_range_time[NUM_SKILL_LEVELS] = {2.0f, 1.4f, 0.75f, 0.0f, -1.0f};
733
734 //      No matter what, a random unit vector gets scaled by this amount in firing at an enemy.
735 //      Note that for shorter in-range times, these values get scaled, so a value of 0.5f is meaningful.
736 float   Aiming_error[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.3f, 0.7f, 0.2f};
737
738 //      Chance a countermeasure will be fired based on skill level.
739 float Cmeasure_fire_chance[NUM_SKILL_LEVELS] = {0.2f, 0.3f, 0.5f, 0.9f, 1.1f};  //      Note, this gets scaled by ai_class
740
741 float Shield_manage_delays[NUM_SKILL_LEVELS] = {5.0f, 4.0f, 2.5f, 1.2f, 0.1f};
742
743 // accuracy we feed into the beam weapons based upon skill system
744 // float Beam_accuracy[NUM_SKILL_LEVELS] = {2.0f, 1.5f, 1.0f, 0.7f, 0.4f};
745
746 extern float Ship_fire_delay_scale_hostile[NUM_SKILL_LEVELS];
747 extern float Ship_fire_delay_scale_friendly[NUM_SKILL_LEVELS];
748
749 pnode           Path_points[MAX_PATH_POINTS];
750 pnode           *Ppfp;                  //      Free pointer in path points.
751
752 float   AI_frametime;
753
754 char *Ai_class_names[MAX_AI_CLASSES];
755
756 // global for rearm status for teams
757 int Ai_friendly_rearm_timestamp, Ai_hostile_rearm_timestamp, Ai_neutral_rearm_timestamp, Ai_traitor_rearm_timestamp, Ai_unknown_rearm_timestamp;
758
759 // globals for dealing with when to fire huge secondary weapons
760 #define MAX_HUGE_SECONDARY_INFO 10
761
762 typedef struct {
763         int team;
764         int weapon_index;
765         int max_fire_count;
766         char    *shipname;
767 } huge_fire_info;
768
769 huge_fire_info Ai_huge_fire_info[MAX_HUGE_SECONDARY_INFO];
770
771 int Ai_last_arrive_path;        // index of ship_bay path used by last arrival from a fighter bay
772
773 // forward declarations
774 int     ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index);
775 void    create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count=1);
776 void    copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt=-1);
777
778 // ai_set_rearm_status takes a team (friendly, hostile, neutral) and a time.  This function
779 // sets the timestamp used to tell is it is a good time for this team to rearm.  Once the timestamp
780 // is no longer valid, then rearming is not a "good time"
781 // not safe.  Called from sexpression code.
782 void ai_set_rearm_status( int team, int time )
783 {
784         Assert( time >= 0 );
785
786         switch (team) {
787         case TEAM_FRIENDLY:
788                 Ai_friendly_rearm_timestamp = timestamp( time * 1000 );
789                 break;
790         case TEAM_HOSTILE:
791                 Ai_hostile_rearm_timestamp = timestamp( time * 1000 );
792                 break;
793         case TEAM_NEUTRAL:
794                 Ai_neutral_rearm_timestamp = timestamp( time * 1000 );
795                 break;
796         case TEAM_TRAITOR:
797                 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
798                 break;
799         case TEAM_UNKNOWN:
800                 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
801                 break;
802         default:
803                 Int3();
804                 break;
805         }
806 }
807
808 // int ai_good_time_to_rearm returns true(1) or false(0) if it is "safe" for the given
809 // object to rearm.  "safe" is currently defined by the mission designer using the good/bad
810 // time to rearm sexpressions.  This status is currently team based.  This function could
811 // be easily expended to further the definition of "safe"
812 int ai_good_time_to_rearm( object *objp )
813 {
814         int team, status;
815
816         Assert(objp->type == OBJ_SHIP);
817         team = Ships[objp->instance].team;
818         status = 0;
819
820         switch(team) {
821         case TEAM_FRIENDLY:
822                 status = timestamp_valid(Ai_friendly_rearm_timestamp);
823                 break;
824         case TEAM_HOSTILE:
825                 status = timestamp_valid(Ai_hostile_rearm_timestamp);
826                 break;
827         case TEAM_NEUTRAL:
828                 status = timestamp_valid(Ai_neutral_rearm_timestamp);
829                 break;
830         case TEAM_TRAITOR:
831                 status = timestamp_valid(Ai_traitor_rearm_timestamp);
832                 break;
833         case TEAM_UNKNOWN:
834                 status = timestamp_valid(Ai_unknown_rearm_timestamp);
835                 break;
836         default:
837                 Int3();
838                 break;
839         }
840
841         return status;
842 }
843
844 // functions to deal with letting the ai know about good times to fire powerful secondary
845 // weapons.
846
847 // this function is entry point from sexpression code to set internal data for use by ai code.
848 void ai_good_secondary_time( int team, int weapon_index, int max_fire_count, char *shipname )
849 {
850         int i, index;
851
852         // find an open slot to put this data
853         for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
854                 if ( Ai_huge_fire_info[i].weapon_index == -1 )
855                         break;
856         }
857
858         Assert( i < MAX_HUGE_SECONDARY_INFO );                  // we've run out of room
859
860         Ai_huge_fire_info[i].weapon_index = weapon_index;
861         Ai_huge_fire_info[i].team = team;
862         Ai_huge_fire_info[i].max_fire_count = max_fire_count;
863
864         Ai_huge_fire_info[i].shipname = ai_get_goal_ship_name( shipname, &index );
865 }
866
867 // function called internally to the ai code to tell whether or not weapon_num can be fired
868 // from firer_objp at target_objp.  This function will resolve the team for the firer.
869 // returns:
870 //              -1  -- when conditions don't allow firer to fire weapon_num on target_objp
871 //              >=0 -- when conditions allow firer to fire.  Return value is max number of weapon_nums
872 //           which can be fired on target_objp
873 int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
874 {
875         int i, firer_team, target_signature;
876         ship *firer_ship;
877         huge_fire_info *hfi = NULL;
878
879         Assert( firer_objp->type == OBJ_SHIP );
880         firer_ship = &Ships[firer_objp->instance];
881         firer_team = firer_ship->team;
882
883         // get target object's signature and try to find it in the list.
884         target_signature = target_objp->signature;
885         for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
886                 int ship_index, signature;
887
888                 hfi = &Ai_huge_fire_info[i];
889                 if ( hfi->weapon_index == -1 )
890                         continue;
891
892                 ship_index = ship_name_lookup( hfi->shipname );
893                 if ( ship_index == -1 )
894                         continue;
895
896                 signature = Objects[Ships[ship_index].objnum].signature;
897
898                 // sigatures, weapon_index, and team must match
899                 if ( (signature == target_signature) && (hfi->weapon_index == weapon_num) && (hfi->team == firer_team) )
900                         break;
901         }
902
903         // return -1 if not found
904         if ( i == MAX_HUGE_SECONDARY_INFO )
905                 return -1;
906
907         // otherwise, we can return the max number of weapons we can fire against target_objps
908
909         return hfi->max_fire_count;
910 }
911
912 // function to clear out secondary firing infomration between levels
913 void ai_init_secondary_info()
914 {
915         int i;
916
917         // clear out the data for dealing with when ai ships can fire huge secondary weapons
918         for (i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
919                 Ai_huge_fire_info[i].weapon_index = -1;
920                 Ai_huge_fire_info[i].team = -1;
921                 Ai_huge_fire_info[i].max_fire_count = -1;
922                 Ai_huge_fire_info[i].shipname = NULL;
923         }
924 }
925
926
927 //      Garbage collect the Path_points buffer.
928 //      Scans all objects, looking for used Path_points records.
929 //      Compresses Path_points buffer, updating aip->path_start and aip->path_cur indices.
930 //      Updates Ppfp to point to first free record.
931 //      This function is fairly fast.  Its worst-case running time is proportional to
932 //      3*MAX_PATH_POINTS + MAX_OBJECTS
933 //      Things to do to optimize this function:
934 //              1. if (t != 0) xlt++; can be replaced by xlt += t; assuming t can only be 0 or 1.
935 //              2. When pp_xlate is getting stuffed the first time, note highest index and use that 
936 //                      instead of MAX_PATH_POINTS in following two for loops.
937 void garbage_collect_path_points()
938 {
939         int     i;
940         int     pp_xlate[MAX_PATH_POINTS];
941         object  *A;
942         ship_obj        *so;
943
944         //      Scan all objects and create Path_points xlate table.
945         for (i=0; i<MAX_PATH_POINTS; i++)
946                 pp_xlate[i] = 0;
947
948         //      in pp_xlate, mark all used Path_point records
949         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
950                 A = &Objects[so->objnum];
951                 ship    *shipp = &Ships[A->instance];
952                 if (shipp->ai_index != -1) {
953                         ai_info *aip = &Ai_info[shipp->ai_index];
954
955                         if ((aip->path_length > 0) && (aip->path_start > -1)) {
956
957                                 for (int i=aip->path_start; i<aip->path_start + aip->path_length; i++) {
958                                         Assert(pp_xlate[i] == 0);       //      If this is not 0, then two paths use this point!
959                                         pp_xlate[i] = 1;
960                                 }
961                         }
962                 }
963         }
964
965         //      Now, stuff xlate index in pp_xlate.  This is the number to translate any path_start
966         //      or path_cur index to.
967         int     xlt = 0;
968         for (i=0; i<MAX_PATH_POINTS; i++) {
969                 int     t = pp_xlate[i];
970
971                 pp_xlate[i] = xlt;
972                 if (t != 0)
973                         xlt++;
974         }
975         
976         //      Update global Path_points free pointer.
977         Ppfp = &Path_points[xlt];
978
979         //      Now, using pp_xlate, fixup all aip->path_cur and aip->path_start indices
980         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
981                 A = &Objects[so->objnum];
982                 ship    *shipp = &Ships[A->instance];
983                 if (shipp->ai_index != -1) {
984                         ai_info *aip = &Ai_info[shipp->ai_index];
985
986                         if ((aip->path_length > 0) && (aip->path_start > -1)) {
987                                 Assert(aip->path_start < MAX_PATH_POINTS);
988                                 aip->path_start = pp_xlate[aip->path_start];
989
990                                 Assert((aip->path_cur >= 0) && (aip->path_cur < MAX_PATH_POINTS));
991                                 aip->path_cur = pp_xlate[aip->path_cur];
992                         }
993                 }
994         }
995
996         //      Now, compress the buffer.
997         for (i=0; i<MAX_PATH_POINTS; i++)
998                 if (i != pp_xlate[i])
999                         Path_points[pp_xlate[i]] = Path_points[i];
1000
1001 }
1002
1003 //      Hash two values together, return result.
1004 //      Hash function: curval shifted right circular by one, newval xored in.
1005 int hash(unsigned int curval, int newval)
1006 {
1007         int     addval = curval & 1;
1008
1009         curval >>= 1;
1010         if (addval)
1011                 curval |= 0x80000000;
1012         curval ^= newval;
1013
1014         return curval;
1015 }
1016
1017 //      Hash some information in an object together.
1018 //      On 2/20/97, the information is position and orientation.
1019 int create_object_hash(object *objp)
1020 {
1021         int     *ip;
1022         unsigned int    hashval = 0;
1023         int     i;
1024
1025         ip = (int *) &objp->orient;
1026
1027         for (i=0; i<9; i++) {
1028                 hashval = hash(hashval, *ip);
1029                 ip++;
1030         }
1031
1032         ip = (int *) &objp->pos;
1033
1034         for (i=0; i<3; i++) {
1035                 hashval = hash(hashval, *ip);
1036                 ip++;
1037         }
1038
1039         return hashval;
1040 }
1041
1042 //      Stuff a list of NUM_SKILL_LEVELS floats at *plist.
1043 void parse_float_list(float *plist)
1044 {
1045         int     i;
1046
1047         for (i=0; i<NUM_SKILL_LEVELS; i++) {
1048                 stuff_float(&plist[i]);
1049         }
1050 }
1051
1052 void parse_ai_class()
1053 {
1054         ai_class        *aicp = &Ai_classes[Num_ai_classes];
1055
1056         required_string("$Name:");
1057         stuff_string(aicp->name, F_NAME, NULL);
1058
1059         Ai_class_names[Num_ai_classes] = aicp->name;
1060
1061         required_string("$accuracy:");
1062         parse_float_list(aicp->ai_accuracy);
1063
1064         required_string("$evasion:");
1065         parse_float_list(aicp->ai_evasion);
1066
1067         required_string("$courage:");
1068         parse_float_list(aicp->ai_courage);
1069
1070         required_string("$patience:");
1071         parse_float_list(aicp->ai_patience);
1072 }
1073
1074 void parse_aitbl()
1075 {
1076         // open localization
1077         lcl_ext_open();
1078
1079         read_file_text("ai.tbl");
1080
1081         reset_parse();
1082
1083         Num_ai_classes = 0;
1084
1085         required_string("#AI Classes");
1086
1087         while (required_string_either("#End", "$Name:")) {
1088                 Assert( Num_ai_classes < MAX_AI_CLASSES);
1089
1090                 parse_ai_class();
1091
1092                 Num_ai_classes++;
1093         }
1094
1095         // close localization
1096         lcl_ext_close();
1097 }
1098
1099 LOCAL int ai_inited = 0;
1100
1101 //========================= BOOK-KEEPING FUNCTIONS =======================
1102
1103 // Called once at game start-up
1104 void ai_init()
1105 {
1106         if ( !ai_inited )       {
1107                 // Do the first time initialization stuff here
1108                 int     rval;
1109
1110                 if ((rval = setjmp(parse_abort)) != 0) {
1111                         Error(LOCATION, "Error parsing 'ai.tbl'\r\nError code = %i.\r\n", rval);
1112                 } else {                        
1113                         parse_aitbl();                  
1114                 }
1115
1116                 ai_inited = 1;
1117         }
1118
1119         init_semirand();
1120         
1121         ai_level_init();
1122 }
1123
1124 // this inits the ai.  You should be able to call this between
1125 // levels to reset everything.
1126 void ai_level_init()
1127 {
1128         int i;
1129  
1130         // Do the stuff to reset all ai stuff here
1131         for (i=0; i<MAX_AI_INFO ; i++) {
1132                 Ai_info[i].shipnum = -1;
1133         }
1134         Ai_goal_signature = 0;
1135         Ai_friendly_rearm_timestamp = timestamp(-1);
1136         Ai_hostile_rearm_timestamp = timestamp(-1);
1137         Ai_neutral_rearm_timestamp = timestamp(-1);
1138         Ai_traitor_rearm_timestamp = timestamp(-1);
1139
1140         // clear out the stuff needed for AI firing powerful secondary weapons
1141         ai_init_secondary_info();
1142
1143         Ai_last_arrive_path=0;
1144 }
1145
1146 // BEGIN STEALTH
1147 // -----------------------------------------------------------------------------
1148 // Check if object is a stealth ship
1149 int is_object_stealth_ship(object* objp)
1150 {
1151         if (objp->type == OBJ_SHIP) {
1152                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_STEALTH) {
1153                         return 1;
1154                 }
1155         }
1156
1157         // not stealth ship
1158         return 0;
1159 }
1160
1161 // -----------------------------------------------------------------------------
1162 // Init necessary ai info for new stealth target
1163 void init_ai_stealth_info(ai_info *aip, object *stealth_objp)
1164 {
1165         Assert(is_object_stealth_ship(stealth_objp));
1166
1167         // set necessary ai info for new stealth target
1168         aip->stealth_last_pos = stealth_objp->pos;
1169         aip->stealth_velocity = stealth_objp->phys_info.vel;
1170         aip->stealth_last_visible_stamp = timestamp();
1171 }
1172
1173 // -----------------------------------------------------------------------------
1174 // Check whether Pl_objp can see a stealth ship object
1175 #define STEALTH_INVISIBLE                       0
1176 #define STEALTH_VISIBLE                         1
1177 #define STEALTH_FULLY_TARGETABLE        2
1178
1179 float get_skill_stealth_dist_scaler()
1180 {
1181         // return dist scaler based on skill level
1182         switch (Game_skill_level) {
1183         case 0: // very easy
1184                 return 0.65f;
1185
1186         case 1: // easy
1187                 return 0.9f;
1188
1189         case 2: // medium
1190                 return 1.0f;
1191
1192         case 3: // hard
1193                 return 1.1f;
1194
1195         case 4: // insane
1196                 return 1.3f;
1197
1198         default:
1199                 Int3();
1200         }
1201
1202         return 1.0f;
1203 }
1204
1205 float get_skill_stealth_dot_scaler()
1206 {
1207         // return multiplier on dot based on skill level
1208         switch (Game_skill_level) {
1209         case 0: // very easy
1210                 return 1.3f;
1211
1212         case 1: // easy
1213                 return 1.1f;
1214
1215         case 2: // medium
1216                 return 1.0f;
1217
1218         case 3: // hard
1219                 return 0.9f;
1220
1221         case 4: // insane
1222                 return 0.7f;
1223
1224         default:
1225                 Int3();
1226         }
1227
1228         return 1.0f;
1229 }
1230
1231 int ai_is_stealth_visible(object *viewer_objp, object *stealth_objp)
1232 {
1233         ship *shipp;
1234         vector vec_to_stealth;
1235         float dot_to_stealth, dist_to_stealth, max_stealth_dist;
1236
1237         Assert(stealth_objp->type == OBJ_SHIP);
1238         shipp = &Ships[stealth_objp->instance];
1239         Assert(viewer_objp->type == OBJ_SHIP);
1240
1241         // check if stealth ship
1242         Assert(Ship_info[shipp->ship_info_index].flags & SIF_STEALTH);
1243
1244         // check if in neb and below awac level for visible
1245         if ( !ship_is_visible_by_team(stealth_objp->instance, Ships[viewer_objp->instance].team) ) {
1246                 vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &viewer_objp->pos);
1247                 dist_to_stealth = vm_vec_mag_quick(&vec_to_stealth);
1248                 dot_to_stealth = vm_vec_dotprod(&viewer_objp->orient.v.fvec, &vec_to_stealth) / dist_to_stealth;
1249
1250                 // get max dist at which stealth is visible
1251                 max_stealth_dist = get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST;
1252
1253                 // now check if within view frustrum
1254                 float needed_dot_to_stealth;
1255                 if (dist_to_stealth < 100) {
1256                         needed_dot_to_stealth = 0.0f;
1257                 } else {
1258                         needed_dot_to_stealth = get_skill_stealth_dot_scaler() * float(STEALTH_VIEW_CONE_DOT) * (dist_to_stealth / max_stealth_dist);
1259                 }
1260                 if (dot_to_stealth > needed_dot_to_stealth) {
1261                         if (dist_to_stealth < max_stealth_dist) {
1262                                 return STEALTH_VISIBLE;
1263                         }
1264                 }
1265
1266                 // not within frustrum
1267                 return STEALTH_INVISIBLE;
1268         }
1269
1270         // visible by awacs level
1271         return STEALTH_FULLY_TARGETABLE;
1272 }
1273
1274 // END STEALTH
1275
1276 //      Compute dot product of direction vector and forward vector.
1277 //      Direction vector is vector from one object to other object.
1278 //      Forward vector is the forward vector of the ship.
1279 //      If from_dot == NULL, don't fill it in.
1280 float compute_dots(object *objp, object *other_objp, float *to_dot, float *from_dot)
1281 {
1282         vector  v2o;
1283         float           dist;
1284
1285         dist = vm_vec_normalized_dir(&v2o, &other_objp->pos, &objp->pos);
1286
1287         *to_dot = vm_vec_dot(&objp->orient.v.fvec, &v2o);
1288
1289         if (from_dot != NULL)
1290                 *from_dot = - vm_vec_dot(&other_objp->orient.v.fvec, &v2o);
1291
1292         return dist;
1293 }
1294
1295 // -----------------------------------------------------------------------------
1296 // update estimated stealth info
1297 // this is a "cheat" update
1298 // error increases with time not seen, true distance away, dot to enemey
1299 // this is done only if we can not see the stealth target
1300 // need to infer its position either by weapon fire pos or last know pos
1301 void update_ai_stealth_info_with_error(ai_info *aip/*, int no_error*/)
1302 {
1303         object *ship;
1304         object *stealth_objp;
1305         /*
1306         float error_time_mult, error_dist_mult, error_dot_mult, error_mult;
1307         float pos_error, vel_error;
1308         vector error_vec, vec_to_stealth;
1309         float dist_to_stealth, dot_to_stealth;
1310         float delta_time, delta_capped;
1311         */
1312
1313         // make sure I am targeting a stealth ship
1314         Assert( is_object_stealth_ship(&Objects[aip->target_objnum]) );
1315         stealth_objp = &Objects[aip->target_objnum];
1316
1317         // my_ship
1318         ship = &Objects[Ships[aip->shipnum].objnum];
1319
1320         // if update is due to weapon fire, get exact stealth position
1321 //      if (no_error) {
1322         aip->stealth_last_pos = stealth_objp->pos;
1323         aip->stealth_velocity = stealth_objp->phys_info.vel;
1324         aip->stealth_last_visible_stamp = timestamp();
1325 //              return;
1326 //      }
1327 /*
1328         // get time since last seen
1329         delta_time = 0.001f * (timestamp() - aip->stealth_last_visible_stamp);
1330
1331         // we don't want our "cheat" guess to more off than what we would get from extrapolating from last visible
1332         // only update if stealth info is "old"
1333         if ( (delta_time) < 0.5 ) {
1334                 return;
1335         }
1336
1337         // find vec_to_stealth and dist
1338         vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &ship->pos);
1339         dist_to_stealth = vm_vec_normalize_quick(&vec_to_stealth);
1340         dot_to_stealth = vm_vec_dotprod(&vec_to_stealth, &ship->orient.v.fvec);
1341
1342         // put cap on time
1343         delta_capped = delta_time;
1344         if (delta_time > 5.0) {
1345                 delta_capped = 5.0f;
1346         }
1347
1348         // erorr_time_mult (for 0-5) -> (1-6)
1349         error_time_mult = (1.0f + delta_capped);
1350
1351         // error_dot_mult (-1 to 1) -> (1-3)
1352         error_dot_mult = (2 - dot_to_stealth);
1353
1354         // error_dist_mult (0-1000+) -> (1-4)
1355         error_dist_mult = dist_to_stealth * 4.0f * 0.001f;
1356         if (error_dist_mult < 1) {
1357                 error_dist_mult = 1.0f;
1358         } else if (error_dist_mult > 4) {
1359                 error_dist_mult = 4.0f;
1360         }
1361
1362         // multiply error out
1363         error_mult = error_time_mult * error_dot_mult * error_dist_mult;
1364
1365         float base_pos_error = 10;
1366         float base_vel_error = 2;
1367
1368         // find the position and velocity error magnitude;
1369         pos_error = base_pos_error * error_mult;
1370         vel_error = base_vel_error * error_mult;
1371
1372         // get an error that changes slowly over time
1373         static_randvec( ((int)aip ^ (Missiontime >> 18)) & 7, &error_vec);
1374         vm_vec_zero(&error_vec);
1375
1376         // update pos and vel with error
1377         vm_vec_scale_add(&aip->stealth_velocity, &stealth_objp->phys_info.vel, &error_vec, vel_error);
1378
1379         // revise last "known" position to arrive at last pos with given error
1380         vm_vec_scale_add(&aip->stealth_last_pos, &stealth_objp->pos, &error_vec, pos_error);
1381         vm_vec_scale_add2(&aip->stealth_last_pos, &aip->stealth_velocity, -(0.001f * delta_time));
1382         */
1383 }
1384
1385 //      Update danger_weapon_objnum and signature in ai_info to say this weapon is to be avoided.
1386 void ai_update_danger_weapon(int attacked_objnum, int weapon_objnum)
1387 {
1388         object  *objp, *weapon_objp;
1389         ai_info *aip;
1390         float           old_dist, new_dist;
1391         float           old_dot, new_dot;
1392         object  *old_weapon_objp = NULL;
1393
1394         if ((attacked_objnum == -1) || (weapon_objnum == -1)) {
1395                 return;
1396         }
1397
1398         objp = &Objects[attacked_objnum];
1399
1400         // AL 2-24-98: If this isn't a ship, we don't need to worry about updating weapon_objnum (ie it would be
1401         //                                      an asteroid or bomb).
1402         if ( objp->type != OBJ_SHIP ) {
1403                 return;
1404         }
1405
1406         weapon_objp = &Objects[weapon_objnum];
1407
1408         aip = &Ai_info[Ships[objp->instance].ai_index];
1409
1410         // if my taraget is a stealth ship and is not visible
1411         if (aip->target_objnum >= 0) {
1412                 if ( is_object_stealth_ship(&Objects[aip->target_objnum]) ) {
1413                         if ( ai_is_stealth_visible(objp, &Objects[aip->target_objnum]) == STEALTH_INVISIBLE ) {
1414                                 // and the weapon is coming from that stealth ship
1415                                 if (weapon_objp->parent == aip->target_objnum) {
1416                                         // update my position estimate for stealth ship
1417                                         update_ai_stealth_info_with_error(aip/*, 1*/);
1418                                 }
1419                         }
1420                 }
1421         }
1422
1423         if (aip->danger_weapon_objnum != -1) {
1424                 old_weapon_objp = &Objects[aip->danger_weapon_objnum];
1425                 if ((old_weapon_objp->type == OBJ_WEAPON) && (old_weapon_objp->signature == aip->danger_weapon_signature)) {
1426                         ;
1427                 } else {
1428                         aip->danger_weapon_objnum = -1;
1429                 }
1430         }
1431
1432         new_dist = compute_dots(weapon_objp, objp, &new_dot, NULL);
1433
1434         if (aip->danger_weapon_objnum == -1) {
1435                 if (new_dist < 1500.0f) {
1436                         if (new_dot > 0.5f) {
1437                                 aip->danger_weapon_objnum = weapon_objnum;
1438                                 aip->danger_weapon_signature = weapon_objp->signature;
1439                         }
1440                 }
1441         } else {
1442                 Assert(old_weapon_objp != NULL);
1443                 old_dist = compute_dots(old_weapon_objp, objp, &old_dot, NULL);
1444         
1445                 if (old_dot < 0.5f) {
1446                         aip->danger_weapon_objnum = -1;
1447                         old_dist = 9999.9f;
1448                 }
1449
1450                 if ((new_dot > 0.5f) && (new_dot > old_dot-0.01f)) {
1451                         if (new_dist < old_dist) {
1452                                 aip->danger_weapon_objnum = weapon_objnum;
1453                                 aip->danger_weapon_signature = weapon_objp->signature;
1454                         }
1455                 }
1456         }
1457 }
1458
1459 //      If rvec != NULL, use it to match bank by calling vm_matrix_interpolate.
1460 //      (rvec defaults to NULL)
1461 void ai_turn_towards_vector(vector *dest, object *objp, 
1462                                                                          float frametime, float turn_time, vector *slide_vec, vector *rel_pos, float bank_override, int flags, vector *rvec)
1463 {
1464         //matrix        goal_orient;
1465         matrix  curr_orient;
1466         vector  vel_in, vel_out, desired_fvec, src;
1467         float           delta_time;
1468         physics_info    *pip;
1469         vector  vel_limit, acc_limit;
1470         float           delta_bank;
1471
1472         //      Don't allow a ship to turn if it has no engine strength.
1473         // AL 3-12-98: objp may not always be a ship!
1474         if ( objp->type == OBJ_SHIP ) {
1475                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f)
1476                         return;
1477         }
1478                         
1479         //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));
1480         pip = &objp->phys_info;
1481
1482         vel_in = pip->rotvel;
1483         curr_orient = objp->orient;
1484         delta_time = flFrametime;
1485
1486         Assert(turn_time > 0.0f);
1487         
1488         //      Scale turn_time based on skill level and team.
1489         if (!(flags & AITTV_FAST)){
1490                 if (objp->type == OBJ_SHIP){
1491                         if (Ships[objp->instance].team != Ships[Player_obj->instance].team){
1492                                 turn_time *= Turn_time_skill_level_scale[Game_skill_level];
1493                         }
1494                 }
1495         }
1496
1497         //      Set max turn rate.
1498         vel_limit.xyz.x = 2*PI/turn_time;
1499         vel_limit.xyz.y = 2*PI/turn_time;
1500         vel_limit.xyz.z = 2*PI/turn_time;
1501
1502         //      Set rate at which ship can accelerate to its rotational velocity.
1503         //      For now, weapons just go much faster.
1504         acc_limit = vel_limit;
1505         if (objp->type == OBJ_WEAPON)
1506                 vm_vec_scale(&acc_limit, 8.0f);
1507
1508         src = objp->pos;
1509
1510         if (rel_pos != NULL) {
1511                 vector  gun_point;
1512                 vm_vec_unrotate(&gun_point, rel_pos, &objp->orient);
1513                 vm_vec_add2(&src, &gun_point);
1514         }
1515
1516         vm_vec_normalized_dir(&desired_fvec, dest, &src);
1517
1518         //      Since ship isn't necessarily moving in the direction it's pointing, sometimes it's better
1519         //      to be moving towards goal rather than just pointing.  So, if slide_vec is !NULL, try to
1520         //      make ship move towards goal, not point at goal.
1521         if (slide_vec != NULL) {
1522                 vm_vec_add2(&desired_fvec, slide_vec);
1523                 vm_vec_normalize(&desired_fvec);
1524         }
1525
1526         //      Should be more general case here.  Currently, anything that is not a weapon will bank when it turns.
1527         if (objp->type == OBJ_WEAPON)
1528                 delta_bank = 0.0f;
1529         else if ((bank_override) && (Ships[objp->instance].team & opposing_team_mask(Player_ship->team))) {     //      Theoretically, this will only happen for Shivans.
1530                 delta_bank = bank_override;
1531                 //nprintf(("AI", "%i: %7.3f\n", Framecount, bank_override));
1532         } else {
1533                 delta_bank = vm_vec_dot(&curr_orient.v.rvec, &objp->last_orient.v.rvec);
1534                 delta_bank = 100.0f * (1.0f - delta_bank);
1535                 if (vm_vec_dot(&objp->last_orient.v.fvec, &objp->orient.v.rvec) < 0.0f)
1536                         delta_bank = -delta_bank;
1537
1538                 //nprintf(("AI", "%s: Frame %i: delta bank = %7.3f\n", Ships[objp->instance].ship_name, Framecount, delta_bank));
1539         }
1540
1541         //      Dave Andsager: The non-indented lines here are debug code to help you track down the problem in the physics
1542         //      that is causing ships to inexplicably rotate very far.  If you hit the Int3(), set the next statement to be
1543         //      the one marked "HERE".  (Do this clicking the cursor there, then right clicking.  Choose the right option.)
1544         //      This will allow you to rerun vm_forward_interpolate() with the values that caused the error.
1545         //      Note, you'll need to enable the Int3() about ten lines below.
1546 #ifndef NDEBUG
1547 vector tvec = objp->orient.v.fvec;
1548 vector  vel_in_copy;
1549 matrix  objp_orient_copy;
1550
1551 vel_in_copy = vel_in;
1552 objp_orient_copy = objp->orient;
1553
1554 vel_in = vel_in_copy;   //      HERE
1555 objp->orient = objp_orient_copy;
1556 #endif
1557         if (rvec != NULL) {
1558                 matrix  out_orient, goal_orient;
1559
1560                 vm_vector_2_matrix(&goal_orient, &desired_fvec, NULL, rvec);
1561                 vm_matrix_interpolate(&goal_orient, &curr_orient, &vel_in, delta_time, &out_orient, &vel_out, &vel_limit, &acc_limit);
1562                 objp->orient = out_orient;
1563         } else {
1564                 vm_forward_interpolate(&desired_fvec, &curr_orient, &vel_in, delta_time, delta_bank, &objp->orient, &vel_out, &vel_limit, &acc_limit);
1565         }
1566 #ifndef NDEBUG
1567 if (!((objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE))) {
1568         if (delta_time < 0.25f && vm_vec_dot(&objp->orient.v.fvec, &tvec) < 0.1f)
1569                 Int3(); //      Get Andsager.  A ship has turned too far in one frame.
1570 }
1571 #endif
1572
1573         pip->rotvel = vel_out;
1574 }
1575
1576 void init_ship_info()
1577 {
1578         int     i;
1579
1580         if (Ship_info_inited)
1581                 return;
1582
1583         for (i=0; i<MAX_SHIP_TYPES; i++) {
1584                 Ship_info[i].min_speed = - Ship_info[i].max_rear_vel;
1585                 Ship_info[i].max_accel = Ship_info[i].max_vel.xyz.z;
1586         }
1587
1588         Ship_info_inited = 1;
1589
1590 }
1591
1592 //      Set aip->target_objnum to objnum
1593 //      Update aip->previous_target_objnum.
1594 //      If new target (objnum) is different than old target, reset target_time.
1595 int set_target_objnum(ai_info *aip, int objnum)
1596 {
1597 /*
1598         char    old_name[32], new_name[32];
1599
1600         if (!timestamp_elapsed(aip->ok_to_target_timestamp))
1601                 return aip->target_objnum;
1602
1603         if (Player_ship && (Ships[aip->shipnum].team == Player_ship->team)) {
1604                 if (aip->target_objnum == -1)
1605                         strcpy(old_name, "none");
1606                 else
1607                         strcpy(old_name, Ships[Objects[aip->target_objnum].instance].ship_name);
1608
1609                 if (objnum == -1)
1610                         strcpy(new_name, "none");
1611                 else
1612                         strcpy(new_name, Ships[Objects[objnum].instance].ship_name);
1613
1614                 nprintf(("AI", "Ship %s changing target from %s to %s\n", Ships[aip->shipnum].ship_name, old_name, new_name));
1615         }
1616 */
1617
1618         // AL 2-25-97: Ensure that a protected ship isn't being set as a target (for non-players only)
1619         /*
1620         if ( objnum >= 0 ) {
1621                 if ( !(Objects[Ships[aip->shipnum].objnum].flags & OF_PLAYER_SHIP) ) {
1622                         if ( Objects[objnum].flags & OF_PROTECTED ) {
1623                                 // AL 2-26-97: removing Int3() until issue with setting OF_PROTECTED in ai_set_attack_subsystem()
1624                                 //Int3();                                                               // this should not happen
1625                                 return aip->target_objnum;              // don't change targets
1626                         }
1627                 }
1628         }
1629         */
1630
1631         if ((aip != Player_ai) && (!timestamp_elapsed(aip->ok_to_target_timestamp))) {
1632                 return aip->target_objnum;
1633         }
1634
1635         if (aip->target_objnum == objnum) {
1636                 aip->previous_target_objnum = aip->target_objnum;
1637         } else {
1638                 aip->previous_target_objnum = aip->target_objnum;
1639
1640                 // ignore this assert if a multiplayer observer
1641                 if((Game_mode & GM_MULTIPLAYER) && (aip == Player_ai) && (Player_obj->type == OBJ_OBSERVER)){
1642                 } else {
1643                         Assert(objnum != Ships[aip->shipnum].objnum);   //      make sure not targeting self
1644                 }
1645
1646                 // if stealth target, init ai_info for stealth
1647                 if ( (objnum > 0) && is_object_stealth_ship(&Objects[objnum]) ) {
1648                         init_ai_stealth_info(aip, &Objects[objnum]);
1649                 }
1650
1651                 aip->target_objnum = objnum;
1652                 aip->target_time = 0.0f;
1653                 aip->target_signature = Objects[objnum].signature;
1654                 // clear targeted subsystem
1655                 set_targeted_subsys(aip, NULL, -1);
1656         }
1657         
1658         return aip->target_objnum;
1659 }
1660
1661 int ai_select_primary_weapon(object *objp, object *other_objp, int flags);
1662
1663 //      Make new_subsys the targeted subsystem of ship *aip.
1664 ship_subsys *set_targeted_subsys(ai_info *aip, ship_subsys *new_subsys, int parent_objnum)
1665 {
1666         Assert(aip != NULL);
1667
1668         aip->last_subsys_target = aip->targeted_subsys;
1669         aip->targeted_subsys = new_subsys;
1670         aip->targeted_subsys_parent = parent_objnum;
1671
1672         if ( new_subsys ) {
1673                 // Make new_subsys target
1674                 if (new_subsys->system_info->type == SUBSYSTEM_ENGINE) {
1675                         if ( aip != Player_ai ) {
1676                                 ai_select_primary_weapon(&Objects[Ships[aip->shipnum].objnum], &Objects[parent_objnum], WIF_PUNCTURE);
1677                                 ship_primary_changed(&Ships[aip->shipnum]);     // AL: maybe send multiplayer information when AI ship changes primaries
1678                         }
1679                 }
1680
1681                 if ( aip == Player_ai ) {
1682                         hud_lock_reset(0.5f);
1683                 }
1684
1685         } else {
1686                 // Cleanup any subsys path information if it exists
1687                 ai_big_subsys_path_cleanup(aip);
1688         }
1689         
1690         return aip->targeted_subsys;
1691 }                                                                                         
1692
1693 // called to init the data for single ai object.  At this point,
1694 // the ship and the object and the ai_info are are correctly
1695 // linked together. Ai_info[ai_index].shipnum is the only valid field 
1696 // in ai_info.
1697 //      This is called right when the object is parsed, so you can't assume much
1698 //      has been initialized.  For example, wings, waypoints, goals are probably
1699 //      not yet loaded. --MK, 10/8/96
1700 void ai_object_init(object * obj, int ai_index)
1701 {
1702         ai_info *aip;
1703         Assert(ai_index >= 0 && ai_index < MAX_AI_INFO);
1704
1705         aip = &Ai_info[ai_index];
1706
1707         aip->type = 0;          //      0 means not in use.
1708         aip->wing = -1;         //      Member of what wing? -1 means none.
1709         aip->ai_class = Ship_info[Ships[obj->instance].ship_info_index].ai_class;
1710         aip->behavior = AIM_NONE;
1711 }
1712
1713 //      If *aip is docked, set max acceleration to A->mass/(A->mass + B->mass) where A is *aip and B is dock object
1714 void adjust_accel_for_docking(ai_info *aip)
1715 {
1716         if (aip->dock_objnum != -1) {
1717                 object  *obj2p = &Objects[aip->dock_objnum];
1718                 object  *obj1p;
1719
1720                 obj1p = &Objects[Ships[aip->shipnum].objnum];
1721
1722                 if (obj2p->signature == aip->dock_signature) {
1723                         float   ratio;
1724
1725                         ratio = obj1p->phys_info.mass / (obj1p->phys_info.mass + obj2p->phys_info.mass);
1726
1727                         // put cap on how much ship can slow down
1728                         if (ratio < 0.8) {
1729                                 ratio = 0.8f;
1730                         }
1731
1732                         if (AI_ci.forward > ratio) {
1733                                 AI_ci.forward = ratio;
1734                         }
1735                 }
1736         }
1737 }
1738
1739 // -------------------------------------------------------------------
1740 void accelerate_ship(ai_info *aip, float accel)
1741 {
1742         aip->prev_accel = accel;
1743         AI_ci.forward = accel;
1744         adjust_accel_for_docking(aip);
1745 }
1746
1747 //      --------------------------------------------------------------------------
1748 void change_acceleration(ai_info *aip, float delta_accel)
1749 {
1750         float   new_accel;
1751
1752         if (delta_accel < 0.0f) {
1753                 if (aip->prev_accel > 0.0f)
1754                         aip->prev_accel = 0.0f;
1755         } else if (aip->prev_accel < 0.0f)
1756                 aip->prev_accel = 0.0f;
1757
1758         new_accel = aip->prev_accel + delta_accel * flFrametime;
1759
1760         if (new_accel > 1.0f)
1761                 new_accel = 1.0f;
1762         else if (new_accel < -1.0f)
1763                 new_accel = -1.0f;
1764         
1765         aip->prev_accel = new_accel;
1766
1767         AI_ci.forward = new_accel;
1768         adjust_accel_for_docking(aip);
1769 }
1770
1771 void set_accel_for_target_speed(object *objp, float tspeed)
1772 {
1773         float   max_speed;
1774         ai_info *aip;
1775
1776         aip = &Ai_info[Ships[objp->instance].ai_index];
1777
1778         max_speed = Ships[objp->instance].current_max_speed;
1779
1780         AI_ci.forward = tspeed/max_speed;
1781         aip->prev_accel = AI_ci.forward;
1782
1783         adjust_accel_for_docking(aip);
1784 }
1785
1786 //      Stuff perim_point with a point on the perimeter of the sphere defined by object *objp
1787 //      on the vector from the center of *objp through the point *vp.
1788 void project_point_to_perimeter(vector *perim_point, vector *pos, float radius, vector *vp)
1789 {
1790         vector  v1;
1791         float           mag;
1792
1793         vm_vec_sub(&v1, vp, pos);
1794         mag = vm_vec_mag(&v1);
1795
1796         if (mag == 0.0f) {
1797                 Warning(LOCATION, "projectable point is at center of sphere.");
1798                 (void) vm_vec_make(&v1, 0.0f, radius, 0.0f);
1799         } else {
1800                 vm_vec_normalize(&v1);
1801                 vm_vec_scale(&v1, 1.1f * radius + 10.0f);
1802         }
1803
1804         vm_vec_add2(&v1, pos);
1805         *perim_point = v1;
1806 }
1807
1808 //      Stuff tan1 with tangent point on sphere.  tan1 is point nearer to *p1
1809 //      *p0 is point through which tangents pass.
1810 //      *centerp is center of sphere.
1811 //      *p1 is another point in space to define the plane in which tan1, tan2 reside.
1812 //      radius is the radius of the sphere.
1813 //      Note, this is a very approximate function just for AI.
1814 //      Note also: On 12/26/96, p1 is used to define the plane perpendicular to that which
1815 //      contains the tangent point.
1816 void get_tangent_point(vector *tan1, vector *p0, vector *centerp, vector *p1, float radius)
1817 {
1818         vector  dest_vec, v2c, perp_vec, temp_vec, v2;
1819         float           dist, ratio;
1820
1821         //      Detect condition of point inside sphere.
1822         if (vm_vec_dist(p0, centerp) < radius)
1823                 project_point_to_perimeter(tan1, centerp, radius, p0);
1824         else {
1825                 vm_vec_normalized_dir(&v2c, centerp, p0);
1826
1827                 //      Compute perpendicular vector using p0, centerp, p1
1828                 vm_vec_normal(&temp_vec, p0, centerp, p1);
1829                 vm_vec_sub(&v2, centerp, p0);
1830                 vm_vec_cross(&perp_vec, &temp_vec, &v2);
1831
1832                 vm_vec_normalize(&perp_vec);
1833
1834                 dist = vm_vec_dist_quick(p0, centerp);
1835                 ratio = dist / radius;
1836
1837                 if (ratio < 2.0f)
1838                         vm_vec_scale_add(&dest_vec, &perp_vec, &v2c, ratio-1.0f);
1839                 else
1840                         vm_vec_scale_add(&dest_vec, &v2c, &perp_vec, (1.0f + 1.0f/ratio));
1841
1842                 vm_vec_scale_add(tan1, p0, &dest_vec, dist + radius);
1843         }
1844 }
1845
1846 //      --------------------------------------------------------------------------
1847 //      Given an object and a point, turn towards the point, resulting in
1848 // approach behavior.
1849 void turn_towards_point(object *objp, vector *point, vector *slide_vec, float bank_override)
1850 {
1851         ai_info *aip;
1852         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1853         
1854         // check if in formation and if not leader, don't change rotvel.xyz.z (bank to match leader elsewhere)
1855         if (aip->ai_flags & AIF_FORMATION) {
1856                 if (&Objects[aip->goal_objnum] != objp) {
1857                         float rotvel_z = objp->phys_info.rotvel.xyz.z;
1858                         ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1859                         objp->phys_info.rotvel.xyz.z = rotvel_z;
1860                 }
1861         } else {
1862                 // normal turn
1863                 ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1864         }
1865 }
1866
1867 //      --------------------------------------------------------------------------
1868 //      Given an object and a point, turn away from the point, resulting in avoidance behavior.
1869 //      Note: Turn away at full speed, not scaled down by skill level.
1870 void turn_away_from_point(object *objp, vector *point, float bank_override)
1871 {
1872         vector  opposite_point;
1873
1874         vm_vec_sub(&opposite_point, &objp->pos, point);
1875         vm_vec_add2(&opposite_point, &objp->pos);
1876
1877         ai_turn_towards_vector(&opposite_point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, NULL, NULL, bank_override, AITTV_FAST);
1878 }
1879
1880
1881 //      --------------------------------------------------------------------------
1882 //      Given an object and a point, turn tangent to the point, resulting in
1883 // a circling behavior.
1884 //      Make object *objp turn around the point *point with a radius of radius.
1885 //      Note that this isn't the same as following a circle of radius radius with
1886 //      center *point, but it should be adequate.
1887 //      Note that if you want to circle an object without hitting it, you should use
1888 //      about twice that object's radius for radius, else you'll certainly bump into it.
1889 //      Return dot product to goal point.
1890 float turn_towards_tangent(object *objp, vector *point, float radius)
1891 {
1892         vector  vec_to_point;
1893         vector  goal_point;
1894         vector  perp_point;                             //      point radius away from *point on vector to objp->pos
1895         vector  up_vec, perp_vec;
1896
1897         vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1898         vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.v.fvec);
1899         vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1900
1901         vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1902         if (vm_vec_dot(&objp->orient.v.fvec, &perp_vec) > 0.0f) {
1903                 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, radius);
1904         } else {
1905                 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, -radius);
1906         }
1907
1908 //      Ai_info[Ships[objp->instance].ai_index].goal_point = goal_point;
1909         turn_towards_point(objp, &goal_point, NULL, 0.0f);
1910
1911         vector  v2g;
1912
1913         vm_vec_normalized_dir(&v2g, &goal_point, &objp->pos);
1914         return vm_vec_dot(&objp->orient.v.fvec, &v2g);
1915 }
1916
1917 float turn_toward_tangent_with_axis(object *objp, object *center_objp, float radius)
1918 {
1919         vector r_vec, theta_vec;
1920         vector center_vec, vec_on_cylinder, sph_r_vec;
1921         float center_obj_z;
1922
1923         // find closest z of center objp
1924         vm_vec_sub(&sph_r_vec, &objp->pos, &center_objp->pos);
1925         center_obj_z = vm_vec_dotprod(&sph_r_vec, &center_objp->orient.v.fvec);
1926
1927         // find pt on axis with closest z
1928         vm_vec_scale_add(&center_vec, &center_objp->pos, &center_objp->orient.v.fvec, center_obj_z);
1929
1930         // get r_vec
1931         vm_vec_sub(&r_vec, &objp->pos, &center_vec);
1932 //      float r_mag = vm_vec_normalize_quick(&r_vec);
1933 //      mprintf(("cur_r: %.1f, desired_r: %.1f\n", r_mag, radius));
1934         Assert( (vm_vec_dotprod(&r_vec, &center_objp->orient.v.fvec) < 0.0001));
1935
1936         // get theta vec - perp to r_vec and z_vec
1937         vm_vec_crossprod(&theta_vec, &center_objp->orient.v.fvec, &r_vec);
1938
1939 #ifndef NDEBUG
1940         float mag = vm_vec_normalize(&theta_vec);
1941         Assert(mag > 0.9999 && mag < 1.0001);
1942 #endif
1943
1944         vector temp;
1945         vm_vec_crossprod(&temp, &r_vec, &theta_vec);
1946
1947 #ifndef NDEBUG
1948         float dot = vm_vec_dotprod(&temp, &center_objp->orient.v.fvec);
1949         Assert( dot >0.9999 && dot < 1.0001);
1950 #endif
1951
1952         // find pt on clylinder with closest z
1953         vm_vec_scale_add(&vec_on_cylinder, &center_vec, &r_vec, radius);
1954
1955         vector goal_pt, v2g;
1956         vm_vec_scale_add(&goal_pt, &vec_on_cylinder, &theta_vec, radius);
1957
1958 //      Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pt;
1959         turn_towards_point(objp, &goal_pt, NULL, 0.0f);
1960
1961         vm_vec_normalized_dir(&v2g, &goal_pt, &objp->pos);
1962         return vm_vec_dot(&objp->orient.v.fvec, &v2g);
1963 }
1964
1965 //      Returns a point radius units away from *point that *objp should turn towards to orbit *point
1966 void get_tangent_point(vector *goal_point, object *objp, vector *point, float radius)
1967 {
1968         vector  vec_to_point;
1969         vector  perp_point;                             //      point radius away from *point on vector to objp->pos
1970         vector  up_vec, perp_vec;
1971
1972         vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1973         vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.v.fvec);
1974         vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1975         vm_vec_normalize(&perp_vec);
1976
1977         vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1978
1979         if (vm_vec_dot(&objp->orient.v.fvec, &perp_vec) > 0.0f) {
1980                 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, radius);
1981         } else {
1982                 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, -radius);
1983         }
1984 }
1985
1986 int     Player_attacking_enabled = 1;
1987
1988 // -----------------------------------------------------------------------------
1989 // Determine whether an object is targetable within a nebula
1990 int object_is_targetable(object *target, ship *viewer)
1991 {
1992         int stealth_ship = 0;
1993
1994         // if target is ship, check if visible by team
1995         if (target->type == OBJ_SHIP) {
1996                 stealth_ship = (Ship_info[Ships[target->instance].ship_info_index].flags & SIF_STEALTH);
1997                 if ( ship_is_visible_by_team(target->instance, viewer->team) == 1) {
1998                         return 1;
1999                 }
2000         }
2001
2002         // for AI partially targetable works as fully targetable, except for stealth ship
2003         if (stealth_ship) {
2004                 // if not team targetable, check if within frustrum
2005                 if ( ai_is_stealth_visible(&Objects[viewer->objnum], target) == STEALTH_VISIBLE ) {
2006                         return 1;
2007                 } else {
2008                         return 0;
2009                 }
2010         }
2011
2012         // if not fully targetable by team, check awacs level with viewer
2013         // allow targeting even if only only partially targetable to player
2014         float radar_return = awacs_get_level(target, viewer);
2015         if ( radar_return > 0.4 ) {
2016                 return 1;
2017         } else {
2018                 return 0;
2019         }
2020 }
2021
2022 //      Return number of enemies attacking object objnum
2023 //
2024 // AL 10.26.97: Also include turrets on large ships when couting enemies attacking
2025 int num_enemies_attacking(int objnum)
2026 {
2027         object          *objp;
2028         ship                    *sp;
2029         ship_subsys     *ssp;
2030         ship_obj                *so;
2031         int                     count;
2032
2033         count = 0;
2034
2035         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2036                 objp = &Objects[so->objnum];
2037                 Assert(objp->instance != -1);
2038                 sp = &Ships[objp->instance];
2039
2040                 if (Ai_info[sp->ai_index].target_objnum == objnum)
2041                         count++;
2042
2043                 // consider turrets that may be attacking objnum (but only turrets on SIF_BIG_SHIP ships)
2044                 if ( Ship_info[sp->ship_info_index].flags & SIF_BIG_SHIP ) {
2045
2046                         // loop through all the subsystems, check if turret has objnum as a target
2047                         ssp = GET_FIRST(&sp->subsys_list);
2048                         while ( ssp != END_OF_LIST( &sp->subsys_list ) ) {
2049
2050                                 if ( ssp->system_info->type == SUBSYSTEM_TURRET ) {
2051                                         if ( (ssp->turret_enemy_objnum == objnum) && (ssp->current_hits > 0) ) {
2052                                                 count++;
2053                                         }
2054                                 }
2055                                 ssp = GET_NEXT( ssp );
2056                         } // end while
2057                 }
2058         }
2059
2060         return count;
2061 }
2062
2063 //      Get the team to fire on given an object.
2064 int get_enemy_team_mask(int objnum)
2065 {
2066         int     my_team, enemy_team_mask;
2067
2068         my_team = Ships[Objects[objnum].instance].team;
2069
2070         if (Mission_all_attack) {
2071                 //      All teams attack all teams.
2072                 switch (my_team) {
2073                 case TEAM_FRIENDLY:
2074                         enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2075                         break;
2076                 case TEAM_HOSTILE:
2077                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2078                         break;
2079                 case TEAM_NEUTRAL:
2080                         enemy_team_mask = TEAM_FRIENDLY | TEAM_HOSTILE | TEAM_TRAITOR;
2081                         break;
2082                 case TEAM_UNKNOWN:
2083                         enemy_team_mask = TEAM_HOSTILE;
2084                         break;
2085                 case TEAM_TRAITOR:
2086                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2087                         break;
2088                 default:
2089                         enemy_team_mask = TEAM_HOSTILE;
2090                         Int3();                 //      Illegal value for team!
2091                         break;
2092                 }
2093         } else {
2094                 switch (my_team) {
2095                 case TEAM_FRIENDLY:
2096                         enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2097                         break;
2098                 case TEAM_HOSTILE:
2099                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2100                         break;
2101                 case TEAM_NEUTRAL:
2102                         enemy_team_mask = TEAM_FRIENDLY | TEAM_TRAITOR;
2103                         break;
2104                 case TEAM_UNKNOWN:
2105                         enemy_team_mask = TEAM_HOSTILE;
2106                         break;
2107                 case TEAM_TRAITOR:
2108                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2109                         break;
2110                 default:
2111                         enemy_team_mask = TEAM_HOSTILE;
2112                         Int3();                 //      Illegal value for team!
2113                         break;
2114                 }
2115         }
2116
2117         return enemy_team_mask;
2118 }
2119
2120 //      Scan all the ships in *objp's wing.
2121 //      Return the lowest maximum speed of a ship in the wing.
2122 //      Current maximum speed (based on energy settings) is shipp->current_max_speed
2123 float get_wing_lowest_max_speed(object *objp)
2124 {
2125         ship            *shipp;
2126         ai_info *aip;
2127         float           lowest_max_speed;
2128         int             wingnum;
2129         object  *o;
2130         ship_obj        *so;
2131
2132         Assert(objp->type == OBJ_SHIP);
2133         Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
2134         shipp = &Ships[objp->instance];
2135         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
2136         aip = &Ai_info[shipp->ai_index];
2137
2138         wingnum = aip->wing;
2139
2140         lowest_max_speed = shipp->current_max_speed;
2141
2142         if ( wingnum == -1 )
2143                 return lowest_max_speed;
2144
2145         Assert(wingnum >= 0);
2146
2147         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2148                 o = &Objects[so->objnum];
2149                 ship    *oshipp = &Ships[o->instance];
2150                 ai_info *oaip = &Ai_info[oshipp->ai_index];
2151
2152                 if ((oaip->mode == AIM_WAYPOINTS) && (oaip->wing == wingnum)) {
2153                         //      Note: If a ship in the wing has a super low max speed, probably its engines are disabled.  So, fly along and
2154                         //      ignore the poor guy.
2155                         float   cur_max = oshipp->current_max_speed;
2156
2157                         if (oaip->ai_flags & AIF_DOCKED) {
2158                                 if (oaip->dock_objnum > -1)
2159                                         if (Objects[oaip->dock_objnum].type == OBJ_SHIP) 
2160                                                 cur_max *= o->phys_info.mass/(o->phys_info.mass + Objects[oaip->dock_objnum].phys_info.mass);
2161                         }
2162                                                         
2163                         if ((oshipp->current_max_speed > 5.0f) && (cur_max < lowest_max_speed)) {
2164                                 lowest_max_speed = cur_max;
2165                         }
2166                 }
2167         }
2168
2169         return lowest_max_speed;
2170 }
2171
2172 /*
2173 //      Tell everyone to ignore object objnum.
2174 void set_global_ignore_object(int objnum)
2175 {
2176         int     i;
2177
2178         Assert(Objects[objnum].type == OBJ_SHIP);
2179
2180         nprintf(("AI", "Telling everyone to ignore object %s\n", Ships[Objects[objnum].instance].ship_name));
2181
2182         for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2183                 if (Ignore_objects[i].objnum == -1) {
2184                         Ignore_objects[i].objnum = objnum;
2185                         Ignore_objects[i].signature = Objects[objnum].signature;
2186                         break;
2187                 }
2188         }
2189
2190         if (i == MAX_IGNORE_OBJECTS) {
2191                 //      Couldn't find a free slot, but maybe one of these objects has died.
2192                 for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2193                         int     o = Ignore_objects[i].objnum;
2194                         if (Objects[o].type != OBJ_SHIP)
2195                                 break;          //      Not a ship, so use this slot.
2196                         if (Objects[o].signature != Ignore_objects[i].signature)
2197                                 break;          //      Signatures don't match, so use this slot.
2198                 }
2199
2200                 if (i != MAX_IGNORE_OBJECTS) {
2201                         Ignore_objects[i].objnum = objnum;
2202                         Ignore_objects[i].signature = Objects[objnum].signature;
2203                 } else {
2204                         nprintf(("Warning", "Ignore_objects buffer full.  Stealing a slot to ignore object #%i\n"));
2205                         Int3();
2206
2207                         int     r;
2208
2209                         r = objnum % MAX_IGNORE_OBJECTS;
2210
2211                         Ignore_objects[r].objnum = objnum;
2212                         Ignore_objects[r].signature = Objects[objnum].signature;
2213                 }
2214         }
2215 }
2216
2217 */
2218
2219 //      Determine if object objnum is supposed to be ignored by object with ai_info *aip.
2220 //      Return:
2221 //              TRUE    if objnum is aip->ignore_objnum (and signatures match)
2222 //                              or objnum is in ignore wing
2223 //              FALSE   otherwise
2224 int is_ignore_object(ai_info *aip, int objnum)
2225 {
2226
2227 /*      //      First, scan all objects in global array of objects to be ignored.
2228         for (int i=0; i<MAX_IGNORE_OBJECTS; i++)
2229                 if (Ignore_objects[i].objnum != -1)
2230                         if (objnum == Ignore_objects[i].objnum)
2231                                 if (Objects[Ignore_objects[i].objnum].signature == Ignore_objects[i].signature)
2232                                         return 1;
2233 */
2234
2235         //      Didn't find in global list.  Now check 
2236         if (aip->ignore_objnum == UNUSED_OBJNUM)
2237                 return 0;                                                                       //      Not ignoring anything.
2238         else if (aip->ignore_objnum >= 0) {             //      This means it's ignoring an object, not a wing.
2239                 if (aip->ignore_objnum == objnum) {
2240                         if (Objects[aip->ignore_objnum].signature == aip->ignore_signature) {
2241                                 return 1;
2242                         } else {
2243                                 aip->ignore_objnum = UNUSED_OBJNUM;
2244                                 return 0;
2245                         }
2246                 } else {
2247                         return 0;
2248                 }
2249         } else {                                                                                        //      Ignoring a wing.
2250                 Int3(); // Should never happen.  I thought I removed this behavior! -- MK, 5/17/98
2251                 return 0;
2252 /*              int     ignore_wingnum = -(aip->ignore_objnum + 1);
2253
2254                 Assert(ignore_wingnum < MAX_WINGS);
2255                 Assert(aip->shipnum >= 0);
2256                 return (Ships[Objects[objnum].instance].wingnum == ignore_wingnum);
2257 */      }
2258 }
2259
2260 // -----------------------------------------------------------------------------
2261
2262 // given a ship with bounding box and a point, find the closest point on the bbox
2263 int get_nearest_bbox_point(object *ship_obj, vector *start, vector *box_pt)
2264 {
2265         vector temp, rf_start;
2266         polymodel *pm;
2267         pm = model_get(Ship_info[Ships[ship_obj->instance].ship_info_index].modelnum);
2268
2269         // get start in ship rf
2270         vm_vec_sub(&temp, start, &ship_obj->pos);
2271         vm_vec_rotate(&rf_start, &temp, &ship_obj->orient);
2272
2273         // find box_pt
2274         int inside = project_point_onto_bbox(&pm->mins, &pm->maxs, &rf_start, &temp);
2275
2276         // get box_pt in world rf
2277         vm_vec_unrotate(box_pt, &temp, &ship_obj->orient);
2278         vm_vec_add2(box_pt, &ship_obj->pos);
2279
2280         return inside;
2281 }
2282
2283
2284 typedef struct eval_nearest_objnum {
2285         int     objnum;
2286         object *trial_objp;
2287         int     enemy_team_mask;
2288         int     enemy_wing;
2289         float   range;
2290         int     max_attackers;
2291         int     nearest_objnum;
2292         float   nearest_dist;
2293         int     check_danger_weapon_objnum;
2294 } eval_nearest_objnum;
2295
2296
2297 void evaluate_object_as_nearest_objnum(eval_nearest_objnum *eno)
2298 {
2299         ai_info *aip;
2300         ship_subsys     *attacking_subsystem;
2301
2302         aip = &Ai_info[Ships[Objects[eno->objnum].instance].ai_index];
2303
2304         attacking_subsystem = aip->targeted_subsys;
2305
2306         if ((attacking_subsystem != NULL) || !(eno->trial_objp->flags & OF_PROTECTED)) {
2307                 if ( OBJ_INDEX(eno->trial_objp) != eno->objnum ) {
2308 #ifndef NDEBUG
2309                         if (!Player_attacking_enabled && (eno->trial_objp == Player_obj))
2310                                 return;
2311 #endif
2312                         //      If only supposed to attack ship in a specific wing, don't attack other ships.
2313                         if ((eno->enemy_wing != -1) && (Ships[eno->trial_objp->instance].wingnum != eno->enemy_wing))
2314                                 return;
2315
2316                         //      Don't keep firing at a ship that is in its death throes.
2317                         if (Ships[eno->trial_objp->instance].flags & SF_DYING)
2318                                 return;
2319
2320                         if (is_ignore_object(aip, ((eno->trial_objp)-Objects)))
2321                                 return;
2322
2323                         if (eno->trial_objp->flags & OF_PROTECTED)
2324                                 return;
2325
2326                         if (Ships[eno->trial_objp->instance].flags & SF_ARRIVING)
2327                                 return;
2328
2329                         ship_info *sip = &Ship_info[Ships[eno->trial_objp->instance].ship_info_index];
2330
2331                         if (sip->flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2332                                 return;
2333
2334                         if (Ships[eno->trial_objp->instance].team & eno->enemy_team_mask) {
2335                                 float   dist;
2336                                 int     num_attacking;
2337
2338                                 // Allow targeting of stealth in nebula by his firing at me
2339                                 // This is done for a specific ship, not generally.
2340                                 if ( !eno->check_danger_weapon_objnum ) {
2341                                         // check if can be targeted if inside nebula
2342                                         if ( !object_is_targetable(eno->trial_objp, &Ships[Objects[eno->objnum].instance]) ) {
2343                                                 // check if stealth ship is visible, but not "targetable"
2344                                                 if ( !((sip->flags & SIF_STEALTH) && ai_is_stealth_visible(&Objects[eno->objnum], eno->trial_objp)) ) {
2345                                                         return;
2346                                                 }
2347                                         }
2348                                 }
2349
2350                                 // if objnum is BIG or HUGE, find distance to bbox
2351                                 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
2352                                         vector box_pt;
2353                                         // check if inside bbox
2354                                         int inside = get_nearest_bbox_point(eno->trial_objp, &Objects[eno->objnum].pos, &box_pt);
2355                                         if (inside) {
2356                                                 dist = 10.0f;
2357                                                 // on the box
2358                                         } else {
2359                                                 dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &box_pt);
2360                                         }
2361                                 } else {
2362                                         dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &eno->trial_objp->pos);
2363                                 }
2364                                 
2365                                 //      Make it more likely that fighters (or bombers) will be picked as an enemy by scaling up distance for other types.
2366                                 if ((Ship_info[Ships[eno->trial_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))) {
2367                                         dist = dist * 0.5f;
2368                                 }
2369
2370                                 num_attacking = num_enemies_attacking(eno->trial_objp-Objects);
2371                                 if ((sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) || (num_attacking < eno->max_attackers)) {
2372                                         if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))){
2373                                                 dist *= (float) (num_attacking+2)/2.0f;                         //      prevents lots of ships from attacking same target
2374                                         }
2375
2376                                         if (eno->trial_objp->flags & OF_PLAYER_SHIP){
2377                                                 dist *= 1.0f + (NUM_SKILL_LEVELS - Game_skill_level - 1)/NUM_SKILL_LEVELS;      //      Favor attacking non-players based on skill level.
2378                                         }
2379
2380                                         if (dist < eno->nearest_dist) {
2381                                                 eno->nearest_dist = dist;
2382                                                 eno->nearest_objnum = eno->trial_objp-Objects;
2383                                         }
2384                                 }
2385                         }
2386                 }
2387         }
2388
2389 }
2390
2391
2392 //      Given an object and an enemy team, return the index of the nearest enemy object.
2393 //      Unless aip->targeted_subsys != NULL, don't allow to attack objects
2394 //      with OF_PROTECTED bit set.
2395 //      Ship must be within range "range".
2396 //      Don't attack a ship that already has at least max_attackers attacking it.
2397 int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers)
2398 {
2399         object  *danger_weapon_objp;
2400         ai_info *aip;
2401         ship_obj        *so;
2402
2403         // initialize eno struct
2404         eval_nearest_objnum eno;
2405         eno.enemy_team_mask = enemy_team_mask;
2406         eno.enemy_wing = enemy_wing;
2407         eno.max_attackers = max_attackers;
2408         eno.objnum = objnum;
2409         eno.range = range;
2410         eno.nearest_dist = range;
2411         eno.nearest_objnum = -1;
2412         eno.check_danger_weapon_objnum = 0;
2413
2414         // go through the list of all ships and evaluate as potential targets
2415         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2416                 eno.trial_objp = &Objects[so->objnum];
2417                 evaluate_object_as_nearest_objnum(&eno);
2418
2419         }
2420
2421         // check if danger_weapon_objnum has will show a stealth ship
2422         aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2423         if (aip->danger_weapon_objnum >= 0) {
2424                 danger_weapon_objp = &Objects[aip->danger_weapon_objnum];
2425                 // validate weapon
2426                 if (danger_weapon_objp->signature == aip->danger_weapon_signature) {
2427                         Assert(danger_weapon_objp->type == OBJ_WEAPON);
2428                         // check if parent is a ship
2429                         if (danger_weapon_objp->parent >= 0) {
2430                                 if ( is_object_stealth_ship(&Objects[danger_weapon_objp->parent]) ) {
2431                                         // check if stealthy
2432                                         if ( ai_is_stealth_visible(&Objects[objnum], &Objects[danger_weapon_objp->parent]) != STEALTH_FULLY_TARGETABLE ) {
2433                                                 // check if weapon is laser
2434                                                 if (Weapon_info[Weapons[danger_weapon_objp->instance].weapon_info_index].subtype == WP_LASER) {
2435                                                         // check stealth ship by its laser fire
2436                                                         eno.check_danger_weapon_objnum = 1;
2437                                                         eno.trial_objp = &Objects[danger_weapon_objp->parent];
2438                                                         evaluate_object_as_nearest_objnum(&eno);
2439                                                 }
2440                                         }
2441                                 }
2442                         }
2443                 }
2444         }
2445
2446         //      If only looking for target in certain wing and couldn't find anything in
2447         //      that wing, look for any object.
2448         if ((eno.nearest_objnum == -1) && (enemy_wing != -1)) {
2449                 return get_nearest_objnum(objnum, enemy_team_mask, -1, range, max_attackers);
2450         }
2451
2452         return eno.nearest_objnum;
2453 }
2454
2455 //      Given an object and an enemy team, return the index of the nearest enemy object.
2456 //      Unlike find_enemy or find_nearest_objnum, this doesn't care about things like the protected flag or number
2457 //      of enemies attacking.
2458 //      It is used to find the nearest enemy to determine things like whether to rearm.
2459 int find_nearby_hostile(int objnum, int enemy_team_mask, float range, int *count)
2460 {
2461         int             nearest_objnum;
2462         float           nearest_dist;
2463         object  *objp;
2464         ai_info *aip;
2465         ship_obj        *so;
2466
2467         nearest_objnum = -1;
2468         nearest_dist = range;
2469
2470         aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2471
2472         *count = 0;
2473
2474         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2475                 objp = &Objects[so->objnum];
2476
2477                 if ( OBJ_INDEX(objp) != objnum ) {
2478                         if (Ships[objp->instance].flags & SF_DYING)
2479                                 continue;
2480
2481                         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2482                                 continue;
2483
2484                         if (Ships[objp->instance].team & enemy_team_mask) {
2485                                 float   dist;
2486
2487                                 dist = vm_vec_dist_quick(&Objects[objnum].pos, &objp->pos) - objp->radius*0.75f;
2488                                 
2489                                 if (dist < range) {
2490                                         (*count)++;
2491
2492                                         if (dist < nearest_dist) {
2493                                                 nearest_dist = dist;
2494                                                 nearest_objnum = objp-Objects;
2495                                         }
2496                                 }
2497                         }
2498                 }
2499         }
2500
2501         return nearest_objnum;
2502 }
2503
2504 // return !0 if objp can be considered for a turret target, 0 otherwise
2505 // input:       objp                            =>      object that turret is considering as an enemy
2506 //                              turret_parent   =>      object index for ship that turret sits on
2507 int valid_turret_enemy(object *objp, object *turret_parent)
2508 {
2509         if ( objp == turret_parent ) {
2510                 return 0;
2511         }
2512
2513         if ( objp->type == OBJ_ASTEROID ) {
2514                 return 1;
2515         }
2516
2517         if ( (objp->type == OBJ_SHIP) ) {
2518                 ship *shipp;
2519                 shipp = &Ships[objp->instance];
2520
2521                 // don't fire at ships with protected bit set!!!
2522                 if ( objp->flags & OF_PROTECTED ) {
2523                         return 0;
2524                 }
2525
2526                 if ( !(Ship_info[shipp->ship_info_index].flags & SIF_DO_COLLISION_CHECK)) {
2527                         return 0;
2528                 }
2529
2530                 if (shipp->flags & SF_ARRIVING) {
2531                         return 0;
2532                 }
2533
2534                 return 1;
2535         }
2536
2537         if ( objp->type == OBJ_WEAPON ) {
2538                 if ( Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_BOMB ) {
2539                         if ( obj_team(turret_parent) != Weapons[objp->instance].team ) {
2540                                 return 1;
2541                         }
2542                 }
2543         }
2544
2545         return 0;
2546 }
2547
2548 // return 1 if objp is in fov of the specified turret, tp.  Otherwise return 0.
2549 //      dist = distance from turret to center point of object
2550 int object_in_turret_fov(object *objp, model_subsystem *tp, vector *tvec, vector *tpos, float dist)
2551 {
2552         vector  v2e;
2553         float           dot;
2554         vm_vec_normalized_dir(&v2e, &objp->pos, tpos);
2555         dot = vm_vec_dot(&v2e, tvec);
2556
2557         dot += objp->radius / (dist + objp->radius);
2558
2559         if ( dot >= tp->turret_fov ) {
2560                 return 1;
2561         }
2562
2563         return 0;
2564 }
2565
2566 // return 1 if bomb_objp is headed towards ship_objp
2567 int bomb_headed_towards_ship(object *bomb_objp, object *ship_objp)
2568 {
2569         float           dot;
2570         vector  bomb_to_ship_vector;
2571
2572         vm_vec_normalized_dir(&bomb_to_ship_vector, &ship_objp->pos, &bomb_objp->pos);
2573         dot = vm_vec_dot(&bomb_objp->orient.v.fvec, &bomb_to_ship_vector);
2574
2575         if ( dot > 0 ) {
2576                 return 1;
2577         }
2578
2579         return 0;
2580 }
2581
2582 // nubmer of live turrets with target_objnum 
2583 int num_turrets_attacking(object *turret_parent, int target_objnum) 
2584 {
2585         ship_subsys *ss;
2586         ship *shipp;
2587         int count = 0;
2588         shipp = &Ships[turret_parent->instance];
2589
2590         Assert(turret_parent->type == OBJ_SHIP);
2591         Assert(Objects[target_objnum].type == OBJ_SHIP);
2592
2593         for (ss=GET_FIRST(&shipp->subsys_list); ss!=END_OF_LIST(&shipp->subsys_list); ss=GET_NEXT(ss)) {
2594                 // check if subsys is alive
2595                 if (ss->current_hits <= 0.0f) {
2596                         continue;
2597                 }
2598
2599                 // check if it's a turret
2600                 if (ss->system_info->type != SUBSYSTEM_TURRET) {
2601                         continue;
2602                 }
2603
2604                 // if the turret is locked
2605                 if(ss->weapons.flags & SW_FLAG_TURRET_LOCK){
2606                         continue;
2607                 }               
2608
2609                 // check if turret is targeting target_objnum
2610                 if (ss->turret_enemy_objnum == target_objnum) {
2611                         count++;
2612                 }
2613         }
2614
2615         return count;
2616 }
2617
2618 float Lethality_range_const = 2.0f;
2619 DCF(lethality_range, "N for modifying range: 1 / (1+N) at 100")
2620 {
2621         dc_get_arg(ARG_FLOAT);
2622         Lethality_range_const = Dc_arg_float;
2623 }
2624
2625 float Player_lethality_bump[NUM_SKILL_LEVELS] = {
2626         // 0.0f, 5.0f, 10.0f, 25.0f, 40.0f
2627         0.0f, 0.0f, 0.0f, 0.0f, 0.0f
2628 };
2629
2630 // evaluate obj as posssible target for turret
2631 void evaluate_obj_as_target(object *objp, eval_enemy_obj_struct *eeo)
2632 {
2633         object  *turret_parent_obj = &Objects[eeo->turret_parent_objnum];
2634         ship            *shipp;
2635         model_subsystem *tp = eeo->turret_subsys->system_info;
2636         float dist;
2637
2638         // Don't look for bombs when weapon system is not ok
2639         if (objp->type == OBJ_WEAPON && !eeo->weapon_system_ok) {
2640                 return;
2641         }
2642
2643         if ( !valid_turret_enemy(objp, turret_parent_obj) ) {
2644                 return;
2645         }
2646
2647 #ifndef NDEBUG
2648         if (!Player_attacking_enabled && (objp == Player_obj)) {
2649                 return;
2650         }
2651 #endif
2652
2653         if ( objp->type == OBJ_SHIP ) {
2654                 shipp = &Ships[objp->instance];
2655
2656                 // check on enemy team
2657                 if ( !(shipp->team & eeo->enemy_team_mask) ) {
2658                         return;
2659                 }
2660
2661                 // check if protected
2662                 if (objp->flags & OF_PROTECTED) {
2663                         return;
2664                 }
2665
2666                 // check if beam protected
2667                 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2668                         if (objp->flags & OF_BEAM_PROTECTED) {
2669                                 return;
2670                         }
2671                 }
2672
2673                 if (eeo->big_only_flag) {
2674                         if (!(Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2675                                 return;
2676                         }
2677                 }
2678
2679                 // check if     turret flagged to only target tagged ships
2680                 if ( (eeo->turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && !ship_is_tagged(objp) ) {
2681                         return;
2682                 }
2683
2684                 // check if valid target in nebula
2685                 if ( !object_is_targetable(objp, &Ships[Objects[eeo->turret_parent_objnum].instance]) ) {
2686                         // BYPASS ocassionally for stealth
2687                         int try_anyway = FALSE;
2688                         if ( is_object_stealth_ship(objp) ) {
2689                                 float turret_stealth_find_chance = 0.5f;
2690                                 float speed_mod = -0.1f + vm_vec_mag_quick(&objp->phys_info.vel) / 70.0f;
2691                                 if (frand() > (turret_stealth_find_chance + speed_mod)) {
2692                                         try_anyway = TRUE;
2693                                 }
2694                         }
2695
2696                         if (!try_anyway) {
2697                                 return;
2698                         }
2699                 }
2700
2701         } else {
2702                 shipp = NULL;
2703         }
2704
2705         // modify dist for BIG|HUGE, getting closest point on bbox, if not inside
2706         dist = vm_vec_dist_quick(eeo->tpos, &objp->pos) - objp->radius;
2707         if (dist < 0.0f) {
2708                 dist = 0.0f;
2709         }
2710
2711         // check if object is a bomb attacking the turret parent
2712         // check if bomb is homing on the turret parent ship
2713         if (objp->type == OBJ_WEAPON) {
2714                 if ( Weapons[objp->instance].homing_object == &Objects[eeo->turret_parent_objnum] ) {
2715                         if ( dist < eeo->nearest_homing_bomb_dist ) {
2716                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2717                                         eeo->nearest_homing_bomb_dist = dist;
2718                                         eeo->nearest_homing_bomb_objnum = OBJ_INDEX(objp);
2719                                 }
2720                         }
2721                 // if not homing, check if bomb is flying towards ship
2722                 } else if ( bomb_headed_towards_ship(objp, &Objects[eeo->turret_parent_objnum]) ) {
2723                         if ( dist < eeo->nearest_bomb_dist ) {
2724                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2725                                         eeo->nearest_bomb_dist = dist;
2726                                         eeo->nearest_bomb_objnum = OBJ_INDEX(objp);
2727                                 }
2728                         }
2729                 }
2730         } // end weapon section
2731
2732         // maybe recalculate dist for big or huge ship
2733 //      if (shipp && (Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2734 //              fvi_ray_boundingbox(min, max, start, direction, hit);
2735 //              dist = vm_vec_dist_quick(hit, tvec);
2736 //      }
2737
2738         // check for nearest attcker
2739         if ( (shipp) && (dist < eeo->weapon_travel_dist) ) {
2740                 ai_info *aip = &Ai_info[shipp->ai_index];
2741
2742                 // modify distance based on number of turrets from my ship attacking enemy (add 10% per turret)
2743                 // dist *= (num_enemies_attacking(OBJ_INDEX(objp))+2)/2;        //      prevents lots of ships from attacking same target
2744                 int num_att_turrets = num_turrets_attacking(turret_parent_obj, OBJ_INDEX(objp));
2745                 dist *= (1.0f + 0.1f*num_att_turrets);
2746
2747                 // return if we're over the cap
2748                 int max_turrets = 3 + Game_skill_level * Game_skill_level;
2749                 if (num_att_turrets > max_turrets) {
2750                         return;
2751                 }
2752
2753                 // modify distance based on lethality of objp to my ship
2754                 float active_lethality = aip->lethality;
2755                 if (objp->flags & OF_PLAYER_SHIP) {
2756                         active_lethality += Player_lethality_bump[Game_skill_level];
2757                 }
2758
2759                 dist /= (1.0f + 0.01f*Lethality_range_const*active_lethality);
2760
2761                 // Make level 2 tagged ships more likely to be targeted
2762                 if (shipp->level2_tag_left > 0.0f) {
2763                         dist *= 0.3f;
2764                 }
2765
2766                 // check if objp is targeting the turret's ship, or if objp has just hit the turret's ship
2767                 if ( aip->target_objnum == eeo->turret_parent_objnum || aip->last_objsig_hit == Objects[eeo->turret_parent_objnum].signature ) {
2768                         // A turret will always target a ship that is attacking itself... self-preservation!
2769                         if ( aip->targeted_subsys == eeo->turret_subsys ) {
2770                                 dist *= 0.5f;   // highest priority
2771                         }
2772                 }
2773
2774                 // maybe update nearest attacker
2775                 if ( dist < eeo->nearest_attacker_dist ) {
2776                         if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2777                                 // 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));
2778                                 eeo->nearest_attacker_dist = dist;
2779                                 eeo->nearest_attacker_objnum = OBJ_INDEX(objp);
2780                         }
2781                 }
2782         } // end ship section
2783
2784 #ifdef MAKE_FS1
2785         // check if object is an asteroid attacking the turret parent - taylor
2786         if (objp->type == OBJ_ASTEROID) {
2787                 if ( eeo->turret_parent_objnum == asteroid_collide_objnum(objp) ) {
2788                         // give priority to the closest asteroid *impact* (ms intervals)
2789                         dist *= 0.9f + (0.01f * asteroid_time_to_impact(objp));
2790
2791                         if (dist < eeo->nearest_dist ) {
2792                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2793                                         eeo->nearest_dist = dist;
2794                                         eeo->nearest_objnum = OBJ_INDEX(objp);
2795                                 }
2796                         }
2797                 }
2798         } // end asteroid selection
2799 #endif
2800 }
2801
2802 // return 0 only if objnum is beam protected and turret is beam turret
2803 int is_target_beam_valid(ship_subsys *turret_subsys, int objnum)
2804 {
2805         // check if turret has beam weapon
2806         model_subsystem *tp = turret_subsys->system_info;
2807
2808         if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2809                 if (Objects[objnum].flags & OF_BEAM_PROTECTED) {
2810                         return 0;
2811                 }
2812
2813                 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_HUGE) {
2814                         if (Objects[objnum].type == OBJ_SHIP && !(Ship_info[Ships[Objects[objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
2815                                 return 0;
2816                         }
2817                 }
2818         }
2819
2820         return 1;
2821 }
2822
2823
2824 //      Given an object and an enemy team, return the index of the nearest enemy object.
2825 //
2826 // input:
2827 //                              turret_parent_objnum    => parent objnum for the turret
2828 //                              turret_subsys                   => pointer to system_info for the turret subsystem
2829 //                              enemy_team_mask         => OR'ed TEAM_ flags for the enemy of the turret parent ship
2830 //                              tpos                                            => position of turret (world coords)
2831 //                              tvec                                            => forward vector of turret (world coords)
2832 //                              current_enemy                   =>      objnum of current turret target
2833 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)
2834 {
2835         float                                   weapon_travel_dist;
2836         int                                     weapon_system_ok;
2837         object                          *objp;
2838         model_subsystem *tp;
2839         eval_enemy_obj_struct eeo;
2840
2841         // list of stuff to go thru
2842         ship_obj                *so;
2843         missile_obj *mo;
2844
2845         tp = turret_subsys->system_info;
2846         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);
2847
2848         // Set flag based on strength of weapons subsystem.  If weapons subsystem is destroyed, don't let turrets fire at bombs
2849         weapon_system_ok = 0;
2850         if ( ship_get_subsystem_strength( &Ships[Objects[turret_parent_objnum].instance], SUBSYSTEM_WEAPONS ) > 0 ) {
2851                 weapon_system_ok = 1;
2852         }
2853
2854         // Initialize eeo struct.
2855         eeo.turret_parent_objnum = turret_parent_objnum;
2856         eeo.weapon_system_ok = weapon_system_ok;
2857         eeo.weapon_travel_dist = weapon_travel_dist;
2858         eeo.big_only_flag = big_only_flag;
2859         eeo.enemy_team_mask = enemy_team_mask;
2860         eeo.current_enemy = current_enemy;
2861         eeo.tpos = tpos;
2862         eeo.tvec = tvec;
2863         eeo.turret_subsys = turret_subsys;
2864
2865         eeo.nearest_attacker_dist = 99999.0f;
2866         eeo.nearest_attacker_objnum = -1;
2867
2868         eeo.nearest_homing_bomb_dist = 99999.0f;
2869         eeo.nearest_homing_bomb_objnum = -1;
2870
2871         eeo.nearest_bomb_dist = 99999.0f;
2872         eeo.nearest_bomb_objnum = -1;
2873
2874         eeo.nearest_dist = 99999.0f;
2875         eeo.nearest_objnum = -1;
2876
2877
2878         // Missile_obj_list
2879         for( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
2880                 objp = &Objects[mo->objnum];
2881                 evaluate_obj_as_target(objp, &eeo);
2882         }
2883         // highest priority
2884         if ( eeo.nearest_homing_bomb_objnum != -1 ) {                                   // highest priority is an incoming homing bomb
2885                 return eeo.nearest_homing_bomb_objnum;
2886         } else if ( eeo.nearest_bomb_objnum != -1 ) {                                   // next highest priority is an incoming dumbfire bomb
2887                 return eeo.nearest_bomb_objnum;
2888         }
2889
2890
2891         // Ship_used_list
2892         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2893                 objp = &Objects[so->objnum];
2894                 evaluate_obj_as_target(objp, &eeo);
2895         }
2896
2897         Assert(eeo.nearest_attacker_objnum < 0 || is_target_beam_valid(turret_subsys, eeo.nearest_attacker_objnum));
2898                 // next highest priority is attacking ship
2899         if ( eeo.nearest_attacker_objnum != -1 ) {                      // next highest priority is an attacking ship
2900                 return eeo.nearest_attacker_objnum;
2901          }
2902
2903
2904 #if !(defined(FS2_DEMO) || defined(FS1_DEMO))
2905                 asteroid_obj *ao;
2906         // Asteroid_obj_list
2907         for( ao = GET_FIRST(&Asteroid_obj_list); ao != END_OF_LIST(&Asteroid_obj_list); ao = GET_NEXT(ao) ) {
2908                 objp = &Objects[ao->objnum];
2909                 evaluate_obj_as_target(objp, &eeo);
2910         }
2911 #endif
2912
2913         return eeo.nearest_objnum;                                                                              // lowest priority is the closest enemy objnum
2914 }
2915
2916 //      Return timestamp until a ship can find an enemy.
2917 //      Yes, no parameters.  Based solely on skill level.
2918 int get_enemy_timestamp()
2919 {
2920         return (NUM_SKILL_LEVELS - Game_skill_level) * ( (myrand() % 500) + 500);
2921 }
2922
2923 // -------------------------------------------------------------------
2924 //      Return objnum if enemy found, else return -1;
2925 //      Don't attack a ship that already has at least max_attackers attacking it.
2926 int find_enemy(int objnum, float range, int max_attackers)
2927 {
2928         int     enemy_team_mask;
2929
2930         enemy_team_mask = get_enemy_team_mask(objnum);
2931
2932         //      if target_objnum != -1, use that as goal.
2933         ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2934         if (timestamp_elapsed(aip->choose_enemy_timestamp)) {
2935                 aip->choose_enemy_timestamp = timestamp(get_enemy_timestamp());
2936                 if (aip->target_objnum != -1) {
2937                         int     target_objnum = aip->target_objnum;
2938
2939                         // DKA don't undo object as target in nebula missions.
2940                         // This could cause attack on ship on fringe on nebula to stop if attackee moves our of nebula range.  (BAD)
2941                         if ( (Objects[target_objnum].signature == aip->target_signature) ) {
2942                                 if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
2943                                         if (!(Objects[target_objnum].flags & OF_PROTECTED)) {
2944                                                 // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
2945                                                 return target_objnum;
2946                                         }
2947                                 }
2948                         } else {
2949                                 aip->target_objnum = -1;
2950                                 aip->target_signature = -1;
2951                         }
2952                 }
2953                 return get_nearest_objnum(objnum, enemy_team_mask, aip->enemy_wing, range, max_attackers);
2954         } else {
2955                 aip->target_objnum = -1;
2956                 aip->target_signature = -1;
2957                 return -1;
2958         }
2959
2960 }
2961
2962 int Use_parent_target = 0;
2963 DCF_BOOL(use_parent_target, Use_parent_target)
2964
2965 // -------------------------------------------------------------------
2966 //      Return objnum if enemy found, else return -1;
2967 //
2968 // input:
2969 //                              turret_subsys   => pointer to turret subsystem
2970 //                              objnum                  => parent objnum for the turret
2971 //                              tpos                            => position of turret (world coords)
2972 //                              tvec                            => forward vector of turret (world coords)
2973 //                              current_enemy   =>      objnum of current turret target
2974 int find_turret_enemy(ship_subsys *turret_subsys, int objnum, vector *tpos, vector *tvec, int current_enemy, float fov, int big_only_flag = 0)
2975 {
2976         int                                     enemy_team_mask, enemy_objnum;
2977         model_subsystem *tp;
2978         ship_info                       *sip;
2979
2980         tp = turret_subsys->system_info;
2981         enemy_team_mask = get_enemy_team_mask(objnum);
2982
2983         //      If a small ship and target_objnum != -1, use that as goal.
2984         ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2985         sip = &Ship_info[Ships[Objects[objnum].instance].ship_info_index];
2986
2987         if ((sip->flags & SIF_SMALL_SHIP) && (aip->target_objnum != -1)) {
2988                 int target_objnum = aip->target_objnum;
2989
2990                 if (Objects[target_objnum].signature == aip->target_signature) {
2991                         if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
2992                                 if ( !(Objects[target_objnum].flags & OF_PROTECTED) ) {         // check this flag as well.
2993                                         // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
2994                                         return target_objnum;
2995                                 }
2996                         }
2997                 } else {
2998                         aip->target_objnum = -1;
2999                         aip->target_signature = -1;
3000                 }
3001         // Not small or small with target objnum
3002         } else {
3003                 // maybe use aip->target_objnum as next target
3004                 if ((frand() < 0.8f) && (aip->target_objnum != -1) && Use_parent_target) {
3005
3006                         //check if aip->target_objnum is valid target
3007                         int target_flags = Objects[aip->target_objnum].flags;
3008                         if ( target_flags & OF_PROTECTED ) {
3009                                 // AL 2-27-98: why is a protected ship being targeted?
3010                                 set_target_objnum(aip, -1);
3011                                 return -1;
3012                         }
3013
3014                         // maybe use ship target_objnum if valid for turret
3015                         // check for beam weapon and beam protected
3016                         if ( !((target_flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) ) {
3017                                 if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
3018                                         // check for huge weapon and huge ship
3019                                         if ( !big_only_flag || (Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
3020                                                 // check for tagged only and tagged ship
3021                                                 if ( (turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && ship_is_tagged(&Objects[aip->target_objnum]) ) {
3022                                                         // select new target if aip->target_objnum is out of field of view
3023                                                         vector v2e;
3024                                                         float dot, dist;
3025                                                         dist = vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, tpos);
3026                                                         dot = vm_vec_dot(&v2e, tvec);
3027                                                         //      MODIFY FOR ATTACKING BIG SHIP
3028                                                         // dot += (0.5f * Objects[aip->target_objnum].radius / dist);
3029                                                         if (dot > fov) {
3030                                                                 return aip->target_objnum;
3031                                                         }
3032                                                 }
3033                                         }
3034                                 }
3035                         }
3036                 }
3037         }
3038
3039         enemy_objnum = get_nearest_turret_objnum(objnum, turret_subsys, enemy_team_mask, tpos, tvec, current_enemy, big_only_flag);
3040         if ( enemy_objnum >= 0 ) {
3041                 Assert( !((Objects[enemy_objnum].flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) );
3042                 if ( Objects[enemy_objnum].flags & OF_PROTECTED ) {
3043                         Int3();
3044                         enemy_objnum = aip->target_objnum;
3045                 }
3046         }
3047
3048         return enemy_objnum;
3049 }
3050
3051 //      If issued an order to a ship that's awaiting repair, abort that process.
3052 //      However, do not abort process for an object that is currently being repaired -- let it finish.
3053 void ai_set_goal_maybe_abort_dock(object *objp, ai_info *aip)
3054 {
3055         if (aip->ai_flags & AIF_AWAITING_REPAIR) {
3056                 object  *repair_obj;
3057
3058                 if (aip->dock_objnum == -1) {
3059                         repair_obj = NULL;
3060                 } else {
3061                         repair_obj = &Objects[aip->dock_objnum];
3062                 }
3063                 ai_do_objects_repairing_stuff( objp, repair_obj, REPAIR_INFO_ABORT );
3064         }
3065         aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);    //      Might request again after 30 seconds.
3066 }
3067
3068 void force_avoid_player_check(object *objp, ai_info *aip)
3069 {
3070         if (Ships[objp->instance].team == Player_ship->team){
3071                 aip->avoid_check_timestamp = timestamp(0);              //      Force a check for collision next frame.
3072         }
3073 }
3074
3075 //      --------------------------------------------------------------------------
3076 //      Set *attacked as object to attack for object *attacker
3077 //      If attacked == NULL, then attack any enemy object.
3078 //      Attack point *rel_pos on object.  This is for supporting attacking subsystems.
3079 void ai_attack_object(object *attacker, object *attacked, int priority, ship_subsys *ssp)
3080 {
3081         ai_info *aip;
3082
3083         Assert(attacker != NULL);
3084         Assert(attacker->instance != -1);
3085         Assert(Ships[attacker->instance].ai_index != -1);
3086
3087         aip = &Ai_info[Ships[attacker->instance].ai_index];
3088         force_avoid_player_check(attacker, aip);
3089
3090         aip->ok_to_target_timestamp = timestamp(0);             //      Guarantee we can target.
3091
3092 //      if (!strnicmp(Ships[attacker->instance].ship_name, NOX("Kami"), 4)) {
3093 //              aip->ai_flags |= AIF_KAMIKAZE;
3094 //              aip->ai_flags |= AIF_NO_DYNAMIC;
3095 //      }
3096
3097         if (attacker == attacked) {
3098                 Int3();         //      Bogus!  Who tried to get me to attack myself!  Trace out and fix!
3099                 return;
3100         }
3101
3102         //      Only set to chase if a fighter or bomber, otherwise just return.
3103         if (!(Ship_info[Ships[attacker->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
3104 //              nprintf(("AI","Note: AI ship %s refusing to set AI mode to AIM_CHASE\n", Ships[attacker->instance].ship_name));
3105 //              return;
3106                 nprintf(("AI", "AI ship %s is large ship ordered to attack %s\n", Ships[attacker->instance].ship_name, Ships[attacked->instance].ship_name));
3107         }
3108
3109         //      This is how "engage enemy" gets processed
3110         if (attacked == NULL) {
3111                 aip->choose_enemy_timestamp = timestamp(0);
3112                 // nebula safe
3113                 set_target_objnum(aip, find_enemy(attacker-Objects, 99999.9f, 4));
3114         } else {
3115                 // check if we can see atacked in nebula
3116                 if (aip->target_objnum != attacked - Objects) {
3117                         aip->aspect_locked_time = 0.0f;
3118                 }
3119                 set_target_objnum(aip, attacked - Objects);
3120         }
3121
3122         ai_set_goal_maybe_abort_dock(attacker, aip);
3123         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);     //      No dynamic targeting for 7 seconds.
3124
3125         if (is_ignore_object(aip, aip->target_objnum)) {
3126                 aip->ignore_objnum = UNUSED_OBJNUM;
3127         }
3128
3129         aip->mode = AIM_CHASE;
3130         aip->submode = SM_ATTACK;       // AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
3131                                                                                 //                                       for AIM_CHASE... it may have been not set correctly here
3132         if (ssp == NULL) {
3133                 set_targeted_subsys(aip, NULL, -1);
3134                 if (aip->target_objnum != -1) {
3135                         //nprintf(("AI", "Unprotecting ship %s\n", Ships[Objects[aip->target_objnum].instance].ship_name));
3136                         Objects[aip->target_objnum].flags &= ~OF_PROTECTED;     //      If ship had been protected, unprotect it.
3137                 }
3138         } else {
3139                 Int3(); //      Not supported yet!
3140         }
3141 }
3142
3143 //      --------------------------------------------------------------------------
3144 //      Set *attacked as object to attack for object *attacker
3145 //      Attack point *rel_pos on object.  This is for supporting attacking subsystems.
3146 void ai_attack_wing(object *attacker, int wingnum, int priority)
3147 {
3148         ai_info *aip;
3149
3150         Assert(attacker != NULL);
3151         Assert(attacker->instance != -1);
3152         Assert(Ships[attacker->instance].ai_index != -1);
3153
3154         aip = &Ai_info[Ships[attacker->instance].ai_index];
3155
3156         aip->enemy_wing = wingnum;
3157         aip->mode = AIM_CHASE;
3158         aip->submode = SM_ATTACK;       // AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
3159                                                                                 //                                       for AIM_CHASE... it may have been not set correctly here
3160
3161         aip->ok_to_target_timestamp = timestamp(0);             //      Guarantee we can target.
3162
3163         int count = Wings[wingnum].current_count;
3164         if (count > 0) {
3165                 int     index;
3166
3167                 index = (int) (frand() * count);
3168
3169                 if (index >= count)
3170                         index = 0;
3171
3172                 set_target_objnum(aip, Ships[Wings[wingnum].ship_index[index]].objnum);
3173
3174                 ai_set_goal_maybe_abort_dock(attacker, aip);
3175                 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);     //      No dynamic targeting for 7 seconds.
3176         }
3177 }
3178
3179 //      --------------------------------------------------------------------------
3180 //      Set *evaded as object for *evader to evade.
3181 void ai_evade_object(object *evader, object *evaded, int priority)
3182 {
3183         ai_info *aip;
3184
3185         Assert(evader != NULL);
3186         Assert(evaded != NULL);
3187         Assert(evader->instance != -1);
3188         Assert(Ships[evader->instance].ai_index != -1);
3189
3190         if (evaded == evader) {
3191                 Int3(); //      Bogus!  Who tried to get me to evade myself!  Trace out and fix!
3192                 return;
3193         }
3194
3195         aip = &Ai_info[Ships[evader->instance].ai_index];
3196
3197         set_target_objnum(aip, evaded - Objects);
3198         aip->mode = AIM_EVADE;
3199
3200 }
3201
3202 //      Ignore some object without changing mode.
3203 void ai_ignore_object(object *ignorer, object *ignored, int priority)
3204 {
3205         ai_info *aip;
3206
3207         Assert(ignorer != NULL);
3208         Assert(ignored != NULL);
3209         Assert(ignorer->instance != -1);
3210         Assert(Ships[ignorer->instance].ai_index != -1);
3211         Assert(ignorer != ignored);
3212
3213         aip = &Ai_info[Ships[ignorer->instance].ai_index];
3214
3215         //      MK, 5/17/98, removing ignoring of wings.
3216         //      It's too confusing.  It often causes mysterious behavior in which fighters unexpectedly refuse to attack anything.
3217 /*      if (Ships[ignored->instance].wingnum > -1) {
3218                 int wingnum, i;
3219
3220                 wingnum = Ships[ignored->instance].wingnum;
3221                 aip->ignore_objnum = -(wingnum+1);
3222                 // set protected bit for each ship in a wing
3223                 //      MK, 4/23/98: Only set for fighters if they are the original "ignored" object
3224                 for (i = 0; i < Wings[wingnum].current_count; i++ ) {
3225                         object  *objp;
3226
3227                         objp = &Objects[Ships[Wings[wingnum].ship_index[i]].objnum];
3228                         if (objp != ignored) {
3229                                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))
3230                                         continue;
3231                         }
3232
3233                         Objects[Ships[Wings[wingnum].ship_index[i]].objnum].flags |= OF_PROTECTED;
3234                 }
3235
3236         } else {
3237         */ {
3238                 aip->ignore_objnum = ignored - Objects;
3239                 aip->ignore_signature = ignored->signature;
3240                 aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3241                 ignored->flags |= OF_PROTECTED;                                 // set protected bit of ignored ship.
3242         }
3243
3244 }
3245
3246 //      Ignore some object without changing mode.
3247 void ai_ignore_wing(object *ignorer, int wingnum, int priority)
3248 {
3249         ai_info *aip;
3250
3251         Assert(ignorer != NULL);
3252         Assert(ignorer->instance != -1);
3253         Assert(Ships[ignorer->instance].ai_index != -1);
3254         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
3255
3256         aip = &Ai_info[Ships[ignorer->instance].ai_index];
3257
3258         aip->ignore_objnum = -(wingnum +1);
3259         aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3260 }
3261
3262
3263 //      Add a path point in the global buffer Path_points.
3264 //      modify_index = index in Path_points at which to store path point.
3265 //      If modify_index == -1, then create a new point.
3266 //      If a new point is created (ie, modify_index == -1), then Ppfp is updated.
3267 void add_path_point(vector *pos, int path_num, int path_index, int modify_index)
3268 {
3269         pnode   *pnp;
3270
3271         if (modify_index == -1) {
3272                 Assert(Ppfp-Path_points < MAX_PATH_POINTS-1);
3273                 pnp = Ppfp;
3274                 Ppfp++;
3275         } else {
3276                 Assert((modify_index >= 0) && (modify_index < MAX_PATH_POINTS-1));
3277                 pnp = &Path_points[modify_index];
3278         }
3279
3280         pnp->pos = *pos;
3281         pnp->path_num = path_num;
3282         pnp->path_index = path_index;
3283 }
3284
3285 //      Given two points on a sphere, the center of the sphere and the radius, return a
3286 //      point on the vector through the midpoint of the chord on the sphere.
3287 void bisect_chord(vector *p0, vector *p1, vector *centerp, float radius)
3288 {
3289         vector  tvec;
3290         vector  new_pnt;
3291
3292         vm_vec_add(&tvec, p0, p1);
3293         vm_vec_sub2(&tvec, centerp);
3294         vm_vec_sub2(&tvec, centerp);
3295         if (vm_vec_mag_quick(&tvec) < 0.1f) {
3296                 vm_vec_sub(&tvec, p0, p1);
3297                 if (fl_abs(tvec.xyz.x) <= fl_abs(tvec.xyz.z)){
3298                         tvec.xyz.x = -tvec.xyz.z;
3299                 } else {
3300                         tvec.xyz.y = -tvec.xyz.x;
3301                 }
3302         }
3303
3304         vm_vec_normalize(&tvec);
3305         vm_vec_scale(&tvec, radius);
3306         vm_vec_add(&new_pnt, centerp, &tvec);
3307
3308         add_path_point(&new_pnt, -1, -1, -1);
3309 }
3310                         
3311 //      Create a path from the current position to a goal position.
3312 //      The current position is in the current object and the goal position is
3313 //      in the goal object.
3314 //      It is ok to intersect the current object, but not the goal object.
3315 //      This function is useful for creating a path to an initial point near a large
3316 //      object.
3317 //
3318 // input:       subsys_path:    optional param (default 0), indicates this is a path to a subsystem
3319 void create_path_to_point(vector *curpos, vector *goalpos, object *curobjp, object *goalobjp, int subsys_path)
3320 {
3321         //      If can't cast vector to goalpos, then create an intermediate point.
3322         if (pp_collide(curpos, goalpos, goalobjp, curobjp->radius)) {
3323                 vector  tan1;
3324                 float           radius;
3325
3326                 // If this is a path to a subsystem, use SUBSYS_PATH_DIST as the radius for the object you are
3327                 // trying to avoid.  This is needed since subsystem paths extend out to SUBSYS_PATH_DIST, and we
3328                 // want ships to reach their path destination without flying to points that sit on the radius of
3329                 // a small ship
3330                 radius = goalobjp->radius;
3331                 if (subsys_path) {
3332                         if ( SUBSYS_PATH_DIST > goalobjp->radius ) {
3333                                 radius = SUBSYS_PATH_DIST;
3334                         }
3335                 }
3336
3337                 //      The intermediate point is at the intersection of:
3338                 //              tangent to *goalobjp sphere at point *goalpos
3339                 //              tangent to *goalobjp sphere through point *curpos in plane defined by *curpos, *goalpos, goalobjp->pos
3340                 //      Note, there are two tangents through *curpos, unless *curpos is on the
3341                 //      sphere.  The tangent that causes the nearer intersection (to *goalpos) is chosen.
3342                 get_tangent_point(&tan1, curpos, &goalobjp->pos, goalpos, radius);
3343
3344                 //      If we can't reach tan1 from curpos, insert a new point.
3345                 if (pp_collide(&tan1, curpos, goalobjp, curobjp->radius))
3346                         bisect_chord(curpos, &tan1, &goalobjp->pos, radius);
3347
3348                 add_path_point(&tan1, -1, -1, -1);
3349
3350                 //      If we can't reach goalpos from tan1, insert a new point.
3351                 if (pp_collide(goalpos, &tan1, goalobjp, curobjp->radius))
3352                         bisect_chord(goalpos, &tan1, &goalobjp->pos, radius);
3353         }
3354
3355 }
3356
3357 //      Given an object and a model path, globalize the points on the model
3358 //      and copy into the global path list.
3359 //      If pnp != NULL, then modify, in place, the path points.  This is used to create new
3360 //      globalized points when the base object has moved.
3361 // input:       randomize_pnt   => optional parameter (default value -1), add random vector in sphere to this path point
3362 void copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt)
3363 {
3364         matrix  m;
3365         int             i;
3366         vector  v1;
3367         int             pp_index;               //      index in Path_points at which to store point, if this is a modify-in-place (pnp ! NULL)
3368         int             start_index, finish_index;
3369         
3370         // nprintf(("AI", "Creating path for object %s in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
3371         
3372         //      Initialize pp_index.
3373         //      If pnp == NULL, that means we're creating new points.  If not NULL, then modify in place.
3374         if (pnp == NULL)
3375                 pp_index = -1;                  //      This tells add_path_point to create a new point.
3376         else
3377                 pp_index = 0;                   //      pp_index will get assigned to index in Path_points to reuse.
3378
3379         vm_copy_transpose_matrix(&m, &objp->orient);
3380
3381         if (dir == 1) {
3382                 start_index = 0;
3383                 finish_index = min(count, mp->nverts);
3384         } else {
3385                 Assert(dir == -1);      //      direction must be up by 1 or down by 1 and it's neither!
3386                 start_index = mp->nverts-1;
3387                 finish_index = max(-1, mp->nverts-1-count);
3388         }
3389
3390         int offset = 0;
3391         for (i=start_index; i != finish_index; i += dir) {
3392                 //      Globalize the point.
3393                 vm_vec_rotate(&v1, &mp->verts[i].pos, &m);
3394                 vm_vec_add2(&v1, &objp->pos);
3395
3396                 if ( randomize_pnt == i ) {
3397                         vector v_rand;
3398                         static_randvec(OBJ_INDEX(objp), &v_rand);
3399                         vm_vec_scale(&v_rand, 30.0f);
3400                         vm_vec_add2(&v1, &v_rand);
3401                 }
3402
3403                 if (pp_index != -1)
3404                         pp_index = pnp-Path_points + offset;
3405
3406                 add_path_point(&v1, path_num, i, pp_index);
3407                 offset++;
3408         }
3409 }
3410
3411
3412 //      For pl_objp, create a path along path path_num into mobjp.
3413 //      The tricky part of this problem is creating the entry to the first point on the
3414 //      predefined path.  The points on this entry path are based on the location of Pl_objp
3415 //      relative to the start of the path.
3416 //
3417 // input:
3418 //                              subsys_path:    optional param (default 0), indicating this is a path to a subsystem
3419 void create_model_path(object *pl_objp, object *mobjp, int path_num, int subsys_path)
3420 {       
3421         ship                    *shipp = &Ships[pl_objp->instance];
3422         ai_info         *aip = &Ai_info[shipp->ai_index];
3423
3424         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3425         polymodel       *pm = model_get(osip->modelnum);
3426         int                     num_points;
3427         model_path      *mp;
3428         pnode                   *ppfp_start = Ppfp;
3429         matrix          m;
3430         vector          gp0;
3431
3432         Assert(path_num >= 0);
3433
3434         //      Do garbage collection if necessary.
3435         if (Ppfp-Path_points + 64 > MAX_PATH_POINTS) {
3436                 garbage_collect_path_points();
3437                 ppfp_start = Ppfp;
3438         }
3439
3440         aip->path_start = Ppfp - Path_points;
3441         Assert(path_num < pm->n_paths);
3442         
3443         mp = &pm->paths[path_num];
3444         num_points = mp->nverts;
3445
3446         Assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
3447
3448         vm_copy_transpose_matrix(&m, &mobjp->orient);
3449         vm_vec_rotate(&gp0, &mp->verts[0].pos, &m);
3450         vm_vec_add2(&gp0, &mobjp->pos);
3451
3452         if (pp_collide(&pl_objp->pos, &gp0, mobjp, pl_objp->radius)) {
3453                 vector  perim_point1;
3454                 vector  perim_point2;
3455
3456                 perim_point2 = pl_objp->pos;
3457                 
3458                 //      If object that wants to dock is inside bounding sphere of object it wants to dock with, make it fly out.
3459                 //      Assume it can fly "straight" out to the bounding sphere.
3460                 if (vm_vec_dist_quick(&pl_objp->pos, &mobjp->pos) < mobjp->radius) {
3461                         project_point_to_perimeter(&perim_point2, &mobjp->pos, mobjp->radius, &pl_objp->pos);
3462                         add_path_point(&perim_point2, path_num, -1, -1);
3463                 }
3464
3465                 //      If last point on pre-defined path is inside bounding sphere, create a new point on the surface of the sphere.
3466                 if (vm_vec_dist_quick(&mobjp->pos, &gp0) < mobjp->radius) {
3467                         project_point_to_perimeter(&perim_point1, &mobjp->pos, mobjp->radius, &gp0);
3468                         create_path_to_point(&perim_point2, &perim_point1, pl_objp, mobjp, subsys_path);
3469                         add_path_point(&perim_point1, path_num, -1, -1);
3470                 } else {                //      The predefined path extends outside the sphere.  Create path to that point.
3471                         create_path_to_point(&perim_point2, &gp0, pl_objp, mobjp, subsys_path);
3472                 }
3473         }
3474
3475         // AL 12-31-97: If following a subsystem path, add random vector to second last path point
3476         if ( subsys_path ) {
3477                 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL, mp->nverts-2);
3478         } else {
3479                 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL);
3480         }
3481
3482         aip->path_cur = aip->path_start;
3483         aip->path_dir = PD_FORWARD;
3484         aip->path_objnum = mobjp-Objects;
3485         aip->mp_index = path_num;
3486         aip->path_length = Ppfp - ppfp_start;
3487         aip->path_next_check_time = timestamp(1);
3488
3489         aip->path_goal_obj_hash = create_object_hash(&Objects[aip->path_objnum]);
3490
3491         aip->path_next_create_time = timestamp(1000);   //      OK to try to create one second later
3492         aip->path_create_pos = pl_objp->pos;
3493         aip->path_create_orient = pl_objp->orient;
3494
3495         aip->ai_flags &= ~AIF_USE_EXIT_PATH;                    // ensure this flag is cleared
3496 }
3497
3498 //      For pl_objp, create a path along path path_num into mobjp.
3499 //      The tricky part of this problem is creating the entry to the first point on the
3500 //      predefined path.  The points on this entry path are based on the location of pl_objp
3501 //      relative to the start of the path.
3502 void create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count)
3503 {       
3504         ship                    *shipp = &Ships[pl_objp->instance];
3505         ai_info         *aip = &Ai_info[shipp->ai_index];
3506
3507         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3508         polymodel       *pm = model_get(osip->modelnum);
3509         int                     num_points;
3510         model_path      *mp;
3511         pnode                   *ppfp_start = Ppfp;
3512
3513         aip->path_start = Ppfp - Path_points;
3514         Assert(path_num < pm->n_paths);
3515         
3516         mp = &pm->paths[path_num];
3517         num_points = mp->nverts;
3518
3519         Assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
3520
3521         copy_xlate_model_path_points(mobjp, mp, -1, count, path_num, NULL);
3522
3523         aip->path_cur = aip->path_start;
3524         aip->path_dir = PD_FORWARD;
3525         aip->path_objnum = mobjp-Objects;
3526         aip->mp_index = path_num;
3527         aip->path_length = Ppfp - ppfp_start;
3528         aip->path_next_check_time = timestamp(1);
3529
3530         aip->ai_flags |= AIF_USE_EXIT_PATH;             // mark as exit path, referenced in maybe
3531 }
3532
3533 //      Return true if the vector from curpos to goalpos intersects with any ship other than the ignore objects.
3534 //      Calls pp_collide
3535 int pp_collide_any(vector *curpos, vector *goalpos, float radius, object *ignore_objp1, object *ignore_objp2, int big_only_flag)
3536 {
3537         ship_obj        *so;    
3538
3539         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
3540                 object *objp = &Objects[so->objnum];
3541
3542                 if (big_only_flag) {
3543                         if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
3544                                 continue;
3545                 }
3546
3547                 if ((objp != ignore_objp1) && (objp != ignore_objp2)) {
3548                         if (pp_collide(curpos, goalpos, objp, radius))
3549                                 return OBJ_INDEX(objp);
3550                 }
3551         }
3552
3553         return -1;
3554 }
3555
3556 //      Used to create docking paths and other pre-defined paths through ships.
3557 //      Creates a path in absolute space.
3558 //      Create a path into the object objnum.
3559 //
3560 // input:
3561 //      pl_objp:                        object that will use the path
3562 //      objnum:                 Object to find path to.
3563 //      path_num:               model path index to use
3564 //      exit_flag:              true means this is an exit path in the model
3565 // subsys_path: optional param (default 0) that indicates this is a path to a subsystem
3566 //      Exit:
3567 //      ai_info struct in Pl_objp gets stuffed with information to enable Pl_objp to fly the path.
3568 void ai_find_path(object *pl_objp, int objnum, int path_num, int exit_flag, int subsys_path)
3569 {
3570         ai_info *aip = &Ai_info[Ships[pl_objp->instance].ai_index];
3571
3572         Assert(path_num >= 0);
3573
3574         //      This is test code, find an object with paths.
3575         if (objnum != -1) {
3576                 object  *objp = &Objects[objnum];
3577
3578                 if (objp->type == OBJ_SHIP) {
3579                         polymodel *pm;
3580
3581                         ship    *shipp = &Ships[objp->instance];
3582                         pm = model_get( shipp->modelnum );
3583                         Assert(pm->n_paths > path_num);
3584                         aip->goal_objnum = objp-Objects;
3585                         aip->goal_signature = objp->signature;
3586                         if (exit_flag)
3587                                 create_model_exit_path(pl_objp, objp, path_num);
3588                         else
3589                                 create_model_path(pl_objp, objp, path_num, subsys_path);
3590                         return;
3591                 }
3592
3593         }
3594 }
3595
3596 extern int vector_object_collision(vector *start_pos, vector *end_pos, object *objp, float radius_scale);
3597
3598 //      Maybe make *objp avoid a player object.
3599 //      For now, 4/6/98, only check Player_obj.
3600 //      If player collision would occur, set AIF_AVOIDING_SMALL_SHIP bit in ai_flags.
3601 //      Set aip->avoid_goal_point
3602 int maybe_avoid_player(object *objp, vector *goal_pos)
3603 {
3604         ai_info *aip;
3605         vector  cur_pos, new_goal_pos;
3606         object  *player_objp;
3607         vector  n_vec_to_goal, n_vec_to_player;
3608
3609         aip = &Ai_info[Ships[objp->instance].ai_index];
3610
3611         if (!timestamp_elapsed(aip->avoid_check_timestamp))
3612                 return 0;
3613
3614         player_objp = Player_obj;
3615
3616         float   speed_time;
3617
3618         //      How far two ships could be apart and still collide within one second.
3619         speed_time = player_objp->phys_info.speed + objp->phys_info.speed;
3620
3621         float   obj_obj_dist;
3622
3623         obj_obj_dist = vm_vec_dist_quick(&player_objp->pos, &objp->pos);
3624
3625         if (obj_obj_dist > speed_time*2.0f)
3626                 return 0;
3627
3628         cur_pos = objp->pos;
3629
3630         new_goal_pos = *goal_pos;
3631
3632         float dist = vm_vec_normalized_dir(&n_vec_to_goal, goal_pos, &objp->pos);
3633         vm_vec_normalized_dir(&n_vec_to_player, &player_objp->pos, &objp->pos);
3634
3635         if (dist > speed_time*2.0f) {
3636                 vm_vec_scale_add(&new_goal_pos, &objp->pos, &n_vec_to_goal, 200.0f);
3637         }
3638
3639         if (vector_object_collision(&objp->pos, &new_goal_pos, player_objp, 1.5f)) {
3640                 aip->ai_flags |= AIF_AVOIDING_SMALL_SHIP;
3641
3642                 vector  avoid_vec;
3643
3644                 vm_vec_sub(&avoid_vec, &n_vec_to_goal, &n_vec_to_player);
3645                 if (vm_vec_mag_quick(&avoid_vec) < 0.01f) {
3646                         vm_vec_copy_scale(&avoid_vec, &objp->orient.v.rvec, frand()-0.5f);
3647                         vm_vec_scale_add2(&avoid_vec, &objp->orient.v.uvec, frand()-0.5f);
3648                         vm_vec_normalize(&avoid_vec);
3649                 } else {
3650                         vector  tvec1;
3651                         vm_vec_normalize(&avoid_vec);
3652                         vm_vec_crossprod(&tvec1, &n_vec_to_goal, &avoid_vec);
3653                         vm_vec_crossprod(&avoid_vec, &tvec1, &n_vec_to_player);
3654                 }
3655
3656                 //      Now, avoid_vec is a vector perpendicular to the vector to the player and the direction *objp
3657                 //      should fly in to avoid the player while still approaching its goal.
3658                 vm_vec_scale_add(&aip->avoid_goal_point, &player_objp->pos, &avoid_vec, 400.0f);
3659
3660                 aip->avoid_check_timestamp = timestamp(1000);
3661
3662                 return 1;
3663         } else {
3664                 aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
3665                 aip->avoid_check_timestamp = timestamp((int) (obj_obj_dist/200.0f) + 500);
3666
3667                 return 0;
3668         }
3669 }
3670
3671 //      Make object *still_objp enter AIM_STILL mode.
3672 //      Make it point at view_pos.
3673 void ai_stay_still(object *still_objp, vector *view_pos)
3674 {
3675         ship    *shipp;
3676         ai_info *aip;
3677
3678         Assert(still_objp->type == OBJ_SHIP);
3679         Assert((still_objp->instance >= 0) && (still_objp->instance < MAX_OBJECTS));
3680
3681         shipp = &Ships[still_objp->instance];
3682         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
3683
3684         aip = &Ai_info[shipp->ai_index];
3685
3686         aip->mode = AIM_STILL;
3687
3688         //      If view_pos not NULL, point at that point.  Else, point at a point directly in front of ship.  Ie, don't turn.
3689         if (view_pos != NULL)
3690                 aip->goal_point = *view_pos;
3691         else
3692                 vm_vec_scale_add(&aip->goal_point, &still_objp->pos, &still_objp->orient.v.fvec, 100.0f);
3693 }
3694
3695 // code which is called from ai_dock_with_object and ai_dock to set flags and apprioriate variable
3696 // when two objects have completed docking.  used because we can dock object initially at misison load
3697 // time (meaning that ai_dock() might never get called).  docker has docked with dockee (i.e. docker
3698 // would be a freighter and dockee would be a cargo).
3699 void ai_do_objects_docked_stuff(object *docker, object *dockee)
3700 {
3701         ai_info *aip, *other_aip;
3702
3703         aip = &Ai_info[Ships[docker->instance].ai_index];
3704         other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3705
3706         // set the flags and dock_objnum for both objects
3707         aip->ai_flags |= AIF_DOCKED;
3708         aip->dock_objnum = OBJ_INDEX(dockee);
3709         other_aip->ai_flags |= AIF_DOCKED;
3710         other_aip->dock_objnum = OBJ_INDEX(docker);
3711         aip->dock_signature = dockee->signature;
3712         other_aip->dock_signature = docker->signature;
3713
3714         // add multiplayer hook here to deal with docked objects.  We need to only send information
3715         // about the object that is docking.  Both flags will get updated.
3716         if ( MULTIPLAYER_MASTER )
3717                 send_ai_info_update_packet( docker, AI_UPDATE_DOCK );
3718
3719 }
3720
3721 // code which is called when objects become undocked. Equivalent of above function.
3722 // dockee might not be valid since this code can get called to cleanup after a ship
3723 // has blown up!
3724 void ai_do_objects_undocked_stuff( object *docker, object *dockee )
3725 {
3726         ai_info *aip, *other_aip;
3727
3728         // add multiplayer hook here to deal with undocked objects.  Do it before we
3729         // do anything else.  We don't need to send info for both objects, since we can find
3730         // it be dock_objnum
3731         if ( MULTIPLAYER_MASTER )
3732                 send_ai_info_update_packet( docker, AI_UPDATE_UNDOCK );
3733
3734         aip = &Ai_info[Ships[docker->instance].ai_index];
3735
3736         // set the flags and dock_objnum for both objects
3737         aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3738         aip->dock_objnum = -1;
3739         
3740         if ( dockee != NULL ) {
3741                 other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3742                 other_aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3743                 other_aip->dock_objnum = -1;
3744         }
3745
3746 }
3747
3748
3749 //      --------------------------------------------------------------------------
3750 //      Interface from goals code to AI.
3751 //      Cause *docker to dock with *dockee.
3752 //      priority is priority of goal from goals code.
3753 //      dock_type is:
3754 //              AIDO_DOCK               set goal of docking
3755 //              AIDO_DOCK_NOW   immediately dock, used for ships that need to be docked at mission start
3756 //              AIDO_UNDOCK             set goal of undocking
3757 void ai_dock_with_object(object *docker, object *dockee, int priority, int dock_type, int docker_index, int dockee_index)
3758 {
3759         ai_info         *aip;
3760         polymodel       *pm;
3761         ai_info         *dockee_aip;
3762
3763         Assert(docker != NULL);
3764         Assert(dockee != NULL);
3765         Assert(docker->instance != -1);
3766         Assert(Ships[docker->instance].ai_index != -1);
3767         Assert(Ships[dockee->instance].ai_index != -1);
3768         Assert( docker_index != -1 );
3769         Assert( dockee_index != -1 );
3770
3771         aip = &Ai_info[Ships[docker->instance].ai_index];
3772
3773         if ((aip->ai_flags & AIF_DOCKED) && (dock_type == AIDO_DOCK)) {
3774                 object  *dockee2;
3775                 int             docker_index2, dockee_index2;
3776
3777                 Assert(aip->dock_objnum > -1);
3778                 dockee2 = &Objects[aip->dock_objnum];
3779                 docker_index2 = aip->dock_index;
3780                 dockee_index2 = aip->dockee_index;
3781                 // MWA -- 2/9/98.  use the goal code to undock the ships since goals might need to get removed
3782                 // and that code will do it properly.  I'd actually be surprised if we got into this code anymore
3783                 // since the outer layer goal code should deal with this issue....but who knows...
3784                 ai_add_goal_ship_internal( aip, AI_GOAL_UNDOCK, NULL, -1, -1, 0 );
3785
3786                 // old code below
3787                 //ai_dock_with_object(docker, dockee2, priority, AIDO_UNDOCK, docker_index2, dockee_index2);
3788                 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));
3789                 nprintf(("AI", "...so ship %s will now undock.\n", Ships[docker->instance].ship_name));
3790                 return;
3791         }
3792
3793         dockee_aip = &Ai_info[Ships[dockee->instance].ai_index];
3794
3795         aip->goal_objnum = dockee - Objects;
3796         aip->goal_signature = dockee->signature;
3797
3798         aip->mode = AIM_DOCK;
3799
3800         switch (dock_type) {
3801         case AIDO_DOCK:
3802                 aip->submode = AIS_DOCK_0;
3803                 break;
3804         case AIDO_DOCK_NOW:
3805                 aip->submode = AIS_DOCK_3A;
3806                 break;
3807         case AIDO_UNDOCK:
3808                 aip->submode = AIS_UNDOCK_0;
3809                 break;
3810         default:
3811                 Int3();         //      Bogus dock_type.
3812         }
3813
3814         aip->submode_start_time = Missiontime;
3815         aip->dock_index = docker_index;
3816         aip->dockee_index = dockee_index;
3817
3818         dockee_aip->dock_index = dockee_index;
3819         dockee_aip->dockee_index = docker_index;
3820
3821         // get the path number to the docking point on the dockee.  Each docking point contains a list
3822         // of paths that the point can be reached by.  Pick the first path in the path list for now.
3823         // We only want to do this stuff if we are docking!!!  Be sure to set the path index
3824         if ((dock_type == AIDO_DOCK) || (dock_type == AIDO_DOCK_NOW)) {
3825                 pm = model_get( Ships[dockee->instance].modelnum );
3826                 Assert( pm->docking_bays[dockee_index].num_spline_paths > 0 );
3827
3828                 // only set the dock path index if we are docking.  undocking will assume that dock_path_index
3829                 // already set from some other docking command
3830                 aip->dock_path_index = dockee_index;
3831                 dockee_aip->dock_path_index = docker_index;
3832         }
3833
3834         if (dock_type != AIDO_DOCK_NOW) {
3835                 int path_num;
3836                 //      Note: Second parameter is dock path index.  This should be specified as an
3837                 //      _input_ to this function and passed through.  The path index should be already
3838                 // set for the undock function
3839                 path_num = ai_return_path_num_from_dockbay(dockee, dockee_index);
3840                 ai_find_path(docker, dockee-Objects, path_num, 0);
3841 //              ai_find_path(dockee-Objects, dockee_index, 0);
3842         } else {
3843                 dock_orient_and_approach(docker, dockee, DOA_DOCK_STAY);
3844                 //aip->dock_objnum = OBJ_INDEX(dockee);
3845                 ai_do_objects_docked_stuff( docker, dockee );
3846         }
3847
3848 }
3849
3850 //      Cause a ship to fly its waypoints.
3851 //      flags tells:
3852 //              WPF_REPEAT      Set -> repeat waypoints.
3853 void ai_start_waypoints(object *objp, int waypoint_list_index, int wp_flags)
3854 {
3855         ai_info *aip;
3856
3857         Assert(waypoint_list_index < Num_waypoint_lists);
3858
3859         //nprintf(("AI", "Frame %i: Ship %s instructed to fly waypoint list #%i\n", AI_FrameCount, Ships[objp->instance].ship_name, waypoint_list_index));
3860         aip = &Ai_info[Ships[objp->instance].ai_index];
3861
3862         if ( (aip->mode == AIM_WAYPOINTS) && (aip->wp_index == waypoint_list_index) )
3863                 return;
3864
3865         aip->ai_flags |= AIF_FORMATION_WING;
3866         aip->ai_flags &= ~AIF_FORMATION_OBJECT;
3867         aip->wp_list = waypoint_list_index;
3868         aip->wp_index = 0;
3869         aip->wp_flags = wp_flags;
3870         aip->mode = AIM_WAYPOINTS;
3871
3872         Assert(aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC);
3873 }
3874
3875 //      Make *objp stay within dist units of *other_objp
3876 void ai_do_stay_near(object *objp, object *other_objp, float dist)
3877 {
3878         ai_info *aip;
3879
3880         Assert(objp != other_objp);             //      Bogus!  Told to stay near self.
3881         Assert(objp->type == OBJ_SHIP);
3882         Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
3883
3884         aip = &Ai_info[Ships[objp->instance].ai_index];
3885
3886         aip->mode = AIM_STAY_NEAR;
3887         aip->submode = -1;
3888         aip->stay_near_distance = dist;
3889         aip->goal_objnum = other_objp-Objects;
3890         aip->goal_signature = other_objp->signature;
3891
3892 }
3893
3894 //      Make object *objp form on wing of object *goal_objp
3895 void ai_form_on_wing(object *objp, object *goal_objp)
3896 {
3897         ai_info *aip;
3898         ship                    *shipp;
3899         ship_info       *sip;
3900
3901         // objp == goal_objp sometimes in multiplayer when someone leaves a game -- make a simple
3902         // out for this case.
3903         if ( Game_mode & GM_MULTIPLAYER ) {
3904                 if ( objp == goal_objp ) {
3905                         return;
3906                 }
3907         }
3908
3909         Assert(objp != goal_objp);              //      Bogus!  Told to form on own's wing!
3910
3911         shipp = &Ships[objp->instance];
3912         sip = &Ship_info[shipp->ship_info_index];
3913
3914         //      Only fighters or bombers allowed to form on wing.
3915         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
3916                 nprintf(("AI", "Warning: Ship %s tried to form on player's wing, but not fighter or bomber.\n", shipp->ship_name));
3917                 return;
3918         }
3919
3920         aip = &Ai_info[Ships[objp->instance].ai_index];
3921
3922         aip->ai_flags &= ~AIF_FORMATION_WING;
3923         aip->ai_flags |= AIF_FORMATION_OBJECT;
3924
3925         aip->goal_objnum = goal_objp-Objects;
3926         ai_set_goal_maybe_abort_dock(objp, aip);
3927         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME*4);           //      Super extra long time until can target another ship.
3928
3929 }
3930
3931 //      Given an object and an object on whose wing to form, return slot to use.
3932 //      Optimize:
3933 //              This function is called per object in formation per frame.  Should store slot in ai_info struct.
3934 int ai_formation_object_get_slotnum(int objnum, object *objp)
3935 {
3936         int     slotnum = 1;                    //      Note: Slot #0 means leader, which isn't someone who was told to form-on-wing.
3937         object *o;
3938
3939         for ( o = GET_FIRST(&obj_used_list); o != END_OF_LIST(&obj_used_list); o = GET_NEXT(o) ) {
3940                 if (objp == o)
3941                         break;
3942                 else if (o->type == OBJ_SHIP)
3943                         if (Ai_info[Ships[o->instance].ai_index].ai_flags & AIF_FORMATION_OBJECT)
3944                                 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == objnum)
3945                                         slotnum++;
3946         }
3947
3948         Assert(o != END_OF_LIST(&obj_used_list));       //      Didn't find objp in list of used ships.  Impossible!
3949
3950         return slotnum;
3951 }
3952
3953 #define BIGNUM  100000.0f
3954
3955 int Debug_k = 0;
3956
3957 //      Given an attacker's position and a target's position and velocity, compute the time of
3958 //      intersection of a weapon fired by the attacker with speed weapon_speed.
3959 //      Return this value.  Return value of 0.0f means no collision is possible.
3960 float compute_collision_time(vector *targpos, vector *targvel, vector *attackpos, float weapon_speed)
3961 {
3962         vector  vec_to_target;
3963         float           pos_dot_vel;
3964         float           vel_sqr;
3965         float           discrim;
3966
3967         vm_vec_sub(&vec_to_target, targpos, attackpos);
3968         pos_dot_vel = vm_vec_dot(&vec_to_target, targvel);
3969         vel_sqr = vm_vec_dot(targvel, targvel) - weapon_speed*weapon_speed;
3970         discrim = pos_dot_vel*pos_dot_vel - vel_sqr*vm_vec_dot(&vec_to_target, &vec_to_target);
3971
3972         if (discrim > 0.0f) {
3973                 float   t1, t2, t_solve;
3974
3975                 t1 = (-pos_dot_vel + fl_sqrt(discrim)) / vel_sqr;
3976                 t2 = (-pos_dot_vel - fl_sqrt(discrim)) / vel_sqr;
3977
3978                 t_solve = BIGNUM;
3979
3980                 if (t1 > 0.0f)
3981                         t_solve = t1;
3982                 if ((t2 > 0.0f) && (t2 < t_solve))
3983                         t_solve = t2;
3984
3985                 if (t_solve < BIGNUM-1.0f) {
3986                         return t_solve + Debug_k * flFrametime;
3987                 }
3988         }
3989
3990         return 0.0f;
3991 }
3992
3993
3994 //      --------------------------------------------------------------------------
3995 //      If far away, use player's speed.
3996 //      If in between, lerp between player and laser speed
3997 //      If close, use laser speed.
3998 // Want to know how much time it will take to get to the enemy.
3999 // This function doesn't account for the fact that by the time the player
4000 // (or his laser) gets to the current enemy position, the enemy will have moved.
4001 // This is dealt with in polish_predicted_enemy_pos.
4002 float compute_time_to_enemy(float dist_to_enemy, object *pobjp, object *eobjp)
4003 {
4004         float   time_to_enemy;
4005         float   pl_speed = pobjp->phys_info.speed;
4006         float   max_laser_distance, max_laser_speed;
4007         int     bank_num, weapon_num;
4008         ship    *shipp = &Ships[pobjp->instance];
4009
4010         bank_num = shipp->weapons.current_primary_bank;
4011         weapon_num = shipp->weapons.primary_bank_weapons[bank_num];
4012         max_laser_speed = Weapon_info[weapon_num].max_speed;
4013         max_laser_distance = max_laser_speed * Weapon_info[weapon_num].lifetime;
4014
4015         //      If pretty far away, use player's speed to predict position, else
4016         //      use laser's speed because when close, we care more about hitting
4017         //      with a laser than about causing ship:ship rendezvous.
4018         if (dist_to_enemy > 1.5 * max_laser_distance) {
4019                 if (pl_speed > 0.0f)
4020                         time_to_enemy = dist_to_enemy/pl_speed;
4021                 else
4022                         time_to_enemy = 1.0f;
4023         } else if (dist_to_enemy > 1.1*max_laser_distance) {
4024                 if (pl_speed > 0.1f) {
4025                         float   scale;
4026
4027                         scale = (float) ((dist_to_enemy - max_laser_distance) / max_laser_distance);
4028                 
4029                         time_to_enemy = (float) (dist_to_enemy/(pl_speed * scale + max_laser_speed * (1.0f - scale)));
4030                 } else
4031                         time_to_enemy = 2.0f;
4032         } else
4033                 time_to_enemy = (float) (dist_to_enemy/max_laser_speed);
4034
4035         // return time_to_enemy * (1.0f + Ai_info[Ships[pobjp->instance].ai_index].lead_scale);
4036         return time_to_enemy + flFrametime;
4037 }
4038
4039 //      Stuff *dot and *tts.
4040 //      *dot is always computed.  If dot is less than zero, the magnitude is
4041 //      incorrect, not having been divided by distance.
4042 //      If *dot is > 0.0f, then tts is computed.  This is the time it will take object
4043 //      *objp to get to *pos, assuming it moves right at it.
4044 void fds_aux(float *dot, float *tts, vector *pos, float dtime, object *objp)
4045 {
4046         vector  v2s;
4047
4048         vm_vec_sub(&v2s, pos, &objp->pos);
4049         *dot = vm_vec_dot(&v2s, &objp->orient.v.fvec);
4050
4051         if (*dot > 0.0f) {
4052                 float   dist;
4053
4054                 dist = vm_vec_dist(&objp->pos, pos);
4055
4056                 if (dist > 0.1f)
4057                         *dot /= dist;
4058                 else
4059                         *dot = 1.0f;
4060
4061                 if (objp->phys_info.speed > 0.1f)
4062                         *tts = dist / objp->phys_info.speed;
4063                 else
4064                         *tts = dist * 100.0f;
4065         }
4066 }
4067
4068 /*
4069 //      Return index of weapon that could hit object *sobjp within dtime seconds.
4070 //      Actual time until impact returned in *atime.
4071 int find_danger_weapon(object *sobjp, float dtime, float *atime, float dot_threshhold)
4072 {
4073         object  *objp, *best_objp = NULL;
4074         float           best_tts = 1000.0f;
4075
4076         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
4077                 if ((objp->type == OBJ_WEAPON) && (sobjp-Objects != objp->parent)) {
4078                         float           dot, tts;
4079                         // vector       psp;            //      Predicted ship position.
4080
4081                         //      Get dot and time to current ship position.
4082                         fds_aux(&dot, &tts, &sobjp->pos, dtime, objp);
4083
4084                         //      If dot and tts are in plausible range, do more expensive stuff.
4085                         if (dot > 0.98f) {
4086 //                              float   dot_from_sobjp;
4087                                 vector  v2e;
4088
4089                                 vm_vec_normalized_dir(&v2e, &objp->pos, &sobjp->pos);
4090 //                              dot_from_sobjp = vm_vec_dot(&sobjp->orient.v.fvec, &v2e);
4091 //                              if (dot_from_sobjp >= dot_threshhold)
4092                                         if (tts < dtime) {
4093                                                 if (tts < best_tts) {
4094                                                         best_tts = tts;
4095                                                         best_objp = objp;
4096                                                 }
4097                                         }
4098                         }
4099                 }
4100         }
4101
4102         *atime = best_tts;
4103
4104         if (best_objp != NULL)
4105                 return best_objp-Objects;
4106         else
4107                 return -1;
4108 }
4109 */
4110
4111 //      --------------------------------------------------------------------------
4112 void ai_set_positions(object *pl_objp, object *en_objp, ai_info *aip, vector *player_pos, vector *enemy_pos)
4113 {
4114         *player_pos = pl_objp->pos;
4115
4116         if (aip->next_predict_pos_time > Missiontime) {
4117                 *enemy_pos = aip->last_predicted_enemy_pos;
4118         } else {
4119                 *enemy_pos = en_objp->pos;
4120
4121                 aip->next_predict_pos_time = Missiontime + Skill_level_delay[Game_skill_level];
4122                 aip->last_predicted_enemy_pos = *enemy_pos;
4123         }
4124
4125
4126 }
4127
4128 //      --------------------------------------------------------------------------
4129 int find_nearest_waypoint(object *objp)
4130 {
4131         int     i;
4132         float   dist, min_dist, dot;
4133         int     min_ind;
4134         ship    *shipp;
4135         int     wp_listnum;
4136         waypoint_list   *wpl;
4137
4138         shipp = &Ships[objp->instance];
4139         wp_listnum = Ai_info[Ships[objp->instance].ai_index].wp_list;
4140         Assert(wp_listnum > 0);
4141         wpl = &Waypoint_lists[wp_listnum];
4142
4143         min_dist = 999999.0f;
4144         min_ind = -1;
4145
4146         for (i=0; i<wpl->count; i++) {
4147                 dist = vm_vec_dist_quick(&objp->pos, &wpl->waypoints[i]);
4148                 dot = vm_vec_dot_to_point(&objp->orient.v.fvec, &objp->pos, &wpl->waypoints[i]);
4149                 dist = (float) (dist * (1.25 - dot));
4150                 if (dist < min_dist) {
4151                         min_dist = dist;
4152                         min_ind = i;
4153                 }
4154         }
4155
4156         Assert(min_ind != -1);
4157
4158         return min_ind;
4159 }
4160
4161 //      Given an ai_info struct, by reading current goal and path information,
4162 //      extract base path information and return in pmp and pmpv.
4163 //      Return true if found, else return false.
4164 //      false means the current point is not on the original path.
4165 int get_base_path_info(int path_cur, int goal_objnum, model_path **pmp, mp_vert **pmpv)
4166 {
4167         pnode                   *pn = &Path_points[path_cur];
4168         ship_info       *sip = &Ship_info[Ships[Objects[goal_objnum].instance].ship_info_index];
4169         polymodel       *pm = model_get(sip->modelnum);
4170         //static        int     debug_last_index = -1;  // no longer used
4171         *pmpv = NULL;
4172         *pmp = NULL;
4173
4174         if (pn->path_num != -1) {
4175                 *pmp = &pm->paths[pn->path_num];
4176                 if (pn->path_index != -1)
4177                         *pmpv = &(*pmp)->verts[pn->path_index];
4178                 else
4179                         return 0;
4180         } else
4181                 return 0;
4182
4183 /*      if (debug_last_index != *pmpv-(*pmp)->verts) {
4184                 debug_last_index = *pmpv-(*pmp)->verts;
4185                 nprintf(("AI", "Point %i has %i turrets: ", *pmpv-(*pmp)->verts, (*pmpv)->nturrets));
4186                 for (int i=0; i<(*pmpv)->nturrets; i++) {
4187                         nprintf(("AI", "%i ", (*pmpv)->turret_ids[i]));
4188                 }
4189                 nprintf(("AI", "\n"));
4190         }
4191 */
4192         return 1;
4193 }
4194
4195 //      Modify, in place, the points in a global model path.
4196 //      Only modify those points that are defined in the model path.  Don't modify the
4197 //      leadin points, such as those that are necessary to get the model on the path.
4198 void modify_model_path_points(object *objp)
4199 {       
4200         ai_info         *aip = &Ai_info[Ships[objp->instance].ai_index];
4201         object          *mobjp = &Objects[aip->path_objnum];
4202         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
4203         polymodel       *pm = model_get(osip->modelnum);
4204         pnode                   *pnp;
4205         int                     path_num, dir;
4206
4207         Assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
4208
4209         pnp = &Path_points[aip->path_start];
4210         while ((pnp->path_index == -1) && (pnp-Path_points - aip->path_start < aip->path_length))
4211                 pnp++;
4212
4213         path_num = pnp->path_num;
4214         Assert((path_num >= 0) && (path_num < pm->n_paths));
4215         
4216         Assert(pnp->path_index != -1);  //      If this is -1, that means we never found the model path points
4217
4218         dir = 1;
4219         if ( aip->ai_flags & AIF_USE_EXIT_PATH ) {
4220                 dir = -1;
4221         }
4222
4223         copy_xlate_model_path_points(mobjp, &pm->paths[path_num], dir, pm->paths[path_num].nverts, path_num, pnp);
4224 }
4225
4226 //      Return an indication of the distance between two matrices.
4227 //      This is the sum of the distances of their dot products from 1.0f.
4228 float ai_matrix_dist(matrix *mat1, matrix *mat2)
4229 {
4230         float   t;
4231
4232         t =  1.0f - vm_vec_dot(&mat1->v.fvec, &mat2->v.fvec);
4233         t += 1.0f - vm_vec_dot(&mat1->v.uvec, &mat2->v.uvec);
4234         t += 1.0f - vm_vec_dot(&mat1->v.rvec, &mat2->v.rvec);
4235
4236         return t;
4237 }
4238
4239
4240 //      Paths are created in absolute space, so a moving object needs to have model paths within it recreated.
4241 //      This uses the hash functions which means the slightest movement will cause a recreate, though the timestamp
4242 //      prevents this from happening too often.
4243 //      force_recreate_flag TRUE means to recreate regardless of timestamp.
4244 //      Returns TRUE if path recreated.
4245 float maybe_recreate_path(object *objp, ai_info *aip, int force_recreate_flag)
4246 {
4247         int     hashval;
4248
4249         Assert(&Ai_info[Ships[objp->instance].ai_index] == aip);
4250
4251         if ((aip->mode == AIM_BAY_EMERGE) || (aip->mode == AIM_BAY_DEPART))
4252                 if ((OBJ_INDEX(objp) % 4) == (Framecount % 4))
4253                         force_recreate_flag = 1;
4254
4255         //      If no path, that means we don't need one.
4256         if (aip->path_start == -1)
4257                 return 0.0f;
4258
4259         // AL 11-12-97: If AIF_USE_STATIC_PATH is set, don't try to recreate.  This is needed when ships
4260         //                                  emerge from fighter bays.  We don't need to recreate the path.. and in case the 
4261         //              parent ship dies, we still want to be able to continue on the path
4262         if ( aip->ai_flags & AIF_USE_STATIC_PATH ) 
4263                 return 0.0f;
4264
4265         if (force_recreate_flag || timestamp_elapsed(aip->path_next_create_time)) {
4266                 object  *path_objp;
4267
4268                 path_objp = &Objects[aip->path_objnum];
4269
4270                 if ((hashval = create_object_hash(path_objp)) != aip->path_goal_obj_hash) {
4271                         float dist;
4272                         
4273                         dist = vm_vec_dist_quick(&path_objp->pos, &aip->path_create_pos);
4274                         dist += ai_matrix_dist(&path_objp->orient, &aip->path_create_orient) * 25.0f;
4275
4276                         if (force_recreate_flag || (dist > 2.0f)) {
4277                                 aip->path_next_create_time = timestamp(1000);   //      Update again in as little as 1000 milliseconds, ie 1 second.
4278                                 aip->path_goal_obj_hash = hashval;
4279                                 modify_model_path_points(objp);
4280
4281                                 aip->path_create_pos = path_objp->pos;
4282                                 aip->path_create_orient = path_objp->orient;
4283                                 
4284                                 return dist;
4285                         }
4286                 }
4287         }
4288
4289         return 0.0f;
4290 }
4291
4292 //      Set acceleration for ai_dock().
4293 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)
4294 {
4295         float prev_dot_to_goal = aip->prev_dot_to_goal;
4296         
4297         aip->prev_dot_to_goal = dot;
4298
4299         if (objp->phys_info.speed < 0.0f) {
4300                 accelerate_ship(aip, 1.0f/32.0f);
4301         } else if ((prev_dot_to_goal-dot) > 0.01) {
4302                 if (prev_dot_to_goal > dot + 0.05f) {
4303                         accelerate_ship(aip, 0.0f);
4304                 } else {
4305                         change_acceleration(aip, -1.0f);        //      -1.0f means subtract off flFrametime from acceleration value in 0.0..1.0
4306                 }
4307         } else {
4308                 if ((aip->mode == AIM_DOCK) && (dist_to_next < 150.0f) && (aip->path_start + aip->path_length - 2 == aip->path_cur)) {
4309                         set_accel_for_target_speed(objp, sip->max_speed * max(dist_to_next/500.0f, 1.0f));
4310                         //mprintf(("dist = %7.3f, speed = %7.3f\n", dist_to_next, objp->phys_info.speed));
4311                 } else if ((dot_to_next >= dot * .9) || (dist_to_next > 100.0f)) {
4312                         if (dist_to_goal > 200.0f)
4313                                 set_accel_for_target_speed(objp, sip->max_speed * (dot + 1.0f) / 2.0f);
4314                         else {
4315                                 float   xdot;
4316
4317                                 xdot = (dot_to_next + dot)/2.0f;
4318                                 if (xdot < 0.0f)
4319                                         xdot = 0.0f;
4320
4321                                 // AL: if following a path not in dock mode, move full speed
4322                                 if (( aip->mode != AIM_DOCK ) && (dot > 0.9f)) {
4323                                         set_accel_for_target_speed(objp, sip->max_speed*dot*dot*dot);
4324                                 } else {
4325                                         if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4326                                                 //nprintf(("AI", "Target speed = %7.3f\n", dist_to_goal/8.0f));
4327                                                 set_accel_for_target_speed(objp, dist_to_goal/8.0f + 2.0f);
4328                                         } else {
4329                                                 set_accel_for_target_speed(objp, sip->max_speed * (2*xdot + 0.25f)/4.0f);
4330                                         }
4331                                 }
4332                         }
4333                 } else {
4334                         float   xdot;
4335
4336                         xdot = max(dot_to_next, 0.1f);
4337                         if ( aip->mode != AIM_DOCK ) {
4338                                 set_accel_for_target_speed(objp, sip->max_speed);
4339                         } else {
4340                                 float   speed;
4341                                 if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4342                                         speed = dist_to_goal/8.0f + 2.0f;
4343                                 } else if (dist_to_goal < 4*objp->radius + 50.0f) {
4344                                         speed = dist_to_goal/4.0f + 4.0f;
4345                                 } else {
4346                                         speed = sip->max_speed * (3*xdot + 1.0f)/4.0f;
4347                                 }
4348                                 if (aip->mode == AIM_DOCK) {
4349                                         speed = speed * 2.0f + 1.0f;
4350                                         if (aip->goal_objnum != -1) {
4351                                                 speed += Objects[aip->goal_objnum].phys_info.speed;
4352                                         }
4353                                 }
4354
4355                                 set_accel_for_target_speed(objp, speed);
4356                         }
4357                 }
4358         }
4359 }
4360
4361 //      --------------------------------------------------------------------------
4362 //      Follow a path associated with a large object, such as a capital ship.
4363 //      The points defined on the path are in the object's reference frame.
4364 //      The object of interest is goal_objnum.
4365 //      The paths are defined in the model.  The path of interest is wp_list.
4366 //      The next goal point in the path is wp_index.
4367 //      wp_flags contain special information specific to the path.
4368
4369 // The path vertices are defined by model_path structs:
4370 //              typedef struct model_path {
4371 //                      char            name[MAX_NAME_LEN];                                     // name of the subsystem.  Probably displayed on HUD
4372 //                      int             nverts;
4373 //                      vector  *verts;
4374 //              } model_path;
4375
4376 //      The polymodel struct for the object contains the following:
4377 //              int                     n_paths;
4378 //              model_path      *paths;
4379
4380 //      Returns distance to goal point.
4381 float ai_path()
4382 {
4383         polymodel       *pm;
4384         int             num_paths, num_points;
4385         float           dot, dist_to_goal, dist_to_next, speed, dot_to_next;
4386         ship            *shipp = &Ships[Pl_objp->instance];
4387         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4388         ai_info *aip;
4389         vector  nvel_vec;
4390         float           mag, prev_dot_to_goal;
4391         vector  temp_vec, *slop_vec;
4392         object  *gobjp;
4393         ship            *gshipp;
4394         vector  *cvp, *nvp, next_vec, gcvp, gnvp;               //      current and next vertices in global coordinates.
4395
4396         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4397
4398         Assert(aip->goal_objnum != -1);
4399         Assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
4400
4401         gobjp = &Objects[aip->goal_objnum];
4402         gshipp = &Ships[gobjp->instance];
4403
4404         pm = model_get( gshipp->modelnum );
4405         num_paths = pm->n_paths;
4406         Assert(num_paths > 0);
4407
4408         if (aip->path_start == -1) {
4409                 int path_num;
4410                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
4411                 Assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
4412                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
4413         }
4414
4415         // nprintf(("AI", "Frame: %i, Path index = %i/%i\n", AI_FrameCount, aip->path_cur-aip->path_start, aip->path_length));
4416
4417         maybe_recreate_path(Pl_objp, aip, 0);
4418
4419         num_points = aip->path_length;
4420
4421         //      Set cvp and nvp as pointers to current and next vertices of interest on path.
4422         cvp = &Path_points[aip->path_cur].pos;
4423         if ((aip->path_cur + aip->path_dir - aip->path_start < num_points) || (aip->path_cur + aip->path_dir < aip->path_start))
4424                 nvp = &Path_points[aip->path_cur + aip->path_dir].pos;
4425         else {
4426                 //      If this is 0, then path length must be 1 which means we have no direction!
4427                 Assert((aip->path_cur - aip->path_dir >= aip->path_start) && (aip->path_cur - aip->path_dir - aip->path_start < num_points));
4428                 //      Cleanup for above Assert() which we hit too near release. -- MK, 5/24/98.
4429                 if (aip->path_cur - aip->path_dir - aip->path_start >= num_points) {
4430                         if (aip->path_dir == 1)
4431                                 aip->path_cur = aip->path_start;
4432                         else
4433                                 aip->path_cur = aip->path_start + num_points - 1;
4434                 }
4435
4436                 vector  delvec;
4437                 vm_vec_sub(&delvec, cvp, &Path_points[aip->path_cur - aip->path_dir].pos);
4438                 vm_vec_normalize(&delvec);
4439                 vm_vec_scale_add(&next_vec, cvp, &delvec, 10.0f);
4440                 nvp = &next_vec;
4441         }
4442
4443         //      Interrupt if can't get to current goal point.  Debug only.
4444 /*      if (pp_collide(&Pl_objp->pos, cvp, gobjp, Pl_objp->radius)) {
4445                 Int3();
4446         }
4447 */
4448         //      See if can reach next point (as opposed to current point)
4449         //      However, don't do this if docking and next point is last point.
4450         //      That is, we don't want to pursue the last point under control of the
4451         //      path code.  In docking, this is a special hack.
4452         if ((aip->mode != AIM_DOCK) || ((aip->path_cur-aip->path_start) < num_points - 2)) {
4453                 if ((aip->path_cur + aip->path_dir > aip->path_start) && (aip->path_cur + aip->path_dir < aip->path_start + num_points-2)) {
4454                         if ( timestamp_elapsed(aip->path_next_check_time)) {
4455                                 aip->path_next_check_time = timestamp( 3000 );
4456                                 if (!pp_collide(&Pl_objp->pos, nvp, gobjp, 1.1f * Pl_objp->radius)) {
4457                                         cvp = nvp;
4458                                         aip->path_cur += aip->path_dir;
4459                                         nvp = &Path_points[aip->path_cur].pos;
4460                                         //nprintf(("AI", "Reach: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4461                                 }
4462                         }
4463                 }
4464         }
4465
4466         gcvp = *cvp;
4467         gnvp = *nvp;
4468
4469         speed = Pl_objp->phys_info.speed;
4470
4471         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &gcvp);
4472         dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, &gnvp);
4473         //      Can't use fvec, need to use velocity vector because we aren't necessarily
4474         //      moving in the direction we're facing.
4475
4476 //      if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4477         if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4478                 mag = 0.0f;
4479                 vm_vec_zero(&nvel_vec);
4480         } else
4481                 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4482
4483         //      If moving not-very-slowly and sliding, then try to slide at goal, rather than
4484         //      point at goal.
4485         slop_vec = NULL;
4486         if (mag < 1.0f)
4487                 nvel_vec = Pl_objp->orient.v.fvec;
4488         else if (mag > 5.0f) {
4489                 float   nv_dot;
4490                 nv_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4491                 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4492                         slop_vec = &temp_vec;
4493                         vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.v.fvec);
4494                 }
4495         }
4496
4497         if (dist_to_goal > 0.1f)
4498                 ai_turn_towards_vector(&gcvp, Pl_objp, flFrametime, sip->srotation_time, slop_vec, NULL, 0.0f, 0);
4499
4500         //      Code to control speed is MUCH less forgiving in path following than in waypoint
4501         //      following.  Must be very close to path or might hit objects.
4502         prev_dot_to_goal = aip->prev_dot_to_goal;
4503         dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gcvp);
4504         dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gnvp);
4505
4506         set_accel_for_docking(Pl_objp, aip, dot, dot_to_next, dist_to_next, dist_to_goal, sip);
4507         aip->prev_dot_to_goal = dot;
4508
4509 //mprintf(("Goal index = %i, dist = %7.3f, dot = %7.3f\n", wp_index, dist_to_goal, dot));
4510
4511         //      If moving at a non-tiny velocity, detect attaining path point by its being close to
4512         //      line between previous and current object location.
4513         if ((dist_to_goal < MIN_DIST_TO_WAYPOINT_GOAL) || (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f)) {
4514                 vector  nearest_point;
4515                 float           r, min_dist_to_goal;
4516
4517                 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, &gcvp);
4518
4519                 //      Set min_dist_to_goal = how close must be to waypoint to pick next one.
4520                 //      If docking and this is the second last waypoint, must be very close.
4521                 if ((aip->mode == AIM_DOCK) && (aip->path_cur >= aip->path_length-2))
4522                         min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL;
4523                 else
4524                         min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius;
4525
4526                 if ( (vm_vec_dist_quick(&Pl_objp->pos, &gcvp) < min_dist_to_goal) ||
4527                         ((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, &gcvp) < (MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius))) {
4528                         aip->path_cur += aip->path_dir;
4529                         //nprintf(("AI", " Near: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4530                         if (((aip->path_cur - aip->path_start) > (num_points+1)) || (aip->path_cur < aip->path_start)) {
4531                                 Assert(aip->mode != AIM_DOCK);          //      If docking, should never get this far, getting to last point handled outside ai_path()
4532                                 aip->path_dir = -aip->path_dir;
4533 //                              aip->path_cur += aip->path_dir;
4534                         }
4535                 }
4536         }
4537
4538         return dist_to_goal;
4539 }
4540
4541 void update_min_max(float val, float *min, float *max)
4542 {
4543         if (val < *min)
4544                 *min = val;
4545         else if (val > *max)
4546                 *max = val;
4547 }
4548
4549 //      Stuff bounding box of all enemy objects within "range" units of object *my_objp.
4550 //      Stuff ni min_vec and max_vec.
4551 //      Return value: Number of enemy objects in bounding box.
4552 int get_enemy_team_range(object *my_objp, float range, int enemy_team_mask, vector *min_vec, vector *max_vec)
4553 {
4554         object  *objp;
4555         ship_obj        *so;
4556         int             count = 0;
4557
4558         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4559                 objp = &Objects[so->objnum];
4560                 if (Ships[objp->instance].team & enemy_team_mask) {
4561                         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))
4562                                 if (vm_vec_dist_quick(&my_objp->pos, &objp->pos) < range) {
4563                                         if (count == 0) {
4564                                                 *min_vec = objp->pos;
4565                                                 *max_vec = objp->pos;
4566                                                 count++;
4567                                         } else {
4568                                                 update_min_max(objp->pos.xyz.x, &min_vec->xyz.x, &max_vec->xyz.x);
4569                                                 update_min_max(objp->pos.xyz.y, &min_vec->xyz.y, &max_vec->xyz.y);
4570                                                 update_min_max(objp->pos.xyz.z, &min_vec->xyz.z, &max_vec->xyz.z);
4571                                         }
4572                                 }
4573
4574                 }
4575         }
4576
4577         return count;
4578 }
4579
4580 //      Pick a relatively safe spot for objp to fly to.
4581 //      Problem:
4582 //              Finds a spot away from any enemy within a bounding box.
4583 //              Doesn't verify that "safe spot" is not near some other enemy.
4584 void ai_safety_pick_spot(object *objp)
4585 {
4586         int             objnum;
4587         int             enemy_team_mask;
4588         vector  min_vec, max_vec;
4589         vector  vec_to_center, center;
4590         vector  goal_pos;
4591
4592         objnum = OBJ_INDEX(objp);
4593
4594         enemy_team_mask = get_enemy_team_mask(objnum);
4595
4596         if (get_enemy_team_range(objp, 1000.0f, enemy_team_mask, &min_vec, &max_vec)) {
4597                 vm_vec_avg(&center, &min_vec, &max_vec);
4598                 vm_vec_normalized_dir(&vec_to_center, &center, &objp->pos);
4599
4600                 vm_vec_scale_add(&goal_pos, &center, &vec_to_center, 2000.0f);
4601         } else
4602                 vm_vec_scale_add(&goal_pos, &objp->pos, &objp->orient.v.fvec, 100.0f);
4603
4604         Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pos;
4605 }
4606
4607 //      Fly to desired safe point.
4608 // Returns distance to that point.
4609 float ai_safety_goto_spot(object *objp)
4610 {
4611         float   dot, dist;
4612         ai_info *aip;
4613         vector  vec_to_goal;
4614         ship_info       *sip;
4615         float   dot_val;
4616
4617         sip = &Ship_info[Ships[objp->instance].ship_info_index];
4618
4619         aip = &Ai_info[Ships[objp->instance].ai_index];
4620         dist = vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
4621         dot = vm_vec_dot(&vec_to_goal, &objp->orient.v.fvec);
4622
4623         dot_val = (1.1f + dot) / 2.0f;
4624         if (dist > 200.0f) {
4625                 set_accel_for_target_speed(objp, sip->max_speed * dot_val);
4626         } else
4627                 set_accel_for_target_speed(objp, sip->max_speed * dot_val * (dist/200.0f + 0.2f));
4628
4629         return dist;
4630 }
4631
4632 void ai_safety_circle_spot(object *objp)
4633 {
4634         vector  goal_point;
4635         ship_info       *sip;
4636         float           dot;
4637
4638         sip = &Ship_info[Ships[objp->instance].ship_info_index];
4639
4640         goal_point = Ai_info[Ships[objp->instance].ai_index].goal_point;
4641         dot = turn_towards_tangent(objp, &goal_point, 250.0f);  //      Increased from 50 to 250 to make circling not look so wacky.
4642
4643         set_accel_for_target_speed(objp, 0.5f * (1.0f + dot) * sip->max_speed/4.0f);
4644
4645 //      float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
4646 //      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));
4647
4648 }
4649
4650 //      --------------------------------------------------------------------------
4651 void ai_safety()
4652 {
4653         ai_info *aip;
4654
4655         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4656
4657         switch (aip->submode) {
4658         case AISS_1:
4659                 ai_safety_pick_spot(Pl_objp);
4660                 aip->submode = AISS_2;
4661                 aip->submode_start_time = Missiontime;
4662                 break;
4663         case AISS_1a:   //      Pick a safe point because we just got whacked!
4664                 Int3();
4665                 break;
4666         case AISS_2:
4667                 if (ai_safety_goto_spot(Pl_objp) < 25.0f) {
4668                         aip->submode = AISS_3;
4669                         aip->submode_start_time = Missiontime;
4670                 }
4671                 break;
4672         case AISS_3:
4673                 ai_safety_circle_spot(Pl_objp);
4674                 break;
4675         default:
4676                 Int3();         //      Illegal submode for ai_safety();
4677                 break;
4678         }
4679 }
4680
4681 //      --------------------------------------------------------------------------
4682 //      make Pl_objp fly waypoints.
4683 void ai_waypoints()
4684 {
4685         int             wp_index;
4686         vector  *wp_cur, *wp_next;
4687         float           dot, dist_to_goal, dist_to_next, speed, dot_to_next;
4688         ship            *shipp = &Ships[Pl_objp->instance];
4689         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4690         waypoint_list   *wpl;
4691         ai_info *aip;
4692         vector  nvel_vec;
4693         float           mag;
4694         float           prev_dot_to_goal;
4695         vector  temp_vec;
4696         vector  *slop_vec;
4697
4698         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4699
4700         wp_index = aip->wp_index;
4701
4702         if (wp_index == -1) {
4703                 ai_start_waypoints(Pl_objp, 0, WPF_REPEAT);
4704                 wp_index = aip->wp_index;
4705                 aip->wp_dir = 1;
4706         }
4707
4708         wpl = &Waypoint_lists[Ai_info[Ships[Pl_objp->instance].ai_index].wp_list];
4709
4710         Assert(wpl->count);     // What? Is this zero? Probably wp_index never got initialized!
4711
4712         wp_cur = &wpl->waypoints[wp_index];
4713         wp_next = &wpl->waypoints[(wp_index+1) % wpl->count];
4714         speed = Pl_objp->phys_info.speed;
4715
4716         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, wp_cur);
4717         dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, wp_next);
4718
4719         //      Can't use fvec, need to use velocity vector because we aren't necessarily
4720         //      moving in the direction we're facing.
4721         // AL 23-3-98: Account for very small velocities by checking result of vm_vec_mag().
4722         //                                      If we don't vm_vec_copy_normalize() will think it is normalizing a null vector.
4723 //      if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4724         if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4725                 mag = 0.0f;
4726                 vm_vec_zero(&nvel_vec);
4727         } else {
4728                 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4729         }
4730
4731         //      If moving not-very-slowly and sliding, then try to slide at goal, rather than
4732         //      point at goal.
4733         slop_vec = NULL;
4734         if (mag < 1.0f) {
4735                 nvel_vec = Pl_objp->orient.v.fvec;
4736         } else if (mag > 5.0f) {
4737                 float   nv_dot;
4738                 nv_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4739                 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4740                         slop_vec = &temp_vec;
4741                         vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.v.fvec);
4742                 }
4743         }
4744
4745         //      If a wing leader, take turns more slowly, based on size of wing.
4746         int     scale;
4747
4748         if (Ai_info[Ships[Pl_objp->instance].ai_index].wing >= 0) {
4749                 scale = Wings[Ai_info[Ships[Pl_objp->instance].ai_index].wing].current_count;
4750                 scale = (int) ((scale+1)/2);
4751         } else {
4752                 scale = 1;
4753         }
4754
4755         if (dist_to_goal > 0.1f) {
4756                 ai_turn_towards_vector(wp_cur, Pl_objp, flFrametime, sip->srotation_time*3.0f*scale, slop_vec, NULL, 0.0f, 0);
4757         }
4758
4759         prev_dot_to_goal = aip->prev_dot_to_goal;
4760         dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_cur);
4761         dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_next);
4762         aip->prev_dot_to_goal = dot;
4763
4764         //      If there is no next point on the path, don't care about dot to next.
4765         if (wp_index + 1 >= wpl->count) {
4766                 dot_to_next = dot;
4767         }
4768
4769         // nprintf(("AI", "Wp #%i, dot = %6.3f, next dot = %6.3f, dist = %7.2f\n", wp_index, dot, dot_to_next, dist_to_goal));
4770
4771         if (Pl_objp->phys_info.speed < 0.0f) {
4772                 accelerate_ship(aip, 1.0f/32);
4773         } else if (prev_dot_to_goal > dot+0.01f) {
4774                 //      We are further from pointing at our goal this frame than last frame, so slow down.
4775                 set_accel_for_target_speed(Pl_objp, Pl_objp->phys_info.speed * 0.95f);
4776         } else if (dist_to_goal < 100.0f) {
4777                 float slew_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4778                 if (fl_abs(slew_dot) < 0.9f) {
4779                         accelerate_ship(aip, 0.0f);
4780                 } else if (dot < 0.88f + 0.1f*(100.0f - dist_to_goal)/100.0f) {
4781                         accelerate_ship(aip, 0.0f);
4782                 } else {
4783                         accelerate_ship(aip, 0.5f * dot * dot);
4784                 }
4785         } else {
4786                 float   dot1;
4787                 if (dist_to_goal < 250.0f) {
4788                         dot1 = dot*dot*dot;                             //      Very important to be pointing towards goal when nearby.  Note, cubing preserves sign.
4789                 } else {
4790                         if (dot > 0.0f) {
4791                                 dot1 = dot*dot;
4792                         } else {
4793                                 dot1 = dot;
4794                         }
4795                 }
4796
4797                 if (dist_to_goal > 100.0f + Pl_objp->radius * 2) {
4798                         if (dot < 0.2f) {
4799                                 dot1 = 0.2f;
4800                         }
4801                 }
4802
4803                 if (sip->flags & SIF_SMALL_SHIP) {
4804                         set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/5.0f);
4805                 } else {
4806                         set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/10.0f);
4807                 }
4808         }
4809
4810         //      Make sure not travelling too fast for someone to keep up.
4811         float   max_allowed_speed = 9999.9f;
4812
4813         if (shipp->wingnum != -1) {
4814                 max_allowed_speed = 0.9f * get_wing_lowest_max_speed(Pl_objp);
4815         }
4816
4817         // check if waypoint speed cap is set and adjust max speed
4818         if (aip->waypoint_speed_cap > 0) {
4819                 max_allowed_speed = (float) aip->waypoint_speed_cap;
4820         }
4821
4822         if (aip->prev_accel * shipp->current_max_speed > max_allowed_speed) {
4823                 accelerate_ship(aip, max_allowed_speed / shipp->current_max_speed);
4824         }
4825
4826         if (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f) {
4827                 vector  nearest_point;
4828                 float           r;
4829
4830                 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, wp_cur);
4831
4832                 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))) ||
4833                         ((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, wp_cur) < (MIN_DIST_TO_WAYPOINT_GOAL + fl_sqrt(Pl_objp->radius)))) {
4834                         wp_index++;
4835                         if (wp_index >= wpl->count)
4836                                 if (aip->wp_flags & WPF_REPEAT) {
4837                                         wp_index = 0;
4838                                 } else {
4839                                         int treat_as_ship;
4840
4841                                         // when not repeating waypoints -- mark the goal as done and put and entry into the mission log
4842                                         // we must be careful when dealing with wings.  A ship in a wing might be completing
4843                                         // a waypoint for for the entire wing, or it might be completing a goal for itself.  If
4844                                         // for itself and in a wing, treat the completion as we would a ship
4845                                         treat_as_ship = 1;
4846                                         if ( Ships[Pl_objp->instance].wingnum != -1 ) {
4847                                                 int type;
4848
4849                                                 // I don't think that you can fly waypoints as dynamic goals!!!
4850                                                 // -- This is legal, just stupid. -- Assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4851                                                 
4852                                                 //      Clean up from above Assert, just in case we ship without fixing it.  (Encountered by JimB on 2/9/98)
4853                                                 if ( (aip->active_goal == AI_GOAL_NONE) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC) ) {
4854                                                         aip->mode = AIM_NONE;
4855                                                         Int3(); //      Look at the ship, find out of it's supposed to be flying waypoints. -- MK.
4856                                                 }
4857
4858                                                 type = aip->goals[aip->active_goal].type;
4859                                                 if ( (type == AIG_TYPE_EVENT_WING) || (type == AIG_TYPE_PLAYER_WING) ) {
4860                                                         treat_as_ship = 0;
4861                                                 } else {
4862                                                         treat_as_ship = 1;
4863                                                 }
4864                                         }
4865
4866                                         // if the ship is not in a wing, remove the goal and continue on
4867                                         if ( treat_as_ship ) {
4868                                                 ai_mission_goal_complete( aip );                                        // this call should reset the AI mode
4869                                                 mission_log_add_entry(LOG_WAYPOINTS_DONE, Ships[Pl_objp->instance].ship_name, wpl->name, -1 );
4870                                         } else {
4871                                                 // this ship is in a wing.  We must mark the goal as being completed for all ships
4872                                                 // in the wing.  We will also mark an entry in the log that the wing completed the goal
4873                                                 // not the individual ship.
4874                                                 ai_mission_wing_goal_complete( Ships[Pl_objp->instance].wingnum, &(aip->goals[aip->active_goal]) );
4875                                                 mission_log_add_entry( LOG_WAYPOINTS_DONE, Wings[Ships[Pl_objp->instance].wingnum].name, wpl->name, -1 );
4876                                         }
4877                                         //wp_index = wpl->count-1;
4878                                 }
4879
4880                         aip->wp_index = wp_index;
4881                 }
4882         }
4883 }
4884
4885 //      Make Pl_objp avoid En_objp
4886 //      Not like evading.  This is for avoiding a collision!
4887 //      Note, use sliding if available.
4888 void avoid_ship()
4889 {
4890         //      To avoid an object, turn towards right or left vector until facing away from object.
4891         //      To choose right vs. left, pick one that is further from center of avoid object.
4892         //      Keep turning away from until pointing away from ship.
4893         //      Stay in avoid mode until at least 3 enemy ship radii away.
4894
4895         //      Speed setting:
4896         //      If inside sphere, zero speed and turn towards outside.
4897         //      If outside sphere, inside 2x sphere, set speed percent of max to:
4898         //              max(away_dot, (dist-rad)/rad)
4899         //      where away_dot is dot(Pl_objp->v.fvec, vec_En_objp_to_Pl_objp)
4900
4901         vector  vec_to_enemy;
4902         float           away_dot;
4903         float           dist;
4904         ship            *shipp = &Ships[Pl_objp->instance];
4905         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4906         ai_info *aip = &Ai_info[shipp->ai_index];
4907         vector  player_pos, enemy_pos;
4908
4909         // if we're avoiding a stealth ship, then we know where he is, update with no error
4910         if ( is_object_stealth_ship(En_objp) ) {
4911                 update_ai_stealth_info_with_error(aip/*, 1*/);
4912         }
4913
4914         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
4915         vm_vec_sub(&vec_to_enemy, &enemy_pos, &Pl_objp->pos);
4916
4917         dist = vm_vec_normalize(&vec_to_enemy);
4918         away_dot = -vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_to_enemy);
4919         
4920         if ((sip->max_vel.xyz.x > 0.0f) || (sip->max_vel.xyz.y > 0.0f)) {
4921                 if (vm_vec_dot(&Pl_objp->orient.v.rvec, &vec_to_enemy) > 0.0f) {
4922                         AI_ci.sideways = -1.0f;
4923                 } else {
4924                         AI_ci.sideways = 1.0f;
4925                 }
4926                 if (vm_vec_dot(&Pl_objp->orient.v.uvec, &vec_to_enemy) > 0.0f) {
4927                         AI_ci.vertical = -1.0f;
4928                 } else {
4929                         AI_ci.vertical = 1.0f;
4930                 }
4931         }               
4932
4933         //nprintf(("AI", "Frame %i: Sliding: %s %s\n", Framecount, AI_ci.sideways < 0 ? "left" : "right", AI_ci.vertical < 0 ? "down" : "up" ));
4934         // nprintf(("AI", "away_dot = %6.3f, dist = %7.2f, dist/radsum = %6.3f\n", away_dot, dist, dist/(Pl_objp->radius + En_objp->radius)));
4935
4936         //      If in front of enemy, turn away from it.
4937         //      If behind enemy, try to get fully behind it.
4938         if (away_dot < 0.0f) {
4939                 turn_away_from_point(Pl_objp, &enemy_pos, Pl_objp->phys_info.speed);
4940         } else {
4941                 vector  goal_pos;
4942
4943                 vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.fvec, -100.0f);
4944                 turn_towards_point(Pl_objp, &goal_pos, NULL, Pl_objp->phys_info.speed);
4945         }
4946
4947         //      Set speed.
4948         float   radsum = Pl_objp->radius + En_objp->radius;
4949
4950         if (dist < radsum)
4951                 accelerate_ship(aip, max(away_dot, 0.2f));
4952         else if (dist < 2*radsum)
4953                 accelerate_ship(aip, max(away_dot, (dist - radsum) / radsum)+0.2f);
4954         else
4955                 accelerate_ship(aip, 1.0f);
4956
4957 }
4958
4959 //      Maybe it's time to resume the previous AI mode in aip->previous_mode.
4960 //      Each type of previous_mode has its own criteria on when to resume.
4961 //      Return true if previous mode was resumed.
4962 int maybe_resume_previous_mode(object *objp, ai_info *aip)
4963 {
4964         //      Only (maybe) resume previous goal if current goal is dynamic.
4965         if (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC)
4966                 return 0;
4967
4968         if (aip->mode == AIM_EVADE_WEAPON) {
4969                 if (timestamp_elapsed(aip->mode_time) || (((aip->nearest_locked_object == -1) || (Objects[aip->nearest_locked_object].type != OBJ_WEAPON)) && (aip->danger_weapon_objnum == -1))) {
4970                         Assert(aip->previous_mode != AIM_EVADE_WEAPON);
4971                         aip->mode = aip->previous_mode;
4972                         aip->submode = aip->previous_submode;
4973                         aip->submode_start_time = Missiontime;
4974                         aip->active_goal = AI_GOAL_NONE;
4975                         aip->mode_time = -1;                    //      Means do forever.
4976                         return 1;
4977                 }
4978         } else if ( aip->previous_mode == AIM_GUARD) {
4979                 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
4980                         object  *guard_objp;
4981                         float   dist;
4982
4983                         guard_objp = &Objects[aip->guard_objnum];
4984                         dist = vm_vec_dist_quick(&guard_objp->pos, &objp->pos);
4985
4986                         //      If guarding ship is far away from guardee and enemy is far away from guardee,
4987                         //      then stop chasing and resume guarding.
4988                         if (dist > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4989                                 if ((En_objp != NULL) && (En_objp->type == OBJ_SHIP)) {
4990                                         if (vm_vec_dist_quick(&guard_objp->pos, &En_objp->pos) > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4991                                                 Assert(aip->previous_mode == AIM_GUARD);
4992                                                 aip->mode = aip->previous_mode;
4993                                                 aip->submode = AIS_GUARD_PATROL;
4994                                                 aip->active_goal = AI_GOAL_NONE;
4995                                                 return 1;
4996                                         }
4997                                 }
4998                         }
4999                 }
5000         }
5001
5002         return 0;
5003
5004 }
5005
5006 //      Call this function if you want something to happen on average every N quarters of a second.
5007 //      The truth value returned by this function will be the same for any given quarter second interval.
5008 //      The value "num" is only passed in to get asynchronous behavior for different objects.
5009 //      modulus == 1 will always return true.
5010 //      modulus == 2 will return true half the time.
5011 //      modulus == 16 will return true for one quarter second interval every four seconds.
5012 int static_rand_timed(int num, int modulus)
5013 {
5014         if (modulus < 2)
5015                 return 1;
5016         else {
5017                 int     t;
5018
5019                 t = Missiontime >> 18;          //      Get time in quarters of a second
5020                 t += num;
5021
5022                 return !(t % modulus);
5023         }
5024 }
5025
5026 //      Maybe fire afterburner based on AI class
5027 int ai_maybe_fire_afterburner(object *objp, ai_info *aip)
5028 {
5029         if (aip->ai_class == 0)
5030                 return 0;               //      Lowest level never aburners away
5031         else  {
5032                 //      Maybe don't afterburner because of a potential collision with the player.
5033                 //      If not multiplayer, near player and player in front, probably don't afterburner.
5034                 if (!(Game_mode & GM_MULTIPLAYER)) {
5035                         if (Ships[objp->instance].team == Player_ship->team) {
5036                                 float   dist;
5037
5038                                 dist = vm_vec_dist_quick(&objp->pos, &Player_obj->pos) - Player_obj->radius - objp->radius;
5039                                 if (dist < 150.0f) {
5040                                         vector  v2p;
5041                                         float           dot;
5042
5043                                         vm_vec_normalized_dir(&v2p, &Player_obj->pos, &objp->pos);
5044                                         dot = vm_vec_dot(&v2p, &objp->orient.v.fvec);
5045
5046                                         if (dot > 0.0f) {
5047                                                 if (dot * dist > 50.0f)
5048                                                         return 0;
5049                                         }
5050                                 }
5051                         }
5052                 }
5053
5054                 if (aip->ai_class >= Num_ai_classes-2)
5055                         return 1;               //      Highest two levels always aburner away.
5056                 else {
5057                         return static_rand_timed(objp-Objects, Num_ai_classes - aip->ai_class);
5058                 }
5059         }
5060 }
5061
5062 //      Maybe engage afterburner after being hit by an object.
5063 void maybe_afterburner_after_ship_hit(object *objp, ai_info *aip, object *en_objp)
5064 {
5065         //      Only do if facing a little away.
5066         if (en_objp != NULL) {
5067                 vector  v2e;
5068
5069                 vm_vec_normalized_dir(&v2e, &en_objp->pos, &objp->pos);
5070                 if (vm_vec_dot(&v2e, &objp->orient.v.fvec) > -0.5f)
5071                         return;
5072         }
5073
5074         if (!( objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5075                 if (ai_maybe_fire_afterburner(objp, aip)) {
5076                         afterburners_start(objp);
5077                         aip->afterburner_stop_time = Missiontime + F1_0/2;
5078                 }
5079         }
5080 }
5081
5082 //      Return true if object *objp is an instructor.
5083 //      Is an instructor if name begins INSTRUCTOR_SHIP_NAME else not.
5084 int is_instructor(object *objp)
5085 {
5086         return !strnicmp(Ships[objp->instance].ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME));
5087 }
5088
5089 //      Evade the weapon aip->danger_weapon_objnum
5090 //      If it's not valid, do a quick out.
5091 //      Evade by accelerating hard.
5092 //      If necessary, turn hard left or hard right.
5093 void evade_weapon()
5094 {
5095         object  *weapon_objp = NULL;
5096         object  *unlocked_weapon_objp = NULL, *locked_weapon_objp = NULL;
5097         vector  weapon_pos, player_pos, goal_point;
5098         vector  vec_from_enemy;
5099         float           dot_from_enemy, dot_to_enemy;
5100         float           dist;
5101         ship            *shipp = &Ships[Pl_objp->instance];
5102         ai_info *aip = &Ai_info[shipp->ai_index];
5103
5104         if (is_instructor(Pl_objp))
5105                 return;
5106
5107         //      Make sure we're actually being attacked.
5108         //      Favor locked objects.
5109         if (aip->nearest_locked_object != -1) {
5110                 if (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)
5111                         locked_weapon_objp = &Objects[aip->nearest_locked_object];
5112         }
5113         
5114         if (aip->danger_weapon_objnum != -1)
5115                 if (Objects[aip->danger_weapon_objnum].signature == aip->danger_weapon_signature)
5116                         unlocked_weapon_objp = &Objects[aip->danger_weapon_objnum];
5117                 else
5118                         aip->danger_weapon_objnum = -1;         //      Signatures don't match, so no longer endangered.
5119
5120         if (locked_weapon_objp != NULL) {
5121                 if (unlocked_weapon_objp != NULL) {
5122                         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))
5123                                 weapon_objp = locked_weapon_objp;
5124                         else
5125                                 weapon_objp = unlocked_weapon_objp;
5126                 } else
5127                         weapon_objp = locked_weapon_objp;
5128         } else if (unlocked_weapon_objp != NULL)
5129                 weapon_objp = unlocked_weapon_objp;
5130         else {
5131                 if (aip->mode == AIM_EVADE_WEAPON)
5132                         maybe_resume_previous_mode(Pl_objp, aip);
5133                 return;
5134         }
5135
5136         Assert(weapon_objp != NULL);
5137
5138         if (weapon_objp->type != OBJ_WEAPON) {
5139                 if (aip->mode == AIM_EVADE_WEAPON)
5140                         maybe_resume_previous_mode(Pl_objp, aip);
5141                 return;
5142         }
5143         
5144         weapon_pos = weapon_objp->pos;
5145         player_pos = Pl_objp->pos;
5146
5147         //      Make speed based on skill level, varying at highest skill level, which is harder to hit.
5148         accelerate_ship(aip, 1.0f);
5149
5150         dist = vm_vec_normalized_dir(&vec_from_enemy, &player_pos, &weapon_pos);
5151
5152         dot_to_enemy = -vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_from_enemy);
5153         dot_from_enemy = vm_vec_dot(&weapon_objp->orient.v.fvec, &vec_from_enemy);
5154         //nprintf(("AI", "dot from enemy = %7.3f\n", dot_from_enemy));
5155
5156         //      If shot is incoming...
5157         if (dot_from_enemy < 0.3f) {
5158                 if (weapon_objp == unlocked_weapon_objp)
5159                         aip->danger_weapon_objnum = -1;
5160                 return;
5161         } else if (dot_from_enemy > 0.7f) {
5162                 if (dist < 200.0f) {
5163                         if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5164                                 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
5165                                         //nprintf(("AI", "Frame %i, turning on afterburner.\n", AI_FrameCount));
5166                                         afterburners_start(Pl_objp);
5167                                         aip->afterburner_stop_time = Missiontime + F1_0/2;
5168                                 }
5169                         }
5170                 }
5171
5172                 //      If we're sort of pointing towards it...
5173                 if ((dot_to_enemy < -0.5f) || (dot_to_enemy > 0.5f)) {
5174                         float   rdot;
5175
5176                         //      Turn hard left or right, depending on which gets out of way quicker.
5177                         rdot = vm_vec_dot(&Pl_objp->orient.v.rvec, &vec_from_enemy);
5178
5179                         if ((rdot < -0.5f) || (rdot > 0.5f))
5180                                 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.v.rvec, -200.0f);
5181                         else
5182                                 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.v.rvec, 200.0f);
5183
5184                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
5185                 }
5186         }
5187
5188 }
5189
5190 //      Use sliding and backwards moving to face enemy.
5191 //      (Coded 2/20/98.  Works fine, but it's hard to see how to integrate it into the AI system.
5192 //       Typically ships are moving so fast that a little sliding isn't enough to gain an advantage.
5193 //       It's currently used to avoid collisions and could be used to evade weapon fire, but the latter
5194 //       would be frustrating, I think.
5195 //       This function is currently not called.)
5196 void slide_face_ship()
5197 {
5198         ship_info       *sip;
5199
5200         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
5201
5202         //      If can't slide, return.
5203         if ((sip->max_vel.xyz.x == 0.0f) && (sip->max_vel.xyz.y == 0.0f))
5204                 return;
5205
5206         vector  goal_pos;
5207         float           dot_from_enemy, dot_to_enemy;
5208         vector  vec_from_enemy, vec_to_goal;
5209         float           dist;
5210         float           up, right;
5211         ai_info         *aip;
5212
5213         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
5214
5215         dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
5216
5217         ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
5218
5219         dot_from_enemy = vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.fvec);
5220         dot_to_enemy = -vm_vec_dot(&vec_from_enemy, &Pl_objp->orient.v.fvec);
5221
5222         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.rvec) > 0.0f)
5223                 right = 1.0f;
5224         else
5225                 right = -1.0f;
5226
5227         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.uvec) > 0.0f)
5228                 up = 1.0f;
5229         else
5230                 up = -1.0f;
5231
5232         vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.rvec, right * 200.0f);
5233         vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.uvec, up * 200.0f);
5234
5235         vm_vec_normalized_dir(&vec_to_goal, &goal_pos, &Pl_objp->pos);
5236
5237         if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.v.rvec) > 0.0f)
5238                 AI_ci.sideways = 1.0f;
5239         else
5240                 AI_ci.sideways = -1.0f;
5241
5242         if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.v.uvec) > 0.0f)
5243                 AI_ci.vertical = 1.0f;
5244         else
5245                 AI_ci.vertical = -1.0f;
5246
5247         if (dist < 200.0f) {
5248                 if (dot_from_enemy < 0.7f)
5249                         accelerate_ship(aip, -1.0f);
5250                 else
5251                         accelerate_ship(aip, dot_from_enemy + 0.5f);
5252         } else {
5253                 if (dot_from_enemy < 0.7f) {
5254                         accelerate_ship(aip, 0.2f);
5255                 } else {
5256                         accelerate_ship(aip, 1.0f);
5257                 }
5258         }
5259 }
5260
5261 //      General code for handling one ship evading another.
5262 //      Problem: This code is also used for avoiding an impending collision.
5263 //      In such a case, it is not good to go to max speed, which is often good
5264 //      for a certain kind of evasion.
5265 void evade_ship()
5266 {
5267         vector  player_pos, enemy_pos, goal_point;
5268         vector  vec_from_enemy;
5269         float           dot_from_enemy;
5270         float           dist;
5271         ship            *shipp = &Ships[Pl_objp->instance];
5272         ship_info       *sip = &Ship_info[shipp->ship_info_index];
5273         ai_info *aip = &Ai_info[shipp->ai_index];
5274         float           bank_override = 0.0f;
5275
5276         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
5277
5278         //      Make speed based on skill level, varying at highest skill level, which is harder to hit.
5279         if (Game_skill_level == NUM_SKILL_LEVELS-1) {
5280                 int     rand_int;
5281                 float   accel_val;
5282
5283                 rand_int = static_rand(Pl_objp-Objects);
5284                 accel_val = (float) (((Missiontime^rand_int) >> 14) & 0x0f)/32.0f + 0.5f;
5285                 accelerate_ship(aip, accel_val);
5286                 //nprintf(("AI", "Accel value = %7.3f\n", accel_val));
5287         } else
5288                 accelerate_ship(aip, (float) (Game_skill_level+2) / (NUM_SKILL_LEVELS+1));
5289
5290         if ((Missiontime - aip->submode_start_time > F1_0/2) && (sip->afterburner_fuel_capacity > 0.0f)) {
5291                 float percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
5292                 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
5293                         afterburners_start(Pl_objp);
5294                         aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
5295                 }
5296         }
5297
5298         vm_vec_sub(&vec_from_enemy, &player_pos, &enemy_pos);
5299
5300         dist = vm_vec_normalize(&vec_from_enemy);
5301         dot_from_enemy = vm_vec_dot(&En_objp->orient.v.fvec, &vec_from_enemy);
5302
5303         if (dist > 250.0f) {
5304                 vector  gp1, gp2;
5305                 //      If far away from enemy, circle, going to nearer of point far off left or right wing
5306                 vm_vec_scale_add(&gp1, &enemy_pos, &En_objp->orient.v.rvec, 250.0f);
5307                 vm_vec_scale_add(&gp2, &enemy_pos, &En_objp->orient.v.rvec, -250.0f);
5308                 if (vm_vec_dist_quick(&gp1, &Pl_objp->pos) < vm_vec_dist_quick(&gp2, &Pl_objp->pos))
5309                         goal_point = gp1;
5310                 else
5311                         goal_point = gp2;
5312         } else if (dot_from_enemy < 0.1f) {
5313                 //      If already close to behind, goal is to get completely behind.
5314                 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.fvec, -1000.0f);
5315         } else if (dot_from_enemy > 0.9f) {
5316                 //      If enemy pointing almost right at self, and self pointing close to enemy, turn away from
5317                 vector  vec_to_enemy;
5318                 float           dot_to_enemy;
5319
5320                 vm_vec_sub(&vec_to_enemy, &enemy_pos, &player_pos);
5321
5322                 vm_vec_normalize(&vec_to_enemy);
5323                 dot_to_enemy = vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_to_enemy);
5324                 if (dot_to_enemy > 0.75f) {
5325                         //      Used to go to En_objp's right vector, but due to banking while turning, that
5326                         //      caused flying in an odd spiral.
5327                         vm_vec_scale_add(&goal_point, &enemy_pos, &Pl_objp->orient.v.rvec, 1000.0f);
5328                         if (dist < 100.0f)
5329                                 bank_override = Pl_objp->phys_info.speed; 
5330                 } else {
5331                         bank_override = Pl_objp->phys_info.speed;                       //      In enemy's sights, not pointing at him, twirl away.
5332                         // nprintf(("Mike", " Do sumpin' else."));
5333                         goto evade_ship_l1;
5334                 }
5335         } else {
5336 evade_ship_l1: ;
5337                 if (aip->ai_evasion > myrand()*100.0f/32767.0f) {
5338                         int     temp;
5339                         float   scale;
5340                         float   psrandval;      //      some value close to zero to choose whether to turn right or left.
5341
5342                         psrandval = (float) (((Missiontime >> 14) & 0x0f) - 8); //      Value between -8 and 7
5343                         psrandval = psrandval/16.0f;                                                    //      Value between -1/2 and 1/2 (approx)
5344
5345                         //      If not close to behind, turn towards his right or left vector, whichever won't cross his path.
5346                         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.rvec) > psrandval) {
5347                                 scale = 1000.0f;
5348                         } else {
5349                                 scale = -1000.0f;
5350                         }
5351
5352                         vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.rvec, scale);
5353
5354                         temp = ((Missiontime >> 16) & 0x07);
5355                         temp = ((temp * (temp+1)) % 16)/2 - 4;
5356                         if ((psrandval == 0) && (temp == 0))
5357                                 temp = 3;
5358
5359                         scale = 200.0f * temp;
5360
5361                         vm_vec_scale_add2(&goal_point, &En_objp->orient.v.uvec, scale);
5362                 } else {
5363                         //      No evasion this frame, but continue with previous turn.
5364                         //      Reason: If you don't, you lose rotational momentum.  Turning every other frame,
5365                         //      and not in between results in a very slow turn because of loss of momentum.
5366                         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))
5367                                 goal_point = aip->prev_goal_point;
5368                         else
5369                                 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.rvec, 100.0f);
5370                 }
5371         }
5372
5373         // nprintf(("Mike", "Goal point = %7.1f %7.1f %7.1f\n", goal_point.xyz.x, goal_point.xyz.y, goal_point.xyz.z));
5374         turn_towards_point(Pl_objp, &goal_point, NULL, bank_override);
5375
5376         aip->prev_goal_point = goal_point;
5377 }
5378
5379 //      --------------------------------------------------------------------------
5380 //      Fly in a manner making it difficult for opponent to attack.
5381 void ai_evade()
5382 {
5383         evade_ship();
5384 }
5385
5386 /*
5387 // -------------------------------------------------------------------
5388 //      Refine predicted enemy position because enemy will move while we move
5389 //      towards predicted enemy position.
5390 //      last_delta_vec is stuffed with size of polishing in last step.  This small amount
5391 //      can be used to perturb the predicted position to make firing not be exact.
5392 //      This function will almost always undershoot actual position, assuming both ships
5393 //      are moving at constant speed.  But with even one polishing step, the error should
5394 //      be under 1%. The number of polishing steps is specified in the parameter num_polish_steps.
5395 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)
5396 {
5397         int     iteration;
5398         vector  player_pos = pobjp->pos;
5399         vector  enemy_pos = *predicted_enemy_pos;
5400         physics_info    *en_physp = &eobjp->phys_info;
5401         float           time_to_enemy;
5402         vector  last_predicted_enemy_pos = *predicted_enemy_pos;
5403         
5404         vm_vec_zero(last_delta_vec);
5405
5406         for (iteration=0; iteration < num_polish_steps; iteration++) {
5407                 dist_to_enemy = vm_vec_dist_quick(predicted_enemy_pos, &player_pos);
5408                 time_to_enemy = compute_time_to_enemy(dist_to_enemy, pobjp, eobjp);
5409                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, time_to_enemy);
5410                 vm_vec_sub(last_delta_vec, predicted_enemy_pos, &last_predicted_enemy_pos);
5411                 last_predicted_enemy_pos= *predicted_enemy_pos;
5412         }
5413 }
5414 */
5415
5416 /*
5417 Relevant variables are:
5418         best_dot_to_enemy               best dot product to enemy in last BEST_DOT_TIME seconds
5419         best_dot_to_time                time at which best dot occurred
5420         best_dot_from_enemy     best dot product for enemy to player in last BEST_DOT_TIME seconds
5421         best_dot_from_time      time at which best dot occurred
5422         submode_start_time      time at which we entered the current submode
5423         previous_submode                previous submode, get it?
5424 Legal submodes are:
5425         CONTINUOUS_TURN vector_id {0..3 = right, -right, up, -up}
5426         ATTACK
5427         EVADE_SQUIGGLE
5428         EVADE_BRAKE
5429 */
5430
5431 float   G_collision_time;
5432 vector  G_predicted_pos, G_fire_pos;
5433
5434 /*
5435 void show_firing_diag()
5436 {
5437         float           dot;
5438         vector  v2t;
5439         vector  pos1, pos2;
5440         float           dist;
5441
5442         if (G_collision_time == 0.0f)
5443                 return;
5444
5445         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",
5446                 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));
5447         vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5448         dot = vm_vec_dot(&v2t, &Pl_objp->orient.v.fvec);
5449         mprintf(("Dot of v.fvec and vector to predicted position = %10.7f (%7.3f degrees)\n", dot, acos(dot)*180.0f/3.141592654f));
5450
5451         vm_vec_scale_add(&pos1, &En_objp->pos, &En_objp->phys_info.vel, G_collision_time);
5452         vm_vec_scale_add(&pos2, &G_fire_pos, &Pl_objp->orient.v.fvec, G_collision_time*300.0f);
5453         dist = vm_vec_dist(&pos1, &pos2);
5454
5455         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));
5456 }
5457 */
5458
5459 //      If:
5460 //              flags & WIF_PUNCTURE
5461 //      Then Select a Puncture weapon.
5462 //      Else
5463 //              Select Any ol' weapon.
5464 //      Returns primary_bank index.
5465 int ai_select_primary_weapon(object *objp, object *other_objp, int flags)
5466 {
5467         ship    *shipp = &Ships[objp->instance];
5468         ship_weapon *swp = &shipp->weapons;
5469         ship_info *sip;
5470
5471         //Assert( other_objp != NULL );
5472         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5473
5474         sip = &Ship_info[shipp->ship_info_index];
5475
5476         if (flags & WIF_PUNCTURE) {
5477                 if (swp->current_primary_bank >= 0) {
5478                         int     bank_index;
5479
5480                         bank_index = swp->current_primary_bank;
5481
5482                         if (Weapon_info[swp->primary_bank_weapons[bank_index]].wi_flags & WIF_PUNCTURE) {
5483                                 //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[bank_index]].name));
5484                                 return swp->current_primary_bank;
5485                         }
5486                 }
5487                 for (int i=0; i<swp->num_primary_banks; i++) {
5488                         int     weapon_info_index;
5489
5490                         weapon_info_index = swp->primary_bank_weapons[i];
5491
5492                         if (weapon_info_index > -1){
5493                                 if (Weapon_info[weapon_info_index].wi_flags & WIF_PUNCTURE) {
5494                                         swp->current_primary_bank = i;
5495                                         //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5496                                         return i;
5497                                 }
5498                         }
5499                 }
5500                 
5501                 // AL 26-3-98: If we couldn't find a puncture weapon, pick first available weapon if one isn't active
5502                 if ( swp->current_primary_bank < 0 ) {
5503                         if ( swp->num_primary_banks > 0 ) {
5504                                 swp->current_primary_bank = 0;
5505                         }
5506                 }
5507
5508         } else {                //      Don't need to be using a puncture weapon.
5509                 if (swp->current_primary_bank >= 0) {
5510                         if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)){
5511                                 return swp->current_primary_bank;
5512                         }
5513                 }
5514                 for (int i=0; i<swp->num_primary_banks; i++) {
5515                         if (swp->primary_bank_weapons[i] > -1) {
5516                                 if (!(Weapon_info[swp->primary_bank_weapons[i]].wi_flags & WIF_PUNCTURE)) {
5517                                         swp->current_primary_bank = i;
5518                                         nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5519                                         return i;
5520                                 }
5521                         }
5522                 }
5523                 //      Wasn't able to find a non-puncture weapon.  Stick with what we have.
5524         }
5525
5526         Assert( swp->current_primary_bank != -1 );              // get Alan or Allender
5527
5528         return swp->current_primary_bank;
5529 }
5530
5531 //      --------------------------------------------------------------------------
5532 //      Maybe link primary weapons.
5533 void set_primary_weapon_linkage(object *objp)
5534 {
5535         ship            *shipp;
5536         ai_info *aip;
5537
5538         shipp = &Ships[objp->instance];
5539         aip     = &Ai_info[shipp->ai_index];
5540
5541         shipp->flags &= ~SF_PRIMARY_LINKED;
5542
5543         if (Num_weapons > (int) (MAX_WEAPONS * 0.75f)) {
5544                 if (shipp->flags & SF_PRIMARY_LINKED)
5545                         nprintf(("AI", "Frame %i, ship %s: Unlinking primaries.\n", Framecount, shipp->ship_name));
5546                 shipp->flags &= ~SF_PRIMARY_LINKED;
5547                 return;         //      If low on slots, don't link.
5548         }
5549
5550         shipp->flags &= ~SF_PRIMARY_LINKED;
5551
5552         // AL: ensure target is a ship!
5553         if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
5554                 // If trying to destroy a big ship (i.e., not disable/disarm), always unleash all weapons
5555                 if ( ship_get_SIF(&Ships[Objects[aip->target_objnum].instance]) & SIF_BIG_SHIP) {
5556                         if ( aip->targeted_subsys == NULL ) {
5557                                 shipp->flags |= SF_PRIMARY_LINKED;
5558                                 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5559                                 return;
5560                         }
5561                 }
5562         }
5563
5564         // AL 2-11-98: If ship has a disarm or disable goal, don't link unless both weapons are
5565         //                                      puncture weapons
5566         if ( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) ) {
5567                 if ( aip->goals[aip->active_goal].ai_mode & (AI_GOAL_DISABLE_SHIP|AI_GOAL_DISARM_SHIP) ) {
5568                         ship_weapon     *swp;
5569                         swp = &shipp->weapons;
5570                         // only continue if both primaries are puncture weapons
5571                         if ( swp->num_primary_banks == 2 ) {
5572                                 if ( !(Weapon_info[swp->primary_bank_weapons[0]].wi_flags & WIF_PUNCTURE) ) 
5573                                         return;
5574                                 if ( !(Weapon_info[swp->primary_bank_weapons[1]].wi_flags & WIF_PUNCTURE) ) 
5575                                         return;
5576                         }
5577                 }
5578         }
5579
5580         //      Don't want all ships always linking weapons at start, so asynchronize.
5581         if (Missiontime < i2f(30))
5582                 return;
5583         else if (Missiontime < i2f(120)) {
5584                 int r = static_rand((Missiontime >> 17) ^ OBJ_INDEX(objp));
5585                 if ( (r&3) != 0)
5586                         return;
5587         }
5588
5589         if (shipp->weapon_energy > Link_energy_levels_always[Game_skill_level]) {
5590                 shipp->flags |= SF_PRIMARY_LINKED;
5591         } else if (shipp->weapon_energy > Link_energy_levels_maybe[Game_skill_level]) {
5592                 if (objp->hull_strength < Ship_info[shipp->ship_info_index].initial_hull_strength/3.0f)
5593                         shipp->flags |= SF_PRIMARY_LINKED;
5594         }
5595 }
5596
5597 //      --------------------------------------------------------------------------
5598 //      Fire the current primary weapon.
5599 //      *objp is the object to fire from.
5600 void ai_fire_primary_weapon(object *objp)
5601 {
5602         ship                    *shipp = &Ships[objp->instance];
5603         ship_weapon     *swp = &shipp->weapons;
5604         ship_info       *sip;
5605         ai_info         *aip;
5606         object          *enemy_objp;
5607
5608         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5609         sip = &Ship_info[shipp->ship_info_index];
5610
5611         aip = &Ai_info[shipp->ai_index];
5612
5613         //      If low on slots, fire a little less often.
5614         if (Num_weapons > (int) (0.9f * MAX_WEAPONS)) {
5615                 if (frand() > 0.5f) {
5616                         nprintf(("AI", "Frame %i, %s not fire.\n", Framecount, shipp->ship_name));
5617                         return;
5618                 }
5619         }
5620
5621         if (!Ai_firing_enabled){
5622                 return;
5623         }
5624
5625         if (aip->target_objnum != -1){
5626                 enemy_objp = &Objects[aip->target_objnum];
5627         } else {
5628                 enemy_objp = NULL;
5629         }
5630
5631         if ( (swp->current_primary_bank < 0) || (swp->current_primary_bank >= swp->num_primary_banks) || timestamp_elapsed(aip->primary_select_timestamp)) {
5632                 int     flags = 0;
5633                 // AL 2-11-98: If attacking any subsystem (not just engines), use disrupter weapon
5634 //              if ((aip->targeted_subsys != NULL) && (aip->targeted_subsys->system_info->type == SUBSYSTEM_ENGINE)) {
5635                 if ( aip->targeted_subsys != NULL ) {
5636                         flags = WIF_PUNCTURE;
5637                 }
5638                 ai_select_primary_weapon(objp, enemy_objp, flags);
5639                 ship_primary_changed(shipp);    // AL: maybe send multiplayer information when AI ship changes primaries
5640                 aip->primary_select_timestamp = timestamp(5 * 1000);    //      Maybe change primary weapon five seconds from now.
5641         }
5642
5643         //      If pointing nearly at predicted collision point of target, bash orientation to be perfectly pointing.
5644         float   dot;
5645         vector  v2t;
5646
5647 //      if (!IS_VEC_NULL(&G_predicted_pos)) {
5648         if (!( vm_vec_mag_quick(&G_predicted_pos) < AICODE_SMALL_MAGNITUDE )) {
5649                 if ( !vm_vec_cmp(&G_predicted_pos, &G_fire_pos) ) {
5650                         nprintf(("Warning", "Avoid NULL vector assert.. why are G_predicted_pos and G_fire_pos the same?\n"));
5651                 } else {
5652                         vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5653                         dot = vm_vec_dot(&v2t, &objp->orient.v.fvec);
5654                         if (dot > .998629534f){ //      if within 3.0 degrees of desired heading, bash
5655                                 vm_vector_2_matrix(&objp->orient, &v2t, &objp->orient.v.uvec, NULL);
5656                         }
5657                 }
5658         }
5659
5660         //      Make sure not firing at a protected ship unless firing at a live subsystem.
5661         //      Note: This happens every time the ship tries to fire, perhaps every frame.
5662         //      Should be wrapped in a timestamp, same one that enables it to fire, but that is complicated
5663         //      by multiple banks it can fire from.
5664         if (aip->target_objnum != -1) {
5665                 object  *tobjp = &Objects[aip->target_objnum];
5666                 if (tobjp->flags & OF_PROTECTED) {
5667                         if (aip->targeted_subsys != NULL) {
5668                                 int     type;
5669
5670                                 type = aip->targeted_subsys->system_info->type;
5671                                 if (ship_get_subsystem_strength(&Ships[tobjp->instance], type) == 0.0f) {
5672                                         aip->target_objnum = -1;
5673                                         return;
5674                                 }
5675                         } else {
5676                                 aip->target_objnum = -1;
5677                                 return;
5678                         }
5679                 }
5680         }
5681
5682         //      If enemy is protected, not firing a puncture weapon and enemy's hull is low, don't fire.
5683         if ((enemy_objp != NULL) && (enemy_objp->flags & OF_PROTECTED)) {
5684                 // AL: 3-6-98: Check if current_primary_bank is valid
5685                 if ((enemy_objp->hull_strength < 750.0f) && 
5686                         ((aip->targeted_subsys == NULL) || (enemy_objp->hull_strength < aip->targeted_subsys->current_hits + 50.0f)) &&
5687                         (swp->current_primary_bank >= 0) ) {
5688                         if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)) {
5689                                 //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));
5690                                 swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(1000);
5691                                 return;
5692                         }
5693
5694                         /*
5695                         int     num_attacking;
5696                         num_attacking = num_enemies_attacking(enemy_objp-Objects);
5697                         if (enemy_objp->hull_strength / num_attacking < 200.0f) {
5698                                 if (frand() < 0.75f) {
5699                                         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));
5700                                         swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(500);
5701                                         return;
5702                                 }
5703                         }
5704                         */
5705                 }
5706         }
5707
5708         set_primary_weapon_linkage(objp);
5709         
5710         // I think this will properly solve the problem
5711         // fire non-streaming weapons
5712         ship_fire_primary(objp, 0);
5713         
5714         // fire streaming weapons
5715         shipp->flags |= SF_TRIGGER_DOWN;
5716         ship_fire_primary(objp, 1);
5717         shipp->flags &= ~SF_TRIGGER_DOWN;
5718 }
5719
5720 //      --------------------------------------------------------------------------
5721 //      Return number of nearby enemy fighters.
5722 //      threshold is the distance within which a ship is considered near.
5723 //
5724 // input:       enemy_team_mask =>      teams that are considered as an enemy
5725 //                              pos                                     =>      world position to measure ship distances from
5726 //                              threshold                       =>      max distance from pos to be considered "near"
5727 //
5728 // exit:                number of ships within threshold units of pos
5729 int num_nearby_fighters(int enemy_team_mask, vector *pos, float threshold)
5730 {
5731         ship_obj        *so;
5732         object  *ship_objp;
5733         int             count = 0;
5734
5735         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5736
5737                 ship_objp = &Objects[so->objnum];
5738
5739                 if (Ships[ship_objp->instance].team & enemy_team_mask) {
5740                         if (Ship_info[Ships[ship_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) {
5741                                 if (vm_vec_dist_quick(pos, &ship_objp->pos) < threshold)
5742                                         count++;
5743                         }
5744                 }
5745         }
5746
5747         return count;
5748 }
5749
5750 //      --------------------------------------------------------------------------
5751 //      Select secondary weapon to fire.
5752 //      Currently, 1/16/98:
5753 //              If 0 secondary weapons available, return -1
5754 //              If 1 available, use it.
5755 //              If 2 or more, if the current weapon is one of them, stick with it, otherwise choose a random one.
5756 //      priority1 and priority2 are Weapon_info[] bitmasks such as WIF_HOMING_ASPECT.  If any weapon has any bit in priority1
5757 //      set, that weapon will be selected.  If not, apply to priority2.  If neither, return -1, meaning no weapon selected.
5758 //      Note, priorityX have default values of -1, meaning if not set, they will match any weapon.
5759 //      Return value:
5760 //              bank index
5761 //      Should do this:
5762 //              Favor aspect seekers when attacking small ships faraway.
5763 //              Favor rapid fire dumbfire when attacking a large ship.
5764 //              Ignore heat seekers because we're not sure how they'll work.
5765 void ai_select_secondary_weapon(object *objp, ship_weapon *swp, int priority1 = -1, int priority2 = -1)
5766 {
5767         int     num_weapon_types;
5768         int     weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
5769         int     i;
5770         int     ignore_mask;
5771         int     initial_bank;
5772
5773         initial_bank = swp->current_secondary_bank;
5774
5775         //      Ignore bombs unless one of the priorities asks for them to be selected.
5776         if (WIF_HUGE & (priority1 | priority2))
5777                 ignore_mask = 0;
5778         else
5779                 ignore_mask = WIF_HUGE;
5780
5781         if (!(WIF_BOMBER_PLUS & (priority1 | priority2)))
5782                 ignore_mask |= WIF_BOMBER_PLUS;
5783
5784 #ifndef NDEBUG
5785         for (i=0; i<MAX_WEAPON_TYPES; i++) {
5786                 weapon_id_list[i] = -1;
5787                 weapon_bank_list[i] = -1;
5788         }
5789 #endif
5790
5791         //      Stuff weapon_bank_list with bank index of available weapons.
5792         num_weapon_types = get_available_secondary_weapons(objp, weapon_id_list, weapon_bank_list);
5793
5794         int     priority2_index = -1;
5795
5796         for (i=0; i<num_weapon_types; i++) {
5797                 int     wi_flags;
5798
5799                 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5800                 if (!(wi_flags & ignore_mask)) {                                        //      Maybe bombs are illegal.
5801                         if (wi_flags & priority1) {
5802                                 swp->current_secondary_bank = weapon_bank_list[i];                              //      Found first priority, return it.
5803                                 break;
5804                         } else if (wi_flags & priority2)
5805                                 priority2_index = weapon_bank_list[i];  //      Found second priority, but might still find first priority.
5806                 }
5807         }
5808
5809         //      If didn't find anything above, then pick any secondary weapon.
5810         if (i == num_weapon_types) {
5811                 swp->current_secondary_bank = priority2_index;  //      Assume we won't find anything.
5812                 if (priority2_index == -1) {
5813                         for (i=0; i<num_weapon_types; i++) {
5814                                 int     wi_flags;
5815
5816                                 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5817                                 if (!(wi_flags & ignore_mask)) {                                        //      Maybe bombs are illegal.
5818                                         if (swp->secondary_bank_ammo[i] > 0) {
5819                                                 swp->current_secondary_bank = i;
5820                                                 break;
5821                                         }
5822                                 }
5823                         }
5824                 }
5825         }
5826
5827         //      If switched banks, force reacquisition of aspect lock.
5828         if (swp->current_secondary_bank != initial_bank) {
5829                 ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
5830                 
5831                 aip->aspect_locked_time = 0.0f;
5832                 aip->current_target_is_locked = 0;
5833         }
5834
5835
5836         ship_secondary_changed(&Ships[objp->instance]); // AL: let multiplayer know if secondary bank has changed
5837         // nprintf(("AI", "Ship %s selected weapon %s\n", Ships[objp->instance].ship_name, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
5838 }
5839
5840 //      Return number of objects homing on object *target_objp
5841 int compute_num_homing_objects(object *target_objp)
5842 {
5843         object  *objp;
5844         int             count = 0;
5845
5846         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
5847                 if (objp->type == OBJ_WEAPON) {
5848                         if (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_HOMING) {
5849                                 if (Weapons[objp->instance].homing_object == target_objp) {
5850                                         count++;
5851                                 }
5852                         }
5853                 }
5854         }
5855
5856         return count;
5857 }
5858
5859 //      Object *firing_objp just fired weapon weapon_index (index in Weapon_info).
5860 //      If it's a shockwave weapon, tell your team about it!
5861 void ai_maybe_announce_shockwave_weapon(object *firing_objp, int weapon_index)
5862 {
5863         if ((firing_objp->type == OBJ_SHIP) && (Weapon_info[weapon_index].shockwave_speed > 0.0f)) {
5864                 ship_obj        *so;
5865                 int             firing_ship_team;
5866
5867                 firing_ship_team = Ships[firing_objp->instance].team;
5868
5869                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5870                         object  *A = &Objects[so->objnum];
5871                         Assert(A->type == OBJ_SHIP);
5872
5873                         if (Ships[A->instance].team == firing_ship_team) {
5874                                 ai_info *aip = &Ai_info[Ships[A->instance].ai_index];
5875                                 // AL 1-5-98: only avoid shockwave if not docked or repairing
5876                                 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
5877                                         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_WEAPON;
5878                                 }
5879                         }
5880                 }
5881         }
5882 }
5883
5884 //      Return total payload of all incoming missiles.
5885 float compute_incoming_payload(object *target_objp)
5886 {
5887         missile_obj     *mo;
5888         float                   payload = 0.0f;
5889
5890         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
5891                 object  *objp;
5892
5893                 objp = &Objects[mo->objnum];
5894                 Assert(objp->type == OBJ_WEAPON);
5895                 if (Weapons[objp->instance].homing_object == target_objp) {
5896                         payload += Weapon_info[Weapons[objp->instance].weapon_info_index].damage;
5897                 }
5898         }
5899
5900         return payload;
5901 }
5902
5903 //      --------------------------------------------------------------------------
5904 //      Return true if OK for *aip to fire its current weapon at its current target.
5905 //      Only reason this function returns false is:
5906 //              weapon is a homer
5907 //              targeted at player
5908 //                      OR:     player has too many homers targeted at him
5909 //                                      Missiontime in that dead zone in which can't fire at this player
5910 //      Note: If player is attacking a ship, that ship is allowed to fire at player.  Otherwise, we get in a situation in which
5911 //      player is attacking a large ship, but that large ship is not defending itself with missiles.
5912 int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip)
5913 {
5914         int     num_homers = 0;
5915         object  *tobjp = &Objects[target_objnum];
5916
5917         if (target_objnum > -1) {
5918                 // AL 3-4-98: Ensure objp target is a ship first 
5919                 if ( tobjp->type == OBJ_SHIP ) {
5920
5921                         // should not get this far. check if ship is protected from beam and weapon is type beam
5922                         if ( (wip->wi_flags & WIF_BEAM) && (tobjp->flags & OF_BEAM_PROTECTED) ) {
5923                                 Int3();
5924                                 return 0;
5925                         }
5926                         if (Ship_info[Ships[tobjp->instance].ship_info_index].flags & SIF_SMALL_SHIP) {
5927                                 num_homers = compute_num_homing_objects(&Objects[target_objnum]);
5928                         }
5929                 }
5930
5931                 //      If player, maybe fire based on Skill_level and number of incoming weapons.
5932                 //      If non-player, maybe fire based on payload of incoming weapons.
5933                 if (wip->wi_flags & WIF_HOMING) {
5934                         if ((target_objnum > -1) && (tobjp->flags & OF_PLAYER_SHIP)) {
5935                                 if (Ai_info[Ships[tobjp->instance].ai_index].target_objnum != objnum) {
5936                                         //      Don't allow AI ships to fire at player for fixed periods of time based on skill level.
5937                                         //      With 5 skill levels, at Very Easy, they fire in 1/7 of every 10 second interval.
5938                                         //      At Easy, 2/7...at Expert, 5/7
5939                                         int t = ((Missiontime /(65536*10)) ^ target_objnum ^ 0x01) % (NUM_SKILL_LEVELS+2);
5940                                         if (t > Game_skill_level) {
5941                                                 //nprintf(("AI", "Not OK to fire homer at time thing %i\n", t));
5942                                                 return 0;
5943                                         }
5944                                 }
5945                                 //nprintf(("AI", " IS OK to fire homer at time thing %i ***\n", t));
5946                                 int     swarmers = 0;
5947                                 if (wip->wi_flags & WIF_SWARM)
5948                                         swarmers = 2;   //      Note, always want to be able to fire swarmers if no currently incident homers.
5949                                 if (Max_allowed_player_homers[Game_skill_level] < num_homers + swarmers) {
5950                                         return 0;
5951                                 }
5952                         } else if (num_homers > 3) {
5953                                 float   incoming_payload;
5954
5955                                 incoming_payload = compute_incoming_payload(&Objects[target_objnum]);
5956
5957                                 if (incoming_payload > tobjp->hull_strength) {
5958                                         return 0;
5959                                 }
5960                         }
5961                 }
5962         }
5963
5964         return 1;
5965 }
5966
5967 //      --------------------------------------------------------------------------
5968 //      Fire a secondary weapon.
5969 //      Maybe choose to fire a different one.
5970 //      priority1 and priority2 are optional parameters with defaults = -1
5971 int ai_fire_secondary_weapon(object *objp, int priority1, int priority2)
5972 {
5973         ship_weapon *swp;
5974         ship    *shipp;
5975         ship_info *sip;
5976         int             current_bank;
5977         int             rval = 0;
5978
5979 #ifndef NDEBUG
5980         if (!Ai_firing_enabled)
5981                 return rval;
5982 #endif
5983
5984         Assert( objp != NULL );
5985         Assert(objp->type == OBJ_SHIP);
5986         shipp = &Ships[objp->instance];
5987         swp = &shipp->weapons;
5988
5989         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5990         sip = &Ship_info[shipp->ship_info_index];
5991
5992         //      Select secondary weapon.
5993         current_bank = swp->current_secondary_bank; //ai_select_secondary_weapon(objp, swp, priority1, priority2);
5994
5995         //nprintf(("AI", "Frame %i: Current bank = %i, ammo remaining = %i\n", Framecount, current_bank, swp->secondary_bank_ammo[current_bank]));
5996         if (current_bank == -1) {
5997                 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5998                 return rval;
5999         }
6000
6001         Assert(current_bank < shipp->weapons.num_secondary_banks);
6002
6003         weapon_info     *wip = &Weapon_info[shipp->weapons.secondary_bank_weapons[current_bank]];
6004
6005         if ((wip->wi_flags & WIF_HOMING_ASPECT) && (!Ai_info[shipp->ai_index].current_target_is_locked)) {
6006                 //nprintf(("AI", "Not firing secondary weapon because not aspect locked.\n"));
6007                 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
6008         } else if ((wip->wi_flags & WIF_BOMB) || (vm_vec_dist_quick(&objp->pos, &En_objp->pos) > 50.0f)) {
6009                 //      This might look dumb, firing a bomb even if closer than 50 meters, but the reason is, if you're carrying
6010                 //      bombs, delivering them is probably more important than surviving.
6011                 ai_info *aip;
6012
6013                 aip = &Ai_info[shipp->ai_index];
6014                 
6015                 //      Note, maybe don't fire if firing at player and any homers yet fired.
6016                 //      Decreasing chance to fire the more homers are incoming on player.
6017                 if (check_ok_to_fire(OBJ_INDEX(objp), aip->target_objnum, wip)) {
6018                         if (ship_fire_secondary(objp)) {
6019                                 rval = 1;
6020                                 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
6021                                 //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));
6022                         }
6023
6024                 } else {
6025                         swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
6026                 }
6027         }
6028
6029         return rval;
6030 }
6031
6032 //      Return true if it looks like obj1, if continuing to move along current vector, will
6033 //      collide with obj2.
6034 int might_collide_with_ship(object *obj1, object *obj2, float dot_to_enemy, float dist_to_enemy, float duration)
6035 {
6036         if (obj1->phys_info.speed * duration + 2*(obj1->radius + obj2->radius) > dist_to_enemy)
6037                 if (dot_to_enemy > 0.8f - 2*(obj1->radius + obj2->radius)/dist_to_enemy)
6038                         return objects_will_collide(obj1, obj2, duration, 2.0f);
6039
6040 //              BABY - 
6041 //              CONDITION 1, dist_to_enemy < o1_rad + o2_rad + (obj1.speed + obj2.speed) * time + 50
6042         
6043         return 0;
6044
6045 }
6046
6047 //      --------------------------------------------------------------------------
6048 //      Return true if ship *objp firing a laser believes it will hit a teammate.
6049 int might_hit_teammate(object *firing_objp)
6050 {
6051         int             team;
6052         object  *objp;
6053         ship_obj        *so;
6054
6055         team = Ships[firing_objp->instance].team;
6056
6057         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6058                 objp = &Objects[so->objnum];
6059                 if (Ships[objp->instance].team == team) {
6060                         float           dist, dot;
6061                         vector  vec_to_objp;
6062
6063                         vm_vec_sub(&vec_to_objp, &firing_objp->pos, &objp->pos);
6064                         dist = vm_vec_mag_quick(&vec_to_objp);
6065                         dot = vm_vec_dot(&firing_objp->orient.v.fvec, &vec_to_objp)/dist;
6066                         if (might_collide_with_ship(firing_objp, objp, dot, dist, 2.0f))
6067                                 return 1;
6068                 }
6069         }
6070
6071         return 0;
6072
6073 }
6074
6075 //int   Team_not_fire_count=0, Team_hit_count = 0;
6076
6077 void render_all_ship_bay_paths(object *objp)
6078 {
6079         int             i,j,color;
6080         ship            *sp = &Ships[objp->instance];
6081         polymodel       *pm;
6082         model_path      *mp;
6083
6084         pm = model_get(sp->modelnum);
6085         vector  global_path_point;
6086         vertex  v, prev_vertex;
6087
6088         if ( pm->ship_bay == NULL )
6089                 return;
6090
6091         for ( i = 0; i < pm->ship_bay->num_paths; i++ ) {
6092                 mp = &pm->paths[pm->ship_bay->paths[i]];
6093
6094                 for ( j = 0; j < mp->nverts; j++ ) {
6095                         vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6096                         vm_vec_add2(&global_path_point, &objp->pos);
6097                         g3_rotate_vertex(&v, &global_path_point);
6098                         color = 255 - j*50;
6099                         if ( color < 50 ) 
6100                                 color = 100;
6101                         gr_set_color(0, color, 0);
6102
6103                         if ( j == mp->nverts-1 ) {
6104                                 gr_set_color(255, 0, 0);
6105                         }
6106
6107                         g3_draw_sphere( &v, 1.5f);
6108
6109                         if ( j > 0 )
6110                                 g3_draw_line(&v, &prev_vertex);
6111
6112                         prev_vertex = v;
6113         
6114                 }
6115         }
6116 }
6117
6118 // debug function to show all path points associated with an object
6119 void render_all_subsys_paths(object *objp)
6120 {
6121         int             i,j,color;
6122         ship            *sp = &Ships[objp->instance];
6123         polymodel       *pm;
6124         model_path      *mp;
6125
6126         pm = model_get(sp->modelnum);
6127         vector  global_path_point;
6128         vertex  v, prev_vertex;
6129
6130         if ( pm->ship_bay == NULL )
6131                 return;
6132
6133         for ( i = 0; i < pm->n_paths; i++ ) {
6134                 mp = &pm->paths[i];
6135                 for ( j = 0; j < mp->nverts; j++ ) {
6136                         vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6137                         vm_vec_add2(&global_path_point, &objp->pos);
6138                         g3_rotate_vertex(&v, &global_path_point);
6139                         color = 255 - j*50;
6140                         if ( color < 50 ) 
6141                                 color = 100;
6142                         gr_set_color(0, color, 0);
6143
6144                         if ( j == mp->nverts-1 ) {
6145                                 gr_set_color(255, 0, 0);
6146                         }
6147
6148                         g3_draw_sphere( &v, 1.5f);
6149
6150                         if ( j > 0 )
6151                                 g3_draw_line(&v, &prev_vertex);
6152
6153                         prev_vertex = v;
6154                 }
6155         }
6156 }
6157
6158 void render_path_points(object *objp)
6159 {
6160         ship            *shipp = &Ships[objp->instance];
6161         ai_info *aip = &Ai_info[shipp->ai_index];
6162         object  *dobjp;
6163         polymodel       *pm;
6164
6165         render_all_subsys_paths(objp);
6166         render_all_ship_bay_paths(objp);
6167
6168         if (aip->goal_objnum < 0)
6169                 return;
6170
6171         dobjp = &Objects[aip->goal_objnum];
6172         pm = model_get(Ships[dobjp->instance].modelnum);
6173         vector  dock_point, global_dock_point;
6174         vertex  v;
6175
6176         ship_model_start(&Objects[aip->goal_objnum]);
6177         if (pm->n_docks) {
6178                 dock_point = pm->docking_bays[0].pnt[0];
6179                 model_find_world_point(&global_dock_point, &dock_point, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6180                 g3_rotate_vertex(&v, &global_dock_point);
6181                 gr_set_color(255, 255, 255);
6182                 g3_draw_sphere( &v, 1.5f);
6183         }
6184
6185         if (aip->path_start != -1) {
6186                 vertex          prev_vertex;
6187                 pnode                   *pp = &Path_points[aip->path_start];
6188                 int                     num_points = aip->path_length;
6189                 int                     i;
6190
6191                 for (i=0; i<num_points; i++) {
6192                         vertex  v0;
6193
6194                         g3_rotate_vertex( &v0, &pp->pos );
6195
6196                         gr_set_color(0, 128, 96);
6197                         if (i != 0)
6198                                 g3_draw_line(&v0, &prev_vertex);
6199
6200                         if (pp-Path_points == aip->path_cur)
6201                                 gr_set_color(255,255,0);
6202                         
6203                         g3_draw_sphere( &v0, 4.5f);
6204
6205                         //      Connect all the turrets that can fire upon this point to this point.
6206 /*                      if (0) { //pp->path_index != -1) {
6207                                 model_path      *pmp;
6208                                 mp_vert         *pmpv;
6209
6210                                 get_base_path_info(pp->path_index, aip->goal_objnum, &pmp, &pmpv);
6211
6212                                 if (pmpv->nturrets) {
6213                                         for (int j = 0; j<pmpv->nturrets; j++) {
6214                                                 vertex  v1;
6215                                                 vector  turret_pos;
6216                                                 ship_subsys     *ssp;
6217
6218                                                 ssp = ship_get_indexed_subsys(&Ships[Objects[aip->goal_objnum].instance], pmpv->turret_ids[j]);
6219
6220 model_find_world_point(&turret_pos, &ssp->system_info->pnt, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6221         
6222                                                 g3_rotate_vertex(&v1, &turret_pos);
6223                                                 gr_set_color(255, 255, 0);
6224                                                 g3_draw_line(&v0, &v1);
6225                                                 g3_draw_sphere( &v1, 1.5f);
6226                                         }
6227                                 }
6228                         } */
6229
6230                         prev_vertex = v0;
6231
6232                         pp++;
6233                 }
6234         }
6235
6236         ship_model_stop(&Objects[aip->goal_objnum]);
6237 }
6238
6239 // Return the distance that the current AI weapon will travel
6240 float ai_get_weapon_dist(ship_weapon *swp)
6241 {
6242         int     bank_num, weapon_num;
6243
6244         bank_num = swp->current_primary_bank;
6245         weapon_num = swp->primary_bank_weapons[bank_num];
6246
6247         //      If weapon_num is illegal, return a reasonable value.  A valid weapon
6248         //      will get selected when this ship tries to fire.
6249         if (weapon_num == -1) {
6250                 // Int3();
6251                 return 1000.0f;
6252         }
6253
6254         return Weapon_info[weapon_num].max_speed * Weapon_info[weapon_num].lifetime;
6255 }
6256
6257 float ai_get_weapon_speed(ship_weapon *swp)
6258 {
6259         int     bank_num, weapon_num;
6260
6261         bank_num = swp->current_primary_bank;
6262         if (bank_num < 0)
6263                 return 100.0f;
6264
6265         weapon_num = swp->primary_bank_weapons[bank_num];
6266
6267         if (weapon_num == -1) {
6268                 //Int3();
6269                 return 100.0f;
6270         }
6271
6272         return Weapon_info[weapon_num].max_speed;
6273 }
6274
6275 //      Compute the predicted position of a ship to be fired upon from a turret.
6276 //      This is based on position of firing gun, enemy object, weapon speed and skill level constraints.
6277 //      Return value in *predicted_enemy_pos.
6278 //      Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6279 //      *pobjp          object firing the weapon
6280 //      *eobjp          object being fired upon
6281 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)
6282 {
6283         ship    *shipp = &Ships[pobjp->instance];
6284         float   range_time;
6285
6286         //weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6287
6288         if (weapon_speed < 1.0f)
6289                 weapon_speed = 1.0f;
6290
6291         range_time = 2.0f;
6292
6293         //      Make it take longer for enemies to get player's allies in range based on skill level.
6294         if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team)
6295                 range_time += In_range_time[Game_skill_level];
6296
6297         //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6298
6299         if (time_enemy_in_range < range_time) {
6300                 float   dist;
6301
6302                 dist = vm_vec_dist_quick(&pobjp->pos, enemy_pos);
6303                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, time_enemy_in_range * dist/weapon_speed);
6304         } else {
6305                 float   collision_time, scale;
6306                 vector  rand_vec;
6307                 ai_info *aip = &Ai_info[shipp->ai_index];
6308
6309                 collision_time = compute_collision_time(enemy_pos, enemy_vel, gun_pos, weapon_speed);
6310
6311                 if (collision_time == 0.0f){
6312                         collision_time = 100.0f;
6313                 }
6314
6315                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, collision_time);
6316                 if (time_enemy_in_range > 2*range_time){
6317                         scale = (1.0f - aip->ai_accuracy) * 4.0f;
6318                 } else {
6319                         scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - time_enemy_in_range/(2*range_time)));
6320                 }               
6321
6322                 static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6323
6324                 vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6325                 G_collision_time = collision_time;
6326                 G_fire_pos = *gun_pos;
6327         }
6328
6329         G_predicted_pos = *predicted_enemy_pos;
6330 }
6331
6332 //      Compute the predicted position of a ship to be fired upon.
6333 //      This is based on current position of firing object, enemy object, relative position of gun on firing object,
6334 //      weapon speed and skill level constraints.
6335 //      Return value in *predicted_enemy_pos.
6336 //      Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6337 void set_predicted_enemy_pos(vector *predicted_enemy_pos, object *pobjp, object *eobjp, ai_info *aip)
6338 {
6339         float   weapon_speed, range_time;
6340         ship    *shipp = &Ships[pobjp->instance];
6341
6342         weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6343         weapon_speed = max(weapon_speed, 1.0f);         // set not less than 1
6344
6345         range_time = 2.0f;
6346
6347         //      Make it take longer for enemies to get player's allies in range based on skill level.
6348         // but don't bias team v. team missions
6349         if ( !((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) ) {
6350                 if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team) {
6351                         range_time += In_range_time[Game_skill_level];
6352                 }
6353         }
6354         //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6355
6356         if (aip->time_enemy_in_range < range_time) {
6357                 float   dist;
6358
6359                 dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6360                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, aip->time_enemy_in_range * dist/weapon_speed);
6361         } else {
6362                 float   collision_time;
6363                 vector  gun_pos, pnt;
6364                 polymodel *po = model_get( Ship_info[shipp->ship_info_index].modelnum );
6365
6366                 //      Compute position of gun in absolute space and use that as fire position.
6367                 if(po->gun_banks != NULL){
6368                         pnt = po->gun_banks[0].pnt[0];
6369                 } else {
6370                         pnt = Objects[shipp->objnum].pos;
6371                 }
6372                 vm_vec_unrotate(&gun_pos, &pnt, &pobjp->orient);
6373                 vm_vec_add2(&gun_pos, &pobjp->pos);
6374
6375                 collision_time = compute_collision_time(&eobjp->pos, &eobjp->phys_info.vel, &gun_pos, weapon_speed);
6376
6377                 if (collision_time == 0.0f) {
6378                         collision_time = 100.0f;
6379                 }
6380
6381                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, collision_time);
6382
6383                 // set globals
6384                 G_collision_time = collision_time;
6385                 G_fire_pos = gun_pos;
6386         }
6387
6388         // Now add error terms (1) regular aim (2) EMP (3) stealth
6389         float scale = 0.0f;
6390         vector rand_vec;
6391
6392         // regular skill level error in aim
6393         if (aip->time_enemy_in_range > 2*range_time) {
6394                 scale = (1.0f - aip->ai_accuracy) * 4.0f;
6395         } else {
6396                 scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - aip->time_enemy_in_range/(2*range_time)));
6397         }
6398
6399         // if this ship is under the effect of an EMP blast, throw his aim off a bit
6400         if (shipp->emp_intensity > 0.0f) {
6401                 // never go lower than 1/2 of the EMP effect max, otherwise things aren't noticeable
6402                 scale += (MAX_EMP_INACCURACY * (shipp->emp_intensity < 0.5f ? 0.5f : shipp->emp_intensity));
6403                 mprintf(("AI miss scale factor (EMP) %f\n",scale));
6404         }
6405
6406         // if stealthy ship, throw his aim off, more when farther away and when dot is small
6407         if ( aip->ai_flags & AIF_STEALTH_PURSIUT ) {
6408                 float dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6409                 vector temp;
6410                 vm_vec_sub(&temp, &eobjp->pos, &pobjp->pos);
6411                 vm_vec_normalize_quick(&temp);
6412                 float dot = vm_vec_dotprod(&temp, &pobjp->orient.v.fvec);
6413                 float st_err = 3.0f * (1.4f - dot) * (1.0f + dist / (get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST)) * (1 - aip->ai_accuracy);
6414                 scale += st_err;
6415                 // mprintf(("error term: %.1f, total %.1f, dot %.3f\n", st_err, scale, dot));
6416         }
6417
6418         // get a random vector that changes slowly over time (1x / sec)
6419         static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6420
6421         vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6422
6423         // set global
6424         G_predicted_pos = *predicted_enemy_pos;
6425 }
6426
6427 //      Handler of submode for Chase.  Go into a continuous turn for awhile.
6428 void ai_chase_ct()
6429 {
6430         vector          tvec;
6431         ship_info       *sip;
6432         ai_info         *aip;
6433
6434         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6435         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6436         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6437         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6438
6439         //      Make a continuous turn towards any combination of possibly negated
6440         // up and right vectors.
6441         tvec = Pl_objp->pos;
6442
6443         if (aip->submode_parm0 & 0x01)
6444                 vm_vec_add2(&tvec, &Pl_objp->orient.v.rvec);
6445         if (aip->submode_parm0 & 0x02)
6446                 vm_vec_sub2(&tvec, &Pl_objp->orient.v.rvec);
6447         if (aip->submode_parm0 & 0x04)
6448                 vm_vec_add2(&tvec, &Pl_objp->orient.v.uvec);
6449         if (aip->submode_parm0 & 0x08)
6450                 vm_vec_sub2(&tvec, &Pl_objp->orient.v.uvec);
6451
6452         //      Detect degenerate cases that cause tvec to be same as player pos.
6453         if (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
6454                 aip->submode_parm0 &= 0x05;
6455                 if (aip->submode_parm0 == 0)
6456                         aip->submode_parm0 = 1;
6457                 vm_vec_add2(&tvec, &Pl_objp->orient.v.rvec);
6458         }
6459
6460         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6461         accelerate_ship(aip, 1.0f);
6462 }
6463
6464 //      ATTACK submode handler for chase mode.
6465 void ai_chase_eb(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
6466 {
6467         vector  _pep;
6468         float           dot_to_enemy, dot_from_enemy;
6469
6470         compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
6471
6472         //      If we're trying to slow down to get behind, then point to turn towards is different.
6473         _pep = *predicted_enemy_pos;
6474         if ((dot_to_enemy > dot_from_enemy + 0.1f) || (dot_to_enemy > 0.9f))
6475                 vm_vec_scale_add(&_pep, &Pl_objp->pos, &En_objp->orient.v.fvec, 100.0f);
6476
6477         ai_turn_towards_vector(&_pep, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6478
6479         accelerate_ship(aip, 0.0f);
6480 }
6481
6482 //      Return time until weapon_objp might hit ship_objp.
6483 //      Assumes ship_objp is not moving.
6484 //      Returns negative time if not going to hit.
6485 //      This is a very approximate function, but is pretty fast.
6486 float ai_endangered_time(object *ship_objp, object *weapon_objp)
6487 {
6488         float           to_dot, from_dot, dist;
6489
6490         dist = compute_dots(ship_objp, weapon_objp, &to_dot, &from_dot);
6491
6492         //      Note, this is bogus.  It assumes only the weapon is moving.
6493         //      Only proceed if weapon sort of pointing at object and object pointing towards or away from weapon
6494         //      (Ie, if object moving at right angle to weapon, just continue for now...)
6495         if (weapon_objp->phys_info.speed < 1.0f)
6496                 return dist + 1.0f;
6497         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))
6498                 return dist / weapon_objp->phys_info.speed;
6499         else
6500                 return -1.0f;
6501 }
6502
6503 //      Return time until danger weapon could hit this ai object.
6504 //      Return negative time if not endangered.
6505 float ai_endangered_by_weapon(ai_info *aip)
6506 {
6507         object  *weapon_objp;
6508
6509         if (aip->danger_weapon_objnum == -1) {
6510                 return -1.0f;
6511         }
6512
6513         weapon_objp = &Objects[aip->danger_weapon_objnum];
6514
6515         if (weapon_objp->signature != aip->danger_weapon_signature) {
6516                 aip->danger_weapon_objnum = -1;
6517                 return -1.0f;
6518         }
6519
6520         return ai_endangered_time(&Objects[Ships[aip->shipnum].objnum], weapon_objp);
6521 }
6522
6523 //      Return true if this ship is near full strength.
6524 int ai_near_full_strength(object *objp, ship_info *sip)
6525 {
6526         return (objp->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp)/sip->shields > 0.8f);
6527 }
6528                                 
6529 //      Set acceleration while in attack mode.
6530 void attack_set_accel(ai_info *aip, float dist_to_enemy, float dot_to_enemy, float dot_from_enemy)
6531 {
6532         float   speed_ratio;
6533
6534         if (En_objp->phys_info.speed > 1.0f)
6535                 speed_ratio = Pl_objp->phys_info.speed/En_objp->phys_info.speed;
6536         else
6537                 speed_ratio = 5.0f;
6538
6539         //      Sometimes, told to attack slowly.  Allows to get in more hits.
6540         if (aip->ai_flags & AIF_ATTACK_SLOWLY) {
6541                 if ((dist_to_enemy > 200.0f) && (dist_to_enemy < 800.0f)) {
6542                         if ((dot_from_enemy < 0.9f) || ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
6543                                 //nprintf(("AI", " slowly "));
6544                                 accelerate_ship(aip, max(1.0f - (dist_to_enemy-200.0f)/600.0f, 0.1f));
6545                                 return;
6546                         }
6547                 } else
6548                         aip->ai_flags &= ~AIF_ATTACK_SLOWLY;
6549         }
6550
6551         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) {
6552                 //nprintf(("AI", "1"));
6553                 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6554                         if (dist_to_enemy > 800.0f) {
6555                                 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6556                                         float percent_left;
6557                                         ship    *shipp;
6558                                         ship_info *sip;
6559
6560                                         shipp = &Ships[Pl_objp->instance];
6561                                         sip = &Ship_info[shipp->ship_info_index];
6562
6563                                         if (sip->afterburner_fuel_capacity > 0.0f) {
6564                                                 percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
6565                                                 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
6566                                                         afterburners_start(Pl_objp);
6567                                                         aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
6568                                                 }
6569                                         }
6570                                 }
6571                         }
6572                 }
6573
6574                 accelerate_ship(aip, 1.0f);
6575         } else if ((Missiontime - aip->last_hit_time > F1_0*7)
6576                 && (En_objp->phys_info.speed < 10.0f) 
6577                 && (dist_to_enemy > 25.0f) 
6578                 && (dot_to_enemy > 0.8f)
6579                 && (dot_from_enemy < 0.8f)) {
6580                 accelerate_ship(aip, 0.0f);             //      No one attacking us, so don't need to move.
6581         } else if ((dot_from_enemy < 0.25f) && (dot_to_enemy > 0.5f)) {
6582                 set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed);
6583         } else if (Pl_objp->phys_info.speed < 15.0f) {
6584                 accelerate_ship(aip, 1.0f);
6585         } else if (Pl_objp->phys_info.speed > En_objp->phys_info.speed - 1.0f) {
6586                 if (dot_from_enemy > 0.75f)
6587                         accelerate_ship(aip, 1.0f);
6588                 else
6589                         set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed*0.75f + 3.0f);
6590         } else {
6591                 change_acceleration(aip, 0.5f);
6592         }
6593 }
6594
6595 //      Pl_objp (aip) tries to get behind En_objp.
6596 //      New on 2/21/98: If this ship can move backwards and slide, maybe do that to get behind.
6597 void get_behind_ship(ai_info *aip, ship_info *sip, float dist_to_enemy)
6598 {
6599         vector  new_pos;
6600         float           dot;
6601         vector  vec_from_enemy;
6602         float           dist;
6603
6604         dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
6605
6606         vm_vec_scale_add(&new_pos, &En_objp->pos, &En_objp->orient.v.fvec, -100.0f);            //      Pick point 100 units behind.
6607         ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6608
6609         dot = vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.fvec);
6610
6611         if (dot > 0.25f) {
6612                 accelerate_ship(aip, 1.0f);
6613         } else {
6614                 accelerate_ship(aip, (dot + 1.0f)/2.0f);
6615         }
6616 }
6617
6618 int avoid_player(object *objp, vector *goal_pos)
6619 {
6620         maybe_avoid_player(Pl_objp, goal_pos);
6621         ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
6622
6623         if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6624                 ship_info *sip = &Ship_info[Ships[objp->instance].ship_info_index];
6625
6626                 if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6627                         ai_turn_towards_vector(&aip->avoid_goal_point, objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6628                         accelerate_ship(aip, 0.5f);
6629                         return 1;
6630                 }
6631         }
6632
6633         return 0;
6634 }
6635
6636 //      Determine if a cylinder of width radius from p0 to p1 will collide with big_objp.
6637 //      If so, stuff *collision_point.
6638 int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6639 {
6640         mc_info mc;
6641
6642         mc.model_num = Ships[big_objp->instance].modelnum;              // Fill in the model to check
6643         mc.orient = &big_objp->orient;                  // The object's orient
6644         mc.pos = &big_objp->pos;                                        // The object's position
6645         mc.p0 = p0;                                                                             // Point 1 of ray to check
6646         mc.p1 = p1;
6647         mc.flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE | MC_SUBMODEL;                                  // flags
6648
6649         mc.radius = radius;
6650
6651         // Only check the 2nd lowest hull object
6652         polymodel *pm = model_get(Ships[big_objp->instance].modelnum);
6653         mc.submodel_num = pm->detail[0]; //pm->submodel->num_details-2];
6654         model_collide(&mc);
6655
6656         if (mc.num_hits)
6657                 *collision_point = mc.hit_point_world;
6658
6659         return mc.num_hits;
6660 }
6661
6662 //      Return true/false if *objp will collide with *big_objp
6663 //      Stuff distance in *distance to collision point if *objp will collide with *big_objp within delta_time seconds.
6664 //      Global collision point stuffed in *collision_point
6665 int will_collide_with_big_ship(object *objp, vector *goal_point, object *big_objp, vector *collision_point, float delta_time)
6666 {
6667         float           radius;
6668         vector  end_pos;
6669
6670         radius = big_objp->radius + delta_time * objp->phys_info.speed;
6671
6672         if (vm_vec_dist_quick(&big_objp->pos, &objp->pos) > radius) {
6673                 return 0;
6674         }
6675
6676         if (goal_point == NULL) {
6677                 vm_vec_scale_add(&end_pos, &objp->pos, &objp->phys_info.vel, delta_time);                                       // Point 2 of ray to check
6678         } else {
6679                 end_pos = *goal_point;
6680         }
6681
6682         return will_collide_pp(&objp->pos, &end_pos, objp->radius, big_objp, collision_point);
6683 }
6684
6685 //      Return true if *objp is expected to collide with a large ship.
6686 //      Stuff global collision point in *collision_point.
6687 //      If *goal_point is not NULL, use that as the point towards which *objp will be flying.  Don't use *objp velocity
6688 //      *ignore_objp will typically be the target this ship is pursuing, either to attack or guard.  We don't want to avoid it.
6689 int will_collide_with_big_ship_all(object *objp, object *ignore_objp, vector *goal_point, vector *collision_point, float *distance, float delta_time)
6690 {
6691         ship_obj        *so;
6692         object  *big_objp;
6693         int             collision_obj_index = -1;
6694         float           min_dist = 999999.9f;
6695         float           collision_time = -1.0f;
6696
6697         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6698                 float   time = 0.0f;
6699                 big_objp = &Objects[so->objnum];
6700
6701                 if (big_objp == ignore_objp)
6702                         continue;
6703
6704                 if (Ship_info[Ships[big_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
6705                         vector  cur_collision_point;
6706                         float           cur_dist;
6707
6708                         if (will_collide_with_big_ship(objp, goal_point, big_objp, &cur_collision_point, delta_time)) {
6709
6710                                 cur_dist = vm_vec_dist(&cur_collision_point, &objp->pos);
6711
6712                                 if (cur_dist < min_dist) {
6713                                         min_dist = cur_dist;
6714                                         *collision_point = cur_collision_point;
6715                                         collision_time = time;
6716                                         collision_obj_index = OBJ_INDEX(big_objp);
6717                                 }
6718                         }
6719                 }
6720         }
6721
6722         *distance = min_dist;
6723         return collision_obj_index;
6724
6725 }
6726
6727 typedef struct {
6728         float           dist;
6729         int             collide;
6730         vector  pos;
6731 } sgoal;
6732
6733 //int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6734 //      Pick a point for *objp to fly towards to avoid a collision with *big_objp at *collision_point
6735 //      Return result in *avoid_pos
6736 void mabs_pick_goal_point(object *objp, object *big_objp, vector *collision_point, vector *avoid_pos)
6737 {
6738         matrix  mat1;
6739         sgoal           goals[4];
6740         vector  v2b;
6741
6742         vm_vec_normalized_dir(&v2b, collision_point, &objp->pos);
6743         vm_vector_2_matrix(&mat1, &v2b, NULL, NULL);
6744
6745         int     found = 0;
6746
6747         //      Try various scales, in 0.5f, 0.75f, 1.0f, 1.25f.
6748         //      First try 0.5f to see if we can find a point that near the center of the target ship, which presumably
6749         //      means less of a turn.
6750         //      Try going as far as 1.25f * radius.
6751         float   s;
6752         for (s=0.5f; s<1.3f; s += 0.25f) {
6753                 int     i;
6754                 for (i=0; i<4; i++) {
6755                         vector p = big_objp->pos;
6756                         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
6757                         float kr = big_objp->radius*s + objp->radius * ((OBJ_INDEX(objp) % 4) ^ 2)/4;
6758                         if (i&1)
6759                                 ku = -ku;
6760                         if (i&2)
6761                                 kr = -kr;
6762                         vm_vec_scale_add2(&p, &mat1.v.uvec, ku);
6763                         vm_vec_scale_add2(&p, &mat1.v.rvec, kr);
6764                         goals[i].pos = p;
6765                         goals[i].dist = vm_vec_dist_quick(&objp->pos, &p);
6766                         goals[i].collide = will_collide_pp(&objp->pos, &p, objp->radius, big_objp, collision_point);
6767                         if (!goals[i].collide)
6768                                 found = 1;
6769                 }
6770
6771                 //      If we found a point that doesn't collide, find the nearest one and make that the *avoid_pos.
6772                 if (found) {
6773                         float   min_dist = 9999999.9f;
6774                         int     min_index = -1;
6775
6776                         for (i=0; i<4; i++) {
6777                                 if (!goals[i].collide && (goals[i].dist < min_dist)) {
6778                                         min_dist = goals[i].dist;
6779                                         min_index = i;
6780                                 }
6781                         }
6782
6783                         Assert(i != -1);
6784                         if (i != -1) {
6785                                 *avoid_pos = goals[min_index].pos;
6786                                 return;
6787                         }
6788                 }
6789         }
6790
6791         //      Drat.  We tried and tried and could not find a point that did not cause a collision.
6792         //      Get this dump pilot far away from the problem ship.
6793         vector  away_vec;
6794         vm_vec_normalized_dir(&away_vec, &objp->pos, collision_point);
6795         vm_vec_scale_add(avoid_pos, &objp->pos, &away_vec, big_objp->radius*1.5f);
6796
6797 }
6798
6799 //      Return true if a large ship is being ignored.
6800 int maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vector *goal_point, float delta_time)
6801 {
6802         if (timestamp_elapsed(aip->avoid_check_timestamp)) {
6803                 float           distance;
6804                 vector  collision_point;
6805                 int             ship_num;
6806                 if ((ship_num = will_collide_with_big_ship_all(Pl_objp, ignore_objp, goal_point, &collision_point, &distance, delta_time)) != -1) {
6807                         aip->ai_flags |= AIF_AVOIDING_BIG_SHIP;
6808                         mabs_pick_goal_point(objp, &Objects[ship_num], &collision_point, &aip->avoid_goal_point);
6809                         float dist = vm_vec_dist_quick(&aip->avoid_goal_point, &objp->pos);
6810                         aip->avoid_check_timestamp = timestamp(2000 + min(1000, (int) (dist * 2.0f)));  //      Delay until check again is based on distance to avoid point.
6811                         aip->avoid_ship_num = ship_num;
6812                 } else {
6813                         aip->ai_flags &= ~AIF_AVOIDING_BIG_SHIP;
6814                         aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
6815                         aip->avoid_ship_num = -1;
6816                         aip->avoid_check_timestamp = timestamp(1500);
6817                 }
6818         }
6819         
6820         if (aip->ai_flags & AIF_AVOIDING_BIG_SHIP) {
6821                 ship_info *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6822
6823                 vector  v2g;
6824
6825                 ai_turn_towards_vector(&aip->avoid_goal_point, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6826                 vm_vec_normalized_dir(&v2g, &aip->avoid_goal_point, &Pl_objp->pos);
6827                 float dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
6828                 float d2 = (1.0f + dot) * (1.0f + dot);
6829                 accelerate_ship(aip, d2/4.0f);
6830                 return 1;
6831         }
6832
6833         return 0;
6834 }
6835
6836 //      Set desired right vector for ships flying towards another ship.
6837 //      Since this is governed only by vector to target, it causes ships to align bank and look less chaotic.
6838 void compute_desired_rvec(vector *rvec, vector *goal_pos, vector *cur_pos)
6839 {
6840         vector  v2e;
6841
6842         vm_vec_normalized_dir(&v2e, goal_pos, cur_pos);
6843         rvec->xyz.x = v2e.xyz.z;
6844         rvec->xyz.y = 0.0f;
6845         rvec->xyz.z = -v2e.xyz.x;
6846         if (vm_vec_mag_squared(rvec) < 0.001f)
6847                 rvec->xyz.y = 1.0f;
6848 }
6849
6850 // Handler for stealth find submode of Chase.
6851 void ai_stealth_find()
6852 {
6853         ai_info         *aip;
6854         ship_info       *sip;
6855
6856         vector new_pos, vec_to_enemy;
6857         float dist_to_enemy, dot_to_enemy, dot_from_enemy;
6858
6859         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6860         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6861         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6862         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6863
6864         // get time since last seen
6865         int delta_time = (timestamp() - aip->stealth_last_visible_stamp);
6866
6867         // if delta_time is really big, i'm real confused, start sweep
6868         if (delta_time > 10000) {
6869                 aip->submode_parm0 = SM_SF_BAIL;
6870         }
6871
6872         // guestimate new position
6873         vm_vec_scale_add(&new_pos, &aip->stealth_last_pos, &aip->stealth_velocity, (delta_time * 0.001f));
6874
6875         // if I think he's behind me, go to the goal point
6876         if ( aip->submode_parm0 == SM_SF_BEHIND ) {
6877                 new_pos = aip->goal_point;
6878         }
6879
6880         // check for collision with big ships
6881         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &new_pos, 10.0f)) {
6882                 // reset ai submode to chase
6883                 return;
6884         }
6885
6886         // if dist is near max and dot is close to 1, accel, afterburn
6887         vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6888         dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6889         dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.v.fvec);
6890
6891         // 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
6892         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) ) {
6893                 // do turn around)
6894                 vm_vec_scale_add(&aip->goal_point, &Pl_objp->pos, &Pl_objp->orient.v.fvec, -300.0f);
6895                 aip->submode_parm0 = SM_SF_BEHIND;
6896                 vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6897                 dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6898                 dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.v.fvec);
6899         }
6900
6901         if ( (dist_to_enemy > get_skill_stealth_dist_scaler()*STEALTH_MAX_VIEW_DIST) && (dot_to_enemy > 0.94f) ) {              // 20 degree half angle
6902                 // accelerate ship
6903                 accelerate_ship(aip, 1.0f);
6904
6905                 // engage afterburner
6906                 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6907                         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6908                                 afterburners_start(Pl_objp);
6909                                 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
6910                         }
6911                 }
6912
6913                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6914                 return;
6915         }
6916
6917         //      If enemy more than 500 meters away, all ships flying there will tend to match bank.
6918         //      They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
6919         //      to interpolate a matrix rather than just a vector.
6920         if (dist_to_enemy > 500.0f) {
6921                 vector  rvec;
6922                 compute_desired_rvec(&rvec, &new_pos, &Pl_objp->pos);
6923                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0, &rvec);
6924         } else {
6925                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6926         }
6927
6928         dot_from_enemy = -vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec);
6929
6930         attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
6931 }
6932
6933 // -----------------------------------------------------------------------------
6934 // try to find stealth ship by sweeping an area
6935 void ai_stealth_sweep()
6936 {
6937         ai_info         *aip;
6938         ship_info       *sip;
6939
6940         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6941         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6942         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6943         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6944
6945         vector goal_pt;
6946         vector forward, right, up;
6947         int lost_time;
6948
6949         // time since stealth last seen
6950         lost_time = (timestamp() - aip->stealth_last_visible_stamp);
6951
6952         // determine which pt to fly to in sweep by keeping track of parm0
6953         if (aip->submode_parm0 == SM_SS_SET_GOAL) {
6954
6955                 // don't make goal pt more than 2k from current pos
6956                 vm_vec_scale_add(&goal_pt, &aip->stealth_last_pos, &aip->stealth_velocity, (0.001f * lost_time));
6957
6958                 // make box size based on speed of stealth and expected time to intercept (keep box in range 200-500)
6959                 float box_size = vm_vec_mag_quick(&aip->stealth_velocity) * (0.001f * lost_time);
6960                 box_size = min(200.0f, box_size);
6961                 box_size = max(500.0f, box_size);
6962                 aip->stealth_sweep_box_size = box_size;
6963
6964                 aip->goal_point = goal_pt;
6965                 aip->submode_parm0 = SM_SS_BOX0;
6966         }
6967
6968         // GET UP, RIGHT, FORWARD FOR BOX based on stealth ship's velocity
6969         // if velocity changes in stealth mode, then ship is *seen*, and falls out of sweep mode
6970         // if stealth has no velocity make a velocity
6971         if ( vm_vec_mag_quick(&aip->stealth_velocity) < 1 ) {
6972                 vm_vec_rand_vec_quick(&aip->stealth_velocity);
6973         }
6974
6975         // get "right" vector for box
6976         vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_y_vector);
6977
6978         if ( vm_vec_mag_quick(&right) < 0.01 ) {
6979                 vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_z_vector);
6980         }
6981
6982         vm_vec_normalize_quick(&right);
6983
6984         // get forward for box
6985         vm_vec_copy_normalize_quick(&forward, &aip->stealth_velocity);
6986
6987         // get "up" for box
6988         vm_vec_crossprod(&up, &forward, &right);
6989         
6990         // lost far away ahead (do box)
6991         switch(aip->submode_parm0) {
6992         case SM_SS_BOX0:
6993                 goal_pt = aip->goal_point;
6994                 break;
6995
6996         // pt1 -U +R
6997         case SM_SS_LR:
6998                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
6999                 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
7000                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
7001                 break;
7002
7003         // pt2 +U -R
7004         case SM_SS_UL:
7005                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
7006                 vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
7007                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
7008                 break;
7009
7010         // pt3 back
7011         case SM_SS_BOX1:
7012                 goal_pt = aip->goal_point;
7013                 break;
7014
7015         // pt4 +U +R
7016         case SM_SS_UR:
7017                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
7018                 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
7019                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
7020                 break;
7021
7022         // pt5 -U -R
7023         case SM_SS_LL:
7024                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
7025                 vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
7026                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
7027                 break;
7028
7029         // pt6 back
7030         case SM_SS_BOX2:
7031                 goal_pt = aip->goal_point;
7032                 break;
7033
7034         default:
7035                 Int3();
7036
7037         }
7038
7039         // when close to goal_pt, update next goal pt
7040         float dist_to_goal = vm_vec_dist(&goal_pt, &Pl_objp->pos);
7041         if (dist_to_goal < 15) {
7042                 aip->submode_parm0++;
7043         }
7044
7045         // check for collision with big ship
7046         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &goal_pt, 10.0f)) {
7047                 // skip to the next pt on box
7048                 aip->submode_parm0++;
7049                 return;
7050         }
7051
7052         ai_turn_towards_vector(&goal_pt, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
7053
7054         float dot = 1.0f;
7055         if (dist_to_goal < 100) {
7056                 vector vec_to_goal;
7057                 vm_vec_normalized_dir(&vec_to_goal, &goal_pt, &Pl_objp->pos);
7058                 dot = vm_vec_dotprod(&vec_to_goal, &Pl_objp->orient.v.fvec);
7059         }
7060
7061         accelerate_ship(aip, 0.8f*dot);
7062 }
7063
7064 //      ATTACK submode handler for chase mode.
7065 void ai_chase_attack(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
7066 {
7067         int             start_bank;
7068         float           dot_to_enemy, dot_from_enemy; //, time_to_hit;
7069         float           bank_override = 0.0f;
7070
7071         if (avoid_player(Pl_objp, predicted_enemy_pos))
7072                 return;
7073
7074         compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
7075
7076         polymodel *po = model_get( sip->modelnum );
7077
7078         vector  *rel_pos;
7079         float           scale;
7080         vector  randvec;
7081         vector  new_pos;
7082
7083         start_bank = Ships[aip->shipnum].weapons.current_primary_bank;
7084         if (po->n_guns && start_bank != -1 ) {
7085                 rel_pos = &po->gun_banks[start_bank].pnt[0];
7086         } else
7087                 rel_pos = NULL;
7088
7089         //      If ship moving slowly relative to its size, then don't attack its center point.
7090         //      How far from center we attack is based on speed, size and distance to enemy
7091         if (En_objp->radius > En_objp->phys_info.speed) {
7092                 static_randvec(Pl_objp-Objects, &randvec);
7093                 scale = dist_to_enemy/(dist_to_enemy + En_objp->radius) * En_objp->radius;
7094                 scale *= 0.5f * En_objp->radius/(En_objp->phys_info.speed + En_objp->radius);   // scale downward by 1/2 to 1/4
7095                 vm_vec_scale_add(&new_pos, predicted_enemy_pos, &randvec, scale);
7096         } else
7097                 new_pos = *predicted_enemy_pos;
7098
7099         if (dist_to_enemy < 250.0f) {
7100                 if (dot_from_enemy > 0.7f) {
7101                         bank_override = Pl_objp->phys_info.speed;
7102                 }
7103         }
7104
7105         //      If enemy more than 500 meters away, all ships flying there will tend to match bank.
7106         //      They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
7107         //      to interpolate a matrix rather than just a vector.
7108         if (dist_to_enemy > 500.0f) {
7109                 vector  rvec;
7110                 compute_desired_rvec(&rvec, predicted_enemy_pos, &Pl_objp->pos);
7111                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0, &rvec);
7112         } else {
7113                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0);
7114         }
7115
7116         attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
7117 }
7118
7119 //      EVADE_SQUIGGLE submode handler for chase mode.
7120 //      Changed by MK on 5/5/97.
7121 //      Used to evade towards a point off the right or up vector.
7122 //      Now, evade straight away to try to get far away.
7123 //      The squiggling should protect against laser fire.
7124 void ai_chase_es(ai_info *aip, ship_info *sip)
7125 {
7126         vector  tvec;
7127         fix             timeslice;
7128         fix             scale;
7129         float           bank_override = 0.0f;
7130
7131         tvec = Pl_objp->pos;
7132
7133         timeslice = (Missiontime >> 16) & 0x0f;
7134         scale = ((Missiontime >> 16) & 0x0f) << 14;
7135
7136         if (timeslice & 0x01)
7137                 vm_vec_scale_add2(&tvec, &Pl_objp->orient.v.rvec, f2fl(scale ^ 0x10000));
7138         if (timeslice & 0x02)
7139                 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.v.rvec, f2fl(scale));
7140         if (timeslice & 0x04)
7141                 vm_vec_scale_add2(&tvec, &Pl_objp->orient.v.uvec, f2fl(scale ^ 0x10000));
7142         if (timeslice & 0x08)
7143                 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.v.uvec, f2fl(scale));
7144
7145         while (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
7146                 tvec.xyz.x += frand();
7147                 tvec.xyz.y += frand();
7148         }
7149
7150         bank_override = Pl_objp->phys_info.speed;
7151
7152         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7153         accelerate_ship(aip, 1.0f);
7154 }
7155
7156 //      Trying to get away from opponent.
7157 void ai_chase_ga(ai_info *aip, ship_info *sip)
7158 {
7159         //      If not near end of this submode, evade squiggly.  If near end, just fly straight for a bit
7160         vector  tvec;
7161         float           bank_override;
7162         vector  vec_from_enemy;
7163
7164         if (En_objp != NULL) {
7165                 vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
7166         } else
7167                 vec_from_enemy = Pl_objp->orient.v.fvec;
7168
7169         static_randvec(Missiontime >> 15, &tvec);
7170         vm_vec_scale(&tvec, 100.0f);
7171         vm_vec_scale_add2(&tvec, &vec_from_enemy, 300.0f);
7172         vm_vec_add2(&tvec, &Pl_objp->pos);
7173
7174         bank_override = Pl_objp->phys_info.speed;
7175
7176         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7177
7178         accelerate_ship(aip, 2.0f);
7179
7180         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
7181                 if (!(Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
7182                         float percent_left = 100.0f * Ships[Pl_objp->instance].afterburner_fuel / sip->afterburner_fuel_capacity;
7183                         if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
7184                                 afterburners_start(Pl_objp);
7185                                 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7186                         }
7187                         afterburners_start(Pl_objp);
7188                         aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7189                 }
7190         }
7191
7192 }
7193
7194 //      Make object *objp attack subsystem with ID = subnum.
7195 //      Return true if found a subsystem to attack, else return false.
7196 //      Note, can fail if subsystem exists, but has no hits.
7197 int ai_set_attack_subsystem(object *objp, int subnum)
7198 {
7199         ship                    *shipp, *attacker_shipp;
7200         ai_info         *aip;
7201         ship_subsys     *ssp;
7202         object          *attacked_objp;
7203
7204         Assert(objp->type == OBJ_SHIP);
7205         Assert(objp->instance >= 0);
7206
7207         attacker_shipp = &Ships[objp->instance];
7208         Assert(attacker_shipp->ai_index >= 0);
7209
7210         aip = &Ai_info[attacker_shipp->ai_index];
7211
7212         // MWA -- 2/27/98.  Due to AL's changes, target_objnum is now not always valid (at least sometimes
7213         // in terms of goals).  So, bail if we don't have a valid target.
7214         if ( aip->target_objnum == -1 )
7215                 return 0;
7216
7217         attacked_objp = &Objects[aip->target_objnum];
7218         shipp = &Ships[attacked_objp->instance];                //  need to get our target's ship pointer!!!
7219
7220         ssp = ship_get_indexed_subsys(shipp, subnum, &objp->pos);
7221         if (ssp == NULL)
7222                 return 0;
7223
7224         set_targeted_subsys(aip, ssp, aip->target_objnum);
7225         
7226         if (aip->ignore_objnum == aip->target_objnum)
7227                 aip->ignore_objnum = UNUSED_OBJNUM;
7228
7229         // -- Done at caller in ai_process_mission_orders -- attacked_objp->flags |= OF_PROTECTED;
7230
7231         ai_set_goal_maybe_abort_dock(objp, aip);
7232         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7233
7234         return 1;
7235 }
7236
7237 void ai_set_guard_vec(object *objp, object *guard_objp)
7238 {
7239         ai_info *aip;
7240         float   radius;
7241
7242         aip = &Ai_info[Ships[objp->instance].ai_index];
7243
7244         //      Handle case of bogus call in which ship is told to guard self.
7245         Assert(objp != guard_objp);
7246         if (objp == guard_objp) {
7247                 vm_vec_rand_vec_quick(&aip->guard_vec);
7248                 vm_vec_scale(&aip->guard_vec, 100.0f);
7249                 return;
7250         }
7251
7252         // check if guard_objp is BIG
7253         radius = 5.0f * (objp->radius + guard_objp->radius) + 50.0f;
7254         if (radius > 300.0f) {
7255                 radius = guard_objp->radius * 1.25f;
7256         }
7257
7258         vm_vec_sub(&aip->guard_vec, &objp->pos, &guard_objp->pos);
7259
7260         if (vm_vec_mag(&aip->guard_vec) > 3.0f*radius) {
7261                 //      Far away, don't just use vector to object, causes clustering of guard ships.
7262                 vector  tvec, rvec;
7263                 float   mag;
7264                 mag = vm_vec_copy_normalize(&tvec, &aip->guard_vec);
7265                 vm_vec_rand_vec_quick(&rvec);                   
7266                 vm_vec_scale_add2(&tvec, &rvec, 0.5f);
7267                 vm_vec_copy_scale(&aip->guard_vec, &tvec, mag);
7268         }
7269
7270         vm_vec_normalize_quick(&aip->guard_vec);
7271         vm_vec_scale(&aip->guard_vec, radius);
7272 }
7273
7274 //      Make object *objp guard object *other_objp.
7275 //      To be called from the goals code.
7276 void ai_set_guard_wing(object *objp, int wingnum)
7277 {
7278         ship            *shipp;
7279         ai_info *aip;
7280         int             leader_objnum, leader_shipnum;
7281
7282         Assert(wingnum >= 0);
7283
7284         Assert(objp->type == OBJ_SHIP);
7285         Assert(objp->instance >= 0);
7286
7287         // shouldn't set the ai mode for the player
7288         if ( objp == Player_obj ) {
7289                 return;
7290         }
7291
7292         shipp = &Ships[objp->instance];
7293
7294         Assert(shipp->ai_index >= 0);
7295
7296         aip = &Ai_info[shipp->ai_index];
7297         force_avoid_player_check(objp, aip);
7298
7299         ai_set_goal_maybe_abort_dock(objp, aip);
7300         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7301
7302         //      This function is called whenever a guarded ship is destroyed, so this code
7303         //      prevents a ship from trying to guard a non-existent wing.
7304         if (Wings[wingnum].current_count < 1) {
7305                 aip->guard_objnum = -1;
7306                 aip->guard_wingnum = -1;
7307                 aip->mode = AIM_NONE;
7308         } else {
7309                 leader_shipnum = Wings[wingnum].ship_index[0];
7310                 leader_objnum = Ships[leader_shipnum].objnum;
7311
7312                 Assert((leader_objnum >= 0) && (leader_objnum < MAX_OBJECTS));
7313                 //Assert(leader_objnum != objp-Objects);        //      Don't allow ships to guard themselves.
7314                 if (leader_objnum == OBJ_INDEX(objp)) {
7315                         //Int3();       //      Seems illegal, but let's clean up.  Get MikeK.
7316                         return;
7317                 }
7318
7319                 aip->guard_wingnum = wingnum;
7320                 aip->guard_objnum = leader_objnum;
7321                 aip->guard_signature = Objects[leader_objnum].signature;
7322                 aip->mode = AIM_GUARD;
7323                 aip->submode = AIS_GUARD_STATIC;
7324
7325                 ai_set_guard_vec(objp, &Objects[leader_objnum]);
7326         }
7327 }
7328
7329 //      Make object *objp guard object *other_objp.
7330 //      To be called from the goals code.
7331 void ai_set_evade_object(object *objp, object *other_objp)
7332 {
7333         ship            *shipp;
7334         ai_info *aip;
7335         int             other_objnum;
7336
7337         Assert(objp->type == OBJ_SHIP);
7338         Assert(objp->instance >= 0);
7339
7340         shipp = &Ships[objp->instance];
7341
7342         Assert(shipp->ai_index >= 0);
7343
7344         aip = &Ai_info[shipp->ai_index];
7345
7346         other_objnum = OBJ_INDEX(other_objp);
7347         Assert(other_objnum >= 0);
7348
7349         Assert(other_objnum != Ships[aip->shipnum].objnum);     //      make sure not targeting self
7350         aip->target_objnum = other_objnum;
7351
7352         aip->mode = AIM_EVADE;
7353 }
7354
7355 //      Make objp guard other_objp
7356 //      If other_objp is a member of a wing, objp will guard that whole wing
7357 //      UNLESS objp is also a member of the wing!
7358 void ai_set_guard_object(object *objp, object *other_objp)
7359 {
7360         ship            *shipp;
7361         ai_info *aip;
7362         int             other_objnum;
7363
7364         Assert(objp->type == OBJ_SHIP);
7365         Assert(objp->instance >= 0);
7366         Assert(objp != other_objp);
7367
7368         shipp = &Ships[objp->instance];
7369
7370         Assert(shipp->ai_index >= 0);
7371
7372         aip = &Ai_info[shipp->ai_index];
7373         aip->avoid_check_timestamp = timestamp(1);
7374
7375         //      If ship to guard is in a wing, guard that whole wing.
7376         ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
7377         if ((other_aip->wing != -1) && (other_aip->wing != aip->wing)) {
7378                 ai_set_guard_wing(objp, Ai_info[Ships[other_objp->instance].ai_index].wing);
7379         } else {
7380
7381                 other_objnum = other_objp-Objects;
7382
7383                 aip->guard_objnum = other_objnum;
7384                 aip->guard_signature = other_objp->signature;
7385                 aip->guard_wingnum = -1;
7386
7387                 aip->mode = AIM_GUARD;
7388                 aip->submode = AIS_GUARD_STATIC;
7389
7390                 Assert(other_objnum >= 0);      //      Hmm, bogus object and we need its position for guard_vec.
7391
7392                 // vm_vec_sub(&aip->guard_vec, &objp->pos, &Objects[other_objnum].pos);
7393                 ai_set_guard_vec(objp, &Objects[other_objnum]);
7394
7395                 ai_set_goal_maybe_abort_dock(objp, aip);
7396                 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7397         }
7398 }
7399
7400 //      Update the aspect_locked_time field based on whether enemy is in view cone.
7401 //      Also set/clear AIF_SEEK_LOCK.
7402 void update_aspect_lock_information(ai_info *aip, vector *vec_to_enemy, float dist_to_enemy, float enemy_radius)
7403 {
7404         float   dot_to_enemy;
7405         int     num_weapon_types;
7406         int     weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
7407         ship    *shipp;
7408         ship_weapon     *swp;
7409         weapon_info     *wip;
7410
7411         shipp = &Ships[aip->shipnum];
7412         swp = &shipp->weapons;
7413
7414         // AL 3-7-98: This probably should never happen, but check to ensure that current_secondary_bank is valid
7415         if ( (swp->current_secondary_bank < 0) || (swp->current_secondary_bank > swp->num_secondary_banks) ) {
7416                 return;
7417         }
7418
7419         num_weapon_types = get_available_secondary_weapons(Pl_objp, weapon_id_list, weapon_bank_list);
7420
7421         wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
7422
7423         if (num_weapon_types && (wip->wi_flags & WIF_HOMING_ASPECT)) {
7424                 if (dist_to_enemy > 300.0f - min(enemy_radius, 100.0f))
7425                         aip->ai_flags |= AIF_SEEK_LOCK;
7426                 else
7427                         aip->ai_flags &= ~AIF_SEEK_LOCK;
7428
7429                 //      Update locking information for aspect seeking missiles.
7430                 aip->current_target_is_locked = 0;
7431                 dot_to_enemy = vm_vec_dot(vec_to_enemy, &Pl_objp->orient.v.fvec);
7432
7433                 float   needed_dot = 0.9f - 0.5f * enemy_radius/(dist_to_enemy + enemy_radius); //      Replaced MIN_TRACKABLE_DOT with 0.9f
7434                 if (dot_to_enemy > needed_dot) {
7435                         aip->aspect_locked_time += flFrametime;
7436                         // nprintf(("AI", "+ Lock time = %7.3f\n", aip->aspect_locked_time));
7437                         if (aip->aspect_locked_time >= wip->min_lock_time) {
7438                                 aip->aspect_locked_time = wip->min_lock_time;
7439                                 aip->current_target_is_locked = 1;
7440                         }
7441                 } else {
7442                         aip->aspect_locked_time -= flFrametime*2;
7443                         // nprintf(("AI", "- Lock time = %7.3f\n", aip->aspect_locked_time));
7444                         if (aip->aspect_locked_time < 0.0f)
7445                                 aip->aspect_locked_time = 0.0f;
7446                 }
7447                 //nprintf(("AI", "dot = %7.3f, time = %7.3f\n", dot_to_enemy, aip->aspect_locked_time));
7448         
7449         } else {
7450                 aip->current_target_is_locked = 0;
7451                 aip->aspect_locked_time = 0.0f; // Used to be this, why?: wip->min_lock_time;
7452                 aip->ai_flags &= ~AIF_SEEK_LOCK;
7453         }
7454
7455 }
7456
7457 //      We're in chase mode and we've recently collided with our target.
7458 //      Fly away from it!
7459 void ai_chase_fly_away(object *objp, ai_info *aip)
7460 {
7461         int     abort_flag = 0;
7462
7463         if (aip->ai_flags & AIF_TARGET_COLLISION) {
7464                 aip->ai_flags &= ~AIF_TARGET_COLLISION; //      Don't process this hit again next frame.
7465                 aip->submode = SM_FLY_AWAY;                                     //      Focus on avoiding target
7466                 aip->submode_start_time = Missiontime;
7467         }
7468
7469         if ((aip->target_objnum == -1) || (Objects[aip->target_objnum].signature != aip->target_signature)) {
7470                 abort_flag = 1;
7471         }
7472
7473         if (abort_flag || (Missiontime > aip->submode_start_time + F1_0)) {
7474                 aip->last_attack_time = Missiontime;
7475                 aip->submode = SM_ATTACK;
7476                 aip->submode_start_time = Missiontime;
7477         } else {
7478                 vector  v2e;
7479                 float           dot;
7480
7481                 vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, &objp->pos);
7482
7483                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2e);
7484                 if (dot < 0.0f)
7485                         accelerate_ship(aip, 1.0f);
7486                 else
7487                         accelerate_ship(aip, 1.0f - dot);
7488                 turn_away_from_point(objp, &Objects[aip->target_objnum].pos, 0.0f);
7489         }
7490 }
7491
7492 //      Return bank index of favored secondary weapon.
7493 //      Return -1 if nothing favored.
7494 //      "favored" means SEXPs have specified the weapon as being good to fire at en_objp.
7495 int has_preferred_secondary(object *objp, object *en_objp, ship_weapon *swp)
7496 {
7497 // int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
7498         int     i;
7499
7500         for (i=0; i<swp->num_secondary_banks; i++) {
7501                 if (swp->secondary_bank_capacity[i] > 0) {
7502                         if (swp->secondary_bank_ammo[i] > 0) {
7503                                 if (is_preferred_weapon(swp->secondary_bank_weapons[i], objp, en_objp) != -1){
7504                                         return i;
7505                                 }
7506                         }
7507                 }
7508         }
7509
7510         return -1;
7511 }
7512
7513 //      Choose which secondary weapon to fire.
7514 //      Note, this is not like ai_select_secondary_weapon().  "choose" means make a choice.
7515 //      "select" means execute an order.  Get it?
7516 //      This function calls ai_select_secondary_weapon() with the characteristics it should search for.
7517 void ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp)
7518 {
7519         float                   subsystem_strength = 0.0f;
7520         int                     is_big_ship, priority1, priority2;
7521         ship_weapon     *swp;
7522         ship_info       *esip;
7523
7524         if ( en_objp->type == OBJ_SHIP ) {
7525                 esip = &Ship_info[Ships[en_objp->instance].ship_info_index];
7526         } else {
7527                 esip = NULL;
7528         }
7529
7530         swp = &Ships[objp->instance].weapons;
7531
7532         // AL 3-5-98: do a quick out if the ship has no secondaries
7533         if ( swp->num_secondary_banks <= 0 ) {
7534                 swp->current_secondary_bank = -1;
7535                 return;
7536         }
7537
7538         int preferred_secondary = has_preferred_secondary(objp, en_objp, swp);
7539
7540         if (preferred_secondary != -1) {
7541                 if (swp->current_secondary_bank != preferred_secondary) {
7542                         aip->current_target_is_locked = 0;
7543                         aip->aspect_locked_time = 0.0f;
7544                         swp->current_secondary_bank = preferred_secondary;
7545                 }
7546                 //nprintf(("AI", "Favored secondary = %s\n", Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7547                 aip->ai_flags |= AIF_UNLOAD_SECONDARIES;
7548         } else {
7549                 aip->ai_flags &= ~AIF_UNLOAD_SECONDARIES;
7550                 if (aip->targeted_subsys) {
7551                         subsystem_strength = aip->targeted_subsys->current_hits;
7552                 }
7553
7554                 if ( esip ) {
7555                         is_big_ship = esip->flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP);
7556                 } else {
7557                         is_big_ship=0;
7558                 }
7559
7560                 if (is_big_ship) {
7561                         priority1 = WIF_HUGE;
7562                         priority2 = WIF_HOMING;
7563                 } else if ( (esip != NULL) && (esip->flags & SIF_BOMBER) ) {
7564                         priority1 = WIF_BOMBER_PLUS;
7565                         priority2 = WIF_HOMING;
7566                 } else if (subsystem_strength > 100.0f) {
7567                         priority1 = WIF_PUNCTURE;
7568                         priority2 = WIF_HOMING;
7569                 } else {
7570                         priority1 = WIF_HOMING;
7571                         priority2 = 0;
7572                 }
7573                 
7574                 ai_select_secondary_weapon(objp, swp, priority1, priority2);
7575         }
7576
7577         // nprintf(("AI", "Frame %i: Chose secondary %s\n", Framecount, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7578 }
7579
7580 //      Return time, in seconds, at which this ship can next fire its current secondary weapon.
7581 float set_secondary_fire_delay(ai_info *aip, ship *shipp, weapon_info *swip)
7582 {
7583         float t = swip->fire_wait;              //      Base delay for this weapon.
7584         if (shipp->team == Player_ship->team) {
7585                 //      On player's team, _lower_ skill level = faster firing
7586                 t = t * (Game_skill_level+2) / (NUM_SKILL_LEVELS);
7587         } else {                //      Not on player's team, higher skill level = faster firing
7588                 t = t * (NUM_SKILL_LEVELS - Game_skill_level+2) / (NUM_SKILL_LEVELS);
7589         }
7590
7591         t += (Num_ai_classes - aip->ai_class + 1) * 0.5f;
7592         t *= frand_range(0.8f, 1.2f);
7593
7594         //      For the missiles that fire fairly quickly, occasionally add an additional substantial delay.
7595         if (t < 5.0f)
7596                 if (frand() < 0.5f)
7597                         t = t * 2.0f + 2.0f;
7598
7599         return t;
7600 }
7601
7602
7603 void ai_chase_big_approach_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7604 {
7605         float dist_to_goal;
7606
7607         // head straight toward him and maybe circle later
7608         vm_vec_avg(goal_pos, &attack_objp->pos, &target_objp->pos);
7609
7610         // get distance to goal
7611         dist_to_goal = vm_vec_dist(goal_pos, &attack_objp->pos);
7612         
7613         // set accel
7614         if (dist_to_goal > 400.0f) {
7615                 *accel = 1.0f;
7616         } else {
7617                 *accel = dist_to_goal/400.0f;
7618         }
7619 }
7620
7621 void ai_chase_big_circle_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7622 {
7623         get_tangent_point(goal_pos, attack_objp, &target_objp->pos, attack_objp->radius + target_objp->radius + 100.0f);
7624
7625         *accel = 1.0f;
7626 }
7627
7628 // get the current and desired horizontal separations between target
7629 void ai_chase_big_get_separations(object *attack_objp, object *target_objp, vector *horz_vec_to_target, float *desired_separation, float *cur_separation)
7630 {
7631         float temp, r_target, r_attacker, h_attacker, h_target;
7632         float perp_dist;
7633         vector vec_to_target;
7634         polymodel *pm;
7635
7636         // get parameters of ships (as cylinders - radius and height)
7637         // get radius of attacker (for rotations about forward)
7638         pm = model_get(Ships[attack_objp->instance].modelnum);
7639         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7640         r_attacker = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7641         r_attacker = max(temp, r_attacker);
7642         h_attacker = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7643
7644         // get radius of target (for rotations about forward)
7645         pm = model_get(Ships[attack_objp->instance].modelnum);
7646         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7647         r_target = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7648         r_target = max(temp, r_target);
7649         h_target = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7650
7651         // find separation between cylinders [if parallel]
7652         vm_vec_sub(&vec_to_target, &attack_objp->pos, &target_objp->pos);
7653
7654         // find the distance between centers along forward direction of ships
7655         perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.v.fvec);
7656
7657         // subtract off perp component to get "horizontal" separation vector between cylinders [ASSUMING parallel]
7658         vm_vec_scale_add(horz_vec_to_target, &vec_to_target, &target_objp->orient.v.fvec, -perp_dist);
7659         *cur_separation = vm_vec_mag_quick(horz_vec_to_target);
7660
7661         // choose "optimal" separation of 1000 + r_target + r_attacker
7662         *desired_separation = 1000 + r_target + r_attacker;
7663 }
7664
7665 void ai_chase_big_parallel_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7666 {
7667         int opposing;
7668         float temp, r_target, r_attacker, h_attacker, h_target;
7669         float separation, optimal_separation;
7670         vector  horz_vec_to_target;
7671         polymodel *pm;
7672
7673         // get parameters of ships (as cylinders - radius and height)
7674         // get radius of attacker (for rotations about forward)
7675         pm = model_get(Ships[attack_objp->instance].modelnum);
7676         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7677         r_attacker = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7678         r_attacker = max(temp, r_attacker);
7679         h_attacker = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7680
7681         // get radius of target (for rotations about forward)
7682         pm = model_get(Ships[attack_objp->instance].modelnum);
7683         temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7684         r_target = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7685         r_target = max(temp, r_target);
7686         h_target = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7687
7688         // are we opposing (only when other ship is not moving)
7689         opposing = ( vm_vec_dotprod(&attack_objp->orient.v.fvec, &target_objp->orient.v.fvec) < 0 );
7690
7691         ai_chase_big_get_separations(attack_objp, target_objp, &horz_vec_to_target, &optimal_separation, &separation);
7692
7693         // choose dist (2000) so that we don't bash
7694         float dist = 2000;
7695         if (opposing) {
7696                 dist = - dist;
7697         }
7698
7699         // set the goal pos as dist forward from target along target forward
7700         vm_vec_scale_add(goal_pos, &target_objp->pos, &target_objp->orient.v.fvec, dist);
7701         // then add horizontal separation
7702         vm_vec_scale_add2(goal_pos, &horz_vec_to_target, optimal_separation/separation);
7703
7704         // find the distance between centers along forward direction of ships
7705         vector vec_to_target;
7706         vm_vec_sub(&vec_to_target, &target_objp->pos, &attack_objp->pos);
7707         float perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.v.fvec);
7708
7709         float match_accel = target_objp->phys_info.vel.xyz.z / Ship_info[Ships[attack_objp->instance].ship_info_index].max_vel.xyz.z;
7710         float length_scale = attack_objp->radius;
7711
7712         // if we're heading toward enemy ship, we want to keep going if we're ahead
7713         if (opposing) {
7714                 perp_dist = -perp_dist;
7715         }
7716
7717         if (perp_dist > 0) {
7718                 // falling behind, so speed up
7719                 *accel = match_accel + (1.0f - match_accel) / length_scale * (perp_dist);
7720         } else {
7721                 // up in front, so slow down
7722                 *accel = match_accel  - match_accel / length_scale * -perp_dist;
7723                 *accel = max(0.0f, *accel);
7724         }
7725
7726 }
7727
7728
7729 //      Return *goal_pos for one cruiser to attack another (big ship).
7730 //      Choose point fairly nearby that is not occupied by another cruiser.
7731 void ai_cruiser_chase_set_goal_pos(vector *goal_pos, object *pl_objp, object *en_objp)
7732 {
7733         ai_info *aip;
7734
7735         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
7736         float accel;
7737
7738         switch (aip->submode) {
7739         case SM_BIG_APPROACH:
7740                 // do approach stuff;
7741                 ai_chase_big_approach_set_goal(goal_pos, pl_objp, en_objp, &accel);
7742                 break;
7743
7744         case SM_BIG_CIRCLE:
7745                 // do circle stuff
7746                 ai_chase_big_circle_set_goal(goal_pos, pl_objp, en_objp, &accel);
7747                 break;
7748
7749         case SM_BIG_PARALLEL:
7750                 // do parallel stuff
7751                 ai_chase_big_parallel_set_goal(goal_pos, pl_objp, en_objp, &accel);
7752                 break;
7753         }
7754 }
7755
7756 int maybe_hack_cruiser_chase_abort()
7757 {
7758         ship                    *shipp = &Ships[Pl_objp->instance];     
7759         ship                    *eshipp = &Ships[En_objp->instance];
7760         ai_info         *aip = &Ai_info[shipp->ai_index];
7761
7762         // mission sm3-08, sathanos chasing collosus
7763         if ( stricmp(Mission_filename, "sm3-08.fs2") == 0 ) {
7764                 if (( stricmp(eshipp->ship_name, "colossus") == 0 ) || ( stricmp(shipp->ship_name, "colossus") == 0 )) {
7765                         // Changed so all big ships attacking the Colossus will not do the chase code.
7766                         // Did this so Beast wouldn't swerve away from Colossus. -- MK, 9/14/99
7767                         //if ( stricmp(shipp->ship_name, "Sathanas") == 0 ) {
7768                                 // do cool hack stuff here
7769                                 ai_clear_ship_goals( aip );
7770                                 aip->mode = AIM_NONE;
7771                                 return 1;
7772                         //}
7773                 }
7774         }
7775
7776         return 0;
7777 }
7778
7779 //      Make a big ship pursue another big ship.
7780 //      (Note, called "ai_cruiser_chase" because we already have ai_chase_big() which means fighter chases big ship.
7781 void ai_cruiser_chase()
7782 {
7783         ship_info       *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7784         ship                    *shipp = &Ships[Pl_objp->instance];     
7785         ai_info         *aip = &Ai_info[shipp->ai_index];
7786
7787         if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7788                 Int3(); //      Hmm, not a very big ship, how did we get in this function?
7789                 aip->mode = AIM_NONE;
7790                 return;
7791         }
7792
7793         if (En_objp->type != OBJ_SHIP) {
7794                 Int3();
7795                 return;
7796         }
7797
7798         if (En_objp->instance < 0) {
7799                 Int3();
7800                 return;
7801         }
7802
7803         ship                    *eshipp;
7804         ship_info       *esip;
7805
7806         eshipp = &Ships[En_objp->instance];
7807         esip = &Ship_info[eshipp->ship_info_index];
7808
7809         if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7810                 // Int3();      //      Hmm, we're big and we're pursuing something other than a big ship?
7811                 aip->mode = AIM_NONE;
7812                 return;
7813         }
7814
7815         vector  goal_pos;
7816         float turn_time = Ship_info[Ships[Pl_objp->instance].ship_info_index].srotation_time;
7817
7818         // kamikaze - ram and explode
7819         if (aip->ai_flags & AIF_KAMIKAZE) {
7820                 ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
7821                 accelerate_ship(aip, 1.0f);
7822         } 
7823         
7824         // really track down and chase
7825         else {
7826                 // check valid submode
7827                 Assert( (aip->submode == SM_ATTACK) || (aip->submode == SM_BIG_APPROACH) || (aip->submode == SM_BIG_CIRCLE) || (aip->submode == SM_BIG_PARALLEL) );
7828
7829                 // just entering, approach enemy ship
7830                 if (aip->submode == SM_ATTACK) {
7831                         aip->submode = SM_BIG_APPROACH;
7832                 }
7833
7834                 // desired accel
7835                 float accel = 0.0f;
7836                 vector *rvecp = NULL;
7837
7838                 switch (aip->submode) {
7839                 case SM_BIG_APPROACH:
7840                         // do approach stuff;
7841                         ai_chase_big_approach_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7842                         // maybe set rvec
7843                         break;
7844
7845                 case SM_BIG_CIRCLE:
7846                         // do circle stuff
7847                         ai_chase_big_circle_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7848                         // maybe set rvec
7849                         break;
7850
7851                 case SM_BIG_PARALLEL:
7852                         // do parallel stuff
7853                         ai_chase_big_parallel_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7854                         //maybe set rvec
7855                         break;
7856                 }
7857
7858
7859                 // now move as desired
7860                 ai_turn_towards_vector(&goal_pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0, rvecp);
7861                 accelerate_ship(aip, accel);
7862
7863
7864                 // maybe switch to new mode
7865                 vector vec_to_enemy;
7866                 float dist_to_enemy;
7867                 int moving = (En_objp->phys_info.vel.xyz.z > 0.5f);
7868                 vm_vec_sub(&vec_to_enemy, &En_objp->pos, &Pl_objp->pos);
7869                 dist_to_enemy = vm_vec_mag_quick(&vec_to_enemy);
7870
7871                 switch (aip->submode) {
7872                 case SM_BIG_APPROACH:
7873                         if ( dist_to_enemy < (Pl_objp->radius + En_objp->radius)*1.25f + 200.0f ) {
7874                                 // moving
7875                                 if (moving) {
7876                                         // if within 90 degrees of en forward, go into parallel, otherwise circle
7877                                         if ( vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0 ) {
7878                                                 aip->submode = SM_BIG_PARALLEL;
7879                                         }
7880                                 }
7881
7882                                 // otherwise cirle
7883                                 if ( !maybe_hack_cruiser_chase_abort() ) {
7884                                         aip->submode = SM_BIG_CIRCLE;
7885                                 }
7886                         }
7887                         break;
7888
7889                 case SM_BIG_CIRCLE:
7890                         // moving
7891                         if (moving) {
7892                                 vector temp;
7893                                 float desired_sep, cur_sep;
7894                                 // we're behind the enemy ship
7895                                 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec) > 0) {
7896                                         // and we're turning toward the enemy
7897                                         if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0) {
7898                                                 // get separation
7899                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7900                                                 // and the separation is > 0.9 desired
7901                                                 if (cur_sep > 0.9 * desired_sep) {
7902                                                         aip->submode = SM_BIG_PARALLEL;
7903                                                 }
7904                                         }
7905                                 }
7906                         } else {
7907                                 // still
7908                                 vector temp;
7909                                 float desired_sep, cur_sep;
7910                                 // we're behind the enemy ship
7911                                 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec) > 0) {
7912                                         // and we're turning toward the enemy
7913                                         if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0) {
7914                                                 // get separation
7915                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7916                                                 //and the separation is [0.9 to 1.1] desired
7917                                                 if ( (cur_sep > 0.9f * desired_sep) ) {
7918                                                         aip->submode = SM_BIG_PARALLEL;
7919                                                 }
7920                                         }
7921                                 }
7922                                 // in front of ship
7923                                 else {
7924                                         // and we're turning toward the enemy
7925                                         if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) < 0) {
7926                                                 // get separation
7927                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7928                                                 //and the separation is [0.9 to 1.1] desired
7929                                                 if ( (cur_sep > 0.9f * desired_sep) ) {
7930                                                         aip->submode = SM_BIG_PARALLEL;
7931                                                 }
7932                                         }
7933                                 }
7934                         }
7935                         break;
7936
7937                 case SM_BIG_PARALLEL:
7938                         // we're opposing
7939                         if ( vm_vec_dotprod(&Pl_objp->orient.v.fvec, &En_objp->orient.v.fvec) < 0 ) {
7940                                 // and the other ship is moving
7941                                 if (moving) {
7942                                         // and we no longer overlap
7943                                         if ( dist_to_enemy > (0.75 * (En_objp->radius + Pl_objp->radius)) ) {
7944                                                 aip->submode = SM_BIG_APPROACH;
7945                                         }
7946                                 }
7947                         }
7948                         break;
7949                 }
7950         }
7951 }
7952
7953 // --------------------------------------------------------------------------
7954 // Make object Pl_objp chase object En_objp
7955 void ai_chase()
7956 {
7957         float                   dist_to_enemy, time_to_enemy;
7958         float                   dot_to_enemy, dot_from_enemy, real_dot_to_enemy;
7959         vector          player_pos, enemy_pos, predicted_enemy_pos, real_vec_to_enemy, predicted_vec_to_enemy;
7960         ship_info       *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7961         ship                    *shipp = &Ships[Pl_objp->instance];
7962         ship_weapon     *swp = &shipp->weapons;
7963         ai_info         *aip = &Ai_info[shipp->ai_index];
7964         int                     enemy_sip_flags;
7965
7966         if (aip->mode != AIM_CHASE) {
7967                 Int3();
7968         }
7969
7970         if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
7971                 ai_cruiser_chase();
7972                 return;
7973         }
7974
7975         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER | SIF_ESCAPEPOD))) {
7976                 Warning(LOCATION, "Ship %s is not 'small', but is in chase mode.\nSwitching to AI=none.\n", shipp->ship_name);
7977                 aip->mode = AIM_NONE;
7978                 return;
7979         }
7980
7981         //nprintf(("AI", "%7s ", Submode_text[aip->submode]));
7982
7983         if ( En_objp->type == OBJ_SHIP ) {
7984                 enemy_sip_flags = Ship_info[Ships[En_objp->instance].ship_info_index].flags;
7985         } else {
7986                 enemy_sip_flags = 0;
7987         }
7988
7989         if ( enemy_sip_flags > 0 ) {
7990                 if (enemy_sip_flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
7991                         ai_big_chase();
7992                         return;
7993                 }
7994         }
7995
7996         //      If collided with target_objnum last frame, avoid that ship.
7997         //      This should prevent the embarrassing behavior of ships getting stuck on each other
7998         //      as if they were magnetically attracted. -- MK, 11/13/97.
7999         if ((aip->ai_flags & AIF_TARGET_COLLISION) || (aip->submode == SM_FLY_AWAY)) {
8000                 ai_chase_fly_away(Pl_objp, aip);
8001                 return;
8002         }
8003
8004         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
8005         dist_to_enemy = vm_vec_dist_quick(&player_pos, &enemy_pos);
8006         time_to_enemy = compute_time_to_enemy(dist_to_enemy, Pl_objp, En_objp);
8007         vm_vec_sub(&real_vec_to_enemy, &enemy_pos, &player_pos);
8008
8009         vm_vec_normalize(&real_vec_to_enemy);
8010
8011         real_dot_to_enemy = vm_vec_dot(&real_vec_to_enemy, &Pl_objp->orient.v.fvec);
8012
8013         int is_stealthy_ship = 0;
8014         if ( (enemy_sip_flags > 0) && (enemy_sip_flags & SIF_STEALTH) ) {
8015                 if ( ai_is_stealth_visible(Pl_objp, En_objp) != STEALTH_FULLY_TARGETABLE ) {
8016                         is_stealthy_ship = 1;
8017                 }
8018         }
8019
8020         // Can only acquire lock on a target that isn't hidden from sensors
8021         if ( !(Ships[En_objp->instance].flags & SF_HIDDEN_FROM_SENSORS) && !is_stealthy_ship ) {
8022                 update_aspect_lock_information(aip, &real_vec_to_enemy, dist_to_enemy, En_objp->radius);
8023         } else {
8024                 aip->current_target_is_locked = 0;
8025                 aip->ai_flags &= ~AIF_SEEK_LOCK;
8026         }
8027
8028         //      If seeking lock, try to point directly at ship, else predict position so lasers can hit it.
8029         //      If just acquired target, or target is not in reasonable cone, don't refine believed enemy position.
8030         if ((real_dot_to_enemy < 0.25f) || (aip->target_time < 1.0f) || (aip->ai_flags & AIF_SEEK_LOCK)) {
8031                 predicted_enemy_pos = enemy_pos;
8032         } else {
8033                 //      Set predicted_enemy_pos.
8034                 //      See if attacking a subsystem.
8035                 if (aip->targeted_subsys != NULL) {
8036                         Assert(En_objp->type == OBJ_SHIP);
8037                         ship_info       *esip = &Ship_info[Ships[En_objp->instance].ship_info_index];
8038                         if (get_shield_strength(En_objp)/esip->shields < HULL_DAMAGE_THRESHOLD_PERCENT) {
8039                                 //int   rval;
8040
8041                                 if (aip->targeted_subsys != NULL) {
8042                                         get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
8043                                         predicted_enemy_pos = enemy_pos;
8044                                         predicted_vec_to_enemy = real_vec_to_enemy;
8045                                 } else {
8046                                         set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8047                                         set_target_objnum(aip, -1);
8048                                 }
8049                                 // 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));
8050
8051                         } else {
8052                                 set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8053                                 // 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));
8054                         }
8055                 } else {
8056                         set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8057                 }
8058         }
8059
8060         vm_vec_sub(&predicted_vec_to_enemy, &predicted_enemy_pos, &player_pos);
8061
8062         vm_vec_normalize(&predicted_vec_to_enemy);
8063
8064         dot_to_enemy = vm_vec_dot(&Pl_objp->orient.v.fvec, &predicted_vec_to_enemy);
8065         dot_from_enemy= - vm_vec_dot(&En_objp->orient.v.fvec, &real_vec_to_enemy);
8066
8067         //
8068         //      Set turn and acceleration based on submode.
8069         //
8070         switch (aip->submode) {
8071         case SM_CONTINUOUS_TURN:
8072                 ai_chase_ct();
8073                 break;
8074
8075         case SM_STEALTH_FIND:
8076                 ai_stealth_find();
8077                 break;
8078
8079         case SM_STEALTH_SWEEP:
8080                 ai_stealth_sweep();
8081                 break;
8082
8083         case SM_ATTACK:
8084         case SM_SUPER_ATTACK:
8085         case SM_ATTACK_FOREVER:
8086                 if (vm_vec_dist_quick(&Pl_objp->pos, &predicted_enemy_pos) > 100.0f + En_objp->radius * 2.0f) {
8087                         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &predicted_enemy_pos, 10.0f))
8088                                 return;
8089                 }
8090
8091                 ai_chase_attack(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8092                 break;
8093
8094         case SM_EVADE_SQUIGGLE:
8095                 ai_chase_es(aip, sip);
8096                 break;
8097
8098         case SM_EVADE_BRAKE:
8099                 ai_chase_eb(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8100                 break;
8101
8102         case SM_EVADE:
8103                 evade_ship();
8104                 break;
8105
8106         case SM_AVOID:
8107                 avoid_ship();
8108                 break;
8109
8110         case SM_GET_BEHIND:
8111                 get_behind_ship(aip, sip, dist_to_enemy);
8112                 break;
8113
8114         case SM_GET_AWAY:               //      Used to get away from opponent to prevent endless circling.
8115                 ai_chase_ga(aip, sip);
8116                 break;
8117
8118         case SM_EVADE_WEAPON:
8119                 evade_weapon();
8120                 break;
8121
8122         default:
8123                 // Int3();
8124                 aip->last_attack_time = Missiontime;
8125                 aip->submode = SM_ATTACK;
8126                 aip->submode_start_time = Missiontime;
8127         }
8128
8129         //
8130         //      Maybe choose a new submode.
8131         //
8132         if ( (aip->submode != SM_AVOID) && (aip->submode != SM_ATTACK_FOREVER) ) {
8133                 //      If a very long time since attacked, attack no matter what!
8134                 if ( (aip->submode != SM_SUPER_ATTACK) && (aip->submode != SM_GET_AWAY) && !(aip->ai_flags & AIF_STEALTH_PURSIUT) ) {
8135                         if (Missiontime - aip->last_attack_time > i2f(6)) {
8136                                 aip->submode = SM_SUPER_ATTACK;
8137                                 aip->submode_start_time = Missiontime;
8138                                 aip->last_attack_time = Missiontime;
8139                         }
8140                 }
8141
8142                 //      If a collision is expected, pull out!
8143                 //      If enemy is pointing away and moving a bit, don't worry about collision detection.
8144                 if ((dot_from_enemy > 0.5f) || (En_objp->phys_info.speed < 10.0f)) {
8145                         if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 4.0f)) {
8146                                 if ((Missiontime - aip->last_hit_time > F1_0*4) && (dist_to_enemy < Pl_objp->radius*2 + En_objp->radius*2)) {
8147                                         accelerate_ship(aip, -1.0f);
8148                                 } else {
8149                                         aip->submode = SM_AVOID;
8150                                         aip->submode_start_time = Missiontime;
8151                                 }
8152                         }
8153                 }
8154         }
8155
8156         switch (aip->submode) {
8157         case SM_CONTINUOUS_TURN:
8158                 if (Missiontime - aip->submode_start_time > i2f(3)) {
8159                         aip->last_attack_time = Missiontime;
8160                         aip->submode = SM_ATTACK;
8161                         aip->submode_start_time = Missiontime;
8162                 }
8163                 break;
8164
8165         case SM_ATTACK:
8166                 // if taraget is stealth and stealth not visible, then enter stealth find mode
8167                 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8168                         aip->submode = SM_STEALTH_FIND;
8169                         aip->submode_start_time = Missiontime;
8170                         aip->submode_parm0 = SM_SF_AHEAD;
8171                 } 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)) {
8172                         aip->submode = SM_SUPER_ATTACK;
8173                         aip->submode_start_time = Missiontime;
8174                         aip->last_attack_time = Missiontime;
8175                 } else if ((Missiontime - aip->last_hit_target_time > i2f(6)) &&
8176                         (dist_to_enemy < 500.0f) && (dot_to_enemy < 0.2f) &&
8177                         (frand() < (float) Game_skill_level/NUM_SKILL_LEVELS)) {
8178                         aip->submode = SM_GET_AWAY;
8179                         aip->submode_start_time = Missiontime;
8180                         aip->last_hit_target_time = Missiontime;
8181                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP)
8182                         && (dot_to_enemy < dot_from_enemy)
8183                         && (En_objp->phys_info.speed > 15.0f) 
8184                         && (dist_to_enemy < 200.0f) 
8185                         && (dist_to_enemy > 50.0f)
8186                         && (dot_to_enemy < 0.1f)
8187                         && (Missiontime - aip->submode_start_time > i2f(2))) {
8188                         aip->submode = SM_EVADE_BRAKE;
8189                         aip->submode_start_time = Missiontime;
8190                 } else if ((dot_to_enemy > 0.2f) && (dot_from_enemy > -0.2f) && (dot_from_enemy < 0.1f)) {
8191                         aip->submode = SM_GET_BEHIND;
8192                         aip->submode_start_time = Missiontime;
8193                 } 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)) {
8194                         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;
8195                                 aip->submode_start_time = Missiontime;
8196                                 aip->last_hit_target_time = Missiontime;
8197                         } else {
8198                                 aip->submode = SM_EVADE_SQUIGGLE;
8199                                 aip->submode_start_time = Missiontime;
8200                         }
8201                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP) && (Missiontime - aip->submode_start_time > F1_0*2)) {
8202                         if ((dot_to_enemy < 0.8f) && (dot_from_enemy > dot_to_enemy)) {
8203                                 if (frand() > 0.5f) {
8204                                         aip->submode = SM_CONTINUOUS_TURN;
8205                                         aip->submode_parm0 = myrand() & 0x0f;
8206                                         aip->submode_start_time = Missiontime;
8207                                 } else {
8208                                         aip->submode = SM_EVADE;
8209                                         aip->submode_start_time = Missiontime;
8210                                 }
8211                         } else {
8212                                 aip->submode_start_time = Missiontime;
8213                         }
8214                 }
8215
8216                 aip->last_attack_time = Missiontime;
8217
8218                 break;
8219                 
8220         case SM_EVADE_SQUIGGLE:
8221                 if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > 300.0f)) {
8222                         if ((dist_to_enemy < 100.0f) && (dot_to_enemy < 0.0f) && (dot_from_enemy > 0.5f)) {
8223                                 aip->submode = SM_EVADE_BRAKE;
8224                                 aip->submode_start_time = Missiontime;
8225                         } else {
8226                                 aip->last_attack_time = Missiontime;
8227                                 aip->submode = SM_ATTACK;
8228                                 aip->submode_start_time = Missiontime;
8229                         }
8230                 }
8231                 break;
8232         
8233         case SM_EVADE_BRAKE:
8234                 if ((dist_to_enemy < 15.0f) || (En_objp->phys_info.speed < 10.0f)) {
8235                         aip->submode = SM_AVOID;
8236                         aip->submode_start_time = Missiontime;
8237                 } else if ((dot_to_enemy > 0.9f) || ((dot_from_enemy > 0.9f) && (Missiontime - aip->submode_start_time > i2f(1)))) {
8238                         aip->last_attack_time = Missiontime;
8239                         aip->submode = SM_ATTACK;
8240                         aip->submode_start_time = Missiontime;
8241                 } else if (Missiontime - aip->submode_start_time > i2f(4)) {
8242                         aip->last_attack_time = Missiontime;
8243                         aip->submode = SM_ATTACK;
8244                         aip->submode_start_time = Missiontime;
8245                 }
8246                 break;
8247
8248         case SM_EVADE:
8249                 //      Modified by MK on 5/5/97 to keep trying to regain attack mode.  It's what a human would do.
8250                 if ((dot_to_enemy < 0.2f) && (dot_from_enemy < 0.8f) && (dist_to_enemy < 100.0f) && (En_objp->phys_info.speed > 15.0f)) {
8251                         aip->last_attack_time = Missiontime;
8252                         aip->submode = SM_EVADE_BRAKE;
8253                         aip->submode_start_time = Missiontime;
8254                 } else if (((dot_to_enemy > dot_from_enemy - 0.1f)
8255                         && (Missiontime > aip->submode_start_time + i2f(1)))
8256                         || (dist_to_enemy > 150.0f + 2*(Pl_objp->radius + En_objp->radius))) {
8257                         aip->last_attack_time = Missiontime;
8258                         aip->submode = SM_ATTACK;
8259                         aip->submode_start_time = Missiontime;
8260                 } else if (Missiontime - aip->submode_start_time > i2f(2))
8261                         if (dot_from_enemy > 0.8f) {
8262                                 aip->submode = SM_EVADE_SQUIGGLE;
8263                                 aip->submode_start_time = Missiontime;
8264                         }
8265
8266                 break;
8267
8268         case SM_SUPER_ATTACK:
8269                 // if stealth and invisible, enter stealth find mode
8270                 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8271                         aip->submode = SM_STEALTH_FIND;
8272                         aip->submode_start_time = Missiontime;
8273                         aip->submode_parm0 = SM_SF_AHEAD;
8274                 } 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) )) {
8275                         aip->ai_flags &= ~AIF_ATTACK_SLOWLY;    //      Just in case, clear here.
8276
8277                         switch (myrand() % 5) {
8278                         case 0:
8279                                 aip->submode = SM_CONTINUOUS_TURN;
8280                                 aip->submode_start_time = Missiontime;
8281                                 break;
8282                         case 1:
8283                                 aip->submode_start_time = Missiontime;  //      Stay in super attack mode
8284                                 break;
8285                         case 2:
8286                         case 3:
8287                                 if (frand() < (float) 0.5f * (aip->ai_class + Game_skill_level)/(Num_ai_classes + NUM_SKILL_LEVELS)) {
8288                                         aip->submode = SM_GET_AWAY;
8289                                         aip->submode_start_time = Missiontime;
8290                                 } else {
8291                                         aip->submode = SM_EVADE;
8292                                         aip->submode_start_time = Missiontime;
8293                                 }
8294                                 break;
8295                         case 4:
8296                                 if (dot_from_enemy + (NUM_SKILL_LEVELS - Game_skill_level) * 0.1f > dot_to_enemy) {     //      Less likely to GET_AWAY at lower skill levels.
8297                                         aip->submode = SM_EVADE;
8298                                         aip->submode_start_time = Missiontime;
8299                                 } else {
8300                                         aip->submode = SM_GET_AWAY;
8301                                         aip->submode_start_time = Missiontime;
8302                                 }
8303                                 break;
8304                         default:
8305                                 Int3(); //      Impossible!
8306                         }
8307                 }
8308
8309                 aip->last_attack_time = Missiontime;
8310
8311                 break;
8312
8313         case SM_AVOID:
8314                 if ((dot_to_enemy > -0.2f) && (dist_to_enemy / (dot_to_enemy + 0.3f) < 100.0f)) {
8315                         aip->submode_start_time = Missiontime;
8316                 } else if (Missiontime - aip->submode_start_time > i2f(1)/2)
8317                         if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 3.0f)) {
8318                                 aip->submode_start_time = Missiontime;
8319                         } else {
8320                                 aip->submode = SM_GET_BEHIND;
8321                                 aip->submode_start_time = Missiontime;
8322                         }
8323
8324                 break;
8325
8326         case SM_GET_BEHIND:
8327                 if ((dot_from_enemy < -0.7f) || (Missiontime - aip->submode_start_time > i2f(2))) {
8328                         aip->submode = SM_ATTACK;
8329                         aip->submode_start_time = Missiontime;
8330                         aip->last_attack_time = Missiontime;
8331                 }
8332                 break;
8333
8334         case SM_GET_AWAY:
8335                 if (Missiontime - aip->submode_start_time > i2f(2)) {
8336                         float   rand_dist;
8337
8338                         rand_dist = ((Missiontime >> 17) & 0x03) * 100.0f + 200.0f;     //      Some value in 200..500
8339                         if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > rand_dist) || (dot_from_enemy < 0.4f)) {
8340                                 aip->ai_flags |= AIF_ATTACK_SLOWLY;
8341                                 aip->submode = SM_ATTACK;
8342                                 aip->time_enemy_in_range = 2.0f;                //      Cheat.  Presumably if they were running away from you, they were monitoring you!
8343                                 aip->submode_start_time = Missiontime;
8344                                 aip->last_attack_time = Missiontime;
8345                         }
8346                 }
8347                 break;
8348
8349         case SM_EVADE_WEAPON:
8350                 if (aip->danger_weapon_objnum == -1) {
8351                         aip->submode = SM_ATTACK;
8352                         aip->submode_start_time = Missiontime;
8353                         aip->last_attack_time = Missiontime;
8354                 }
8355                 break;
8356
8357         // Either change to SM_ATTACK or AIM_FIND_STEALTH
8358         case SM_STEALTH_FIND:
8359                 // if time > 5 sec change mode to sweep
8360                 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8361                         aip->submode = SM_ATTACK;
8362                         aip->submode_start_time = Missiontime;
8363                         aip->last_attack_time = Missiontime;
8364                         // sweep if I can't find in 5 sec or bail from find
8365                 } else if ( ((Missiontime - aip->submode_start_time) > i2f(5)) || (aip->submode_parm0 == SM_SF_BAIL) ) {
8366                         // begin sweep mode
8367                         aip->submode = SM_STEALTH_SWEEP;
8368                         aip->submode_start_time = Missiontime;
8369                         aip->last_attack_time = Missiontime;
8370                         aip->submode_parm0 = SM_SS_SET_GOAL;
8371                 }
8372                 break;
8373
8374         case SM_STEALTH_SWEEP:
8375                 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8376                         aip->submode = SM_ATTACK;
8377                         aip->submode_start_time = Missiontime;
8378                         aip->last_attack_time = Missiontime;
8379                 } else if ( (timestamp() - aip->stealth_last_visible_stamp) < 5000 ) {
8380                         // go back to find mode
8381                         aip->submode = SM_STEALTH_FIND;
8382                         aip->submode_start_time = Missiontime;
8383                         aip->submode_parm0 = SM_SF_AHEAD;
8384                 } else if ( /*(Missiontime - aip->submode_start_time) > i2f(30) || */(aip->submode_parm0 == SM_SS_DONE) ) {
8385                         // set target objnum = -1
8386                         set_target_objnum(aip, -1);
8387
8388                         // set submode to attack
8389                         aip->submode = SM_ATTACK;
8390                         aip->submode_start_time = Missiontime;
8391                         aip->last_attack_time = Missiontime;
8392                 }
8393                 break;
8394
8395         case SM_ATTACK_FOREVER: //      Engines blown, just attack.
8396                 break;
8397
8398         default:
8399                 //Int3();
8400                 aip->submode = SM_ATTACK;
8401                 aip->last_attack_time = Missiontime;
8402
8403                 aip->submode_start_time = Missiontime;
8404         }
8405
8406         //
8407         //      Maybe fire primary weapon and update time_enemy_in_range
8408         //
8409         //nprintf(("AI", "time_enemy_in_range = %7.3f, dot = %7.3f\n", aip->time_enemy_in_range, dot_to_enemy));
8410
8411         if (aip->mode != AIM_EVADE) {
8412                 if (dot_to_enemy > 0.95f - 0.5f * En_objp->radius/max(1.0f, En_objp->radius + dist_to_enemy)) {
8413                         aip->time_enemy_in_range += flFrametime;
8414                         
8415                         //      Chance of hitting ship is based on dot product of firing ship's forward vector with vector to ship
8416                         //      and also the size of the target relative to distance to target.
8417                         if (dot_to_enemy > max(0.5f, 0.90f + aip->ai_accuracy/10.0f - En_objp->radius/max(1.0f,dist_to_enemy))) {
8418
8419                                 ship *temp_shipp;
8420                                 ship_weapon *tswp;
8421
8422                                 temp_shipp = &Ships[Pl_objp->instance];
8423                                 tswp = &temp_shipp->weapons;
8424                                 if ( tswp->num_primary_banks > 0 ) {
8425                                         float   scale;
8426                                         Assert(tswp->current_primary_bank < tswp->num_primary_banks);
8427                                         weapon_info     *pwip = &Weapon_info[tswp->primary_bank_weapons[tswp->current_primary_bank]];
8428
8429                                         //      Less likely to fire if far away and moving.
8430                                         scale = pwip->max_speed/(En_objp->phys_info.speed + pwip->max_speed);
8431                                         if (scale > 0.6f)
8432                                                 scale = (scale - 0.6f) * 1.5f;
8433                                         else
8434                                                 scale = 0.0f;
8435                                         if (dist_to_enemy < pwip->max_speed * (1.0f + scale)) {
8436                                                 ai_fire_primary_weapon(Pl_objp);
8437                                         }
8438
8439                                         //      Don't fire secondaries at a protected ship.
8440                                         if (!(En_objp->flags & OF_PROTECTED)) {
8441                                                 ai_choose_secondary_weapon(Pl_objp, aip, En_objp);
8442                                                 int current_bank = tswp->current_secondary_bank;
8443                                                 weapon_info     *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8444
8445                                                 if (current_bank > -1) {
8446                                                         if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8447                                                                 if (timestamp_until(swp->next_secondary_fire_stamp[current_bank]) > swip->fire_wait*1000.0f) {
8448                                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (swip->fire_wait*1000.0f));
8449                                                                 }
8450                                                         }
8451
8452                                                         if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
8453                                                                 if (tswp->current_secondary_bank >= 0) {
8454                                                                         weapon_info     *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8455                                                                         float firing_range;
8456                                                                         
8457                                                                         if (swip->wi_flags & WIF_BOMB)
8458                                                                                 firing_range = swip->max_speed * swip->lifetime * 0.75f;
8459                                                                         else
8460                                                                                 firing_range = swip->max_speed * swip->lifetime * (Game_skill_level + 1 + aip->ai_class/2)/NUM_SKILL_LEVELS;
8461
8462                                                                         // reduce firing range in nebula
8463                                                                         extern int Nebula_sec_range;
8464                                                                         if ((The_mission.flags & MISSION_FLAG_FULLNEB) && Nebula_sec_range) {
8465                                                                                 firing_range *= 0.8f;
8466                                                                         }
8467
8468                                                                         //      If firing a spawn weapon, distance doesn't matter.
8469                                                                         int     spawn_fire = 0;
8470
8471                                                                         if (swip->wi_flags & WIF_SPAWN) {
8472                                                                                 int     count;
8473
8474                                                                                 count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(Pl_objp)), &Pl_objp->pos, 1000.0f);
8475
8476                                                                                 if (count > 3)
8477                                                                                         spawn_fire = 1;
8478                                                                                 else if (count >= 1) {
8479                                                                                         float hull_percent = Pl_objp->hull_strength/sip->initial_hull_strength;
8480
8481                                                                                         if (hull_percent < 0.01f)
8482                                                                                                 hull_percent = 0.01f;
8483
8484                                                                                         if (frand() < 0.25f/(30.0f*hull_percent) * count)       //      With timestamp below, this means could fire in 30 seconds if one enemy.
8485                                                                                                 spawn_fire = 1;
8486                                                                                 }
8487                                                                         }
8488
8489                                                                         if (spawn_fire || (dist_to_enemy < firing_range)) {
8490                                                                                 if (ai_fire_secondary_weapon(Pl_objp)) {
8491                                                                                         //      Only if weapon was fired do we specify time until next fire.  If not fired, done in ai_fire_secondary...
8492                                                                                         float t;
8493                                                                                         
8494                                                                                         if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8495                                                                                                 t = swip->fire_wait;
8496                                                                                         } else {
8497                                                                                                 t = set_secondary_fire_delay(aip, temp_shipp, swip);
8498                                                                                         }
8499                                                                                         //nprintf(("AI", "Next secondary to be fired in %7.3f seconds.\n", t));
8500                                                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
8501                                                                                 }
8502                                                                         } else {
8503                                                                                 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
8504                                                                         }
8505                                                                 }
8506                                                         }
8507                                                 }
8508                                         }
8509                                 }
8510                         }
8511                 } else {
8512                         aip->time_enemy_in_range *= (1.0f - flFrametime);
8513                 }
8514         } else
8515                 aip->time_enemy_in_range *= (1.0f - flFrametime);
8516
8517 }
8518
8519 //      Make the object *objp move so that the point *dp on the object moves towards the point *vp
8520 //      Return distance.
8521 void dock_move_towards_point(object *objp, vector *dp, vector *vp, float speed_scale, float other_obj_speed = 0.0f)
8522 {
8523         physics_info    *pi = &objp->phys_info;
8524         float                           dist;                   //      dist to goal
8525         vector                  v2g;                    //      vector to goal
8526         vector                  abs_pnt;                //      location of dock point, ie objp->pos + db
8527
8528         if (dp == NULL)
8529                 abs_pnt = objp->pos;
8530         else
8531                 vm_vec_add(&abs_pnt, &objp->pos, dp);
8532
8533         dist = vm_vec_dist_quick(vp, &abs_pnt);
8534         if (dist > 0.0f) {
8535                 float   speed;
8536
8537                 dist = vm_vec_normalized_dir(&v2g, vp, &abs_pnt);
8538                 speed = fl_sqrt(dist) * speed_scale;
8539                 if (other_obj_speed < MAX_REPAIR_SPEED*0.75f)
8540                         speed += other_obj_speed;
8541                 else
8542                         speed += MAX_REPAIR_SPEED*0.75f;
8543
8544                 vm_vec_copy_scale(&pi->desired_vel, &v2g, speed);
8545         } else
8546                 vm_vec_zero(&pi->desired_vel);
8547 }
8548
8549 //      Set the orientation in the global reference frame for an object to attain
8550 //      to dock with another object.
8551 //      *dom            resultant global matrix
8552 //      *db_dest        pointer to destination docking bay information
8553 //      *db_src pointer to source docking bay information
8554 //      *dorient        pointer to global orientation of docking bay (ie, the dockee object's orient)
8555 //      *sorient        pointer to global orientation of docker
8556 void set_goal_dock_orient(matrix *dom, dock_bay *db_dest, dock_bay *db_src, matrix *dorient, matrix *sorient)
8557 {
8558         vector  fvec, uvec;
8559         matrix  m1, m2, m3;
8560
8561         //      Compute the global orientation of the docker's (dest) docking bay.
8562         fvec = db_dest->norm[0];
8563         vm_vec_negate(&fvec);
8564
8565         vm_vec_normalized_dir(&uvec, &db_dest->pnt[1], &db_dest->pnt[0]);
8566         vm_vector_2_matrix(&m1, &fvec, &uvec, NULL);
8567
8568         vm_matrix_x_matrix(&m3, dorient, &m1);
8569
8570         //      Compute the matrix given by the source docking bay.
8571         //      Pre-multiply the orientation of the source object (sorient) by the transpose
8572         //      of the docking bay's orientation, ie unrotate the source object's matrix.
8573         fvec = db_src->norm[0];
8574         vm_vec_normalized_dir(&uvec, &db_src->pnt[1], &db_src->pnt[0]);
8575         vm_vector_2_matrix(&m2, &fvec, &uvec, NULL);
8576         vm_transpose(&m2);
8577
8578         vm_matrix_x_matrix(dom, &m3, &m2);
8579 }
8580
8581 #define DOCK_BACKUP_RETURN_VAL  99999.9f
8582
8583 //      Make objp dock with dobjp
8584 //      Returns distance to goal, defined as distance between corresponding dock points, plus 10.0f * rotational velocity vector (DOA_DOCK only)
8585 //      DOA_APPROACH    means   approach point aip->path_cur
8586 //      DOA_DOCK                        means dock
8587 //      DOA_UNDOCK_1    means undock, moving to point nearest dock bay
8588 //      DOA_UNDOCK_2    means undock, moving to point nearest dock bay and facing away from ship
8589 //      DOA_DOCK_STAY   means rigidly maintain position in dock bay.
8590 float dock_orient_and_approach(object *objp, object *dobjp, int dock_mode)
8591 {
8592         ship_info       *sip0, *sip1;
8593         polymodel       *pm0, *pm1;
8594         ai_info         *aip;
8595         matrix          dom, nm;
8596         vector          goal_point, docker_point;
8597         float                   fdist = UNINITIALIZED_VALUE;
8598         int                     docker_index, dockee_index;             // index into docking_bays[] array for objects docking
8599                                                                                                                                 // docker is Pl_objp -- dockee is dobjp
8600         aip = &Ai_info[Ships[objp->instance].ai_index];
8601
8602         //      If dockee has moved much, then path will be recreated.
8603         //      Might need to change state if moved too far.
8604         if ((dock_mode != DOA_DOCK_STAY) && (dock_mode != DOA_DOCK)) {
8605                 if (maybe_recreate_path(objp, &Ai_info[Ships[objp->instance].ai_index], 0) > 5.0f) {
8606 /*                      if (dock_mode == DOA_APPROACH) {
8607                                 return DOCK_BACKUP_RETURN_VAL;
8608                         } else if (dock_mode == DOA_DOCK) {
8609                                 return DOCK_BACKUP_RETURN_VAL;          
8610                         }
8611 */              }
8612         }
8613
8614         objp->phys_info.forward_thrust = 0.0f;          //      Kill thrust so we don't have a sputtering thruster.
8615
8616         sip0 = &Ship_info[Ships[objp->instance].ship_info_index];
8617         sip1 = &Ship_info[Ships[dobjp->instance].ship_info_index];
8618         pm0 = model_get( sip0->modelnum );
8619         pm1 = model_get( sip1->modelnum );
8620
8621         docker_index = aip->dock_index;
8622         dockee_index = aip->dockee_index;
8623
8624         Assert( docker_index >= 0 );
8625         Assert( dockee_index >= 0 );
8626
8627         Assert(pm0->docking_bays[docker_index].num_slots == 2);
8628         Assert(pm1->docking_bays[dockee_index].num_slots == 2);
8629
8630         float speed_scale = 1.0f;
8631         if (sip0->flags & SIF_SUPPORT) {
8632                 speed_scale = 3.0f;
8633         }
8634
8635         switch (dock_mode) {
8636         case DOA_APPROACH:
8637                 {
8638                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8639                         return 9999.9f;
8640                 }
8641                 
8642                 //      Compute the desired global orientation matrix for the docker's station.
8643                 //      That is, the normal vector of the docking station must be the same as the
8644                 //      forward vector and the vector between its two points must be the uvec.
8645                 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8646
8647                 //      Compute new orientation matrix and update rotational velocity.
8648                 vector  w_in, w_out, vel_limit, acc_limit;
8649                 float           tdist, mdist, ss1;
8650
8651                 w_in = objp->phys_info.rotvel;
8652                 vel_limit = objp->phys_info.max_rotvel;
8653                 vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8654                 
8655                 if (sip0->flags & SIF_SUPPORT)
8656                         vm_vec_scale(&acc_limit, 2.0f);
8657
8658                 // 1 at end of line prevent overshoot
8659                 vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit, 1);
8660                 objp->phys_info.rotvel = w_out;
8661                 objp->orient = nm;
8662
8663                 //      Translate towards goal and note distance to goal.
8664                 goal_point = Path_points[aip->path_cur].pos;
8665                 mdist = ai_matrix_dist(&objp->orient, &dom);
8666                 tdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8667
8668                 //      If translation is badly lagging rotation, speed up translation.
8669                 if (mdist > 0.1f) {
8670                         ss1 = tdist/(10.0f * mdist);
8671                         if (ss1 > 2.0f)
8672                                 ss1 = 2.0f;
8673                 } else
8674                         ss1 = 2.0f;
8675
8676                 // nprintf(("AI", "speed scale = %7.3f\n", ss1));
8677                 speed_scale *= 1.0f + ss1;
8678
8679                 dock_move_towards_point(objp, NULL, &goal_point, speed_scale, dobjp->phys_info.speed);
8680
8681                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8682
8683                 //      Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8684                 // nprintf(("AI", "matrix dist = %7.3f, threshold = %7.3f\n", mdist, 2*flFrametime));
8685                 fdist += 2.0f * mdist;
8686
8687                 break;
8688         }
8689         case DOA_DOCK:
8690                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8691                         return 9999.9f;
8692                 }
8693         case DOA_DOCK_STAY:
8694                 //      Compute the desired global orientation matrix for the docker's station.
8695                 //      That is, the normal vector of the docking station must be the same as the
8696                 //      forward vector and the vector between its two points must be the uvec.
8697                 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8698
8699                 //      Compute distance between dock bay points.
8700                 vector  db0, db1, db2, db3;
8701
8702                 vm_vec_unrotate(&db0, &pm0->docking_bays[docker_index].pnt[0], &objp->orient);
8703                 vm_vec_add2(&db0, &objp->pos);
8704
8705                 vm_vec_unrotate(&db1, &pm0->docking_bays[docker_index].pnt[1], &objp->orient);
8706                 vm_vec_add2(&db1, &objp->pos);
8707
8708                 vm_vec_unrotate(&db2, &pm1->docking_bays[dockee_index].pnt[0], &dobjp->orient);
8709                 vm_vec_add2(&db2, &dobjp->pos);
8710
8711                 vm_vec_unrotate(&db3, &pm1->docking_bays[dockee_index].pnt[1], &dobjp->orient);
8712                 vm_vec_add2(&db3, &dobjp->pos);
8713
8714                 vm_vec_avg(&goal_point, &db2, &db3);
8715
8716                 vm_vec_avg(&docker_point, &db0, &db1);
8717                 vm_vec_sub2(&docker_point, &objp->pos);
8718
8719                 if (dock_mode == DOA_DOCK) {
8720                         vector  t1, t2;
8721                         vector  w_in, w_out, vel_limit, acc_limit;
8722
8723                         fdist = vm_vec_dist_quick(vm_vec_avg(&t1, &db0, &db1), vm_vec_avg(&t2, &db2, &db3));
8724
8725                         //      Compute new orientation matrix and update rotational velocity.
8726                         w_in = objp->phys_info.rotvel;
8727                         vel_limit = objp->phys_info.max_rotvel;
8728                         vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8729
8730                         if (sip0->flags & SIF_SUPPORT)
8731                                 vm_vec_scale(&acc_limit, 2.0f);
8732
8733                         vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit);
8734                         objp->phys_info.rotvel = w_out;
8735                         objp->orient = nm;
8736
8737                         //      Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8738                         fdist += 10.0f * vm_vec_mag_quick(&w_out);
8739
8740                         dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale, dobjp->phys_info.speed);
8741                 } else {
8742                         Assert(dock_mode == DOA_DOCK_STAY);
8743                         objp->orient = dom;
8744                         vector  temp;
8745                         vm_vec_sub(&temp, &goal_point, &docker_point);
8746                         vm_vec_sub(&objp->pos, &goal_point, &docker_point);
8747                 }
8748
8749                 break;
8750         case DOA_UNDOCK_1: {
8751                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8752                         return 9999.9f;
8753                 }
8754
8755                 //      Undocking.
8756                 //      Move to point on dock path nearest to dock station.
8757                 Assert(aip->path_length >= 2);
8758                 goal_point = Path_points[aip->path_start + aip->path_length-2].pos;
8759
8760                 vm_vec_zero(&docker_point);
8761                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8762
8763                 dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale);
8764
8765                 break;
8766                           }
8767
8768         case DOA_UNDOCK_2: {
8769                 //      Undocking.
8770                 //      Move to point on dock path nearest to dock station and orient away from big ship.
8771                 int             desired_index;
8772
8773                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8774                         return 9999.9f;
8775                 }
8776
8777                 Assert(aip->path_length >= 2);
8778 //              if (aip->path_length >= 3)
8779 //                      desired_index = aip->path_length-3;
8780 //              else
8781                         desired_index = aip->path_length-2;
8782
8783                 goal_point = Path_points[aip->path_start + desired_index].pos;
8784
8785                 dock_move_towards_point(objp, NULL, &goal_point, speed_scale);
8786
8787                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8788                 break;
8789                           }
8790         case DOA_UNDOCK_3: {
8791                 float           dist, goal_dist;
8792                 vector  away_vec;
8793
8794                 goal_dist = objp->radius + dobjp->radius + 25.0f;
8795
8796                 dist = vm_vec_normalized_dir(&away_vec, &objp->pos, &dobjp->pos);
8797                 vm_vec_scale_add(&goal_point, &dobjp->pos, &away_vec, goal_dist);
8798                 if (vm_vec_dist_quick(&goal_point, &dobjp->pos) < vm_vec_dist_quick(&objp->pos, &dobjp->pos))
8799                         fdist = 0.0f;
8800                 else {
8801                         float   dot, accel;
8802                         float turn_time = Ship_info[Ships[objp->instance].ship_info_index].srotation_time;
8803                         ai_turn_towards_vector(&goal_point, objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
8804
8805                         dot = vm_vec_dot(&objp->orient.v.fvec, &away_vec);
8806                         accel = 0.1f;
8807                         if (dot > accel)
8808                                 accel = dot;
8809                         if (dist > goal_dist/2)
8810                                 accel *= 1.2f - 0.5f*goal_dist/dist;
8811
8812                         accelerate_ship(aip, accel);
8813                         fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8814                 }
8815
8816                 break;
8817                                                          }
8818         }
8819
8820 #ifndef NDEBUG
8821         //      For debug purposes, compute global orientation of both dock vectors and show
8822         //      how close they are.
8823         vector  d0, d1;
8824
8825         vm_vec_unrotate(&d0, &pm0->docking_bays[docker_index].norm[0], &objp->orient);
8826         vm_vec_unrotate(&d1, &pm1->docking_bays[dockee_index].norm[0], &dobjp->orient);
8827
8828         //nprintf(("AI", "or/app: dist = %7.3f/%7.3f, dot = %7.3f, global dot = %7.3f\n", 
8829         //      vm_vec_dist_quick(&goal_point, &objp->pos), fdist,
8830         //      vm_vec_dot(&objp->orient.v.fvec, &dom.v.fvec), 
8831         //      vm_vec_dot(&d0, &d1)));
8832 #endif
8833
8834         // -- Note, A lot of callers don't care about fdist, so OK to return ERROR value: Assert(fdist != UNINITIALIZED_VALUE);
8835         return fdist;
8836
8837 }
8838
8839 void debug_find_guard_object()
8840 {
8841         ship                    *shipp = &Ships[Pl_objp->instance];     
8842         object          *objp;
8843
8844         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
8845                 if ((Pl_objp != objp) && (objp->type == OBJ_SHIP)) {
8846                         if (objp->instance != -1) {
8847                                 if (Ships[objp->instance].team == shipp->team)  {
8848                                         // nprintf(("AI", "Setting guard object for %s to %s\n", shipp->ship_name, Ships[objp->instance].ship_name));
8849                                         ai_set_guard_object(Pl_objp, objp);
8850                                 }
8851                         }
8852                 }
8853         }
8854
8855 }
8856
8857 //      Given an object number, return the number of ships attacking it.
8858 int num_ships_attacking(int objnum)
8859 {
8860         object  *objp;
8861         ship_obj        *so;
8862         int             count = 0;
8863
8864         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8865                 objp = &Objects[so->objnum];
8866                 if (objp->instance != -1) {
8867                         ai_info *aip;
8868                         aip = &Ai_info[Ships[objp->instance].ai_index];
8869
8870                         if ((aip->mode == AIM_CHASE) && (aip->target_objnum == objnum))
8871                                 if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team)
8872                                         count++;
8873                 }
8874         }
8875
8876         return count;
8877 }
8878
8879 //      For all objects attacking object #objnum, remove the one that is farthest away.
8880 //      Do this by resuming previous behavior, if any.  If not, set target_objnum to -1.
8881 void remove_farthest_attacker(int objnum)
8882 {
8883         object  *objp, *objp2, *farthest_objp;
8884         ship_obj        *so;
8885         float           farthest_dist;
8886
8887         objp2 = &Objects[objnum];
8888
8889         farthest_dist = 9999999.9f;
8890         farthest_objp = NULL;
8891
8892         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8893                 objp = &Objects[so->objnum];
8894                 if ( !(objp->flags & OF_PLAYER_SHIP)) {
8895                         if (objp->instance != -1) {
8896                                 ai_info *aip2;
8897
8898                                 aip2 = &Ai_info[Ships[objp->instance].ai_index];
8899
8900                                 if ((aip2->mode == AIM_CHASE) && (aip2->target_objnum == objnum)) {
8901                                         if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team) {
8902                                                 float   dist;
8903
8904                                                 dist = vm_vec_dist_quick(&objp->pos, &objp2->pos);
8905                                                 if (dist < farthest_dist) {
8906                                                         farthest_dist = dist;
8907                                                         farthest_objp = objp;
8908                                                 }
8909                                         }
8910                                 }
8911                         }
8912                 }
8913         }
8914
8915         if (farthest_objp != NULL) {
8916                 ai_info *aip;
8917                 Assert(farthest_objp->type == OBJ_SHIP);
8918                 Assert((farthest_objp->instance > -1) && (farthest_objp->instance < MAX_SHIPS));
8919                 Assert(Ships[farthest_objp->instance].ai_index > -1);
8920
8921                 aip = &Ai_info[Ships[farthest_objp->instance].ai_index];
8922
8923                 if (!maybe_resume_previous_mode(Pl_objp, aip)) {
8924                         //      If already ignoring something under player's orders, don't ignore current target.
8925                         if ((aip->ignore_objnum == UNUSED_OBJNUM) || (aip->ai_flags & AIF_TEMPORARY_IGNORE)) {
8926                                 aip->ignore_objnum = aip->target_objnum;
8927                                 aip->ignore_signature = Objects[aip->target_objnum].signature;
8928                                 aip->ai_flags |= AIF_TEMPORARY_IGNORE;
8929                                 aip->ignore_expire_timestamp = timestamp(((myrand() % 10) + 20) * 1000);        //      OK to attack again in 20 to 24 seconds.
8930                         }
8931                         aip->target_objnum = -1;
8932                         ai_do_default_behavior(farthest_objp);
8933                 }
8934         }
8935 }
8936
8937 // Maybe limit the number of attackers on attack_objnum.  For now, only limit attackers
8938 // in attacked_objnum is the player
8939 // input:       attacked_objnum =>              object index for ship we want to limit attacks on
8940 //
8941 //      exit:                   1       =>      num attackers exceeds maximum, abort
8942 //                                      0       =>      removed the farthest attacker
8943 //                                      -1      =>      nothing was done
8944 int ai_maybe_limit_attackers(int attacked_objnum)
8945 {
8946         int rval=-1;
8947
8948         // limit the number of ships attacking the _player_ only
8949 //      if ( attacked_objnum == OBJ_INDEX(Player_obj) ) {
8950         if ( Objects[attacked_objnum].flags & OF_PLAYER_SHIP) {
8951                 int num_attacking;
8952                 num_attacking = num_ships_attacking(attacked_objnum);
8953
8954                 if (num_attacking == Skill_level_max_attackers[Game_skill_level]) {
8955                         remove_farthest_attacker(attacked_objnum);
8956                         rval=0;
8957                 } else if (num_attacking > Skill_level_max_attackers[Game_skill_level]) {
8958                         rval=1;
8959                 }
8960                 //nprintf(("AI", "Num attacking player = %i\n", num_attacking));
8961         }
8962
8963         return rval;
8964 }
8965
8966 //      Object being guarded by object *guard_objp was hit by object *hitter_objp
8967 void guard_object_was_hit(object *guard_objp, object *hitter_objp)
8968 {
8969         int             hitter_objnum;
8970         ai_info *aip;
8971
8972         aip = &Ai_info[Ships[guard_objp->instance].ai_index];
8973
8974         if (guard_objp == hitter_objp) {
8975                 // Int3();      //      Bogus!  Who tried to get me to attack myself!  Trace out and fix!
8976                 return;
8977         }
8978
8979         if (guard_objp->type == OBJ_GHOST || hitter_objp->type == OBJ_GHOST)
8980                 return;
8981
8982         if (aip->ai_flags & AIF_NO_DYNAMIC)     //      Not allowed to pursue dynamic goals.  So, why are we guarding?
8983                 return;
8984
8985         Assert( (hitter_objp->type == OBJ_SHIP) || (hitter_objp->type == OBJ_ASTEROID) || (hitter_objp->type == OBJ_WEAPON) );
8986
8987         hitter_objnum = OBJ_INDEX(hitter_objp);
8988
8989         if ( hitter_objp->type == OBJ_SHIP ) {
8990                 //      If the hitter object is the ignore object, don't attack it.
8991                 if (is_ignore_object(aip, hitter_objp-Objects))
8992                         return;
8993
8994                 //      If hitter is on same team as me, don't attack him.
8995                 if (Ships[guard_objp->instance].team == Ships[hitter_objp->instance].team)
8996                         return;
8997
8998                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
8999                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9000                         return;
9001                 }
9002
9003                 // dont attack if you can't see him
9004                 if ( awacs_get_level(hitter_objp, &Ships[aip->shipnum], 1) < 1 ) {
9005                         // if he's a stealth and visible, but not targetable, ok to attack.
9006                         if ( is_object_stealth_ship(hitter_objp) ) {
9007                                 if ( ai_is_stealth_visible(guard_objp, hitter_objp) != STEALTH_VISIBLE ) {
9008                                         return;
9009                                 }
9010                         }
9011                 }
9012         }
9013
9014         if (aip->target_objnum == -1) {
9015                 aip->ok_to_target_timestamp = timestamp(0);
9016         }
9017
9018         if ((aip->submode == AIS_GUARD_PATROL) || (aip->submode == AIS_GUARD_STATIC)) {
9019
9020                 if ( hitter_objp->type == OBJ_SHIP ) {
9021                         if (!(Ship_info[Ships[guard_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
9022                                 return;
9023                         }
9024
9025                         // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9026                         if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9027                                 return;
9028                         }
9029                 }
9030
9031                 if (aip->target_objnum != hitter_objnum) {
9032                         aip->aspect_locked_time = 0.0f;
9033                 }
9034
9035                 aip->ok_to_target_timestamp = timestamp(0);
9036
9037                 set_target_objnum(aip, hitter_objnum);
9038                 //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));
9039                 aip->previous_mode = AIM_GUARD;
9040                 aip->previous_submode = aip->submode;
9041                 aip->mode = AIM_CHASE;
9042                 aip->submode = SM_ATTACK;
9043                 aip->submode_start_time = Missiontime;
9044                 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9045         } else if (aip->previous_mode == AIM_GUARD) {
9046                 if (aip->target_objnum == -1) {
9047
9048                         if ( hitter_objp->type == OBJ_SHIP ) {
9049                                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9050                                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9051                                         return;
9052                                 }
9053                         }
9054
9055                         set_target_objnum(aip, hitter_objnum);
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                 } else {
9062                         int     num_attacking_cur, num_attacking_new;
9063
9064                         num_attacking_cur = num_ships_attacking(aip->target_objnum);
9065                         if (num_attacking_cur > 1) {
9066                                 num_attacking_new = num_ships_attacking(hitter_objnum);
9067
9068                                 if (num_attacking_new < num_attacking_cur) {
9069
9070                                         if ( hitter_objp->type == OBJ_SHIP ) {
9071                                                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9072                                                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9073                                                         return;
9074                                                 }
9075                                         }
9076                                         set_target_objnum(aip, hitter_objp-Objects);
9077                 //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));
9078                                         aip->mode = AIM_CHASE;
9079                                         aip->submode = SM_ATTACK;
9080                                         aip->submode_start_time = Missiontime;
9081                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9082                                 }
9083                         }
9084                 }
9085         }
9086 }
9087
9088 //      Ship object *hit_objp was hit by ship object *hitter_objp.
9089 //      See if anyone is guarding hit_objp and, if so, do something useful.
9090 void maybe_update_guard_object(object *hit_objp, object *hitter_objp)
9091 {
9092         object  *objp;
9093         ship_obj        *so;
9094
9095         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9096                 objp = &Objects[so->objnum];
9097                 if (objp->instance != -1) {
9098                         ai_info *aip;
9099                         aip = &Ai_info[Ships[objp->instance].ai_index];
9100
9101                         if ((aip->mode == AIM_GUARD) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
9102                                 if (aip->guard_objnum == hit_objp-Objects) {
9103                                         guard_object_was_hit(objp, hitter_objp);
9104                                 } else if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[hit_objp->instance].ai_index].wing)) {
9105                                         guard_object_was_hit(objp, hitter_objp);
9106                                 }
9107                         }
9108                 }
9109         }
9110 }
9111
9112 // Scan missile list looking for bombs homing on guarded_objp
9113 // return 1 if bomb is found (and targeted by guarding_objp), otherwise return 0
9114 int ai_guard_find_nearby_bomb(object *guarding_objp, object *guarded_objp)
9115 {       
9116         missile_obj     *mo;
9117         object          *bomb_objp, *closest_bomb_objp=NULL;
9118         float                   dist, dist_to_guarding_obj,closest_dist_to_guarding_obj=999999.0f;
9119         weapon          *wp;
9120         weapon_info     *wip;
9121
9122         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
9123                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
9124                 bomb_objp = &Objects[mo->objnum];
9125
9126                 wp = &Weapons[bomb_objp->instance];
9127                 wip = &Weapon_info[wp->weapon_info_index];
9128
9129                 if ( !(wip->wi_flags & WIF_BOMB) ) {
9130                         continue;
9131                 }
9132
9133                 if ( wp->homing_object != guarded_objp ) {
9134                         continue;
9135                 }
9136
9137                 dist = vm_vec_dist_quick(&bomb_objp->pos, &guarded_objp->pos);
9138
9139                 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9140                         dist_to_guarding_obj = vm_vec_dist_quick(&bomb_objp->pos, &guarding_objp->pos);
9141                         if ( dist_to_guarding_obj < closest_dist_to_guarding_obj ) {
9142                                 closest_dist_to_guarding_obj = dist_to_guarding_obj;
9143                                 closest_bomb_objp = bomb_objp;
9144                         }
9145                 }
9146         }
9147
9148         if ( closest_bomb_objp ) {
9149                 guard_object_was_hit(guarding_objp, closest_bomb_objp);
9150                 return 1;
9151         }
9152
9153         return 0;
9154 }
9155
9156 //      Scan enemy ships and see if one is near enough to guard object to be pursued.
9157 void ai_guard_find_nearby_ship(object *guarding_objp, object *guarded_objp)
9158 {
9159         ship            *guarding_shipp = &Ships[guarding_objp->instance];
9160         ai_info *guarding_aip = &Ai_info[guarding_shipp->ai_index];
9161         ship_obj        *so;
9162         object  *enemy_objp;
9163         float           dist;
9164
9165         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9166                 enemy_objp = &Objects[so->objnum];
9167
9168                 if (enemy_objp->instance < 0) {
9169                         continue;
9170                 }
9171
9172                 ship    *eshipp = &Ships[enemy_objp->instance];
9173
9174                 //      Don't attack a cargo container or other harmless ships
9175                 if (!(Ship_info[eshipp->ship_info_index].flags & SIF_HARMLESS)) {
9176                         if (guarding_shipp->team != eshipp->team)       {
9177                                 dist = vm_vec_dist_quick(&enemy_objp->pos, &guarded_objp->pos);
9178                                 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9179                                         guard_object_was_hit(guarding_objp, enemy_objp);
9180                                 } else if ((dist < 3000.0f) && (Ai_info[eshipp->ai_index].target_objnum == guarding_aip->guard_objnum)) {
9181                                         //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));
9182                                         guard_object_was_hit(guarding_objp, enemy_objp);
9183                                 }
9184                         }
9185                 }
9186         }
9187 }
9188
9189 // Scan for nearby asteroids.  Favor asteroids which have their collide_objnum set to that of the
9190 // guarded ship.  Also, favor asteroids that are closer to the guarding ship, since it looks cooler
9191 // when a ship blows up an asteroid then goes after the pieces that break off.
9192 void ai_guard_find_nearby_asteroid(object *guarding_objp, object *guarded_objp)
9193 {       
9194         float           dist;
9195
9196         object  *closest_asteroid_objp=NULL, *danger_asteroid_objp=NULL, *asteroid_objp;
9197         float           dist_to_self, closest_danger_asteroid_dist=999999.0f, closest_asteroid_dist=999999.0f;
9198
9199         for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp != END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) {
9200                 if ( asteroid_objp->type == OBJ_ASTEROID ) {
9201                         // Attack asteroid if near guarded ship
9202                         dist = vm_vec_dist_quick(&asteroid_objp->pos, &guarded_objp->pos);
9203                         if ( dist < (MAX_GUARD_DIST + guarded_objp->radius)*2) {
9204                                 dist_to_self = vm_vec_dist_quick(&asteroid_objp->pos, &guarding_objp->pos);
9205                                 if ( OBJ_INDEX(guarded_objp) == asteroid_collide_objnum(asteroid_objp) ) {
9206                                         if( dist_to_self < closest_danger_asteroid_dist ) {
9207                                                 danger_asteroid_objp=asteroid_objp;
9208                                                 closest_danger_asteroid_dist=dist_to_self;
9209                                         }
9210                                 } 
9211                                 if ( dist_to_self < closest_asteroid_dist ) {
9212                                         // only attack if moving slower than own max speed
9213                                         if ( vm_vec_mag_quick(&asteroid_objp->phys_info.vel) < guarding_objp->phys_info.max_vel.xyz.z ) {
9214                                                 closest_asteroid_dist = dist_to_self;
9215                                                 closest_asteroid_objp = asteroid_objp;
9216                                         }
9217                                 }
9218                         }
9219                 }
9220         }
9221
9222         if ( danger_asteroid_objp ) {
9223                 guard_object_was_hit(guarding_objp, danger_asteroid_objp);
9224         } else if ( closest_asteroid_objp ) {
9225                 guard_object_was_hit(guarding_objp, closest_asteroid_objp);
9226         }
9227 }
9228
9229 //      Scan potential harmful objects and see if one is near enough to guard object to be pursued.
9230 void ai_guard_find_nearby_object()
9231 {
9232         ship                    *shipp = &Ships[Pl_objp->instance];
9233         ai_info         *aip = &Ai_info[shipp->ai_index];
9234         object          *guardobjp;
9235         int                     bomb_found=0;
9236
9237         guardobjp = &Objects[aip->guard_objnum];
9238         
9239         // highest priority is a bomb fired on guarded ship
9240         bomb_found = ai_guard_find_nearby_bomb(Pl_objp, guardobjp);
9241
9242         if ( !bomb_found ) {
9243                 // check for ships if there are no bombs fired at guarded ship
9244                 ai_guard_find_nearby_ship(Pl_objp, guardobjp);
9245
9246                 // if not attacking anything, go for asteroid close to guarded ship
9247                 if ( (aip->target_objnum == -1) && asteroid_count() ) {
9248                         ai_guard_find_nearby_asteroid(Pl_objp, guardobjp);
9249                 }
9250         }
9251 }
9252
9253 // gets closest point on extended axis of cylinder, r_vec, and radius of cylinder
9254 // returns z of axis_point in cyl_objp reference frame
9255 float get_cylinder_points(object *other_objp, object *cyl_objp, vector *axis_pt, vector *r_vec, float *radius)
9256 {
9257         Assert(other_objp->type == OBJ_SHIP);
9258         Assert(cyl_objp->type == OBJ_SHIP);
9259
9260         // get radius of cylinder
9261         polymodel *pm = model_get(Ships[cyl_objp->instance].modelnum);
9262         float tempx, tempy;
9263         tempx = max(-pm->mins.xyz.x, pm->maxs.xyz.x);
9264         tempy = max(-pm->mins.xyz.y, pm->maxs.xyz.y);
9265         *radius = max(tempx, tempy);
9266
9267         // get vec from cylinder to other_obj
9268         vector r_sph;
9269         vm_vec_sub(&r_sph, &other_objp->pos, &cyl_objp->pos);
9270
9271         // get point on axis and on cylinder
9272         // extended_cylinder_z is along extended cylinder
9273         // cylinder_z is capped within cylinder
9274         float extended_cylinder_z = vm_vec_dotprod(&r_sph, &cyl_objp->orient.v.fvec);
9275
9276         // get pt on axis of extended cylinder
9277         vm_vec_scale_add(axis_pt, &cyl_objp->pos, &cyl_objp->orient.v.fvec, extended_cylinder_z);
9278
9279         // get r_vec (pos - axis_pt) normalized
9280         vm_vec_normalized_dir(r_vec, &other_objp->pos, axis_pt);
9281
9282         return extended_cylinder_z;
9283 }
9284
9285 // handler for guard behavior when guarding BIG ships
9286 //      When someone has attacked guarded ship, then attack that ship.
9287 // To attack another ship, switch out of guard mode into chase mode.
9288 void ai_big_guard()
9289 {
9290         
9291         ship                    *shipp = &Ships[Pl_objp->instance];
9292         ai_info         *aip = &Ai_info[shipp->ai_index];
9293         object          *guard_objp;
9294
9295         // sanity checks already done in ai_guard()
9296         guard_objp = &Objects[aip->guard_objnum];
9297
9298         switch (aip->submode) {
9299         case AIS_GUARD_STATIC:
9300         case AIS_GUARD_PATROL:
9301                 {
9302                 vector axis_pt, r_vec, theta_vec;
9303                 float radius, extended_z;
9304
9305                 // get random [0 to 1] based on OBJNUM
9306                 float objval = static_randf(Pl_objp-Objects);
9307
9308                 // get position relative to cylinder of guard_objp              
9309                 extended_z = get_cylinder_points(Pl_objp, guard_objp, &axis_pt, &r_vec, &radius);
9310                 vm_vec_crossprod(&theta_vec, &guard_objp->orient.v.fvec, &r_vec);
9311
9312                 // half ships circle each way
9313                 if (objval > 0.5f) {
9314                         vm_vec_negate(&theta_vec);
9315                 }
9316
9317                 float min_guard_dist = radius + Pl_objp->radius + 50.0f;
9318                 float desired_guard_dist = min_guard_dist + 0.5f * ((1.0f + objval) * MAX_GUARD_DIST);
9319                 float max_guard_dist =     min_guard_dist + 1.0f * ((1.0f + objval) * MAX_GUARD_DIST);
9320
9321                 // get z extents
9322                 float min_z, max_z, length;
9323                 polymodel *pm = model_get(Ships[guard_objp->instance].modelnum);
9324                 min_z = pm->mins.xyz.z;
9325                 max_z = pm->maxs.xyz.z;
9326                 length = max_z - min_z;
9327
9328                 // get desired z
9329                 // how often to choose new desired_z
9330                 // 1*(64) sec < 2000, 2*(64) < 2-4000 3*(64) > 4-8000, etc (Missiontime >> 22 is 64 sec intervals)
9331                 int time_choose = int(floor(log(length * 0.001) / log(2)));
9332                 float desired_z = min_z + length * static_randf( Pl_objp-Objects ^ (Missiontime >> (22 + time_choose)) );
9333
9334                 // get r from guard_ship
9335                 float cur_guard_rad = vm_vec_dist(&Pl_objp->pos, &axis_pt);
9336
9337                 // is ship within extents of cylinder of ship it is guarding
9338                 int inside = (extended_z > min_z) && (extended_z < min_z + length);
9339
9340                 vector goal_pt;
9341                 // maybe go into orbit mode
9342                 if (cur_guard_rad < max_guard_dist) {
9343                         if ( cur_guard_rad > min_guard_dist ) {
9344                                 if (inside) {
9345                                         // orbit
9346                                         vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, desired_guard_dist);
9347                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9348                                 } else {
9349                                         // move to where I can orbit
9350                                         if (extended_z < min_z) {
9351                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, min_z);
9352                                         } else {
9353                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, max_z);
9354                                         }
9355                                         vm_vec_scale_add2(&goal_pt, &r_vec, desired_guard_dist);
9356                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9357                                 }
9358                         } else {
9359                                 // too close for orbit mode
9360                                 if (inside) {
9361                                         // inside (fly straight out and return circle)
9362                                         vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, max_guard_dist);
9363                                 } else {
9364                                         // outside (fly to edge and circle)
9365                                         if (extended_z < min_z) {
9366                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, min_z);
9367                                         } else {
9368                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, max_z);
9369                                         }
9370                                         vm_vec_scale_add2(&goal_pt, &r_vec, max_guard_dist);
9371                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9372                                 }
9373                         }
9374
9375                         if (Pl_objp->phys_info.fspeed > 0) {
9376                                 // modify goal_pt to take account moving guard objp
9377                                 float dist = vm_vec_dist_quick(&Pl_objp->pos, &goal_pt);
9378                                 float time = dist / Pl_objp->phys_info.fspeed;
9379                                 vm_vec_scale_add2(&goal_pt, &guard_objp->phys_info.vel, time);
9380
9381                                 // now modify to move to desired z (at a max of 20 m/s)
9382                                 float delta_z = desired_z - extended_z;
9383                                 float v_z = delta_z * 0.2f;
9384                                 if (v_z < -20) {
9385                                         v_z = -20.0f;
9386                                 } else if (v_z > 20) {
9387                                         v_z = 20.0f;
9388                                 }
9389
9390                                 vm_vec_scale_add2(&goal_pt, &guard_objp->orient.v.fvec, v_z*time);
9391                         }
9392
9393                 } else {
9394                         // cast vector to center of guard_ship adjusted by desired_z
9395                         float delta_z = desired_z - extended_z;
9396                         vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, delta_z);
9397                 }
9398
9399                 // try not to bump into things along the way
9400                 if ( (cur_guard_rad > max_guard_dist) || (extended_z < min_z) || (extended_z > max_z) ) {
9401                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9402                                 return;
9403                         }
9404
9405                         if (avoid_player(Pl_objp, &goal_pt)) {
9406                                 return;
9407                         }
9408                 } else {
9409                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9410                                 return;
9411                         }
9412                 }
9413
9414                 // got the point, now let's go there
9415                 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);
9416 //              aip->goal_point = goal_pt;
9417                 accelerate_ship(aip, 1.0f);
9418
9419                 //      Periodically, scan for a nearby ship to attack.
9420                 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9421                         ai_guard_find_nearby_object();
9422                 }
9423                 }
9424                 break;
9425
9426         case AIS_GUARD_ATTACK:
9427                 //      The guarded ship has been attacked.  Do something useful!
9428                 ai_chase();
9429                 break;
9430
9431         default:
9432                 //Int3();       //      Illegal submode for Guard mode.
9433                 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9434                 aip->submode = AIS_GUARD_PATROL;
9435                 break;
9436         }
9437 }
9438
9439 //      Main handler for guard behavior.
9440 //      When someone has attacked guarded ship, then attack that ship.
9441 // To attack another ship, switch out of guard mode into chase mode.
9442 void ai_guard()
9443 {
9444         ship                    *shipp = &Ships[Pl_objp->instance];
9445         ai_info         *aip = &Ai_info[shipp->ai_index];
9446         object          *guard_objp;    
9447         ship                    *gshipp;
9448         float                   dist_to_guardobj, dot_to_guardobj;
9449         vector          vec_to_guardobj;
9450
9451         /*      //      Debug code, find an object to guard.
9452         int finding_guard_objnum = 0;   //      Debug code, to see if body of "if" below gets executed. 
9453         if (aip->guard_objnum == -1) {
9454                 finding_guard_objnum = 1;
9455                 debug_find_guard_object();
9456                 if (aip->guard_objnum == -1)
9457                         return;
9458         }
9459 */
9460         if (aip->guard_objnum == -1) {
9461                 aip->mode = AIM_NONE;
9462                 return;
9463         }
9464
9465         Assert(aip->guard_objnum != -1);
9466
9467         guard_objp = &Objects[aip->guard_objnum];
9468
9469         if (guard_objp == Pl_objp) {
9470                 Int3();         //      This seems illegal.  Why is a ship guarding itself?
9471                 aip->guard_objnum = -1;
9472                 return;
9473         }
9474
9475         // check that I have someone to guard
9476         if (guard_objp->instance == -1) {
9477                 return;
9478         }
9479
9480         //      Not sure whether this should be impossible, or a reasonable cleanup condition.
9481         //      For now (3/31/97), it's getting trapped by an Assert, so clean it up.
9482         if (guard_objp->type != OBJ_SHIP) {
9483                 aip->guard_objnum = -1;
9484                 return;
9485         }
9486
9487         // handler for gurad object with BIG radius
9488         if (guard_objp->radius > BIG_GUARD_RADIUS) {
9489                 ai_big_guard();
9490                 return;
9491         }
9492
9493         gshipp = &Ships[guard_objp->instance];
9494
9495         float                   objval;
9496         vector          goal_point;
9497         vector          rel_vec;
9498         float                   dist_to_goal_point, dot_to_goal_point, accel_scale;
9499         vector          v2g, rvec;
9500
9501         // get random [0 to 1] based on OBJNUM
9502         objval = static_randf(Pl_objp-Objects);
9503
9504         switch (aip->submode) {
9505         case AIS_GUARD_STATIC:
9506         case AIS_GUARD_PATROL:
9507                 //      Stay near ship
9508                 dist_to_guardobj = vm_vec_normalized_dir(&vec_to_guardobj, &guard_objp->pos, &Pl_objp->pos);
9509                 dot_to_guardobj = vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_to_guardobj);
9510
9511                 rel_vec = aip->guard_vec;
9512                 vm_vec_add(&goal_point, &guard_objp->pos, &rel_vec);
9513
9514                 vm_vec_normalized_dir(&v2g, &goal_point, &Pl_objp->pos);
9515                 dist_to_goal_point = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
9516                 dot_to_goal_point = vm_vec_dot(&v2g, &Pl_objp->orient.v.fvec);
9517                 accel_scale = (1.0f + dot_to_goal_point)/2.0f;
9518
9519                 //      If far away, get closer
9520                 if (dist_to_goal_point > MAX_GUARD_DIST + 1.5 * (Pl_objp->radius + guard_objp->radius)) {
9521                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 5.0f)) {
9522                                 return;
9523                         }
9524
9525                         if (avoid_player(Pl_objp, &goal_point)) {
9526                                 return;
9527                         }
9528
9529                         // quite far away, so try to go straight to 
9530                         compute_desired_rvec(&rvec, &goal_point, &Pl_objp->pos);
9531                         ai_turn_towards_vector(&goal_point, Pl_objp, flFrametime, Ship_info[shipp->ship_info_index].srotation_time, NULL, NULL, 0.0f, 0, &rvec);
9532
9533                         accelerate_ship(aip, accel_scale * (0.25f + dist_to_goal_point/700.0f));
9534                 } else {
9535                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 2.0f)) {
9536                                 return;
9537                         }
9538
9539                         // get max of guard_objp (1) normal speed (2) dock speed
9540                         float speed = guard_objp->phys_info.speed;
9541
9542                         if (guard_objp->type == OBJ_SHIP) {
9543                                 ai_info *guard_aip = &Ai_info[Ships[guard_objp->instance].ai_index];
9544
9545                                 if (guard_aip->dock_objnum != -1) {
9546                                         speed = max(speed, Objects[guard_aip->dock_objnum].phys_info.speed);
9547                                 }
9548                         }
9549                         
9550                         //      Deal with guarding a small object.
9551                         //      If going to guard_vec might cause a collision with guarded object, pick a new guard point.
9552                         if (vm_vec_dot(&v2g, &vec_to_guardobj) > 0.8f) {
9553                                 if (dist_to_guardobj < dist_to_goal_point) {
9554                                         ai_set_guard_vec(Pl_objp, guard_objp);  //      OK to return here.
9555                                         return;
9556                                 }
9557                         } 
9558
9559                         if (speed > 10.0f) {
9560                                 //      If goal ship is moving more than a tiny bit, don't orbit it, get near it.
9561                                 if (vm_vec_dist_quick(&goal_point, &Pl_objp->pos) > 40.0f) {
9562                                         if (vm_vec_dot(&Pl_objp->orient.v.fvec, &v2g) < 0.0f) {
9563                                                 //      Just slow down, don't turn.
9564                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point/10.0f);
9565                                         } else {
9566                                                 //      Goal point is in front.
9567
9568                                                 //      If close to goal point, don't change direction, just change speed.
9569                                                 if (dist_to_goal_point > Pl_objp->radius + 10.0f) {
9570                                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9571                                                 }
9572                                                 
9573                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + (dist_to_goal_point-40.0f)/20.0f);
9574                                         }
9575                                 } else {
9576                                         if (dot_to_goal_point > 0.8f) {
9577                                                 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9578                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + dist_to_goal_point*0.1f);
9579                                         } else {
9580                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point*0.1f - 1.0f);
9581                                         }
9582                                 }
9583                         // consider guard object STILL
9584                         } else if (guard_objp->radius < 50.0f) {
9585                                 if (dist_to_goal_point > 15.0f) {
9586                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9587                                         set_accel_for_target_speed(Pl_objp, (dist_to_goal_point-10.0f)/2.0f);
9588                                 } else if (Pl_objp->phys_info.speed < 1.0f) {
9589                                         turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9590                                 }
9591                                 //      It's a big ship
9592                         } else if (dist_to_guardobj > MAX_GUARD_DIST + Pl_objp->radius + guard_objp->radius) {
9593                                 //      Orbiting ship, too far away
9594                                 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.0f + objval/2) * guard_objp->radius);
9595                                 accelerate_ship(aip, (1.0f + dot)/2.0f);
9596                         } else if (dist_to_guardobj < Pl_objp->radius + guard_objp->radius) {
9597                                 //      Orbiting ship, got too close
9598                                 turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9599                                 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9600                                         change_acceleration(aip, 0.25f);
9601                                 else
9602                                         accelerate_ship(aip, 0.5f + objval/4.0f);
9603                         } else {
9604                                 //      Orbiting ship, about the right distance away.
9605                                 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.5f + objval/2.0f)*guard_objp->radius);
9606                                 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9607                                         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));
9608                                 else
9609                                         accelerate_ship(aip, 0.5f * (1.0f + dot) * (0.3f + objval/3.0f));
9610                         }
9611                 }
9612
9613                 //      Periodically, scan for a nearby ship to attack.
9614                 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9615                         ai_guard_find_nearby_object();
9616                 }
9617                 break;
9618
9619         case AIS_GUARD_ATTACK:
9620                 //      The guarded ship has been attacked.  Do something useful!
9621                 ai_chase();
9622
9623                 break;
9624         default:
9625                 //Int3();       //      Illegal submode for Guard mode.
9626                 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9627                 aip->submode = AIS_GUARD_PATROL;
9628                 break;
9629         }
9630
9631 }
9632
9633 // Return the object of the ship that the given object is docked
9634 // with.  Currently, we know a ship is docked when his ai_mode is AIM_DOCK,
9635 // and his submode is AIS_DOCK_3.  I suppose that this is likely to change though.
9636 // Also, the objnum that was is passed in may not be the object that actually
9637 // performed the docking maneuver.  This code will account for that case.
9638 object *ai_find_docked_object( object *docker )
9639 {
9640         ai_info *aip;
9641
9642         // we are trying to find the dockee of docker.  (Note that that these terms
9643         // are totally relative to what is passed in as a parameter.)
9644
9645         // first thing to attempt is to check and see if this object is docked with something.
9646         Assert( docker->type == OBJ_SHIP );             // this had probably better be a ship!!!
9647         aip = &Ai_info[Ships[docker->instance].ai_index];
9648         if ( !(aip->ai_flags & AIF_DOCKED) )            // flag not set if not docked with anything
9649                 return NULL;
9650
9651         if ( aip->dock_objnum == -1 ) {
9652                 Int3();                                                                                 // mwa says this is wrong wrong wrong
9653                 ai_do_objects_undocked_stuff( docker, NULL );
9654                 return NULL;
9655         }
9656
9657         return &Objects[aip->dock_objnum];
9658
9659 }
9660
9661
9662 // define for the points subtracted from score for a rearm started on a player.
9663 #define REPAIR_PENALTY          50
9664
9665
9666 // function to clean up ai flags, variables, and other interesting information
9667 // for a ship that was getting repaired.  The how parameter is useful for multiplayer
9668 // only in that it tells us why the repaired ship is being cleaned up.
9669 void ai_do_objects_repairing_stuff( object *repaired_objp, object *repair_objp, int how )
9670 {
9671         ai_info *aip, *repair_aip;
9672         int             stamp = -1;
9673
9674         Assert( repaired_objp->type == OBJ_SHIP);
9675         aip = &Ai_info[Ships[repaired_objp->instance].ai_index];
9676
9677         // multiplayer
9678         int p_index;
9679         p_index = -1;
9680         if(Game_mode & GM_MULTIPLAYER){
9681                 p_index = multi_find_player_by_object(repaired_objp);           
9682         }               
9683         else {          
9684                 if(repaired_objp == Player_obj){
9685                         p_index = Player_num;
9686                 }
9687         }
9688
9689         switch( how ) {
9690         case REPAIR_INFO_BEGIN:
9691                 aip->ai_flags |= AIF_BEING_REPAIRED;
9692                 aip->ai_flags &= ~AIF_AWAITING_REPAIR;
9693                 stamp = timestamp(-1);
9694
9695                 // if this is a player ship, then subtract the repair penalty from this player's score
9696                 if ( repaired_objp->flags & OF_PLAYER_SHIP ) {
9697                         if ( !(Game_mode & GM_MULTIPLAYER) ) {
9698                                 Player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());                    // subtract the penalty
9699                         } else {
9700                                 /*
9701                                 int pnum;
9702
9703                                 // multiplayer game -- find the player, then subtract the score
9704                                 pnum = multi_find_player_by_object( repaired_objp );
9705                                 if ( pnum != -1 ) {
9706                                         Net_players[pnum].player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());
9707
9708                                         // squad war
9709                                         multi_team_maybe_add_score(-(int)(REPAIR_PENALTY * scoring_get_scale_factor()), Net_players[pnum].p_info.team);
9710                                 } else {
9711                                         nprintf(("Network", "Couldn't find player for ship %s for repair penalty\n", Ships[repaired_objp->instance].ship_name));
9712                                 }
9713                                 */
9714                         }
9715                 }
9716                 break;
9717
9718         case REPAIR_INFO_BROKEN:
9719                 aip->ai_flags &= ~AIF_BEING_REPAIRED;
9720                 aip->ai_flags |= AIF_AWAITING_REPAIR;
9721                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9722                 break;
9723
9724         case REPAIR_INFO_END:
9725                 // when only awaiting repair, and the repair is ended, then set dock_objnum to -1.
9726                 if ( aip->ai_flags & AIF_AWAITING_REPAIR ){
9727                         aip->dock_objnum = -1;
9728                 }
9729                 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9730                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9731                 break;
9732
9733         case REPAIR_INFO_QUEUE:
9734                 aip->ai_flags |= AIF_AWAITING_REPAIR;
9735                 if ( aip == Player_ai ){
9736                         hud_support_view_start();
9737                 }
9738                 stamp = timestamp(-1);
9739                 break;
9740
9741         case REPAIR_INFO_ABORT:
9742         case REPAIR_INFO_KILLED:
9743                 // 5/4/98 -- MWA -- Need to set dock objnum to -1 to let code know this guy who was getting
9744                 // repaired (or queued for repair), isn't really going to be docked with anyone anymore.
9745                 aip->dock_objnum = -1;
9746                 aip->ai_flags &= ~AIF_DOCKED;
9747                 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9748                 if (repair_objp != NULL) {
9749                         repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9750                         repair_aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9751                 }               
9752
9753                 if ( p_index >= 0 ) {
9754                         hud_support_view_abort();
9755
9756                         // send appropriate message to player here
9757                         if ( how == REPAIR_INFO_KILLED ){
9758                                 message_send_builtin_to_player( MESSAGE_SUPPORT_KILLED, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9759                         } else {
9760                                 if ( repair_objp ){
9761                                         message_send_builtin_to_player( MESSAGE_REPAIR_ABORTED, &Ships[repair_objp->instance], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9762                                 }
9763                         }
9764                 }
9765
9766                 // add log entry if this is a player
9767                 if ( repaired_objp->flags & OF_PLAYER_SHIP ){
9768                         mission_log_add_entry(LOG_PLAYER_REARM_ABORT, Ships[repaired_objp->instance].ship_name, NULL);
9769                 }
9770
9771                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9772                 break;
9773
9774         case REPAIR_INFO_COMPLETE:
9775                 // clear the being repaired flag -- and 
9776                 if ( p_index >= 0 ) {
9777                         Assert( repair_objp );
9778                         
9779                         hud_support_view_stop();                        
9780
9781                         message_send_builtin_to_player(MESSAGE_REPAIR_DONE, &Ships[repair_objp->instance], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, p_index, -1);
9782                 }
9783                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9784                 break;
9785
9786         case REPAIR_INFO_ONWAY:
9787                 // need to set the dock_signature so that clients in multiplayer games rearm correctly
9788                 Assert( repair_objp );
9789                 aip->dock_signature = repair_objp->signature; 
9790                 aip->dock_objnum = OBJ_INDEX(repair_objp);
9791                 stamp = timestamp(-1);
9792                 break;
9793
9794         default:
9795                 Int3();                 // bogus type of repair info
9796         }
9797
9798         if (repair_objp){
9799                 Ai_info[Ships[repair_objp->instance].ai_index].warp_out_timestamp = stamp;
9800         }
9801
9802         // repair_objp might be NULL is we are cleaning up this mode because of the support ship
9803         // getting killed.
9804         if ( repair_objp ) {
9805                 aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9806                 switch ( how ) {
9807                 case REPAIR_INFO_ONWAY:
9808                         Assert( repaired_objp != NULL );
9809                         aip->goal_objnum = OBJ_INDEX(repaired_objp);
9810                         aip->ai_flags |= AIF_REPAIRING;
9811                         break;
9812
9813                 case REPAIR_INFO_BROKEN:
9814                         break;
9815
9816                 case REPAIR_INFO_END:
9817                 case REPAIR_INFO_ABORT:
9818                 case REPAIR_INFO_KILLED:
9819                         if ( how == REPAIR_INFO_ABORT )
9820                                 aip->goal_objnum = -1;
9821
9822                         aip->ai_flags &= ~AIF_REPAIRING;
9823                         break;
9824                         
9825                 case REPAIR_INFO_QUEUE:
9826                         ai_add_rearm_goal( repaired_objp, repair_objp );
9827                         break;
9828
9829                 case REPAIR_INFO_BEGIN:
9830                 case REPAIR_INFO_COMPLETE:
9831                         break;
9832
9833                 default:
9834                         Int3();         // bogus type of repair info
9835                 }
9836         }
9837
9838         multi_maybe_send_repair_info( repaired_objp, repair_objp, how );
9839 }
9840
9841 //      Cleanup AI stuff for when a ship was supposed to dock with another, but the ship
9842 //      it was supposed to dock with is no longer valid.
9843 void ai_cleanup_dock_mode(ai_info *aip, ship *shipp)
9844 {
9845         object *objp;
9846
9847         objp = &Objects[shipp->objnum];
9848         aip->mode = AIM_NONE;
9849
9850         if (aip->ai_flags & AIF_REPAIRING) {
9851                 Assert( aip->goal_objnum != -1 );
9852                 ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], &Objects[shipp->objnum], REPAIR_INFO_KILLED );
9853         } else if ( aip->ai_flags & AIF_BEING_REPAIRED ) {
9854                 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9855                 Assert( aip->dock_objnum != -1 );
9856                 ai_do_objects_repairing_stuff( &Objects[shipp->objnum], &Objects[aip->dock_objnum], REPAIR_INFO_KILLED );
9857         } else if ( aip->ai_flags & AIF_AWAITING_REPAIR ) {
9858                 // need to find the support ship that has me as a goal_objnum
9859                 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9860                 // MWA -- 3/38/98  Check to see if this guy is queued for a support ship, or there is already
9861                 // one in the mission
9862                 if ( mission_is_repair_scheduled(objp) ) {
9863                         mission_remove_scheduled_repair( objp );                        // this function will notify multiplayer clients.
9864                 } else {
9865                         if ( aip->dock_objnum != -1 )
9866                                 ai_do_objects_repairing_stuff( objp, &Objects[aip->dock_objnum], REPAIR_INFO_ABORT );
9867                         else
9868                                 ai_do_objects_repairing_stuff( objp, NULL, REPAIR_INFO_ABORT );
9869                 }
9870         }
9871
9872         if ( aip->ai_flags & AIF_DOCKED ) {
9873                 ai_info *other_aip;
9874
9875                 Assert( aip->dock_objnum != -1 );
9876
9877                 // if docked, and the dock_objnum is not undocking, force them to near last stage
9878                 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
9879                 if ( (other_aip->mode == AIM_DOCK) && (other_aip->submode < AIS_UNDOCK_3) )
9880                         other_aip->submode = AIS_UNDOCK_3;
9881                 ai_do_objects_undocked_stuff( objp, &Objects[aip->dock_objnum] );
9882         }
9883 }
9884
9885 /*
9886 //      Make dockee_objp shake a bit due to docking.
9887 void ai_dock_shake(object *docker_objp, object *dockee_objp)
9888 {
9889         vector  tangles;
9890         matrix  rotmat, tmp;
9891         float           scale;
9892         angles  *ap;
9893
9894         scale = 0.25f;          //      Compute this based on mass and speed at time of docking.
9895
9896         vm_vec_rand_vec_quick(&tangles);
9897         vm_vec_scale(&tangles, scale);
9898
9899         ap = (angles *) &tangles;
9900
9901         vm_angles_2_matrix(&rotmat, ap);
9902         vm_matrix_x_matrix( &tmp, &dockee_objp->orient, &rotmat );
9903         dockee_objp->orient = tmp;
9904
9905         vm_orthogonalize_matrix(&dockee_objp->orient);
9906
9907         dock_orient_and_approach(docker_objp, dockee_objp, DOA_DOCK_STAY);
9908
9909 }
9910 */
9911
9912 //      Make Pl_objp point at aip->goal_point.
9913 void ai_still()
9914 {
9915         ship    *shipp;
9916         ai_info *aip;
9917
9918         Assert(Pl_objp->type == OBJ_SHIP);
9919         Assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_OBJECTS));
9920
9921         shipp = &Ships[Pl_objp->instance];
9922         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
9923
9924         aip = &Ai_info[shipp->ai_index];
9925
9926         turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
9927 }
9928
9929 //      Make *Pl_objp stay near another ship.
9930 void ai_stay_near()
9931 {
9932         ai_info *aip;
9933         int             goal_objnum;
9934
9935         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
9936
9937         goal_objnum = aip->goal_objnum;
9938
9939         if ((goal_objnum < 0) || (Objects[goal_objnum].type != OBJ_SHIP) || (Objects[goal_objnum].signature != aip->goal_signature)) {
9940                 aip->mode = AIM_NONE;
9941         } else {
9942                 float           dist, max_dist, scale;
9943                 vector  rand_vec, goal_pos, vec_to_goal;
9944                 object  *goal_objp;
9945
9946                 goal_objp = &Objects[goal_objnum];
9947
9948                 //      Make not all ships pursue same point.
9949                 static_randvec(Pl_objp-Objects, &rand_vec);
9950
9951                 //      Make sure point is in front hemisphere (relative to Pl_objp's position.
9952                 vm_vec_sub(&vec_to_goal, &goal_objp->pos, &Pl_objp->pos);
9953                 if (vm_vec_dot(&rand_vec, &vec_to_goal) > 1.0f) {
9954                         vm_vec_negate(&rand_vec);
9955                 }
9956
9957                 //      Scale the random vector by an amount proportional to the distance from Pl_objp to the true goal.
9958                 dist = vm_vec_dist_quick(&goal_objp->pos, &Pl_objp->pos);
9959                 max_dist = aip->stay_near_distance;
9960                 scale = dist - max_dist/2;
9961                 if (scale < 0.0f)
9962                         scale = 0.0f;
9963
9964                 vm_vec_scale_add(&goal_pos, &goal_objp->pos, &rand_vec, scale);
9965
9966                 if (max_dist < Pl_objp->radius + goal_objp->radius + 25.0f)
9967                         max_dist = Pl_objp->radius + goal_objp->radius + 25.0f;
9968
9969                 if (dist > max_dist) {
9970                         turn_towards_point(Pl_objp, &goal_pos, NULL, 0.0f);
9971                         accelerate_ship(aip, dist / max_dist - 0.8f);
9972                 }
9973         
9974         }
9975
9976 }
9977
9978 //      Warn player if dock path is obstructed.
9979 int maybe_dock_obstructed(object *cur_objp, object *goal_objp, int big_only_flag)
9980 {
9981         vector  *goalpos, *curpos;
9982         float           radius;
9983         ai_info *aip;
9984         int             collide_objnum;
9985
9986         aip = &Ai_info[Ships[cur_objp->instance].ai_index];
9987
9988         Ai_info[Ships[goal_objp->instance].ai_index].ai_flags &= ~AIF_REPAIR_OBSTRUCTED;
9989
9990         if (goal_objp != Player_obj)
9991                 return -1;
9992
9993         curpos = &cur_objp->pos;
9994         radius = cur_objp->radius;
9995         goalpos = &Path_points[aip->path_cur].pos;
9996         collide_objnum = pp_collide_any(curpos, goalpos, radius, cur_objp, goal_objp, big_only_flag);
9997
9998         if (collide_objnum != -1)
9999                 Ai_info[Ships[goal_objp->instance].ai_index].ai_flags |= AIF_REPAIR_OBSTRUCTED;
10000
10001         return collide_objnum;
10002 }
10003
10004
10005 int Dock_path_warning_given = 0;
10006
10007 //      Docking behavior.
10008 //      Approach a ship, follow path to docking platform, approach platform, after awhile,
10009 //      undock.
10010 void ai_dock()
10011 {
10012         ship                    *shipp = &Ships[Pl_objp->instance];
10013         ai_info         *aip = &Ai_info[shipp->ai_index];
10014         object          *goal_objp;
10015         ship_info       *sip = &Ship_info[shipp->ship_info_index];
10016
10017         //      Make sure object we're supposed to dock with still exists.
10018         if ((aip->goal_objnum == -1) || (Objects[aip->goal_objnum].signature != aip->goal_signature)) {
10019                 ai_cleanup_dock_mode(aip, shipp);
10020                 return;
10021         }
10022
10023         goal_objp = &Objects[aip->goal_objnum];
10024
10025         //      For docking submodes (ie, not undocking), follow path.  Once at second last
10026         //      point on path (point just before point on dock platform), orient into position.
10027         // For undocking, first mode pushes docked ship straight back from docking point
10028         // second mode turns ship and moves to point on docking radius
10029         switch (aip->submode) {
10030
10031                 //      This mode means to find the path to the docking point.
10032         case AIS_DOCK_0:
10033                 //aip->path_start = -1;
10034                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10035                 ai_path();
10036                 if (!Dock_path_warning_given && (aip->path_length < 4)) {
10037                         Warning( LOCATION, "Ship '%s' has only %i points on dock path.  Docking will look strange.  Contact Adam.", shipp->ship_name, aip->path_length );
10038                         Dock_path_warning_given = 1;            //      This is on a mission-wide basis, but it's just a hack for now...
10039                 }
10040
10041                 aip->submode = AIS_DOCK_1;
10042                 aip->path_start = -1;
10043                 aip->submode_start_time = Missiontime;
10044                 break;
10045
10046                 //      This mode means to follow the path until just before the end.
10047         case AIS_DOCK_1: {
10048                 float   dist;
10049                 int     r;
10050
10051                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp, 1)) != -1) {
10052                         int     r1;
10053                         if ((r1 = maybe_avoid_big_ship(Pl_objp, goal_objp, aip, &goal_objp->pos, 7.0f)) != 0) {
10054                                 nprintf(("AI", "Support ship %s avoiding large ship %s\n", Ships[Pl_objp->instance].ship_name, Ships[Objects[r1].instance].ship_name));
10055                                 break;
10056                         } /*else {
10057                                 nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10058                                 accelerate_ship(aip, 0.0f);
10059                                 aip->submode = AIS_DOCK_0;
10060                         } */
10061                 } //else {
10062                 {
10063                         dist = ai_path();
10064                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10065                         //nprintf(("AI", "Dock 1: Frame: %i, goal point = %i, dist = %7.3f\n", Framecount, aip->path_cur-aip->path_start, dist));
10066
10067                         if (aip->path_cur-aip->path_start >= aip->path_length-1) {              //      If got this far, advance no matter what.
10068                                 aip->submode = AIS_DOCK_2;
10069                                 aip->submode_start_time = Missiontime;
10070                                 aip->path_cur--;
10071                                 Assert(aip->path_cur-aip->path_start >= 0);
10072                         } else if (aip->path_cur-aip->path_start >= aip->path_length-2) {
10073                                 if (Pl_objp->phys_info.speed > goal_objp->phys_info.speed + 1.5f) {
10074                                         set_accel_for_target_speed(Pl_objp, goal_objp->phys_info.speed);
10075                                 } else {
10076                                         aip->submode = AIS_DOCK_2;
10077                                         aip->submode_start_time = Missiontime;
10078                                 }
10079                         }
10080                 }
10081                 break;
10082                                           }
10083         //      This mode means to drag oneself right to the second last point on the path.
10084         //      Path code allows it to overshoot.
10085         case AIS_DOCK_2: {
10086                 float           dist;
10087                 int     r;
10088
10089                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10090                         nprintf(("AI", "Dock 2: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10091                         accelerate_ship(aip, 0.0f);
10092                         aip->submode = AIS_DOCK_1;
10093                 } else {
10094                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10095                         dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_APPROACH);
10096                         Assert(dist != UNINITIALIZED_VALUE);
10097
10098                         if (dist == DOCK_BACKUP_RETURN_VAL) {
10099                                 int path_num;
10100                                 aip->submode = AIS_DOCK_1;
10101                                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
10102                                 Assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
10103                                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
10104                                 break;
10105                         }
10106
10107                         //nprintf(("AI", "Dock 2: dist = %7.3f\n", vm_vec_dist_quick(&Pl_objp->pos, &goal_point)));
10108                         float   tolerance;
10109                         if (Objects[aip->goal_objnum].flags & OF_PLAYER_SHIP)
10110                                 tolerance = 6*flFrametime + 1.0f;
10111                         else
10112                                 tolerance = 4*flFrametime + 0.5f;
10113
10114                         if ( dist < tolerance) {
10115                                 aip->submode = AIS_DOCK_3;
10116                                 aip->submode_start_time = Missiontime;
10117                                 aip->path_cur++;
10118                         }
10119                 }
10120                 break;
10121                                                   }
10122
10123         case AIS_DOCK_3:
10124         case AIS_DOCK_3A:
10125                 {
10126                 Assert(aip->goal_objnum != -1);
10127                 int     r;
10128
10129                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10130                         nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10131                         accelerate_ship(aip, 0.0f);
10132                         aip->submode = AIS_DOCK_2;
10133                 } else {
10134
10135                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10136                         float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10137                         Assert(dist != UNINITIALIZED_VALUE);
10138
10139                         if (dist == DOCK_BACKUP_RETURN_VAL) {
10140                                 aip->submode = AIS_DOCK_2;
10141                                 break;
10142                         }
10143
10144                         //nprintf(("AI", "Dock 3: dist = %7.3f\n", dist));
10145
10146                         if (dist < 2*flFrametime * (1.0f + fl_sqrt(goal_objp->phys_info.speed))) {
10147                                 // - Removed by MK on 11/7/97, causes errors for ships docked at mission start: maybe_recreate_path(Pl_objp, aip, 1);
10148                                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10149                                 Assert(dist != UNINITIALIZED_VALUE);
10150
10151                                 physics_ship_init(Pl_objp);
10152
10153                                 ai_do_objects_docked_stuff( Pl_objp, goal_objp );
10154
10155                                 if (aip->submode == AIS_DOCK_3) {
10156                                         snd_play_3d( &Snds[SND_DOCK_ATTACH], &Pl_objp->pos, &View_position );
10157                                         hud_maybe_flash_docking_text(Pl_objp);
10158                                         // ai_dock_shake(Pl_objp, goal_objp);
10159
10160                                         if ((Pl_objp == Player_obj) || (goal_objp == Player_obj))
10161                                                 joy_ff_docked();  // shake player's joystick a little
10162                                 }
10163
10164                                 //      If this ship is repairing another ship...
10165                                 if (aip->ai_flags & AIF_REPAIRING) {
10166                                         aip->submode = AIS_DOCK_4;                      //      Special rearming only dock mode.
10167                                         aip->submode_start_time = Missiontime;
10168                                 } else {
10169                                         aip->submode = AIS_DOCK_4A;
10170                                         aip->submode_start_time = Missiontime;
10171                                 }
10172                         }
10173                 }
10174                 break;
10175                 }
10176
10177                 //      Yes, we just sit here.  We wait for further orders.  No, it's not a bug.
10178         case AIS_DOCK_4A:
10179                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10180                 //nprintf(("AI", "."));
10181                 if (aip->active_goal >= 0) {
10182                         mission_log_add_entry(LOG_SHIP_DOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10183
10184                         if (aip->goals[aip->active_goal].ai_mode == AI_GOAL_DOCK) {
10185                                 ai_mission_goal_complete( aip );                                        // Note, this calls ai_set_default_behavior().
10186                         } 
10187                 } else {        //      Can happen for initially docked ships.
10188                         ai_do_default_behavior( &Objects[Ships[aip->shipnum].objnum] );         // do the default behavior
10189                 }
10190                 
10191                 break;
10192
10193         case AIS_DOCK_4: {
10194                 //      This mode is only for rearming/repairing.
10195                 //      The ship that is performing the rearm enters this mode after it docks.
10196                 Assert((aip->goal_objnum >= -1) && (aip->goal_objnum < MAX_OBJECTS));
10197
10198                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10199                 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10200                 Assert(dist != UNINITIALIZED_VALUE);
10201
10202                 object  *goal_objp = &Objects[aip->goal_objnum];
10203                 Assert(goal_objp->type == OBJ_SHIP);
10204                 ship                    *goal_shipp = &Ships[goal_objp->instance];              
10205                 ai_info         *goal_aip = &Ai_info[goal_shipp->ai_index];
10206
10207                 //nprintf(("AI", "Dock 4: dist = %7.3f\n", dist));
10208
10209                 //      Make sure repair has not broken off.
10210                 if (dist > 5.0f) {      //      Oops, too far away!
10211                         if ( goal_aip->ai_flags & AIF_BEING_REPAIRED )
10212                                 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BROKEN);
10213
10214                         if (dist > Pl_objp->radius*2 + goal_objp->radius*2) {
10215                                 //      Got real far away from goal, so move back a couple modes and try again.
10216                                 aip->submode = AIS_DOCK_2;
10217                                 aip->submode_start_time = Missiontime;
10218                         }
10219                 } else {
10220                         if ( goal_aip->ai_flags & AIF_AWAITING_REPAIR )
10221                                 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BEGIN );
10222                 }
10223
10224                 break;
10225                                                   }
10226
10227         case AIS_UNDOCK_0: {
10228                 int path_num;
10229                 //      First stage of undocking.
10230
10231                 //nprintf(("AI", "Undock 0:\n"));
10232
10233                 aip->submode = AIS_UNDOCK_1;
10234                 aip->submode_start_time = Missiontime;
10235                 if (aip->dock_objnum == -1) {
10236                         aip->submode = AIS_UNDOCK_3;
10237                 } else {
10238
10239                         // set up the path points for the undocking procedure.  dock_path_index member should
10240                         // have gotten set in the docking code.
10241                         Assert( aip->dock_path_index != -1 );
10242                         path_num = ai_return_path_num_from_dockbay(goal_objp, aip->dock_path_index);
10243                         ai_find_path(Pl_objp, goal_objp-Objects, path_num, 0);
10244
10245                         // Play a ship docking detach sound
10246                         snd_play_3d( &Snds[SND_DOCK_DETACH], &Pl_objp->pos, &View_position );
10247                 }
10248                 break;
10249                                                          }
10250         case AIS_UNDOCK_1: {
10251                 //      Using thrusters, exit from dock station to nearest next dock path point.
10252                 float   dist;
10253                 
10254                 //nprintf(("AI", "Undock 1: time in this mode = %7.3f\n", f2fl(Missiontime - aip->submode_start_time)));
10255
10256                 if (Missiontime - aip->submode_start_time < REARM_BREAKOFF_DELAY) {
10257                         break;          //      Waiting for one second to elapse to let detach sound effect play out.
10258                 }
10259                 else {  // AL - added 05/16/97.  Hack to play depart sound.  Will probably take out.
10260                                         // Assumes that the submode_start_time is not used for AIS_UNDOCK_1 anymore
10261                         if ( aip->submode_start_time != 0 )
10262                                 snd_play_3d( &Snds[SND_DOCK_DEPART], &Pl_objp->pos, &View_position );
10263                         aip->submode_start_time = 0;
10264                 }
10265
10266                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_1);
10267                 Assert(dist != UNINITIALIZED_VALUE);
10268
10269                 float dist_to_dock_obj = vm_vec_dist_quick(&Pl_objp->pos, &Objects[aip->goal_objnum].pos);
10270
10271                 //      Move to within 0.1 units of second last point on path before orienting, or just plain far away from docked-to ship.
10272                 //      This allows undock to complete if first ship flies away.
10273                 if ((dist < 2*flFrametime) || (dist_to_dock_obj > 2*Pl_objp->radius)) {
10274                         aip->submode = AIS_UNDOCK_2;
10275                         aip->submode_start_time = Missiontime;
10276                 }
10277                 break;
10278                                                          }
10279         case AIS_UNDOCK_2: {
10280                 float dist;
10281                 ai_info *other_aip;
10282
10283                 // get pointer to docked object's aip to reset flags, etc
10284                 Assert( aip->dock_objnum != -1 );
10285                 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
10286
10287                 //      Second stage of undocking.
10288                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_2);
10289                 Assert(dist != UNINITIALIZED_VALUE);
10290
10291
10292                 //nprintf(("AI", "Undock 2: dist = %7.3f\n", dist));
10293                 
10294                 //      If at goal point, or quite far away from dock object
10295                 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) ) {
10296                         // reset the dock flags.  If rearm/repair, reset rearm repair flags for those ships as well.
10297                         if ( sip->flags & SIF_SUPPORT ) {
10298                                 ai_do_objects_repairing_stuff( &Objects[aip->dock_objnum], Pl_objp, REPAIR_INFO_END );
10299                         }
10300
10301                         // clear out flags for AIF_DOCKED for both objects.
10302                         ai_do_objects_undocked_stuff( Pl_objp, goal_objp );
10303                         physics_ship_init(Pl_objp);
10304                         aip->submode = AIS_UNDOCK_3;                            //      The do-nothing mode, until another order is issued
10305
10306                         //aip->ai_flags &= ~AIF_DOCKED;         //      @MK, 9/18/97
10307                         //other_aip->ai_flags &= ~AIF_DOCKED;
10308                         //aip->dock_objnum = -1;                                        // invalidate who obj is docked with
10309                         //other_aip->dock_objnum = -1;                  // MWA 10/07/97 invalide docked objects dock_objnum value as well
10310
10311                         // don't add undock log entries for support ships.
10312                         if ( !(sip->flags & SIF_SUPPORT) )
10313                                 mission_log_add_entry(LOG_SHIP_UNDOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10314
10315                 }
10316                 break;
10317                 }
10318         case AIS_UNDOCK_3: {
10319                 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_3);
10320                 Assert(dist != UNINITIALIZED_VALUE);
10321
10322                 if (dist < Pl_objp->radius/2 + 5.0f) {
10323                         aip->submode = AIS_UNDOCK_4;
10324                 }
10325
10326                 // possible that this flag hasn't been cleared yet.  When aborting a rearm, this submode might
10327                 // be entered directly.
10328                 if ( (sip->flags & SIF_SUPPORT) && (aip->ai_flags & AIF_REPAIRING) ) {
10329                         ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], Pl_objp, REPAIR_INFO_ABORT );
10330                 }
10331
10332                 break;
10333                                                  }
10334         case AIS_UNDOCK_4: {
10335                 ai_info *other_aip;
10336
10337                 // MWA 10/07/97  I'm slightly confused by the dual use of goal_objnum and dock_objnum.  Seems to me
10338                 // that goal_objnum and dock_objnum are the same through this whole docking/undocking process, although
10339                 // I could be wrong.  dock_objnum was reset in undock_2 submode so try to use goal_objnum here to
10340                 // get other ships ai_info pointer
10341                 Assert( aip->goal_objnum != -1 );
10342                 other_aip = &Ai_info[Ships[Objects[aip->goal_objnum].instance].ai_index];
10343
10344                 aip->mode = AIM_NONE;
10345                 aip->dock_path_index = -1;              // invalidate the docking path index
10346
10347                 // these flags should have been cleared long ago!
10348                 // Get Allender if you hit one of these!!!!!
10349                 // removed by allender on 2/16 since a ship may be docked with some other ship, but still be the
10350                 // goal_objnum of this ship ending it's undocking mode.
10351                 //Assert( !(aip->ai_flags & AIF_DOCKED) );
10352                 //Assert( !(other_aip->ai_flags & AIF_DOCKED) );
10353                 //Assert( !(aip->ai_flags & AIF_REPAIRING) );
10354                 //Assert( !(other_aip->ai_flags & AIF_BEING_REPAIRED) );
10355                 //Assert( !(other_aip->ai_flags & AIF_AWAITING_REPAIR) );
10356
10357                 // only call mission goal complete if this was indeed an undock goal
10358                 if ( aip->active_goal > -1 ) {
10359                         if ( aip->goals[aip->active_goal].ai_mode == AI_GOAL_UNDOCK )
10360                                 ai_mission_goal_complete( aip );                        // this call should reset the AI mode
10361                         //else
10362                         //      aip->active_goal = -1;                                          // this ensures that this ship might get new goal
10363                 }
10364
10365                 break;
10366                                                          }
10367         default:
10368                 Int3(); //      Error, bogus submode
10369         }
10370
10371 }
10372
10373 // TURRET BEGIN
10374
10375 //      Given an object and a turret on that object, return the global position and forward vector
10376 //      of the turret.   The gun normal is the unrotated gun normal, (the center of the FOV cone), not
10377 // the actual gun normal given using the current turret heading.  But it _is_ rotated into the model's orientation
10378 //      in global space.
10379 void ship_get_global_turret_info(object *objp, model_subsystem *tp, vector *gpos, vector *gvec)
10380 {
10381         matrix  m;
10382         vm_copy_transpose_matrix(&m, &objp->orient);
10383 //      vm_vec_rotate(gpos, &tp->turret_avg_firing_point, &m);
10384         vm_vec_rotate(gpos, &tp->pnt, &m);
10385         vm_vec_add2(gpos, &objp->pos);
10386         vm_vec_rotate(gvec, &tp->turret_norm, &m);      
10387 }
10388
10389 // Given an object and a turret on that object, return the actual firing point of the gun
10390 // and its normal.   This uses the current turret angles.  We are keeping track of which
10391 // gun to fire next in the ship specific info for this turret subobject.  Use this info
10392 // to determine which position to fire from next.
10393 //      Stuffs:
10394 //              *gpos: absolute position of gun firing point
10395 //              *gvec: vector fro *gpos to *targetp
10396 void ship_get_global_turret_gun_info(object *objp, ship_subsys *ssp, vector *gpos, vector *gvec, int use_angles, vector *targetp)
10397 {
10398         vector * gun_pos;
10399         model_subsystem *tp = ssp->system_info;
10400
10401         ship_model_start(objp);
10402
10403         gun_pos = &tp->turret_firing_point[ssp->turret_next_fire_pos % tp->turret_num_firing_points];
10404
10405         model_find_world_point(gpos, gun_pos, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10406
10407         if (use_angles)
10408                 model_find_world_dir(gvec, &tp->turret_norm, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10409         else {
10410                 //vector        gun_pos2;
10411                 //vm_vec_add(&gun_pos2, gpos, gun_pos);
10412                 vm_vec_normalized_dir(gvec, targetp, gpos);
10413         }
10414
10415         ship_model_stop(objp);  
10416 }
10417
10418 //      Rotate a turret towards an enemy.
10419 //      Return TRUE if caller should use angles in subsequent rotations.
10420 //      Some obscure model thing only John Slagel knows about.
10421 //      Sets predicted enemy position.
10422 //      If the turret (*ss) has a subsystem targeted, the subsystem is used as the predicted point.
10423 int aifft_rotate_turret(ship *shipp, int parent_objnum, ship_subsys *ss, object *objp, object *lep, vector *predicted_enemy_pos, vector *gvec)
10424 {
10425         if (ss->turret_enemy_objnum != -1)      {
10426                 model_subsystem *tp = ss->system_info;
10427                 vector  gun_pos, gun_vec;
10428                 float           weapon_speed;
10429                 float           weapon_system_strength;
10430
10431                 //      weapon_system_strength scales time enemy in range in 0..1.  So, the lower this is, the worse the aiming will be.
10432                 weapon_system_strength = ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS);
10433
10434                 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gun_pos, &gun_vec);
10435
10436                 weapon_speed = Weapon_info[tp->turret_weapon_type].max_speed;
10437                 float weapon_travel_dist = weapon_speed * Weapon_info[tp->turret_weapon_type].lifetime;
10438
10439                 vector  enemy_point;
10440                 if (ss->targeted_subsys != NULL) {
10441                         if (ss->turret_enemy_objnum != -1) {
10442                                 vm_vec_unrotate(&enemy_point, &ss->targeted_subsys->system_info->pnt, &Objects[ss->turret_enemy_objnum].orient);
10443                                 vm_vec_add2(&enemy_point, &Objects[ss->turret_enemy_objnum].pos);
10444                         }
10445                 } else {
10446                         if ((lep->type == OBJ_SHIP) && (Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
10447                                 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));
10448                         } else {
10449                                 enemy_point = lep->pos;
10450                         }
10451                 }
10452
10453                 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);
10454
10455                 if (weapon_system_strength < 0.7f) {
10456                         vector  rand_vec;
10457
10458                         static_randvec(Missiontime >> 18, &rand_vec);   //      Return same random number for two seconds.
10459                         //      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.
10460                         vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, (1.0f - weapon_system_strength)*1.5f * lep->radius);
10461                 }
10462
10463                 vector  v2e;
10464                 vm_vec_normalized_dir(&v2e, predicted_enemy_pos, &gun_pos);
10465                 if (vm_vec_dot(&v2e, gvec) > tp->turret_fov) {
10466                         int     rval;
10467
10468                         rval = model_rotate_gun(shipp->modelnum, ss->system_info, &Objects[parent_objnum].orient, 
10469                                 &ss->submodel_info_1.angs, &ss->submodel_info_2.angs,
10470                                 &Objects[parent_objnum].pos, predicted_enemy_pos);
10471                 }
10472         }
10473
10474         return 0;
10475 }
10476
10477 //      Determine if subsystem *enemy_subsysp is hittable from objp.
10478 //      If so, return dot product of vector from point abs_gunposp to *enemy_subsysp
10479 float   aifft_compute_turret_dot(object *objp, object *enemy_objp, vector *abs_gunposp, ship_subsys *turret_subsysp, ship_subsys *enemy_subsysp)
10480 {
10481         float   dot_out;
10482         vector  subobj_pos, vector_out;
10483
10484         vm_vec_unrotate(&subobj_pos, &enemy_subsysp->system_info->pnt, &enemy_objp->orient);
10485         vm_vec_add2(&subobj_pos, &enemy_objp->pos);
10486
10487         if (ship_subsystem_in_sight(enemy_objp, enemy_subsysp, abs_gunposp, &subobj_pos, 1, &dot_out, &vector_out)) {
10488                 vector  turret_norm;
10489
10490                 vm_vec_rotate(&turret_norm, &turret_subsysp->system_info->turret_norm, &objp->orient);
10491                 return vm_vec_dot(&turret_norm, &vector_out);
10492         } else
10493                 return -1.0f;
10494
10495 }
10496
10497 #define MAX_AIFFT_TURRETS                       60
10498 ship_subsys *aifft_list[MAX_AIFFT_TURRETS];
10499 float aifft_rank[MAX_AIFFT_TURRETS];
10500 int aifft_list_size = 0;
10501 int aifft_max_checks = 5;
10502 DCF(mf, "")
10503 {
10504         dc_get_arg(ARG_INT);
10505         aifft_max_checks = Dc_arg_int;
10506 }
10507
10508
10509 //      Pick a subsystem to attack on enemy_objp.
10510 //      Only pick one if enemy_objp is a big ship or a capital ship.
10511 //      Returns dot product from turret to subsystem in *dot_out
10512 ship_subsys *aifft_find_turret_subsys(object *objp, ship_subsys *ssp, object *enemy_objp, float *dot_out)
10513 {
10514         ship    *eshipp, *shipp;
10515         ship_info       *esip;
10516         ship_subsys     *best_subsysp = NULL;
10517         float dot;
10518
10519         Assert(enemy_objp->type == OBJ_SHIP);
10520
10521         eshipp = &Ships[enemy_objp->instance];
10522         esip = &Ship_info[eshipp->ship_info_index];
10523
10524         shipp = &Ships[objp->instance];
10525
10526         float   best_dot = 0.0f;
10527         *dot_out = best_dot;
10528
10529         //      Compute absolute gun position.
10530         vector  abs_gun_pos;
10531         vm_vec_unrotate(&abs_gun_pos, &ssp->system_info->pnt, &objp->orient);
10532         vm_vec_add2(&abs_gun_pos, &objp->pos);
10533
10534         //      Only pick a turret to attack on large ships.
10535         if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
10536                 return best_subsysp;
10537
10538         // Make sure big or huge ship *actually* has subsystems  (ie, knossos)
10539         if (esip->n_subsystems == 0) {
10540                 return best_subsysp;
10541         }
10542
10543         // first build up a list subsystems to traverse
10544         ship_subsys     *pss;
10545         aifft_list_size = 0;
10546         for ( pss = GET_FIRST(&eshipp->subsys_list); pss !=END_OF_LIST(&eshipp->subsys_list); pss = GET_NEXT(pss) ) {
10547                 model_subsystem *psub = pss->system_info;
10548
10549                 // if we've reached max turrets bail
10550                 if(aifft_list_size >= MAX_AIFFT_TURRETS){
10551                         break;
10552                 }
10553
10554                 // Don't process destroyed objects
10555                 if ( pss->current_hits <= 0.0f ){
10556                         continue;
10557                 }
10558                 
10559                 switch (psub->type) {
10560                 case SUBSYSTEM_WEAPONS:
10561                         aifft_list[aifft_list_size] = pss;
10562                         aifft_rank[aifft_list_size++] = 1.4f;
10563                         break;
10564
10565                 case SUBSYSTEM_TURRET:
10566                         aifft_list[aifft_list_size] = pss;
10567                         aifft_rank[aifft_list_size++] = 1.2f;
10568                         break;
10569
10570                 case SUBSYSTEM_SENSORS:
10571                 case SUBSYSTEM_ENGINE:
10572                         aifft_list[aifft_list_size] = pss;
10573                         aifft_rank[aifft_list_size++] = 1.0f;
10574                         break;
10575                 }
10576         }
10577
10578         // DKA:  6/28/99 all subsystems can be destroyed.
10579         //Assert(aifft_list_size > 0);
10580         if (aifft_list_size == 0) {
10581                 return best_subsysp;
10582         }
10583
10584         // determine a stride value so we're not checking too many turrets
10585         int stride = aifft_list_size > aifft_max_checks ? aifft_list_size / aifft_max_checks : 1;
10586         if(stride <= 0){
10587                 stride = 1;
10588         }
10589         int offset = (int)frand_range(0.0f, (float)(aifft_list_size % stride));
10590         int idx;
10591         for(idx=offset; idx<aifft_list_size; idx+=stride){
10592                 dot = aifft_compute_turret_dot(objp, enemy_objp, &abs_gun_pos, ssp, aifft_list[idx]);                   
10593
10594                 if (dot* aifft_rank[idx] > best_dot) {
10595                         best_dot = dot*aifft_rank[idx];
10596                         best_subsysp = aifft_list[idx];
10597                 }
10598         }
10599
10600         Assert(best_subsysp != &eshipp->subsys_list);
10601
10602         *dot_out = best_dot;
10603         return best_subsysp;
10604 }
10605
10606 // Set active weapon for turret
10607 void ai_turret_select_default_weapon(ship_subsys *turret)
10608 {
10609         ship_weapon *twp;
10610
10611         twp = &turret->weapons;
10612
10613         // If a primary weapon is available, select it
10614         if ( twp->num_primary_banks > 0 ) {
10615                 turret->system_info->turret_weapon_type = twp->primary_bank_weapons[0];
10616         } else if ( twp->num_secondary_banks > 0 ) {
10617                 turret->system_info->turret_weapon_type = twp->secondary_bank_weapons[0];
10618         }
10619 }
10620
10621 // return !0 if the specified target should scan for a new target, otherwise return 0
10622 int turret_should_pick_new_target(ship_subsys *turret)
10623 {
10624 //      int target_type;
10625
10626         if ( timestamp_elapsed(turret->turret_next_enemy_check_stamp) ) {
10627                 return 1;
10628         }
10629
10630         return 0;
10631
10632 /*
10633         if ( turret->turret_enemy_objnum == -1 ) {
10634                 return 1;
10635         }
10636                 
10637         target_type = Objects[turret->turret_enemy_objnum].type;
10638         if ( (target_type != OBJ_SHIP) && (target_type != OBJ_ASTEROID) ) {
10639                 return 1;
10640         }
10641
10642         return 0;
10643 */
10644 }
10645
10646 // Set the next fire timestamp for a turret, based on weapon type and ai class
10647 void turret_set_next_fire_timestamp(ship_subsys *turret, ai_info *aip)
10648 {
10649         float   wait;
10650         int     weapon_id;
10651
10652         weapon_id = turret->system_info->turret_weapon_type;
10653
10654         wait = Weapon_info[weapon_id].fire_wait * 1000.0f;
10655
10656         // make side even for team vs. team
10657         if ((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) {
10658                 // flak guns need to fire more rapidly
10659                 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10660                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10661                         wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10662                 } else {
10663                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10664                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10665                 }
10666         } else {
10667                 // flak guns need to fire more rapidly
10668                 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10669                         if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10670                                 wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10671                         } else {
10672                                 wait *= Ship_fire_delay_scale_hostile[Game_skill_level] * 0.5f;
10673                         }       
10674                         wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10675
10676                 } else if (Weapon_info[weapon_id].wi_flags & WIF_HUGE) {
10677                         // make huge weapons fire independently of team
10678                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10679                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10680                 } else {
10681                         // give team friendly an advantage
10682                         if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10683                                 wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10684                         } else {
10685                                 wait *= Ship_fire_delay_scale_hostile[Game_skill_level];
10686                         }       
10687                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10688                 }
10689         }
10690
10691         // vary wait time +/- 10%
10692         wait *= frand_range(0.9f, 1.1f);
10693         turret->turret_next_fire_stamp = timestamp((int) wait);
10694 }
10695
10696 // Decide  if a turret should launch an aspect seeking missile
10697 int turret_should_fire_aspect(ship_subsys *turret, float dot, int weapon_class)
10698 {
10699         weapon_info *wip;
10700
10701         wip = &Weapon_info[weapon_class];
10702
10703         if ( (dot > AICODE_TURRET_DUMBFIRE_ANGLE) && (turret->turret_time_enemy_in_range >= min(wip->min_lock_time,AICODE_TURRET_MAX_TIME_IN_RANGE)) ) {
10704                 return 1;
10705         }
10706
10707         return 0;
10708 }
10709
10710 // Update how long current target has been in this turrets range
10711 void turret_update_enemy_in_range(ship_subsys *turret, float seconds)
10712 {
10713         turret->turret_time_enemy_in_range += seconds;
10714
10715         if ( turret->turret_time_enemy_in_range < 0.0f ) {
10716                 turret->turret_time_enemy_in_range = 0.0f;
10717         }
10718
10719         if ( turret->turret_time_enemy_in_range > AICODE_TURRET_MAX_TIME_IN_RANGE ) {
10720                 turret->turret_time_enemy_in_range = AICODE_TURRET_MAX_TIME_IN_RANGE;
10721         }
10722 }
10723
10724
10725
10726 // Fire a weapon from a turret
10727 void turret_fire_weapon(ship_subsys *turret, int parent_objnum, vector *turret_pos, vector *turret_fvec, vector *predicted_pos = NULL)
10728 {
10729         matrix  turret_orient;
10730         int             turret_weapon_class, weapon_objnum;
10731         ai_info *parent_aip;
10732         ship            *parent_ship;
10733         beam_fire_info fire_info;
10734         float flak_range = 0.0f;
10735
10736         parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
10737         parent_ship = &Ships[Objects[parent_objnum].instance];
10738         turret_weapon_class = turret->system_info->turret_weapon_type;
10739
10740         if (check_ok_to_fire(parent_objnum, turret->turret_enemy_objnum, &Weapon_info[turret_weapon_class])) {
10741                 vm_vector_2_matrix(&turret_orient, turret_fvec, NULL, NULL);
10742                 turret->turret_last_fire_direction = *turret_fvec;
10743
10744                 // set next fire timestamp for the turret
10745                 turret_set_next_fire_timestamp(turret, parent_aip);
10746
10747                 // if this weapon is a beam weapon, handle it specially
10748                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM){
10749                         // if this beam isn't free to fire
10750                         if (!(turret->weapons.flags & SW_FLAG_BEAM_FREE)) {
10751                                 Int3(); // should never get this far
10752                                 return;
10753                         }
10754
10755                         // stuff beam firing info
10756                         memset(&fire_info, 0, sizeof(beam_fire_info));
10757                         fire_info.accuracy = 1.0f;
10758                         fire_info.beam_info_index = turret_weapon_class;
10759                         fire_info.beam_info_override = NULL;
10760                         fire_info.shooter = &Objects[parent_objnum];
10761                         fire_info.target = &Objects[turret->turret_enemy_objnum];
10762                         fire_info.target_subsys = NULL;
10763                         fire_info.turret = turret;
10764
10765                         // fire a beam weapon
10766                         beam_fire(&fire_info);
10767                 } else {
10768
10769                         // don't fire swarm, but set up swarm info
10770                         if (Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM) {
10771                                 turret_swarm_set_up_info(parent_objnum, turret, turret_weapon_class);
10772                                 return;
10773                         } else {
10774                                 weapon_objnum = weapon_create( turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10775                                 weapon_set_tracking_info(weapon_objnum, parent_objnum, turret->turret_enemy_objnum, 1, turret->targeted_subsys);                
10776                         }
10777
10778                         //nprintf(("AI", "Turret_time_enemy_in_range = %7.3f\n", ss->turret_time_enemy_in_range));              
10779                         if (weapon_objnum != -1) {
10780                                 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10781                                 // AL 1-6-97: Store pointer to turret subsystem
10782                                 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10783
10784                                 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10785                                         // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10786                                         if ( parent_objnum != OBJ_INDEX(Player_obj) ) {                                         
10787                                                 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], turret_pos, &View_position );                                          
10788                                         }
10789                                 }               
10790
10791                                 // if the gun is a flak gun
10792                                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){                       
10793                                         // show a muzzle flash
10794                                         flak_muzzle_flash(turret_pos, turret_fvec, turret_weapon_class);
10795
10796                                         // pick a firing range so that it detonates properly                    
10797                                         flak_pick_range(&Objects[weapon_objnum], predicted_pos, ship_get_subsystem_strength(parent_ship, SUBSYSTEM_WEAPONS));
10798
10799                                         // determine what that range was
10800                                         flak_range = flak_get_range(&Objects[weapon_objnum]);
10801                                 }
10802
10803                                 // in multiplayer (and the master), then send a turret fired packet.
10804                                 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10805                                         int subsys_index;
10806
10807                                         subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10808                                         Assert( subsys_index != -1 );
10809                                         if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){                       
10810                                                 send_flak_fired_packet( parent_objnum, subsys_index, weapon_objnum, flak_range );
10811                                         } else {
10812                                                 send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10813                                         }
10814                                 }
10815                         }
10816                 }
10817         } else {
10818                 float wait = 1000.0f * frand_range(0.9f, 1.1f);
10819                 turret->turret_next_fire_stamp = timestamp((int) wait);
10820         }
10821 }
10822
10823 void turret_swarm_fire_from_turret(ship_subsys *turret, int parent_objnum, int target_objnum, ship_subsys *target_subsys)
10824 {
10825         int turret_weapon_class, weapon_objnum;
10826         matrix turret_orient;
10827         vector turret_pos, turret_fvec;
10828
10829         // parent not alive, quick out.
10830         if (Objects[parent_objnum].type != OBJ_SHIP) {
10831                 return;
10832         }
10833
10834         //      change firing point
10835         ship_get_global_turret_gun_info(&Objects[parent_objnum], turret, &turret_pos, &turret_fvec, 1, NULL);
10836         turret->turret_next_fire_pos++;
10837
10838         // get class [index into Weapon_info array
10839         turret_weapon_class = turret->system_info->turret_weapon_type;
10840         Assert(Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM);
10841
10842         // make turret_orient from turret_fvec -- turret->turret_last_fire_direction
10843         vm_vector_2_matrix(&turret_orient, &turret_fvec, NULL, NULL);
10844
10845         // create weapon and homing info
10846         weapon_objnum = weapon_create(&turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10847         weapon_set_tracking_info(weapon_objnum, parent_objnum, target_objnum, 1, target_subsys);
10848
10849         // do other cool stuff if weapon is created.
10850         if (weapon_objnum > -1) {
10851                 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10852                 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10853
10854                 // maybe sound
10855                 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10856                         // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10857                         if ( parent_objnum != OBJ_INDEX(Player_obj) ) {
10858                                 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], &turret_pos, &View_position );
10859                         }
10860                 }
10861                 
10862                 // in multiplayer (and the master), then send a turret fired packet.
10863                 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10864                         int subsys_index;
10865
10866                         subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10867                         Assert( subsys_index != -1 );
10868                         send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10869                 }
10870         }
10871 }
10872
10873 int Num_ai_firing = 0;
10874 int Num_find_turret_enemy = 0;
10875 int Num_turrets_fired = 0;
10876 //      Given a turret tp and its parent parent_objnum, fire from the turret at its enemy.
10877 void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
10878 {
10879         float           weapon_firing_range;
10880         vector  v2e;
10881         object  *lep;           //      Last enemy pointer
10882         model_subsystem *tp = ss->system_info;
10883         int             use_angles, turret_weapon_class;
10884         vector  predicted_enemy_pos;
10885         object  *objp;
10886         ai_info *aip;
10887
10888         if (!Ai_firing_enabled) {
10889                 return;
10890         }
10891
10892         if (ss->current_hits < 0.0f) {
10893                 return;
10894         }
10895
10896         if ( ship_subsys_disrupted(ss) ){               // AL 1/19/98: Make sure turret isn't suffering disruption effects
10897                 return;
10898         }
10899
10900         // Check turret free
10901         if (ss->weapons.flags & SW_FLAG_TURRET_LOCK) {
10902                 return;
10903         }
10904
10905         // If beam weapon, check beam free
10906         if ( (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) && !(ss->weapons.flags & SW_FLAG_BEAM_FREE) ) {
10907                 return;
10908         }
10909
10910         Assert( shipp->objnum == parent_objnum );
10911
10912         if ( tp->turret_weapon_type < 0 ){
10913                 return;
10914         }
10915
10916         // Monitor number of calls to ai_fire_from_turret
10917         Num_ai_firing++;
10918
10919         turret_weapon_class = tp->turret_weapon_type;
10920
10921         // AL 09/14/97: ensure ss->turret_enemy_objnum != -1 before setting lep
10922         if ( (ss->turret_enemy_objnum >= 0 && ss->turret_enemy_objnum < MAX_OBJECTS) && (ss->turret_enemy_sig == Objects[ss->turret_enemy_objnum].signature)) {
10923                 lep = &Objects[ss->turret_enemy_objnum];
10924
10925                 // MK -- here is where turret is targeting a bomb.  I simply return for now.  We should force
10926                 // a target change -- or better yet, never pick a weapon when this turret has a "huge" weapon
10927                 // loaded.
10928
10929                 // we only care about targets which are ships.
10930                 //if ( lep->type != OBJ_SHIP )
10931                 //      return;
10932
10933                 //      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.
10934                 if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HUGE ) {
10935                         if ( lep->type != OBJ_SHIP ) {
10936                                 return;
10937                         }
10938                         if ( !(Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
10939                                 return;
10940                         }
10941                 }
10942
10943                 // If targeting protected or beam protected ship, don't fire.  Reset enemy objnum
10944                 if (lep->type == OBJ_SHIP) {
10945                         // Check if we're targeting a protected ship
10946                         if (lep->flags & OF_PROTECTED) {
10947                                 ss->turret_enemy_objnum = -1;
10948                                 ss->turret_time_enemy_in_range = 0.0f;
10949                                 return;
10950                         }
10951
10952                         // Check if we're targeting a beam protected ship with a beam weapon
10953                         if ( (lep->flags & OF_BEAM_PROTECTED) && (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) ) {
10954                                 ss->turret_enemy_objnum = -1;
10955                                 ss->turret_time_enemy_in_range = 0.0f;
10956                                 return;
10957                         }
10958                 }
10959         } else {
10960                 ss->turret_enemy_objnum = -1;
10961                 lep = NULL;
10962         }
10963         
10964         Assert((parent_objnum >= 0) && (parent_objnum < MAX_OBJECTS));
10965         objp = &Objects[parent_objnum];
10966         Assert(objp->type == OBJ_SHIP);
10967         aip = &Ai_info[Ships[objp->instance].ai_index];
10968
10969         // Use the turret info for all guns, not one gun in particular.
10970         vector   gvec, gpos;
10971         ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
10972
10973         // Rotate the turret even if time hasn't elapsed, since it needs to turn to face its target.
10974         use_angles = aifft_rotate_turret(shipp, parent_objnum, ss, objp, lep, &predicted_enemy_pos, &gvec);
10975
10976         if ( !timestamp_elapsed(ss->turret_next_fire_stamp)){
10977                 return;
10978         }
10979
10980         // Don't try to fire beyond weapon_limit_range
10981         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);
10982
10983         // if beam weapon in nebula and target not tagged, decrase firing range
10984         extern int Nebula_sec_range;
10985         if (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) {
10986                 if ( !((shipp->tag_left > 0) || (shipp->level2_tag_left > 0)) ) {
10987                         if (Nebula_sec_range) {
10988                                 weapon_firing_range *= float(BEAM_NEBULA_RANGE_REDUCE_FACTOR);
10989                         }
10990                 }
10991         }
10992
10993         if (ss->turret_enemy_objnum != -1) {
10994                 float dist_to_enemy = vm_vec_normalized_dir(&v2e, &predicted_enemy_pos, &gpos) - lep->radius;
10995                 if (dist_to_enemy > weapon_firing_range) {
10996                         ss->turret_enemy_objnum = -1;           //      Force picking of new enemy.
10997                 }
10998         }
10999
11000         // Turret spawn weapons are a special case.  They fire if there are enough enemies in the 
11001         // immediate area (not necessarily in the turret fov).
11002         if ( Weapon_info[turret_weapon_class].wi_flags & WIF_SPAWN ) {
11003                 int num_ships_nearby;
11004                 num_ships_nearby = num_nearby_fighters(get_enemy_team_mask(parent_objnum), &gpos, 1500.0f);
11005                 if (( num_ships_nearby >= 3 ) || ((num_ships_nearby >= 2) && (frand() < 0.1f))) {
11006                         turret_fire_weapon(ss, parent_objnum, &gpos, &ss->turret_last_fire_direction);
11007                 } else {
11008                         ss->turret_next_fire_stamp = timestamp(1000);   //      Regardless of firing rate, don't check whether should fire for awhile.
11009                 }
11010                 return;
11011         }
11012
11013         //      Maybe pick a new enemy.
11014         if ( turret_should_pick_new_target(ss) ) {
11015                 Num_find_turret_enemy++;
11016                 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);
11017                 Assert(objnum < 0 || is_target_beam_valid(ss, objnum));
11018
11019                 if (objnum != -1) {
11020                         if (ss->turret_enemy_objnum == -1) {
11021                                 ss->turret_enemy_objnum = objnum;
11022                                 ss->turret_enemy_sig = Objects[objnum].signature;
11023                                 // why return?
11024                                 return;
11025                         } else {
11026                                 ss->turret_enemy_objnum = objnum;
11027                                 ss->turret_enemy_sig = Objects[objnum].signature;
11028                         }
11029                 } else {
11030                         ss->turret_enemy_objnum = -1;
11031                 }
11032
11033                 if (ss->turret_enemy_objnum != -1) {
11034                         float   dot = 1.0f;
11035                         lep = &Objects[ss->turret_enemy_objnum];
11036                         if ( lep->type == OBJ_SHIP ) {
11037                                 ss->targeted_subsys = aifft_find_turret_subsys(objp, ss, lep, &dot);                            
11038                         }
11039                         ss->turret_next_enemy_check_stamp = timestamp((int) (max(dot, 0.5f)*2000.0f) + 1000);
11040                 } else {
11041                         ss->turret_next_enemy_check_stamp = timestamp((int) (2000.0f * frand_range(0.9f, 1.1f)));       //      Check every two seconds
11042                 }
11043         }
11044
11045         //      If still don't have an enemy, return.  Or, if enemy is protected, return.
11046         if (ss->turret_enemy_objnum != -1) {
11047                 //      Don't shoot at ship we're going to dock with.
11048                 if (ss->turret_enemy_objnum == aip->dock_objnum) {
11049                         ss->turret_enemy_objnum = -1;
11050                         return;
11051                 }
11052
11053                 if (Objects[ss->turret_enemy_objnum].flags & OF_PROTECTED) {
11054                         //      This can happen if the enemy was selected before it became protected.
11055                         ss->turret_enemy_objnum = -1;
11056                         return;
11057                 }
11058                 lep = &Objects[ss->turret_enemy_objnum];
11059         } else {
11060                 if (timestamp_until(ss->turret_next_fire_stamp) < 500) {
11061                         ss->turret_next_fire_stamp = timestamp(500);
11062                 }
11063                 return;
11064         }
11065
11066         if ( lep == NULL ){
11067                 return;
11068         }
11069
11070         Assert(ss->turret_enemy_objnum != -1);
11071
11072         float dot = vm_vec_dot(&v2e, &gvec);
11073
11074         if (dot > tp->turret_fov ) {
11075                 // Ok, the turret is lined up... now line up a particular gun.
11076                 int ok_to_fire = 0;
11077                 float dist_to_enemy;
11078
11079                 // We're ready to fire... now get down to specifics, like where is the
11080                 // actual gun point and normal, not just the one for whole turret.
11081                 ship_get_global_turret_gun_info(&Objects[parent_objnum], ss, &gpos, &gvec, use_angles, &predicted_enemy_pos);
11082                 ss->turret_next_fire_pos++;
11083
11084                 // Fire in the direction the turret is facing, not right at the target regardless of turret dir.
11085                 vm_vec_sub(&v2e, &predicted_enemy_pos, &gpos);
11086                 dist_to_enemy = vm_vec_normalize(&v2e);
11087                 dot = vm_vec_dot(&v2e, &gvec);
11088
11089                 // 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
11090                 // and make them less lethal
11091                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){
11092                         flak_jitter_aim(&v2e, dist_to_enemy, ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS));
11093                 }
11094
11095                 // Fire if:
11096                 //              dumbfire and nearly pointing at target.
11097                 //              heat seeking and target in a fairly wide cone.
11098                 //              aspect seeking and target is locked.
11099                 turret_weapon_class = tp->turret_weapon_type;
11100
11101                 // if dumbfire (lasers and non-homing missiles)
11102                 if ( !(Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING) ) {
11103                         if ((dist_to_enemy < 75.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11104                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11105                                 ok_to_fire = 1;
11106                         }
11107                 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_HEAT ) {     // if heat seekers
11108                         if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_HEATSEEK_ANGLE )) {
11109                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11110                                 ok_to_fire = 1;
11111                         }
11112                 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_ASPECT ) {   // if aspect seeker
11113                         if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11114                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11115                         }
11116                         if ( turret_should_fire_aspect(ss, dot, turret_weapon_class) ) {
11117                                 ok_to_fire = 1;
11118                         }
11119                 }
11120
11121                 if ( ok_to_fire ) {
11122                         Num_turrets_fired++;
11123                         
11124                         turret_fire_weapon(ss, parent_objnum, &gpos, &v2e, &predicted_enemy_pos);                                               
11125                 } else {
11126                         turret_update_enemy_in_range(ss, -4*Weapon_info[tp->turret_weapon_type].fire_wait);
11127                         ss->turret_next_fire_stamp = timestamp(500);
11128                 }
11129         } else {
11130                 // Lost him!
11131                 ss->turret_enemy_objnum = -1;           //      Reset enemy objnum, find a new one next frame.
11132                 ss->turret_time_enemy_in_range = 0.0f;
11133         }
11134 }
11135
11136 // TURRET END
11137
11138 #ifndef NDEBUG
11139 #define MAX_AI_DEBUG_RENDER_STUFF       100
11140 typedef struct ai_render_stuff {
11141         ship_subsys     *ss;
11142         int                     parent_objnum;
11143 } ai_render_stuff;
11144
11145 ai_render_stuff AI_debug_render_stuff[MAX_AI_DEBUG_RENDER_STUFF];
11146
11147 int     Num_AI_debug_render_stuff = 0;
11148
11149 void ai_debug_render_stuff()
11150 {
11151         vertex  vert1, vert2;
11152         vector  gpos2;
11153         int             i;
11154
11155         for (i=0; i<Num_AI_debug_render_stuff; i++) {
11156                 ship_subsys     *ss;
11157                 int     parent_objnum;
11158                 vector  gpos, gvec;
11159                 model_subsystem *tp;
11160
11161                 ss = AI_debug_render_stuff[i].ss;
11162                 tp = ss->system_info;
11163
11164                 parent_objnum = AI_debug_render_stuff[i].parent_objnum;
11165
11166                 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
11167                 g3_rotate_vertex(&vert1, &gpos);
11168                 vm_vec_scale_add(&gpos2, &gpos, &gvec, 20.0f);
11169                 g3_rotate_vertex(&vert2, &gpos2);
11170                 gr_set_color(0, 0, 255);
11171                 g3_draw_sphere(&vert1, 2.0f);
11172                 gr_set_color(255, 0, 255);
11173                 g3_draw_sphere(&vert2, 2.0f);
11174                 g3_draw_line(&vert1, &vert2);
11175         }
11176
11177         // draw from beta to its goal point
11178 /*      for (i=0; i<6; i++) {
11179                 ai_info *aip = &Ai_info[i];
11180                 gr_set_color(0, 0, 255);
11181                 g3_rotate_vertex(&vert1, &Objects[i].pos);
11182                 g3_rotate_vertex(&vert2, &aip->goal_point);
11183                 g3_draw_line(&vert1, &vert2);
11184         } */
11185         
11186
11187         Num_AI_debug_render_stuff = 0;
11188 }
11189
11190 #endif
11191
11192 #ifndef NDEBUG
11193 int     Msg_count_4996 = 0;
11194 #endif
11195
11196 //      --------------------------------------------------------------------------
11197 // Process subobjects of object objnum.
11198 //      Deal with engines disabled.
11199 void process_subobjects(int objnum)
11200 {
11201         model_subsystem *psub;
11202         ship_subsys     *pss;
11203         object  *objp = &Objects[objnum];
11204         ship            *shipp = &Ships[objp->instance];
11205         ai_info *aip = &Ai_info[shipp->ai_index];
11206         ship_info       *sip = &Ship_info[shipp->ship_info_index];
11207
11208         for ( pss = GET_FIRST(&shipp->subsys_list); pss !=END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
11209                 psub = pss->system_info;
11210
11211                 // Don't process destroyed objects
11212                 if ( pss->current_hits <= 0.0f ) 
11213                         continue;
11214
11215                 switch (psub->type) {
11216                 case SUBSYSTEM_TURRET:
11217                         if ( psub->turret_num_firing_points > 0 )       {
11218                                 ai_fire_from_turret(shipp, pss, objnum);
11219                         } else {
11220 #ifndef NDEBUG
11221                                 if (!Msg_count_4996) {
11222                                         Warning( LOCATION, "Ship '%s' has turrets with no guns!\nProbably a model problem, so get an artist!", shipp->ship_name );
11223                                         Msg_count_4996++;
11224                                 }
11225 #endif
11226                                 }
11227                         break;
11228
11229                 case SUBSYSTEM_ENGINE:
11230                 case SUBSYSTEM_NAVIGATION:
11231                 case SUBSYSTEM_COMMUNICATION:
11232                 case SUBSYSTEM_WEAPONS:
11233                 case SUBSYSTEM_SENSORS:
11234                 case SUBSYSTEM_UNKNOWN:
11235                         break;
11236
11237                 // next set of subsystems may rotation
11238                 case SUBSYSTEM_RADAR:
11239                 case SUBSYSTEM_SOLAR:
11240                 case SUBSYSTEM_GAS_COLLECT:
11241                 case SUBSYSTEM_ACTIVATION:
11242                         break;
11243                 default:
11244                         Error(LOCATION, "Illegal subsystem type.\n");
11245                 }
11246
11247                 // do solar/radar/gas/activator rotation here
11248                 if ( psub->flags & MSS_FLAG_ROTATES )   {
11249                         if (psub->flags & MSS_FLAG_STEPPED_ROTATE       ) {
11250                                 submodel_stepped_rotate(psub, &pss->submodel_info_1);
11251                         } else {
11252                                 submodel_rotate(psub, &pss->submodel_info_1 );
11253                         }
11254                 }
11255
11256         }
11257
11258         //      Deal with a ship with blown out engines.
11259         if (ship_get_subsystem_strength(shipp, SUBSYSTEM_ENGINE) == 0.0f) {
11260                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
11261                         // AL: Only attack forever if not trying to depart to a docking bay.  Need to have this in, since
11262                         //     a ship may get repaired... and it should still try to depart.  Since docking bay departures
11263                         //     are not handled as goals, we don't want to leave the AIM_BAY_DEPART mode.
11264                         if ( aip->mode != AIM_BAY_DEPART ) {
11265                                 ai_attack_object(objp, NULL, 99, NULL);         //      Regardless of current mode, enter attack mode.
11266                                 aip->submode = SM_ATTACK_FOREVER;                               //      Never leave attack submode, don't avoid, evade, etc.
11267                         }
11268                 }
11269         }
11270
11271
11272 }
11273
11274 //      Given an object and the wing it's in, return its index in the wing list.
11275 //      This defines its location in the wing formation.
11276 //      If the object can't be found in the wing, return -1.
11277 //      *objp           object of interest
11278 //      wingnum the wing *objp is in
11279 int get_wing_index(object *objp, int wingnum)
11280 {
11281         wing    *wingp;
11282         int     i;
11283
11284         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11285
11286         wingp = &Wings[wingnum];
11287
11288         for (i=wingp->current_count-1; i>=0; i--)
11289                 if ( objp->instance == wingp->ship_index[i] )
11290                         break;
11291
11292         return i;               //      Note, returns -1 if string not found.
11293 }
11294
11295 //      Given a wing, return a pointer to the object of its leader.
11296 //      Asserts if object not found.
11297 //      Currently, the wing leader is defined as the first object in the wing.
11298 //      wingnum         Wing number in Wings array.
11299 //      If wing leader is disabled, swap it with another ship.
11300 object * get_wing_leader(int wingnum)
11301 {
11302         wing            *wingp;
11303         int             ship_num;
11304
11305         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11306
11307         wingp = &Wings[wingnum];
11308
11309         Assert(wingp->current_count != 0);                      //      Make sure there is a leader
11310
11311         ship_num = wingp->ship_index[0];
11312
11313         //      If this ship is disabled, try another ship in the wing.
11314         int n = 0;
11315         while (ship_get_subsystem_strength(&Ships[ship_num], SUBSYSTEM_ENGINE) == 0.0f) {
11316                 n++;
11317                 if (n >= wingp->current_count)
11318                         break;  
11319                 ship_num = wingp->ship_index[n];
11320         }
11321
11322         if (( n != 0) && (n != wingp->current_count)) {
11323                 int t = wingp->ship_index[0];
11324                 wingp->ship_index[0] = wingp->ship_index[n];
11325                 wingp->ship_index[n] = t;
11326         }
11327
11328         return &Objects[Ships[ship_num].objnum];
11329 }
11330
11331 #define DEFAULT_WING_X_DELTA            1.0f
11332 #define DEFAULT_WING_Y_DELTA            0.25f
11333 #define DEFAULT_WING_Z_DELTA            0.75f
11334 #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))
11335 // next constant is higher that MAX_SHIPS_IN_WINGS to deal with forming on player's wing
11336 #define MAX_FORMATION_ROWS              4
11337
11338 //      Given a position in a wing, return the desired location of the ship relative to the leader
11339 //      *_delta_vec             OUTPUT.  delta vector based on wing_index
11340 //      wing_index              position in wing.
11341 void get_wing_delta(vector *_delta_vec, int wing_index)
11342 {
11343         int     wi0;
11344
11345         Assert(wing_index >= 0);
11346
11347         int     k, row, column;
11348
11349         int bank = wing_index / (MAX_FORMATION_ROWS*(MAX_FORMATION_ROWS+1)/2);
11350         wi0 = wing_index % (MAX_FORMATION_ROWS * (MAX_FORMATION_ROWS+1)/2);
11351
11352         k = 0;
11353         for (row=1; row<MAX_FORMATION_ROWS+1; row++) {
11354                 k += row;
11355                 if (wi0 < k)
11356                         break;
11357         }
11358
11359         row--;
11360         column = wi0 - k + row + 1;
11361
11362         _delta_vec->xyz.x = ((float) column - (float) row/2.0f) * DEFAULT_WING_X_DELTA/DEFAULT_WING_MAG;
11363         _delta_vec->xyz.y = ((float)row + (float)bank*2.25f) * DEFAULT_WING_Y_DELTA/DEFAULT_WING_MAG;
11364         _delta_vec->xyz.z = - ((float)row + 0.5f * (float) bank) * DEFAULT_WING_Z_DELTA/DEFAULT_WING_MAG;
11365 }
11366
11367 //      Compute the largest radius of a ship in a *objp's wing.
11368 float gwlr_1(object *objp, ai_info *aip)
11369 {
11370         int             wingnum = aip->wing;
11371         float           max_radius;
11372         object  *o;
11373         ship_obj        *so;
11374
11375         Assert(wingnum >= 0);
11376
11377         max_radius = objp->radius;
11378
11379         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11380                 o = &Objects[so->objnum];
11381                 if (Ai_info[Ships[o->instance].ai_index].wing == wingnum)
11382                         if (o->radius > max_radius)
11383                                 max_radius = o->radius;
11384         }
11385
11386         return max_radius;
11387 }
11388
11389 //      Compute the largest radius of a ship forming on *objp's wing.
11390 float gwlr_object_1(object *objp, ai_info *aip)
11391 {
11392         float           max_radius;
11393         object  *o;
11394         ship_obj        *so;
11395
11396         max_radius = objp->radius;
11397
11398         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11399                 o = &Objects[so->objnum];
11400                 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == OBJ_INDEX(objp))
11401                         if (o->radius > max_radius)
11402                                 max_radius = o->radius;
11403         }
11404
11405         return max_radius;
11406 }
11407
11408 //      For the wing that *objp is part of, return the largest ship radius in that wing.
11409 float get_wing_largest_radius(object *objp, int formation_object_flag)
11410 {
11411         ship            *shipp;
11412         ai_info *aip;
11413
11414         Assert(objp->type == OBJ_SHIP);
11415         Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
11416         shipp = &Ships[objp->instance];
11417         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11418         aip = &Ai_info[shipp->ai_index];
11419
11420         if (formation_object_flag) {
11421                 return gwlr_object_1(objp, aip);
11422         } else {
11423                 return gwlr_1(objp, aip);
11424         }
11425
11426 }
11427
11428 float Wing_y_scale = 2.0f;
11429 float Wing_scale = 1.0f;
11430 DCF(wing_y_scale, "")
11431 {
11432         dc_get_arg(ARG_FLOAT);
11433         Wing_y_scale = Dc_arg_float;
11434 }
11435
11436 DCF(wing_scale, "")
11437 {
11438         dc_get_arg(ARG_FLOAT);
11439         Wing_scale = Dc_arg_float;
11440 }
11441
11442 // Given a wing leader and a position in the wing formation, return the desired absolute location to fly to.
11443 //      Returns result in *result_pos.
11444 void get_absolute_wing_pos(vector *result_pos, object *leader_objp, int wing_index, int formation_object_flag)
11445 {
11446         vector  wing_delta, rotated_wing_delta;
11447         float           wing_spread_size;
11448
11449         get_wing_delta(&wing_delta, wing_index);                //      Desired location in leader's reference frame
11450
11451         wing_spread_size = max(50.0f, 3.0f * get_wing_largest_radius(leader_objp, formation_object_flag) + 15.0f);
11452
11453         // for player obj (1) move ships up 20% (2) scale formation up 20%
11454         if (leader_objp->flags & OF_PLAYER_SHIP) {
11455                 wing_delta.xyz.y *= Wing_y_scale;
11456                 wing_spread_size *= Wing_scale;
11457         }
11458
11459         vm_vec_scale(&wing_delta, wing_spread_size * (1.0f + leader_objp->phys_info.speed/70.0f));
11460
11461         vm_vec_unrotate(&rotated_wing_delta, &wing_delta, &leader_objp->orient);        //      Rotate into leader's reference.
11462
11463         vm_vec_add(result_pos, &leader_objp->pos, &rotated_wing_delta); //      goal_point is absolute 3-space point.
11464 }
11465
11466 #ifndef NDEBUG
11467 int Debug_render_wing_phantoms;
11468
11469 void render_wing_phantoms(object *objp)
11470 {
11471         int             i;
11472         ship            *shipp;
11473         ai_info *aip;
11474         int             wingnum;
11475         int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11476         vector  goal_point;
11477         
11478         Assert(objp->type == OBJ_SHIP);
11479         Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11480
11481         shipp = &Ships[objp->instance];
11482         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11483
11484         aip = &Ai_info[shipp->ai_index];
11485
11486         wingnum = aip->wing;
11487
11488         if (wingnum == -1)
11489                 return;
11490
11491         wing_index = get_wing_index(objp, wingnum);
11492
11493         //      If this ship is NOT the leader, abort.
11494         if (wing_index != 0)
11495                 return;
11496
11497         for (i=0; i<32; i++)
11498                 if (Debug_render_wing_phantoms & (1 << i)) {
11499                         get_absolute_wing_pos(&goal_point, objp, i, 0);
11500         
11501                         vertex  vert;
11502                         gr_set_color(255, 0, 128);
11503                         g3_rotate_vertex(&vert, &goal_point);
11504                         g3_draw_sphere(&vert, 2.0f);
11505                 }
11506
11507         Debug_render_wing_phantoms = 0;
11508
11509 }
11510
11511 void render_wing_phantoms_all()
11512 {
11513         object  *objp;
11514         ship_obj        *so;
11515
11516         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11517                 ship            *shipp;
11518                 ai_info *aip;
11519                 int             wingnum;
11520                 int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11521
11522                 objp = &Objects[so->objnum];
11523                 
11524                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11525                 shipp = &Ships[objp->instance];
11526                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11527
11528                 aip = &Ai_info[shipp->ai_index];
11529
11530                 wingnum = aip->wing;
11531
11532                 if (wingnum == -1)
11533                         continue;
11534
11535                 wing_index = get_wing_index(objp, wingnum);
11536
11537                 //      If this ship is NOT the leader, abort.
11538                 if (wing_index != 0)
11539                         continue;
11540                 
11541                 render_wing_phantoms(objp);
11542
11543                 return;
11544         }
11545 }
11546
11547 #endif
11548
11549 //      Hook from goals code to AI.
11550 //      Force a wing to fly in formation.
11551 //      Sets AIF_FORMATION bit in ai_flags.
11552 //      wingnum         Wing to force to fly in formation
11553 void ai_fly_in_formation(int wingnum)
11554 {
11555         object  *objp;
11556         ship            *shipp;
11557         ship_obj        *so;
11558
11559         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11560                 objp = &Objects[so->objnum];
11561                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11562
11563                 shipp = &Ships[objp->instance];
11564                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11565
11566                 if (Ai_info[shipp->ai_index].wing == wingnum) {
11567                         Ai_info[shipp->ai_index].ai_flags |= AIF_FORMATION_WING;
11568                         Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_OBJECT;
11569                 }
11570         }
11571 }
11572
11573 //      Hook from goals code to AI.
11574 //      Force a wing to abandon formation flying.
11575 //      Clears AIF_FORMATION bit in ai_flags.
11576 //      wingnum         Wing to force to fly in formation
11577 void ai_disband_formation(int wingnum)
11578 {
11579         object  *objp;
11580         ship            *shipp;
11581         ship_obj        *so;
11582
11583         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11584                 objp = &Objects[so->objnum];
11585                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11586
11587                 shipp = &Ships[objp->instance];
11588                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11589
11590                 if (Ai_info[shipp->ai_index].wing == wingnum) {
11591                         Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_WING;
11592                 }
11593         }
11594 }
11595
11596 float   Leader_chaos = 0.0f;
11597 int Chaos_frame = -1;
11598
11599 //      Return true if objp is flying in an erratic manner
11600 //      Only true if objp is a player
11601 int formation_is_leader_chaotic(object *objp)
11602 {
11603         if (Game_mode & GM_MULTIPLAYER)
11604                 return 0;
11605
11606         if (objp != Player_obj)
11607                 return 0;
11608
11609         if (Framecount != Chaos_frame) {
11610                 float   speed_scale;
11611                 float   fdot, udot;
11612
11613                 speed_scale = 3.0f + objp->phys_info.speed * 0.1f;
11614
11615                 fdot = 5.0f * (1.0f - vm_vec_dot(&objp->orient.v.fvec, &objp->last_orient.v.fvec)) * flFrametime;
11616                 udot = 8.0f * (1.0f - vm_vec_dot(&objp->orient.v.uvec, &objp->last_orient.v.uvec)) * flFrametime;
11617
11618                 Leader_chaos += fdot * speed_scale + udot * speed_scale;
11619
11620                 Leader_chaos *= (1.0f - flFrametime*0.2f);
11621
11622                 if (Leader_chaos < 0.0f)
11623                         Leader_chaos = 0.0f;
11624                 else if (Leader_chaos > 1.7f)
11625                         Leader_chaos = 1.7f;
11626
11627                 //nprintf(("AI", "Frame %i: chaos = %7.4f\n", Framecount, Leader_chaos));
11628
11629                 Chaos_frame = Framecount;
11630         }
11631
11632         return (Leader_chaos > 1.0f);
11633 }
11634
11635 // Fly in formation.
11636 //      Make Pl_objp assume its proper place in formation.
11637 //      If the leader of the wing is doing something stupid, like fighting a battle,
11638 //      then the poor sap wingmates will be in for a "world of hurt"
11639 //      Return TRUE if we need to process this object's normal mode
11640 int ai_formation()
11641 {
11642         object  *leader_objp;
11643         ship            *shipp;
11644         ai_info *aip, *laip;
11645         int             wingnum;
11646         int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11647         int             player_wing;    // index of the players wingnum
11648         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;
11649         float           dot_to_goal, dist_to_goal, leader_speed;
11650
11651         Assert(Pl_objp->type == OBJ_SHIP);
11652         Assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_SHIPS));
11653
11654         shipp = &Ships[Pl_objp->instance];
11655
11656         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11657
11658         aip = &Ai_info[shipp->ai_index];
11659
11660         Assert((aip->ai_flags & AIF_FORMATION) != AIF_FORMATION);       //      Make sure not both types of formation flying in effect.
11661
11662         //      Determine which kind of formation flying.
11663         //      If tracking an object, not in waypoint mode:
11664         if (aip->ai_flags & AIF_FORMATION_OBJECT) {
11665                 if ((aip->goal_objnum < 0) || (aip->goal_objnum >= MAX_OBJECTS)) {
11666                         aip->ai_flags &= ~AIF_FORMATION_OBJECT;
11667                         return 1;
11668                 }
11669                 
11670                 wing_index = ai_formation_object_get_slotnum(aip->goal_objnum, Pl_objp);
11671                 leader_objp = &Objects[aip->goal_objnum];
11672         } else {        //      Formation flying in waypoint mode.
11673                 Assert(aip->ai_flags & AIF_FORMATION_WING);
11674                 if (aip->mode != AIM_WAYPOINTS) {
11675                         aip->ai_flags &= ~AIF_FORMATION_WING;
11676                         return 1;
11677                 }
11678
11679                 wingnum = aip->wing;
11680
11681                 if (wingnum == -1)
11682                         return 1;
11683
11684                 // disable formation flying for any ship in the players wing
11685                 player_wing = Ships[Player_obj->instance].wingnum;
11686                 if ( (player_wing != -1) && (wingnum == player_wing) )
11687                         return 1;
11688
11689                 wing_index = get_wing_index(Pl_objp, wingnum);
11690
11691                 leader_objp = get_wing_leader(wingnum);
11692
11693         }
11694
11695         //      If docked with a ship in this wing, only the more massive one actually flies in formation.
11696         if (aip->dock_objnum != -1) {
11697                 object  *other_objp = &Objects[aip->dock_objnum];
11698                 ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
11699
11700                 if (aip->wing == other_aip->wing) {
11701                         if (Pl_objp->phys_info.mass < other_objp->phys_info.mass)
11702                                 return 0;
11703                         else if (Pl_objp->phys_info.mass == other_objp->phys_info.mass) {
11704                                 if (Pl_objp->signature < other_objp->signature)
11705                                         return 0;
11706                         }
11707                 }
11708         }
11709
11710         Assert(leader_objp != NULL);
11711         laip = &Ai_info[Ships[leader_objp->instance].ai_index];
11712
11713         //      Make sure we're really in this wing.
11714         if (wing_index == -1)
11715                 return 1;
11716
11717         //      If this ship is the leader, abort, as he doesn't have to follow anyone.
11718         if (wing_index == 0) {
11719                 // nprintf(("AI", "Hmm, wing leader %s in ai_formation for no good reason.\n", shipp->ship_name));
11720                 return 1;
11721         }
11722
11723         if (aip->mode == AIM_WAYPOINTS) {
11724                 aip->wp_list = laip->wp_list;
11725                 if (laip->wp_index < Waypoint_lists[laip->wp_list].count)
11726                         aip->wp_index = laip->wp_index;
11727                 else
11728                         aip->wp_index = Waypoint_lists[laip->wp_list].count - 1;
11729                 aip->wp_flags = laip->wp_flags;
11730                 aip->wp_dir = laip->wp_dir;
11731         }
11732
11733         #ifndef NDEBUG
11734         Debug_render_wing_phantoms |= (1 << wing_index);
11735         #endif
11736
11737         leader_speed = leader_objp->phys_info.speed;
11738         vector leader_vec = leader_objp->phys_info.vel;
11739
11740         get_absolute_wing_pos(&goal_point, leader_objp, wing_index, aip->ai_flags & AIF_FORMATION_OBJECT);
11741         vm_vec_scale_add(&future_goal_point_5, &goal_point, &leader_vec, 10.0f);
11742         vm_vec_scale_add(&future_goal_point_2, &goal_point, &leader_vec, 5.0f);
11743         vm_vec_scale_add(&future_goal_point_x, &goal_point, &leader_objp->orient.v.fvec, 10.0f);        //      used when very close to destination
11744         vm_vec_scale_add(&future_goal_point_1000x, &goal_point, &leader_objp->orient.v.fvec, 1000.0f);  //      used when very close to destination
11745
11746         //      Now, get information telling this object how to turn and accelerate to get to its
11747         //      desired location.
11748         vm_vec_sub(&vec_to_goal, &goal_point, &Pl_objp->pos);
11749         if ( vm_vec_mag_quick(&vec_to_goal) < AICODE_SMALL_MAGNITUDE )
11750                 vec_to_goal.xyz.x += 0.1f;
11751
11752         vm_vec_copy_normalize(&dir_to_goal, &vec_to_goal);
11753         //dot_to_goal = vm_vec_dot(&dir_to_goal, &leader_objp->orient.v.fvec);
11754         dot_to_goal = vm_vec_dot(&dir_to_goal, &Pl_objp->orient.v.fvec);
11755         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &goal_point);
11756         float   dist_to_goal_2 = vm_vec_dist_quick(&Pl_objp->pos, &future_goal_point_2);
11757
11758         // 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));
11759
11760         int     chaotic_leader = 0;
11761
11762         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.
11763
11764         if (dist_to_goal > 500.0f) {
11765                 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11766                 accelerate_ship(aip, 1.0f);
11767         } else if (dist_to_goal > 200.0f) {
11768                 if (dot_to_goal > -0.5f) {
11769                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11770                         float range_speed = shipp->current_max_speed - leader_speed;
11771                         if (range_speed > 0.0f)
11772                                 set_accel_for_target_speed(Pl_objp, leader_speed + range_speed * (dist_to_goal+100.0f)/500.0f);
11773                         else
11774                                 set_accel_for_target_speed(Pl_objp, shipp->current_max_speed);
11775                 } else {
11776                         turn_towards_point(Pl_objp, &future_goal_point_5, NULL, 0.0f);
11777                         if (leader_speed > 10.0f)
11778                                 set_accel_for_target_speed(Pl_objp, leader_speed *(1.0f + dot_to_goal));
11779                         else
11780                                 set_accel_for_target_speed(Pl_objp, 10.0f);
11781                 }
11782         } else {
11783                 vector  v2f2;
11784                 float   dot_to_f2;
11785                 float   dist_to_f2;
11786
11787                 dist_to_f2 = vm_vec_normalized_dir(&v2f2, &future_goal_point_2, &Pl_objp->pos);
11788                 dot_to_f2 = vm_vec_dot(&v2f2, &Pl_objp->orient.v.fvec);
11789
11790                 //      Leader flying like a maniac.  Don't try hard to form on wing.
11791                 if (chaotic_leader) {
11792                         turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11793                         set_accel_for_target_speed(Pl_objp, min(leader_speed*0.8f, 20.0f));
11794                 } else if (dist_to_goal > 75.0f) {
11795                         turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11796                         float   delta_speed;
11797                         float range_speed = shipp->current_max_speed - leader_speed;
11798                         if (range_speed > 0.0f)
11799                                 delta_speed = dist_to_goal_2/500.0f * range_speed;
11800                         else
11801                                 delta_speed = shipp->current_max_speed - leader_speed;
11802                         if (dot_to_goal < 0.0f) {
11803                                 delta_speed = -delta_speed;
11804                                 if (-delta_speed > leader_speed/2)
11805                                         delta_speed = -leader_speed/2;
11806                         }
11807
11808                         if (leader_speed < 5.0f)
11809                                 if (delta_speed < 5.0f)
11810                                         delta_speed = 5.0f;
11811
11812                         float scale = dot_to_f2;
11813                         if (scale < 0.1f)
11814                                 scale = 0.0f;
11815                         else
11816                                 scale *= scale;
11817
11818                         set_accel_for_target_speed(Pl_objp, scale * (leader_speed + delta_speed));
11819                 } else {
11820                         //nprintf(("AI", "Dot = %7.3f\n", dot_to_goal));
11821
11822                         if (leader_speed < 5.0f) {
11823                                 //      Leader very slow.  If not close to goal point, get very close.  Note, keep trying to get close unless
11824                                 //      moving very slowly, else momentum can carry far away from goal.
11825
11826                                 if ((dist_to_goal > 10.0f) || ((Pl_objp->phys_info.speed > leader_speed + 2.5f) && (dot_to_goal > 0.5f))) {
11827                                         //nprintf(("MK", "(1) "));
11828                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11829                                         set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/10.0f);
11830                                 } else {
11831                                         if (Pl_objp->phys_info.speed < 0.5f) {
11832                                                 //nprintf(("MK", "(2) "));
11833                                                 turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11834                                         } else {
11835                                                 //nprintf(("MK", "(3) "));
11836                                         }
11837                                         set_accel_for_target_speed(Pl_objp, leader_speed);
11838                                 }
11839                                 //nprintf(("MK", "dist: %7.3f, dot: %6.3f, speed: %7.3f\n", dist_to_goal, dot_to_goal, Pl_objp->phys_info.speed));
11840                         } else if (dist_to_goal > 10.0f) {
11841                                 float   dv;
11842
11843                                 //future_goal_point_2;
11844
11845                                 turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11846
11847                                 if (dist_to_goal > 25.0f) {
11848                                         if (dot_to_goal < 0.3f)
11849                                                 dv = -0.1f;
11850                                         else
11851                                                 dv = dot_to_goal - 0.2f;
11852
11853                                         set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/5.0f * dv);
11854                                 } else {
11855                                         set_accel_for_target_speed(Pl_objp, leader_speed + 1.5f * dot_to_goal - 1.0f);
11856                                 }
11857                         } else {
11858                                 if (Pl_objp->phys_info.speed < 0.1f)
11859                                         turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11860                                 else
11861                                         turn_towards_point(Pl_objp, &future_goal_point_x, NULL, 0.0f);
11862                                 set_accel_for_target_speed(Pl_objp, 0.0f);
11863                         }
11864                 }
11865
11866         }
11867
11868         //      See how different this ship's bank is relative to wing leader
11869         float   up_dot = vm_vec_dot(&leader_objp->orient.v.uvec, &Pl_objp->orient.v.uvec);
11870         if (up_dot < 0.996f) {
11871                 vector  w_out;
11872                 matrix  new_orient;
11873                 vector  angular_accel;
11874
11875                 vm_vec_copy_scale(&angular_accel, &Pl_objp->phys_info.max_rotvel, 0.2f);
11876                 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);
11877
11878         //      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)));
11879                 Pl_objp->orient = new_orient;
11880                 Pl_objp->phys_info.rotvel = w_out;
11881         //      Pl_objp->phys_info.desired_rotvel = w_out;
11882         } else {
11883                 Pl_objp->phys_info.rotvel.xyz.z = 0.0f;
11884         }
11885
11886         return 0;
11887 }
11888
11889 //      Return index of object repairing object objnum.
11890 int find_repairing_objnum(int objnum)
11891 {
11892         object          *objp;
11893         ship                    *shipp;
11894         ship_info       *sip;
11895         ship_obj                *so;
11896
11897         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11898                 objp = &Objects[so->objnum];
11899
11900                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11901
11902                 shipp = &Ships[objp->instance];
11903                 sip = &Ship_info[shipp->ship_info_index];
11904
11905                 if (sip->flags & SIF_SUPPORT) {
11906                         ai_info *aip;
11907
11908                         aip = &Ai_info[shipp->ai_index];
11909
11910                         if (aip->goal_objnum == objnum) {
11911                                 return objp-Objects;
11912                         }
11913                 }
11914         }
11915
11916         return -1;
11917 }
11918
11919 //      If object *objp is being repaired, deal with it!
11920 void ai_do_repair_frame(object *objp, ai_info *aip, float frametime)
11921 {
11922         if (Ships[objp->instance].team == TEAM_TRAITOR) {
11923                 ai_abort_rearm_request(objp);
11924                 return;
11925         }
11926
11927         if (aip->ai_flags & (AIF_BEING_REPAIRED | AIF_AWAITING_REPAIR)) {
11928                 int     dock_objnum;
11929                 ai_info *repair_aip;
11930
11931                 dock_objnum = aip->dock_objnum; // find_repairing_objnum(objp-Objects);
11932                 //Assert(dock_objnum != -1);
11933                 if (dock_objnum == -1)
11934                         return;
11935                 if (Objects[dock_objnum].signature != aip->dock_signature) {
11936                         Int3();         //      Curious -- object numbers match, but signatures do not.
11937                                                         //      Must mean original repair ship died and was replaced by current ship.
11938                         return;
11939                 }
11940         
11941                 repair_aip = &Ai_info[Ships[Objects[dock_objnum].instance].ai_index];
11942                 //Assert(repair_aip->mode == AIM_DOCK);
11943
11944                 if (aip->ai_flags & AIF_BEING_REPAIRED) {
11945                         // Assert(repair_aip->submode == AIS_DOCK_4);
11946
11947                         //      Wait awhile into the mode to synchronize with sound effect.
11948                         if (Missiontime - repair_aip->submode_start_time > REARM_SOUND_DELAY) {
11949                                 int repaired;
11950
11951                                 repaired = ship_do_rearm_frame( objp, frametime );              // hook to do missile rearming
11952
11953                                 //      See if fully repaired.  If so, cause process to stop.
11954                                 if ( repaired && (repair_aip->submode == AIS_DOCK_4)) {
11955
11956                                         repair_aip->submode = AIS_UNDOCK_0;
11957                                         repair_aip->submode_start_time = Missiontime;
11958
11959                                         // if repairing player object -- tell him done with repair
11960                                         if ( !MULTIPLAYER_CLIENT ){
11961                                                 ai_do_objects_repairing_stuff( objp, &Objects[dock_objnum], REPAIR_INFO_COMPLETE );
11962                                         }
11963                                 }
11964                         }
11965                 } else if (aip->ai_flags & AIF_AWAITING_REPAIR) {
11966                         //      If this ship has been awaiting repair for 90+ seconds, abort.
11967                         if ( !MULTIPLAYER_CLIENT ) {
11968                                 if ((Game_mode & GM_MULTIPLAYER) || (objp != Player_obj)) {
11969                                         if ((repair_aip->goal_objnum == OBJ_INDEX(objp)) && (timestamp_elapsed(aip->abort_rearm_timestamp))) {
11970                                                 ai_abort_rearm_request(objp);
11971                                                 aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);
11972                                         }
11973                                 }
11974                         }
11975                 }
11976         } else {
11977                 // AL 11-24-97: If this is the player ship, ensure the repair sound isn't playing.  We need to
11978                 //              do this check, since this is a looping sound, and may continue on if rearm/repair
11979                 //              finishes abnormally once sound begins looping.
11980                 if ( objp == Player_obj ) {
11981                         player_stop_repair_sound();
11982                 }
11983         }
11984 }
11985
11986 //      Shell around dock_orient_and_approach to detect whether dock process should be aborted.
11987 //      obj1 is the ship performing the repair.
11988 //      obj2 is the ship being repaired.
11989 void call_doa(object *obj1, object *obj2, ship_info *sip1)
11990 {
11991         if (sip1->flags & SIF_SUPPORT) {
11992                 if (obj2->phys_info.speed > MAX_REPAIR_SPEED) {
11993
11994                         // call the ai_abort rearm request code
11995                         ai_abort_rearm_request( obj2 );
11996                 } else
11997                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11998         } else {
11999                 if (Ship_info[Ships[obj1->instance].ship_info_index].flags & SIF_CARGO)
12000                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
12001                 else if (Ship_info[Ships[obj2->instance].ship_info_index].flags & SIF_CARGO)
12002                         dock_orient_and_approach(obj2, obj1, DOA_DOCK_STAY);
12003                 else {
12004                         //mprintf(("Warning: Not sure, but making %s [%s] move to stay docked with %s [%s]\n",
12005                         //      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));
12006                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
12007
12008                 }
12009         }
12010
12011 }
12012
12013 //      Maybe launch a countermeasure.
12014 //      Also, detect a supposed homing missile that no longer exists.
12015 void ai_maybe_launch_cmeasure(object *objp, ai_info *aip)
12016 {
12017         float                   dist;
12018         ship_info       *sip;
12019         ship                    *shipp;
12020
12021         shipp = &Ships[objp->instance];
12022         sip = &Ship_info[shipp->ship_info_index];
12023
12024         if (!(sip->flags & (SIF_SMALL_SHIP | SIF_TRANSPORT)))
12025                 return;
12026
12027         if (!shipp->cmeasure_count)
12028                 return;
12029
12030         if ( !timestamp_elapsed(shipp->cmeasure_fire_stamp) )
12031                 return;
12032
12033         //      If not on player's team and Skill_level + ai_class is low, never fire a countermeasure.  The ship is too dumb.
12034         if (shipp->team != Player_ship->team) {
12035                 if (Game_skill_level + aip->ai_class < 4){
12036                         return;
12037                 }
12038         }
12039
12040         if ((aip->nearest_locked_object != -1) && (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)) {
12041                 object  *weapon_objp;
12042                 weapon  *weaponp;
12043                 weapon_info     *wip;
12044
12045                 weapon_objp = &Objects[aip->nearest_locked_object];
12046                 weaponp = &Weapons[weapon_objp->instance];
12047                 wip = &Weapon_info[weaponp->weapon_info_index];
12048
12049                 if ((dist = vm_vec_dist_quick(&objp->pos, &weapon_objp->pos)) < weapon_objp->phys_info.speed*2.0f) {
12050         
12051                         aip->nearest_locked_distance = dist;
12052                         //      Verify that this object is really homing on us.
12053                         object  *weapon_objp;
12054
12055                         weapon_objp = &Objects[aip->nearest_locked_object];
12056
12057                         float   fire_chance;
12058
12059                         //      For ships on player's team, have constant, average chance to fire.
12060                         //      For enemies, increasing chance with higher skill level.
12061                         if (shipp->team == Player_ship->team)
12062                                 fire_chance = Cmeasure_fire_chance[NUM_SKILL_LEVELS/2];
12063                         else
12064                                 fire_chance = Cmeasure_fire_chance[Game_skill_level];
12065
12066                         //      Decrease chance to fire at lower ai class.
12067                         fire_chance *= (float) aip->ai_class/Num_ai_classes;
12068
12069                         float r = frand();
12070                         if (fire_chance < r) {
12071                                 //nprintf(("AI", "Not firing countermeasure due to skill level: %7.3f < %7.3f\n", fire_chance, r));
12072                                 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.
12073                                 return;
12074                         }
12075
12076                         if (weapon_objp->type == OBJ_WEAPON) {
12077                                 if (weapon_objp->instance >= 0) {
12078                                         //nprintf(("AI", "Firing countermeasure at time t=%7.3f\n", f2fl(Missiontime)));
12079                                         ship_launch_countermeasure(objp);
12080                                         shipp->cmeasure_fire_stamp = timestamp(2*CMEASURE_WAIT);
12081                                         return;
12082                                 }
12083                         }
12084         
12085                 }
12086         }
12087
12088         return;
12089 }
12090
12091 //      --------------------------------------------------------------------------
12092 void ai_preprocess_ignore_objnum(object *objp, ai_info *aip)
12093 {
12094 //      if (aip->ignore_objnum == UNUSED_OBJNUM)
12095 //              return;
12096
12097         if (aip->ai_flags & AIF_TEMPORARY_IGNORE) {
12098                 if (timestamp_elapsed(aip->ignore_expire_timestamp)) {
12099                         aip->ignore_objnum = UNUSED_OBJNUM;
12100                 }
12101         }
12102
12103         if (is_ignore_object(aip, aip->goal_objnum)) {
12104                 aip->goal_objnum = -1;
12105                 // AL 12-11-97: If in STRAFE mode, we need to ensure that target_objnum is also
12106                 //              set to -1
12107                 if ( aip->mode == AIM_STRAFE ) {
12108                         aip->target_objnum = -1;
12109                 }
12110         }
12111
12112         if (is_ignore_object(aip, aip->target_objnum))
12113                 aip->target_objnum = -1;
12114 }
12115
12116 /*
12117 void ai_safety_circle_spot()
12118 {
12119         vector  goal_point;
12120         ship_info       *sip;
12121
12122         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12123
12124         goal_point = Ai_info[Ships[Pl_objp->instance].ai_index].goal_point;
12125         turn_towards_tangent(Pl_objp, &goal_point, 50.0f);
12126
12127         set_accel_for_target_speed(Pl_objp, sip->max_speed/4.0f);
12128
12129 //      float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
12130 //      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));
12131
12132 }
12133 */
12134
12135 #define CHASE_CIRCLE_DIST               100.0f
12136
12137 void ai_chase_circle(object *objp)
12138 {
12139         float           dist_to_goal;
12140         float           target_speed;
12141         vector  goal_point;
12142         ship_info       *sip;
12143         ai_info         *aip;
12144
12145         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12146
12147         target_speed = sip->max_speed/4.0f;
12148         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12149
12150         Assert(vm_vec_mag(&aip->goal_point) >= 0.0f);           //      Supposedly detects bogus vector
12151
12152         goal_point = aip->goal_point;
12153
12154         if (aip->ignore_objnum == UNUSED_OBJNUM) {
12155                 dist_to_goal = vm_vec_dist_quick(&aip->goal_point, &objp->pos);
12156
12157                 if (dist_to_goal > 2*CHASE_CIRCLE_DIST) {
12158                         vector  vec_to_goal;
12159                         //      Too far from circle goal, create a new goal point.
12160                         vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
12161                         vm_vec_scale_add(&aip->goal_point, &objp->pos, &vec_to_goal, CHASE_CIRCLE_DIST);
12162                 }
12163
12164                 goal_point = aip->goal_point;
12165         } else if (is_ignore_object(aip, aip->ignore_objnum)) {
12166                 object  *ignore_objp = &Objects[aip->ignore_objnum];
12167
12168                 vector  tvec1;
12169                 float           dist;
12170
12171                 dist = vm_vec_normalized_dir(&tvec1, &Pl_objp->pos, &ignore_objp->pos);
12172
12173                 if (dist < ignore_objp->radius*2 + 1500.0f) {
12174                         vm_vec_scale_add(&goal_point, &Pl_objp->pos, &tvec1, ignore_objp->radius*2 + 1400.0f);
12175                         if (dist < ignore_objp->radius*2 + 1300.0f)
12176                                 target_speed = sip->max_speed * (1.25f - dist/(ignore_objp->radius*2 + 1500.0f));
12177                 }
12178         }
12179
12180         Assert(vm_vec_mag(&aip->goal_point) >= 0.0f);           //      Supposedly detects bogus vector
12181
12182         turn_towards_tangent(Pl_objp, &goal_point, 10*objp->radius + 200.0f);
12183
12184         set_accel_for_target_speed(Pl_objp, target_speed);
12185
12186 }
12187
12188 #define SHIELD_BALANCE_RATE     0.2f            //      0.1f -> takes 10 seconds to equalize shield.
12189
12190 //      Transfer shield energy to most recently hit section from others.
12191 void ai_transfer_shield(object *objp, int quadrant_num)
12192 {
12193         int     i;
12194         float   transfer_amount;
12195         float   transfer_delta;
12196         ship_info       *sip;
12197         float   max_quadrant_strength;
12198
12199         sip = &Ship_info[Ships[objp->instance].ship_info_index];
12200         max_quadrant_strength = sip->shields/MAX_SHIELD_SECTIONS;
12201
12202         transfer_amount = 0.0f;
12203         transfer_delta = (SHIELD_BALANCE_RATE/2) * max_quadrant_strength;
12204
12205         if (objp->shields[quadrant_num] + (MAX_SHIELD_SECTIONS-1)*transfer_delta > max_quadrant_strength)
12206                 transfer_delta = (max_quadrant_strength - objp->shields[quadrant_num])/(MAX_SHIELD_SECTIONS-1);
12207
12208         for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12209                 if (i != quadrant_num) {
12210                         if (objp->shields[i] >= transfer_delta) {
12211                                 objp->shields[i] -= transfer_delta;
12212                                 transfer_amount += transfer_delta;
12213                         } else {
12214                                 transfer_amount += objp->shields[i];
12215                                 objp->shields[i] = 0.0f;
12216                         }
12217                 }
12218
12219         objp->shields[quadrant_num] += transfer_amount;
12220 }
12221
12222 void ai_balance_shield(object *objp)
12223 {
12224         int     i;
12225         float   shield_strength_avg;
12226         float   delta;
12227
12228
12229         shield_strength_avg = get_shield_strength(objp)/MAX_SHIELD_SECTIONS;
12230
12231         delta = SHIELD_BALANCE_RATE * shield_strength_avg;
12232
12233         for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12234                 if (objp->shields[i] < shield_strength_avg) {
12235                         add_shield_strength(objp, delta);
12236                         if (objp->shields[i] > shield_strength_avg)
12237                                 objp->shields[i] = shield_strength_avg;
12238                 } else {
12239                         add_shield_strength(objp, -delta);
12240                         if (objp->shields[i] < shield_strength_avg)
12241                                 objp->shields[i] = shield_strength_avg;
12242                 }
12243 }
12244
12245 //      Manage the shield for this ship.
12246 //      Try to max out the side that was most recently hit.
12247 void ai_manage_shield(object *objp, ai_info *aip)
12248 {
12249         ship_info *sip;
12250
12251         sip = &Ship_info[Ships[objp->instance].ship_info_index];
12252
12253         if (timestamp_elapsed(aip->shield_manage_timestamp)) {
12254                 float           delay;
12255
12256                 //      Scale time until next manage shield based on Skill_level.
12257                 //      Ships on player's team are treated as if Skill_level is average.
12258                 if (Ships[objp->instance].team != Player_ship->team){
12259                         delay = Shield_manage_delays[Game_skill_level];
12260                 } else {
12261                         delay = Shield_manage_delays[NUM_SKILL_LEVELS/2];
12262                 }
12263
12264                 //      Scale between 1x and 3x based on ai_class
12265                 delay = delay + delay * (float) (3*(Num_ai_classes - aip->ai_class - 1) / (Num_ai_classes - 1));
12266                 aip->shield_manage_timestamp = timestamp((int) (delay * 1000.0f));
12267
12268                 if (sip->flags & SIF_SMALL_SHIP) {
12269                         if (Missiontime - aip->last_hit_time < F1_0*10)
12270                                 ai_transfer_shield(objp, aip->last_hit_quadrant);
12271                         else
12272                                 ai_balance_shield(objp);
12273                 }
12274
12275                 // 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]));
12276         }
12277 }
12278
12279 //      See if object *objp should evade an incoming missile.
12280 //      *aip is the ai_info pointer within *objp.
12281 void ai_maybe_evade_locked_missile(object *objp, ai_info *aip)
12282 {
12283         ship                    *shipp;
12284         ship_info       *sip;
12285
12286         shipp = &Ships[objp->instance];
12287         sip = &Ship_info[shipp->ship_info_index];
12288
12289         //      Only small ships evade an incoming missile.  Why would a capital ship try to swerve?
12290         if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12291                 return;
12292         }
12293
12294         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
12295                 return;
12296         }
12297
12298         if (aip->nearest_locked_object != -1) {
12299                 object  *missile_objp;
12300
12301                 missile_objp = &Objects[aip->nearest_locked_object];
12302
12303                 if (Weapons[missile_objp->instance].homing_object != objp) {
12304                         //nprintf(("AI", "\nMissile lost home!"));
12305                         aip->nearest_locked_object = -1;
12306                         return;
12307                 }
12308
12309                 if ((missile_objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[missile_objp->instance].weapon_info_index].wi_flags & WIF_HOMING)) {
12310                         float dist = vm_vec_dist_quick(&missile_objp->pos, &objp->pos);
12311                         float dist2 = 4.0f  * vm_vec_mag_quick(&missile_objp->phys_info.vel);                   
12312                         if (dist < dist2) {
12313                                 switch (aip->mode) {
12314                                 //      If in AIM_STRAFE mode, don't evade if parent of weapon is targeted ship.
12315                                 case AIM_STRAFE:
12316                                         if ((missile_objp->parent != -1) && (missile_objp->parent == aip->target_objnum)) {
12317                                                 ;
12318                                         } else {
12319                                                 ;               //      Alan -- If you want to handle incoming weapons from someone other than the ship
12320                                                                 //      the strafing ship is attacking, do it here.
12321                                         }
12322                                         break;
12323                                 case AIM_CHASE:
12324                                         //      Don't always go into evade weapon mode.  Usually, a countermeasure gets launched.
12325                                         // If low on countermeasures, more likely to try to evade.  If 8+, never evade due to low cmeasures.
12326                                         if (((((Missiontime >> 18) ^ OBJ_INDEX(objp)) & 3) == 0) || 
12327                                                 (objp->phys_info.speed < 40.0f) ||
12328                                                 (frand() < 1.0f - (float) shipp->cmeasure_count/8.0f)) {
12329                                                 if (aip->submode != SM_ATTACK_FOREVER) {        //      SM_ATTACK_FOREVER means engines blown.
12330                                                         aip->submode = SM_EVADE_WEAPON;
12331                                                         aip->submode_start_time = Missiontime;
12332                                                 }
12333                                         }
12334                                         break;
12335                                 case AIM_DOCK:  //      Ships in dock mode can evade iif they are not currently repairing or docked.
12336                                         if (aip->ai_flags & (AIF_REPAIRING | AIF_DOCKED))
12337                                                 break;
12338                                 case AIM_GUARD:
12339                                         //      If in guard mode and far away from guard object, don't pursue guy that hit me.
12340                                         if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12341                                                 if (vm_vec_dist_quick(&objp->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12342                                                         return;
12343                                                 }
12344                                         }
12345                                 case AIM_EVADE:
12346                                 case AIM_GET_BEHIND:
12347                                 case AIM_STAY_NEAR:
12348                                 case AIM_STILL:
12349                                 case AIM_AVOID:
12350                                 case AIM_WAYPOINTS:
12351                                 case AIM_NONE:
12352                                 case AIM_BIGSHIP:
12353                                 case AIM_PATH:
12354                                 case AIM_BE_REARMED:
12355                                 case AIM_SAFETY:
12356                                 case AIM_BAY_EMERGE:
12357                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12358                                         aip->previous_mode = aip->mode;
12359                                         aip->previous_submode = aip->submode;
12360                                         aip->mode = AIM_EVADE_WEAPON;
12361                                         aip->submode = -1;
12362                                         aip->submode_start_time = Missiontime;
12363                                         aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Max time to evade.
12364                                         //nprintf(("AI", "%s Evade weapon in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
12365                                         break;
12366                                 case AIM_EVADE_WEAPON:          //      Note: We don't want to change mode on another evasion, or previous_mode will get bashed.
12367                                 case AIM_PLAY_DEAD:
12368                                 case AIM_BAY_DEPART:
12369                                 case AIM_SENTRYGUN:
12370                                         break;
12371                                 case AIM_WARP_OUT:
12372                                         break;
12373                                 default:
12374                                         Int3();                 //      Hey, what mode is it?
12375                                         break;
12376                                 }
12377                         }
12378                 } else {
12379                         aip->nearest_locked_object = -1;
12380                 }
12381         }
12382 }
12383
12384 //      Maybe evade a dumbfire weapon that was fired when Pl_objp was targeted.
12385 //      Have an 80% chance of evading in a second
12386 void maybe_evade_dumbfire_weapon(ai_info *aip)
12387 {
12388         //      Only small ships evade an incoming missile.  Why would a capital ship try to swerve?
12389         if (!(Ship_info[Ships[Pl_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12390                 return;
12391         }
12392
12393         //      Make sure in a mode in which we evade dumbfire weapons.
12394         switch (aip->mode) {
12395         case AIM_CHASE:
12396                 if (aip->submode == SM_ATTACK_FOREVER) {
12397                         return;
12398                 }
12399         case AIM_GUARD:
12400                 //      If in guard mode and far away from guard object, don't pursue guy that hit me.
12401                 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12402                         if (vm_vec_dist_quick(&Objects[Ships[aip->shipnum].objnum].pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12403                                 return;
12404                         }
12405                 }
12406         case AIM_STILL:
12407         case AIM_STAY_NEAR:
12408         case AIM_EVADE:
12409         case AIM_GET_BEHIND:
12410         case AIM_AVOID:
12411         case AIM_PATH:
12412         case AIM_NONE:
12413         case AIM_WAYPOINTS:
12414         case AIM_SAFETY:
12415                 break;
12416         case AIM_STRAFE:
12417         case AIM_BIGSHIP:
12418         case AIM_DOCK:
12419         case AIM_PLAY_DEAD:
12420         case AIM_EVADE_WEAPON:
12421         case AIM_BAY_EMERGE:
12422         case AIM_BAY_DEPART:
12423         case AIM_SENTRYGUN:
12424         case AIM_WARP_OUT:
12425                 return;
12426         default:
12427                 Int3(); //      Bogus mode!
12428                 return;
12429         }
12430
12431         if (is_instructor(&Objects[Ships[aip->shipnum].objnum]))
12432                 return; //      Instructor doesn't evade.
12433
12434         float t = ai_endangered_by_weapon(aip);
12435         if ((t > 0.0f) && (t < 1.0f)) {
12436         // Check if this weapon is from a large ship Pl_objp is attacking... if so, enter strafe mode
12437                 if ( ai_big_maybe_enter_strafe_mode(Pl_objp, aip->danger_weapon_objnum) ) {
12438                         return;
12439                 }
12440
12441                 switch (aip->mode) {
12442                 case AIM_CHASE:
12443                         switch (aip->submode) {
12444                         case SM_EVADE:
12445                         case SM_ATTACK_FOREVER:
12446                         case SM_AVOID:
12447                         case SM_GET_AWAY:
12448                         case SM_EVADE_WEAPON:
12449                                 break;
12450                         default:
12451                                 if (ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
12452                                         //mprintf(("Ship %s entered super mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12453                                         aip->submode = SM_SUPER_ATTACK;
12454                                         aip->submode_start_time = Missiontime;
12455                                         aip->last_attack_time = Missiontime;
12456                                 } else {
12457                                         //mprintf(("Ship %s entered dumbfire evade mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12458                                         aip->submode = SM_EVADE_WEAPON;
12459                                         aip->submode_start_time = Missiontime;
12460                                 }
12461                                 break;
12462                         }
12463                         break;
12464                 case AIM_GUARD:
12465                 case AIM_STILL:
12466                 case AIM_STAY_NEAR:
12467                 case AIM_EVADE:
12468                 case AIM_GET_BEHIND:
12469                 case AIM_AVOID:
12470                 case AIM_PATH:
12471                 case AIM_NONE:
12472                 case AIM_WAYPOINTS:
12473                 case AIM_SAFETY:
12474                         if (!(aip->ai_flags & (AIF_NO_DYNAMIC | AIF_KAMIKAZE)) && (Ship_info[Ships[aip->shipnum].ship_info_index].flags & SIF_SMALL_SHIP)) {
12475                                 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12476                                 aip->previous_mode = aip->mode;
12477                                 aip->previous_submode = aip->submode;
12478                                 aip->mode = AIM_EVADE_WEAPON;
12479                                 aip->submode = -1;
12480                                 aip->submode_start_time = Missiontime;
12481                                 aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Evade for up to five seconds.
12482                         }
12483                         break;
12484                 case AIM_STRAFE:
12485                 case AIM_BIGSHIP:
12486                 case AIM_DOCK:
12487                 case AIM_PLAY_DEAD:
12488                 case AIM_EVADE_WEAPON:
12489                 case AIM_BAY_EMERGE:
12490                 case AIM_BAY_DEPART:
12491                 case AIM_SENTRYGUN:
12492                         break;
12493                 default:
12494                         Int3(); //      Bogus mode!
12495                 }
12496         }
12497 }
12498
12499 // determine what path to use when emerging from a fighter bay
12500 // input:       pl_objp =>      pointer to object for ship that is arriving
12501 //                              pos             =>      output parameter, it is the starting world pos for path choosen
12502 //                              v.fvec          =>      output parameter, this is the forward vector that ship has when arriving
12503 //
12504 // exit:                -1              =>      path could not be located
12505 //                               0              => success
12506 int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, vector *pos, vector *fvec)
12507 {
12508         int                     path_index, sb_path_index;
12509         ship                    *parent_sp = NULL;
12510         polymodel       *pm;
12511         ai_info         *aip;
12512         ship_bay                *sb;
12513         pnode                   *pnp;
12514         vector          *next_point;
12515
12516         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12517
12518         if ( parent_objnum == -1 ) {
12519                 Int3();
12520                 return -1;
12521         }
12522
12523         parent_sp = &Ships[Objects[parent_objnum].instance];
12524
12525         Assert(parent_sp != NULL);
12526         pm = model_get( parent_sp->modelnum );
12527         sb = pm->ship_bay;
12528
12529         if ( sb == NULL ) 
12530                 return -1;
12531
12532         if ( sb->num_paths <= 0 ) 
12533                 return -1;
12534
12535         // try to find a bay path that is not taken
12536         path_index = -1;
12537         sb_path_index = Ai_last_arrive_path++;
12538
12539         if ( sb_path_index >= sb->num_paths ) {
12540                 sb_path_index=0;
12541                 Ai_last_arrive_path=0;
12542         }
12543
12544         path_index = sb->paths[sb_path_index];
12545         if ( path_index == -1 ) 
12546                 return -1;
12547
12548         // create the path for pl_objp to follow
12549         create_model_exit_path(pl_objp, &Objects[parent_objnum], path_index, pm->paths[path_index].nverts);
12550         
12551         // Set this flag, so we don't bother recreating the path... we won't need to update the path
12552         // that has just been created.
12553 //      aip->ai_flags |= AIF_USE_STATIC_PATH;
12554
12555         // now return to the caller what the starting world pos and starting fvec for the ship will be
12556         Assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
12557         pnp = &Path_points[aip->path_start];
12558         *pos = pnp->pos;
12559
12560         // calc the forward vector using the starting two points of the path
12561         pnp = &Path_points[aip->path_start+1];
12562         next_point = &pnp->pos;
12563         vm_vec_normalized_dir(fvec, next_point, pos);
12564
12565         // record the parent objnum, since we'll need it once we're done with following the path
12566         aip->goal_objnum = parent_objnum;
12567         aip->goal_signature = Objects[parent_objnum].signature;
12568         aip->mode = AIM_BAY_EMERGE;
12569         aip->submode_start_time = Missiontime;
12570
12571         // set up starting vel
12572         vector vel;
12573         float speed;
12574         speed = Ship_info[Ships[pl_objp->instance].ship_info_index].max_speed;
12575         vel = *fvec;
12576         vm_vec_scale( &vel, speed );
12577         pl_objp->phys_info.vel = vel;
12578         pl_objp->phys_info.desired_vel = vel;
12579         pl_objp->phys_info.prev_ramp_vel.xyz.x = 0.0f;
12580         pl_objp->phys_info.prev_ramp_vel.xyz.y = 0.0f;
12581         pl_objp->phys_info.prev_ramp_vel.xyz.z = speed;
12582         pl_objp->phys_info.forward_thrust = 0.0f;               // How much the forward thruster is applied.  0-1.
12583
12584         return 0;       
12585 }
12586
12587 // clean up path data used for emerging from a fighter bay
12588 void ai_emerge_bay_path_cleanup(ai_info *aip)
12589 {
12590         aip->path_start = -1;
12591         aip->path_cur = -1;
12592         aip->path_length = 0;
12593         aip->mode = AIM_NONE;
12594 }
12595
12596 // handler for AIM_BAY_EMERGE
12597 void ai_bay_emerge()
12598 {
12599         ai_info *aip;
12600         int             parent_died=0;
12601
12602         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12603
12604         // if no path to follow, leave this mode
12605         if ( aip->path_start < 0 ) {
12606                 aip->mode = AIM_NONE;
12607                 return;
12608         }
12609
12610         // ensure parent ship is still alive
12611         if ( aip->goal_objnum < 0 ) {
12612                 parent_died=1;
12613         } 
12614         if ( !parent_died ) {
12615                 if ( Objects[aip->goal_objnum].signature != aip->goal_signature ) {
12616                         parent_died=1;
12617                 }
12618         }
12619
12620         if ( !parent_died ) {
12621                 Assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
12622                 if ( Ships[Objects[aip->goal_objnum].instance].flags & SF_DYING ) {
12623                         parent_died = 1;
12624                 }
12625         }
12626
12627         if ( parent_died ) {
12628                 ai_emerge_bay_path_cleanup(aip);
12629                 return;
12630         }
12631
12632         // follow the path to the final point
12633         ai_path();
12634
12635         // New test: must have been in AI_EMERGE mode for at least 10 seconds, and be a minimum distance from the start point
12636         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)) {
12637                 // erase path
12638                 ai_emerge_bay_path_cleanup(aip);
12639         }
12640
12641         // 2-25-99: Need this check to fix an assert for supercap ships... maybe we'll only do this check for supercaps 
12642         if (aip->path_cur > (aip->path_start+aip->path_length-1)) {
12643                 ai_emerge_bay_path_cleanup(aip);
12644         }       
12645 }
12646
12647 // Select the closest depart path
12648 //
12649 //      input:  aip     =>              ai info pointer to ship seeking to depart
12650 //                              pm              =>              pointer to polymodel for the ship contining the ship bay/depart paths
12651 //
12652 // exit:                >=0     =>              ship bay path index for depart path (ie index into sb->paths[])
12653 //                              -1              =>              no path could be found
12654 //
12655 // NOTE: this function should only be used for calculating closest depart paths for ai mode
12656 //                      AI_BAY_DEPART.  It tries to find the closest path that isn't already in use
12657 int ai_find_closest_depart_path(ai_info *aip, polymodel *pm)
12658 {
12659         int                     i, j, best_path, best_free_path;
12660         float                   dist, min_dist, min_free_dist;
12661         vector          *source;
12662         model_path      *mp;
12663         ship_bay                *sb;
12664
12665         sb = pm->ship_bay;
12666
12667         best_free_path = best_path = -1;
12668         min_free_dist = min_dist = 1e20f;
12669         Assert(aip->shipnum >= 0);
12670         source = &Objects[Ships[aip->shipnum].objnum].pos;
12671
12672         for ( i = 0; i < sb->num_paths; i++ ) {
12673
12674
12675                 mp = &pm->paths[sb->paths[i]];
12676                 for ( j = 0; j < mp->nverts; j++ ) {
12677                         dist = vm_vec_dist_squared(source, &mp->verts[j].pos);
12678
12679                         if ( dist < min_dist ) {
12680                                 min_dist = dist;
12681                                 best_path = i;
12682                         }
12683
12684                         // If this is a free path
12685                         if ( !(sb->depart_flags & (1<<i)) ) {
12686                                 if ( dist < min_free_dist ) {
12687                                         min_free_dist = dist;
12688                                         best_free_path = i;
12689                                 }
12690                         }
12691                 }
12692         }
12693
12694         if ( best_free_path >= 0 ) {
12695                 return best_free_path;          
12696         }
12697
12698         return best_path;
12699 }
12700
12701 // determine what path to use when trying to depart to a fighter bay
12702 // NOTE: this should be called when AIM_BAY_DEPART mode is set
12703 //
12704 // input:       pl_objp =>      pointer to object for ship that is departing
12705 //
12706 // exit:                -1      =>      could not find depart path
12707 //                              0       => found depart path
12708 int ai_acquire_depart_path(object *pl_objp, int parent_objnum)
12709 {
12710         int                     objnum, path_index;
12711         polymodel       *pm;
12712         ai_info         *aip;
12713         ship                    *sp;
12714         ship_bay                *sb;
12715
12716         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12717
12718         if ( parent_objnum == -1 ) {
12719                 ship_obj        *so;
12720
12721                 // for now just locate a captial ship on the same team:
12722                 so = GET_FIRST(&Ship_obj_list);
12723                 objnum = -1;
12724                 while(so != END_OF_LIST(&Ship_obj_list)){
12725                         sp = &Ships[Objects[so->objnum].instance];
12726                         if ( (Ship_info[sp->ship_info_index].flags & (SIF_HUGE_SHIP)) && (sp->team == Ships[pl_objp->instance].team) ) {
12727                                 objnum = so->objnum;
12728                                 break;
12729                         }
12730                         so = GET_NEXT(so);
12731                 } 
12732         } else {
12733                 objnum = parent_objnum;
12734         }
12735
12736         aip->path_start = -1;
12737
12738         if ( objnum == -1 )
12739                 return -1;
12740
12741         pm = model_get( Ships[Objects[objnum].instance].modelnum );
12742         sb = pm->ship_bay;
12743
12744         if ( sb == NULL ) 
12745                 return -1;
12746         if ( sb->num_paths <= 0 ) 
12747                 return -1;
12748
12749 /*
12750         
12751         path_index = -1;
12752         for ( i = 0; i < sb->num_paths; i++ ) {
12753                 if ( !(sb->depart_flags & (1<<i)) ) {
12754                         sb->depart_flags |= (1<<i);
12755                         path_index = sb->paths[i];
12756                         aip->submode_parm0 = i;                 // use mode-specific parameter to record ship bay path index
12757                         break;
12758                 }
12759         }
12760 */
12761         
12762         // take the closest path we can find
12763         int ship_bay_path;
12764         ship_bay_path = ai_find_closest_depart_path(aip, pm);
12765         path_index = sb->paths[ship_bay_path];
12766         aip->submode_parm0 = ship_bay_path;
12767         sb->depart_flags |= (1<<ship_bay_path);
12768
12769         if ( path_index == -1 ) {
12770                 return -1;
12771         }
12772
12773         Assert(pm->n_paths > path_index);
12774         ai_find_path(pl_objp, objnum, path_index, 0);
12775
12776         // Set this flag, so we don't bother recreating the path... we won't need to update the path
12777         // that has just been created.
12778         aip->ai_flags &= ~AIF_USE_STATIC_PATH;
12779
12780         aip->goal_objnum = objnum;
12781         aip->goal_signature = Objects[objnum].signature;
12782         aip->mode = AIM_BAY_DEPART;
12783
12784         Ships[pl_objp->instance].flags |= SF_DEPART_DOCKBAY;
12785         return 0;
12786 }
12787
12788 // handler for AIM_BAY_DEPART
12789 void ai_bay_depart()
12790 {
12791         ai_info *aip;
12792
12793         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12794
12795         // if no path to follow, leave this mode
12796         if ( aip->path_start < 0 ) {
12797                 aip->mode = AIM_NONE;
12798                 return;
12799         }
12800
12801         // check if parent ship still exists, if not abort depart 
12802         if ( aip->goal_signature != Objects[aip->goal_objnum].signature ) {
12803                 aip->mode = AIM_NONE;
12804                 return;
12805         }
12806
12807         // follow the path to the final point
12808         ai_path();
12809
12810         // if the final point is reached, let default AI take over
12811         if ( aip->path_cur >= (aip->path_start+aip->path_length) ) {
12812                 polymodel       *pm;
12813                 ship_bay                *sb;
12814
12815                 pm = model_get( Ships[Objects[aip->goal_objnum].instance].modelnum );
12816                 sb = pm->ship_bay;
12817                 if ( sb != NULL ) {
12818                         sb->depart_flags &= ~(1<<aip->submode_parm0);
12819                 }
12820
12821                 // make ship disappear
12822                 Pl_objp->flags |= OF_SHOULD_BE_DEAD;
12823                 ship_departed( Pl_objp->instance );
12824
12825                 // clean up path stuff
12826                 aip->path_start = -1;
12827                 aip->path_cur = -1;
12828                 aip->path_length = 0;
12829                 aip->mode = AIM_NONE;
12830         }
12831 }
12832
12833 // Handler for AIM_SENTRYGUN.  This AI mode is for sentry guns only (ie floating turrets).
12834 void ai_sentrygun()
12835 {
12836         // Nothing to do here.  Turret firing is handled via process_subobjects().
12837         // If you want the sentry guns to do anything beyond firing their turrets at enemies, add it here!
12838 }
12839
12840 //      --------------------------------------------------------------------------
12841 //      Execute behavior given by aip->mode.
12842 void ai_execute_behavior(ai_info *aip)
12843 {
12844         switch (aip->mode) {
12845         case AIM_CHASE:
12846                 if (En_objp) {
12847                         ai_chase();
12848                 } else if (aip->submode == SM_EVADE_WEAPON) {
12849                         evade_weapon();
12850                         // maybe reset submode
12851                         if (aip->danger_weapon_objnum == -1) {
12852                                 aip->submode = SM_ATTACK;
12853                                 aip->submode_start_time = Missiontime;
12854                                 aip->last_attack_time = Missiontime;
12855                         }
12856                 } else {
12857                         //      Don't circle if this is the instructor.
12858                         ship    *shipp = &Ships[aip->shipnum];
12859                         ship_info       *sip = &Ship_info[shipp->ship_info_index];
12860
12861                         if (strnicmp(shipp->ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME))) {
12862                                 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
12863                                         aip->mode = AIM_NONE;
12864                                 } else {
12865                                         ai_chase_circle(Pl_objp);
12866                                 }
12867                         }
12868                 }
12869                 break;
12870         case AIM_EVADE:
12871                 if (En_objp) {
12872                         ai_evade();
12873                 } else {
12874                         vector  tvec;
12875                         vm_vec_scale_add(&tvec, &Pl_objp->pos, &Pl_objp->orient.v.rvec, 100.0f);
12876                         turn_towards_point(Pl_objp, &tvec, NULL, 0.0f);
12877                         accelerate_ship(aip, 0.5f);
12878                 }
12879                 break;
12880         case AIM_STILL:
12881                 ai_still();
12882                 break;
12883         case AIM_STAY_NEAR:
12884                 ai_stay_near();
12885                 break;
12886         case AIM_GUARD:
12887                 ai_guard();
12888                 break;
12889         case AIM_WAYPOINTS:
12890                 ai_waypoints();
12891                 break;
12892         case AIM_DOCK:
12893                 ai_dock();
12894                 break;
12895         case AIM_NONE:
12896                 // ai_formation();
12897                 break;
12898         case AIM_BIGSHIP:
12899                 ai_big_ship(Pl_objp);
12900                 break;
12901         case AIM_PATH: {
12902                 int path_num;
12903                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], 0);
12904                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
12905                 ai_path();
12906                 break;
12907         }
12908         case AIM_SAFETY:
12909                 ai_safety();
12910                 break;
12911         case AIM_EVADE_WEAPON:
12912                 evade_weapon();
12913                 break;
12914         case AIM_STRAFE:
12915                 if (En_objp) {
12916                         Assert(En_objp->type == OBJ_SHIP);
12917                         ai_big_strafe();        // strafe a big ship
12918                 } else {
12919                         aip->mode = AIM_NONE;
12920                 }
12921                 break;
12922         case AIM_BAY_EMERGE:
12923                 ai_bay_emerge();
12924                 break;
12925         case AIM_BAY_DEPART:
12926                 ai_bay_depart();
12927                 break;
12928         case AIM_SENTRYGUN:
12929                 ai_sentrygun();
12930                 break;
12931         case AIM_WARP_OUT:
12932                 break;          //      Note, handled directly from ai_frame().
12933         default:
12934                 Int3();         //      This should never happen -- MK, 5/12/97 
12935                 break;
12936         }
12937
12938         if ( !(ship_get_SIF(aip->shipnum) & SIF_NOT_FLYABLE) ) {
12939                 maybe_evade_dumbfire_weapon(aip);
12940         }
12941 }
12942
12943 //      Auxiliary function for maybe_request_support.
12944 //      Return 1 if subsystem "type" is worthy of repair, else return 0.
12945 //      Since subsystems cannot be repaired if they are at 0 strength, don't return 1 if subsystem is dead.
12946 int mrs_subsystem(ship *shipp, int type)
12947 {
12948         float   t;
12949
12950         t = ship_get_subsystem_strength(shipp, type);
12951
12952         if (t > 0.0f) {
12953                 return (int) ((1.0f - t) * 3);
12954         } else {
12955                 return 3;
12956         }
12957 }
12958
12959 //      Return number of ships on *objp's team that are currently rearming.
12960 int num_allies_rearming(object *objp)
12961 {
12962         ship_obj        *so;
12963         int             team;
12964         int             count = 0;
12965
12966         team = Ships[objp->instance].team;
12967
12968         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
12969                 object  *A;
12970                 
12971                 Assert (so->objnum != -1);
12972                 A = &Objects[so->objnum];
12973
12974                 if (Ships[A->instance].team == team) {
12975                         if (Ai_info[Ships[A->instance].ai_index].ai_flags & (AIF_REPAIRING | AIF_AWAITING_REPAIR)) {
12976                                 count++;
12977                         }
12978                 }
12979         }
12980
12981         return count;
12982 }
12983
12984
12985 //      Maybe ship *objp should request support (rearm/repair).
12986 //      If it does, return TRUE, else return FALSE.
12987 int maybe_request_support(object *objp)
12988 {
12989         ship_info       *sip;
12990         ship                    *shipp;
12991         ai_info         *aip;
12992         int                     desire;
12993
12994         Assert(objp->type == OBJ_SHIP);
12995         shipp = &Ships[objp->instance];
12996         aip = &Ai_info[shipp->ai_index];
12997         sip = &Ship_info[shipp->ship_info_index];
12998
12999         if (!timestamp_elapsed(aip->next_rearm_request_timestamp))
13000                 return 0;
13001
13002         //      Only fighters and bombers request support.
13003         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER)))
13004                 return 0;
13005
13006         //      A ship that is currently awaiting does not need support!
13007         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
13008                 return 0;
13009
13010         if (!is_support_allowed(objp))
13011                 return 0;
13012
13013         //if (shipp->team != TEAM_FRIENDLY)
13014         //      return 0;
13015
13016         //      Compute a desire value.
13017         //      Desire of 0 means no reason to request support.
13018         //      1 is slight, 2 more, etc.  Maximum is around 20.  Anything larger than 3 is pretty strong.
13019         desire = 0;
13020
13021         //      Set desire based on hull strength.
13022         //      No: We no longer repair hull, so this would cause repeated repair requests.
13023         //desire += 6 - (int) ((objp->hull_strength/sip->initial_hull_strength) * 6.0f);
13024
13025         //      Set desire based on key subsystems.
13026         desire += 2*mrs_subsystem(shipp, SUBSYSTEM_ENGINE);     //      Note, disabled engine forces repair request, regardless of nearby enemies.
13027         desire += mrs_subsystem(shipp, SUBSYSTEM_COMMUNICATION);
13028         desire += mrs_subsystem(shipp, SUBSYSTEM_WEAPONS);
13029         desire += mrs_subsystem(shipp, SUBSYSTEM_SENSORS);
13030
13031         //      Set desire based on percentage of secondary weapons.
13032         ship_weapon *swp = &shipp->weapons;
13033
13034         for ( int i = 0; i < swp->num_secondary_banks; i++ ) {
13035                 if (swp->secondary_bank_start_ammo[i] > 0) {
13036 //                      float r = (float) swp->secondary_bank_ammo[i]*Weapon_info[swp->secondary_bank_weapons[i]].cargo_size/swp->secondary_bank_capacity[i];
13037                         float r = (float) swp->secondary_bank_ammo[i]/swp->secondary_bank_start_ammo[i];
13038                         desire += (int) ((1.0f - r) * 3.0f);
13039                 }
13040         }
13041
13042         //      If no reason to repair, don't bother to see if it's safe to repair.
13043         if (desire == 0){
13044                 return 0;
13045         }
13046
13047         //      Compute danger threshold.
13048         //      Balance this with desire and maybe request support.
13049         if (ai_good_time_to_rearm( objp )) {
13050                 ai_issue_rearm_request(objp);
13051                 return 1;
13052         } else if (num_allies_rearming(objp) < 2) {
13053                 if (desire >= 8) {      //      guarantees disabled will cause repair request
13054                         ai_issue_rearm_request(objp);
13055                 } else if (desire >= 3) {               //      >= 3 means having a single subsystem fully blown will cause repair.
13056                         int     count;
13057                         int objnum = find_nearby_hostile(OBJ_INDEX(objp), get_enemy_team_mask(OBJ_INDEX(objp)), 2000.0f, &count);
13058
13059                         if ((objnum == -1) || (count < 2) || (vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos) > 3000.0f*count/desire)) {
13060                                 ai_issue_rearm_request(objp);
13061                                 return 1;
13062                         } else {
13063                                 //nprintf(("AI", "Would like to rearm, but enemy only %7.3f units away.\n", vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos)));
13064                         }
13065                 }
13066         }
13067
13068         return 0;
13069
13070 }
13071
13072 void ai_set_mode_warp_out(object *objp, ai_info *aip)
13073 {
13074         ai_abort_rearm_request(objp);
13075         if (aip->mode != AIM_WARP_OUT) {
13076                 aip->mode = AIM_WARP_OUT;
13077                 aip->submode = AIS_WARP_1;
13078         }
13079 }
13080
13081 //      Maybe warp ship out.
13082 //      Shivan and HoL fighter/bomber warp out if their weapons subsystems have been destroyed.
13083 void ai_maybe_warp_out(object *objp)
13084 {
13085         ship    *shipp;
13086
13087         // don't do anything if in a training mission.
13088         if ( The_mission.game_type & MISSION_TYPE_TRAINING )
13089                 return;
13090
13091         Assert(objp->type == OBJ_SHIP);
13092
13093         shipp = &Ships[objp->instance];
13094         ai_info *aip = &Ai_info[shipp->ai_index];
13095
13096         if (aip->mode == AIM_WARP_OUT)
13097                 return;
13098
13099         //      If a support ship with no goals and low hull, warp out.  Be sure that there are no pending goals
13100         // in the support ships ai_goal array.  Just process this ships goals.
13101         ship_info       *sip = &Ship_info[shipp->ship_info_index];
13102         if (sip->flags & SIF_SUPPORT) {
13103                 if ( timestamp_elapsed(aip->warp_out_timestamp) ) {
13104                         ai_process_mission_orders( OBJ_INDEX(objp), aip );
13105                         if ( (aip->dock_objnum == -1) && (objp->hull_strength/sip->initial_hull_strength < 0.25f) ) {
13106                                 ai_set_mode_warp_out(objp, aip);
13107                         }
13108                 }
13109         }
13110
13111         //      Friendly don't warp out, they'll eventually request support.
13112         if (shipp->team == TEAM_FRIENDLY)
13113                 return;
13114
13115         if (!(shipp->flags & SF_DEPARTING)) {
13116                 ship_info       *sip;
13117
13118                 sip = &Ship_info[shipp->ship_info_index];
13119                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
13120                         if (aip->warp_out_timestamp == 0) {
13121                                 //if (ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS) == 0.0f) {
13122                                 //      aip->warp_out_timestamp = timestamp(((myrand() % 10) + 10) * 1000);
13123                                 //}
13124                         } else if (timestamp_elapsed(aip->warp_out_timestamp)) {
13125                                 ai_set_mode_warp_out(objp, aip);
13126                         }
13127                 }
13128         }
13129 }
13130
13131 //      Warp this ship out.
13132 void ai_warp_out(object *objp)
13133 {
13134         // if dying, don't warp out.
13135         if (Ships[objp->instance].flags & SF_DYING) {
13136                 return;
13137         }
13138
13139         ai_info *aip;
13140
13141         aip = &Ai_info[Ships[objp->instance].ai_index];
13142
13143         switch (aip->submode) {
13144         case AIS_WARP_1:
13145                 aip->force_warp_time = timestamp(10*1000);      //      Try to avoid a collision for up to ten seconds.
13146                 aip->submode = AIS_WARP_2;
13147                 break;
13148         case AIS_WARP_2:                        //      Make sure won't collide with any object.
13149                 if (timestamp_elapsed(aip->force_warp_time) || !collide_predict_large_ship(objp, objp->radius*2.0f + 100.0f)) {
13150                         aip->submode = AIS_WARP_3;
13151
13152                         // maybe recalculate collision pairs.
13153                         if (ship_get_warp_speed(objp) > ship_get_max_speed(&Ships[objp->instance])) {
13154                                 // recalculate collision pairs
13155                                 OBJ_RECALC_PAIRS(objp); 
13156                         }
13157
13158                         aip->force_warp_time = timestamp(4*1000);               //      Try to attain target speed for up to 4 seconds.
13159                 } else {
13160                         vector  goal_point;
13161                         vm_vec_scale_add(&goal_point, &objp->pos, &objp->orient.v.uvec, 100.0f);
13162                         turn_towards_point(objp, &goal_point, NULL, 0.0f);
13163                         accelerate_ship(aip, 0.0f);
13164                 }
13165                 break;
13166         case AIS_WARP_3:
13167                 //      Rampup desired_vel in here from current to desired velocity and set PF_USE_VEL. (not sure this is the right flag)
13168                 //      desired velocity is computed in shipfx_calculate_warp_time().  See shipfx#572 for sample code.
13169                 float   speed, goal_speed;
13170                 float shipfx_calculate_warp_speed(object*);
13171                 goal_speed = shipfx_calculate_warp_speed(objp);
13172
13173                 // HUGE ships go immediately to AIS_WARP_4
13174                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_HUGE_SHIP) {
13175                         aip->submode = AIS_WARP_4;
13176                         break;
13177                 }
13178                 //compute_warpout_stuff(objp, &goal_speed, &warp_time, &warp_pos);
13179                 //goal_speed = 80.0f;
13180                 //set_accel_for_target_speed(objp, 40.0f);
13181                 // 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
13182                 speed = goal_speed * flFrametime + objp->phys_info.speed * (1.0f - flFrametime);
13183                 vm_vec_copy_scale(&objp->phys_info.vel, &objp->orient.v.fvec, speed);
13184                 objp->phys_info.desired_vel = objp->phys_info.vel;
13185                 // nprintf(("AI", "Frame %i, speed = %7.3f, goal = %7.3f\n", Framecount, vm_vec_mag_quick(&objp->phys_info.vel), goal_speed));
13186                 if (timestamp_elapsed(aip->force_warp_time) || (fl_abs(objp->phys_info.speed - goal_speed) < 2.0f))
13187                         aip->submode = AIS_WARP_4;
13188                 break;
13189         case AIS_WARP_4: {
13190                 shipfx_warpout_start(objp);
13191                 aip->submode = AIS_WARP_5;
13192                 break;
13193         }
13194         case AIS_WARP_5:
13195                 break;
13196         default:
13197                 Int3();         //      Illegal submode for warping out.
13198         }
13199 }
13200
13201 //      Return object index of weapon that could produce a shockwave that should be known about to *objp.
13202 //      Return nearest one.
13203 int ai_find_shockwave_weapon(object *objp, ai_info *aip)
13204 {
13205         missile_obj     *mo;
13206         float   nearest_dist = 999999.9f;
13207         int     nearest_index = -1;
13208
13209         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
13210                 object          *A;
13211                 weapon          *wp;
13212                 weapon_info     *wip;
13213         
13214                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
13215                 A = &Objects[mo->objnum];
13216
13217                 Assert(A->type == OBJ_WEAPON);
13218                 Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
13219                 wp = &Weapons[A->instance];
13220                 wip = &Weapon_info[wp->weapon_info_index];
13221                 Assert( wip->subtype == WP_MISSILE );
13222
13223                 if (wip->shockwave_speed > 0.0f) {
13224                         float   dist;
13225
13226                         dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13227                         if (dist < nearest_dist) {
13228                                 nearest_dist = dist;
13229                                 nearest_index = mo->objnum;
13230                         }
13231                 }
13232         }
13233
13234         return nearest_index;
13235
13236 }
13237
13238 #define EVADE_SHOCKWAVE_DAMAGE_THRESHOLD                100.0f
13239
13240 //      Tell all ships to avoid a big ship that is blowing up.
13241 //      Only avoid if shockwave is fairly large.
13242 //      OK to tell everyone to avoid.  If they're too far away, that gets cleaned up in the frame interval.
13243 void ai_announce_ship_dying(object *dying_objp)
13244 {
13245         float damage = ship_get_exp_damage(dying_objp);
13246         if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {
13247                 ship_obj        *so;
13248
13249                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13250                         if (Ship_info[Ships[Objects[so->objnum].instance].ship_info_index].flags & (SIF_SMALL_SHIP | SIF_FREIGHTER)) {
13251                                 ai_info *aip;
13252
13253                                 aip = &Ai_info[Ships[Objects[so->objnum].instance].ai_index];
13254
13255                                 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
13256                                         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_SHIP;
13257                                 }
13258                         }
13259                 }
13260         }
13261 }
13262
13263
13264 //      Return object index of weapon that could produce a shockwave that should be known about to *objp.
13265 //      Return nearest one.
13266 int ai_find_shockwave_ship(object *objp, ai_info *aip)
13267 {
13268         ship_obj        *so;
13269         float   nearest_dist = 999999.9f;
13270         int     nearest_index = -1;
13271
13272         for ( so = GET_NEXT(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13273                 object          *A;
13274                 ship                    *shipp;
13275         
13276                 Assert(so->objnum >= 0 && so->objnum < MAX_OBJECTS);
13277                 A = &Objects[so->objnum];
13278
13279                 Assert(A->type == OBJ_SHIP);
13280                 Assert((A->instance >= 0) && (A->instance < MAX_SHIPS));
13281                 shipp = &Ships[A->instance];
13282                 //      Only look at objects in the process of dying.
13283                 if (shipp->flags & SF_DYING) {
13284                         float damage = ship_get_exp_damage(objp);
13285
13286                         if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {               //      Only evade quite large blasts
13287                                 float   dist;
13288
13289                                 dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13290                                 if (dist < nearest_dist) {
13291                                         nearest_dist = dist;
13292                                         nearest_index = so->objnum;
13293                                 }
13294                         }
13295                 }
13296         }
13297
13298         return nearest_index;
13299
13300 }
13301
13302 int aas_1(object *objp, ai_info *aip, vector *safe_pos)
13303 {
13304         // MAKE SURE safe_pos DOES NOT TAKE US TOWARD THE A SHIP WE'RE ATTACKING.
13305         if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_WEAPON) {
13306                 //      If we don't currently know of a weapon to avoid, try to find one.
13307                 //      If we can't find one, then clear the bit so we don't keep coming here.
13308                 if (aip->shockwave_object == -1) {
13309                         int shockwave_weapon = ai_find_shockwave_weapon(objp, aip);
13310                         if (shockwave_weapon == -1) {
13311                                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13312                                 return 0;
13313                         } else {
13314                                 aip->shockwave_object = shockwave_weapon;
13315                         }
13316                 }
13317
13318                 //      OK, we have reason to believe we should avoid aip->shockwave_object.
13319                 Assert(aip->shockwave_object > -1);
13320                 object  *weapon_objp = &Objects[aip->shockwave_object];
13321                 if (weapon_objp->type != OBJ_WEAPON) {
13322                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13323                         aip->shockwave_object = -1;
13324                         return 0;
13325                 }
13326
13327                 weapon  *weaponp = &Weapons[weapon_objp->instance];
13328                 weapon_info     *wip = &Weapon_info[weaponp->weapon_info_index];
13329                 object *target_ship_obj = NULL;
13330
13331                 if (wip->shockwave_speed == 0.0f) {
13332                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13333                         aip->shockwave_object = -1;
13334                         return 0;
13335                 }
13336
13337                 float   danger_dist;
13338                 vector  expected_pos;           //      Position at which we expect the weapon to detonate.
13339                 int             pos_set = 0;
13340
13341                 danger_dist = wip->outer_radius;
13342                 //      Set predicted position of detonation.
13343                 //      If an aspect locked missile, assume it will detonate at the homing position.
13344                 //      If not, which is not possible in a default FreeSpace weapon, then predict it will detonate at some
13345                 //      time in the future, this time based on max lifetime and life left.
13346                 if (wip->wi_flags & WIF_HOMING_ASPECT) {
13347                         expected_pos = weaponp->homing_pos;
13348                         if (weaponp->homing_object && weaponp->homing_object->type == OBJ_SHIP) {
13349                                 target_ship_obj = weaponp->homing_object;
13350                         }
13351                         pos_set = 1;
13352                         if (IS_VEC_NULL(&weaponp->homing_pos)) {
13353                                 pos_set = 0;
13354                                 if (weaponp->target_num != -1) {
13355                                         if (Objects[weaponp->target_num].type == OBJ_SHIP) {
13356                                                 target_ship_obj = &Objects[weaponp->target_num];
13357                                                 expected_pos = target_ship_obj->pos;
13358                                                 pos_set = 1;
13359                                         }
13360                                 }
13361                         }
13362                 }
13363
13364                 if (!pos_set) {
13365                         float   time_scale;
13366
13367                         if (wip->lifetime - weaponp->lifeleft > 5.0f) {
13368                                 time_scale = 1.0f;
13369                         } else {
13370                                 time_scale = weaponp->lifeleft/2.0f;
13371                         }
13372
13373                         vm_vec_scale_add(&expected_pos, &weapon_objp->pos, &weapon_objp->orient.v.fvec, time_scale);
13374                 }
13375
13376                 //      See if too far away to care about shockwave.
13377                 if (vm_vec_dist_quick(&objp->pos, &expected_pos) > danger_dist*2.0f) {
13378                         //aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13379                         return 0;
13380                 } else {
13381                         // try to find a safe position
13382                         vector vec_from_exp;
13383                         float dir = 1.0f;
13384                         vm_vec_sub(&vec_from_exp, &objp->pos, &expected_pos);
13385                         float dot = vm_vec_dotprod(&vec_from_exp, &weapon_objp->orient.v.fvec);
13386                         if (dot > -30) {
13387                                 // if we're already on the other side of the explosion, don't try to fly behind it
13388                                 dir = -1.0f;
13389                         }
13390
13391                         //      Fly towards a point behind the weapon.
13392                         vm_vec_scale_add(safe_pos, &weapon_objp->pos, &weapon_objp->orient.v.fvec, -50000.0f*dir);
13393
13394                         // verify safe_pos will not make us collide with our target objnum, else try 2 other vecs
13395                         // don't bang your head, else go
13396 //                      int go_safe = FALSE;
13397                         int go_safe = TRUE;
13398 /*                      if (target_ship_obj) {
13399                                 if (pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius)) {
13400                                         // try up to 2 other random directions
13401                                         vector dir_vec, rand_vec;
13402                                         int idx;
13403                                         for (idx=0; idx<2; idx++) {
13404                                                 vm_vec_rand_vec_quick(&rand_vec);
13405                                                 vm_vec_scale_add(&dir_vec, &weapon_objp->orient.v.fvec, &rand_vec, 0.5f);
13406                                                 vm_vec_scale_add(safe_pos, &weapon_objp->pos, &dir_vec, -50000.0f*dir);
13407                                                 if ( !pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius) ) {
13408                                                         go_safe = TRUE;
13409                                                         break;
13410                                                 }
13411                                         }
13412                                 } else { // direct path is safe
13413                                         go_safe = TRUE;
13414                                 }
13415                         } else { // no target_obj_ship
13416                                 go_safe = TRUE;
13417                         } */
13418
13419                         if (go_safe) {
13420                                 return 1;
13421                         } else {
13422                                 // can't figure out a good way to go
13423                                 return 0;
13424                         }
13425                 }
13426         } else if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_SHIP) {
13427                 if (aip->shockwave_object == -1) {
13428                         int shockwave_ship = ai_find_shockwave_ship(objp, aip);
13429                         if (shockwave_ship == -1) {
13430                                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13431                                 return 0;
13432                         } else {
13433                                 aip->shockwave_object = shockwave_ship;
13434                         }
13435                 }
13436
13437                 Assert(aip->shockwave_object > -1);
13438                 object  *ship_objp = &Objects[aip->shockwave_object];
13439                 if (ship_objp == objp) {
13440                         aip->shockwave_object = -1;
13441                         return 0;
13442                 }
13443
13444                 if (ship_objp->type != OBJ_SHIP) {
13445                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13446                         return 0;
13447                 }
13448
13449                 //      Optimize note! Don't really have to normalize.  We only need a point away from the blowing-up ship.
13450                 vector safe_vec;
13451
13452                 vm_vec_normalized_dir(&safe_vec, &objp->pos, &ship_objp->pos);
13453                 vm_vec_scale_add(safe_pos, &ship_objp->pos, &safe_vec, 50000.0f);       //      Fly away from the ship.
13454
13455                 float outer_rad = ship_get_exp_outer_rad(ship_objp);
13456
13457                 if (vm_vec_dist_quick(&objp->pos, &ship_objp->pos) > outer_rad*1.5f) {
13458                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13459                         return 0;
13460                 }
13461
13462                 return 1;
13463
13464         } else {
13465                 Int3(); //      Illegal -- supposedly avoiding a shockwave, but neither ship nor weapon.  What is it!?
13466         }
13467
13468         return 0;
13469 }
13470
13471 /*
13472 int rct_done = 0;
13473
13474 void rand_chance_test()
13475 {
13476         int     i;
13477         float   frametime;
13478
13479         if (rct_done)
13480                 return;
13481
13482         rct_done = 1;
13483
13484         for (frametime=0.02f; frametime<0.25f; frametime *= 1.25f) {
13485                 float   chance;
13486
13487                 nprintf(("AI", "%6.4f: ", frametime));
13488                 for (chance=0.25f; chance<2.5f; chance += 0.25f) {
13489                         int count = 0;
13490
13491                         for (i=0; i<100.0f/frametime; i++) {
13492                                 if (rand_chance(frametime, chance))
13493                                         count++;
13494                         }
13495                         nprintf(("AI", "%3i ", count));
13496                 }
13497                 nprintf(("AI", "\n"));
13498         }
13499 }
13500 */
13501
13502 //      --------------------------------------------------------------------------
13503 //      Make object *objp avoid the nearest dangerous shockwave-producing weapon.
13504 //      If it looks like there is no valid shockwave-producing weapon then clear the AIF_AVOID_SHOCKWAVE_WEAPON bit in ai_flags and return.
13505 //      Return 1 if avoiding a shockwave, else return 0.
13506 int ai_avoid_shockwave(object *objp, ai_info *aip)
13507 {
13508         vector  safe_pos;
13509
13510         //rand_chance_test();
13511         // BIG|HUGE do not respond to shockwaves
13512         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
13513                 // don't come here again
13514                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE;
13515                 return 0;
13516         }
13517
13518         //      Don't all react right away.
13519         if (!(aip->ai_flags & AIF_AVOID_SHOCKWAVE_STARTED))
13520                 if (!rand_chance(flFrametime, (float) aip->ai_class/4.0f + 0.25f))      //      Chance to avoid in 1 second is 0.25 + ai_class/4
13521                         return 0;
13522
13523         if (!aas_1(objp, aip, &safe_pos)) {
13524                 aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13525                 return 0;
13526         }
13527
13528         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13529
13530         //      OK, evade the shockwave!
13531         turn_towards_point(objp, &safe_pos, NULL, 0.0f);
13532         vector  vec_to_safe_pos;
13533         float           dot_to_goal;
13534
13535         vm_vec_normalized_dir(&vec_to_safe_pos, &safe_pos, &objp->pos);
13536
13537         dot_to_goal = vm_vec_dot(&objp->orient.v.fvec, &vec_to_safe_pos);
13538         if (dot_to_goal < -0.5f)
13539                 accelerate_ship(aip, 0.3f);
13540         else {
13541                 accelerate_ship(aip, 1.0f + dot_to_goal);
13542                 if (dot_to_goal > 0.2f) {
13543                         if (!(objp->phys_info.flags & PF_AFTERBURNER_ON )) {
13544                                 afterburners_start(objp);
13545                                 aip->afterburner_stop_time = Missiontime + 2*F1_0;
13546                         }
13547                 }
13548         }
13549
13550         return 1;
13551 }
13552
13553 //      Awaiting repair.  Be useful.
13554 //      Probably fly towards incoming repair ship.
13555 //      Return true if this ship is close to being repaired, else return false.
13556 int ai_await_repair_frame(object *objp, ai_info *aip)
13557 {
13558         if (!(aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)))
13559                 return 0;
13560
13561         if (aip->dock_objnum == -1)
13562                 return 0;
13563
13564         ship    *shipp;
13565         ship_info       *sip;
13566
13567         shipp = &Ships[Objects[aip->dock_objnum].instance];
13568         sip = &Ship_info[shipp->ship_info_index];
13569
13570         aip->ai_flags &= ~AIF_FORMATION_OBJECT; //      Prevents endless rotation.
13571
13572         if (!(sip->flags & SIF_SUPPORT))
13573                 return 0;
13574
13575         vector  goal_point;
13576         object  *repair_objp;
13577
13578         repair_objp = &Objects[aip->dock_objnum];
13579
13580         if (Ships[repair_objp->instance].team == TEAM_TRAITOR) {
13581                 ai_abort_rearm_request(repair_objp);
13582                 return 0;
13583         }
13584
13585         vm_vec_scale_add(&goal_point, &repair_objp->pos, &repair_objp->orient.v.uvec, -50.0f);  //      Fly towards point below repair ship.
13586
13587         vector  vtr;
13588         float dist = vm_vec_normalized_dir(&vtr, &goal_point, &objp->pos);
13589         float dot = vm_vec_dot(&vtr, &objp->orient.v.fvec);
13590
13591         if (dist > 200.0f) {
13592                 //nprintf(("AI", "%s flying towards %s for repair, dist = %7.3f\n", Ships[objp->instance].ship_name, &Ships[repair_objp->instance].ship_name, dist));
13593                 accelerate_ship(aip, (0.9f + dot) * dist/1500.0f);
13594                 turn_towards_point(objp, &goal_point, NULL, 0.0f);
13595         } else {
13596                 accelerate_ship(aip, 0.0f);
13597                 //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));
13598         }
13599
13600         return 1;
13601 }
13602
13603 //      Maybe cause this ship to self-destruct.
13604 //      Currently, any small ship (SIF_SMALL_SHIP) that has been disabled will self-destruct after awhile.
13605 //      Maybe should only do this if they are preventing their wing from re-entering.
13606 void ai_maybe_self_destruct(object *objp, ai_info *aip)
13607 {
13608         //      Friendly ships can be repaired, so no self-destruct.
13609         //      In multiplayer, just don't self-destruct.  I figured there would be a problem. -- MK, 3/19/98.
13610         if ((Ships[objp->instance].team == TEAM_FRIENDLY) || (Game_mode & GM_MULTIPLAYER))
13611                 return;
13612
13613         //      Small ships in a wing blow themselves up after awhile if engine or weapons system has been destroyed.
13614         //      Reason: Don't want them to prevent a re-emergence of the wing.
13615         //      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
13616         //      mission would be broken.
13617         if ((Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP) && (Ships[objp->instance].wingnum != -1)) {
13618                 if ((ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) ||
13619                         (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_WEAPONS) <= 0.0f)) {
13620                         if (aip->self_destruct_timestamp < 0)
13621                                 aip->self_destruct_timestamp = timestamp(90 * 1000);    //      seconds until self-destruct
13622                 } else {
13623                         aip->self_destruct_timestamp = -1;
13624                 }
13625
13626                 if (aip->self_destruct_timestamp < 0) {
13627                         return;
13628                 }
13629
13630                 if (timestamp_elapsed(aip->self_destruct_timestamp)) {
13631                         ship_apply_local_damage( objp, objp, &objp->pos, objp->hull_strength*flFrametime + 1.0f, MISS_SHIELDS);
13632                 }
13633         }
13634 }
13635
13636 // Determine if pl_objp needs a new target, called from ai_frame()
13637 int ai_need_new_target(object *pl_objp, int target_objnum)
13638 {
13639         object *objp;
13640
13641         if ( target_objnum < 0 ) {
13642                 return 1;
13643         }
13644
13645         objp = &Objects[target_objnum];
13646
13647         if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID) && (objp->type != OBJ_WEAPON) ) {
13648                 return 1;
13649         }
13650
13651         if ( objp->type == OBJ_SHIP ) {
13652                 if ( Ships[objp->instance].flags & SF_DYING ) {
13653                         return 1;
13654                 } else if (Ships[objp->instance].team == Ships[pl_objp->instance].team)
13655                         return 1;
13656         }
13657
13658         return 0;
13659 }
13660
13661 //      If *objp is recovering from a collision with a big ship, handle it.
13662 //      Return true if recovering.
13663 int maybe_big_ship_collide_recover_frame(object *objp, ai_info *aip)
13664 {
13665         float   dot, dist;
13666         vector  v2g;
13667         
13668         if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1) {
13669                 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);
13670                 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_1, &objp->pos);
13671                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
13672                 accelerate_ship(aip, dot);
13673
13674                 //      If close to desired point, or 15+ seconds since entered this mode, continue to next mode.
13675                 if ((timestamp_until(aip->big_recover_timestamp) < -15*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13676                         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_1;
13677                         aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13678                 }
13679
13680                 return 1;
13681
13682         } else if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_2) {
13683                 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);
13684                 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_2, &objp->pos);
13685                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
13686                 accelerate_ship(aip, dot);
13687
13688                 //      If close to desired point, or 30+ seconds since started avoiding collision, done avoiding.
13689                 if ((timestamp_until(aip->big_recover_timestamp) < -30*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13690                         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13691                         aip->ai_flags &= ~AIF_TARGET_COLLISION;
13692                 }
13693
13694                 return 1;
13695         }
13696
13697         if (aip->ai_flags & AIF_TARGET_COLLISION) {
13698                 aip->ai_flags &= ~AIF_TARGET_COLLISION;
13699         }
13700         return 0;
13701 }
13702
13703 void validate_mode_submode(ai_info *aip)
13704 {
13705         switch (aip->mode) {
13706         case AIM_CHASE:
13707                 // check valid submode
13708                 switch (aip->submode) {
13709                 case SM_CONTINUOUS_TURN:
13710                 case SM_ATTACK:
13711                 case SM_EVADE_SQUIGGLE:
13712                 case SM_EVADE_BRAKE:    
13713                 case SM_EVADE:          
13714                 case SM_SUPER_ATTACK:
13715                 case SM_AVOID:  
13716                 case SM_GET_BEHIND:
13717                 case SM_GET_AWAY:               
13718                 case SM_EVADE_WEAPON:
13719                 case SM_FLY_AWAY:       
13720                 case SM_ATTACK_FOREVER:
13721                         break;
13722                 default:
13723                         Int3();
13724                 }
13725                 break;
13726
13727         case AIM_STRAFE:
13728                 // check valid submode
13729                 switch(aip->submode) {
13730                 case AIS_STRAFE_ATTACK:
13731                 case AIS_STRAFE_AVOID:
13732                 case AIS_STRAFE_RETREAT1:
13733                 case AIS_STRAFE_RETREAT2:
13734                 case AIS_STRAFE_POSITION:
13735                         break;
13736                 default:
13737                         Int3();
13738                 }
13739                 break;
13740         }
13741 }
13742
13743 //      --------------------------------------------------------------------------
13744 // Process AI object "objnum".
13745 void ai_frame(int objnum)
13746 {
13747         ship            *shipp = &Ships[Objects[objnum].instance];
13748         ai_info *aip = &Ai_info[shipp->ai_index];
13749         int             target_objnum;
13750
13751 //      validate_mode_submode(aip);
13752
13753         Assert((aip->mode != AIM_WAYPOINTS) || (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC));
13754
13755         // Set globals defining the current object and its enemy object.
13756         Pl_objp = &Objects[objnum];
13757
13758         if (aip->mode == AIM_WARP_OUT) {
13759                 ai_warp_out(Pl_objp);
13760                 return;
13761         }
13762
13763 /*      //      HACK! TEST! REMOVE ME!
13764         if (Ship_info[shipp->ship_info_index].flags & SIF_BIG_SHIP)
13765                 if (shipp->team == Player_ship->team)
13766                         aip->mode = AIM_CHASE;
13767 */
13768
13769 //      if (!strnicmp(Ships[Pl_objp->instance].ship_name, "cancer", 6))
13770 //              nprintf(("AI", "Ship %s: mode = %s, submode = %i\n", Ships[Pl_objp->instance].ship_name, Mode_text[aip->mode], aip->submode));
13771
13772         ai_maybe_self_destruct(Pl_objp, aip);
13773
13774 //      if ( timestamp_elapsed(aip->goal_check_time) ) {
13775                 ai_process_mission_orders( objnum, aip );
13776 //              aip->goal_check_time = timestamp_rand(1000,2000);
13777 //      }
13778
13779         //      Avoid a shockwave, if necessary.  If a shockwave and rearming, stop rearming.
13780         if (aip->ai_flags & AIF_AVOID_SHOCKWAVE) {
13781                 if (ai_avoid_shockwave(Pl_objp, aip)) {
13782                         aip->ai_flags &= ~(AIF_BIG_SHIP_COLLIDE_RECOVER_1 | AIF_BIG_SHIP_COLLIDE_RECOVER_2);
13783                         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
13784                                 ai_abort_rearm_request(Pl_objp);
13785                         return;
13786                 }
13787         } else {
13788                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_STARTED;
13789         }
13790
13791         // moved call to ai_do_repair frame here from below because of the subsequent if statment returning
13792         // if the ship is getting repaired
13793         //      If waiting to be repaired, just stop and sit.
13794         ai_do_repair_frame(Pl_objp, aip, flFrametime);
13795         if ((aip->ai_flags & AIF_AWAITING_REPAIR) || (aip->ai_flags & AIF_BEING_REPAIRED)) {
13796                 if (ai_await_repair_frame(Pl_objp, aip))
13797                         return;
13798         }
13799
13800         if (aip->mode == AIM_PLAY_DEAD)
13801                 return;
13802
13803         //      If recovering from a collision with a big ship, don't continue.
13804         if (maybe_big_ship_collide_recover_frame(Pl_objp, aip))
13805                 return;
13806
13807         ai_preprocess_ignore_objnum(Pl_objp, aip);
13808         target_objnum = set_target_objnum(aip, aip->target_objnum);
13809
13810         // 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));
13811
13812         Assert(objnum != target_objnum);
13813
13814         ai_manage_shield(Pl_objp, aip);
13815         
13816         if ( maybe_request_support(Pl_objp) ) {
13817                 if ( Ships[Pl_objp->instance].flags & SF_FROM_PLAYER_WING ) {
13818                         ship_maybe_tell_about_rearm(shipp);
13819                 }
13820         }
13821
13822         ai_maybe_warp_out(Pl_objp);
13823
13824 /*
13825         //      If this ship is attacking an object's subsystems and someone else destroyed
13826         //      the subsystem, it could continue attacking the ship.  Need to invalidate the objnum.
13827         if (target_objnum >= 0)
13828                 if (Objects[target_objnum].flags & OF_PROTECTED) {
13829                         // if (aip->targeted_subsys != NULL)
13830                         //      ; //nprintf(("AI", "subsys hits = %7.3f\n", aip->targeted_subsys->current_hits));
13831
13832                         if ((aip->targeted_subsys == NULL) || (aip->targeted_subsys->current_hits <= 0.0f)) {
13833                                 target_objnum = -1;
13834                                 aip->target_objnum = -1;
13835                         }
13836                 }
13837 */
13838
13839
13840         //      Find an enemy if don't already have one.
13841         En_objp = NULL;
13842         if ( ai_need_new_target(Pl_objp, target_objnum) ) {
13843                 if ((aip->mode != AIM_EVADE_WEAPON) && (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
13844                         aip->resume_goal_time = -1;
13845                         aip->active_goal = AI_GOAL_NONE;
13846                 } else if (aip->resume_goal_time == -1) {
13847                         // AL 12-9-97: Don't allow cargo and navbuoys to set their aip->target_objnum
13848                         if ( !(Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS) ) {
13849                                 target_objnum = find_enemy(objnum, MAX_ENEMY_DISTANCE, Skill_level_max_attackers[Game_skill_level]);            //      Attack up to 25K units away.
13850                                 if (target_objnum != -1) {
13851                                         if (aip->target_objnum != target_objnum)
13852                                                 aip->aspect_locked_time = 0.0f;
13853                                         set_target_objnum(aip, target_objnum);
13854                                         En_objp = &Objects[target_objnum];
13855                                 }
13856                         }
13857                 }
13858         } else if (target_objnum >= 0) {
13859                 En_objp = &Objects[target_objnum];
13860         }
13861
13862         // set base stealth info each frame
13863         aip->ai_flags &= ~AIF_STEALTH_PURSIUT;
13864         if (En_objp && En_objp->type == OBJ_SHIP) {
13865                 if (Ship_info[Ships[En_objp->instance].ship_info_index].flags & SIF_STEALTH) {
13866                         int stealth_state = ai_is_stealth_visible(Pl_objp, En_objp);
13867                         float dist = vm_vec_dist_quick(&En_objp->pos, &Pl_objp->pos);
13868
13869                         if (stealth_state != STEALTH_FULLY_TARGETABLE) {
13870                                 aip->ai_flags |= AIF_STEALTH_PURSIUT;
13871                         }
13872
13873                         if ( (stealth_state == STEALTH_FULLY_TARGETABLE) || (stealth_state == STEALTH_VISIBLE) ) {
13874                                 aip->stealth_last_visible_stamp = timestamp();
13875                                 aip->stealth_last_cheat_visible_stamp = aip->stealth_last_visible_stamp;
13876                                 aip->stealth_last_pos = En_objp->pos;
13877                                 aip->stealth_velocity = En_objp->phys_info.vel;
13878                         } else if (dist < 100) {
13879                                 // get cheat timestamp
13880                                 aip->stealth_last_cheat_visible_stamp = timestamp();
13881
13882                                 // set approximate pos and vel, with increasing error as time from last_visible_stamp increases
13883                                 update_ai_stealth_info_with_error(aip/*, 0*/);
13884                         }
13885                 }
13886         }
13887
13888         /*      if ((Pl_objp != NULL) && (En_objp != NULL)) {
13889                 slide_face_ship();
13890                 return;
13891         }
13892 */
13893         // AL 12-10-97: ensure that cargo and navbuoys aip->target_objnum is always -1.
13894         if ( Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS ) {
13895                 aip->target_objnum = -1;
13896         }
13897
13898         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)) {
13899                 mprintf(("Warning: Object and its enemy have same position.  Object #%i\n", Pl_objp-Objects));
13900                 En_objp = NULL;
13901         }
13902
13903         if (aip->mode == AIM_CHASE) {
13904                 if (En_objp == NULL) {
13905                         aip->active_goal = -1;
13906                 }
13907         }
13908
13909         //      If there is a goal to resume and enough time has elapsed, resume the goal.
13910         if ((aip->resume_goal_time > 0) && (aip->resume_goal_time < Missiontime)) {
13911                 aip->active_goal = AI_GOAL_NONE;
13912                 aip->resume_goal_time = -1;
13913                 target_objnum = find_enemy(objnum, 2000.0f, Skill_level_max_attackers[Game_skill_level]);
13914                 if (target_objnum != -1) {
13915                         if (aip->target_objnum != target_objnum) {
13916                                 aip->aspect_locked_time = 0.0f;
13917                         }
13918                         set_target_objnum(aip, target_objnum);
13919                 }
13920         }
13921
13922         // check if targeted subsystem has been destroyed, if so, move onto another subsystem
13923         // if trying to disable or disarm the target
13924         if ((En_objp != NULL) && ( aip->targeted_subsys != NULL )) {
13925                 Assert(En_objp->type == OBJ_SHIP);
13926                 if ( aip->targeted_subsys->current_hits <= 0.0f ) {
13927                         int subsys_type;
13928
13929                         if ( aip->goals[0].ai_mode == AI_GOAL_DISABLE_SHIP ) {
13930                                 subsys_type = SUBSYSTEM_ENGINE;
13931                         } else if ( aip->goals[0].ai_mode == AI_GOAL_DISARM_SHIP ) {
13932                                 subsys_type = SUBSYSTEM_TURRET;
13933                         } else {
13934                                 subsys_type = -1;
13935                         }
13936
13937                         if ( subsys_type != -1 ) {
13938                                 ship_subsys *new_subsys;
13939                                 new_subsys = ship_return_next_subsys(&Ships[En_objp->instance], subsys_type, &Pl_objp->pos);
13940                                 if ( new_subsys != NULL ) {
13941                                         set_targeted_subsys(aip, new_subsys, aip->target_objnum);
13942                                 } else {
13943                                         // AL 12-16-97: no more subsystems to attack... reset targeting info
13944                                         aip->target_objnum = -1;
13945                                         set_targeted_subsys(aip, NULL, -1);
13946                                 }
13947                         } else {
13948                                 // targeted subsys is destroyed, so stop attacking it
13949                                 set_targeted_subsys(aip, NULL, -1);
13950                         }
13951                 }
13952         }
13953
13954         ai_maybe_launch_cmeasure(Pl_objp, aip);
13955         ai_maybe_evade_locked_missile(Pl_objp, aip);
13956
13957         aip->target_time += flFrametime;
13958
13959         int in_formation = 0;
13960         if (aip->ai_flags & AIF_FORMATION) {
13961                 in_formation = !ai_formation();
13962         }
13963
13964         if ( !in_formation ) {
13965                 ai_execute_behavior(aip);
13966         }
13967
13968         process_subobjects(objnum);
13969         maybe_resume_previous_mode(Pl_objp, aip);
13970         
13971         if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON ) {
13972                 if (Missiontime > aip->afterburner_stop_time) {
13973                         //nprintf(("AI", "Frame %i, turning off afterburner.\n", AI_FrameCount));
13974                         afterburners_stop(Pl_objp);
13975                 }
13976         }
13977 //      validate_mode_submode(aip);
13978 }
13979
13980 int Waypoints_created = 0;
13981
13982 //      Find the ship with the name *name in the Ship_info array.
13983 int find_ship_name(char *name)
13984 {
13985         int     i;
13986
13987         for (i=0; i<Num_ship_types; i++)
13988                 if (!strcmp(Ship_info[i].name, name))
13989                         return i;
13990
13991         return -1;
13992 }
13993
13994 void create_waypoints()
13995 {
13996         int     i, j, z;
13997
13998         // Waypoints_created = 1;
13999
14000         if (Waypoints_created)
14001                 return;
14002
14003         for (j=0; j<Num_waypoint_lists; j++)
14004                 for (i=0; i<Waypoint_lists[j].count; i++) {
14005                         z = obj_create(OBJ_WAYPOINT, 0, j * 65536 + i, NULL,
14006                                 &Waypoint_lists[j].waypoints[i], 0.0f, OF_RENDERS);
14007                 }
14008
14009         Waypoints_created = 1;
14010 }
14011
14012 int Last_ai_obj = -1;
14013
14014 void ai_process( object * obj, int ai_index, float frametime )
14015 {
14016 //      if (Ships[obj->instance].flags & SF_DYING)
14017 //              nprintf(("AI", "Frame: %i Ship %s is dying!\n", Framecount, Ships[obj->instance].ship_name));
14018
14019         if (obj->flags & OF_SHOULD_BE_DEAD)
14020                 return;
14021
14022         // 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.
14023         if ((Ships[obj->instance].flags & SF_DYING ) && !(Ship_info[Ships[obj->instance].ship_info_index].flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP))){
14024                 return;
14025         }
14026
14027         int rfc = 1;            //      Assume will be Reading Flying Controls.
14028
14029         Assert( obj->type == OBJ_SHIP );
14030         Assert( ai_index >= 0 );
14031
14032         init_ship_info();
14033
14034         create_waypoints();
14035
14036         AI_frametime = frametime;
14037         if (obj-Objects <= Last_ai_obj) {
14038                 AI_FrameCount++;
14039         }
14040
14041         memset( &AI_ci, 0, sizeof(AI_ci) );
14042
14043         ai_frame(obj-Objects);
14044
14045         AI_ci.pitch = 0.0f;
14046         AI_ci.bank = 0.0f;
14047         AI_ci.heading = 0.0f;
14048
14049         // the ships maximum velocity now depends on the energy flowing to engines
14050         obj->phys_info.max_vel.xyz.z = Ships[obj->instance].current_max_speed;
14051         ai_info *aip = &Ai_info[Ships[obj->instance].ai_index];
14052
14053         //      In certain circumstances, the AI says don't fly in the normal way.
14054         //      One circumstance is in docking and undocking, when the ship is moving
14055         //      under thruster control.
14056         switch (aip->mode) {
14057         case AIM_DOCK:
14058                 if ((aip->submode >= AIS_DOCK_2) && (aip->submode != AIS_UNDOCK_3))
14059                         rfc = 0;
14060                 break;
14061         case AIM_WARP_OUT:
14062                 if (aip->submode >= AIS_WARP_3)
14063                         rfc = 0;
14064                 break;
14065 //      case AIM_NONE:
14066 //              if (aip->submode == AIS_NONE_FORMATION)
14067 //                      rfc = 0;
14068 //              break;
14069         default:
14070                 break;
14071         }
14072
14073         if (rfc == 1) {
14074                 vector copy_desired_rotvel = obj->phys_info.rotvel;
14075                 physics_read_flying_controls( &obj->orient, &obj->phys_info, &AI_ci, frametime);
14076                 // if obj is in formation and not flight leader, don't update rotvel
14077                 if (aip->ai_flags & AIF_FORMATION) {
14078                         if (&Objects[aip->goal_objnum] != obj) {
14079                                 obj->phys_info.desired_rotvel = copy_desired_rotvel;
14080                                 obj->phys_info.rotvel = copy_desired_rotvel;
14081                         }
14082                 }
14083         }
14084
14085         Last_ai_obj = obj-Objects;
14086 }
14087
14088 //      Initialize ai_info struct of object objnum.
14089 void init_ai_object(int objnum)
14090 {
14091         int     ship_index, ai_index;
14092         ai_info *aip;
14093         int ship_type;
14094         object  *objp;
14095         vector  near_vec;                       //      A vector nearby and mainly in front of this object.
14096
14097         objp = &Objects[objnum];
14098         ship_index = objp->instance;
14099         ai_index = Ships[ship_index].ai_index;
14100         Assert((ai_index >= 0) && (ai_index < MAX_AI_INFO));
14101
14102         aip = &Ai_info[ai_index];
14103
14104         ship_type = Ships[ship_index].ship_info_index;
14105
14106         vm_vec_scale_add(&near_vec, &objp->pos, &objp->orient.v.fvec, 100.0f);
14107         vm_vec_scale_add2(&near_vec, &objp->orient.v.rvec, 10.0f);
14108
14109         // Things that shouldn't have to get initialized, but initialize them just in case!
14110         aip->ai_flags = 0;
14111         aip->previous_mode = AIM_NONE;
14112         aip->mode_time = -1;
14113         aip->target_objnum = -1;
14114         aip->target_signature = -1;
14115         aip->previous_target_objnum = -1;
14116         aip->target_time = 0.0f;
14117         aip->enemy_wing = -1;
14118         aip->attacker_objnum = -1;
14119         aip->goal_objnum = -1;
14120         aip->goal_signature = -1;
14121         aip->guard_objnum = -1;
14122         aip->guard_signature = -1;
14123         aip->guard_wingnum = -1;
14124         aip->dock_signature = -1;
14125         aip->submode = 0;
14126         aip->previous_submode = 0;
14127         aip->best_dot_to_enemy = -1.0f;
14128         aip->best_dot_from_enemy = -1.0f;
14129         aip->best_dot_to_time = 0;
14130         aip->best_dot_from_time = 0;
14131         aip->submode_start_time = 0;
14132         aip->submode_parm0 = 0;
14133         aip->active_goal = -1;
14134         aip->goal_check_time = timestamp(0);
14135         aip->last_predicted_enemy_pos = near_vec;
14136         aip->prev_goal_point = near_vec;
14137         aip->goal_point = near_vec;
14138         aip->time_enemy_in_range = 0.0f;
14139         aip->last_attack_time = 0;
14140         aip->last_hit_time = 0;
14141         aip->last_hit_quadrant = 0;
14142         aip->hitter_objnum = -1;
14143         aip->hitter_signature = -1;
14144         aip->resume_goal_time = -1;
14145         aip->prev_accel = 0.0f;
14146         aip->prev_dot_to_goal = 0.0f;
14147
14148         aip->ignore_objnum = UNUSED_OBJNUM;
14149         aip->ignore_signature = -1;
14150
14151         // aip->mode = AIM_NONE;
14152
14153         // End of Things that shouldn't have to get initialized, but initialize them just in case!
14154
14155         aip->ai_courage = Ai_classes[Ship_info[ship_type].ai_class].ai_courage[Game_skill_level];
14156         aip->ai_patience = Ai_classes[Ship_info[ship_type].ai_class].ai_patience[Game_skill_level];
14157         aip->ai_evasion = Ai_classes[Ship_info[ship_type].ai_class].ai_evasion[Game_skill_level];
14158         aip->ai_accuracy = Ai_classes[Ship_info[ship_type].ai_class].ai_accuracy[Game_skill_level];
14159
14160         if (Num_waypoint_lists > 0) {
14161                 aip->wp_index = -1;
14162                 aip->wp_list = -1;
14163         } else {
14164                 aip->wp_index = -1;
14165                 aip->wp_list = -1;
14166         }
14167
14168         aip->attacker_objnum = -1;
14169         aip->goal_signature = -1;
14170
14171         Objects[objnum].phys_info.prev_fvec = Objects[objnum].orient.v.fvec;
14172
14173         aip->last_predicted_enemy_pos.xyz.x = 0.0f;     //      Says this value needs to be recomputed!
14174         aip->time_enemy_in_range = 0.0f;
14175
14176         aip->resume_goal_time = -1;                                     //      Say there is no goal to resume.
14177
14178         aip->active_goal = -1;
14179         aip->path_start = -1;
14180         aip->path_goal_dist = -1;
14181         aip->path_length = 0;
14182         aip->path_subsystem_next_check = 1;
14183         aip->dock_path_index = -1;
14184         aip->dock_index = -1;
14185         aip->dock_objnum = -1;
14186
14187         aip->danger_weapon_objnum = -1;
14188         aip->danger_weapon_signature = -1;
14189
14190         aip->lead_scale = 0.0f;
14191         aip->last_hit_target_time = Missiontime;
14192
14193         aip->nearest_locked_object = -1;
14194         aip->nearest_locked_distance = 99999.0f;
14195
14196         aip->targeted_subsys = NULL;
14197         aip->last_subsys_target = NULL;
14198         aip->targeted_subsys_parent = -1;
14199
14200         // The next two fields are used to time the rearming to allow useful sound effects for missile rearming
14201         aip->rearm_first_missile = TRUE;                //      flag to indicate that next missile to load is the first missile
14202         aip->rearm_release_delay = 0;                   //      timestamp to delay the separation of docked ships after rearm
14203
14204         aip->next_predict_pos_time = 0;
14205
14206         aip->afterburner_stop_time = 0;
14207         aip->last_objsig_hit = -1;                              // object signature of the ship most recently hit by aip
14208
14209         aip->path_next_create_time = timestamp(1);
14210         aip->path_create_pos = Objects[objnum].pos;
14211         aip->path_create_orient = Objects[objnum].orient;
14212
14213         aip->ignore_expire_timestamp = timestamp(1);
14214         aip->warp_out_timestamp = 0;
14215         aip->next_rearm_request_timestamp = timestamp(1);
14216         aip->primary_select_timestamp = timestamp(1);
14217         aip->secondary_select_timestamp = timestamp(1);
14218         aip->scan_for_enemy_timestamp = timestamp(1);
14219
14220         aip->choose_enemy_timestamp = timestamp(3*(NUM_SKILL_LEVELS-Game_skill_level) * ((rand_alt() % 500) + 500));
14221
14222         aip->shockwave_object = -1;
14223         aip->shield_manage_timestamp = timestamp(1);
14224         aip->self_destruct_timestamp = -1;      //      This is a flag that we have not yet set this.
14225         aip->ok_to_target_timestamp = timestamp(1);
14226         aip->pick_big_attack_point_timestamp = timestamp(1);
14227         vm_vec_zero(&aip->big_attack_point);
14228
14229         aip->avoid_check_timestamp = timestamp(1);
14230
14231         aip->abort_rearm_timestamp = -1;
14232
14233         // artillery stuff
14234         aip->artillery_objnum = -1;
14235         aip->artillery_sig = -1;        
14236
14237         // waypoint speed cap
14238         aip->waypoint_speed_cap = -1;
14239
14240         // set lethality to enemy team
14241         aip->lethality = 0.0f;
14242 }
14243
14244 void init_ai_objects()
14245 {
14246         int     i;
14247
14248         for (i=0; i<num_objects; i++){
14249                 if (Objects[i].type == OBJ_SHIP){
14250                         init_ai_object(i);
14251                 }
14252         }
14253 }
14254
14255 void init_ai_system()
14256 {
14257         // MWA -- removed next line of code on 11/12/97.  When a ship is created
14258         // it calls init_ai_object() on it's objnum.  Doing this init at the point where
14259         // this function gets called messes things up.
14260         //init_ai_objects();
14261
14262         Ppfp = Path_points;
14263         Waypoints_created = 0;
14264
14265         Dock_path_warning_given = 0;
14266
14267 /*      for (int i=0; i<MAX_IGNORE_OBJECTS; i++) {
14268                 Ignore_objects[i].objnum = -1;
14269                 Ignore_objects[i].signature = -1;
14270         }
14271 */
14272
14273 }
14274
14275 void ai_set_default_behavior(object *obj, int classnum)
14276 {
14277         ai_info *aip;
14278
14279         Assert(obj != NULL);
14280         Assert(obj->instance != -1);
14281         Assert(Ships[obj->instance].ai_index != -1);
14282
14283         aip = &Ai_info[Ships[obj->instance].ai_index];
14284
14285         aip->behavior = classnum;
14286
14287 }
14288
14289 void ai_do_default_behavior(object *obj)
14290 {
14291         ai_info *aip;
14292         int             ship_flags;
14293
14294         Assert(obj != NULL);
14295         Assert(obj->instance != -1);
14296         Assert(Ships[obj->instance].ai_index != -1);
14297
14298         aip = &Ai_info[Ships[obj->instance].ai_index];
14299
14300         ship_flags = Ship_info[Ships[obj->instance].ship_info_index].flags;
14301         if (!is_instructor(obj) && (ship_flags & (SIF_FIGHTER | SIF_BOMBER))) {
14302                 int enemy_objnum = find_enemy(OBJ_INDEX(obj), 1000.0f, Skill_level_max_attackers[Game_skill_level]);
14303                 set_target_objnum(aip, enemy_objnum);
14304                 aip->mode = AIM_CHASE;
14305                 aip->submode = SM_ATTACK;
14306         } else if (ship_flags & (SIF_SUPPORT)) {
14307                 aip->mode = AIM_SAFETY;
14308                 aip->submode = AISS_1;
14309                 aip->ai_flags &= ~(AIF_REPAIRING);
14310         } else if ( ship_flags & SIF_SENTRYGUN ) {
14311                 aip->mode = AIM_SENTRYGUN;
14312         } else {
14313                 aip->mode = AIM_NONE;
14314         }
14315         
14316         aip->submode_start_time = Missiontime;
14317         aip->active_goal = AI_GOAL_NONE;
14318 }
14319
14320 #define FRIENDLY_DAMAGE_THRESHOLD       50.0f           //      Display a message at this threshold.  Note, this gets scaled by Skill_level
14321
14322 // send the given message from objp.  called from the maybe_process_friendly_hit
14323 // code below when a message must get send to the player when he fires on friendlies
14324 void process_friendly_hit_message( int message, object *objp )
14325 {
14326         int index;
14327
14328         // no traitor in multiplayer
14329         if(Game_mode & GM_MULTIPLAYER){
14330                 return;
14331         }
14332
14333         // don't send this message if a player ship was hit.
14334         if ( objp->flags & OF_PLAYER_SHIP ){
14335                 return;
14336         }
14337
14338         // check if objp is a cargo contianer -- if so, then find a new ship to send the message
14339         index = objp->instance;
14340         if ( !(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER|SIF_BOMBER)) ){
14341                 index = -1;
14342         }
14343
14344         // if the message is "oops" (the don't hit me message), always make come from Terran command
14345         if ( message == MESSAGE_OOPS ){
14346                 index = -1;
14347         }
14348
14349         if ( index >= 0){
14350                 message_send_builtin_to_player( message, &Ships[index], MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14351         } else {
14352                 message_send_builtin_to_player( message, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14353         }
14354 }
14355
14356 extern  void ship_set_subsystem_strength( ship *shipp, int type, float strength );
14357
14358 //      Object *objp_weapon, fired by *objp_hitter, hit object *objp_ship.
14359 void maybe_process_friendly_hit(object *objp_hitter, object *objp_hit, object *objp_weapon)
14360 {
14361         // no turning traitor in multiplayer
14362         if ( Game_mode & GM_MULTIPLAYER ) {
14363                 return;
14364         }
14365
14366         // ditto if mission says no traitors allowed
14367         if (The_mission.flags & MISSION_FLAG_NO_TRAITOR) {
14368                 return;
14369         }
14370
14371         if ((objp_hitter == Player_obj) && (Player_ship->team == TEAM_FRIENDLY)) {
14372
14373                 // AL 12-4-97: It is possible the Player is a OBJ_GHOST at this point.  If so, bail out.
14374                 if ( objp_hitter->type != OBJ_SHIP ) {
14375                         return;
14376                 }
14377
14378                 Assert(objp_hitter->type == OBJ_SHIP);
14379                 Assert(objp_hit->type == OBJ_SHIP);
14380                 Assert(objp_weapon->type == OBJ_WEAPON);
14381
14382                 ship    *shipp_hitter = &Ships[objp_hitter->instance];
14383                 ship    *shipp_hit = &Ships[objp_hit->instance];
14384
14385                 if (shipp_hitter->team != shipp_hit->team) {
14386                         return;
14387                 }
14388
14389                 // get the player
14390                 player *pp = &Players[Player_num];
14391
14392                 // wacky stuff here
14393                 if (pp->friendly_hits != 0) {
14394                         float   time_since_last_hit = f2fl(Missiontime - pp->friendly_last_hit_time);
14395                         if ((time_since_last_hit >= 0.0f) && (time_since_last_hit < 10000.0f)) {
14396                                 if (time_since_last_hit > 60.0f) {
14397                                         pp->friendly_hits = 0;
14398                                         pp->friendly_damage = 0.0f;
14399                                 } else if (time_since_last_hit > 2.0f) {
14400                                         pp->friendly_hits -= (int) time_since_last_hit/2;
14401                                         pp->friendly_damage -= time_since_last_hit;
14402                                 }
14403
14404                                 if (pp->friendly_damage < 0.0f) {
14405                                         pp->friendly_damage = 0.0f;
14406                                 }
14407
14408                                 if (pp->friendly_hits < 0) {
14409                                         pp->friendly_hits = 0;
14410                                 }
14411                         }
14412                 }
14413
14414                 float   damage;         //      Damage done by weapon.  Gets scaled down based on size of ship.
14415
14416                 damage = Weapon_info[Weapons[objp_weapon->instance].weapon_info_index].damage;
14417                 
14418                 // wacky stuff here
14419                 ship_info *sip = &Ship_info[Ships[objp_hit->instance].ship_info_index];
14420                 if (sip->initial_hull_strength > 1000.0f) {
14421                         float factor = sip->initial_hull_strength / 1000.0f;
14422                         factor = min(100.0f, factor);
14423                         damage /= factor;
14424                 }
14425
14426                 //      Don't penalize much at all for hitting cargo
14427                 if (sip->flags & (SIF_CARGO | SIF_SENTRYGUN)) {
14428                         damage /= 10.0f;
14429                 }
14430
14431                 //      Hit ship, but not targeting it, so it's not so heinous, maybe an accident.
14432                 if (Ai_info[shipp_hitter->ai_index].target_objnum != OBJ_INDEX(objp_hit)) {
14433                         damage /= 5.0f;
14434                 }
14435
14436                 pp->friendly_last_hit_time = Missiontime;
14437                 pp->friendly_hits++;
14438
14439                 // cap damage and number of hits done this frame
14440                 float accredited_damage = min(MAX_BURST_DAMAGE, pp->damage_this_burst + damage) - pp->damage_this_burst;
14441                 pp->friendly_damage += accredited_damage;
14442                 pp->damage_this_burst += accredited_damage;
14443
14444                 // Done with adjustments to damage.  Evaluate based on current friendly_damage
14445                 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 ));
14446                 
14447                 if (is_instructor(objp_hit)) {
14448                         // it's not nice to hit your instructor
14449                         if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD) {
14450                                 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_ATTACK, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14451                                 pp->last_warning_message_time = Missiontime;
14452                                 ship_set_subsystem_strength( Player_ship, SUBSYSTEM_WEAPONS, 0.0f);
14453
14454                                 training_fail();
14455
14456                                 //      Instructor warp out.
14457                                 ai_set_mode_warp_out(objp_hit, &Ai_info[Ships[objp_hit->instance].ai_index]);
14458                                 gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START_FORCED );     //      Force player to warp out.
14459
14460                                 //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) );
14461                                 //ship_apply_global_damage( objp_hitter, objp_hit, NULL, 1.0f );
14462                         } else if (Missiontime - pp->last_warning_message_time > F1_0*4) {
14463                                 // warning every 4 sec
14464                                 // use NULL as the message sender here since it is the Terran Command persona
14465                                 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_HIT, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14466                                 pp->last_warning_message_time = Missiontime;
14467                         }
14468
14469                 // not nice to hit your friends
14470                 } else if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f)) {
14471                         process_friendly_hit_message( MESSAGE_HAMMER_SWINE, objp_hit );
14472                         mission_goal_fail_all();
14473                         ai_abort_rearm_request( Player_obj );
14474
14475                         Player_ship->team = TEAM_TRAITOR;
14476
14477                 } else if ((damage > frand()) && (Missiontime - pp->last_warning_message_time > F1_0*4) && (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD)) {
14478                         // no closer than 4 sec intervals
14479                         //      Note: (damage > frand()) added on 12/9/97 by MK.  Since damage is now scaled down for big ships, we could get too
14480                         //      many warnings.  Kind of tedious.  frand() returns a value in 0..1, so this won't affect legit hits.
14481                         process_friendly_hit_message( MESSAGE_OOPS, objp_hit );
14482                         pp->last_warning_message_time = Missiontime;
14483                 }
14484         }
14485 }
14486
14487 //      Maybe make ship with ai_info *aip attack hitter_objnum as a dynamic goal
14488 void maybe_set_dynamic_chase(ai_info *aip, int hitter_objnum)
14489 {
14490         Assert(Ship_info[Ships[aip->shipnum].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER));
14491
14492         // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
14493         if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
14494                 return;
14495         }
14496
14497         // only set as target if can be targeted.
14498         if (awacs_get_level(&Objects[hitter_objnum], &Ships[aip->shipnum], 1) < 1) {
14499                 return;
14500         }
14501
14502         if (aip->target_objnum != hitter_objnum)
14503                 aip->aspect_locked_time = 0.0f;
14504         set_target_objnum(aip, hitter_objnum);
14505         aip->resume_goal_time = Missiontime + i2f(20);  //      Only chase up to 20 seconds.
14506         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14507
14508         set_targeted_subsys(aip, NULL, -1);             //      Say not attacking any particular subsystem.
14509
14510         aip->previous_submode = aip->mode;
14511         aip->mode = AIM_CHASE;
14512         aip->submode = SM_ATTACK;
14513 }
14514
14515
14516 //      Return true if *objp has armed an aspect seeking bomb.
14517 //      This function written so a ship with an important bomb to fire will willingly take hits in the face to fire its bomb.
14518 int firing_aspect_seeking_bomb(object *objp)
14519 {
14520         ship    *shipp;
14521         int     bank_index;
14522         ship_weapon     *swp;
14523
14524         shipp = &Ships[objp->instance];
14525
14526         swp = &shipp->weapons;
14527
14528         bank_index = swp->current_secondary_bank;
14529
14530         if (bank_index != -1)
14531                 if (swp->secondary_bank_ammo[bank_index] > 0) {
14532                         if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_BOMB) {
14533                                 if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_HOMING_ASPECT) {
14534                                         return 1;
14535                                 }
14536                         }
14537                 }
14538
14539         return 0;
14540 }
14541
14542 //      *objp collided with big ship *big_objp at global point *collide_pos
14543 //      Make it fly away from the collision point.
14544 // collision_normal is NULL, when a collision is imminent and we just want to bug out.
14545 void big_ship_collide_recover_start(object *objp, object *big_objp, vector *collide_pos, vector *collision_normal)
14546 {
14547         ai_info *aip;
14548
14549         Assert(objp->type == OBJ_SHIP);
14550
14551         aip = &Ai_info[Ships[objp->instance].ai_index];
14552
14553         if (!timestamp_elapsed(aip->big_recover_timestamp) && (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1))
14554                 return;
14555
14556         //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)));
14557         if (collision_normal) {
14558                 aip->big_recover_timestamp = timestamp(2000);
14559                 aip->big_collision_normal = *collision_normal;
14560         //      nprintf(("AI", " normal\n"));
14561         } else {
14562                 aip->big_recover_timestamp = timestamp(500);
14563         //      nprintf(("AI", " no normal\n"));
14564         }
14565
14566
14567         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
14568         aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_1;
14569
14570 //      vector  out_vec;
14571 //      vm_vec_normalized_dir(&out_vec, &objp->pos, collide_pos);
14572
14573         // big_recover_pos_1 is 100 m out along normal
14574         vector direction;
14575         if (collision_normal) {
14576                 direction = *collision_normal;
14577         } else {
14578                 vm_vec_copy_scale(&direction, &objp->orient.v.fvec, -1.0f);
14579         }
14580         vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &direction, 100.0f);
14581
14582         // go out 200 m from box closest box point
14583         get_world_closest_box_point_with_delta(&aip->big_recover_pos_2, big_objp, &aip->big_recover_pos_1, NULL, 300.0f);
14584
14585         accelerate_ship(aip, 0.0f);
14586 /*
14587         if (vm_vec_dot(collision_normal, &objp->orient.v.fvec) > 0.5f) {
14588 //              vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14589 //              vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.v.uvec, big_objp->radius/2.0f);
14590 //              vm_vec_scale_add(&aip->big_recover_pos_2, &objp->pos, &out_vec, big_objp->radius*2.0f);
14591                 accelerate_ship(aip, 2.0f);
14592         } else {
14593 //              vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14594 //              vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.v.uvec, big_objp->radius/2.0f);
14595                 accelerate_ship(aip, 0.0f);
14596         } */
14597 }
14598
14599 float max_lethality = 0.0f;
14600
14601 void ai_update_lethality(object *ship_obj, object *other_obj, float damage)
14602 {
14603         Assert(ship_obj->type == OBJ_SHIP);
14604         Assert(other_obj->type == OBJ_WEAPON || other_obj->type == OBJ_SHOCKWAVE);
14605         int dont_count = FALSE;
14606
14607         int parent = other_obj->parent;
14608         if (Objects[parent].type == OBJ_SHIP) {
14609                 if (Objects[parent].signature == other_obj->parent_sig) {
14610
14611                         // check damage done to enemy team
14612                         if (Ships[ship_obj->instance].team != Ships[Objects[parent].instance].team) {
14613
14614                                 // other is weapon
14615                                 if (other_obj->type == OBJ_WEAPON) {
14616                                         weapon *wp = &Weapons[other_obj->instance];
14617                                         weapon_info *wif = &Weapon_info[wp->weapon_info_index];
14618
14619                                         // if parent is BIG|HUGE, don't count beam
14620                                         if (Ship_info[Ships[Objects[parent].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
14621                                                 if (wif->wi_flags & WIF_BEAM) {
14622                                                         dont_count = TRUE;
14623                                                 }
14624                                         }
14625                                 }
14626
14627                                 if (!dont_count) {
14628                                         float lethality = 0.025f * damage;      // 2 cyclops (@2000) put you at 100 lethality
14629
14630                                         // increase lethality weapon's parent ship
14631                                         ai_info *aip = &Ai_info[Ships[Objects[parent].instance].ai_index];
14632                                         aip->lethality += lethality;
14633                                         aip->lethality = min(110.0f, aip->lethality);
14634                                         // if you hit, don;t be less than 0
14635                                         aip->lethality = max(0.0f, aip->lethality);
14636
14637 //                                      if (aip->lethality > max_lethality) {
14638 //                                              max_lethality = aip->lethality;
14639 //                                              mprintf(("new lethalilty high: %.1f\n", max_lethality));
14640 //                                      }
14641
14642                                         // if parent is player, show his lethality
14643 //                                      if (Objects[parent].flags & OF_PLAYER_SHIP) {
14644 //                                              mprintf(("Player lethality: %.1f\n", aip->lethality));
14645 //                                      }
14646                                 }
14647                         }
14648                 }
14649         }
14650 }
14651
14652
14653 //      Object *objp_ship was hit by either weapon *objp_weapon or collided into by ship hit_objp at point *hitpos.
14654 void ai_ship_hit(object *objp_ship, object *hit_objp, vector *hitpos, int shield_quadrant, vector *hit_normal)
14655 {
14656         int             hitter_objnum = -2;
14657         object  *objp_hitter = NULL;
14658         ship            *shipp;
14659         ai_info *aip, *hitter_aip;
14660
14661         shipp = &Ships[objp_ship->instance];
14662         aip = &Ai_info[shipp->ai_index];
14663
14664         if (objp_ship->flags & OF_PLAYER_SHIP)
14665                 return;
14666
14667         if ((aip->mode == AIM_WARP_OUT) || (aip->mode == AIM_PLAY_DEAD))
14668                 return;
14669
14670         if (hit_objp->type == OBJ_SHIP) {
14671                 //      If the object that this ship collided with is a big ship
14672                 if (Ship_info[Ships[hit_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
14673                         //      And the current object is _not_ a big ship
14674                         if (!(Ship_info[Ships[objp_ship->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
14675                                 //      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.
14676                                 big_ship_collide_recover_start(objp_ship, hit_objp, hitpos, hit_normal);
14677                         }
14678                 }
14679         }
14680
14681         if (hit_objp->type == OBJ_WEAPON) {
14682                 //      Make sure the object that fired this weapon is still alive.  If not, abort.
14683                 // Assert(hit_objp->parent >= 0);
14684                 if(hit_objp->parent < 0){
14685                         return;
14686                 }
14687                 if ( hit_objp->parent_sig != Objects[hit_objp->parent].signature ){
14688                         return;
14689                 }
14690
14691                 //      Hit by a protected ship, don't attack it.
14692                 if (Objects[hit_objp->parent].flags & OF_PROTECTED) {
14693                         if ((Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) && (aip->target_objnum == -1)) {
14694                                 if (aip->mode == AIM_CHASE) {
14695                                         if (aip->submode != SM_EVADE_WEAPON) {
14696                                                 aip->mode = AIM_CHASE;
14697                                                 aip->submode = SM_EVADE_WEAPON;
14698                                                 aip->submode_start_time = Missiontime;
14699                                         }
14700                                 } else if (aip->mode != AIM_EVADE_WEAPON) {
14701                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14702                                         aip->previous_mode = aip->mode;
14703                                         aip->previous_submode = aip->submode;
14704                                         aip->mode = AIM_EVADE_WEAPON;
14705                                         aip->submode = -1;
14706                                         aip->submode_start_time = Missiontime;
14707                                         aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Evade for up to five seconds.
14708                                 }
14709
14710                         }
14711                         return;
14712                 }
14713
14714                 hitter_objnum = hit_objp->parent;
14715                 Assert((hitter_objnum >= 0) && (hitter_objnum < MAX_OBJECTS));
14716                 objp_hitter = &Objects[hitter_objnum];
14717                 maybe_process_friendly_hit(objp_hitter, objp_ship, hit_objp);           //      Deal with player's friendly fire.
14718
14719                 if ( (shipp->team & TEAM_FRIENDLY) && !(Game_mode & GM_MULTIPLAYER) ) {
14720                         ship_maybe_ask_for_help(shipp);
14721                 }
14722         } else if (hit_objp->type == OBJ_SHIP) {
14723                 if (shipp->team == Ships[hit_objp->instance].team)              //      Don't have AI react to collisions between teammates.
14724                         return;
14725                 objp_hitter = hit_objp;
14726                 hitter_objnum = hit_objp-Objects;
14727         } else {
14728                 Int3(); //      Hmm, what kind of object hit this if not weapon or ship?  Get MikeK.
14729                 return;
14730         }
14731
14732         //      Collided into a protected ship, don't attack it.
14733         if (hit_objp->flags & OF_PROTECTED)
14734                 return;
14735
14736         Assert(objp_hitter != NULL);
14737         hitter_aip = &Ai_info[Ships[objp_hitter->instance].ai_index];
14738         hitter_aip->last_hit_target_time = Missiontime;
14739         
14740         // store the object signature of objp_ship into ai_info, since we want to track the last ship hit by 'hitter_objnum'
14741         hitter_aip->last_objsig_hit = objp_ship->signature; 
14742
14743         aip->last_hit_time = Missiontime;
14744
14745         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
14746                 return;
14747
14748         //      If this ship is awaiting repair, abort!
14749         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)) {
14750                 ship_info       *sip = &Ship_info[shipp->ship_info_index];
14751
14752                 if (objp_ship->hull_strength/sip->initial_hull_strength < 0.3f) {
14753                         //      No, only abort if hull below a certain level.
14754                         aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP/2);  //      Might request again after 15 seconds.
14755                         if ( !(objp_ship->flags & OF_PLAYER_SHIP) )                                             // mwa -- don't abort rearm for a player
14756                                 ai_abort_rearm_request(objp_ship);
14757                 }
14758         }
14759
14760         //      If firing a bomb, ignore enemy fire so we can gain lock drop the bomb.
14761         //      Only ignore fire if aspect_locked_time > 0.5f, as this means we're in range.
14762         if (firing_aspect_seeking_bomb(objp_ship)) {
14763                 if ((aip->ai_flags & AIF_SEEK_LOCK) && (aip->aspect_locked_time > 0.1f))
14764                         return;
14765         }
14766
14767         //      If in AIM_STRAFE mode and got hit by target, maybe attack turret if appropriate
14768         if (aip->mode == AIM_STRAFE) {
14769                 Assert(hitter_objnum != -2);
14770                 if (aip->target_objnum == hitter_objnum) {
14771                         if ( hit_objp->type == OBJ_WEAPON ) {
14772                                 ai_big_strafe_maybe_attack_turret(objp_ship, hit_objp);
14773                         }
14774                         return;
14775                 }
14776                 else {
14777                                 // AL 11-10-97:
14778                         ;       // do nothing here, we'll attack this hitter if it is a fighter or bomber (this is handled
14779                                 // in code later in this function
14780                 }
14781         }
14782
14783         if (objp_ship == Player_obj)
14784                 return;         //      We don't do AI for the player.
14785
14786         maybe_update_guard_object(objp_ship, objp_hitter);
14787
14788         //      Big ships don't go any further.
14789         if (!(Ship_info[shipp->ship_info_index].flags & SIF_SMALL_SHIP))
14790                 return;
14791
14792         //      If the hitter object is the ignore object, don't attack it.
14793         ship_info       *sip = &Ship_info[shipp->ship_info_index];
14794         if ((is_ignore_object(aip, objp_hitter-Objects)) && (sip->flags & (SIF_BOMBER | SIF_FIGHTER))) {
14795                 if (aip->mode == AIM_NONE) {
14796                         aip->mode = AIM_CHASE;  //      This will cause the ship to move, if not attack.
14797                         aip->submode = SM_EVADE;
14798                 }
14799                 return;
14800         }
14801
14802         //      Maybe abort based on mode.
14803         switch (aip->mode) {
14804         case AIM_CHASE:
14805                 if (aip->submode == SM_ATTACK_FOREVER)
14806                         return;
14807
14808                 if ( hit_objp->type == OBJ_WEAPON ) {
14809                         if ( ai_big_maybe_enter_strafe_mode(objp_ship, OBJ_INDEX(hit_objp), 1) )
14810                                 return;
14811                 }
14812
14813         case AIM_GUARD:
14814                 //      If in guard mode and far away from guard object, don't pursue guy that hit me.
14815                         if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
14816                                 if (vm_vec_dist_quick(&objp_ship->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
14817                                         return;
14818                                 }
14819                         }
14820         case AIM_STILL:
14821         case AIM_STAY_NEAR:
14822                 // Note: Dealt with above, at very top.  case AIM_PLAY_DEAD:
14823         case AIM_STRAFE:
14824                 break;
14825         case AIM_EVADE_WEAPON:
14826         case AIM_EVADE:
14827         case AIM_GET_BEHIND:
14828         case AIM_AVOID:
14829         case AIM_DOCK:
14830         case AIM_BIGSHIP:
14831         case AIM_PATH:
14832         case AIM_NONE:
14833         case AIM_BAY_DEPART:
14834         case AIM_SENTRYGUN:
14835                 return;
14836         case AIM_BAY_EMERGE:
14837                 // If just leaving the docking bay, don't react to enemy fire... just keep flying away from docking bay
14838                 if ( (Missiontime - aip->submode_start_time) < 5*F1_0 ) {
14839                         return;
14840                 }
14841                 break;
14842         case AIM_WAYPOINTS:
14843                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER))
14844                         break;
14845                 else
14846                         return;
14847                 break;
14848         case AIM_SAFETY:
14849                 if ((aip->submode != AISS_1) || (Missiontime - aip->submode_start_time > i2f(1))) {
14850                         aip->submode = AISS_1;
14851                         aip->submode_start_time = Missiontime;
14852                 }
14853                 return;
14854                 break;
14855         case AIM_WARP_OUT:
14856                 return;
14857                 break;
14858         default:
14859                 Int3(); //      Bogus mode!
14860         }
14861
14862         if (timestamp_elapsed(aip->ok_to_target_timestamp))
14863                 aip->ai_flags &= ~AIF_FORMATION;                        //      If flying in formation, bug out!
14864
14865         aip->hitter_objnum = hitter_objnum;
14866         aip->hitter_signature = Objects[hitter_objnum].signature;
14867
14868         //      If the hitter is not on the same team as the hittee, do some stuff.
14869         if (shipp->team != Ships[objp_hitter->instance].team) {
14870                 //nprintf(("AI", "Object %i attacking %i, who just hit him!\n", objp_ship-Objects, hitter_objnum));
14871
14872                 if ((hitter_objnum != aip->target_objnum) && (sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
14873                         maybe_set_dynamic_chase(aip, hitter_objnum);
14874                         maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14875                 } else {
14876                         if ((aip->mode == AIM_CHASE) && ((objp_ship->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp_ship)/sip->shields > 0.8f))) {
14877                                 switch (aip->submode) {
14878                                 case SM_ATTACK:
14879                                 case SM_SUPER_ATTACK:
14880                                 case SM_GET_AWAY:
14881                                         break;
14882                                 default:
14883                                         if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
14884                                                 maybe_set_dynamic_chase(aip, hitter_objnum);
14885                                         }
14886                                         maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14887                                         break;
14888                                 }
14889                         } else if (aip->mode == AIM_CHASE) {
14890                                 switch (aip->submode) {
14891                                 case SM_ATTACK:
14892                                         aip->submode = SM_EVADE;
14893                                         aip->submode_start_time = Missiontime;
14894                                         break;
14895                                 case SM_SUPER_ATTACK:
14896                                         if (Missiontime - aip->submode_start_time > i2f(1)) {
14897                                                 aip->submode = SM_EVADE;
14898                                                 aip->submode_start_time = Missiontime;
14899                                         }
14900                                         break;
14901                                 case SM_EVADE_BRAKE:
14902                                         break;
14903                                 case SM_EVADE_SQUIGGLE:
14904                                         aip->submode = SM_EVADE;
14905                                         aip->submode_start_time = Missiontime;
14906                                         break;
14907                                 default:
14908                                         if (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) {
14909                                                 maybe_set_dynamic_chase(aip, hitter_objnum);
14910                                                 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14911                                         }
14912
14913                                         break;
14914                                 }
14915                         } else {
14916                                 // AL 3-15-98: Prevent escape pods from entering chase mode
14917                                 if ( (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) ) {
14918                                         maybe_set_dynamic_chase(aip, hitter_objnum);
14919                                 }
14920                                 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14921                         }
14922                 }
14923         }
14924 }
14925
14926 //      Ship shipnum has been destroyed.
14927 //      Cleanup.
14928 // the parameter 'method' is used to tell is this ship was destroyed or it departed normally.
14929 // This function will get called in either case, and there are things that should be done if
14930 // the ship actually gets destroyed which shouldn't get done if it departed.
14931 void ai_ship_destroy(int shipnum, int method)
14932 {
14933         int             objnum;
14934         object  *other_objp;
14935         ship            *shipp;
14936         ship_obj        *so;
14937         ai_info *dead_aip;
14938
14939         Assert((shipnum >= 0) && (shipnum < MAX_SHIPS));
14940         objnum = Ships[shipnum].objnum;
14941         dead_aip = &Ai_info[Ships[shipnum].ai_index];
14942
14943         // if I was getting repaired, or awaiting repair, then cleanup the repair mode.  When awaiting repair, the dock_objnum
14944         // is -1.  When the support ship is on the way, the dock_objnum >= 0 (points to support ship).
14945         if ( dead_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14946                 if ( dead_aip->dock_objnum >= 0 )
14947                         ai_do_objects_repairing_stuff( &Objects[objnum], &Objects[dead_aip->dock_objnum], REPAIR_INFO_END);
14948                 else
14949                         ai_do_objects_repairing_stuff( &Objects[objnum], NULL, REPAIR_INFO_END );
14950         }
14951
14952         //      For all objects that had this ship as a target, wipe it out, forcing find of a new enemy.
14953         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
14954                 other_objp = &Objects[so->objnum];
14955                 Assert(other_objp->instance != -1);
14956
14957                 shipp = &Ships[other_objp->instance];
14958                 Assert(shipp->ai_index != -1);
14959
14960                 ai_info *aip = &Ai_info[shipp->ai_index];
14961
14962                 // MWA 2/11/98
14963                 // code commented out below is taken care of in ai_cleanup_dock_mode when gets called when the
14964                 // support ship starts it's death roll.
14965
14966                 //      If the destroyed ship was on its way to repair the current ship
14967                 if (aip->dock_objnum == objnum) {
14968
14969                         // clean up the flags for any kind of docking mode.  If aip was part of a goal of dock/undock
14970                         // then it will get cleaned up by the goal code.
14971                         ai_do_objects_undocked_stuff( other_objp, NULL );
14972
14973                         if ( aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14974                                 int abort_reason;
14975                                 if ( method == SEF_DEPARTED ) {
14976                                         abort_reason = REPAIR_INFO_ABORT;
14977                                 } else {
14978                                         abort_reason = REPAIR_INFO_KILLED;
14979                                 }
14980                                 ai_do_objects_repairing_stuff( other_objp, NULL, abort_reason );
14981                         }
14982                 }
14983
14984                 if (aip->target_objnum == objnum) {
14985                         set_target_objnum(aip, -1);
14986                         //      If this ship had a dynamic goal of chasing the dead ship, clear the dynamic goal.
14987                         if (aip->resume_goal_time != -1)
14988                                 aip->active_goal = AI_GOAL_NONE;
14989                 }
14990
14991                 if (aip->goal_objnum == objnum) {
14992                         aip->goal_objnum = -1;
14993                         aip->goal_signature = -1;
14994                 }
14995
14996                 if (aip->guard_objnum == objnum) {
14997                         aip->guard_objnum = -1;
14998                         aip->guard_signature = -1;
14999                 }
15000
15001                 if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[Objects[objnum].instance].ai_index].wing)) {
15002                         if (aip->guard_wingnum != aip->wing)
15003                                 ai_set_guard_wing(other_objp, aip->guard_wingnum);
15004                 }
15005
15006                 if (aip->hitter_objnum == objnum)
15007                         aip->hitter_objnum = -1;
15008
15009         }
15010
15011 }
15012
15013 /*
15014 //      Interface function to goals code.
15015 //      Make object *objp fly to point *vp and warp out.
15016 void ai_warp_out(object *objp, vector *vp)
15017 {
15018         ai_info *aip;
15019
15020         aip = &Ai_info[Ships[objp->instance].ai_index];
15021
15022         if (aip->mode != AIM_WARP_OUT) {
15023                 ai_set_mode_warp_out(objp, aip);
15024         }
15025         float   dist;
15026         float   dot;
15027         vector  v2v;
15028         ai_info *aip;
15029
15030         dist = vm_vec_normalized_dir(&v2v, vp, &objp->pos);
15031
15032         if (dist < objp->radius + 5.0f) {
15033
15034                 // Start the warp out effect 
15035                 shipfx_warpout_start(objp);
15036
15037         } else {
15038                 dot = vm_vec_dot(&objp->orient.v.fvec, &v2v);
15039
15040                 aip = &Ai_info[Ships[objp->instance].ai_index];
15041
15042                 if (dist > 500.0f)
15043                         accelerate_ship(aip, 1.0f);
15044                 else
15045                         accelerate_ship(aip, (3*dot + 1.0f)/4.0f);
15046
15047                 turn_towards_point(objp, vp, NULL, 0.0f);
15048         }
15049 }
15050 */
15051
15052
15053 //      Do stuff at start of deathroll.
15054 void ai_deathroll_start(object *ship_obj)
15055 {
15056         ai_info *aip;
15057         ship            *shipp, *other_ship;
15058
15059         shipp = &Ships[ship_obj->instance];
15060         aip = &Ai_info[shipp->ai_index];
15061
15062         // mark object we are docked with so we can do damage and separate during deathroll
15063         // keep dock_objnum_when_dead from being changed if already set (only allow to be set when -1)
15064         if (Ships[ship_obj->instance].dock_objnum_when_dead == -1) {
15065                 Ships[ship_obj->instance].dock_objnum_when_dead = aip->dock_objnum;
15066                 // set other_ship dock_objnum_when_dead, if other_ship exits.
15067                 if (Ships[ship_obj->instance].dock_objnum_when_dead != -1) {
15068                         other_ship = &Ships[Objects[aip->dock_objnum].instance];
15069                         other_ship->dock_objnum_when_dead = shipp->objnum;
15070                 }
15071         }
15072
15073         ai_cleanup_dock_mode(aip, shipp);
15074
15075         aip->mode = AIM_NONE;
15076 }
15077
15078 //      Object *requester_objp tells rearm ship to abort rearm.
15079 //      Returns true if it succeeded, else false.
15080 //      To succeed means you were previously rearming.
15081 int ai_abort_rearm_request(object *requester_objp)
15082 {
15083         ship            *requester_shipp;
15084         ai_info *requester_aip;
15085
15086         Assert(requester_objp->type == OBJ_SHIP);
15087         if(requester_objp->type != OBJ_SHIP){
15088                 return 0;
15089         }
15090         Assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));      
15091         if((requester_objp->instance < 0) || (requester_objp->instance >= MAX_SHIPS)){
15092                 return 0;
15093         }
15094         requester_shipp = &Ships[requester_objp->instance];
15095         Assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));          
15096         if((requester_shipp->ai_index < 0) || (requester_shipp->ai_index >= MAX_AI_INFO)){
15097                 return 0;
15098         }       
15099         requester_aip = &Ai_info[requester_shipp->ai_index];
15100         
15101         if (requester_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)){
15102
15103                 // dock_objnum is always valid once a rearm repair has been requested.  It points to the
15104                 // ship that is coming to repair me.
15105                 if (requester_aip->dock_objnum != -1) {
15106                         object  *repair_objp;
15107                         ai_info *repair_aip;
15108
15109                         repair_objp = &Objects[requester_aip->dock_objnum];
15110                         repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
15111
15112                         //      Make sure signatures match.  This prevents nasty bugs in which an object
15113                         //      that was repairing another is destroyed and is replaced by another ship
15114                         //      before this code comes around.
15115                         if (repair_objp->signature == requester_aip->dock_signature) {
15116
15117                                 Assert( repair_objp->type == OBJ_SHIP );
15118
15119                                 // if support ship is in the process of undocking, don't do anything.
15120                                 if ( repair_aip->submode < AIS_UNDOCK_0 ) {
15121                                         ai_do_objects_repairing_stuff( requester_objp, repair_objp, REPAIR_INFO_ABORT );
15122
15123                                         if ( repair_aip->submode == AIS_DOCK_4 )
15124                                                 repair_aip->submode = AIS_UNDOCK_0;
15125                                         else
15126                                                 repair_aip->submode = AIS_UNDOCK_3;
15127
15128                                         repair_aip->submode_start_time = Missiontime;
15129                                 } else {
15130                                         nprintf(("AI", "Not aborting rearm since already undocking\n"));
15131                                 }
15132                         }
15133                 } else {
15134                         // setting these flags is the safe things to do.  There may not be a corresponding repair
15135                         // ship for this guys since a repair ship may be currently repairing someone else.
15136                         ai_do_objects_repairing_stuff( requester_objp, NULL, REPAIR_INFO_ABORT );
15137
15138                         // try and remove this guy from an arriving support ship.
15139                         mission_remove_scheduled_repair(requester_objp);
15140                 }
15141
15142                 return 1;
15143         } else if ( requester_aip->ai_flags & AIF_REPAIRING ) {
15144                 // a support ship can request to abort when he is told to do something else (like warp out).
15145                 // see if this support ships goal_objnum is valid.  If so, then issue this ai_abort comment
15146                 // for the ship that he is enroute to repair
15147                 if ( requester_aip->goal_objnum != -1 ) {
15148                         int val;
15149
15150                         val = ai_abort_rearm_request( &Objects[requester_aip->goal_objnum] );
15151                         return val;
15152                 }
15153         }
15154
15155         return 0;
15156 }
15157
15158 // function which gets called from ai-issue_rearm_request and from code in missionparse.cpp
15159 // to actually issue the rearm goal (support_obj to rearm requester_obj);
15160 void ai_add_rearm_goal( object *requester_objp, object *support_objp )
15161 {
15162         ship *support_shipp, *requester_shipp;
15163         ai_info *support_aip, *requester_aip;
15164
15165         support_shipp = &Ships[support_objp->instance];
15166         requester_shipp = &Ships[requester_objp->instance];
15167         requester_aip = &Ai_info[requester_shipp->ai_index];
15168
15169         Assert( support_shipp->ai_index != -1 );
15170         support_aip = &Ai_info[support_shipp->ai_index];
15171
15172         // if the requester is a player object, issue the order as the squadmate messaging code does.  Doing so
15173         // ensures that the player get a higher priority!
15174         requester_aip->ai_flags |= AIF_AWAITING_REPAIR; //      Tell that I'm awaiting repair.
15175         if ( requester_objp->flags & OF_PLAYER_SHIP )
15176                 ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_REARM_REPAIR, -1, requester_shipp->ship_name, support_aip );
15177         else
15178                 ai_add_goal_ship_internal( support_aip, AI_GOAL_REARM_REPAIR, requester_shipp->ship_name, -1, -1 );
15179
15180 }
15181
15182 //      Object *requester_objp requests rearming.
15183 //      Returns objnum of ship coming to repair requester on success
15184 //      Success means you found someone to rearm you and you weren't previously rearming.
15185 int ai_issue_rearm_request(object *requester_objp)
15186 {
15187         object  *objp;
15188         ship            *requester_shipp;
15189         ai_info *requester_aip;
15190
15191         Assert(requester_objp->type == OBJ_SHIP);
15192         Assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));
15193         requester_shipp = &Ships[requester_objp->instance];
15194         Assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));
15195         requester_aip = &Ai_info[requester_shipp->ai_index];
15196         
15197         //      Make sure not already awaiting repair.
15198         if (requester_aip->ai_flags & AIF_AWAITING_REPAIR) {
15199                 nprintf(("AI", "Ship %s already awaiting rearm by ship %s.\n", requester_shipp->ship_name, &Ships[Objects[requester_aip->dock_objnum].instance].ship_name));    
15200                 return -1;
15201         }
15202
15203         if ( !is_support_allowed(requester_objp) )
15204                 return -1;
15205
15206         //nprintf(("AI", "Ship %s requesting rearming.\n", requester_shipp->ship_name));
15207         requester_aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);  //      Might request again after this much time.
15208
15209         // call ship_find_repair_ship to get a support ship.  If none is found, then we will warp one in.  This
15210         // function will return the next available ship which can repair requester
15211         objp = ship_find_repair_ship( requester_objp );
15212         ai_do_objects_repairing_stuff( requester_objp, objp, REPAIR_INFO_QUEUE );
15213         if ( objp ) {
15214
15215                 // MWA 5/14/98 -- moved next item into the ai_do_objects_repairing_stuff function so that clients
15216                 // would properly update their hud support view
15217                 //ai_add_rearm_goal( requester_objp, objp );
15218                 return OBJ_INDEX(objp);
15219
15220         } else {
15221                 // call to warp in repair ship!!!!  for now, warp in any number of ships needed.  Should cap it to
15222                 // some reasonable max (or let support ships warp out).  We should assume here that ship_find_repair_ship()
15223                 // would have returned a valid object if there are too many support ships already in the mission
15224                 mission_warp_in_support_ship( requester_objp );
15225
15226                 return -1;
15227         }
15228
15229 }
15230
15231 // make objp rearm and repair goal_objp
15232 void ai_rearm_repair( object *objp, object  *goal_objp, int priority, int docker_index, int dockee_index )
15233 {
15234         ai_info *aip, *goal_aip;
15235
15236         aip = &Ai_info[Ships[objp->instance].ai_index];
15237         aip->goal_objnum = goal_objp-Objects;
15238
15239         // nprintf(("AI", "Ship %s preparing to rearm ship %s.\n", shipp->ship_name, requester_shipp->ship_name));
15240
15241         ai_dock_with_object(objp, goal_objp, priority, AIDO_DOCK, docker_index, dockee_index);
15242         aip->ai_flags |= AIF_REPAIRING;                                         //      Tell that repair guy is busy trying to repair someone.
15243
15244         goal_aip = &Ai_info[Ships[goal_objp->instance].ai_index];
15245         goal_aip->dock_objnum = objp-Objects;           //      Tell which object is coming to repair.
15246         goal_aip->dock_signature = objp->signature;
15247
15248         ai_do_objects_repairing_stuff( goal_objp, objp, REPAIR_INFO_ONWAY );
15249
15250         goal_aip->abort_rearm_timestamp = timestamp(NEXT_REARM_TIMESTAMP*3/2);
15251 }
15252
15253 // Given a dockee object and the index of the dockbay for that object (ie the dockbay index
15254 // into polymodel->dockbays[] for the model associated with the object), return the index
15255 // of a path_num associated with than dockbay (this is an index into polymodel->paths[])
15256 int ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index)
15257 {
15258         if ( dockbay_index < 0 || dockee_objp == NULL ) {
15259                 Int3();         // should never happen
15260                 return -1;
15261         }
15262
15263         if ( dockee_objp->type == OBJ_SHIP ) {
15264                 int                     path_num;
15265                 polymodel       *pm;
15266
15267                 pm = model_get( Ships[dockee_objp->instance].modelnum );
15268
15269                 // sanity checks
15270                 Assert(pm->n_docks > dockbay_index);
15271                 Assert(pm->docking_bays[dockbay_index].num_spline_paths > 0);
15272                 Assert(pm->docking_bays[dockbay_index].splines != NULL);
15273                 if(pm->n_docks <= dockbay_index){
15274                         return -1;
15275                 }
15276                 if(pm->docking_bays[dockbay_index].num_spline_paths <= 0){
15277                         return -1;
15278                 }
15279                 if(pm->docking_bays[dockbay_index].splines == NULL){
15280                         return -1;
15281                 }
15282
15283                 // We only need to return one path for the dockbay, so return the first
15284                 path_num = pm->docking_bays[dockbay_index].splines[0];
15285                 return path_num;
15286         } else {
15287                 return -1;
15288         }
15289 }
15290
15291 //      Actually go ahead and fire the synaptics.
15292 void cheat_fire_synaptic(object *objp, ship *shipp, ai_info *aip)
15293 {
15294         ship_weapon     *swp;
15295         swp = &shipp->weapons;
15296         int     current_bank = swp->current_secondary_bank;
15297
15298         ai_select_secondary_weapon(objp, swp, WIF_SPAWN, 0);
15299         if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
15300                 if (ship_fire_secondary(objp)) {
15301                         nprintf(("AI", "ship %s cheat fired synaptic!\n", shipp->ship_name));
15302                         swp->next_secondary_fire_stamp[current_bank] = timestamp(2500);
15303                 }
15304         }
15305 }
15306
15307 //      For the subspace mission (sm3-09a)
15308 //              for delta wing
15309 //                      if they're sufficiently far into the mission
15310 //                              if they're near one or more enemies
15311 //                                      every so often
15312 //                                              fire a synaptic if they have one.
15313 void maybe_cheat_fire_synaptic(object *objp, ai_info *aip)
15314 {
15315         //      Only do in subspace missions.
15316         if ( The_mission.flags & MISSION_FLAG_SUBSPACE )        {
15317                 ship    *shipp;
15318                 int     num, time;
15319
15320                 shipp = &Ships[objp->instance];
15321
15322                 if (!(strnicmp(shipp->ship_name, NOX("delta"), 5))) {
15323                         num = shipp->ship_name[6] - '1';
15324
15325                         if ((num >= 0) && (num <= 3)) {
15326                                 time = Missiontime >> 16;       //      Convert to seconds.
15327
15328                                 time -= 2*60;   //      Subtract off two minutes.
15329
15330                                 if (time > 0) {
15331                                         int modulus = 17 + num*3;
15332
15333                                         if ((time % modulus) < 2) {
15334                                                 int count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(objp)), &objp->pos, 1500.0f);
15335
15336                                                 if (count > 0) {
15337                                                         cheat_fire_synaptic(objp, shipp, aip);
15338                                                 }
15339                                         }
15340                                 }
15341                         }
15342                 }
15343         }
15344
15345 }
15346