]> icculus.org git repositories - taylor/freespace2.git/blob - src/ship/ship.cpp
Freespace 1 support
[taylor/freespace2.git] / src / ship / ship.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/Ship.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * Ship (and other object) handling functions
16  *
17  * $Log$
18  * Revision 1.6  2003/05/25 02:30:44  taylor
19  * Freespace 1 support
20  *
21  * Revision 1.5  2002/06/17 06:33:11  relnev
22  * ryan's struct patch for gcc 2.95
23  *
24  * Revision 1.4  2002/06/09 04:41:26  relnev
25  * added copyright header
26  *
27  * Revision 1.3  2002/06/02 00:31:36  relnev
28  * implemented osregistry
29  *
30  * Revision 1.2  2002/05/03 13:34:34  theoddone33
31  * More stuff compiles
32  *
33  * Revision 1.1.1.1  2002/05/03 03:28:10  root
34  * Initial import.
35  *
36  * 
37  * 144   10/13/99 3:43p Jefff
38  * fixed unnumbered XSTRs
39  * 
40  * 143   9/14/99 3:26a Dave
41  * Fixed laser fogging problem in nebula (D3D)> Fixed multiplayer
42  * respawn-too-early problem. Made a few crash points safe.
43  * 
44  * 142   9/11/99 4:02p Dave
45  * Don't page in model textures when doing ship_model_change() in fred.
46  * 
47  * 141   9/10/99 9:44p Dave
48  * Bumped version # up. Make server reliable connects not have such a huge
49  * timeout. 
50  * 
51  * 140   9/06/99 3:30p Mikek
52  * Added system to restrict weapon choices for dogfight missions.
53  * 
54  * 139   9/01/99 10:15a Dave
55  * 
56  * 138   9/01/99 8:43a Andsager
57  * supress WARNING from no_fred ships flag
58  * 
59  * 137   8/26/99 8:52p Dave
60  * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
61  * 
62  * 136   8/26/99 6:08p Andsager
63  * Add debug code for lethality and number of turrets targeting player.
64  * 
65  * 135   8/26/99 5:14p Andsager
66  * 
67  * 134   8/26/99 9:45a Dave
68  * First pass at easter eggs and cheats.
69  * 
70  * 133   8/24/99 4:25p Andsager
71  * Add ship-vanish sexp
72  * 
73  * 132   8/23/99 11:59a Andsager
74  * Force choice of big fireball when Knossos destroyed.  Allow logging of
75  * ship destroyed when no killer_name (ie, from debug).
76  * 
77  * 131   8/23/99 11:09a Andsager
78  * Round 2 of Knossos explosion
79  * 
80  * 130   8/20/99 5:09p Andsager
81  * Second pass on Knossos device explosion
82  * 
83  * 129   8/18/99 10:59p Andsager
84  * Enable "b" key to target bombers.
85  * 
86  * 128   8/18/99 12:09p Andsager
87  * Add debug if message has no anim for message.  Make messages come from
88  * wing leader.
89  * 
90  * 127   8/16/99 10:04p Andsager
91  * Add special-warp-dist and special-warpout-name sexp for Knossos device
92  * warpout.
93  * 
94  * 126   8/16/99 4:06p Dave
95  * Big honking checkin.
96  * 
97  * 125   8/16/99 2:01p Andsager
98  * Knossos warp-in warp-out.
99  * 
100  * 124   8/13/99 10:49a Andsager
101  * Knossos and HUGE ship warp out.  HUGE ship warp in.  Stealth search
102  * modes dont collide big ships.
103  * 
104  * 123   8/05/99 6:19p Dave
105  * New demo checksums.
106  * 
107  * 122   8/05/99 12:57a Andsager
108  * the insanity of it all!
109  * 
110  * 121   8/03/99 11:13a Andsager
111  * Bump up number of ship_subsystems for demo
112  * 
113  * 120   8/02/99 10:39p Dave
114  * Added colored shields. OoOoOoooOoo
115  * 
116  * 119   7/29/99 10:47p Dave
117  * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs.
118  * 
119  * 118   7/29/99 12:05a Dave
120  * Nebula speed optimizations.
121  * 
122  * 117   7/28/99 1:36p Andsager
123  * Modify cargo1 to include flag CARGO_NO_DEPLETE.  Add sexp
124  * cargo-no-deplete (only for BIG / HUGE).  Modify ship struct to pack
125  * better.
126  * 
127  * 116   7/26/99 5:50p Dave
128  * Revised ingame join. Better? We'll see....
129  * 
130  * 115   7/26/99 8:06a Andsager
131  * Consistent personas
132  * 
133  * 114   7/24/99 5:22p Jefff
134  * Removed debug rendering of interpolated ships in multiplayer.
135  * 
136  * 113   7/19/99 8:57p Andsager
137  * Added Ship_exited red_alert_carry flag and hull strength for RED ALERT
138  * carry over of Exited_ships
139  * 
140  * 112   7/19/99 7:20p Dave
141  * Beam tooling. Specialized player-killed-self messages. Fixed d3d nebula
142  * pre-rendering.
143  * 
144  * 111   7/19/99 12:02p Andsager
145  * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
146  * only blow up subsystem if its strength is > 0
147  * 
148  * 110   7/18/99 9:56p Andsager
149  * Make max_ship_subsys 300 again!
150  * 
151  * 109   7/18/99 5:20p Dave
152  * Jump node icon. Fixed debris fogging. Framerate warning stuff.
153  * 
154  * 108   7/18/99 12:32p Dave
155  * Randomly oriented shockwaves.
156  * 
157  * 107   7/15/99 6:36p Jamesa
158  * Moved default ship name into the ships.tbl
159  * 
160  * 104   7/15/99 9:20a Andsager
161  * FS2_DEMO initial checkin
162  * 
163  * 103   7/13/99 5:03p Alanl
164  * make sure object sounds get assigned to ships
165  * 
166  * 102   7/09/99 5:54p Dave
167  * Seperated cruiser types into individual types. Added tons of new
168  * briefing icons. Campaign screen.
169  * 
170  * 101   7/08/99 5:49p Andsager
171  * Fixed bug colliding with just warped in Cap ship
172  * 
173  * 100   7/08/99 10:53a Dave
174  * New multiplayer interpolation scheme. Not 100% done yet, but still
175  * better than the old way.
176  * 
177  * 99    7/06/99 4:24p Dave
178  * Mid-level checkin. Starting on some potentially cool multiplayer
179  * smoothness crap.
180  * 
181  * 98    7/06/99 10:45a Andsager
182  * Modify engine wash to work on any ship that is not small.  Add AWACS
183  * ask for help.
184  * 
185  * 97    7/01/99 4:23p Dave
186  * Full support for multiple linked ambient engine sounds. Added "big
187  * damage" flag.
188  * 
189  * 96    7/01/99 11:44a Dave
190  * Updated object sound system to allow multiple obj sounds per ship.
191  * Added hit-by-beam sound. Added killed by beam sound.
192  * 
193  * 95    6/30/99 5:53p Dave
194  * Put in new anti-camper code.
195  * 
196  * 94    6/20/99 12:06a Alanl
197  * new event music changes
198  * 
199  * 93    6/16/99 4:06p Dave
200  * New pilot info popup. Added new draw-bitmap-as-poly function.
201  * 
202  * 92    6/16/99 10:21a Dave
203  * Added send-message-list sexpression.
204  * 
205  * 91    6/14/99 3:21p Andsager
206  * Allow collisions between ship and its debris.  Fix up collision pairs
207  * when large ship is warping out.
208  * 
209  * 90    6/10/99 3:43p Dave
210  * Do a better job of syncing text colors to HUD gauges.
211  * 
212  * 89    6/07/99 4:21p Andsager
213  * Add HUD color for tagged object.  Apply to target and radar.
214  * 
215  * 88    6/04/99 5:08p Andsager
216  * Sort of hack to allow minicap and supercap tat the same time.
217  * 
218  * 87    6/03/99 9:29p Andsager
219  * Remove special case for Asteroid ship LOD warning
220  * 
221  * 86    6/03/99 11:43a Dave
222  * Added the ability to use a different model when rendering to the HUD
223  * target box.
224  * 
225  * 85    6/01/99 8:35p Dave
226  * Finished lockarm weapons. Added proper supercap weapons/damage. Added
227  * awacs-set-radius sexpression.
228  * 
229  * 84    5/28/99 9:26a Andsager
230  * Added check_world_pt_in_expanded_ship_bbox() function
231  * 
232  * 83    5/26/99 4:00p Dave
233  * Fixed small lighting bug,
234  * 
235  * 82    5/26/99 11:46a Dave
236  * Added ship-blasting lighting and made the randomization of lighting
237  * much more customizable.
238  * 
239  * 81    5/24/99 5:45p Dave
240  * Added detail levels to the nebula, with a decent speedup. Split nebula
241  * lightning into its own section.
242  * 
243  * 80    5/21/99 5:03p Andsager
244  * Add code to display engine wash death.  Modify ship_kill_packet
245  * 
246  * 79    5/20/99 7:00p Dave
247  * Added alternate type names for ships. Changed swarm missile table
248  * entries.
249  * 
250  * 78    5/19/99 11:09a Andsager
251  * Turn on engine wash.  Check every 1/4 sec.
252  * 
253  * 77    5/18/99 1:30p Dave
254  * Added muzzle flash table stuff.
255  * 
256  * 76    5/18/99 12:08p Andsager
257  * Added observer_process_post to handle observer too far away
258  * 
259  * 75    5/18/99 11:15a Andsager
260  * Fix bug in mulitplayer max rangel
261  * 
262  * 74    5/18/99 10:08a Andsager
263  * Modified single maximum range before blown up to also be multi
264  * friendly.
265  * 
266  * 73    5/14/99 3:01p Andsager
267  * Fix bug in ship_do_cap_subsys_cargo_revealed
268  * 
269  * 72    5/14/99 1:59p Andsager
270  * Multiplayer message for subsystem cargo revealed.
271  * 
272  * 71    5/14/99 11:50a Andsager
273  * Added vaporize for SMALL ships hit by HUGE beams.  Modified dying
274  * frame.  Enlarged debris shards and range at which visible.
275  * 
276  * 70    5/12/99 2:55p Andsager
277  * Implemented level 2 tag as priority in turret object selection
278  * 
279  * 69    5/11/99 10:16p Andsager
280  * First pass on engine wash effect.  Rotation (control input), damage,
281  * shake.  
282  * 
283  * 68    5/10/99 4:54p Dave
284  * Fixed particularly hideous subsystem bug related to multiple ship types
285  * using the same model.
286  * 
287  * 67    4/30/99 12:18p Dave
288  * Several minor bug fixes.
289  * 
290  * 66    4/29/99 2:29p Dave
291  * Made flak work much better in multiplayer.
292  * 
293  * 65    4/28/99 11:13p Dave
294  * Temporary checkin of artillery code.
295  * 
296  * 64    4/28/99 3:11p Andsager
297  * Stagger turret weapon fire times.  Make turrets smarter when target is
298  * protected or beam protected.  Add weaopn range to weapon info struct.
299  * 
300  * 63    4/27/99 12:16a Dave
301  * Fixed beam weapon muzzle glow problem. Fixed premature timeout on the
302  * pxo server list screen. Fixed secondary firing for hosts on a
303  * standalone. Fixed wacky multiplayer weapon "shuddering" problem.
304  * 
305  * 62    4/23/99 12:30p Andsager
306  * Add debug code for showing attack point against big ships.
307  * 
308  * 61    4/23/99 12:01p Johnson
309  * Added SIF_HUGE_SHIP
310  * 
311  * 60    4/20/99 6:39p Dave
312  * Almost done with artillery targeting. Added support for downloading
313  * images on the PXO screen.
314  * 
315  * 59    4/19/99 11:01p Dave
316  * More sophisticated targeting laser support. Temporary checkin.
317  * 
318  * 58    4/19/99 12:21p Johnson
319  * Allow ships with invisible polygons which do not collide
320  * 
321  * 57    4/16/99 5:54p Dave
322  * Support for on/off style "stream" weapons. Real early support for
323  * target-painting lasers.
324  * 
325  * 56    4/12/99 10:07p Dave
326  * Made network startup more forgiving. Added checkmarks to dogfight
327  * screen for players who hit commit.
328  * 
329  * 55    4/02/99 9:55a Dave
330  * Added a few more options in the weapons.tbl for beam weapons. Attempt
331  * at putting "pain" packets into multiplayer.
332  * 
333  * 54    3/31/99 8:24p Dave
334  * Beefed up all kinds of stuff, incluging beam weapons, nebula effects
335  * and background nebulae. Added per-ship non-dimming pixel colors.
336  * 
337  * 53    3/30/99 5:40p Dave
338  * Fixed reinforcements for TvT in multiplayer.
339  * 
340  * 52    3/29/99 6:17p Dave
341  * More work on demo system. Got just about everything in except for
342  * blowing ships up, secondary weapons and player death/warpout.
343  * 
344  * 51    3/28/99 5:58p Dave
345  * Added early demo code. Make objects move. Nice and framerate
346  * independant, but not much else. Don't use yet unless you're me :)
347  * 
348  * 50    3/26/99 5:23p Andsager
349  * Fix bug with special explostions sometimes not generating shockwaves.
350  * 
351  * 49    3/26/99 4:49p Dave
352  * Made cruisers able to dock with stuff. Made docking points and paths
353  * visible in fred.
354  * 
355  * 48    3/25/99 4:47p Johnson
356  * HACK allow Mycernus to dock with Enif
357  * 
358  * 47    3/25/99 2:38p Johnson
359  * Give brad special mission docking stuff
360  * 
361  * 46    3/25/99 1:30p Johnson
362  * Allow Arcadia/Mentu docking
363  * 
364  * 45    3/24/99 4:05p Dave
365  * Put in support for assigning the player to a specific squadron with a
366  * specific logo. Preliminary work for doing pos/orient checksumming in
367  * multiplayer to reduce bandwidth.
368  * 
369  * 44    3/23/99 2:29p Andsager
370  * Fix shockwaves for kamikazi and Fred defined.  Collect together
371  * shockwave_create_info struct.
372  * 
373  * 43    3/20/99 3:46p Dave
374  * Added support for model-based background nebulae. Added 3 new
375  * sexpressions.
376  * 
377  * 42    3/19/99 9:51a Dave
378  * Checkin to repair massive source safe crash. Also added support for
379  * pof-style nebulae, and some new weapons code.
380  * 
381  * 43    3/12/99 4:30p Anoop
382  * Check for OBJ_NONE as well as OBJ_GHOST when firing secondary weapons
383  * 
384  * 42    3/11/99 2:22p Dave
385  * Fixed a countermeasure firing assert for multiplayer.
386  * 
387  * 41    3/10/99 6:51p Dave
388  * Changed the way we buffer packets for all clients. Optimized turret
389  * fired packets. Did some weapon firing optimizations.
390  * 
391  * 40    3/10/99 2:29p Dan
392  * disable lod warning for asteroid ships
393  * 
394  * 39    3/09/99 6:24p Dave
395  * More work on object update revamping. Identified several sources of
396  * unnecessary bandwidth.
397  * 
398  * 38    3/08/99 7:03p Dave
399  * First run of new object update system. Looks very promising.
400  * 
401  * 37    3/05/99 1:33p Dave
402  * Upped subsystem max to 700
403  * 
404  * 36    3/04/99 6:09p Dave
405  * Added in sexpressions for firing beams and checking for if a ship is
406  * tagged.
407  * 
408  * 35    3/02/99 9:25p Dave
409  * Added a bunch of model rendering debug code. Started work on fixing
410  * beam weapon wacky firing.
411  * 
412  * 34    3/01/99 7:39p Dave
413  * Added prioritizing ship respawns. Also fixed respawns in TvT so teams
414  * don't mix respawn points.
415  * 
416  * 33    2/26/99 6:01p Andsager
417  * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay
418  * 
419  * 32    2/26/99 4:14p Dave
420  * Put in the ability to have multiple shockwaves for ships.
421  * 
422  * 31    2/21/99 1:48p Dave
423  * Some code for monitoring datarate for multiplayer in detail.
424  * 
425  * 30    2/19/99 3:52p Neilk
426  * Put in some proper handling code for undocking dying objects (handle
427  * wacky object types like OBJ_GHOST).
428  * 
429  * 29    2/11/99 5:22p Andsager
430  * Fixed bugs, generalized block Sexp_variables
431  * 
432  * 28    2/11/99 2:15p Andsager
433  * Add ship explosion modification to FRED
434  * 
435  * 27    2/05/99 12:52p Dave
436  * Fixed Glide nondarkening textures.
437  * 
438  * 26    2/03/99 12:42p Andsager
439  * Add escort priority.  Modify ship_flags_dlg to include field.  Save and
440  * Load.  Add escort priority field to ship.
441  * 
442  * 25    2/02/99 9:36a Andsager
443  * Bash hull strength to zero when ship is killed (by sexp)
444  * 
445  * 24    1/29/99 2:25p Andsager
446  * Added turret_swarm_missiles
447  * 
448  * 23    1/29/99 12:47a Dave
449  * Put in sounds for beam weapon. A bunch of interface screens (tech
450  * database stuff).
451  * 
452  * 22    1/27/99 9:56a Dave
453  * Temporary checkin of beam weapons for Dan to make cool sounds.
454  * 
455  * 21    1/25/99 5:03a Dave
456  * First run of stealth, AWACS and TAG missile support. New mission type
457  * :)
458  * 
459  * 20    1/24/99 11:37p Dave
460  * First full rev of beam weapons. Very customizable. Removed some bogus
461  * Int3()'s in low level net code.
462  * 
463  * 19    1/14/99 6:06p Dave
464  * 100% full squad logo support for single player and multiplayer.
465  * 
466  * 18    1/14/99 12:48a Dave
467  * Todo list bug fixes. Made a pass at putting briefing icons back into
468  * FRED. Sort of works :(
469  * 
470  * 17    1/12/99 5:45p Dave
471  * Moved weapon pipeline in multiplayer to almost exclusively client side.
472  * Very good results. Bandwidth goes down, playability goes up for crappy
473  * connections. Fixed object update problem for ship subsystems.
474  * 
475  * 16    1/08/99 2:08p Dave
476  * Fixed software rendering for pofview. Super early support for AWACS and
477  * beam weapons.
478  * 
479  * 15    1/06/99 2:24p Dave
480  * Stubs and release build fixes.
481  * 
482  * 14    12/23/98 2:53p Andsager
483  * Added ship activation and gas collection subsystems, removed bridge
484  * 
485  * 13    12/09/98 7:34p Dave
486  * Cleanup up nebula effect. Tweaked many values.
487  * 
488  * 12    12/08/98 9:36a Dave
489  * Almost done nebula effect for D3D. Looks 85% as good as Glide.
490  * 
491  * 11    12/06/98 2:36p Dave
492  * Drastically improved nebula fogging.
493  * 
494  * 10    11/19/98 4:19p Dave
495  * Put IPX sockets back in psnet. Consolidated all multiplayer config
496  * files into one.
497  * 
498  * 9     11/14/98 5:33p Dave
499  * Lots of nebula work. Put in ship contrails.
500  * 
501  * 8     11/11/98 5:37p Dave
502  * Checkin for multiplayer testing.
503  * 
504  * 7     10/26/98 9:42a Dave
505  * Early flak gun support.
506  * 
507  * 6     10/23/98 3:51p Dave
508  * Full support for tstrings.tbl and foreign languages. All that remains
509  * is to make it active in Fred.
510  * 
511  * 5     10/23/98 3:03p Andsager
512  * Initial support for changing rotation rate.
513  * 
514  * 4     10/20/98 1:39p Andsager
515  * Make so sparks follow animated ship submodels.  Modify
516  * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
517  * submodel_num.  Add submodel_num to multiplayer hit packet.
518  * 
519  * 3     10/13/98 9:29a Dave
520  * Started neatening up freespace.h. Many variables renamed and
521  * reorganized. Added AlphaColors.[h,cpp]
522  * 
523  * 2     10/07/98 10:53a Dave
524  * Initial checkin.
525  * 
526  * 1     10/07/98 10:51a Dave
527  * 
528  * 915   8/28/98 3:29p Dave
529  * EMP effect done. AI effects may need some tweaking as required.
530  * 
531  * 914   8/25/98 1:48p Dave
532  * First rev of EMP effect. Player side stuff basically done. Next comes
533  * AI code.
534  * 
535  * 913   8/17/98 5:07p Dave
536  * First rev of corkscrewing missiles.
537  * 
538  * 912   7/15/98 11:29a Allender
539  * quick error dialog to prevent the > 50 ships per mission problem
540  * 
541  * 911   7/06/98 6:11p Dave
542  * More object update stuff.
543  * 
544  * 910   6/30/98 2:23p Dave
545  * Revised object update system. Removed updates for all weapons. Put
546  * button info back into control info packet.
547  * 
548  * 909   6/12/98 2:49p Dave
549  * Patch 1.02 changes.
550  * 
551  * 908   6/10/98 6:46p Lawrance
552  * increase SHIP_MULTITEXT_LENGTH to 1500
553  * 
554  * 906   6/09/98 10:31a Hoffoss
555  * Created index numbers for all xstr() references.  Any new xstr() stuff
556  * added from here on out should be added to the end if the list.  The
557  * current list count can be found in FreeSpace.cpp (search for
558  * XSTR_SIZE).
559  * 
560  * 905   6/01/98 11:43a John
561  * JAS & MK:  Classified all strings for localization.
562  * 
563  * 904   5/24/98 10:50p Mike
564  * Fix problem with ships with propagating explosions not being able to
565  * kamikaze.
566  * 
567  * 903   5/23/98 4:14p John
568  * Added code to preload textures to video card for AGP.   Added in code
569  * to page in some bitmaps that weren't getting paged in at level start.
570  * 
571  * 902   5/23/98 12:05a Adam
572  * change ship_is_getting_locked() to take weapon range into account
573  * 
574  * 901   5/22/98 5:32p Andsager
575  * Make big ship explosion sounds play all the way through.  remove
576  * cur_snd from ship struct.
577  * 
578  * 900   5/21/98 7:11p Sandeep
579  * Increased the buffer size a bit during parse ships.tbl to account for
580  * slightly lengthy descriptions
581  * 
582  * 899   5/21/98 3:31p Allender
583  * fix bug where Ship_obj_list was getting overwritten by the exited ships
584  * list
585  * 
586  * 898   5/21/98 1:44p Lawrance
587  * add ship_obj list validation
588  * 
589  * 897   5/21/98 11:33a Lawrance
590  * Check aspect_locked_time when determining if another ship is seeking
591  * lock
592  * 
593  * 896   5/19/98 8:42p Andsager
594  * Add cur_snd (used for sound management of big ship explosions)
595  * 
596  * 895   5/18/98 2:50p Peter
597  * AL: Check to make sure we don't overflow support_ships[] array in
598  * ship_find_repair_ship
599  * 
600  * 894   5/15/98 11:11p Mike
601  * Make game a bit easier based on skill level, mainly at Easy and, to a
602  * lesser extent, Medium.
603  * 
604  * 893   5/15/98 6:45p Hoffoss
605  * Made some things not appear in the release version of Fred.
606  * 
607  * 892   5/15/98 5:37p Hoffoss
608  * Added new 'tech description' fields to ship and weapon tables, and
609  * added usage of it in tech room.
610  * 
611  * 891   5/15/98 12:07p Allender
612  * make messaging come from proper ships when in team vs. team games.
613  * 
614  * 890   5/14/98 9:38p Andsager
615  * Fixed bug in dying_undock_physics when both ships have ovelapping dying
616  * times.
617  * 
618  * 889   5/13/98 6:54p Dave
619  * More sophistication to PXO interface. Changed respawn checking so
620  * there's no window for desynchronization between the server and the
621  * clients.
622  * 
623  * 888   5/12/98 10:54p Andsager
624  * Add new sound manager for big ship sub-explosion sounds
625  * 
626  * 887   5/12/98 2:34p Adam
627  * re-instated the old nicer particle ANI's for ship explosions.
628  * 
629  * 886   5/12/98 9:15a Andsager
630  * Clean up big ship sub-explosion sound.  Make single instance of sounds.
631  * Choose random sounds.  Add timestamp to post split sounds.  Make
632  * explosion flash depend on wheth ship was visible if within ~1.5 radii.
633  * 
634  * 885   5/11/98 4:33p Allender
635  * fixed ingame join problems -- started to work on new object updating
636  * code (currently ifdef'ed out)
637  * 
638  * 884   5/11/98 4:05p Lawrance
639  * Increase sanity timestamp for next_fire_time to 60 seconds
640  * 
641  * 883   5/10/98 11:30p Mike
642  * Better firing of bombs, less likely to go into strafe mode.
643  * 
644  * 882   5/09/98 4:52p Lawrance
645  * Implement padlock view (up/rear/left/right)
646  * 
647  * 881   5/08/98 5:31p Hoffoss
648  * Isolated the joystick force feedback code more from dependence on other
649  * libraries.
650  * 
651  * 880   5/08/98 4:39p Mike
652  * Comment out two Asserts that trap a condition that is actually legal. 
653  *
654  * $NoKeywords: $
655  */
656
657 #include <string.h>
658 #include <setjmp.h>
659
660 #include "pstypes.h"
661 #include "object.h"
662 #include "physics.h"
663 #include "vecmat.h"
664 #include "ship.h"
665 #include "model.h"
666 #include "key.h"
667 #include "weapon.h"
668 #include "radar.h"
669 #include "2d.h"
670 #include "3d.h"
671 #include "floating.h"
672 #include "ai.h"
673 #include "ailocal.h"
674 #include "fireballs.h"
675 #include "debris.h"
676 #include "hud.h"
677 #include "timer.h"
678 #include "cfile.h"
679 #include "missionlog.h"
680 #include "missionparse.h"
681 #include "bmpman.h"
682 #include "joy.h"
683 #include "joy_ff.h"
684 #include "player.h"
685 #include "parselo.h"
686 #include "freespace.h"
687 #include "sound.h"
688 #include "model.h"
689 #include "linklist.h"
690 #include "hudets.h"
691 #include "hudtarget.h"
692 #include "hudshield.h"
693 #include "multi.h"
694 #include "multiutil.h"
695 #include "multimsgs.h"
696 #include "aigoals.h"
697 #include "gamesnd.h"
698 #include "eventmusic.h"
699 #include "shipfx.h"
700 #include "sexp.h"
701 #include "gamesequence.h"
702 #include "objectsnd.h"
703 #include "cmeasure.h"
704 #include "animplay.h"
705 #include "controlsconfig.h"
706 #include "afterburner.h"
707 #include "shockwave.h"
708 #include "hudsquadmsg.h"
709 #include "swarm.h"
710 #include "fvi.h"
711 #include "subsysdamage.h"
712 #include "missionmessage.h"
713 #include "lighting.h"
714 #include "particle.h"
715 #include "shiphit.h"
716 #include "asteroid.h"
717 #include "hudtargetbox.h"
718 #include "multi_respawn.h"
719 #include "hudartillery.h"
720 #include "hudwingmanstatus.h"
721 #include "jumpnode.h"
722 #include "redalert.h"
723 #include "corkscrew.h"
724 #include "emp.h"
725 #include "localize.h"
726 #include "neb.h"
727 #include "shipcontrails.h"
728 #include "alphacolors.h"
729 #include "demo.h"
730 #include "beam.h"
731 #include "staticrand.h"
732 #include "missionshipchoice.h"
733
734 #if defined(FS2_DEMO) || defined(FS1_DEMO)
735         #define MAX_SHIP_SUBOBJECTS             360
736 #else
737         #define MAX_SHIP_SUBOBJECTS             700                     //      Reduced from 1000 to 400 by MK on 4/1/98.  
738                                                                                                                                 // Highest I saw was 164 in sm2-03a which Sandeep says has a lot of ships.
739                                                                                                                                 // JAS: sm3-01 needs 460.   You cannot know this number until *all* ships
740                                                                                                                                 // have warped in.   So I put code in the paging code which knows all ships
741                                                                                                                                 // that will warp in.
742 #endif
743
744 //#define MIN_COLLISION_MOVE_DIST               5.0
745 //#define COLLISION_VEL_CONST                   0.1
746 #define COLLISION_FRICTION_FACTOR       0.0             // ratio of maximum friction impulse to repulsion impulse
747 #define COLLISION_ROTATION_FACTOR       1.0             // increase in rotation from collision
748
749 int     Ai_render_debug_flag=0;
750 #ifndef NDEBUG
751 int     Ship_sphere_check = 0;
752 int     Ship_auto_repair = 1;           // flag to indicate auto-repair of subsystem should occur
753 extern void render_path_points(object *objp);
754 #endif
755
756 // mwa -- removed 11/24/97 int  num_ships = 0;
757 int     num_wings = 0;
758 int     Num_reinforcements = 0;
759 ship    Ships[MAX_SHIPS];
760 ship    *Player_ship;
761 wing    Wings[MAX_WINGS];
762 int     Starting_wings[MAX_PLAYER_WINGS];  // wings player starts a mission with (-1 = none)
763 int     ships_inited = 0;
764
765 engine_wash_info Engine_wash_info[MAX_ENGINE_WASH_TYPES];
766 char get_engine_wash_index(char *engine_wash_name);
767
768 // information for ships which have exited the game
769 exited_ship Ships_exited[MAX_EXITED_SHIPS];
770 int Num_exited_ships;
771
772 int     Num_engine_wash_types;
773 int     Num_ship_types;
774 int     Num_ship_subobj_types;
775 int     Num_ship_subobjects;
776 int     Player_ship_class;      // needs to be player specific, move to player structure        
777
778 #define         SHIP_OBJ_USED   (1<<0)                          // flag used in ship_obj struct
779 #define         MAX_SHIP_OBJS   MAX_SHIPS                       // max number of ships tracked in ship list
780 ship_obj                Ship_objs[MAX_SHIP_OBJS];               // array used to store ship object indexes
781 ship_obj                Ship_obj_list;                                                  // head of linked list of ship_obj structs
782
783 ship_info               Ship_info[MAX_SHIP_TYPES];
784 ship_subsys             Ship_subsystems[MAX_SHIP_SUBOBJECTS];
785 ship_subsys             ship_subsys_free_list;
786 reinforcements  Reinforcements[MAX_REINFORCEMENTS];
787
788 int Num_player_ship_precedence;                         // Number of ship types in Player_ship_precedence
789 int Player_ship_precedence[MAX_PLAYER_SHIP_CHOICES];    // Array of ship types, precedence list for player ship/wing selection
790
791 static int Laser_energy_out_snd_timer;  // timer so we play out of laser sound effect periodically
792 static int Missile_out_snd_timer;       // timer so we play out of laser sound effect periodically
793
794 // structure used to hold ship counts of particular types.  The order in which these appear is crucial
795 // since the goal code relies on this placement to find the array index in the Ship_counts array
796 char *Ship_type_names[MAX_SHIP_TYPE_COUNTS] = {
797 //XSTR:OFF
798         "no type",
799         "cargo",
800         "fighter/bomber",
801         "cruiser",
802         "freighter",
803         "capital",
804         "transport",
805         "support",
806         "navbuoy",
807         "sentry gun",
808         "escape pod",
809         "super cap",
810         "stealth",
811         "fighter",
812         "bomber",
813         "drydock",
814         "awacs",
815         "gas miner",
816         "corvette",
817         "knossos device"
818 //XSTR:ON
819 };
820
821 int Ship_type_flags[MAX_SHIP_TYPE_COUNTS] = {
822         0,
823         SIF_CARGO,
824         SIF_FIGHTER | SIF_BOMBER,
825         SIF_CRUISER,
826         SIF_FREIGHTER,
827         SIF_CAPITAL,
828         SIF_TRANSPORT,
829         SIF_SUPPORT,
830         SIF_NAVBUOY,
831         SIF_SENTRYGUN,
832         SIF_ESCAPEPOD,
833         SIF_SUPERCAP,
834         SIF_STEALTH,
835         SIF_FIGHTER,
836         SIF_BOMBER,
837         SIF_DRYDOCK,
838         SIF_AWACS,
839         SIF_GAS_MINER,
840         SIF_CORVETTE,
841         SIF_KNOSSOS_DEVICE,
842 };
843
844 ship_counts Ship_counts[MAX_SHIP_TYPE_COUNTS];
845
846 // I don't want to do an AI cargo check every frame, so I made a global timer to limit check to
847 // every SHIP_CARGO_CHECK_INTERVAL ms.  Didn't want to make a timer in each ship struct.  Ensure
848 // inited to 1 at mission start.
849 static int Ship_cargo_check_timer;
850
851 // a global definition of the IFF colors
852 color IFF_colors[MAX_IFF_COLORS][2];    // AL 1-2-97: Create two IFF colors, regular and bright
853
854 void ship_iff_init_colors()
855 {
856         int i, alpha;
857         int iff_bright_delta=4;
858
859         // init IFF colors
860         for ( i=0; i<2; i++ ) {
861
862                 if ( i == 0 )
863                         alpha = (HUD_COLOR_ALPHA_MAX - iff_bright_delta) * 16;
864                 else 
865                         alpha = HUD_COLOR_ALPHA_MAX * 16;
866
867                 gr_init_alphacolor( &IFF_colors[IFF_COLOR_HOSTILE][i],  0xff, 0x00, 0x00, alpha );
868                 gr_init_alphacolor( &IFF_colors[IFF_COLOR_FRIENDLY][i], 0x00, 0xff, 0x00, alpha );
869                 gr_init_alphacolor( &IFF_colors[IFF_COLOR_NEUTRAL][i],  0xff, 0x00, 0x00, alpha );
870                 gr_init_alphacolor( &IFF_colors[IFF_COLOR_UNKNOWN][i],  0xff, 0x00, 0xff, alpha );
871                 gr_init_alphacolor( &IFF_colors[IFF_COLOR_SELECTION][i], 0xff, 0xff, 0xff, alpha );
872                 gr_init_alphacolor( &IFF_colors[IFF_COLOR_MESSAGE][i],  0x7f, 0x7f, 0x7f, alpha );
873                 gr_init_alphacolor( &IFF_colors[IFF_COLOR_TAGGED][i],           0xff, 0xff, 0x00, alpha );
874         }
875 }
876
877 // set the ship_obj struct fields to default values
878 void ship_obj_list_reset_slot(int index)
879 {
880         Ship_objs[index].flags = 0;
881         Ship_objs[index].next = NULL;
882         Ship_objs[index].prev = (ship_obj*)-1;
883 }
884
885 // if the given ship is in alpha/beta/gamma/zeta wings
886 int ship_in_abgz(ship *shipp)
887 {
888         if(!strcmp(shipp->ship_name, "Alpha 1")) return 1;
889         if(!strcmp(shipp->ship_name, "Alpha 2")) return 1;
890         if(!strcmp(shipp->ship_name, "Alpha 3")) return 1;
891         if(!strcmp(shipp->ship_name, "Alpha 4")) return 1;
892
893         if(!strcmp(shipp->ship_name, "Beta 1")) return 1;
894         if(!strcmp(shipp->ship_name, "Beta 2")) return 1;
895         if(!strcmp(shipp->ship_name, "Beta 3")) return 1;
896         if(!strcmp(shipp->ship_name, "Beta 4")) return 1;
897
898         if(!strcmp(shipp->ship_name, "Gamma 1")) return 1;
899         if(!strcmp(shipp->ship_name, "Gamma 2")) return 1;
900         if(!strcmp(shipp->ship_name, "Gamma 3")) return 1;
901         if(!strcmp(shipp->ship_name, "Gamma 4")) return 1;
902
903         if(!strcmp(shipp->ship_name, "Zeta 1")) return 1;
904         if(!strcmp(shipp->ship_name, "Zeta 2")) return 1;
905         if(!strcmp(shipp->ship_name, "Zeta 3")) return 1;
906         if(!strcmp(shipp->ship_name, "Zeta 4")) return 1;
907
908         // not in
909         return 0;
910 }
911
912 // ---------------------------------------------------
913 // ship_obj_list_init()
914 //
915 void ship_obj_list_init()
916 {
917         int i;
918
919         list_init(&Ship_obj_list);
920         for ( i = 0; i < MAX_SHIP_OBJS; i++ ) {
921                 ship_obj_list_reset_slot(i);
922         }
923 }
924
925 // ---------------------------------------------------
926 // ship_obj_list_add()
927 //
928 // Function to add a node to the Ship_obj_list.  Only
929 // called from ship_create()
930 int ship_obj_list_add(int objnum)
931 {
932         int i;
933
934         for ( i = 0; i < MAX_SHIP_OBJS; i++ ) {
935                 if ( !(Ship_objs[i].flags & SHIP_OBJ_USED) )
936                         break;
937         }
938         if ( i == MAX_SHIP_OBJS ) {
939                 Error(LOCATION, "Fatal Error: Ran out of ship object nodes\n");
940                 return -1;
941         }
942         
943         Ship_objs[i].flags = 0;
944         Ship_objs[i].objnum = objnum;
945         list_append(&Ship_obj_list, &Ship_objs[i]);
946         Ship_objs[i].flags |= SHIP_OBJ_USED;
947
948         return i;
949 }
950
951 // ---------------------------------------------------
952 // ship_obj_list_remove()
953 //
954 // Function to remove a node from the Ship_obj_list.  Only
955 // called from ship_delete()
956 void ship_obj_list_remove(int index)
957 {
958         Assert(index >= 0 && index < MAX_SHIP_OBJS);
959         list_remove(&Ship_obj_list, &Ship_objs[index]); 
960         ship_obj_list_reset_slot(index);
961 }
962
963 // ---------------------------------------------------
964 // ship_obj_list_rebuild()
965 //
966 // Called from the save/restore code to re-create the Ship_obj_list
967 //
968 void ship_obj_list_rebuild()
969 {
970         object *objp;
971
972         ship_obj_list_init();
973
974         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
975                 if ( objp->type == OBJ_SHIP ) {
976                         Ships[objp->instance].ship_list_index = ship_obj_list_add(OBJ_INDEX(objp));
977                 }
978         }
979 }
980
981 ship_obj *get_ship_obj_ptr_from_index(int index)
982 {
983         Assert(index >= 0 && index < MAX_SHIP_OBJS);
984         return &Ship_objs[index];
985 }
986
987
988 // return number of ships in the game.
989 int ship_get_num_ships()
990 {
991         int count;
992         ship_obj *so;
993
994         count = 0;
995         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) )
996                 count++;
997
998         return count;
999 }
1000
1001 // parse an engine wash info record
1002 void parse_engine_wash()
1003 {
1004         engine_wash_info *ewp;
1005         ewp = &Engine_wash_info[Num_engine_wash_types];
1006
1007         // name of engine wash info
1008         required_string("$Name:");
1009         stuff_string(ewp->name, F_NAME, NULL);
1010
1011         // half angle of cone of wash from thruster
1012         required_string("$Angle:");
1013         stuff_float(&ewp->angle);
1014         ewp->angle *= (PI / 180.0f);
1015
1016         // radius multiplier for hemisphere around thruster pt
1017         required_string("$Radius Mult:");
1018         stuff_float(&ewp->radius_mult);
1019
1020         // length of cone
1021         required_string("$Length:");
1022         stuff_float(&ewp->length);
1023
1024         // intensity inside hemisphere (or at 0 distance from frustated cone)
1025         required_string("$Intensity:");
1026         stuff_float(&ewp->intensity);
1027 }
1028
1029
1030 #define SHIP_MULTITEXT_LENGTH 1500
1031 // function to parse the information for a specific ship type.  
1032 int parse_ship()
1033 {
1034         char buf[SHIP_MULTITEXT_LENGTH + 1];
1035         ship_info *sip;
1036         int cont_flag = 1;
1037         float   hull_percentage_of_hits = 100.0f;
1038         int n_subsystems = 0;
1039         model_subsystem subsystems[MAX_MODEL_SUBSYSTEMS];               // see model.h for max_model_subsystems
1040         for (int idx=0; idx<MAX_MODEL_SUBSYSTEMS; idx++) {
1041                 subsystems[idx].stepped_rotation = NULL;
1042                 subsystems[idx].ai_rotation = NULL;
1043         }
1044         int i, num_allowed, rtn = 0;
1045         int allowed_weapons[MAX_WEAPON_TYPES];
1046
1047         sip = &Ship_info[Num_ship_types];
1048
1049         //      Defaults!
1050         //      These should be specified in ships.tbl eventually!
1051         //      End of defaults.
1052
1053         required_string("$Name:");
1054         stuff_string(sip->name, F_NAME, NULL);
1055
1056         // AL 28-3-98: If this is a demo build, we only want to parse weapons that are preceded with
1057         //             the '@' symbol
1058 #ifdef DEMO // not needed FS2_DEMO (using separate table file)
1059         if ( sip->name[0] != '@' ) {
1060                 // advance to next weapon, and return -1
1061                 if ( skip_to_start_of_strings("$Name:", "#End") != 1 ) {
1062                         Int3();
1063                 }
1064                 return -1;
1065         }
1066 #endif
1067
1068 #ifdef NDEBUG
1069         if (strchr(sip->name, '#') && Fred_running)
1070                 rtn = 1;
1071 #endif
1072
1073         if ( sip->name[0] == '@' ) {
1074                 char old_name[NAME_LENGTH];
1075                 strcpy(old_name, sip->name);
1076                 strcpy(sip->name, old_name+1);
1077         }
1078
1079         diag_printf ("Ship name -- %s\n", sip->name);
1080         if ( ship_info_lookup( sip->name ) != -1 ){
1081                 Error(LOCATION, "Error:  Ship name %s already exists in ships.tbl.  All ship class names must be unique.", sip->name);
1082         }
1083
1084         required_string("$Short name:");
1085         stuff_string(sip->short_name, F_NAME, NULL);
1086         diag_printf ("Ship short name -- %s\n", sip->short_name);
1087
1088         find_and_stuff("$Species:", &sip->species, F_NAME, Species_names, MAX_SPECIES_NAMES, "species names");
1089         diag_printf ("Ship species -- %s\n", Species_names[sip->species]);
1090
1091         sip->type_str = sip->maneuverability_str = sip->armor_str = sip->manufacturer_str = NULL;
1092         if (optional_string("+Type:")) {
1093                 stuff_string(buf, F_MESSAGE, NULL);
1094                 sip->type_str = strdup(buf);
1095         }
1096
1097         if (optional_string("+Maneuverability:")) {
1098                 stuff_string(buf, F_MESSAGE, NULL);
1099                 sip->maneuverability_str = strdup(buf);
1100         }
1101
1102         if (optional_string("+Armor:")) {
1103                 stuff_string(buf, F_MESSAGE, NULL);
1104                 sip->armor_str = strdup(buf);
1105         }
1106
1107         if (optional_string("+Manufacturer:")) {
1108                 stuff_string(buf, F_MESSAGE, NULL);
1109                 sip->manufacturer_str = strdup(buf);
1110         }
1111
1112         sip->desc = NULL;
1113         if (optional_string("+Description:")) {
1114                 stuff_string(buf, F_MULTITEXT, NULL);
1115                 sip->desc = strdup(buf);
1116         }
1117
1118         sip->tech_desc = NULL;
1119         if (optional_string("+Tech Description:")) {
1120                 stuff_string(buf, F_MULTITEXT, NULL, SHIP_MULTITEXT_LENGTH);
1121                 sip->tech_desc = strdup(buf);
1122         }
1123
1124         // Code added here by SS to parse the optional strings for length, gun_mounts, missile_banks
1125
1126         sip->ship_length = NULL;
1127         if (optional_string("+Length:")) {
1128                 stuff_string(buf, F_MESSAGE, NULL);
1129                 sip->ship_length = strdup(buf);
1130         }
1131         
1132         sip->gun_mounts = NULL;
1133         if (optional_string("+Gun Mounts:")) {
1134                 stuff_string(buf, F_MESSAGE, NULL);
1135                 sip->gun_mounts = strdup(buf);
1136         }
1137         
1138         sip->missile_banks = NULL;
1139         if (optional_string("+Missile Banks:")) {
1140                 stuff_string(buf, F_MESSAGE, NULL);
1141                 sip->missile_banks = strdup(buf);
1142         }
1143
1144
1145         // End code by SS
1146
1147         sip->num_detail_levels = 0;
1148
1149         required_string( "$POF file:" );
1150         stuff_string( sip->pof_file, F_NAME, NULL );
1151
1152         // optional hud targeting model
1153         strcpy(sip->pof_file_hud, "");
1154         if(optional_string( "$POF target file:")){
1155                 stuff_string(sip->pof_file_hud, F_NAME, NULL);
1156         }
1157
1158         required_string("$Detail distance:");
1159         sip->num_detail_levels = stuff_int_list(sip->detail_distance, MAX_SHIP_DETAIL_LEVELS, RAW_INTEGER_TYPE);
1160
1161         // check for optional pixel colors
1162         sip->num_nondark_colors = 0;
1163         while(optional_string("$ND:")){         
1164                 ubyte nr, ng, nb;
1165                 stuff_byte(&nr);
1166                 stuff_byte(&ng);
1167                 stuff_byte(&nb);
1168
1169                 if(sip->num_nondark_colors < MAX_NONDARK_COLORS){
1170                         sip->nondark_colors[sip->num_nondark_colors][0] = nr;
1171                         sip->nondark_colors[sip->num_nondark_colors][1] = ng;
1172                         sip->nondark_colors[sip->num_nondark_colors++][2] = nb;
1173                 }
1174         }
1175
1176         required_string("$Show damage:");
1177         int bogus_bool;
1178         stuff_boolean(&bogus_bool);
1179
1180         required_string("$Density:");
1181         stuff_float( &(sip->density) );
1182         diag_printf ("Ship density -- %7.3f\n", sip->density);
1183
1184         required_string("$Damp:");
1185         stuff_float( &(sip->damp) );
1186         diag_printf ("Ship damp -- %7.3f\n", sip->damp);
1187
1188         required_string("$Rotdamp:");
1189         stuff_float( &(sip->rotdamp) );
1190         diag_printf ("Ship rotdamp -- %7.3f\n", sip->rotdamp);
1191
1192         required_string("$Max Velocity:");
1193         stuff_vector(&sip->max_vel);
1194
1195         // calculate the max speed from max_velocity
1196         sip->max_speed = vm_vec_mag(&sip->max_vel);
1197
1198         required_string("$Rotation Time:");
1199         stuff_vector(&sip->rotation_time);
1200
1201         sip->srotation_time = (sip->rotation_time.xyz.x + sip->rotation_time.xyz.y)/2.0f;
1202
1203         sip->max_rotvel.xyz.x = (2 * PI) / sip->rotation_time.xyz.x;
1204         sip->max_rotvel.xyz.y = (2 * PI) / sip->rotation_time.xyz.y;
1205         sip->max_rotvel.xyz.z = (2 * PI) / sip->rotation_time.xyz.z;
1206
1207         // get the backwards velocity;
1208         required_string("$Rear Velocity:");
1209         stuff_float(&sip->max_rear_vel);
1210
1211         // get the accelerations
1212         required_string("$Forward accel:");
1213         stuff_float(&sip->forward_accel );
1214
1215         required_string("$Forward decel:");
1216         stuff_float(&sip->forward_decel );
1217
1218         required_string("$Slide accel:");
1219         stuff_float(&sip->slide_accel );
1220
1221         required_string("$Slide decel:");
1222         stuff_float(&sip->slide_decel );
1223
1224         // get ship explosion info
1225         required_string("$Expl inner rad:");
1226         stuff_float(&sip->inner_rad);
1227
1228         required_string("$Expl outer rad:");
1229         stuff_float(&sip->outer_rad);
1230
1231         required_string("$Expl damage:");
1232         stuff_float(&sip->damage);
1233
1234         required_string("$Expl blast:");
1235         stuff_float(&sip->blast);
1236
1237         required_string("$Expl Propagates:");
1238         stuff_boolean(&sip->explosion_propagates);
1239
1240         required_string("$Shockwave Speed:");
1241         stuff_float( &sip->shockwave_speed );
1242
1243         sip->shockwave_count = 1;
1244         if(optional_string("$Shockwave Count:")){
1245                 stuff_int(&sip->shockwave_count);
1246         }
1247
1248         for ( i = 0; i < MAX_WEAPON_TYPES; i++ ){
1249                 sip->allowed_weapons[i] = 0;
1250         }
1251
1252         // Set the weapons filter used in weapons loadout (for primary weapons)
1253         if (optional_string("$Allowed PBanks:")) {
1254                 num_allowed = stuff_int_list(allowed_weapons, MAX_WEAPON_TYPES, WEAPON_LIST_TYPE);
1255
1256                 // actually say which weapons are allowed
1257                 for ( i = 0; i < num_allowed; i++ ) {
1258                         if ( allowed_weapons[i] >= 0 ) {                //      MK, Bug fix, 9/6/99.  Used to be "allowed_weapons" not "allowed_weapons[i]".
1259                                 sip->allowed_weapons[allowed_weapons[i]] |= 1;
1260                         }
1261                 }
1262         }
1263
1264         // Set the weapons filter used in weapons loadout (for primary weapons)
1265         if (optional_string("$Allowed Dogfight PBanks:")) {
1266                 num_allowed = stuff_int_list(allowed_weapons, MAX_WEAPON_TYPES, WEAPON_LIST_TYPE);
1267
1268                 // actually say which weapons are allowed
1269                 for ( i = 0; i < num_allowed; i++ ) {
1270                         if ( allowed_weapons[i] >= 0 ) {
1271                                 sip->allowed_weapons[allowed_weapons[i]] |= 2;
1272                         }
1273                 }
1274         }
1275
1276         //      Get default primary bank weapons
1277         for ( i = 0; i < MAX_PRIMARY_BANKS; i++ ){
1278                 sip->primary_bank_weapons[i] = -1;
1279         }
1280         required_string("$Default PBanks:");
1281         sip->num_primary_banks = stuff_int_list(sip->primary_bank_weapons, MAX_PRIMARY_BANKS, WEAPON_LIST_TYPE);
1282
1283         // error checking
1284         for ( i = 0; i < sip->num_primary_banks; i++ ) {
1285                 Assert(sip->primary_bank_weapons[i] >= 0);
1286         }
1287
1288         // Set the weapons filter used in weapons loadout (for secondary weapons)
1289         if (optional_string("$Allowed SBanks:")) {
1290                 num_allowed = stuff_int_list(allowed_weapons, MAX_WEAPON_TYPES, WEAPON_LIST_TYPE);
1291
1292                 // actually say which weapons are allowed
1293                 for ( i = 0; i < num_allowed; i++ ) {
1294                         if ( allowed_weapons[i] >= 0 ) {
1295                                 sip->allowed_weapons[allowed_weapons[i]] |= 1;
1296                         }
1297                 }
1298         }
1299
1300         // Set the weapons filter used in weapons loadout (for secondary weapons)
1301         if (optional_string("$Allowed Dogfight SBanks:")) {
1302                 num_allowed = stuff_int_list(allowed_weapons, MAX_WEAPON_TYPES, WEAPON_LIST_TYPE);
1303
1304                 // actually say which weapons are allowed
1305                 for ( i = 0; i < num_allowed; i++ ) {
1306                         if ( allowed_weapons[i] >= 0 ) {
1307                                 sip->allowed_weapons[allowed_weapons[i]] |= 2;
1308                         }
1309                 }
1310         }
1311
1312         //      Get default secondary bank weapons
1313         for ( i = 0; i < MAX_SECONDARY_BANKS; i++ ){
1314                 sip->secondary_bank_weapons[i] = -1;
1315         }
1316         required_string("$Default SBanks:");
1317         sip->num_secondary_banks = stuff_int_list(sip->secondary_bank_weapons, MAX_SECONDARY_BANKS, WEAPON_LIST_TYPE);
1318
1319         // error checking
1320         for ( i = 0; i < sip->num_secondary_banks; i++ ) {
1321                 Assert(sip->secondary_bank_weapons[i] >= 0);
1322         }
1323
1324         // Get the capacity of each secondary bank
1325         required_string("$Sbank Capacity:");
1326         int capacity_count;
1327         capacity_count = stuff_int_list(sip->secondary_bank_ammo_capacity, MAX_SECONDARY_BANKS, RAW_INTEGER_TYPE);
1328         if ( capacity_count != sip->num_secondary_banks ) {
1329                 Warning(LOCATION, "Secondary bank capacities have not been specified for ship class %s... fix this!!", sip->name);
1330         }
1331
1332         required_string("$Shields:");
1333         stuff_float(&sip->shields);
1334
1335         // optional shield color
1336         sip->shield_color[0] = 255;
1337         sip->shield_color[1] = 255;
1338         sip->shield_color[2] = 255;
1339         if(optional_string("$Shield Color:")){
1340                 stuff_byte(&sip->shield_color[0]);
1341                 stuff_byte(&sip->shield_color[1]);
1342                 stuff_byte(&sip->shield_color[2]);
1343         }
1344
1345         // The next three fields are used for the ETS
1346         required_string("$Power Output:");
1347         stuff_float(&sip->power_output);
1348
1349         required_string("$Max Oclk Speed:");
1350         stuff_float(&sip->max_overclocked_speed);
1351
1352         required_string("$Max Weapon Eng:");
1353         stuff_float(&sip->max_weapon_reserve);
1354
1355         required_string("$Hitpoints:");
1356         stuff_float(&sip->initial_hull_strength);
1357
1358         required_string("$Flags:");
1359         char    ship_strings[MAX_SHIP_FLAGS][NAME_LENGTH];
1360         int num_strings = stuff_string_list(ship_strings, MAX_SHIP_FLAGS);
1361         sip->flags = SIF_DEFAULT_VALUE;
1362         for ( i=0; i<num_strings; i++ ) {
1363                 if (!stricmp(NOX("no_collide"), ship_strings[i]))
1364                         sip->flags &= ~SIF_DO_COLLISION_CHECK;
1365                 else if (!stricmp(NOX("player_ship"), ship_strings[i]))
1366                         sip->flags |= SIF_PLAYER_SHIP;
1367                 else if (!stricmp(NOX("default_player_ship"), ship_strings[i]))
1368                         sip->flags |= SIF_DEFAULT_PLAYER_SHIP;
1369                 else if ( !stricmp(NOX("repair_rearm"), ship_strings[i]))
1370                         sip->flags |= SIF_SUPPORT;
1371                 else if ( !stricmp(NOX("cargo"), ship_strings[i]))
1372                         sip->flags |= SIF_CARGO;
1373                 else if ( !stricmp( NOX("fighter"), ship_strings[i]))
1374                         sip->flags |= SIF_FIGHTER;
1375                 else if ( !stricmp( NOX("bomber"), ship_strings[i]))
1376                         sip->flags |= SIF_BOMBER;
1377                 else if ( !stricmp( NOX("transport"), ship_strings[i]))
1378                         sip->flags |= SIF_TRANSPORT;
1379                 else if ( !stricmp( NOX("freighter"), ship_strings[i]))
1380                         sip->flags |= SIF_FREIGHTER;
1381                 else if ( !stricmp( NOX("capital"), ship_strings[i]))
1382                         sip->flags |= SIF_CAPITAL;
1383                 else if (!stricmp( NOX("supercap"), ship_strings[i]))
1384                         sip->flags |= SIF_SUPERCAP;
1385                 else if (!stricmp( NOX("drydock"), ship_strings[i]))
1386                         sip->flags |= SIF_DRYDOCK;
1387                 else if ( !stricmp( NOX("cruiser"), ship_strings[i]))
1388                         sip->flags |= SIF_CRUISER;
1389                 else if ( !stricmp( NOX("navbuoy"), ship_strings[i]))
1390                         sip->flags |= SIF_NAVBUOY;
1391                 else if ( !stricmp( NOX("sentrygun"), ship_strings[i]))
1392                         sip->flags |= SIF_SENTRYGUN;
1393                 else if ( !stricmp( NOX("escapepod"), ship_strings[i]))
1394                         sip->flags |= SIF_ESCAPEPOD;
1395                 else if ( !stricmp( NOX("no type"), ship_strings[i]))
1396                         sip->flags |= SIF_NO_SHIP_TYPE;
1397                 else if ( !stricmp( NOX("ship copy"), ship_strings[i]))
1398                         sip->flags |= SIF_SHIP_COPY;
1399                 else if ( !stricmp( NOX("in tech database"), ship_strings[i]))
1400                         sip->flags |= SIF_IN_TECH_DATABASE | SIF_IN_TECH_DATABASE_M;
1401                 else if ( !stricmp( NOX("in tech database multi"), ship_strings[i]))
1402                         sip->flags |= SIF_IN_TECH_DATABASE_M;
1403                 else if ( !stricmp( NOX("dont collide invisible"), ship_strings[i]))
1404                         sip->flags |= SIF_DONT_COLLIDE_INVIS;
1405                 else if ( !stricmp( NOX("big damage"), ship_strings[i]))
1406                         sip->flags |= SIF_BIG_DAMAGE;
1407                 else if ( !stricmp( NOX("corvette"), ship_strings[i]))
1408                         sip->flags |= SIF_CORVETTE;
1409                 else if ( !stricmp( NOX("gas miner"), ship_strings[i]))
1410                         sip->flags |= SIF_GAS_MINER;
1411                 else if ( !stricmp( NOX("awacs"), ship_strings[i]))
1412                         sip->flags |= SIF_AWACS;
1413                 else if ( !stricmp( NOX("knossos"), ship_strings[i]))
1414                         sip->flags |= SIF_KNOSSOS_DEVICE;
1415                 else if ( !stricmp( NOX("no_fred"), ship_strings[i]))
1416                         sip->flags |= SIF_NO_FRED;
1417                 else
1418                         Warning(LOCATION, "Bogus string in ship flags: %s\n", ship_strings[i]);
1419         }
1420
1421         find_and_stuff("$AI Class:", &sip->ai_class, F_NAME, Ai_class_names, Num_ai_classes, "AI class names");
1422
1423         // Get Afterburner information
1424         // Be aware that if $Afterburner is not 1, the other Afterburner fields are not read in
1425         required_string("$Afterburner:");
1426         int has_afterburner;
1427         stuff_boolean(&has_afterburner);
1428         if ( has_afterburner == 1 ) {
1429                 sip->flags |= SIF_AFTERBURNER;
1430
1431                 required_string("+Aburn Max Vel:");
1432                 stuff_vector(&sip->afterburner_max_vel);
1433
1434                 required_string("+Aburn For accel:");
1435                 stuff_float(&sip->afterburner_forward_accel);
1436
1437                 required_string("+Aburn Fuel:");
1438                 stuff_float(&sip->afterburner_fuel_capacity);
1439
1440                 required_string("+Aburn Burn Rate:");
1441                 stuff_float(&sip->afterburner_burn_rate);
1442
1443
1444                 required_string("+Aburn Rec Rate:");
1445                 stuff_float(&sip->afterburner_recover_rate);
1446         } else {
1447                 sip->afterburner_max_vel.xyz.x = 0.0f;
1448                 sip->afterburner_max_vel.xyz.y = 0.0f;
1449                 sip->afterburner_max_vel.xyz.z = 0.0f;
1450         }
1451
1452         required_string("$Countermeasures:");
1453         stuff_int(&sip->cmeasure_max);
1454
1455         required_string("$Scan time:");
1456         stuff_int(&sip->scan_time);
1457
1458         required_string("$EngineSnd:");
1459         stuff_int(&sip->engine_snd);
1460
1461         required_string("$Closeup_pos:");
1462         stuff_vector(&sip->closeup_pos);
1463
1464         required_string("$Closeup_zoom:");
1465         stuff_float(&sip->closeup_zoom);
1466
1467         sip->shield_icon_index = 255;           // stored as ubyte
1468         if (optional_string("$Shield_icon:")) {
1469                 char tmpbuf[NAME_LENGTH];
1470                 stuff_string(tmpbuf, F_NAME, NULL);
1471                 hud_shield_assign_info(sip, tmpbuf);
1472         }
1473
1474         // read in filename for icon that is used in ship selection
1475         sip->icon_filename[0] = 0;
1476         if ( optional_string("$Ship_icon:") ) {
1477                 stuff_string(sip->icon_filename, F_NAME, NULL);
1478         }
1479
1480         // read in filename for animation that is used in ship selection
1481         sip->anim_filename[0] = 0;
1482         if ( optional_string("$Ship_anim:") ) {
1483                 stuff_string(sip->anim_filename, F_NAME, NULL);
1484         }
1485
1486         // read in filename for animation that is used in ship selection
1487         sip->overhead_filename[0] = 0;
1488         if ( optional_string("$Ship_overhead:") ) {
1489                 stuff_string(sip->overhead_filename, F_NAME, NULL);
1490         }
1491
1492         sip->score = 0;
1493         if ( optional_string("$Score:") ){
1494                 stuff_int( &sip->score );
1495         }
1496
1497         // if the ship is a stealth ship
1498         if ( optional_string("$Stealth:") ){
1499                 sip->flags |= SIF_STEALTH;
1500         }
1501
1502 #ifndef MAKE_FS1
1503         // parse contrail info
1504         char trail_name[MAX_FILENAME_LEN] = "";
1505         trail_info *ci;
1506         memset(&sip->ct_info, 0, sizeof(trail_info) * MAX_SHIP_CONTRAILS);
1507         sip->ct_count = 0;
1508         while(optional_string("$Trail:")){
1509                 // this means you've reached the max # of contrails for a ship
1510                 Assert(sip->ct_count <= MAX_SHIP_CONTRAILS);
1511
1512                 ci = &sip->ct_info[sip->ct_count++];
1513                 
1514                 required_string("+Offset:");
1515                 stuff_vector(&ci->pt);
1516                 
1517                 required_string("+Start Width:");
1518                 stuff_float(&ci->w_start);
1519                 
1520                 required_string("+End Width:");
1521                 stuff_float(&ci->w_end);
1522                 
1523                 required_string("+Start Alpha:");
1524                 stuff_float(&ci->a_start);
1525                 
1526                 required_string("+End Alpha:");
1527                 stuff_float(&ci->a_end);
1528
1529                 required_string("+Max Life:");
1530                 stuff_float(&ci->max_life);
1531                 
1532                 required_string("+Spew Time:");
1533                 stuff_int(&ci->stamp);          
1534
1535                 required_string("+Bitmap:");
1536                 stuff_string(trail_name, F_NAME, NULL);
1537                 ci->bitmap = bm_load(trail_name);
1538         }
1539 #endif
1540
1541         while (cont_flag) {
1542                 int r = required_string_3("#End", "$Subsystem:", "$Name" );
1543                 switch (r) {
1544                 case 0:
1545                         cont_flag = 0;
1546                         break;
1547                 case 1:
1548                 {
1549                         float   turning_rate;
1550                         float   percentage_of_hits;
1551                         model_subsystem *sp;                    // to append on the ships list of subsystems
1552
1553                         Assert ( n_subsystems < MAX_MODEL_SUBSYSTEMS );
1554                         sp = &subsystems[n_subsystems++];                       // subsystems a local -- when done, we will malloc and copy
1555                         required_string("$Subsystem:");
1556                         stuff_string(sp->subobj_name, F_NAME, ",");
1557                         Mp++;
1558                         stuff_float(&percentage_of_hits);
1559                         stuff_float(&turning_rate);
1560                         hull_percentage_of_hits -= percentage_of_hits;
1561                         sp->max_hits = sip->initial_hull_strength * (percentage_of_hits / 100.0f);
1562                         sp->type = SUBSYSTEM_UNKNOWN;
1563                         // specified as how long to turn 360 degrees in ships.tbl
1564                         if ( turning_rate > 0.0f ){
1565                                 sp->turret_turning_rate = PI2 / turning_rate;           
1566                         } else {
1567                                 sp->turret_turning_rate = 0.0f;         
1568                         }
1569
1570                         for (i=0; i<MAX_PRIMARY_BANKS; i++)
1571                                 sp->primary_banks[i] = -1;
1572                         for (i=0; i<MAX_SECONDARY_BANKS; i++) {
1573                                 sp->secondary_banks[i] = -1;
1574                                 sp->secondary_bank_capacity[i] = 0;
1575                         }
1576
1577                         //      Get default primary bank weapons
1578                         if (optional_string("$Default PBanks:")){
1579                                 stuff_int_list(sp->primary_banks, MAX_PRIMARY_BANKS, WEAPON_LIST_TYPE);
1580                         }
1581
1582                         //      Get default secondary bank weapons
1583                         if (optional_string("$Default SBanks:")){
1584                                 stuff_int_list(sp->secondary_banks, MAX_SECONDARY_BANKS, WEAPON_LIST_TYPE);
1585                         }
1586
1587                         // Get the capacity of each secondary bank
1588                         if (optional_string("$Sbank Capacity:")){
1589                                 stuff_int_list(sp->secondary_bank_capacity, MAX_SECONDARY_BANKS, RAW_INTEGER_TYPE);
1590                         }
1591
1592                         // Get optional engine wake info
1593                         if (optional_string("$Engine Wash:")) {
1594                                 char engine_wash_name[32];
1595                                 stuff_string(engine_wash_name, F_NAME, NULL);
1596                                 // get and set index
1597                                 sp->engine_wash_index = get_engine_wash_index(engine_wash_name);
1598                         } else {
1599                                 sp->engine_wash_index = -1;
1600                         }
1601                                 
1602                         // Get any AWACS info
1603                         sp->awacs_intensity = 0.0f;
1604                         if(optional_string("$AWACS:")){
1605                                 stuff_float(&sp->awacs_intensity);
1606                                 stuff_float(&sp->awacs_radius);
1607                                 sip->flags |= SIF_HAS_AWACS;
1608                         }
1609
1610                         sp->turret_weapon_type = sp->primary_banks[0];  // temporary, will be obsolete later.
1611                         if ( sp->turret_weapon_type < 0 ) {
1612                                 sp->turret_weapon_type = sp->secondary_banks[0];
1613                         }
1614                         sp->model_num = -1;             // init value for later sanity checking!!
1615                 }
1616                 break;
1617                 case 2:
1618                         cont_flag = 0;
1619                         break;
1620                 default:
1621                         Int3(); // Impossible return value from required_string_3.
1622                 }
1623         }       
1624         Assert( hull_percentage_of_hits > 0.0f );               // must be > 0
1625
1626         // when done reading subsystems, malloc and copy the subsystem data to the ship info structure
1627         sip->n_subsystems = n_subsystems;
1628         if ( n_subsystems > 0 ) {
1629                 sip->subsystems = (model_subsystem *)malloc(sizeof(model_subsystem) * n_subsystems );
1630                 Assert( sip->subsystems != NULL );
1631         }
1632         else {
1633                 sip->subsystems = NULL;
1634         }
1635                 
1636         for ( i = 0; i < n_subsystems; i++ ){
1637                 sip->subsystems[i] = subsystems[i];
1638         }
1639
1640         // if we have a ship copy, then check to be sure that our base ship exists
1641         if ( sip->flags & SIF_SHIP_COPY ) {
1642                 int index;
1643
1644                 index = ship_info_base_lookup( Num_ship_types );                // Num_ship_types is our current entry into the array
1645                 if ( index == -1 ) {
1646                         char *p, name[NAME_LENGTH];;
1647
1648                         strcpy( name, sip->name );
1649                         p = strchr(name, '#');
1650                         if ( p )
1651                                 *p = '\0';
1652                         Error(LOCATION, "Ship %s is a copy, but base ship %s couldn't be found.", sip->name, name);
1653                 }
1654         }
1655
1656         return rtn;
1657 }
1658
1659 char get_engine_wash_index(char *engine_wash_name)
1660 {
1661         int i;
1662
1663         for (i=0; i<Num_engine_wash_types; i++) {
1664                 if ( 0 == stricmp(engine_wash_name, Engine_wash_info[i].name) ) {
1665                         return (char)i;
1666                 }
1667         }
1668
1669         // not found, so return -1
1670         return -1;
1671 }
1672
1673 void parse_shiptbl()
1674 {
1675         // open localization
1676         lcl_ext_open();
1677         
1678         read_file_text("ships.tbl");
1679         reset_parse();
1680
1681 #ifndef MAKE_FS1
1682         // parse default ship
1683         required_string("#Default Player Ship");
1684         required_string("$Name:");
1685         stuff_string(default_player_ship, F_NAME, NULL, 254);
1686         required_string("#End");
1687 #endif
1688
1689 #ifndef MAKE_FS1
1690         Num_engine_wash_types = 0;
1691 #endif
1692         Num_ship_types = 0;
1693
1694 #ifndef MAKE_FS1
1695         required_string("#Engine Wash Info");
1696         while (required_string_either("#End", "$Name:")) {
1697                 Assert( Num_engine_wash_types < MAX_ENGINE_WASH_TYPES );
1698
1699                 parse_engine_wash();
1700                 Num_engine_wash_types++;
1701         }
1702
1703         required_string("#End");
1704 #endif
1705         required_string("#Ship Classes");
1706
1707         while (required_string_either("#End","$Name:")) {
1708                 Assert( Num_ship_types < MAX_SHIP_TYPES );
1709
1710                 if ( parse_ship() ) {
1711                         continue;
1712                 }
1713
1714                 Num_ship_types++;
1715         }
1716
1717         required_string("#End");
1718
1719         // Read in a list of ship_info indicies that are an ordering of the player ship precedence.
1720         // This list is used to select an alternate ship when a particular ship is not available
1721         // during ship selection.
1722         required_string("$Player Ship Precedence:");
1723         Num_player_ship_precedence = stuff_int_list(Player_ship_precedence, MAX_PLAYER_SHIP_CHOICES, SHIP_INFO_TYPE);
1724
1725         // close localization
1726         lcl_ext_close();
1727 }
1728
1729 int ship_show_velocity_dot = 0;
1730
1731
1732 DCF_BOOL( show_velocity_dot, ship_show_velocity_dot )
1733
1734
1735 // Called once at the beginning of the game to parse ships.tbl and stuff the Ship_info[]
1736 // structure
1737 void ship_init()
1738 {
1739         int rval;
1740
1741         if ( !ships_inited ) {
1742                 
1743                 if ((rval = setjmp(parse_abort)) != 0) {
1744                         Error(LOCATION, "Error parsing 'ships.tbl'\r\nError code = %i.\r\n", rval);
1745                 } else {                        
1746                         parse_shiptbl();
1747                         ships_inited = 1;
1748                 }
1749
1750                 ship_iff_init_colors();
1751         }
1752
1753         ship_level_init();      // needed for FRED
1754 }
1755
1756
1757 // This will get called at the start of each level.
1758 void ship_level_init()
1759 {
1760         int i;
1761
1762         // Reset everything between levels
1763
1764
1765         // Mark all the models as invalid, since all the models get paged out 
1766         // between levels.
1767         for (i=0; i<MAX_SHIP_TYPES; i++ )       {
1768                 Ship_info[i].modelnum = -1;
1769                 Ship_info[i].modelnum_hud = -1;
1770         }
1771
1772         // mwa removed 11/24/97  num_ships = 0;
1773         Num_exited_ships = 0;
1774         for (i=0; i<MAX_SHIPS; i++ )    {
1775                 Ships[i].objnum = -1;
1776         }
1777
1778         for ( i = 0; i < MAX_EXITED_SHIPS; i++ ) {
1779                 memset ( &Ships_exited[i], 0, sizeof(exited_ship) );
1780                 Ships_exited[i].obj_signature = -1;
1781         }
1782
1783         num_wings = 0;
1784         for (i = 0; i < MAX_WINGS; i++ )
1785                 Wings[i].num_waves = -1;
1786
1787         for (i=0; i<MAX_PLAYER_WINGS; i++)
1788                 Starting_wings[i] = -1;
1789
1790         // Empty the subsys list
1791         memset( Ship_subsystems, 0, sizeof(ship_subsys)*MAX_SHIP_SUBOBJECTS );
1792
1793         list_init( &ship_subsys_free_list );
1794         for ( i = 0; i < MAX_SHIP_SUBOBJECTS; i++ )
1795                 list_append( &ship_subsys_free_list, &Ship_subsystems[i] );
1796
1797         Laser_energy_out_snd_timer = 1;
1798         Missile_out_snd_timer           = 1;
1799
1800         for (i = 0; i < MAX_SHIP_TYPE_COUNTS; i++ ) {
1801                 Ship_counts[i].total = 0;
1802                 Ship_counts[i].killed = 0;
1803         }
1804
1805         ship_obj_list_init();
1806
1807         Ship_cargo_check_timer = 1;
1808
1809         shipfx_large_blowup_level_init();
1810 }
1811
1812 // function to add a ship onto the exited ships list.  The reason parameter
1813 // tells us why the ship left the mission (i.e. departed or destroyed)
1814 void ship_add_exited_ship( ship *sp, int reason )
1815 {
1816         int i, entry;
1817
1818         // reuse oldest slots if none left
1819         if ( Num_exited_ships == MAX_EXITED_SHIPS ) {
1820                 int oldest_entry;
1821
1822                 // find the oldest entry
1823                 oldest_entry = 0;
1824                 for ( i = 1; i < MAX_SHIPS; i++ ) {
1825                         if ( Ships_exited[i].time < Ships_exited[oldest_entry].time ) {
1826                                 oldest_entry = i;
1827                         }
1828                 }
1829                 entry = oldest_entry;
1830         } else {
1831                 entry = Num_exited_ships;
1832                 Num_exited_ships++;
1833         }
1834
1835         strcpy( Ships_exited[entry].ship_name, sp->ship_name );
1836         Ships_exited[entry].obj_signature = Objects[sp->objnum].signature;
1837         Ships_exited[entry].team = sp->team;
1838         Ships_exited[entry].flags = reason;
1839         // if ship is red alert, flag as such
1840         if (sp->flags & SF_RED_ALERT_STORE_STATUS) {
1841                 Ships_exited[entry].flags |= SEF_RED_ALERT_CARRY;
1842         }
1843         Ships_exited[entry].time = Missiontime;
1844         Ships_exited[entry].hull_strength = int(Objects[sp->objnum].hull_strength);
1845
1846         if ( sp->flags & SF_CARGO_REVEALED )
1847                 Ships_exited[entry].flags |= SEF_CARGO_KNOWN;
1848         if ( sp->time_first_tagged > 0 )
1849                 Ships_exited[entry].flags |= SEF_BEEN_TAGGED;
1850 }
1851
1852 // function which attempts to find information about an exited ship based on shipname
1853 int ship_find_exited_ship_by_name( char *name )
1854 {
1855         int i;
1856
1857         for (i = 0; i < Num_exited_ships; i++) {
1858                 if ( !stricmp(name, Ships_exited[i].ship_name) )
1859                         break;
1860         }
1861
1862         if ( i == Num_exited_ships )
1863                 return -1;
1864         else
1865                 return i;
1866 }
1867
1868 // function which attempts to find information about an exited ship based on shipname
1869 int ship_find_exited_ship_by_signature( int signature )
1870 {
1871         int i;
1872
1873         for (i = 0; i < Num_exited_ships; i++) {
1874                 if ( signature == Ships_exited[i].obj_signature )
1875                         break;
1876         }
1877
1878         if ( i == Num_exited_ships )
1879                 return -1;
1880         else
1881                 return i;
1882 }
1883
1884
1885 void physics_ship_init(object *objp)
1886 {
1887         ship_info       *sinfo = &Ship_info[Ships[objp->instance].ship_info_index];
1888         physics_info    *pi = &objp->phys_info;
1889         polymodel *pm = model_get( Ships[objp->instance].modelnum );
1890
1891         // use mass and I_body_inv from POF read into polymodel
1892         physics_init(pi);
1893         pi->mass = pm->mass * sinfo->density;
1894         pi->I_body_inv = pm->moment_of_inertia;
1895         // scale pm->I_body_inv value by density
1896         vm_vec_scale( &pi->I_body_inv.v.rvec, sinfo->density );
1897         vm_vec_scale( &pi->I_body_inv.v.uvec, sinfo->density );
1898         vm_vec_scale( &pi->I_body_inv.v.fvec, sinfo->density );
1899
1900         pi->side_slip_time_const = sinfo->damp;
1901         pi->rotdamp = sinfo->rotdamp;
1902         pi->max_vel = sinfo->max_vel;
1903         pi->afterburner_max_vel = sinfo->afterburner_max_vel;
1904         pi->max_rotvel = sinfo->max_rotvel;
1905         pi->max_rear_vel = sinfo->max_rear_vel;
1906         pi->flags |= PF_ACCELERATES;
1907
1908
1909
1910
1911         pi->forward_accel_time_const=sinfo->forward_accel;
1912         pi->afterburner_forward_accel_time_const=sinfo->afterburner_forward_accel;
1913         pi->forward_decel_time_const=sinfo->forward_decel;
1914         pi->slide_accel_time_const=sinfo->slide_accel;
1915         pi->slide_decel_time_const=sinfo->slide_decel;
1916
1917         if ( (pi->max_vel.xyz.x > 0.000001f) || (pi->max_vel.xyz.y > 0.000001f) )
1918                 pi->flags |= PF_SLIDE_ENABLED;
1919
1920         vm_vec_zero(&pi->vel);
1921         vm_vec_zero(&pi->rotvel);
1922         pi->speed = 0.0f;
1923         pi->heading = 0.0f;
1924 //      pi->accel = 0.0f;
1925         vm_set_identity(&pi->last_rotmat);
1926 }
1927
1928 // function to set the orders allowed for a ship -- based on ship type.  This value might get overridden
1929 // by a value in the mission file.
1930 int ship_get_default_orders_accepted( ship_info *sip )
1931 {
1932         int ship_info_flag;
1933
1934         ship_info_flag = sip->flags;
1935
1936         if ( ship_info_flag & SIF_FIGHTER )
1937                 return FIGHTER_MESSAGES;
1938         else if ( ship_info_flag & SIF_BOMBER )
1939                 return BOMBER_MESSAGES;
1940         else if ( ship_info_flag & (SIF_CRUISER | SIF_GAS_MINER | SIF_AWACS | SIF_CORVETTE) )
1941                 return CRUISER_MESSAGES;
1942         else if ( ship_info_flag & SIF_FREIGHTER )
1943                 return FREIGHTER_MESSAGES;
1944         else if ( ship_info_flag & SIF_CAPITAL )
1945                 return CAPITAL_MESSAGES;
1946         else if ( ship_info_flag & SIF_TRANSPORT )
1947                 return TRANSPORT_MESSAGES;
1948         else if ( ship_info_flag & SIF_SUPPORT )
1949                 return SUPPORT_MESSAGES;
1950         else
1951                 return 0;
1952 }
1953
1954 void ship_set(int ship_index, int objnum, int ship_type)
1955 {
1956         int i;
1957
1958         object  *objp = &Objects[objnum];
1959         ship    *shipp = &Ships[ship_index];
1960         ship_weapon     *swp = &shipp->weapons;
1961         ship_info       *sip = &(Ship_info[ship_type]);
1962
1963         // Create n!
1964         // sprintf(shipp->ship_name, "%s %d", Ship_info[ship_type].name, ship_index); // moved to ship_create()
1965         Assert(strlen(shipp->ship_name) < NAME_LENGTH - 1);
1966         shipp->ship_info_index = ship_type;
1967         shipp->objnum = objnum;
1968         shipp->group = 0;
1969         shipp->reinforcement_index = -1;
1970         shipp->cargo1 = 0;
1971         shipp->hotkey = -1;
1972         shipp->score = 0;
1973         shipp->escort_priority = 0;
1974         shipp->special_exp_index = -1;
1975         shipp->num_hits = 0;
1976         shipp->flags = 0;
1977         shipp->wash_killed = 0;
1978         shipp->time_cargo_revealed = 0;
1979         shipp->time_first_tagged = 0;
1980         shipp->wash_timestamp = timestamp(0);
1981         shipp->dock_objnum_when_dead = -1;
1982         shipp->large_ship_blowup_index = -1;
1983         shipp->respawn_priority = 0;
1984         for (i=0; i<NUM_SUB_EXPL_HANDLES; i++) {
1985                 shipp->sub_expl_sound_handle[i] = -1;
1986         }
1987
1988         if ( !Fred_running ) {
1989                 shipp->final_warp_time = timestamp(-1);
1990                 shipp->final_death_time = timestamp(-1);        // There death sequence ain't start et.
1991                 shipp->really_final_death_time = timestamp(-1); // There death sequence ain't start et.
1992                 shipp->next_fireball = timestamp(-1);           // When a random fireball will pop up
1993                 shipp->next_hit_spark = timestamp(-1);          // when a hit spot will spark
1994                 for (i=0; i<MAX_SHIP_ARCS; i++ )        {
1995                         shipp->arc_timestamp[i] = timestamp(-1);                // Mark this arc as unused
1996                 }
1997                 shipp->arc_next_time = timestamp(-1);           // No electrical arcs yet.
1998         } else {                // the values should be different for Fred
1999                 shipp->final_warp_time = -1;
2000                 shipp->final_death_time = 0;
2001                 shipp->really_final_death_time = -1;
2002                 shipp->next_fireball = -1;
2003                 shipp->next_hit_spark = -1;
2004                 for (i=0; i<MAX_SHIP_ARCS; i++ )        {
2005                         shipp->arc_timestamp[i] = -1;
2006                 }
2007                 shipp->arc_next_time = -1;
2008         }
2009         shipp->team = TEAM_FRIENDLY;                                    //      Default friendly, probably get overridden.
2010         shipp->arrival_location = 0;
2011         shipp->arrival_distance = 0;
2012         shipp->arrival_anchor = -1;
2013         shipp->arrival_delay = 0;
2014         shipp->arrival_cue = -1;
2015         shipp->departure_location = 0;
2016         shipp->departure_delay = 0;
2017         shipp->departure_cue = -1;
2018         shipp->shield_hits = 0;                                                 //      No shield hits yet on this baby.
2019         shipp->current_max_speed = Ship_info[ship_type].max_speed;
2020
2021         shipp->alt_type_index = -1;
2022
2023         shipp->lightning_stamp = -1;
2024
2025         shipp->emp_intensity = -1.0f;
2026         shipp->emp_decr = 0.0f;
2027
2028         shipp->targeting_laser_bank = -1;
2029         shipp->targeting_laser_objnum = -1;
2030
2031         shipp->determination = 10;
2032         shipp->wingnum = -1;
2033         for (i = 0; i < MAX_PLAYERS; i++)
2034                 shipp->last_targeted_subobject[i] = NULL;
2035
2036         if (Fred_running){
2037                 objp->hull_strength = 100.0f;
2038         } else {
2039                 objp->hull_strength = sip->initial_hull_strength;
2040         }
2041         
2042         shipp->afterburner_fuel = sip->afterburner_fuel_capacity;
2043
2044         shipp->cmeasure_count = sip->cmeasure_max;
2045
2046         for ( i = 0; i < MAX_PRIMARY_BANKS; i++ ){
2047                 swp->next_primary_fire_stamp[i] = timestamp(0);         
2048         }
2049
2050         shipp->cmeasure_fire_stamp = timestamp(0);
2051
2052         for ( i = 0; i < MAX_SECONDARY_BANKS; i++ ) {
2053                 if (Fred_running){
2054                         shipp->weapons.secondary_bank_ammo[i] = 100;
2055                 } else {
2056                         shipp->weapons.secondary_bank_ammo[i] = 0;
2057                 }
2058                         
2059                 swp->secondary_bank_ammo[i] = 0;
2060                 swp->secondary_next_slot[i] = 0;
2061                 swp->next_secondary_fire_stamp[i] = timestamp(0);
2062                 swp->secondary_bank_rearm_time[i] = timestamp(0);               // will be able to rearm this bank immediately
2063         }
2064
2065         for ( i = 0; i < sip->num_secondary_banks; i++ ) {
2066                 float weapon_size;
2067                 weapon_size = Weapon_info[sip->secondary_bank_weapons[i]].cargo_size;
2068                 Assert( weapon_size > 0.0f );
2069                 if (Fred_running){
2070                         swp->secondary_bank_ammo[i] = 100;
2071                 } else {
2072                         swp->secondary_bank_ammo[i] = fl2i(sip->secondary_bank_ammo_capacity[i] / weapon_size + 0.5f );
2073                 }
2074         }
2075
2076         swp->current_primary_bank = -1;
2077         swp->current_secondary_bank = -1;
2078
2079
2080         if ( sip->num_primary_banks > 0 ) {
2081                 if ( swp->primary_bank_weapons[BANK_1] >= 0 ) {
2082                         swp->current_primary_bank = BANK_1;
2083                 } else {
2084                         swp->current_primary_bank = -1;
2085                 }
2086         }
2087         else {
2088                 swp->current_primary_bank = -1;
2089         }
2090
2091         if ( sip->num_secondary_banks > 0 ) {
2092                 if ( swp->secondary_bank_weapons[BANK_1] >= 0 ) {
2093                         swp->current_secondary_bank = BANK_1;
2094                 } else {
2095                         swp->current_secondary_bank = -1;
2096                 }
2097         }
2098         else {
2099                 swp->current_secondary_bank = -1;
2100         }
2101
2102         shipp->current_cmeasure = 0;
2103
2104         ets_init_ship(objp);    // init ship fields that are used for the ETS
2105
2106         physics_ship_init(objp);
2107         if (Fred_running)
2108                 objp->shields[0] = 100.0f;
2109         else
2110                 set_shield_strength(objp, sip->shields);
2111
2112         shipp->target_shields_delta = 0.0f;
2113         shipp->target_weapon_energy_delta = 0.0f;
2114
2115         ai_object_init(objp, shipp->ai_index);
2116         shipp->weapons.ai_class = Ai_info[shipp->ai_index].ai_class;
2117         shipp->shield_integrity = NULL;
2118 //      shipp->sw.blast_duration = -1;  // init shockwave struct
2119         subsys_set(objnum);
2120         shipp->orders_accepted = ship_get_default_orders_accepted( sip );
2121         shipp->num_swarm_missiles_to_fire = 0;  
2122         shipp->num_turret_swarm_info = 0;
2123         shipp->death_roll_snd = -1;
2124         shipp->thruster_bitmap = -1;
2125         shipp->thruster_frame = 0.0f;
2126         shipp->thruster_glow_bitmap = -1;
2127         shipp->thruster_glow_noise = 1.0f;
2128         shipp->thruster_glow_frame = 0.0f;
2129         shipp->next_engine_stutter = 1;
2130         shipp->persona_index = -1;
2131         shipp->flags |= SF_ENGINES_ON;
2132         shipp->subsys_disrupted_flags=0;
2133         shipp->subsys_disrupted_check_timestamp=timestamp(0);
2134
2135         // swarm missile stuff
2136         shipp->next_swarm_fire = 1;
2137
2138         // corkscrew missile stuff
2139         shipp->next_corkscrew_fire = 1;
2140
2141         // field for score
2142         shipp->score = sip->score;
2143
2144         // tag
2145         shipp->tag_left = -1.0f;
2146         shipp->level2_tag_left = -1.0f;
2147
2148         // multiplayer field initializations
2149         for (i = 0; i < MAX_PLAYERS; i++ ) {
2150                 shipp->np_updates[i].update_stamp = -1;
2151                 shipp->np_updates[i].status_update_stamp = -1;
2152                 shipp->np_updates[i].subsys_update_stamp = -1;
2153                 shipp->np_updates[i].seq = 0;           
2154         }               
2155         extern int oo_arrive_time_count[MAX_SHIPS];             
2156         extern int oo_interp_count[MAX_SHIPS];  
2157         oo_arrive_time_count[shipp - Ships] = 0;                                
2158         oo_interp_count[shipp - Ships] = 0;     
2159
2160         shipp->special_warp_objnum = -1;
2161
2162         // set awacs warning flags so awacs ship only asks for help once at each level
2163         shipp->awacs_warning_flag = AWACS_WARN_NONE;
2164 }
2165
2166 // function which recalculates the overall strength of subsystems.  Needed because
2167 // several places in FreeSpace change subsystem strength and all this data needs to
2168 // be kept up to date.
2169 void ship_recalc_subsys_strength( ship *shipp )
2170 {
2171         int i;
2172         ship_subsys *ship_system;
2173
2174         // fill in the subsys_info fields for all particular types of subsystems
2175         // make the current strength be 1.0.  If there are initial conditions on the ship, then
2176         // the mission parse code should take care of setting that.
2177         for (i = 0; i < SUBSYSTEM_MAX; i++) {
2178                 shipp->subsys_info[i].num = 0;
2179                 shipp->subsys_info[i].total_hits = 0.0f;
2180                 shipp->subsys_info[i].current_hits = 0.0f;
2181         }
2182
2183         // count all of the subsystems of a particular type.  For each generic type of subsystem, we store the
2184         // total count of hits.  (i.e. for 3 engines, we store the sum of the max_hits for each engine)
2185         for ( ship_system = GET_FIRST(&shipp->subsys_list); ship_system != END_OF_LIST(&shipp->subsys_list); ship_system = GET_NEXT(ship_system) ) {
2186                 int type;
2187
2188                 type = ship_system->system_info->type;
2189                 Assert ( (type >= 0) && (type < SUBSYSTEM_MAX) );
2190                 shipp->subsys_info[type].num++;
2191                 shipp->subsys_info[type].total_hits += ship_system->system_info->max_hits;
2192                 shipp->subsys_info[type].current_hits += ship_system->current_hits;
2193         }
2194
2195         // set any ship flags which should be set.  unset the flags since we might be repairing a subsystem
2196         // through sexpressions.
2197         shipp->flags &= ~SF_DISABLED;
2198         if ( (shipp->subsys_info[SUBSYSTEM_ENGINE].num > 0) && (shipp->subsys_info[SUBSYSTEM_ENGINE].current_hits == 0.0f) ){
2199                 shipp->flags |= SF_DISABLED;
2200         }
2201
2202         /*
2203         shipp->flags &= ~SF_DISARMED;
2204         if ( (shipp->subsys_info[SUBSYSTEM_TURRET].num > 0) && (shipp->subsys_info[SUBSYSTEM_TURRET].current_hits == 0.0f) ){
2205                 shipp->flags |= SF_DISARMED;
2206         }
2207         */
2208 }
2209
2210 // routine to possibly fixup the model subsystem information for this ship pointer.  Needed when
2211 // ships share the same model.
2212 void ship_copy_subsystem_fixup(ship_info *sip)
2213 {
2214         int i, model_num;
2215
2216         model_num = sip->modelnum;
2217
2218         // since we allow a model file to be shared between several ships, we must check to be sure that our
2219         // subsystems have been loaded properly
2220         /*
2221         subsystems_needed = 0;
2222         for (i = 0; i < sip->n_subsystems; i++ ) {
2223                 if ( sip->subsystems[i].model_num == -1 ){
2224                         subsystems_needed++;
2225                 }
2226         }
2227         */
2228
2229         // if we need to get information for all our subsystems, we need to find another ship with the same model
2230         // number as our own and that has the model information
2231         // if ( subsystems_needed == sip->n_subsystems ) {
2232                 for ( i = 0; i < Num_ship_types; i++ ) {
2233                         model_subsystem *msp;
2234
2235                         if ( (Ship_info[i].modelnum != model_num) || (&Ship_info[i] == sip) ){
2236                                 continue;
2237                         }
2238
2239                         // see if this ship has subsystems and a model for the subsystems.  We only need check the first
2240                         // subsystem since previous error checking would have trapped it's loading as an error.
2241                         Assert( Ship_info[i].n_subsystems == sip->n_subsystems );
2242
2243                         msp = &Ship_info[i].subsystems[0];
2244                         model_copy_subsystems( sip->n_subsystems, &(sip->subsystems[0]), msp );
2245                         sip->flags |= SIF_PATH_FIXUP;
2246                         break;
2247                 }
2248         // }
2249
2250 }
2251
2252
2253 // ignore_subsys_info => default parameter with value of 0.  This is
2254 //                                                               only set to 1 by the save/restore code
2255 void subsys_set(int objnum, int ignore_subsys_info)
2256 {       
2257         ship    *shipp = &Ships[Objects[objnum].instance];
2258         ship_info       *sinfo = &Ship_info[Ships[Objects[objnum].instance].ship_info_index];
2259         model_subsystem *sp;
2260         ship_subsys *ship_system;
2261         int i, j, k;
2262
2263         // set up the subsystems for this ship.  walk through list of subsystems in the ship-info array.
2264         // for each subsystem, get a new ship_subsys instance and set up the pointers and other values
2265         list_init ( &shipp->subsys_list );                                                              // initialize the ship's list of subsystems
2266         for ( i = 0; i < sinfo->n_subsystems; i++ ) {
2267
2268                 sp = &(sinfo->subsystems[i]);
2269                 if ( sp->model_num == -1 ) {
2270                         Warning (LOCATION, "Invalid subobj_num or model_num in subsystem %s on ship type %s.\nNot linking into ship!\n\n(This warning means that a subsystem was present in ships.tbl and not present in the model\nit should probably be removed from the model.)\n", sp->subobj_name, sinfo->name );
2271                         continue;
2272                 }
2273
2274                 // set up the linked list
2275                 ship_system = GET_FIRST( &ship_subsys_free_list );              // get a new element from the ship_subsystem array
2276                 Assert ( ship_system != &ship_subsys_free_list );               // shouldn't have the dummy element
2277                 list_remove( &ship_subsys_free_list, ship_system );     // remove the element from the array
2278                 list_append( &shipp->subsys_list, ship_system );                // link the element into the ship
2279
2280                 ship_system->system_info = sp;                                          // set the system_info pointer to point to the data read in from the model
2281                 if ( !Fred_running ){
2282                         ship_system->current_hits = sp->max_hits;               // set the max hits 
2283                 } else {
2284                         ship_system->current_hits = 0.0f;                               // Jason wants this to be 0 in Fred.
2285                 }
2286                 ship_system->turret_next_fire_stamp = timestamp(0);
2287                 ship_system->turret_next_enemy_check_stamp = timestamp(0);
2288                 ship_system->turret_enemy_objnum = -1;
2289                 ship_system->turret_next_fire_stamp = timestamp((int) frand_range(1.0f, 500.0f));       // next time this turret can fire
2290                 ship_system->turret_last_fire_direction = sp->turret_norm;
2291                 ship_system->turret_next_fire_pos = 0;
2292                 ship_system->turret_time_enemy_in_range = 0.0f;
2293                 ship_system->disruption_timestamp=timestamp(0);
2294                 ship_system->turret_pick_big_attack_point_timestamp = timestamp(0);
2295                 vm_vec_zero(&ship_system->turret_big_attack_point);
2296                 ship_system->subsys_cargo_name = -1;
2297                 ship_system->subsys_cargo_revealed = 0;
2298                 
2299                 // zero flags
2300                 ship_system->weapons.flags = 0;
2301
2302                 j = 0;
2303                 for (k=0; k<MAX_PRIMARY_BANKS; k++){
2304                         if (sp->primary_banks[k] != -1) {
2305                                 ship_system->weapons.primary_bank_weapons[j] = sp->primary_banks[k];
2306                                 ship_system->weapons.next_primary_fire_stamp[j++] = 0;
2307                         }
2308                 }
2309
2310                 ship_system->weapons.num_primary_banks = j;
2311
2312                 j = 0;
2313                 for (k=0; k<MAX_SECONDARY_BANKS; k++){
2314                         if (sp->secondary_banks[k] != -1) {
2315                                 ship_system->weapons.secondary_bank_weapons[j] = sp->secondary_banks[k];
2316                                 ship_system->weapons.secondary_bank_capacity[j] = sp->secondary_bank_capacity[k];
2317                                 ship_system->weapons.next_secondary_fire_stamp[j++] = timestamp(0);
2318                         }
2319                 }
2320
2321                 ship_system->weapons.num_secondary_banks = j;
2322                 ship_system->weapons.current_primary_bank = -1;
2323                 ship_system->weapons.current_secondary_bank = -1;
2324                 
2325                 for (k=0; k<MAX_SECONDARY_BANKS; k++) {
2326                         ship_system->weapons.secondary_bank_ammo[k] = (Fred_running ? 100 : ship_system->weapons.secondary_bank_capacity[k]);
2327
2328                         ship_system->weapons.secondary_next_slot[k] = 0;
2329                 }
2330
2331                 ship_system->weapons.last_fired_weapon_index = -1;
2332                 ship_system->weapons.last_fired_weapon_signature = -1;
2333                 ship_system->weapons.detonate_weapon_time = -1;
2334                 ship_system->weapons.ai_class = sinfo->ai_class;  // assume ai class of ship for turret
2335
2336                 // rapid fire (swarm) stuff
2337                 ship_system->turret_swarm_info_index = -1;
2338
2339                 // AWACS stuff
2340                 ship_system->awacs_intensity = sp->awacs_intensity;
2341                 ship_system->awacs_radius = sp->awacs_radius;
2342                 if (ship_system->awacs_intensity > 0) {
2343                         ship_system->system_info->flags |= MSS_FLAG_AWACS;
2344                 }
2345
2346                 // turn_rate, turn_accel
2347                 // model_set_instance_info
2348                 float turn_accel = 0.5f;
2349                 model_set_instance_info(&ship_system->submodel_info_1, sp->turn_rate, turn_accel);
2350
2351                 // model_clear_instance_info( &ship_system->submodel_info_1 );
2352                 model_clear_instance_info( &ship_system->submodel_info_2 );
2353         }
2354
2355         if ( !ignore_subsys_info ) {
2356                 ship_recalc_subsys_strength( shipp );
2357         }
2358 }
2359
2360
2361 #ifndef NDEBUG
2362
2363 //      Render docking information, NOT while in object's reference frame.
2364 void render_dock_bays(object *objp)
2365 {
2366         ship_info       *sip;
2367         polymodel       *pm;
2368         dock_bay                *db;
2369
2370         sip = &Ship_info[Ships[objp->instance].ship_info_index];
2371         pm = model_get( sip->modelnum );
2372
2373         if (pm->docking_bays == NULL)
2374                 return;
2375
2376         if (pm->docking_bays[0].num_slots != 2)
2377                 return;
2378
2379         db = &pm->docking_bays[0];
2380
2381         vertex  v0, v1;
2382         vector  p0, p1, p2, p3, nr;
2383
2384         vm_vec_unrotate(&p0, &db->pnt[0], &objp->orient);
2385         vm_vec_add2(&p0, &objp->pos);
2386         g3_rotate_vertex(&v0, &p0);
2387
2388         vm_vec_unrotate(&p1, &db->pnt[1], &objp->orient);
2389         vm_vec_add2(&p1, &objp->pos);
2390         g3_rotate_vertex(&v1, &p1);
2391
2392         gr_set_color(255, 0, 0);
2393         g3_draw_line(&v0, &v1);
2394
2395         vm_vec_avg(&p2, &p0, &p1);
2396
2397         vm_vec_unrotate(&nr, &db->norm[0], &objp->orient);
2398         vm_vec_scale_add(&p3, &p2, &nr, 10.0f);
2399
2400         g3_rotate_vertex(&v0, &p2);
2401         g3_rotate_vertex(&v1, &p3);
2402         gr_set_color(255, 255, 0);
2403         g3_draw_line(&v0, &v1);
2404         g3_draw_sphere(&v1, 1.25f);
2405
2406 }
2407
2408 #endif
2409
2410 int Ship_shadows = 0;
2411
2412 DCF_BOOL( ship_shadows, Ship_shadows );
2413
2414 MONITOR( NumShipsRend );        
2415
2416 int Show_shield_hits = 0;
2417 DCF_BOOL( show_shield_hits, Show_shield_hits );
2418
2419 int Show_tnorms = 0;
2420 DCF_BOOL( show_tnorms, Show_tnorms );
2421
2422 int Show_paths = 0;
2423 DCF_BOOL( show_paths, Show_paths );
2424
2425 int Show_fpaths = 0;
2426 DCF_BOOL( show_fpaths, Show_fpaths );
2427
2428 void ship_render(object * obj)
2429 {
2430         int num;
2431         ship_info * si;
2432         ship * shipp;
2433
2434         num = obj->instance;
2435
2436         Assert( num >= 0);      
2437
2438 #if 0
2439         // show target when attacking big ship
2440         vector temp, target;
2441         ai_info *aip = &Ai_info[Ships[obj->instance].ai_index];
2442         if ( (aip->target_objnum >= 0)  && (Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].flags & (SIF_SUPERCAP|SIF_CAPITAL|SIF_CRUISER)) ) {
2443                 vm_vec_unrotate(&temp, &aip->big_attack_point, &Objects[aip->target_objnum].orient);
2444                 vm_vec_add(&target, &temp, &Objects[aip->target_objnum].pos);
2445
2446                 vertex v0, v1;
2447                 gr_set_color(128,0,0);
2448                 g3_rotate_vertex( &v0, &obj->pos );
2449                 g3_rotate_vertex( &v1, &target );
2450
2451                 g3_draw_line(&v0, &v1);
2452
2453                 g3_draw_sphere(&v1, 5.0f);
2454         }
2455 #endif
2456
2457
2458 //      if (Ships[num].subtype == SHIP_PLAYER ) return; 
2459         if ( obj == Viewer_obj ) {
2460                 if (ship_show_velocity_dot && (obj==Player_obj) )       {
2461                         vector p0,v;
2462                         vertex v0;
2463
2464                         vm_vec_scale_add( &v, &obj->phys_info.vel, &obj->orient.v.fvec, 3.0f );
2465                         vm_vec_normalize( &v );
2466                         
2467                                         
2468                         vm_vec_scale_add( &p0, &obj->pos, &v, 20.0f);
2469
2470                         g3_rotate_vertex( &v0, &p0 );
2471                         
2472                         gr_set_color(0,128,0);
2473                         g3_draw_sphere( &v0, 0.1f );
2474                 }
2475
2476                 // Show the shield hit effect for the viewer.
2477                 if ( Show_shield_hits ) {
2478                         shipp = &Ships[num];
2479                         if (shipp->shield_hits) {
2480                                 create_shield_explosion_all(obj);
2481                                 shipp->shield_hits = 0;
2482                         }
2483                 }               
2484
2485                 return;
2486         }
2487
2488         MONITOR_INC( NumShipsRend, 1 ); 
2489
2490         shipp = &Ships[num];
2491         si = &Ship_info[Ships[num].ship_info_index];
2492
2493         // Make ships that are warping in not render during stage 1
2494         if ( shipp->flags & SF_ARRIVING_STAGE_1 ){                              
2495                 return;
2496         }
2497
2498         if ( Ship_shadows && shipfx_in_shadow( obj ) )  {
2499                 light_set_shadow(1);
2500         } else {
2501                 light_set_shadow(0);
2502         }
2503
2504         ship_model_start(obj);
2505
2506         uint render_flags = MR_NORMAL;
2507
2508         // Turn off model caching for the player ship in external view.
2509         if (obj == Player_obj)  {
2510                 render_flags |= MR_ALWAYS_REDRAW;       
2511         }
2512
2513         // Turn off model caching if this is the player's target.
2514         if ( Player_ai->target_objnum == OBJ_INDEX(obj))        {
2515                 render_flags |= MR_ALWAYS_REDRAW;       
2516         }       
2517
2518 #ifndef NDEBUG
2519         if(Show_paths || Show_fpaths){
2520                 render_flags |= MR_BAY_PATHS;
2521         }
2522 #endif
2523
2524         // Only render electrical arcs if within 500m of the eye (for a 10m piece)
2525         if ( vm_vec_dist_quick( &obj->pos, &Eye_position ) < obj->radius*50.0f )        {
2526                 int i;
2527                 for (i=0; i<MAX_SHIP_ARCS; i++ )        {
2528                         if ( timestamp_valid( shipp->arc_timestamp[i] ) )       {
2529                                 render_flags |= MR_ALWAYS_REDRAW;       // Turn off model caching if arcing.
2530                                 model_add_arc( shipp->modelnum, -1, &shipp->arc_pts[i][0], &shipp->arc_pts[i][1], shipp->arc_type[i] );
2531                         }
2532                 }
2533         }
2534
2535         if ( shipp->large_ship_blowup_index > -1 )      {
2536                 shipfx_large_blowup_render(shipp);
2537         } else {
2538
2539                 //      ship_get_subsystem_strength( shipp, SUBSYSTEM_ENGINE)>ENGINE_MIN_STR
2540
2541                 if ( (shipp->thruster_bitmap > -1) && (!(shipp->flags & SF_DISABLED)) && (!ship_subsys_disrupted(shipp, SUBSYSTEM_ENGINE)) ) {
2542                         float   ft;
2543
2544                         //      Add noise to thruster geometry.
2545                         ft = obj->phys_info.forward_thrust;
2546                         ft *= (1.0f + frand()/5.0f - 1.0f/10.0f);
2547                         if (ft > 1.0f)
2548                                 ft = 1.0f;
2549
2550                         model_set_thrust( shipp->modelnum, ft, shipp->thruster_bitmap, shipp->thruster_glow_bitmap, shipp->thruster_glow_noise );
2551                         render_flags |= MR_SHOW_THRUSTERS;
2552                 }
2553
2554                 // fill the model flash lighting values in
2555                 shipfx_flash_light_model( obj, shipp );
2556
2557                 object *docked_objp = NULL;
2558                 ship * docked_shipp = NULL;
2559                 ship * warp_shipp = shipp;
2560                          
2561                 // check to see if departing ship is docked with anything.
2562                 docked_objp = ai_find_docked_object( obj );
2563                 if ( docked_objp ) {
2564                         docked_shipp = &Ships[docked_objp->instance];
2565
2566                         if ( docked_shipp->flags & (SF_DEPART_WARP|SF_ARRIVING) )       {
2567                                 warp_shipp = docked_shipp;
2568                         }
2569                 }
2570
2571                 // Warp_shipp points to the ship that is going through a
2572                 // warp... either this ship or the ship it is docked with.
2573                 
2574                 // If the ship is going "through" the warp effect, then
2575                 // set up the model renderer to only draw the polygons in front
2576                 // of the warp in effect
2577                 int clip_started = 0;
2578
2579                 if ( warp_shipp->flags & (SF_ARRIVING|SF_DEPART_WARP) ) {
2580
2581                         clip_started = 1;
2582                         g3_start_user_clip_plane( &warp_shipp->warp_effect_pos, &warp_shipp->warp_effect_fvec );
2583
2584                         // Turn off model caching while going thru warp effect.
2585                         render_flags |= MR_ALWAYS_REDRAW;       
2586                 }
2587
2588                 // maybe set squad logo bitmap
2589                 model_set_insignia_bitmap(-1);
2590                 if(Game_mode & GM_MULTIPLAYER){
2591                         // if its any player's object
2592                         int np_index = multi_find_player_by_object( obj );
2593                         if((np_index >= 0) && (np_index < MAX_PLAYERS) && MULTI_CONNECTED(Net_players[np_index]) && (Net_players[np_index].player != NULL)){
2594                                 model_set_insignia_bitmap(Net_players[np_index].player->insignia_texture);
2595                         }
2596                 }
2597                 // in single player, we want to render model insignias on all ships in alpha beta and gamma
2598                 else {                  
2599                         // if its an object in my squadron
2600                         if(ship_in_abgz(shipp)){
2601                                 model_set_insignia_bitmap(Player->insignia_texture);
2602                         }
2603                 }
2604
2605                 // maybe disable lighting
2606                 // if((The_mission.flags & MISSION_FLAG_FULLNEB) && (neb2_get_fog_intensity(obj) > 0.33f) && (si->flags & SIF_SMALL_SHIP)){
2607                         // render_flags |= MR_NO_LIGHTING;
2608                 // }
2609
2610                 // nebula               
2611                 if(The_mission.flags & MISSION_FLAG_FULLNEB){           
2612                         extern void model_set_fog_level(float l);
2613                         model_set_fog_level(neb2_get_fog_intensity(obj));
2614                 }
2615
2616                 // small ships
2617                 if((The_mission.flags & MISSION_FLAG_FULLNEB) && (si->flags & SIF_SMALL_SHIP)){                 
2618                         // force detail levels
2619                         float fog_val = neb2_get_fog_intensity(obj);
2620                         if(fog_val >= 0.6f){
2621                                 model_set_detail_level(2);
2622                                 model_render( shipp->modelnum, &obj->orient, &obj->pos, render_flags | MR_LOCK_DETAIL, OBJ_INDEX(obj) );
2623                         } else {
2624                                 model_render( shipp->modelnum, &obj->orient, &obj->pos, render_flags, OBJ_INDEX(obj) );
2625                         }
2626                 } else {
2627                         model_render( shipp->modelnum, &obj->orient, &obj->pos, render_flags, OBJ_INDEX(obj) );
2628                 }
2629
2630                 // always turn off fog after rendering a ship
2631                 gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0);
2632
2633                 light_set_shadow(0);
2634
2635                 #ifndef NDEBUG
2636                 if (Show_shield_mesh)
2637                         ship_draw_shield( obj);         //      Render the shield.
2638                 #endif
2639
2640                 if ( clip_started )     {
2641                         g3_stop_user_clip_plane();
2642                 }
2643         } 
2644
2645 /*      if (Mc.shield_hit_tri != -1) {
2646                 //render_shield_explosion(model_num, orient, pos, &Hit_point, Hit_tri);
2647                 Mc.shield_hit_tri = -1;
2648         }
2649 */
2650
2651         ship_model_stop(obj);
2652
2653         if (shipp->shield_hits) {
2654                 create_shield_explosion_all(obj);
2655                 shipp->shield_hits = 0;
2656         }
2657
2658 #ifndef NDEBUG
2659         if (Ai_render_debug_flag || Show_paths) {
2660                 if ( shipp->ai_index != -1 ){
2661                         render_path_points(obj);
2662                 }
2663
2664                 render_dock_bays(obj);
2665         }
2666 #endif
2667         
2668 #ifndef NDEBUG
2669         if(Show_tnorms){
2670                 ship_subsys *systemp;
2671                 vector tpos, tnorm, temp;
2672                 vector v1, v2;
2673                 vertex l1, l2;
2674
2675                 gr_set_color(0, 0, 255);
2676                 systemp = GET_FIRST( &shipp->subsys_list );             
2677                 while ( systemp != END_OF_LIST(&shipp->subsys_list) ) {
2678                         ship_get_global_turret_gun_info(obj, systemp, &tpos, &tnorm, 1, &temp);
2679                         
2680                         v1 = tpos;
2681                         vm_vec_scale_add(&v2, &v1, &tnorm, 20.0f);
2682
2683                         g3_rotate_vertex(&l1, &v1);
2684                         g3_rotate_vertex(&l2, &v2);
2685
2686                         g3_draw_sphere(&l1, 2.0f);
2687                         g3_draw_line(&l1, &l2);
2688
2689                         systemp = GET_NEXT(systemp);
2690                 }
2691         }
2692 #endif
2693 }
2694
2695 void ship_subsystem_delete(ship *shipp)
2696 {
2697         ship_subsys *systemp, *temp;
2698
2699         systemp = GET_FIRST( &shipp->subsys_list );
2700         while ( systemp != END_OF_LIST(&shipp->subsys_list) ) {
2701                 temp = GET_NEXT( systemp );                                                             // use temporary since pointers will get screwed with next operation
2702                 list_remove( &shipp->subsys_list, systemp );                    // remove the element
2703                 list_append( &ship_subsys_free_list, systemp );         // and place back onto free list
2704                 systemp = temp;                                                                                         // use the temp variable to move right along
2705         }
2706 }
2707
2708 void ship_delete( object * obj )
2709 {
2710         ship    *shipp;
2711         int     num, objnum;
2712
2713         num = obj->instance;
2714         Assert( num >= 0);
2715
2716         objnum = OBJ_INDEX(obj);
2717         Assert( Ships[num].objnum == objnum );
2718
2719         shipp = &Ships[num];
2720
2721         if (shipp->ai_index != -1){
2722                 ai_free_slot(shipp->ai_index);
2723         }       
2724
2725         // free up the list of subsystems of this ship.  walk through list and move remaining subsystems
2726         // on ship back to the free list for other ships to use.
2727         ship_subsystem_delete(&Ships[num]);
2728
2729         shipp->objnum = -1;
2730         // mwa 11/24/97 num_ships--;
2731
2732         if (model_get(shipp->modelnum)->shield.ntris) {
2733                 free(shipp->shield_integrity);
2734                 shipp->shield_integrity = NULL;
2735         }
2736
2737         if ( shipp->ship_list_index != -1 ) {
2738                 ship_obj_list_remove(shipp->ship_list_index);
2739                 shipp->ship_list_index = -1;
2740         }
2741
2742         free_sexp2(shipp->arrival_cue);
2743         free_sexp2(shipp->departure_cue);
2744
2745         // call the contrail system
2746         ct_ship_delete(shipp);
2747 }
2748
2749 // function used by ship_destroyed and ship_departed which is called if the ship
2750 // is in a wing.  This function updates the ship_index list (i.e. removes it's
2751 // entry in the list), and packs the array accordingly.
2752 void ship_wing_cleanup( int shipnum, wing *wingp )
2753 {
2754         int i, index = -1, team;
2755
2756         team = Ships[shipnum].team;
2757         // compress the ship_index array and mark the last entry with a -1
2758         for (i = 0; i < wingp->current_count; i++ ) {
2759                 if ( wingp->ship_index[i] == shipnum ) {
2760                         index = i;
2761                         break;
2762                 }
2763         }
2764
2765         // Assert(index != -1);
2766         
2767         // this can happen in multiplayer (dogfight, ingame join specifically)
2768         if(index == -1){
2769                 return;
2770         }
2771
2772         for ( i = index; i < wingp->current_count - 1; i++ ){
2773                 wingp->ship_index[i] = wingp->ship_index[i+1];
2774         }
2775
2776         wingp->current_count--;
2777         Assert ( wingp->current_count >= 0 );
2778         wingp->ship_index[wingp->current_count] = -1;
2779
2780         // if the current count is 0, check to see if the wing departed or was destroyed.
2781         if ( wingp->current_count == 0 ) {
2782
2783                 // if this wing was ordered to depart by the player, set the current_wave equal to the total
2784                 // waves so we can mark the wing as gone and no other ships arrive
2785                 if ( wingp->flags & WF_DEPARTURE_ORDERED ) 
2786                         wingp->current_wave = wingp->num_waves;
2787
2788                 // first, be sure to mark a wing destroyed event if all members of wing were destroyed and on
2789                 // the last wave.  This circumvents a problem where the wing could be marked as departed and
2790                 // destroyed if the last ships were destroyed after the wing's departure cue became true.
2791
2792                 // if the wing wasn't destroyed, and it is departing, then mark it as departed -- in this
2793                 // case, there had better be ships in this wing with departure entries in the log file.  The
2794                 // logfile code checks for this case.  
2795                 if ( (wingp->current_wave == wingp->num_waves) && (wingp->total_destroyed == wingp->total_arrived_count) ) {
2796                         mission_log_add_entry(LOG_WING_DESTROYED, wingp->name, NULL, team);
2797                         wingp->flags |= WF_WING_GONE;
2798                         wingp->time_gone = Missiontime;
2799                 } else if ( (wingp->flags & WF_WING_DEPARTING) || (wingp->current_wave == wingp->num_waves) ) {
2800 #ifndef NDEBUG
2801                         ship_obj *so;
2802
2803
2804                         // apparently, there have been reports of ships still present in the mission when this log
2805                         // entry if written.  Do a sanity check here to find out for sure.
2806                         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2807
2808                                 // skip the player -- stupid special case.
2809                                 if ( &Objects[so->objnum] == Player_obj )
2810                                         continue;
2811
2812                                 if ( (Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_INGAME_JOIN) )
2813                                         continue;
2814
2815                                 if ( (Ships[Objects[so->objnum].instance].wingnum == WING_INDEX(wingp)) && !(Ships[Objects[so->objnum].instance].flags & (SF_DEPARTING|SF_DYING)) )
2816                                         Int3();
2817                         }
2818 #endif
2819
2820                         if ( wingp->flags & (WF_WING_DEPARTING|WF_DEPARTURE_ORDERED) )
2821                                 mission_log_add_entry(LOG_WING_DEPART, wingp->name, NULL, team);
2822
2823                         wingp->flags |= WF_WING_GONE;
2824                         wingp->time_gone = Missiontime;
2825                 }
2826         }
2827 }
2828
2829 // function to do management, like log entries and wing cleanup after a ship has been destroyed
2830
2831 void ship_destroyed( int num )
2832 {
2833         ship            *shipp;
2834         object  *objp;
2835
2836         shipp = &Ships[num];
2837         objp = &Objects[shipp->objnum];
2838
2839         // add the information to the exited ship list
2840         ship_add_exited_ship( shipp, SEF_DESTROYED );
2841
2842         // determine if we need to count this ship as a klll in counting number of kills per ship type
2843         // look at the ignore flag for the ship (if not in a wing), or the ignore flag for the wing
2844         // (if the ship is in a wing), and add to the kill count if the flags are not set
2845         if ( !(shipp->flags & SF_IGNORE_COUNT) ||  ((shipp->wingnum != -1) && !(Wings[shipp->wingnum].flags & WF_IGNORE_COUNT)) )
2846                 ship_add_ship_type_kill_count( Ship_info[shipp->ship_info_index].flags );
2847
2848         // if ship belongs to a wing -- increment the total number of ships in the wing destroyed
2849         if ( shipp->wingnum != -1 ) {
2850                 wing *wingp;
2851
2852                 wingp = &Wings[shipp->wingnum];
2853                 wingp->total_destroyed++;
2854                 ship_wing_cleanup( num, wingp );
2855         }
2856
2857         //      Note, this call to ai_ship_destroy must come after ship_wing_cleanup for guarded wings to
2858         //      properly note the destruction of a ship in their wing.
2859         if ( shipp->ai_index != -1 ) {
2860                 ai_ship_destroy(num, SEF_DESTROYED);            //      Do AI stuff for destruction of ship.
2861         }
2862
2863         nprintf(("Alan","SHIP DESTROYED: %s\n", shipp->ship_name));
2864
2865         if ( (shipp->wing_status_wing_index >= 0) && (shipp->wing_status_wing_pos >= 0) ) {
2866                 nprintf(("Alan","STATUS UPDATED: %s\n", shipp->ship_name));
2867                 hud_set_wingman_status_dead(shipp->wing_status_wing_index, shipp->wing_status_wing_pos);
2868         }
2869
2870         // let the event music system know a hostile was destoyed (important for deciding when to transition from battle to normal music)
2871         if (Player_ship != NULL) {
2872                 if (shipp->team != Player_ship->team) {
2873                         event_music_hostile_ship_destroyed();
2874                 }
2875         }
2876 }
2877
2878 void ship_vanished(int num)
2879 {
2880         ship *sp;
2881         object *objp;   
2882
2883         sp = &Ships[num];
2884         objp = &Objects[sp->objnum];
2885
2886         // demo recording
2887         if(Game_mode & GM_DEMO_RECORD){
2888                 demo_POST_departed(Objects[Ships[num].objnum].signature, Ships[num].flags);
2889         }
2890
2891         // add the information to the exited ship list
2892         ship_add_exited_ship( sp, SEF_DEPARTED );
2893
2894         // update wingman status gauge
2895         if ( (sp->wing_status_wing_index >= 0) && (sp->wing_status_wing_pos >= 0) ) {
2896                 hud_set_wingman_status_departed(sp->wing_status_wing_index, sp->wing_status_wing_pos);
2897         }
2898
2899         ai_ship_destroy(num, SEF_DEPARTED);             // should still do AI cleanup after ship has departed
2900 }
2901
2902 void ship_departed( int num )
2903 {
2904         ship *sp;
2905         int i;
2906
2907         sp = &Ships[num];
2908
2909         // demo recording
2910         if(Game_mode & GM_DEMO_RECORD){
2911                 demo_POST_departed(Objects[Ships[num].objnum].signature, Ships[num].flags);
2912         }
2913
2914         // add the information to the exited ship list
2915         ship_add_exited_ship( sp, SEF_DEPARTED );
2916
2917         // update wingman status gauge
2918         if ( (sp->wing_status_wing_index >= 0) && (sp->wing_status_wing_pos >= 0) ) {
2919                 hud_set_wingman_status_departed(sp->wing_status_wing_index, sp->wing_status_wing_pos);
2920         }
2921
2922         // see if this ship departed within the radius of a jump node -- if so, put the node name into
2923         // the secondary mission log field
2924         for ( i = 0; i < Num_jump_nodes; i++ ) {
2925                 float radius, dist;
2926                 vector ship_pos, node_pos;
2927
2928                 ship_pos = Objects[sp->objnum].pos;
2929                 node_pos = Objects[Jump_nodes[i].objnum].pos;
2930                 radius = model_get_radius( Jump_nodes[i].modelnum );
2931                 dist = vm_vec_dist( &ship_pos, &node_pos );
2932                 if ( dist <= radius ) {
2933                         mission_log_add_entry(LOG_SHIP_DEPART, sp->ship_name, Jump_nodes[i].name, sp->wingnum);
2934                         break;
2935                 }
2936                 dist = 1.0f;
2937         }
2938
2939         if ( i == Num_jump_nodes ){
2940                 mission_log_add_entry(LOG_SHIP_DEPART, sp->ship_name, NULL, sp->wingnum);
2941         }
2942                 
2943         ai_ship_destroy(num, SEF_DEPARTED);             // should still do AI cleanup after ship has departed
2944
2945         // don't bother doing this for demo playback - we don't keep track of wing info
2946         if(!(Game_mode & GM_DEMO_PLAYBACK)){
2947                 if ( sp->wingnum != -1 ) {
2948                         wing *wingp;
2949
2950                         wingp = &Wings[sp->wingnum];
2951                         wingp->total_departed++;
2952                         ship_wing_cleanup( num, wingp );
2953                 }
2954         }
2955 }
2956
2957 // --------------------------------------------------------------------------------------------------------------------
2958 // ship_explode_area_calc_damage
2959 // 
2960 // input                        pos1                    =>              ship explosion position
2961 //                                      pos2                    =>              other ship position
2962 //                                      inner_rad       =>              distance from ship center for which full damage is applied
2963 //                                      outer_rad       =>              distance from ship center for which no damage is applied
2964 //                                      max_damage      =>              maximum damage applied
2965 //                                      max_blast       =>              maximum impulse applied from blast
2966 // 
2967 // calculates the blast and damage applied to a ship from another ship blowing up.
2968 //
2969 int ship_explode_area_calc_damage( vector *pos1, vector *pos2, float inner_rad, float outer_rad, float max_damage, float max_blast, float *damage, float *blast )
2970 {
2971         float dist;
2972
2973         dist = vm_vec_dist_quick( pos1, pos2 );
2974
2975         // check outside outer radius
2976         if ( dist > outer_rad )
2977                 return -1;
2978
2979         if ( dist < inner_rad ) {
2980         // check insider inner radius
2981                 *damage = max_damage;
2982                 *blast = max_blast;
2983         } else {
2984         // between inner and outer
2985                 float fraction = 1.0f - (dist - inner_rad) / (outer_rad - inner_rad);
2986                 *damage  = fraction * max_damage;
2987                 *blast   = fraction * max_blast;
2988         }
2989
2990         return 1;
2991 }
2992
2993 // --------------------------------------------------------------------------------------------------------------------
2994 // ship_blow_up_area_apply_blast
2995 // this function applies damage to ship close to others when a ship dies and blows up
2996 //
2997 //              inputs: objp                    =>              ship object pointers
2998 //                                      pos                     =>              position of the ship when it finally blows up
2999 //                                      inner_rad       =>              distance from ship center for which full damage is applied
3000 //                                      outer_rad       =>              distance from ship center for which no damage is applied
3001 //                                      damage          =>              maximum damage applied
3002 //                                      blast                   =>              maximum impulse applied from blast
3003
3004 void ship_blow_up_area_apply_blast( object *exp_objp)
3005 {
3006         ship_info       *sip;
3007         Assert( exp_objp->type == OBJ_SHIP );
3008         float   inner_rad, outer_rad, max_damage, max_blast, shockwave_speed;
3009         shockwave_create_info sci;
3010
3011         //      No area explosion in training missions.
3012         if (The_mission.game_type & MISSION_TYPE_TRAINING){
3013                 return;
3014         }
3015                 
3016         if ((exp_objp->hull_strength <= KAMIKAZE_HULL_ON_DEATH) && (Ai_info[Ships[exp_objp->instance].ai_index].ai_flags & AIF_KAMIKAZE) && (Ships[exp_objp->instance].special_exp_index < 0)) {
3017                 float override = Ai_info[Ships[exp_objp->instance].ai_index].kamikaze_damage;
3018
3019                 inner_rad = exp_objp->radius*2.0f;
3020                 outer_rad = exp_objp->radius*4.0f; // + (override * 0.3f);
3021                 max_damage = override;
3022                 max_blast = override * 5.0f;
3023                 shockwave_speed = 100.0f;
3024         } else {
3025                 sip = &Ship_info[Ships[exp_objp->instance].ship_info_index];
3026
3027                 if (Ships[exp_objp->instance].special_exp_index != -1) {
3028                         int start = Ships[exp_objp->instance].special_exp_index;
3029                         int propagates;
3030                         inner_rad = (float) atoi(Sexp_variables[start+INNER_RAD].text);
3031                         outer_rad = (float) atoi(Sexp_variables[start+OUTER_RAD].text);
3032                         max_damage = (float) atoi(Sexp_variables[start+DAMAGE].text);
3033                         max_blast = (float) atoi(Sexp_variables[start+BLAST].text);
3034                         propagates = atoi(Sexp_variables[start+PROPAGATE].text);
3035                         if (propagates) {
3036                                 shockwave_speed = (float) atoi(Sexp_variables[start+SHOCK_SPEED].text);
3037                         } else {
3038                                 shockwave_speed = 0.0f;
3039                         }
3040                 } else {
3041                         inner_rad = sip->inner_rad;
3042                         outer_rad = sip->outer_rad;
3043                         max_damage = sip->damage;
3044                         max_blast  = sip->blast;
3045                         shockwave_speed = sip->shockwave_speed;
3046                 }
3047         }
3048
3049         // nprintf(("AI", "Frame %i: Area effect blast from ship %s\n", Framecount, Ships[exp_objp->instance].ship_name));
3050
3051         // account for ships that give no damage when they blow up.
3052         if ( (max_damage < 0.1f) && (max_blast < 0.1f) ){
3053                 return;
3054         }
3055
3056         if ( shockwave_speed > 0 ) {
3057                 sci.inner_rad = inner_rad;
3058                 sci.outer_rad = outer_rad;
3059                 sci.blast = max_blast;
3060                 sci.damage = max_damage;
3061                 sci.speed = shockwave_speed;
3062                 sci.rot_angle = frand_range(0.0f, 359.0f);
3063                 shipfx_do_shockwave_stuff(&Ships[exp_objp->instance], &sci);
3064                 // shockwave_create(Ships[exp_objp->instance].objnum, &exp_objp->pos, shockwave_speed, inner_rad, outer_rad, max_damage, max_blast, SW_SHIP_DEATH);
3065         } else {
3066                 object *objp;
3067                 float blast = 0.0f;
3068                 float damage = 0.0f;
3069                 for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
3070                         if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID) ) {
3071                                 continue;
3072                         }
3073                 
3074                         if ( objp == exp_objp ){
3075                                 continue;
3076                         }
3077
3078                         // don't blast navbuoys
3079                         if ( objp->type == OBJ_SHIP ) {
3080                                 if ( ship_get_SIF(objp->instance) & SIF_NAVBUOY ) {
3081                                         continue;
3082                                 }
3083                         }
3084
3085                         if ( ship_explode_area_calc_damage( &exp_objp->pos, &objp->pos, inner_rad, outer_rad, max_damage, max_blast, &damage, &blast ) == -1 ){
3086                                 continue;
3087                         }
3088
3089                         switch ( objp->type ) {
3090                         case OBJ_SHIP:
3091                                 ship_apply_global_damage( objp, exp_objp, &exp_objp->pos, damage );
3092                                 vector force, vec_ship_to_impact;
3093                                 vm_vec_sub( &vec_ship_to_impact, &objp->pos, &exp_objp->pos );
3094                                 vm_vec_copy_normalize( &force, &vec_ship_to_impact );
3095                                 vm_vec_scale( &force, blast );
3096                                 ship_apply_whack( &force, &vec_ship_to_impact, objp );
3097                                 break;
3098                         case OBJ_ASTEROID:
3099                                 asteroid_hit(objp, NULL, NULL, damage);
3100                                 break;
3101                         default:
3102                                 Int3();
3103                                 break;
3104                         }
3105                 }       // end for
3106         }
3107 }
3108
3109 void do_dying_undock_physics(object* objp, ship* sp) 
3110 {
3111         Assert(sp->dock_objnum_when_dead >= 0);
3112         if(sp->dock_objnum_when_dead < 0){
3113                 return;
3114         }
3115         object* dock_obj = &Objects[sp->dock_objnum_when_dead];
3116
3117         // sanity checks
3118         Assert(objp->type == OBJ_SHIP);
3119         Assert(dock_obj->type == OBJ_SHIP);
3120         if((objp->type != OBJ_SHIP) || (dock_obj->type != OBJ_SHIP)){
3121                 return;
3122         }
3123
3124         float damage = 0.2f*Ship_info[sp->ship_info_index].initial_hull_strength;
3125         ship_apply_global_damage(dock_obj, objp, &objp->pos, damage);
3126
3127         // do physics
3128         vector impulse_norm, impulse_vec, pos;
3129         vm_vec_sub(&impulse_norm, &dock_obj->pos, &objp->pos);
3130         vm_vec_normalize(&impulse_norm);
3131         // set for relative separation velocity of ~30
3132         float impulse_mag = 50.f*dock_obj->phys_info.mass*objp->phys_info.mass/(dock_obj->phys_info.mass + objp->phys_info.mass);
3133         vm_vec_copy_scale(&impulse_vec, &impulse_norm, impulse_mag);
3134         vm_vec_rand_vec_quick(&pos);
3135         vm_vec_scale(&pos, dock_obj->radius);
3136         // apply whack to dock obj
3137         physics_apply_whack(&impulse_vec, &pos, &dock_obj->phys_info, &dock_obj->orient, dock_obj->phys_info.mass);
3138         // enhance rotation of the docked ship
3139         vm_vec_scale(&dock_obj->phys_info.rotvel, 2.0f);
3140
3141         // apply whack to ship
3142         vm_vec_negate(&impulse_vec);
3143         vm_vec_rand_vec_quick(&pos);
3144         vm_vec_scale(&pos, objp->radius);
3145         physics_apply_whack(&impulse_vec, &pos, &objp->phys_info, &objp->orient, objp->phys_info.mass);
3146
3147         // reset dock_objnum_when_dead to -1 for dockee, since docker has blown up.
3148         if (Ships[dock_obj->instance].dock_objnum_when_dead == sp->objnum) {
3149                 Ships[dock_obj->instance].dock_objnum_when_dead = -1;
3150         }
3151 }
3152
3153 //      Do the stuff we do in a frame for a ship that's in its death throes.
3154 void ship_dying_frame(object *objp, int ship_num)
3155 {
3156         ship    *sp;
3157         sp = &Ships[ship_num];
3158         int knossos_ship = false;
3159
3160         if ( sp->flags & SF_DYING )     {
3161                 knossos_ship = (Ship_info[sp->ship_info_index].flags & SIF_KNOSSOS_DEVICE);
3162
3163                 // bash hull value toward 0 (from self destruct)
3164                 if (objp->hull_strength > 0) {
3165                         int time_left = timestamp_until(sp->final_death_time);
3166                         float hits_left = objp->hull_strength;
3167
3168                         objp->hull_strength -= hits_left * (1000.0f * flFrametime) / time_left;
3169                 }
3170
3171                 // special case of VAPORIZE
3172                 if (sp->flags & SF_VAPORIZE) {
3173                         // Assert(Ship_info[sp->ship_info_index].flags & SIF_SMALL_SHIP);
3174                         if (timestamp_elapsed(sp->final_death_time)) {
3175
3176                                 // play death sound
3177                                 snd_play_3d( &Snds[SND_VAPORIZED], &objp->pos, &View_position, objp->radius, NULL, 0, 1.0f, SND_PRIORITY_MUST_PLAY  );
3178
3179                                 // do joystick effect
3180                                 if (objp == Player_obj) {
3181                                         joy_ff_explode();
3182                                 }
3183
3184                                 // if dying ship is docked, do damage to docked and physics
3185                                 if (sp->dock_objnum_when_dead != -1)  {
3186                                         do_dying_undock_physics(objp, sp);
3187                                 }                       
3188
3189                                 // do all accounting for respawning client and server side here.
3190                                 if (objp == Player_obj) {                               
3191                                         gameseq_post_event(GS_EVENT_DEATH_BLEW_UP);
3192                                 }
3193
3194                                 // mark object as dead
3195                                 objp->flags |= OF_SHOULD_BE_DEAD;
3196
3197                                 // Don't blow up model.  Only use debris shards.
3198                                 // call ship function to clean up after the ship is destroyed.
3199                                 ship_destroyed(ship_num);
3200                                 return;
3201                         } else {
3202                                 return;
3203                         }
3204                 }
3205
3206                 // bash the desired rotvel
3207                 objp->phys_info.desired_rotvel = sp->deathroll_rotvel;
3208
3209                 // Do fireballs for Big ship with propagating explostion, but not Kamikaze
3210                 if (!(Ai_info[sp->ai_index].ai_flags & AIF_KAMIKAZE) && ship_get_exp_propagates(sp)) {
3211                         if ( timestamp_elapsed(Ships[ship_num].next_fireball))  {
3212                                 vector outpnt, pnt1, pnt2;
3213                                 polymodel *pm = model_get(sp->modelnum);
3214
3215                                 // Gets two random points on the surface of a submodel
3216                                 submodel_get_two_random_points(sp->modelnum, pm->detail[0], &pnt1, &pnt2 );
3217
3218                                 //      vm_vec_avg( &tmp, &pnt1, &pnt2 ); [KNOSSOS get random in plane 1/1.414 in rad
3219                                 model_find_world_point(&outpnt, &pnt1, sp->modelnum, pm->detail[0], &objp->orient, &objp->pos );
3220
3221                                 float rad = objp->radius*0.1f;
3222                                 int fireball_type = FIREBALL_EXPLOSION_LARGE1 + rand()%FIREBALL_NUM_LARGE_EXPLOSIONS;
3223                                 fireball_create( &outpnt, fireball_type, OBJ_INDEX(objp), rad, 0, &objp->phys_info.vel );
3224                                 // start the next fireball up in the next 50 - 200 ms (2-3 per frame)
3225                                 sp->next_fireball = timestamp_rand(333,500);
3226
3227                                 // do sound - maybe start a random sound, if it has played far enough.
3228                                 do_sub_expl_sound(objp->radius, &outpnt, sp->sub_expl_sound_handle);
3229                         }
3230                 }
3231
3232                 // create little fireballs for knossos as it dies
3233                 if (knossos_ship) {
3234                         if ( timestamp_elapsed(Ships[ship_num].next_fireball)) {
3235                                 vector rand_vec, outpnt; // [0-.7 rad] in plane
3236                                 vm_vec_rand_vec_quick(&rand_vec);
3237                                 float scale = -vm_vec_dotprod(&objp->orient.v.fvec, &rand_vec) * (0.9f + 0.2f * frand());
3238                                 vm_vec_scale_add2(&rand_vec, &objp->orient.v.fvec, scale);
3239                                 vm_vec_normalize_quick(&rand_vec);
3240                                 scale = objp->radius * frand() * 0.717f;
3241                                 vm_vec_scale(&rand_vec, scale);
3242                                 vm_vec_add(&outpnt, &objp->pos, &rand_vec);
3243
3244                                 float rad = objp->radius*0.2f;
3245                                 int fireball_type = FIREBALL_EXPLOSION_LARGE1 + rand()%FIREBALL_NUM_LARGE_EXPLOSIONS;
3246                                 fireball_create( &outpnt, fireball_type, OBJ_INDEX(objp), rad, 0, &objp->phys_info.vel );
3247                                 // start the next fireball up in the next 50 - 200 ms (2-3 per frame)
3248                                 sp->next_fireball = timestamp_rand(333,500);
3249
3250                                 // emit particles
3251                                 particle_emitter        pe;
3252
3253                                 pe.num_low = 15;                                        // Lowest number of particles to create
3254                                 pe.num_high = 30;                               // Highest number of particles to create
3255                                 pe.pos = outpnt;                                // Where the particles emit from
3256                                 pe.vel = objp->phys_info.vel;   // Initial velocity of all the particles
3257                                 pe.min_life = 2.0f;     // How long the particles live
3258                                 pe.max_life = 12.0f;    // How long the particles live
3259                                 pe.normal = objp->orient.v.uvec;        // What normal the particle emit around
3260                                 pe.normal_variance = 2.0f;              //      How close they stick to that normal 0=on normal, 1=180, 2=360 degree
3261                                 pe.min_vel = 50.0f;
3262                                 pe.max_vel = 350.0f;
3263                                 pe.min_rad = 30.0f;     // * objp->radius;
3264                                 pe.max_rad = 100.0f; // * objp->radius;
3265                                 particle_emit( &pe, PARTICLE_SMOKE2, 0, 50 );
3266
3267                                 // do sound - maybe start a random sound, if it has played far enough.
3268                                 do_sub_expl_sound(objp->radius, &outpnt, sp->sub_expl_sound_handle);
3269                         }
3270                 }
3271
3272
3273                 //nprintf(("AI", "Ship.cpp: Frame=%i, Time = %7.3f, Ship %s will die in %7.3f seconds.\n", Framecount, f2fl(Missiontime), Ships[ship_num].ship_name, (float) timestamp_until(sp->final_death_time)/1000.0f));
3274                 int time_until_minor_explosions = timestamp_until(sp->final_death_time);
3275
3276                 // Wait until just before death and set off some explosions
3277                 // If it is less than 1/2 second until large explosion, but there is
3278                 // at least 1/10th of a second left, then create 5 small explosions
3279                 if ( (time_until_minor_explosions < 500) && (time_until_minor_explosions > 100) && (!sp->pre_death_explosion_happened) ) {
3280                         //mprintf(( "Ship almost dying!!\n" ));
3281                         sp->next_fireball = timestamp(-1);      // never time out again
3282                         sp->pre_death_explosion_happened=1;             // Mark this event as having occurred
3283
3284                         polymodel *pm = model_get(sp->modelnum);
3285
3286                         // Start shockwave for ship with propagating explosion, do now for timing
3287                         if ( ship_get_exp_propagates(sp) ) {
3288                                 ship_blow_up_area_apply_blast( objp );
3289                         }
3290
3291                         for (int zz=0; zz<6; zz++ ) {
3292                                 // dont make sequence of fireballs for knossos
3293                                 if (knossos_ship) {
3294                                         break;
3295                                 }
3296                                 // Find two random vertices on the model, then average them
3297                                 // and make the piece start there.
3298                                 vector tmp, outpnt, pnt1, pnt2;
3299
3300                                 // Gets two random points on the surface of a submodel [KNOSSOS]
3301                                 submodel_get_two_random_points(sp->modelnum, pm->detail[0], &pnt1, &pnt2 );
3302
3303                                 vm_vec_avg( &tmp, &pnt1, &pnt2 );
3304                                 model_find_world_point(&outpnt, &tmp, sp->modelnum, pm->detail[0], &objp->orient, &objp->pos );
3305
3306                                 float rad = frand()*0.30f;
3307                                 rad += objp->radius*0.40f;
3308                                 fireball_create( &outpnt, FIREBALL_EXPLOSION_MEDIUM, OBJ_INDEX(objp), rad, 0, &objp->phys_info.vel );
3309                         }
3310
3311                         // if ship is docked, undock now.
3312                         if (sp->dock_objnum_when_dead != -1)  {                         
3313                                 // other ship undocks
3314                                 //      These asserts should no longer be needed and they cause a problem that is not obvious how to fix.
3315                                 //Assert( !(Ai_info[Ships[dock_obj->instance].ai_index].ai_flags & AIF_DOCKED) );
3316                                 //Assert( Ai_info[Ships[dock_obj->instance].ai_index].dock_objnum == -1 );
3317                                 // MWA  Ai_info[Ships[dock_obj->instance].ai_index].ai_flags &= ~AIF_DOCKED;
3318                                 // MWA  Ai_info[Ships[dock_obj->instance].ai_index].dock_objnum = -1;
3319                                 // MWA Ai_info[Ships[dock_obj->instance].ai_index].mode = AIM_NONE;
3320                         }
3321                 }
3322
3323                 if ( timestamp_elapsed(sp->final_death_time))   {
3324
3325                         sp->final_death_time = timestamp(-1);   // never time out again
3326                         //mprintf(( "Ship dying!!\n" ));
3327                         
3328                         // play ship explosion sound effect, pick appropriate explosion sound
3329                         int sound_index;
3330                         if ( Ship_info[sp->ship_info_index].flags & (SIF_CAPITAL | SIF_KNOSSOS_DEVICE) ) {
3331                                 sound_index=SND_CAPSHIP_EXPLODE;
3332                         } else {
3333                                 if ( OBJ_INDEX(objp) & 1 ) {
3334                                         sound_index=SND_SHIP_EXPLODE_1;
3335                                 } else {
3336                                         sound_index=SND_SHIP_EXPLODE_2;
3337                                 }
3338                         }
3339
3340                         snd_play_3d( &Snds[sound_index], &objp->pos, &View_position, objp->radius, NULL, 0, 1.0f, SND_PRIORITY_MUST_PLAY  );
3341                         if (objp == Player_obj)
3342                                 joy_ff_explode();
3343
3344                         if ( sp->death_roll_snd != -1 ) {
3345                                 snd_stop(sp->death_roll_snd);
3346                                 sp->death_roll_snd = -1;
3347                         }
3348
3349                         // if dying ship is docked, do damage to docked and physics
3350                         if (sp->dock_objnum_when_dead != -1)  {
3351                                 do_dying_undock_physics(objp, sp);
3352                         }                       
3353
3354                         // play a random explosion
3355                         particle_emitter        pe;
3356
3357                         pe.num_low = 50;                                        // Lowest number of particles to create
3358                         pe.num_high = 100;                              // Highest number of particles to create
3359                         pe.pos = objp->pos;                             // Where the particles emit from
3360                         pe.vel = objp->phys_info.vel;   // Initial velocity of all the particles
3361                         pe.min_life = 0.5f;                             // How long the particles live
3362                         pe.max_life = 4.0f;                             // How long the particles live
3363                         pe.normal = objp->orient.v.uvec;        // What normal the particle emit around
3364                         pe.normal_variance = 2.0f;              //      How close they stick to that normal 0=on normal, 1=180, 2=360 degree
3365                         pe.min_vel = 0.0f;                              // How fast the slowest particle can move
3366                         pe.max_vel = 20.0f;                             // How fast the fastest particle can move
3367                         pe.min_rad = 0.1f;                              // Min radius
3368                         pe.max_rad = 1.5f;                              // Max radius
3369
3370                         if (!knossos_ship) {
3371                                 particle_emit( &pe, PARTICLE_SMOKE2, 0 );
3372                         }
3373
3374                         // If this is a large ship with a propagating explosion, set it to blow up.
3375                         if ( ship_get_exp_propagates(sp) )      {
3376                                 if (Ai_info[sp->ai_index].ai_flags & AIF_KAMIKAZE) {
3377                                         ship_blow_up_area_apply_blast( objp );
3378                                 }
3379                                 shipfx_large_blowup_init(sp);
3380                                 // need to timeout immediately to keep physics in sync
3381                                 sp->really_final_death_time = timestamp(0);
3382                         } else {
3383                                 // only do big fireball if not big ship
3384                                 float big_rad;
3385                                 int fireball_objnum, fireball_type;
3386                                 float explosion_life;
3387                                 big_rad = objp->radius*1.75f;
3388                                 fireball_type = FIREBALL_EXPLOSION_LARGE1 + rand()%FIREBALL_NUM_LARGE_EXPLOSIONS;
3389                                 if (knossos_ship) {
3390                                         big_rad = objp->radius * 1.2f;
3391                                         fireball_type = FIREBALL_EXPLOSION_LARGE1;
3392                                 }
3393                                 fireball_objnum = fireball_create( &objp->pos, fireball_type, OBJ_INDEX(objp), big_rad, 0, &objp->phys_info.vel );
3394                                 if ( fireball_objnum > -1 )     {
3395                                         explosion_life = fireball_lifeleft(&Objects[fireball_objnum]);
3396                                 } else {
3397                                         explosion_life = 0.0f;
3398                                 }
3399
3400                                 // JAS:  I put in all this code because of an item on my todo list that
3401                                 // said that the ship destroyed debris shouldn't pop in until the
3402                                 // big explosion is 30% done.  I did this on Oct24 and me & Adam 
3403                                 // thought it looked dumb since the explosion didn't move with the
3404                                 // ship, so instead of just taking this code out, since we might need
3405                                 // it in the future, I disabled it.   You can reenable it by changing
3406                                 // the commenting on the following two lines.
3407                                 sp->really_final_death_time = timestamp( fl2i(explosion_life*1000.0f)/5 );      // Wait till 30% of vclip time before breaking the ship up.
3408                                 //sp->really_final_death_time = timestamp(0);   // Make ship break apart the instant the explosion starts
3409                         }
3410
3411                         sp->flags |= SF_EXPLODED;
3412
3413                         if ( !(ship_get_exp_propagates(sp)) ) {
3414                                 // apply area of effect blast damage from ship explosion
3415                                 ship_blow_up_area_apply_blast( objp );
3416                         }
3417                 }
3418
3419                 if ( timestamp_elapsed(sp->really_final_death_time))    {
3420
3421                         //mprintf(( "Ship really dying!!\n" ));
3422                         // do large_ship_split and explosion
3423                         if ( sp->large_ship_blowup_index > -1 ) {
3424                                 if ( shipfx_large_blowup_do_frame(sp, flFrametime) )    {
3425                                         // do all accounting for respawning client and server side here.
3426                                         if(objp == Player_obj) {                                
3427                                                 gameseq_post_event(GS_EVENT_DEATH_BLEW_UP);
3428                                         }
3429
3430                                         objp->flags |= OF_SHOULD_BE_DEAD;                                                                       
3431                                         
3432                                         ship_destroyed(ship_num);               // call ship function to clean up after the ship is destroyed.
3433                                 }
3434                                 return;
3435                         } 
3436
3437                         //fireball_create( &objp->pos, FIREBALL_SHIP_EXPLODE1, OBJ_INDEX(objp), objp->radius/2.0f );
3438                         //mprintf(("Frame %i: Died!\n", Framecount));
3439
3440                         shipfx_blow_up_model(objp, Ships[ship_num].modelnum, 0, 20, &objp->pos );
3441
3442                         // do all accounting for respawning client and server side here.
3443                         if(objp == Player_obj) {                                
3444                                 gameseq_post_event(GS_EVENT_DEATH_BLEW_UP);
3445                         }
3446
3447                         objp->flags |= OF_SHOULD_BE_DEAD;
3448                                                                 
3449                         ship_destroyed(ship_num);               // call ship function to clean up after the ship is destroyed.
3450                         sp->really_final_death_time = timestamp( -1 );  // Never time out again!
3451                 }
3452
3453                 // If a ship is dying (and not a capital or big ship) then stutter the engine sound
3454                 if ( timestamp_elapsed(sp->next_engine_stutter) ) {
3455                         if ( !(Ship_info[sp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
3456                                 sp->flags ^= SF_ENGINES_ON;                     // toggle state of engines
3457                                 sp->next_engine_stutter = timestamp_rand(50, 250);
3458                         }
3459                 }
3460         }
3461 }
3462
3463 void ship_chase_shield_energy_targets(ship *shipp, object *obj, float frametime)
3464 {
3465         float delta;
3466         ship_info       *sip;
3467
3468         if (shipp->flags & SF_DYING)
3469                 return;
3470
3471         sip = &Ship_info[shipp->ship_info_index];
3472
3473         delta = frametime * ETS_RECHARGE_RATE * sip->shields / 100.0f;
3474
3475         //      Chase target_shields and target_weapon_energy
3476         if (shipp->target_shields_delta > 0.0f) {
3477                 if (delta > shipp->target_shields_delta)
3478                         delta = shipp->target_shields_delta;
3479
3480                 add_shield_strength(obj, delta);
3481                 shipp->target_shields_delta -= delta;
3482         } else if (shipp->target_shields_delta < 0.0f) {
3483                 if (delta < -shipp->target_shields_delta)
3484                         delta = -shipp->target_shields_delta;
3485
3486                 add_shield_strength(obj, -delta);
3487                 shipp->target_shields_delta += delta;
3488         }
3489
3490         delta = frametime * ETS_RECHARGE_RATE * sip->max_weapon_reserve / 100.0f;
3491
3492         if (shipp->target_weapon_energy_delta > 0.0f) {
3493                 if (delta > shipp->target_weapon_energy_delta)
3494                         delta = shipp->target_weapon_energy_delta;
3495
3496                 shipp->weapon_energy += delta;
3497                 shipp->target_weapon_energy_delta -= delta;
3498         } else if (shipp->target_weapon_energy_delta < 0.0f) {
3499                 if (delta < -shipp->target_weapon_energy_delta)
3500                         delta = -shipp->target_weapon_energy_delta;
3501
3502                 shipp->weapon_energy -= delta;
3503                 shipp->target_weapon_energy_delta += delta;
3504         }
3505
3506 }
3507
3508 // Stuff for showing ship thrusters. 
3509 typedef struct thrust_anim {
3510         int     num_frames;
3511         int     first_frame;
3512         float time;                             // in seconds
3513 } thrust_anim;
3514
3515 #define NUM_THRUST_ANIMS                        6
3516 #define NUM_THRUST_GLOW_ANIMS           6
3517
3518 // These are indexed by:  Species*2 + (After_burner_on?1:0)
3519 static thrust_anim      Thrust_anims[NUM_THRUST_ANIMS];
3520 char    Thrust_anim_names[NUM_THRUST_ANIMS][MAX_FILENAME_LEN] = {       
3521 //XSTR:OFF
3522         "thruster01", "thruster01a", 
3523         "thruster02", "thruster02a", 
3524         "thruster03", "thruster03a" 
3525 //XSTR:ON
3526 };
3527
3528 // These are indexed by:  Species*2 + (After_burner_on?1:0)
3529 static thrust_anim      Thrust_glow_anims[NUM_THRUST_GLOW_ANIMS];
3530 char    Thrust_glow_anim_names[NUM_THRUST_GLOW_ANIMS][MAX_FILENAME_LEN] = {     
3531 //XSTR:OFF
3532         "thrusterglow01", "thrusterglow01a", 
3533         "thrusterglow02", "thrusterglow02a", 
3534         "thrusterglow03", "thrusterglow03a" 
3535 //XSTR:ON
3536 };
3537
3538 static int Thrust_anim_inited = 0;
3539
3540 // loads the animations for ship's afterburners
3541 void ship_init_thrusters()
3542 {
3543         int                     fps, i;
3544         thrust_anim     *ta;
3545
3546         if ( Thrust_anim_inited == 1 )
3547                 return;
3548
3549         // AL 29-3-98: Don't want to include Shivan thrusters in the demo build
3550         int num_thrust_anims = NUM_THRUST_ANIMS;
3551         #ifdef DEMO // N/A FS2_DEMO
3552                 num_thrust_anims = NUM_THRUST_ANIMS - 2;
3553         #endif
3554
3555         for ( i = 0; i < num_thrust_anims; i++ ) {
3556                 ta = &Thrust_anims[i];
3557                 ta->first_frame = bm_load_animation(Thrust_anim_names[i],  &ta->num_frames, &fps, 1);
3558                 if ( ta->first_frame == -1 ) {
3559                         Error(LOCATION,"Error loading animation file: %s\n",Thrust_anim_names[i]);
3560                         return;
3561                 }
3562                 Assert(fps != 0);
3563                 ta->time = i2fl(ta->num_frames)/fps;
3564         }
3565
3566         // AL 29-3-98: Don't want to include Shivan thrusters in the demo build
3567         int num_thrust_glow_anims = NUM_THRUST_GLOW_ANIMS;
3568         #ifdef DEMO // N/A FS2_DEMO
3569                 num_thrust_glow_anims = NUM_THRUST_GLOW_ANIMS - 2;
3570         #endif
3571
3572         for ( i = 0; i < num_thrust_glow_anims; i++ ) {
3573                 ta = &Thrust_glow_anims[i];
3574                 ta->num_frames = NOISE_NUM_FRAMES;
3575                 fps = 15;
3576                 ta->first_frame = bm_load( Thrust_glow_anim_names[i] );
3577                 if ( ta->first_frame == -1 ) {
3578                         Error(LOCATION,"Error loading bitmap file: %s\n",Thrust_glow_anim_names[i]);
3579                         return;
3580                 }
3581                 Assert(fps != 0);
3582                 ta->time = i2fl(ta->num_frames)/fps;
3583         }
3584
3585         Thrust_anim_inited = 1;
3586 }
3587
3588
3589 // JAS - figure out which thruster bitmap will get rendered next
3590 // time around.  ship_render needs to have shipp->thruster_bitmap set to
3591 // a valid bitmap number, or -1 if we shouldn't render thrusters.
3592 void ship_do_thruster_frame( ship *shipp, object *objp, float frametime )
3593 {
3594         float rate;
3595         int framenum;
3596         int anim_index;
3597         thrust_anim *the_anim;
3598         ship_info       *sinfo = &Ship_info[shipp->ship_info_index];
3599
3600         if ( !Thrust_anim_inited )      ship_init_thrusters();
3601
3602         // The animations are organized by:
3603         // Species*2 + (After_burner_on?1:0)
3604         anim_index = sinfo->species*2;
3605
3606         if ( objp->phys_info.flags & PF_AFTERBURNER_ON )        {
3607                 anim_index++;           //      select afterburner anim.
3608                 rate = 1.5f;            // go at 1.5x faster when afterburners on
3609         } else {
3610                 // If thrust at 0, go at half as fast, full thrust; full framerate
3611                 // so set rate from 0.5 to 1.0, depending on thrust from 0 to 1
3612                 // rate = 0.5f + objp->phys_info.forward_thrust / 2.0f;
3613                 rate = 0.67f * (1.0f + objp->phys_info.forward_thrust);
3614         }
3615
3616 //      rate = 0.1f;
3617
3618         Assert( anim_index > -1 );
3619         Assert( anim_index < NUM_THRUST_ANIMS );
3620
3621         the_anim = &Thrust_anims[anim_index];
3622
3623         Assert( frametime > 0.0f );
3624         shipp->thruster_frame += frametime * rate;
3625
3626         // Sanity checks
3627         if ( shipp->thruster_frame < 0.0f )     shipp->thruster_frame = 0.0f;
3628         if ( shipp->thruster_frame > 100.0f ) shipp->thruster_frame = 0.0f;
3629
3630         while ( shipp->thruster_frame > the_anim->time )        {
3631                 shipp->thruster_frame -= the_anim->time;
3632         }
3633         framenum = fl2i( (shipp->thruster_frame*the_anim->num_frames) / the_anim->time );
3634         if ( framenum < 0 ) framenum = 0;
3635         if ( framenum >= the_anim->num_frames ) framenum = the_anim->num_frames-1;
3636
3637 //      if ( anim_index == 0 )
3638 //              mprintf(( "Frame = %d/%d, anim=%d\n", framenum+1,  the_anim->num_frames, anim_index ));
3639         
3640         // Get the bitmap for this frame
3641         shipp->thruster_bitmap = the_anim->first_frame + framenum;
3642
3643 //      mprintf(( "TF: %.2f\n", shipp->thruster_frame ));
3644
3645         // Do it for glow bitmaps
3646         the_anim = &Thrust_glow_anims[anim_index];
3647
3648         Assert( frametime > 0.0f );
3649         shipp->thruster_glow_frame += frametime * rate;
3650
3651         // Sanity checks
3652         if ( shipp->thruster_glow_frame < 0.0f )        shipp->thruster_glow_frame = 0.0f;
3653         if ( shipp->thruster_glow_frame > 100.0f ) shipp->thruster_glow_frame = 0.0f;
3654
3655         while ( shipp->thruster_glow_frame > the_anim->time )   {
3656                 shipp->thruster_glow_frame -= the_anim->time;
3657         }
3658         framenum = fl2i( (shipp->thruster_glow_frame*the_anim->num_frames) / the_anim->time );
3659         if ( framenum < 0 ) framenum = 0;
3660         if ( framenum >= the_anim->num_frames ) framenum = the_anim->num_frames-1;
3661
3662 //      if ( anim_index == 0 )
3663 //              mprintf(( "Frame = %d/%d, anim=%d\n", framenum+1,  the_anim->num_frames, anim_index ));
3664         
3665         // Get the bitmap for this frame
3666         shipp->thruster_glow_bitmap = the_anim->first_frame;    // + framenum;
3667         shipp->thruster_glow_noise = Noise[framenum];
3668
3669 }
3670
3671
3672 // JAS - figure out which thruster bitmap will get rendered next
3673 // time around.  ship_render needs to have shipp->thruster_bitmap set to
3674 // a valid bitmap number, or -1 if we shouldn't render thrusters.
3675 // This does basically the same thing as ship_do_thruster_frame, except it
3676 // operates on a weapon.   This is in the ship code because it needs
3677 // the same thruster animation info as the ship stuff, and I would
3678 // rather extern this one function than all the thruster animation stuff.
3679 void ship_do_weapon_thruster_frame( weapon *weaponp, object *objp, float frametime )
3680 {
3681         float rate;
3682         int framenum;
3683         int anim_index;
3684         thrust_anim *the_anim;
3685
3686         if ( !Thrust_anim_inited )      ship_init_thrusters();
3687
3688         // The animations are organized by:
3689         // Species*2 + (After_burner_on?1:0)
3690         anim_index = weaponp->species*2;
3691
3692         // If thrust at 0, go at half as fast, full thrust; full framerate
3693         // so set rate from 0.5 to 1.0, depending on thrust from 0 to 1
3694         // rate = 0.5f + objp->phys_info.forward_thrust / 2.0f;
3695         rate = 0.67f * (1.0f + objp->phys_info.forward_thrust);
3696
3697         Assert( anim_index > -1 );
3698         Assert( anim_index < NUM_THRUST_ANIMS );
3699
3700         the_anim = &Thrust_anims[anim_index];
3701
3702         Assert( frametime > 0.0f );
3703         weaponp->thruster_frame += frametime * rate;
3704
3705         // Sanity checks
3706         if ( weaponp->thruster_frame < 0.0f )   weaponp->thruster_frame = 0.0f;
3707         if ( weaponp->thruster_frame > 100.0f ) weaponp->thruster_frame = 0.0f;
3708
3709         while ( weaponp->thruster_frame > the_anim->time )      {
3710                 weaponp->thruster_frame -= the_anim->time;
3711         }
3712         framenum = fl2i( (weaponp->thruster_frame*the_anim->num_frames) / the_anim->time );
3713         if ( framenum < 0 ) framenum = 0;
3714         if ( framenum >= the_anim->num_frames ) framenum = the_anim->num_frames-1;
3715
3716 //      if ( anim_index == 0 )
3717 //              mprintf(( "Frame = %d/%d, anim=%d\n", framenum+1,  the_anim->num_frames, anim_index ));
3718         
3719         // Get the bitmap for this frame
3720         weaponp->thruster_bitmap = the_anim->first_frame + framenum;
3721
3722 //      mprintf(( "TF: %.2f\n", weaponp->thruster_frame ));
3723
3724         // Do it for glow bitmaps
3725         the_anim = &Thrust_glow_anims[anim_index];
3726
3727         Assert( frametime > 0.0f );
3728         weaponp->thruster_glow_frame += frametime * rate;
3729
3730         // Sanity checks
3731         if ( weaponp->thruster_glow_frame < 0.0f )      weaponp->thruster_glow_frame = 0.0f;
3732         if ( weaponp->thruster_glow_frame > 100.0f ) weaponp->thruster_glow_frame = 0.0f;
3733
3734         while ( weaponp->thruster_glow_frame > the_anim->time ) {
3735                 weaponp->thruster_glow_frame -= the_anim->time;
3736         }
3737         framenum = fl2i( (weaponp->thruster_glow_frame*the_anim->num_frames) / the_anim->time );
3738         if ( framenum < 0 ) framenum = 0;
3739         if ( framenum >= the_anim->num_frames ) framenum = the_anim->num_frames-1;
3740
3741 //      if ( anim_index == 0 )
3742 //              mprintf(( "Frame = %d/%d, anim=%d\n", framenum+1,  the_anim->num_frames, anim_index ));
3743         
3744         // Get the bitmap for this frame
3745         weaponp->thruster_glow_bitmap = the_anim->first_frame;  // + framenum;
3746         weaponp->thruster_glow_noise = Noise[framenum];
3747 }
3748
3749
3750
3751 // Repair damaged subsystems for a ship, called for each ship once per frame.
3752 // TODO: optimize by only calling ever N seconds and keeping track of elapsed time
3753 //
3754 // NOTE: need to update current_hits in the sp->subsys_list element, and the sp->subsys_info[]
3755 // element.
3756 #define SHIP_REPAIR_SUBSYSTEM_RATE      0.01f   // percent repair per second for a subsystem
3757 #define SUBSYS_REPAIR_THRESHOLD         0.1     // only repair subsystems that have > 10% strength
3758 void ship_auto_repair_frame(int shipnum, float frametime)
3759 {
3760         ship_subsys                     *ssp;
3761         ship_subsys_info        *ssip;
3762         ship                                    *sp;
3763         ship_info                       *sip;
3764
3765         #ifndef NDEBUG
3766         if ( !Ship_auto_repair )        // only repair subsystems if Ship_auto_repair flag is set
3767                 return;
3768         #endif
3769
3770         Assert( shipnum >= 0 && shipnum < MAX_SHIPS);
3771         sp = &Ships[shipnum];
3772         sip = &Ship_info[sp->ship_info_index];
3773
3774         // only allow for the auto-repair of subsystems on small ships
3775         if ( !(sip->flags & SIF_SMALL_SHIP) )
3776                 return;
3777
3778         // AL 3-14-98: only allow auto-repair if power output not zero
3779         if ( sip->power_output <= 0 )
3780                 return;
3781         
3782         // iterate through subsystems, repair as needed based on elapsed frametime
3783         for ( ssp = GET_FIRST(&sp->subsys_list); ssp != END_OF_LIST(&sp->subsys_list); ssp = GET_NEXT(ssp) ) {
3784                 Assert(ssp->system_info->type >= 0 && ssp->system_info->type < SUBSYSTEM_MAX);
3785                 ssip = &sp->subsys_info[ssp->system_info->type];
3786
3787                 if ( ssp->current_hits != ssp->system_info->max_hits ) {                
3788
3789                         // only repair those subsystems which are not destroyed
3790                         if ( ssp->system_info->max_hits <= 0 || ssp->current_hits <= 0 )
3791                                 continue;
3792
3793                         // do incremental repair on the subsystem
3794                         ssp->current_hits += ssp->system_info->max_hits * SHIP_REPAIR_SUBSYSTEM_RATE * frametime;
3795                         ssip->current_hits += ssip->total_hits * SHIP_REPAIR_SUBSYSTEM_RATE * frametime;
3796                 
3797                         // check for overflow of current_hits
3798                         if ( ssp->current_hits >= ssp->system_info->max_hits ) {
3799                                 // TODO: here is hook for when a subsystem is fully repaired (eg add voice)
3800                                 ssp->current_hits = ssp->system_info->max_hits;
3801                         }
3802                         if ( ssip->current_hits >= ssip->total_hits ) {
3803                                 ssip->current_hits = ssip->total_hits;
3804                         }
3805                 }
3806         }       // end for
3807 }
3808
3809 // this function checks to see how far the player has strayed from his starting location (should be
3810 // single player only).  Issues a warning at some distance.  Makes mission end if he keeps flying away
3811 // 3 strikes and you're out or too far away
3812 #define PLAYER_MAX_DIST_WARNING                 70000                   // distance in KM at which player gets warning to return to battle
3813 #define PLAYER_DISTANCE_MAX_WARNINGS    3                               // maximum number of warnings player can receive before mission ends
3814 #define PLAYER_MAX_DIST_END                             75000                   // distance from starting loc at which we end mission
3815 #define PLAYER_WARN_DELTA_TIME                  10000
3816 #define PLAYER_DEATH_DELTA_TIME                 5000
3817
3818 void ship_check_player_distance_sub(player *p, int multi_target=-1)
3819 {
3820         // only check distance for ships
3821         if ( p->control_mode != PCM_NORMAL )    {
3822                 // already warping out... don't bother checking anymore
3823                 return;
3824         }
3825
3826         float dist = vm_vec_dist_quick(&Objects[p->objnum].pos, &vmd_zero_vector);
3827
3828         int give_warning_to_player = 0;
3829         if ( dist > PLAYER_MAX_DIST_WARNING ) {
3830                 if (p->distance_warning_count == 0) {
3831                         give_warning_to_player = 1;
3832                 } else {
3833                         if (timestamp_until(p->distance_warning_time) < 0) {
3834                                 give_warning_to_player = 1;
3835                         }
3836                 }
3837         }
3838
3839         if ( give_warning_to_player ) {
3840                 // increase warning count
3841                 p->distance_warning_count++;
3842                 // set timestamp unless player PLAYER_FLAGS_DIST_TO_BE_KILLED flag is set
3843                 if ( !(p->flags & PLAYER_FLAGS_DIST_TO_BE_KILLED) ) {
3844                         p->distance_warning_time = timestamp(PLAYER_WARN_DELTA_TIME);
3845                 }
3846                 // issue up to max warnings
3847                 if (p->distance_warning_count <= PLAYER_DISTANCE_MAX_WARNINGS) {
3848                         message_send_builtin_to_player( MESSAGE_STRAY_WARNING, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_SOON, 0, 0, multi_target, -1 );
3849                 }
3850
3851 //              HUD_sourced_printf(HUD_SOURCE_TERRAN_CMD, XSTR("Terran Command: You're straying too far from battle pilot, return immediately or be taken from the battlefield.", -1));
3852                 if (p->distance_warning_count > PLAYER_DISTANCE_MAX_WARNINGS) {
3853                         p->flags |= PLAYER_FLAGS_DIST_WARNING;
3854                 }
3855         }
3856
3857         if ( !(p->flags & PLAYER_FLAGS_FORCE_MISSION_OVER) && ((p->distance_warning_count > PLAYER_DISTANCE_MAX_WARNINGS) || (dist > PLAYER_MAX_DIST_END)) ) {
3858 //              DKA 5/17/99 - DONT force warpout.  Won't work multiplayer.  Blow up ship.
3859                 if ( !(p->flags & PLAYER_FLAGS_DIST_TO_BE_KILLED) ) {
3860                         message_send_builtin_to_player( MESSAGE_STRAY_WARNING_FINAL, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, multi_target, -1 );
3861                         p->flags |= PLAYER_FLAGS_DIST_TO_BE_KILLED;
3862                         p->distance_warning_time = timestamp(PLAYER_DEATH_DELTA_TIME);
3863                 }
3864 //              HUD_sourced_printf(HUD_SOURCE_TERRAN_CMD, XSTR("Terran Command: Sorry pilot, removing you from battle because of your insubordination!!!", -1));
3865 //              gameseq_post_event(GS_EVENT_PLAYER_WARPOUT_START_FORCED);
3866
3867                 // get hull strength and blow up
3868                 if ( (p->flags & PLAYER_FLAGS_DIST_TO_BE_KILLED) && (timestamp_until(p->distance_warning_time) < 0) ) {
3869                         p->flags |= PLAYER_FLAGS_FORCE_MISSION_OVER;
3870                         float damage = 10.0f * Objects[p->objnum].hull_strength;
3871                         ship_apply_global_damage(&Objects[p->objnum], &Objects[p->objnum], NULL, damage);
3872                 }
3873         }
3874
3875         // see if player has moved back into "bounds"
3876         if ( (dist < PLAYER_MAX_DIST_WARNING) && (p->flags & PLAYER_FLAGS_DIST_WARNING) && !(p->flags & PLAYER_FLAGS_DIST_TO_BE_KILLED) ) {
3877                 p->flags &= ~PLAYER_FLAGS_DIST_WARNING;
3878                 p->distance_warning_count = 1;
3879         }
3880 }
3881
3882 void ship_check_player_distance()
3883 {
3884         int idx;
3885
3886         // multiplayer
3887         if (Game_mode & GM_MULTIPLAYER) {
3888                 // if I'm the server, check all non-observer players including myself
3889                 if (MULTIPLAYER_MASTER) {
3890                         // warn all players
3891                         for (idx=0; idx<MAX_PLAYERS; idx++) {
3892                                 if (MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx]) && (Objects[Net_players[idx].player->objnum].type != OBJ_GHOST) ) {
3893                                         // if bad, blow him up
3894                                         ship_check_player_distance_sub(Net_players[idx].player, idx);
3895                                 }
3896                         }
3897                 }
3898         }
3899         // single player
3900         else {
3901                 // maybe blow him up
3902                 ship_check_player_distance_sub(Player);
3903         }               
3904 }
3905
3906 void observer_process_post(object *objp)
3907 {
3908         Assert(objp->type == OBJ_OBSERVER);
3909
3910         if (Game_mode & GM_MULTIPLAYER) {
3911                 // if I'm just an observer
3912                 if (MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])) {
3913                         float dist = vm_vec_dist_quick(&Player_obj->pos, &vmd_zero_vector);
3914                         // if beyond max dist, reset to 0
3915                         if (dist > PLAYER_MAX_DIST_END) {
3916                                 // set me to zero
3917                                 if ((Player_obj != NULL) && (Player_obj->type != OBJ_GHOST)) {
3918                                         Player_obj->pos = vmd_zero_vector;
3919                                 }
3920                         }
3921                 }
3922         }
3923 }
3924
3925 // reset some physics info when ship's engines goes from disabled->enabled 
3926 void ship_reset_disabled_physics(object *objp, int ship_class)
3927 {
3928         objp->phys_info.flags &= ~(PF_REDUCED_DAMP | PF_DEAD_DAMP);
3929         objp->phys_info.side_slip_time_const = Ship_info[ship_class].damp;
3930 }
3931
3932 // Clear/set the subsystem disrupted flags
3933 void ship_subsys_disrupted_check(ship *sp)
3934 {
3935         ship_subsys *ss;
3936         int engines_disabled=0;
3937         
3938         if ( sp->subsys_disrupted_flags & (1<<SUBSYSTEM_ENGINE) ) {
3939                 engines_disabled=1;
3940         }
3941
3942         sp->subsys_disrupted_flags=0;
3943
3944         ss = GET_FIRST(&sp->subsys_list);
3945         while ( ss != END_OF_LIST( &sp->subsys_list ) ) {
3946                 if ( !timestamp_elapsed(ss->disruption_timestamp) ) {
3947                         sp->subsys_disrupted_flags |= (1<<ss->system_info->type);
3948                 }
3949                 ss = GET_NEXT( ss );
3950         }
3951
3952         if ( engines_disabled ) {
3953                 if ( !(sp->subsys_disrupted_flags & (1<<SUBSYSTEM_ENGINE)) ) {
3954                         if ( !(sp->flags & SF_DISABLED) ) {
3955                                 ship_reset_disabled_physics(&Objects[sp->objnum], sp->ship_info_index);
3956                         }
3957                 }
3958         }
3959 }
3960
3961 // Maybe check ship subsystems for disruption, and set/clear flags
3962 void ship_subsys_disrupted_maybe_check(ship *shipp)
3963 {
3964         if ( timestamp_elapsed(shipp->subsys_disrupted_check_timestamp) ) {
3965                 ship_subsys_disrupted_check(shipp);
3966                 shipp->subsys_disrupted_check_timestamp=timestamp(250);
3967         }
3968 }
3969
3970 // Determine if a given subsystem is disrupted (ie inoperable)
3971 // input:       ss              =>              pointer to ship subsystem
3972 // exit:                1               =>              subsystem is disrupted
3973 //                              0               =>              subsystem is not disrupted
3974 int ship_subsys_disrupted(ship_subsys *ss)
3975 {
3976         if ( !ss ) {
3977                 Int3();         // should never happen, get Alan if it does.
3978                 return 0;
3979         }
3980
3981         if ( timestamp_elapsed(ss->disruption_timestamp) ) {
3982                 return 0;
3983         } else {
3984                 return 1;
3985         }
3986 }
3987
3988 // Disrupt a subsystem (ie make it inoperable for a time)
3989 // input:       ss              =>              ship subsystem to be disrupted
3990 //                              time    =>              time in ms that subsystem should be disrupted
3991 void ship_subsys_set_disrupted(ship_subsys *ss, int time)
3992 {
3993         int time_left=0;
3994
3995         if ( !ss ) {
3996                 Int3();         // should never happen, get Alan if it does.
3997                 return;
3998         }
3999
4000         time_left=timestamp_until(ss->disruption_timestamp);
4001         if ( time_left < 0 ) {
4002                 time_left=0;
4003         }
4004
4005         ss->disruption_timestamp = timestamp(time+time_left);
4006 }
4007
4008 // Determine if a given subsystem is disrupted (ie inoperable)
4009 // input:       sp              =>              pointer to ship containing subsystem
4010 //                              type    =>              type of subsystem (SUBSYSTEM_*)
4011 // exit:                1               =>              subsystem is disrupted
4012 //                              0               =>              subsystem is not disrupted
4013 //
4014 int ship_subsys_disrupted(ship *sp, int type)
4015 {
4016         if ( sp->subsys_disrupted_flags & (1<<type) ) {
4017                 return 1;
4018         } else {
4019                 return 0;
4020         }
4021 }
4022
4023 float Decay_rate = 1.0f / 120.0f;
4024 DCF(lethality_decay, "time in sec to return from 100 to 0")
4025 {
4026         dc_get_arg(ARG_FLOAT);
4027         Decay_rate = Dc_arg_float;
4028 }
4029
4030 float min_lethality = 0.0f;
4031
4032 void lethality_decay(ai_info *aip)
4033 {
4034         float decay_rate = Decay_rate;
4035         aip->lethality -= 100.0f * decay_rate * flFrametime;
4036         aip->lethality = max(-10.0f, aip->lethality);
4037
4038 //      if (aip->lethality < min_lethality) {
4039 //              min_lethality = aip->lethality;
4040 //              mprintf(("new lethality low: %.1f\n", min_lethality));
4041 //      }
4042
4043 #ifndef NDEBUG
4044         if (Objects[Ships[aip->shipnum].objnum].flags & OF_PLAYER_SHIP) {
4045                 if (Framecount % 10 == 0) {
4046                         int num_turrets = 0;
4047                         if ((aip->target_objnum != -1) && (Objects[aip->target_objnum].type == OBJ_SHIP)) {
4048                                 int num_turrets_attacking(object *turret_parent, int target_objnum);
4049                                 num_turrets = num_turrets_attacking(&Objects[aip->target_objnum], Ships[aip->shipnum].objnum);
4050                         }
4051                         nprintf(("lethality", "Player lethality: %.1f, num turrets targeting player: %d\n", aip->lethality, num_turrets));
4052                 }
4053         }
4054 #endif
4055 }
4056
4057 void ship_process_pre(object *objp, float frametime)
4058 {
4059 }
4060
4061 MONITOR( NumShips );    
4062
4063 //      Player ship uses this code, but does a quick out after doing a few things.
4064 // when adding code to this function, decide whether or not a client in a multiplayer game
4065 // needs to execute the code you are adding.  Code which moves things, creates things, etc
4066 // probably doesn't need to be called.  If you don't know -- find Allender!!!
4067 void ship_process_post(object * obj, float frametime)
4068 {
4069         int     num;
4070         ship    *shipp;
4071
4072         if(obj->type != OBJ_SHIP){
4073                 nprintf(("Network","Ignoring non-ship object in ship_process_post()\n"));
4074                 return;
4075         }
4076
4077         MONITOR_INC( NumShips, 1 );     
4078
4079         num = obj->instance;
4080         Assert( num >= 0 && num < MAX_SHIPS);
4081         Assert( obj->type == OBJ_SHIP );
4082         Assert( Ships[num].objnum == OBJ_INDEX(obj));   
4083
4084         shipp = &Ships[num];
4085
4086         shipp->shield_hits = 0;
4087
4088         update_ets(obj, frametime);
4089
4090         afterburners_update(obj, frametime);
4091
4092         ship_subsys_disrupted_maybe_check(shipp);
4093
4094         ship_dying_frame(obj, num);
4095
4096         ship_chase_shield_energy_targets(shipp, obj, frametime);
4097
4098         // AL 1-6-98: record the initial ammo counts for ships, which is used as the max limit for rearming
4099         if ( !(shipp->flags & SF_AMMO_COUNT_RECORDED) ) {
4100                 for ( int i=0; i<MAX_SECONDARY_BANKS; i++ ) {
4101                         if ( red_alert_mission() ) {
4102                                 int max_missiles = get_max_ammo_count_for_bank(shipp->ship_info_index, i, shipp->weapons.secondary_bank_weapons[i]);
4103                                 shipp->weapons.secondary_bank_start_ammo[i] = max_missiles;
4104                         } else {
4105                                 shipp->weapons.secondary_bank_start_ammo[i] = shipp->weapons.secondary_bank_ammo[i];
4106                         }
4107                 }
4108                 shipp->flags |= SF_AMMO_COUNT_RECORDED;
4109         }
4110
4111         if(!(Game_mode & GM_STANDALONE_SERVER)){
4112                 // Plot ship on the radar.  What about multiplayer ships?
4113                 if ( obj != Player_obj )                        // don't plot myself.
4114                         radar_plot_object( obj );
4115
4116                 // MWA -- move the spark code to before the check for multiplayer master
4117                 //      Do ship sparks.  Don't do sparks on my ship (since I cannot see it).  This
4118                 // code will do sparks on other ships in multiplayer though.
4119                 // JAS: Actually in external view, you can see sparks, so I don't do sparks
4120                 // on the Viewer_obj, not Player_obj.
4121                 if ( (obj != Viewer_obj) && timestamp_elapsed(Ships[num].next_hit_spark) )      {
4122                         shipfx_emit_spark(num,-1);      // -1 means choose random spark location
4123                 }
4124
4125                 if ( obj != Viewer_obj )        {
4126                         shipfx_do_damaged_arcs_frame( shipp );
4127                 }
4128
4129                 // JAS - flicker the thruster bitmaps
4130                 ship_do_thruster_frame(shipp,obj,frametime);            
4131         }
4132
4133         ship_auto_repair_frame(num, frametime);
4134
4135         // MWA -- move the spark code to before the check for multiplayer master
4136         //      Do ship sparks.
4137 //      if (timestamp_elapsed(Ships[num].next_hit_spark))       {
4138 //              ship_spark(num);
4139 //              Ships[num].next_hit_spark = timestamp_rand(100,500);
4140 //      }
4141
4142         shipfx_do_lightning_frame(shipp);
4143
4144         // if the ship has an EMP effect active, process it
4145         emp_process_ship(shipp);        
4146
4147         // call the contrail system
4148         ct_ship_process(shipp);
4149
4150         // process engine wash
4151         void engine_wash_ship_process(ship *shipp);
4152         engine_wash_ship_process(shipp);
4153
4154         // update TAG info
4155         if(shipp->tag_left > 0.0f){
4156                 shipp->tag_left -= flFrametime;
4157                 if(shipp->tag_left <= 0.000001f){
4158                         shipp->tag_left = -1.0f;
4159
4160                         mprintf(("Killing TAG for %s\n", shipp->ship_name));
4161                 }
4162         }
4163         
4164         // update level 2 TAG info
4165         if(shipp->level2_tag_left > 0.0f){
4166                 shipp->level2_tag_left -= flFrametime;
4167                 if(shipp->level2_tag_left <= 0.000001f){
4168                         shipp->level2_tag_left = -1.0f;
4169
4170                         mprintf(("Killing level 2 TAG for %s\n", shipp->ship_name));
4171                 }
4172         }
4173         
4174         if ( shipp->flags & SF_ARRIVING && Ai_info[shipp->ai_index].mode != AIM_BAY_EMERGE )    {
4175                 // JAS -- if the ship is warping in, just move it forward at a speed
4176                 // fast enough to move 2x it's radius in SHIP_WARP_TIME seconds.
4177                 shipfx_warpin_frame( obj, frametime );
4178         } else if ( shipp->flags & SF_DEPART_WARP ) {
4179                 // JAS -- if the ship is warping out, just move it forward at a speed
4180                 // fast enough to move 2x it's radius in SHIP_WARP_TIME seconds.
4181                 shipfx_warpout_frame( obj, frametime );
4182         } else {
4183                 //      Do AI.
4184
4185                 // for multiplayer people.  return here if in multiplay and not the host
4186                 if ( (Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) )
4187                         return; 
4188
4189                 // MWA -- moved the code to maybe fire swarm missiles to after the check for
4190                 // multiplayer master.  Only single player and multi server needs to do this code
4191                 // this code might call ship_fire_secondary which will send the fire packets
4192                 swarm_maybe_fire_missile(num);
4193
4194                 // maybe fire turret swarm missiles
4195                 void turret_swarm_maybe_fire_missile(int num);
4196                 turret_swarm_maybe_fire_missile(num);
4197
4198                 // maybe fire a corkscrew missile (just like swarmers)
4199                 cscrew_maybe_fire_missile(num);
4200
4201                 // AL 2-19-98: Fire turret for player if it exists
4202                 if ( obj->flags & OF_PLAYER_SHIP ) {
4203                         player_maybe_fire_turret(obj);
4204                 }
4205
4206                 // if single player, check player object is not too far from starting location
4207                 // DKA 5/17/99 check SINGLE and MULTI
4208 //              if ( !(Game_mode & GM_MULTIPLAYER) && (obj == Player_obj) )
4209                 if (obj == Player_obj) {
4210                         ship_check_player_distance();
4211                 }
4212
4213                 // update ship lethality
4214                 if ( Ships[num].ai_index >= 0 ){
4215                         if (!physics_paused && !ai_paused){
4216                                 lethality_decay(&Ai_info[Ships[num].ai_index]);
4217                         }
4218                 }
4219
4220                 // if the ship is a player ship or an observer ship don't need to do AI
4221                 if ( (obj->flags & OF_PLAYER_SHIP) || (obj->type == OBJ_OBSERVER) ) {
4222                         return;
4223                 }
4224
4225                 if ( Ships[num].ai_index >= 0 ){
4226                         if (!physics_paused && !ai_paused){
4227                                 ai_process( obj, Ships[num].ai_index, frametime );
4228                         }
4229                 }
4230         }                       
4231 }
4232
4233
4234 // ------------------------------------------------------------------------
4235 //      ship_set_default_weapons()
4236 //
4237 //      Set the ship level weapons based on the information contained in the ship
4238 // info.  Weapon assignments are checked against the model to ensure the models
4239 // and the ship info weapon data are in synch.
4240 //
4241 //
4242
4243 void ship_set_default_weapons(ship *shipp, ship_info *sip)
4244 {
4245         int                     i;
4246         polymodel       *po;
4247         ship_weapon *swp = &shipp->weapons;
4248
4249         //      Copy primary and secondary weapons from ship_info to ship.
4250         //      Later, this will happen in the weapon loadout screen.
4251         for (i=0; i < MAX_PRIMARY_BANKS; i++){
4252                 swp->primary_bank_weapons[i] = sip->primary_bank_weapons[i];
4253         }
4254
4255         for (i=0; i < MAX_SECONDARY_BANKS; i++){
4256                 swp->secondary_bank_weapons[i] = sip->secondary_bank_weapons[i];
4257         }
4258
4259         // Copy the number of primary and secondary banks to ship, and verify that
4260         // model is in synch
4261         po = model_get( sip->modelnum );
4262
4263         // Primary banks
4264         if ( po->n_guns > sip->num_primary_banks ) {
4265                 Assert(po->n_guns <= MAX_PRIMARY_BANKS);
4266                 Warning(LOCATION, "There are %d primary banks in the model file,\nbut only %d primary banks in ships.tbl for %s\n", po->n_guns, sip->num_primary_banks, sip->name);
4267                 for ( i = sip->num_primary_banks; i < po->n_guns; i++ ) {
4268                         // Make unspecified weapon for bank be a Light Laser
4269                         swp->primary_bank_weapons[i] = weapon_info_lookup(NOX("Light Laser"));
4270                         Assert(swp->primary_bank_weapons[i] >= 0);
4271                 }
4272                 sip->num_primary_banks = po->n_guns;
4273         }
4274         else if ( po->n_guns < sip->num_primary_banks ) {
4275                 Warning(LOCATION, "There are %d primary banks in ships.tbl for %s\nbut only %d primary banks in the model\n", sip->num_primary_banks, sip->name, po->n_guns);
4276                 sip->num_primary_banks = po->n_guns;
4277         }
4278
4279         // Secondary banks
4280         if ( po->n_missiles > sip->num_secondary_banks ) {
4281                 Assert(po->n_missiles <= MAX_SECONDARY_BANKS);
4282                 Warning(LOCATION, "There are %d secondary banks in model,\nbut only %d secondary banks in ships.tbl for %s\n", po->n_missiles, sip->num_secondary_banks, sip->name);
4283                 for ( i = sip->num_secondary_banks; i < po->n_missiles; i++ ) {
4284                         // Make unspecified weapon for bank be a Rockeye Missile
4285                         swp->secondary_bank_weapons[i] = weapon_info_lookup(NOX("Rockeye Missile"));
4286                         Assert(swp->secondary_bank_weapons[i] >= 0);
4287                 }
4288                 sip->num_secondary_banks = po->n_missiles;
4289         }
4290         else if ( po->n_missiles < sip->num_secondary_banks ) {
4291                 Warning(LOCATION, "There are %d secondary banks in ships.tbl for %s,\n but only %d secondary banks in the model.\n", sip->num_secondary_banks, sip->name, po->n_missiles);
4292                 sip->num_secondary_banks = po->n_missiles;
4293         }
4294
4295         swp->num_primary_banks = sip->num_primary_banks;
4296         swp->num_secondary_banks = sip->num_secondary_banks;
4297         for ( i = 0; i < swp->num_secondary_banks; i++ ) {
4298                 if (Fred_running){
4299                         swp->secondary_bank_ammo[i] = 100;
4300                 } else {
4301                         swp->secondary_bank_ammo[i] = sip->secondary_bank_ammo_capacity[i];
4302                 }
4303
4304                 swp->secondary_bank_capacity[i] = sip->secondary_bank_ammo_capacity[i];
4305         }
4306
4307         for ( i = 0; i < MAX_PRIMARY_BANKS; i++ ){
4308                 swp->next_primary_fire_stamp[i] = timestamp(0);
4309         }
4310
4311         for ( i = 0; i < MAX_SECONDARY_BANKS; i++ ){
4312                 swp->next_secondary_fire_stamp[i] = timestamp(0);
4313         }
4314 }
4315
4316
4317 //      A faster version of ship_check_collision that does not do checking at the polygon
4318 //      level.  Just checks to see if a vector will intersect a sphere.
4319 int ship_check_collision_fast( object * obj, object * other_obj, vector * hitpos)
4320 {
4321         int num;
4322         mc_info mc;
4323
4324         Assert( obj->type == OBJ_SHIP );
4325         Assert( obj->instance >= 0 );
4326
4327         num = obj->instance;
4328
4329         ship_model_start(obj);  // are these needed in this fast case? probably not.
4330
4331         mc.model_num = Ships[num].modelnum;     // Fill in the model to check
4332         mc.orient = &obj->orient;                                       // The object's orient
4333         mc.pos = &obj->pos;                                                     // The object's position
4334         mc.p0 = &other_obj->last_pos;                   // Point 1 of ray to check
4335         mc.p1 = &other_obj->pos;                                        // Point 2 of ray to check
4336         mc.flags = MC_ONLY_SPHERE;                              // flags
4337
4338         model_collide(&mc);
4339         if (mc.num_hits)
4340                 *hitpos = mc.hit_point_world;
4341         
4342         ship_model_stop(obj);   // are these needed in this fast case? probably not.
4343
4344         return mc.num_hits;
4345 }
4346
4347 // ensure that the subsys path is at least SUBSYS_PATH_DIST from the 
4348 // second last to last point.
4349 void ship_maybe_fixup_subsys_path(polymodel *pm, int path_num)
4350 {
4351         vector  *v1, *v2, dir;
4352         float           dist;
4353         int             index_1, index_2;
4354
4355         model_path *mp;
4356         mp = &pm->paths[path_num];
4357
4358         Assert(mp != NULL);
4359         Assert(mp->nverts > 1);
4360         
4361         index_1 = 1;
4362         index_2 = 0;
4363
4364         v1 = &mp->verts[index_1].pos;
4365         v2 = &mp->verts[index_2].pos;
4366         
4367         dist = vm_vec_dist(v1, v2);
4368         if ( dist < SUBSYS_PATH_DIST-10 ) {
4369                 vm_vec_normalized_dir(&dir, v2, v1);
4370                 vm_vec_scale_add(v2, v1, &dir, SUBSYS_PATH_DIST);
4371         }
4372 }
4373
4374 // fill in the path_num field inside the model_subsystem struct.  This is an index into
4375 // the pm->paths[] array, which is a path that provides a frontal approach to a subsystem
4376 // (used for attacking purposes)
4377 //
4378 // NOTE: path_num in model_subsystem has the follows the following convention:
4379 //                      > 0     => index into pm->paths[] for model that subsystem sits on
4380 //                      -1              => path is not yet determined (may or may not exist)
4381 //                      -2              => path doesn't yet exist for this subsystem
4382 void ship_set_subsys_path_nums(ship_info *sip, polymodel *pm)
4383 {
4384         int i,j,found_path;
4385
4386         for ( i = 0; i < sip->n_subsystems; i++ ) {
4387                 sip->subsystems[i].path_num = -1;
4388         }
4389
4390         for ( i = 0; i < sip->n_subsystems; i++ ) {
4391                 found_path = 0;
4392                 for ( j = 0; j < pm->n_paths; j++ ) {
4393                         if ( (sip->subsystems[i].subobj_num != -1) && (sip->subsystems[i].subobj_num == pm->paths[j].parent_submodel) ) {
4394                                 found_path = 1;
4395                         } else if ( !stricmp(sip->subsystems[i].subobj_name, pm->paths[j].parent_name) ) {
4396                                 found_path = 1;
4397                         }
4398         
4399                         if ( found_path ) {
4400                                 if ( pm->n_paths > j ) {
4401                                         sip->subsystems[i].path_num = j;
4402                                         ship_maybe_fixup_subsys_path(pm, j);
4403                                         break;
4404                                 }
4405                         }
4406                 }
4407
4408                 // If a path num wasn't located, then set value to -2
4409                 if ( sip->subsystems[i].path_num == -1 )
4410                         sip->subsystems[i].path_num = -2;
4411         }
4412 }
4413
4414 // Determine the path indices (indicies into pm->paths[]) for the paths used for approaching/departing
4415 // a fighter bay on a capital ship.
4416 void ship_set_bay_path_nums(ship_info *sip, polymodel *pm)
4417 {
4418         int     bay_num, i;
4419         char    bay_num_str[3];
4420
4421         if ( pm->ship_bay != NULL ) {
4422                 free(pm->ship_bay);
4423                 pm->ship_bay = NULL;
4424         }
4425
4426         // currently only capital ships have fighter bays
4427         if ( !(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
4428                 return;
4429         }
4430
4431         // malloc out storage for the path information
4432         pm->ship_bay = (ship_bay*)malloc(sizeof(ship_bay));
4433         Assert(pm->ship_bay != NULL);
4434
4435         pm->ship_bay->num_paths = 0;
4436         // TODO: determine if zeroing out here is affecting any earlier initializations
4437         pm->ship_bay->arrive_flags = 0; // bitfield, set to 1 when that path number is reserved for an arrival
4438         pm->ship_bay->depart_flags = 0; // bitfield, set to 1 when that path number is reserved for a departure
4439
4440
4441         // iterate through the paths that exist in the polymodel, searching for $bayN pathnames
4442         for ( i = 0; i < pm->n_paths; i++ ) {
4443                 if ( !strnicmp(pm->paths[i].name, NOX("$bay"), 4) ) {
4444                         strncpy(bay_num_str, pm->paths[i].name+4, 2);
4445                         bay_num_str[2] = 0;
4446                         bay_num = atoi(bay_num_str);
4447                         Assert(bay_num >= 1 && bay_num <= MAX_SHIP_BAY_PATHS);
4448                         pm->ship_bay->paths[bay_num-1] = i;
4449                         pm->ship_bay->num_paths++;
4450                 }
4451         }
4452 }
4453
4454 // Ensure create time for ship is unqiue
4455 void ship_make_create_time_unique(ship *shipp)
4456 {
4457         int             sanity_counter = 0, collision;
4458         ship            *compare_shipp;
4459         ship_obj        *so;
4460         uint            new_create_time;
4461
4462         new_create_time = shipp->create_time;
4463
4464         while (1) {
4465
4466                 if ( sanity_counter++ > 50 ) {
4467                         Int3();
4468                         break;
4469                 }
4470
4471                 collision = 0;
4472
4473                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4474                         compare_shipp = &Ships[Objects[so->objnum].instance];
4475
4476                         if ( compare_shipp == shipp ) {
4477                                 continue;
4478                         }
4479
4480                         if ( compare_shipp->create_time == new_create_time ) {
4481                                 new_create_time++;
4482                                 collision = 1;
4483                                 break;
4484                         }
4485                 }
4486
4487                 if ( !collision ) {
4488                         shipp->create_time = new_create_time;
4489                         break;
4490                 }
4491         }
4492 }
4493
4494 int     Ship_subsys_hwm = 0;
4495
4496 void show_ship_subsys_count()
4497 {
4498         object  *objp;
4499         int             count = 0;      
4500
4501         for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
4502                 if (objp->type == OBJ_SHIP) {
4503                         count += Ship_info[Ships[objp->type].ship_info_index].n_subsystems;
4504                 }
4505         }
4506
4507         //nprintf(("AI", "Num subsystems, high water mark = %i, %i\n", count, Ship_subsys_hwm));
4508
4509         if (count > Ship_subsys_hwm) {
4510                 Ship_subsys_hwm = count;
4511         }
4512 }
4513
4514 //      Returns object index of ship.
4515 //      -1 means failed.
4516 int ship_create(matrix *orient, vector *pos, int ship_type)
4517 {
4518         int                     i, n, objnum, j, k, t;
4519         ship_info       *sip;
4520         ship                    *shipp;
4521
4522         t = ship_get_num_ships();
4523         
4524         // The following check caps the number of ships that can be created.  Because Fred needs
4525         // to create all the ships, regardless of when they arrive/depart, it needs a higher
4526         // limit than FreeSpace.  On release, however, we will reduce it, thus FreeSpace needs
4527         // to check against what this limit will be, otherwise testing the missions before
4528         // release could work fine, yet not work anymore once a release build is made.
4529         if (Fred_running) {
4530                 if (t >= MAX_SHIPS)
4531                         return -1;
4532
4533         } else {
4534                 if (t >= SHIPS_LIMIT) {
4535                         Error(LOCATION, XSTR("There is a limit of %d ships in the mission at once.  Please be sure that you do not have more than %d ships present in the mission at the same time.", 1495), SHIPS_LIMIT, SHIPS_LIMIT );
4536                         return -1;
4537                 }
4538         }
4539
4540         //nprintf(("AI", "Number of ships = %i\n", t));
4541
4542         for (n=0; n<MAX_SHIPS; n++){
4543                 if (Ships[n].objnum == -1){
4544                         break;
4545                 }
4546         }
4547
4548         if (n == MAX_SHIPS){
4549                 return -1;
4550         }
4551
4552         Assert((ship_type >= 0) && (ship_type < Num_ship_types));
4553         sip = &(Ship_info[ship_type]);
4554         shipp = &Ships[n];
4555
4556         //  check to be sure that this ship falls into a ship size category!!!
4557         //  get Allender or Mike if you hit this Assert
4558         Assert( sip->flags & (SIF_SMALL_SHIP | SIF_BIG_SHIP | SIF_CAPITAL | SIF_NO_SHIP_TYPE | SIF_NOT_FLYABLE | SIF_ESCAPEPOD | SIF_SUPERCAP | SIF_DRYDOCK | SIF_KNOSSOS_DEVICE) );
4559
4560         sip->modelnum = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]);              // use the highest detail level
4561         shipp->modelnum = sip->modelnum;
4562
4563         // maybe load an optional hud target model
4564         if(strlen(sip->pof_file_hud)){
4565                 // check to see if a "real" ship uses this model. if so, load it up for him so that subsystems are setup properly
4566                 int idx;
4567                 for(idx=0; idx<Num_ship_types; idx++){
4568                         if(!stricmp(Ship_info[idx].pof_file, sip->pof_file_hud)){
4569                                 Ship_info[idx].modelnum = model_load(Ship_info[idx].pof_file, Ship_info[idx].n_subsystems, &Ship_info[idx].subsystems[0]);
4570                         }
4571                 }
4572
4573                 // mow load it for me with no subsystems
4574                 sip->modelnum_hud = model_load(sip->pof_file_hud, 0, NULL);
4575         }
4576
4577         polymodel * pm;
4578         pm = model_get(shipp->modelnum);
4579
4580         ship_copy_subsystem_fixup(sip);
4581
4582         show_ship_subsys_count();
4583
4584         if ( sip->num_detail_levels < pm->n_detail_levels )     {
4585                 Warning(LOCATION, "For ship '%s', detail level\nmismatch (POF needs %d)", sip->name, pm->n_detail_levels );
4586
4587                 for (i=0; i<pm->n_detail_levels; i++ )  {
4588                         sip->detail_distance[i] = 0;
4589                 }
4590         }
4591
4592         for (i=0; i<sip->num_detail_levels; i++ )       {
4593                 pm->detail_depth[i] = i2fl(sip->detail_distance[i]);
4594         }
4595
4596         if ( sip->flags & SIF_NAVBUOY ) {
4597                 // JAS: Nav buoys don't need to do collisions!
4598                 objnum = obj_create(OBJ_SHIP, -1, n, orient, pos, model_get_radius(shipp->modelnum), OF_RENDERS | OF_PHYSICS );
4599         } else {
4600                 objnum = obj_create(OBJ_SHIP, -1, n, orient, pos, model_get_radius(shipp->modelnum), OF_RENDERS | OF_COLLIDES | OF_PHYSICS );
4601         }
4602         Assert( objnum >= 0 );
4603
4604         shipp->ai_index = ai_get_slot(n);
4605         Assert( shipp->ai_index >= 0 );
4606
4607         sprintf(shipp->ship_name, NOX("%s %d"), Ship_info[ship_type].name, n);
4608         ship_set_default_weapons(shipp, sip);   //      Moved up here because ship_set requires that weapon info be valid.  MK, 4/28/98
4609         ship_set(n, objnum, ship_type);
4610
4611         // fill in the path_num field inside the model_subsystem struct.  This is an index into
4612         // the pm->paths[] array, which is a path that provides a frontal approach to a subsystem
4613         // (used for attacking purposes)
4614         //
4615         // NOTE: path_num in model_subsystem has the follows the following convention:
4616         //                      > 0     => index into pm->paths[] for model that subsystem sits on
4617         //                      -1              => path is not yet determined (may or may not exist)
4618         //                      -2              => path doesn't yet exist for this subsystem
4619         ship_set_subsys_path_nums(sip, pm);
4620
4621         // set the path indicies for fighter bays on the ship (currently, only capital ships have fighter bays)
4622         ship_set_bay_path_nums(sip, pm);        
4623
4624         init_ai_object(objnum);
4625         ai_clear_ship_goals( &Ai_info[Ships[n].ai_index] );             // only do this one here.  Can't do it in init_ai because it might wipe out goals in mission file
4626
4627         //ship_set_default_weapons(shipp, sip);
4628
4629         //      Allocate shield and initialize it.
4630         if (pm->shield.ntris) {
4631                 shipp->shield_integrity = (float *)malloc(sizeof(float)*pm->shield.ntris);
4632                 for (i=0; i<pm->shield.ntris; i++)
4633                         shipp->shield_integrity[i] = 1.0f;
4634
4635         } else
4636                 shipp->shield_integrity = NULL;
4637
4638         // fix up references into paths for this ship's model to point to a ship_subsys entry instead
4639         // of a submodel index.  The ship_subsys entry should be the same for *all* instances of the
4640         // same ship.
4641
4642         if ( !(sip->flags & SIF_PATH_FIXUP )) {
4643                 for ( i = 0; i < pm->n_paths; i++ ) {
4644                         for ( j = 0; j < pm->paths[i].nverts; j++ ) {
4645                                 for ( k = 0; k < pm->paths[i].verts[j].nturrets; k++ ) {
4646                                         int ptindex = pm->paths[i].verts[j].turret_ids[k];              // this index is a submodel number (ala bspgen)
4647                                         int index;
4648                                         ship_subsys *ss;
4649
4650                                         // iterate through the ship_subsystems looking for an id that matches
4651                                         index = 0;
4652                                         ss = GET_FIRST(&Ships[n].subsys_list);
4653                                         while ( ss != END_OF_LIST( &Ships[n].subsys_list ) ) {
4654                                                 if ( ss->system_info->subobj_num == ptindex ) {                 // when these are equal, fix up the ref
4655                                                         pm->paths[i].verts[j].turret_ids[k] = index;                            // in path structure to index a ship_subsys
4656                                                         break;                                                                                  
4657                                                 }
4658                                                 index++;
4659                                                 ss = GET_NEXT( ss );
4660                                         }
4661
4662                                         if ( ss == END_OF_LIST(&Ships[n].subsys_list) )
4663                                                 Warning(LOCATION, "Couldn't fix up turret indices in spline path\n\nModel: %s\nPath: %s\nVertex: %d\nTurret model id:%d\n\nThis probably means the turret was not specified in ships.tbl", sip->pof_file, pm->paths[i].name, j, ptindex );
4664                                 }
4665                         }
4666                 }
4667                 sip->flags |= SIF_PATH_FIXUP;
4668         }
4669
4670         // reset the damage record fields (for scoring purposes)
4671         shipp->total_damage_received = 0.0f;
4672    for(i=0;i<MAX_DAMAGE_SLOTS;i++){
4673                 shipp->damage_ship[i] = 0.0f;
4674                 shipp->damage_ship_id[i] = -1;
4675         }
4676
4677         // Add this ship to Ship_obj_list
4678         shipp->ship_list_index = ship_obj_list_add(objnum);
4679
4680         // Set time when ship is created
4681         shipp->create_time = timer_get_milliseconds();
4682
4683         ship_make_create_time_unique(shipp);
4684
4685         // set the team select index to be -1
4686         shipp->ts_index = -1;
4687
4688         shipp->wing_status_wing_index = -1;             // wing index (0-4) in wingman status gauge
4689         shipp->wing_status_wing_pos = -1;               // wing position (0-5) in wingman status gauge
4690
4691         // call the contrail system
4692         ct_ship_create(shipp);
4693
4694         return objnum;
4695 }
4696
4697 // ----------------------------------------------------------------
4698 // ship_model_change()
4699 //
4700 // Change the ship model for a ship to that for ship class 'ship_type'
4701 //
4702 // input:       n                               =>              index of ship in Ships[] array
4703 //                              ship_type       =>              ship class (index into Ship_info[])
4704 //
4705 void ship_model_change(int n, int ship_type)
4706 {
4707         int                     model_num, i;
4708         ship_info       *sip;
4709         ship                    *sp;
4710
4711
4712         Assert( n >= 0 && n < MAX_SHIPS );
4713         sp = &Ships[n];
4714         sip = &(Ship_info[ship_type]);
4715
4716         model_num = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]);          // use the highest detail level
4717
4718         // page in nondims
4719         if(!Fred_running){
4720                 model_page_in_textures(model_num, ship_type);
4721         }
4722
4723         Objects[sp->objnum].radius = model_get_radius(model_num);
4724         sip->modelnum = model_num;
4725         sp->modelnum = model_num;
4726
4727         polymodel * pm;
4728         pm = model_get(sp->modelnum);
4729
4730         ship_copy_subsystem_fixup(sip);
4731
4732         if ( sip->num_detail_levels < pm->n_detail_levels )     {
4733                 Warning(LOCATION, "For ship '%s', detail level\nmismatch (POF needs %d)", sip->name, pm->n_detail_levels );
4734
4735                 for (i=0; i<pm->n_detail_levels; i++ )  {
4736                         sip->detail_distance[i] = 0;
4737                 }
4738         }
4739
4740         for (i=0; i<sip->num_detail_levels; i++ )       {
4741                 pm->detail_depth[i] = i2fl(sip->detail_distance[i]);
4742         }
4743 }
4744
4745 // ----------------------------------------------------------------
4746 // change_ship_type()
4747 //
4748 // Change the ship class on a ship, and changing all required information
4749 // for consistency (ie textures, subsystems, weapons, physics)
4750 //
4751 // input:       n                               =>              index of ship in Ships[] array
4752 //                              ship_type       =>              ship class (index into Ship_info[])
4753 //
4754 void change_ship_type(int n, int ship_type)
4755 {
4756         ship_info       *sip;
4757         ship                    *sp;
4758         object          *objp;
4759
4760
4761         Assert( n >= 0 && n < MAX_SHIPS );
4762         sp = &Ships[n];
4763         sip = &(Ship_info[ship_type]);
4764         objp = &Objects[sp->objnum];
4765
4766         // point to new ship data
4767         sp->ship_info_index = ship_type;
4768
4769         ship_model_change(n, ship_type);
4770
4771         // if the subsystem list is not currently empty, then we need to clear it out first.
4772         if ( NOT_EMPTY(&sp->subsys_list) ) {
4773                 ship_subsys *ship_system, *tmp;
4774
4775                 for ( ship_system = GET_FIRST(&sp->subsys_list); ship_system != END_OF_LIST(&sp->subsys_list);  ) {
4776                         tmp = GET_NEXT(ship_system);
4777                         list_remove( &sp->subsys_list, ship_system );
4778                         list_append( &ship_subsys_free_list, ship_system );
4779                         ship_system = tmp;
4780                 }
4781         }
4782         // fix up the subsystems
4783         subsys_set( sp->objnum );
4784
4785         // set the correct hull strength
4786         if (Fred_running) {
4787                 objp->hull_strength = 100.0f;
4788         } else {
4789                 objp->hull_strength = sip->initial_hull_strength;
4790         }
4791
4792         // set the correct shields strength
4793         if (Fred_running) {
4794                 objp->shields[0] = 100.0f;
4795         } else {
4796                 set_shield_strength(objp, sip->shields);
4797         }
4798
4799         sp->afterburner_fuel = sip->afterburner_fuel_capacity;
4800
4801         ship_set_default_weapons(sp, sip);
4802         physics_ship_init(&Objects[sp->objnum]);
4803         ets_init_ship(&Objects[sp->objnum]);
4804         // mwa removed the next line in favor of simply setting the ai_class in AI_info.  ai_object_init
4805         // was trashing mode in ai_info when it was valid due to goals.
4806         //ai_object_init(&Objects[sp->objnum], sp->ai_index);
4807         Ai_info[sp->ai_index].ai_class = sip->ai_class;
4808 }
4809
4810 #ifndef NDEBUG
4811 //      Fire the debug laser
4812 int ship_fire_primary_debug(object *objp)
4813 {
4814         int     i;
4815         ship    *shipp = &Ships[objp->instance];
4816         vector wpos;
4817
4818         if ( !timestamp_elapsed(shipp->weapons.next_primary_fire_stamp[0]) )
4819                 return 0;
4820
4821         // do timestamp stuff for next firing time
4822         shipp->weapons.next_primary_fire_stamp[0] = timestamp(250);
4823
4824         //      Debug code!  Make the single laser fire only one bolt and from the object center!
4825         for (i=0; i<MAX_WEAPONS; i++)
4826                 if (!stricmp(Weapon_info[i].name, NOX("Debug Laser")))
4827                         break;
4828         
4829         vm_vec_add(&wpos, &objp->pos, &(objp->orient.v.fvec) );
4830         if (i != MAX_WEAPONS) {
4831                 int weapon_objnum;
4832                 weapon_objnum = weapon_create( &wpos, &objp->orient, i, OBJ_INDEX(objp), 0 );
4833                 weapon_set_tracking_info(weapon_objnum, OBJ_INDEX(objp), Ai_info[shipp->ai_index].target_objnum);
4834                 return 1;
4835         } else
4836                 return 0;
4837 }
4838 #endif
4839
4840 //      Launch countermeasures from object *objp.  rand_val is used in multiplayer to ensure that all
4841 // clients in the game fire countermeasure the same way
4842 int ship_launch_countermeasure(object *objp, int rand_val)
4843 {
4844         int     fired, check_count, cmeasure_count;
4845         vector  pos;
4846         ship    *shipp;
4847
4848         shipp = &Ships[objp->instance];
4849
4850         // in the case where the server is an observer, he can launch countermeasures unless we do this.
4851         if( objp->type == OBJ_OBSERVER){
4852                 return 0;
4853         }
4854
4855         if ( !timestamp_elapsed(shipp->cmeasure_fire_stamp) ){
4856                 return 0;
4857         }
4858
4859         shipp->cmeasure_fire_stamp = timestamp(CMEASURE_WAIT);  //      Can launch every half second.
4860 #ifndef NDEBUG
4861         if (Weapon_energy_cheat) {
4862                 shipp->cmeasure_count++;
4863         }
4864 #endif
4865
4866         // we might check the count of countermeasures left depending on game state.  Multiplayer clients
4867         // do not need to check any objects other than themselves for the count
4868         fired = -1;
4869         check_count = 1;
4870         if ( MULTIPLAYER_CLIENT && (objp != Player_obj) ){
4871                 check_count = 0;
4872         }
4873
4874         if (check_count && (shipp->cmeasure_count <= 0) ) {
4875                 if ( objp == Player_obj ) {
4876                         HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "No more countermeasure charges.", 485));
4877                         snd_play( &Snds[SND_OUT_OF_MISSLES], 0.0f );
4878                 }
4879
4880                 // if we have a player ship, then send the fired packet anyway so that the player
4881                 // who fired will get his 'out of countermeasures' sound
4882                 cmeasure_count = 0;
4883                 if ( objp->flags & OF_PLAYER_SHIP ){
4884                         goto send_countermeasure_fired;
4885                 }
4886
4887                 return 0;
4888         }
4889
4890         cmeasure_count = shipp->cmeasure_count;
4891         shipp->cmeasure_count--;
4892
4893         vm_vec_scale_add(&pos, &objp->pos, &objp->orient.v.fvec, -objp->radius/2.0f);
4894
4895         // cmeasure_create fires 1 countermeasure.  returns -1 if not fired, otherwise a non-negative
4896         // value
4897         fired = cmeasure_create( objp, &pos, shipp->current_cmeasure, rand_val );
4898
4899         // Play sound effect for counter measure launch
4900         Assert(shipp->current_cmeasure < Num_cmeasure_types);
4901         if ( Cmeasure_info[shipp->current_cmeasure].launch_sound != -1 ) {
4902                 snd_play_3d( &Snds[Cmeasure_info[shipp->current_cmeasure].launch_sound], &pos, &View_position );
4903         }
4904
4905         
4906 send_countermeasure_fired:
4907         // the new way of doing things
4908         // if(Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING){
4909         if(Game_mode & GM_MULTIPLAYER){
4910                 send_NEW_countermeasure_fired_packet( objp, cmeasure_count, fired );
4911         }
4912         // }
4913         // the old way of doing things
4914         //else {
4915          //     if ( MULTIPLAYER_MASTER ){
4916                 //      send_countermeasure_fired_packet( objp, cmeasure_count, fired );
4917                 //}
4918         //}
4919
4920         return (fired>0);               // return 0 if not fired, 1 otherwise
4921 }
4922
4923 // internal function.. see if enough time has elapsed to play fail sound again
4924 int ship_maybe_play_primary_fail_sound()
4925 {
4926         hud_start_flash_weapon(Player_ship->weapons.current_primary_bank);
4927
4928         if ( timestamp_elapsed(Laser_energy_out_snd_timer) ) {
4929                 Laser_energy_out_snd_timer = timestamp(50);
4930                 snd_play( &Snds[SND_OUT_OF_WEAPON_ENERGY]);
4931                 return 1;
4932         }
4933         return 0;
4934 }
4935
4936 // internal function.. see if enough time has elapsed to play fail sound again
4937 int ship_maybe_play_secondary_fail_sound(weapon_info *wip)
4938 {
4939         hud_start_flash_weapon(Player_ship->weapons.num_primary_banks + Player_ship->weapons.current_secondary_bank);
4940
4941         if ( timestamp_elapsed(Missile_out_snd_timer) ) {
4942                 
4943                 if ( wip->wi_flags & WIF_SWARM ) {
4944                         Missile_out_snd_timer = timestamp(500);
4945                 } else {
4946                         Missile_out_snd_timer = timestamp(50);
4947                 }
4948                 snd_play( &Snds[SND_OUT_OF_MISSLES] );
4949                 return 1;
4950         }
4951         return 0;
4952 }
4953
4954 // internal function.. see if weapon for ship can fire based on weapons subystem
4955 // strength.
4956 //
4957 // returns:             1       =>              weapon failed to fire
4958 //                                      0       =>              weapon can fire
4959 int ship_weapon_maybe_fail(ship *sp)
4960 {
4961         int     rval;
4962         float   weapons_subsys_str;
4963
4964         // If playing on lowest skill level, weapons will not fail due to subsystem damage
4965         if ( Game_skill_level == 0 ){
4966                 return 0;
4967         }
4968
4969         rval = 0;
4970         weapons_subsys_str = ship_get_subsystem_strength( sp, SUBSYSTEM_WEAPONS );
4971         if ( weapons_subsys_str < SUBSYS_WEAPONS_STR_FIRE_FAIL ) {
4972                 rval = 1;
4973         }
4974         else if ( weapons_subsys_str < SUBSYS_WEAPONS_STR_FIRE_OK ) {
4975                 // chance to fire depends on weapons subsystem strength
4976                 if ( (frand()-0.2f) > weapons_subsys_str )              
4977                         rval = 1;
4978         }
4979
4980         if (!rval) {
4981                 // is subsystem disrupted?
4982                 if ( ship_subsys_disrupted(sp, SUBSYSTEM_WEAPONS) ) {
4983                         rval=1;
4984                 }
4985         }
4986                 
4987         return rval;
4988 }
4989
4990 // create a moving tracer based upon a weapon which just fired
4991 float t_rad = 0.5f;
4992 float t_len = 10.0f;
4993 float t_vel = 0.2f;
4994 float t_min = 150.0f;
4995 float t_max = 300.0f;
4996 DCF(t_rad, "")
4997 {
4998         dc_get_arg(ARG_FLOAT);
4999         t_rad = Dc_arg_float;
5000 }
5001 DCF(t_len, "")
5002 {
5003         dc_get_arg(ARG_FLOAT);
5004         t_len = Dc_arg_float;
5005 }
5006 DCF(t_vel, "")
5007 {
5008         dc_get_arg(ARG_FLOAT);
5009         t_vel = Dc_arg_float;
5010 }
5011 DCF(t_min, "")
5012 {
5013         dc_get_arg(ARG_FLOAT);
5014         t_min = Dc_arg_float;
5015 }
5016 DCF(t_max, "")
5017 {
5018         dc_get_arg(ARG_FLOAT);
5019         t_max = Dc_arg_float;
5020 }
5021 void ship_fire_tracer(int weapon_objnum)
5022 {
5023         particle_info pinfo;
5024         object *objp = &Objects[weapon_objnum];
5025         weapon_info *wip = &Weapon_info[Weapons[Objects[weapon_objnum].instance].weapon_info_index];
5026
5027         // setup particle info
5028         memset(&pinfo, 0, sizeof(particle_info));
5029         pinfo.pos = objp->pos;
5030         pinfo.vel = objp->phys_info.vel;
5031         vm_vec_scale(&pinfo.vel, t_vel);
5032         pinfo.lifetime = wip->lifetime;
5033         pinfo.rad = t_rad;
5034         pinfo.type = PARTICLE_BITMAP;
5035         pinfo.optional_data = wip->laser_bitmap;
5036         pinfo.tracer_length = t_len;
5037         pinfo.reverse = 0;
5038         pinfo.attached_objnum = -1;
5039         pinfo.attached_sig = 0;
5040
5041         // create the particle
5042         particle_create(&pinfo);
5043 }
5044
5045 //      Multiplicative delay factors for increasing skill levels.
5046 float Ship_fire_delay_scale_hostile[NUM_SKILL_LEVELS] =  {4.0f, 2.5f, 1.75f, 1.25f, 1.0f};
5047 float Ship_fire_delay_scale_friendly[NUM_SKILL_LEVELS] = {2.0f, 1.4f, 1.25f, 1.1f, 1.0f};
5048
5049 int tracers[MAX_SHIPS][4][4];   
5050
5051 // fires a primary weapon for the given object.  It also handles multiplayer cases.
5052 // in multiplayer, the starting network signature, and number of banks fired are sent
5053 // to all the clients in the game. All the info is passed to send_primary at the end of
5054 // the function.  The check_energy parameter (defaults to 1) tells us whether or not
5055 // we should check the energy.  It will be 0 when a multiplayer client is firing an AI
5056 // primary.
5057 int ship_fire_primary(object * obj, int stream_weapons, int force)
5058 {
5059         vector          gun_point, pnt, firing_pos;
5060         int                     n = obj->instance;
5061         ship                    *shipp;
5062         ship_weapon     *swp;
5063         ship_info       *sip;
5064         ai_info         *aip;
5065         int                     weapon, i, j, weapon_objnum;
5066         int                     bank_to_fire, num_fired = 0;    
5067         int                     banks_fired, have_timeout;                              // used for multiplayer to help determine whether or not to send packet
5068         have_timeout = 0;                       // used to help tell us whether or not we need to send a packet
5069         banks_fired = 0;                        // used in multiplayer -- bitfield of banks that were fired
5070
5071         int                     sound_played;   // used to track what sound is played.  If the player is firing two banks
5072                                                                                 // of the same laser, we only want to play one sound
5073         Assert( obj != NULL );
5074
5075         if(obj == NULL){
5076                 return 0;
5077         }
5078
5079         // in the case where the server is an observer, he can fire (which) would be bad - unless we do this.
5080         if( obj->type == OBJ_OBSERVER){
5081                 return 0;
5082         }
5083
5084         Assert( obj->type == OBJ_SHIP );
5085         Assert( n >= 0 );
5086         Assert( Ships[n].objnum == OBJ_INDEX(obj));
5087         if((obj->type != OBJ_SHIP) || (n < 0) || (n >= MAX_SHIPS) || (Ships[n].objnum != OBJ_INDEX(obj))){
5088                 return 0;
5089         }
5090         
5091         shipp = &Ships[n];
5092         swp = &shipp->weapons;
5093
5094         // bogus 
5095         if((shipp->ship_info_index < 0) || (shipp->ship_info_index >= Num_ship_types)){
5096                 return 0;
5097         }
5098         if((shipp->ai_index < 0) || (shipp->ai_index >= MAX_AI_INFO)){
5099                 return 0;
5100         }
5101         sip = &Ship_info[shipp->ship_info_index];
5102         aip = &Ai_info[shipp->ai_index];
5103
5104         if ( swp->num_primary_banks <= 0 ) {
5105                 return 0;
5106         }
5107
5108         if ( swp->current_primary_bank < 0 ){
5109                 return 0;
5110         }       
5111
5112         sound_played = -1;
5113
5114         // Fire the correct primary bank.  If primaries are linked (SF_PRIMARY_LINKED set), then fire 
5115         // both primary banks.
5116         int     num_primary_banks;
5117
5118         if ( shipp->flags & SF_PRIMARY_LINKED ) {
5119                 num_primary_banks = swp->num_primary_banks;
5120         } else {
5121                 num_primary_banks = min(1, swp->num_primary_banks);
5122         }
5123
5124         Assert(num_primary_banks > 0);
5125         if (num_primary_banks < 1){
5126                 return 0;
5127         }
5128
5129         // if we're firing stream weapons, but the trigger is not down, do nothing
5130         if(stream_weapons && !(shipp->flags & SF_TRIGGER_DOWN)){
5131                 return 0;
5132         }
5133
5134         for ( i = 0; i < num_primary_banks; i++ ) {             
5135                 bank_to_fire = (swp->current_primary_bank+i)%2; // Max supported banks is 2
5136                 
5137                 weapon = swp->primary_bank_weapons[bank_to_fire];
5138                 Assert( weapon >= 0 && weapon < MAX_WEAPONS );          
5139                 if ( (weapon < 0) || (weapon >= MAX_WEAPON_TYPES) ) {
5140                         Int3();         // why would a ship try to fire a weapon that doesn't exist?
5141                         continue;
5142                 }               
5143                 weapon_info* winfo_p = &Weapon_info[weapon];
5144
5145                 // if this is a targeting laser, start it up
5146                 if((winfo_p->wi_flags & WIF_BEAM) && (winfo_p->b_info.beam_type == BEAM_TYPE_C)){
5147                         ship_start_targeting_laser(shipp);
5148                         continue;
5149                 }
5150
5151                 // if we're firing stream weapons and this is a non stream weapon, skip it
5152                 if(stream_weapons && !(winfo_p->wi_flags & WIF_STREAM)){
5153                         continue;
5154                 }
5155                 // if we're firing non stream weapons and this is a stream weapon, skip it
5156                 if(!stream_weapons && (winfo_p->wi_flags & WIF_STREAM)){
5157                         continue;
5158                 }
5159
5160                 // only non-multiplayer clients (single, multi-host) need to do timestamp checking
5161                 if ( !timestamp_elapsed(swp->next_primary_fire_stamp[bank_to_fire]) ) {
5162                         if (timestamp_until(swp->next_primary_fire_stamp[bank_to_fire]) > 5000){
5163                                 swp->next_primary_fire_stamp[bank_to_fire] = timestamp(1000);
5164                         }
5165
5166                         have_timeout = 1;
5167                         continue;
5168                 }
5169
5170                 //nprintf(("AI", "Time = %7.3f, firing %s\n", f2fl(Missiontime), Weapon_info[weapon].name));
5171
5172                 // do timestamp stuff for next firing time
5173                 float next_fire_delay = (float) winfo_p->fire_wait * 1000.0f;
5174                 if (!(obj->flags & OF_PLAYER_SHIP)) {
5175                         if (shipp->team == Ships[Player_obj->instance].team){
5176                                 next_fire_delay *= Ship_fire_delay_scale_friendly[Game_skill_level];
5177                         } else {
5178                                 next_fire_delay *= Ship_fire_delay_scale_hostile[Game_skill_level];
5179                         }
5180                 }
5181                 
5182                 next_fire_delay *= 1.0f + (num_primary_banks - 1) * 0.5f;               //      50% time penalty if banks linked
5183
5184                 //      MK, 2/4/98: Since you probably were allowed to fire earlier, but couldn't fire until your frame interval
5185                 //      rolled around, subtract out up to half the previous frametime.
5186                 //      Note, unless we track whether the fire button has been held down, and not tapped, it's hard to
5187                 //      know how much time to subtract off.  It could be this fire is "late" because the user didn't want to fire.
5188                 if (next_fire_delay > 0.0f) {
5189                         if (obj->flags & OF_PLAYER_SHIP) {
5190                                 int     t = timestamp_until(swp->next_primary_fire_stamp[bank_to_fire]);
5191                                 if (t < 0) {
5192                                         float   tx;
5193
5194                                         tx = (float) t/-1000.0f;
5195                                         if (tx > flFrametime/2.0f){
5196                                                 tx = 1000.0f * flFrametime * 0.7f;
5197                                         }
5198                                         next_fire_delay -= tx;
5199                                 }
5200                                 
5201                                 if ((int) next_fire_delay < 1){
5202                                         next_fire_delay = 1.0f;
5203                                 }
5204                         }
5205
5206                         swp->next_primary_fire_stamp[bank_to_fire] = timestamp((int)(next_fire_delay));
5207                 }
5208
5209                 // Here is where we check if weapons subsystem is capable of firing the weapon.
5210                 // Note that we can have partial bank firing, if the weapons subsystem is partially
5211                 // functional, which should be cool.            
5212                 if ( ship_weapon_maybe_fail(shipp) && !force) {
5213                         if ( obj == Player_obj ) {
5214                                 if ( ship_maybe_play_primary_fail_sound() ) {
5215                                 }
5216                         }
5217                         continue;
5218                 }               
5219
5220                 polymodel *po = model_get( Ship_info[shipp->ship_info_index].modelnum );
5221                 if ( po->n_guns > 0 ) {
5222                         int num_slots = po->gun_banks[bank_to_fire].num_slots;
5223
5224                         // fail unless we're forcing (energy based primaries)
5225                         if ( (shipp->weapon_energy < num_slots*winfo_p->energy_consumed) && !force) {
5226                                 if ( obj == Player_obj ) {
5227                                         swp->next_primary_fire_stamp[bank_to_fire] = timestamp(swp->next_primary_fire_stamp[bank_to_fire]);
5228                                         if ( ship_maybe_play_primary_fail_sound() ) {
5229                                         }
5230                                 }
5231                                 continue;
5232                         }                       
5233
5234                         // deplete the weapon reserve energy by the amount of energy used to fire the weapon
5235                         shipp->weapon_energy -= num_slots*winfo_p->energy_consumed;
5236                         if(shipp->weapon_energy < 0.0f){
5237                                 shipp->weapon_energy = 0.0f;
5238                         }                       
5239
5240                         // Mark all these weapons as in the same group
5241                         int new_group_id = weapon_create_group_id();
5242                         
5243                         for ( j = 0; j < num_slots; j++ ) {
5244                                 pnt = po->gun_banks[bank_to_fire].pnt[j];
5245                                 vm_vec_unrotate(&gun_point, &pnt, &obj->orient);
5246                                 vm_vec_add(&firing_pos, &gun_point, &obj->pos);
5247
5248                                 // create the weapon -- the network signature for multiplayer is created inside
5249                                 // of weapon_create
5250                                 weapon_objnum = weapon_create( &firing_pos, &obj->orient, weapon, OBJ_INDEX(obj),0, new_group_id );
5251                                 weapon_set_tracking_info(weapon_objnum, OBJ_INDEX(obj), aip->target_objnum, aip->current_target_is_locked, aip->targeted_subsys);                               
5252
5253                                 // create the muzzle flash effect
5254                                 shipfx_flash_create( obj, shipp, &pnt, &obj->orient.v.fvec, 1, weapon );
5255
5256                                 // maybe shudder the ship - if its me
5257                                 if((winfo_p->wi_flags & WIF_SHUDDER) && (obj == Player_obj) && !(Game_mode & GM_STANDALONE_SERVER)){
5258                                         // calculate some arbitrary value between 100
5259                                         // (mass * velocity) / 10
5260                                         game_shudder_apply(500, (winfo_p->mass * winfo_p->max_speed) / 10.0f);
5261                                 }
5262
5263                                 num_fired++;
5264                         }                                               
5265
5266                         banks_fired |= (1<<bank_to_fire);                               // mark this bank as fired.
5267                 }               
5268
5269                 // Only play the weapon fired sound if it hasn't been played yet.  This is to 
5270                 // avoid playing the same sound multiple times when banks are linked with the
5271                 // same weapon.
5272                 if ( sound_played != winfo_p->launch_snd ) {
5273                         sound_played = winfo_p->launch_snd;
5274                         if ( obj == Player_obj ) {
5275                                 if ( winfo_p->launch_snd != -1 ) {
5276                                         weapon_info *wip;
5277                                         ship_weapon *swp;
5278
5279                                         // HACK
5280                                         if(winfo_p->launch_snd == SND_AUTOCANNON_SHOT){
5281                                                 snd_play( &Snds[winfo_p->launch_snd], 0.0f, 1.0f, SND_PRIORITY_TRIPLE_INSTANCE );
5282                                         } else {
5283                                                 snd_play( &Snds[winfo_p->launch_snd], 0.0f, 1.0f, SND_PRIORITY_MUST_PLAY );
5284                                         }
5285         //                              snd_play( &Snds[winfo_p->launch_snd] );
5286
5287                                         swp = &Player_ship->weapons;
5288                                         if (swp->current_primary_bank >= 0) {
5289                                                 wip = &Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]];
5290                                                 joy_ff_play_primary_shoot((int) ((wip->armor_factor + wip->shield_factor * 0.2f) * (wip->damage * wip->damage - 7.5f) * 0.45f + 0.6f) * 10 + 2000);
5291                                         }
5292                                 }
5293                         }
5294                         else {
5295                                 if ( winfo_p->launch_snd != -1 ) {
5296                                         snd_play_3d( &Snds[winfo_p->launch_snd], &obj->pos, &View_position );
5297                                 }
5298                         }
5299                 }               
5300         }       // end for (go to next primary bank)
5301         
5302         // if multiplayer and we're client-side firing, send the packet
5303         // if((Game_mode & GM_MULTIPLAYER) && (Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING)){
5304         if(Game_mode & GM_MULTIPLAYER){
5305                 // if i'm a client, and this is not me, don't send
5306                 if(!(MULTIPLAYER_CLIENT && (shipp != Player_ship))){
5307                         send_NEW_primary_fired_packet( shipp, banks_fired );
5308                 }
5309         }
5310
5311         // post a primary fired event
5312         if(Game_mode & GM_DEMO_RECORD){
5313                 demo_POST_primary_fired(obj, swp->current_primary_bank, shipp->flags & SF_PRIMARY_LINKED);
5314         }
5315
5316    // STATS
5317    if (obj->flags & OF_PLAYER_SHIP) {
5318                 // in multiplayer -- only the server needs to keep track of the stats.  Call the cool
5319                 // function to find the player given the object *.  It had better return a valid player
5320                 // or our internal structure as messed up.
5321                 if( Game_mode & GM_MULTIPLAYER ) {
5322                         if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
5323                                 int player_num;
5324
5325                                 player_num = multi_find_player_by_object ( obj );
5326                                 Assert ( player_num != -1 );
5327
5328                                 Net_players[player_num].player->stats.mp_shots_fired += num_fired;
5329                         }
5330                 } else {
5331                         Player->stats.mp_shots_fired += num_fired;
5332                 }
5333         }
5334
5335         return num_fired;
5336 }
5337
5338 void ship_start_targeting_laser(ship *shipp)
5339 {       
5340         int bank0_laser = 0;
5341         int bank1_laser = 0;
5342
5343         // determine if either of our banks have a targeting laser
5344         if((shipp->weapons.primary_bank_weapons[0] >= 0) && (Weapon_info[shipp->weapons.primary_bank_weapons[0]].wi_flags & WIF_BEAM) && (Weapon_info[shipp->weapons.primary_bank_weapons[0]].b_info.beam_type == BEAM_TYPE_C)){
5345                 bank0_laser = 1;
5346         }
5347         if((shipp->weapons.primary_bank_weapons[1] >= 0) && (Weapon_info[shipp->weapons.primary_bank_weapons[1]].wi_flags & WIF_BEAM) && (Weapon_info[shipp->weapons.primary_bank_weapons[1]].b_info.beam_type == BEAM_TYPE_C)){
5348                 bank1_laser = 1;
5349         }
5350
5351         // if primary banks are linked
5352         if(shipp->flags & SF_PRIMARY_LINKED){
5353                 if(bank0_laser){
5354                         shipp->targeting_laser_bank = 0;
5355                         return;
5356                 } 
5357                 if(bank1_laser){
5358                         shipp->targeting_laser_bank = 1;
5359                         return;
5360                 }
5361         }
5362         // if we only have 1 bank selected
5363         else {
5364                 if(bank0_laser && (shipp->weapons.current_primary_bank == 0)){
5365                         shipp->targeting_laser_bank = 0;
5366                         return;
5367                 }
5368                 if(bank1_laser && (shipp->weapons.current_primary_bank == 1)){
5369                         shipp->targeting_laser_bank = 1;
5370                         return;
5371                 }
5372         }
5373 }
5374
5375 void ship_stop_targeting_laser(ship *shipp)
5376 {
5377         shipp->targeting_laser_bank = -1;
5378         shipp->targeting_laser_objnum = -1;
5379 }
5380
5381 void ship_process_targeting_lasers()
5382 {
5383         beam_fire_info fire_info;
5384         ship_obj *so;
5385         ship *shipp;    
5386         polymodel *m;
5387
5388         // interate over all ships
5389         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5390                 // sanity checks
5391                 if(so->objnum < 0){
5392                         continue;
5393                 }
5394                 if(Objects[so->objnum].type != OBJ_SHIP){
5395                         continue;
5396                 }
5397                 if(Objects[so->objnum].instance < 0){
5398                         continue;
5399                 }
5400                 shipp = &Ships[Objects[so->objnum].instance];
5401
5402                 // if our trigger is no longer down, switch it off
5403                 if(!(shipp->flags & SF_TRIGGER_DOWN)){
5404                         ship_stop_targeting_laser(shipp);
5405                         continue;
5406                 }               
5407
5408                 // if we have a bank to fire - fire it
5409                 if((shipp->targeting_laser_bank >= 0) && (shipp->targeting_laser_bank < 2)){
5410                         // try and get the model
5411                         m = model_get(shipp->modelnum);
5412                         if(m == NULL){
5413                                 continue;
5414                         }
5415
5416                         // fire a targeting laser
5417                         fire_info.accuracy = 0.0f;
5418                         fire_info.beam_info_index = shipp->weapons.primary_bank_weapons[shipp->targeting_laser_bank];
5419                         fire_info.beam_info_override = NULL;
5420                         fire_info.shooter = &Objects[shipp->objnum];
5421                         fire_info.target = NULL;
5422                         fire_info.target_subsys = NULL;
5423                         fire_info.turret = NULL;
5424                         fire_info.targeting_laser_offset = m->gun_banks[shipp->targeting_laser_bank].pnt[0];                    
5425                         shipp->targeting_laser_objnum = beam_fire_targeting(&fire_info);                        
5426
5427                         // hmm, why didn't it fire?
5428                         if(shipp->targeting_laser_objnum < 0){
5429                                 Int3();
5430                                 ship_stop_targeting_laser(shipp);
5431                         }
5432                 }
5433         }
5434 }
5435
5436 //      Attempt to detonate weapon last fired by *shipp.
5437 //      Only used for weapons that support remote detonation.
5438 //      Return true if detonated, else return false.
5439 //      Calls weapon_hit() to detonate weapon.
5440 //      If it's a weapon that spawns particles, those will be released.
5441 int maybe_detonate_weapon(ship_weapon *swp, object *src)
5442 {
5443         int                     objnum = swp->last_fired_weapon_index;
5444         object          *objp;
5445         weapon_info     *wip;
5446
5447         objp = &Objects[objnum];
5448
5449         if (objp->type != OBJ_WEAPON){
5450                 return 0;
5451         }
5452
5453         if ((objp->instance < 0) || (objp->instance > MAX_WEAPONS)){
5454                 return 0;
5455         }
5456
5457         // check to make sure that the weapon to detonate still exists
5458         if ( swp->last_fired_weapon_signature != objp->signature ){
5459                 return 0;
5460         }
5461
5462         Assert(Weapons[objp->instance].weapon_info_index != -1);
5463         wip = &Weapon_info[Weapons[objp->instance].weapon_info_index];
5464
5465         if (wip->wi_flags & WIF_REMOTE) {
5466
5467                 if ((objnum >= 0) && (objnum < MAX_OBJECTS)) {
5468                         int     weapon_sig;
5469
5470                         weapon_sig = objp->signature;
5471
5472                         if (swp->last_fired_weapon_signature == weapon_sig) {                           
5473                                 weapon_detonate(objp);
5474                                 swp->last_fired_weapon_index = -1;
5475
5476                                 /*
5477                                 if (src == Player_obj) {
5478                                         char missile_name[NAME_LENGTH];
5479                                         strcpy(missile_name, wip->name);
5480                                         hud_end_string_at_first_hash_symbol(missile_name);
5481                                         HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Detonated %s!", 486), missile_name);
5482                                 }
5483                                 */
5484
5485                                 return 1;
5486                         }
5487                 }
5488         }
5489
5490         return 0;
5491 }
5492
5493 //      Maybe detonate secondary weapon that's already out.
5494 //      Return true if we detonate it, false if not.
5495 int ship_fire_secondary_detonate(object *obj, ship_weapon *swp)
5496 {
5497         if (swp->last_fired_weapon_index != -1)
5498                 if (timestamp_elapsed(swp->detonate_weapon_time)) {
5499                         object  *first_objp = &Objects[swp->last_fired_weapon_index];
5500                         if (maybe_detonate_weapon(swp, obj)) {
5501                                 //      If dual fire was set, there could be another weapon to detonate.  Scan all weapons.
5502                                 missile_obj     *mo;
5503
5504                                 //nprintf(("AI", "Weapon %i detonated\n", first_objp-Objects));
5505
5506                                 // check for currently locked missiles (highest precedence)
5507                                 for ( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
5508                                         object  *mobjp;
5509                                         Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
5510                                         mobjp = &Objects[mo->objnum];
5511                                         if ((mobjp != first_objp) && (mobjp->parent_sig == obj->parent_sig)) {
5512                                                 if (Weapon_info[Weapons[mobjp->instance].weapon_info_index].wi_flags & WIF_REMOTE) {
5513                                                         //nprintf(("AI", "Also detonating weapon %i whose parent is %s\n", mobjp-Objects, Ships[Objects[mobjp->parent].instance].ship_name));
5514                                                         weapon_detonate(mobjp);
5515                                                 }
5516                                         }
5517                                 }
5518                                 
5519                                 return 1;
5520                         }
5521                 }
5522
5523         return 0;
5524 }
5525
5526 // Try to switch to a secondary bank that has ammo
5527 int ship_select_next_valid_secondary_bank(ship_weapon *swp)
5528 {
5529         int cycled=0;
5530
5531         int ns = swp->num_secondary_banks;
5532
5533         if ( ns > 1 ) {
5534                 int i,j=swp->current_secondary_bank+1;
5535                 for (i=0; i<ns; i++) {
5536                         if ( j >= ns ) {
5537                                 j=0;
5538                         }
5539
5540                         if ( swp->secondary_bank_ammo[j] > 0 ) {
5541                                 swp->current_secondary_bank=j;
5542                                 cycled = 1;
5543                                 break;
5544                         }
5545
5546                         j++;
5547                 }
5548         }
5549
5550         return cycled;
5551 }
5552
5553
5554 extern void ai_maybe_announce_shockwave_weapon(object *firing_objp, int weapon_index);
5555
5556 //      Object *obj fires its secondary weapon, if it can.
5557 //      If its most recently fired weapon is a remotely detonatable weapon, detonate it.
5558 //      Returns number of weapons fired.  Note, for swarmers, returns 1 if it is allowed
5559 //      to fire the missiles when allow_swarm is NOT set.  They don't actually get fired on a call here unless allow_swarm is set.
5560 //      When you want to fire swarmers, you call this function with allow_swarm NOT set and frame interval
5561 //      code comes aruond and fires it.
5562 // allow_swarm -> default value is 0... since swarm missiles are fired over several frames,
5563 //                need to avoid firing when normally called
5564 int ship_fire_secondary( object *obj, int allow_swarm )
5565 {
5566         int                     n, weapon, j, bank, have_timeout, starting_bank_count = -1, num_fired;
5567         ushort          starting_sig = 0;
5568         ship                    *shipp;
5569         ship_weapon *swp;
5570         ship_info       *sip;
5571         weapon_info     *wip;
5572         ai_info         *aip;
5573         polymodel       *po;
5574         vector          missile_point, pnt, firing_pos;
5575
5576         Assert( obj != NULL );
5577
5578         // in the case where the server is an observer, he can fire (which would be bad) - unless we do this.
5579         if( obj->type == OBJ_OBSERVER ){
5580                 return 0;
5581         }
5582
5583         // in the case where the object is a ghost (a delayed fire packet from right before he died, for instance)
5584         if( (obj->type == OBJ_GHOST) || (obj->type == OBJ_NONE) ){
5585                 return 0;
5586         }
5587
5588         Assert( obj->type == OBJ_SHIP );
5589         if(obj->type != OBJ_SHIP){
5590                 return 0;
5591         }
5592         n = obj->instance;
5593         Assert( n >= 0 && n < MAX_SHIPS );
5594         if((n < 0) || (n >= MAX_SHIPS)){
5595                 return 0;
5596         }
5597         Assert( Ships[n].objnum == OBJ_INDEX(obj));
5598         if(Ships[n].objnum != OBJ_INDEX(obj)){
5599                 return 0;
5600         }
5601         
5602         shipp = &Ships[n];
5603         swp = &shipp->weapons;
5604         sip = &Ship_info[shipp->ship_info_index];
5605         aip = &Ai_info[shipp->ai_index];
5606
5607         // if no secondary weapons are present on ship, return
5608         if ( swp->num_secondary_banks <= 0 ){
5609                 return 0;
5610         }
5611
5612         // If ship is being repaired/rearmed, it cannot fire missiles
5613         if ( aip->ai_flags & AIF_BEING_REPAIRED ) {
5614                 return 0;
5615         }
5616
5617         num_fired = 0;          // tracks how many missiles actually fired
5618
5619         bank = swp->current_secondary_bank;
5620         if ( bank < 0 ) {
5621                 return 0;
5622         }
5623
5624         weapon = swp->secondary_bank_weapons[bank];
5625         Assert( (swp->secondary_bank_weapons[bank] >= 0) && (swp->secondary_bank_weapons[bank] < MAX_WEAPON_TYPES) );
5626         if((swp->secondary_bank_weapons[bank] < 0) || (swp->secondary_bank_weapons[bank] >= MAX_WEAPON_TYPES)){
5627                 return 0;
5628         }
5629         wip = &Weapon_info[swp->secondary_bank_weapons[bank]];
5630
5631         have_timeout = 0;                       // used to help tell whether or not we have a timeout
5632         if ( MULTIPLAYER_MASTER ) {
5633                 starting_sig = multi_get_next_network_signature( MULTI_SIG_NON_PERMANENT );
5634                 starting_bank_count = swp->secondary_bank_ammo[bank];
5635         }
5636
5637         if (ship_fire_secondary_detonate(obj, swp)) {
5638                 // in multiplayer, master sends a secondary fired packet with starting signature of -1 -- indicates
5639                 // to client code to set the detonate timer to 0.
5640                 if ( MULTIPLAYER_MASTER ) {
5641                         // MWA -- 4/6/98  Assert invalid since the bank count could have gone to 0.
5642                         //Assert(starting_bank_count != 0);
5643                         send_secondary_fired_packet( shipp, 0, starting_bank_count, 1, allow_swarm );
5644                 }
5645         
5646                 //      For all banks, if ok to fire a weapon, make it wait a bit.
5647                 //      Solves problem of fire button likely being down next frame and
5648                 //      firing weapon despite fire causing detonation of existing weapon.
5649                 if (swp->current_secondary_bank >= 0) {
5650                         if (timestamp_elapsed(swp->next_secondary_fire_stamp[bank])){
5651                                 swp->next_secondary_fire_stamp[bank] = timestamp(max((int) flFrametime*3000, 250));
5652                         }
5653                 }
5654                 return 0;
5655         }
5656
5657         if ( swp->current_secondary_bank < 0 ){
5658                 return 0;
5659         }
5660
5661         if ( !timestamp_elapsed(swp->next_secondary_fire_stamp[bank]) && !allow_swarm) {
5662                 if (timestamp_until(swp->next_secondary_fire_stamp[bank]) > 60000){
5663                         swp->next_secondary_fire_stamp[bank] = timestamp(1000);
5664                 }
5665                 have_timeout = 1;
5666                 goto done_secondary;
5667         }
5668
5669         // Ensure if this is a "require-lock" missile, that a lock actually exists
5670         if ( wip->wi_flags & WIF_NO_DUMBFIRE ) {
5671                 if ( aip->current_target_is_locked <= 0 ) {
5672                         if ( obj == Player_obj ) {                      
5673                                 if ( !Weapon_energy_cheat ) {
5674                                         if ((aip->target_objnum != -1) && (vm_vec_dist_quick(&obj->pos, &Objects[aip->target_objnum].pos) > wip->lifetime * wip->max_speed)) {
5675                                                 HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Too far from target to acquire lock", 487));
5676                                         } else {
5677                                                 char missile_name[NAME_LENGTH];
5678                                                 strcpy(missile_name, wip->name);
5679                                                 hud_end_string_at_first_hash_symbol(missile_name);
5680                                                 HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Cannot fire %s without a lock", 488), missile_name);
5681                                         }
5682
5683                                         snd_play( &Snds[SND_OUT_OF_MISSLES] );
5684                                         swp->next_secondary_fire_stamp[bank] = timestamp(800);  // to avoid repeating messages
5685                                         return 0;
5686                                 }
5687                         } else {
5688                                 // multiplayer clients should always fire the weapon here, so return only if not
5689                                 // a multiplayer client.
5690                                 if ( !MULTIPLAYER_CLIENT ){
5691                                         return 0;
5692                                 }
5693                         }
5694                 }
5695         }
5696
5697         // if trying to fire a swarm missile, make sure being called from right place
5698         if ( (wip->wi_flags & WIF_SWARM) && !allow_swarm ) {
5699                 Assert(wip->swarm_count > 0);
5700                 if(wip->swarm_count <= 0){
5701                         shipp->num_swarm_missiles_to_fire += SWARM_DEFAULT_NUM_MISSILES_FIRED;
5702                 } else {
5703                         shipp->num_swarm_missiles_to_fire += wip->swarm_count;
5704                 }
5705                 return 1;               //      Note: Missiles didn't get fired, but the frame interval code will fire them.
5706         }
5707
5708         // if trying to fire a corkscrew missile, make sure being called from right place       
5709         if ( (wip->wi_flags & WIF_CORKSCREW) && !allow_swarm ) {
5710                 shipp->num_corkscrew_to_fire = (ubyte)(shipp->num_corkscrew_to_fire + (ubyte)Corkscrew_num_missiles_fired);
5711                 return 1;               //      Note: Missiles didn't get fired, but the frame interval code will fire them.
5712         }       
5713
5714         swp->next_secondary_fire_stamp[bank] = timestamp((int)(Weapon_info[weapon].fire_wait * 1000.0f));       // They can fire 5 times a second
5715
5716         // Here is where we check if weapons subsystem is capable of firing the weapon.
5717         // do only in single plyaer or if I am the server of a multiplayer game
5718         if ( !(Game_mode & GM_MULTIPLAYER) || MULTIPLAYER_MASTER ) {
5719                 if ( ship_weapon_maybe_fail(shipp) ) {
5720                         if ( obj == Player_obj ) 
5721                                 if ( ship_maybe_play_secondary_fail_sound(wip) ) {
5722                                         char missile_name[NAME_LENGTH];
5723                                         strcpy(missile_name, Weapon_info[weapon].name);
5724                                         hud_end_string_at_first_hash_symbol(missile_name);
5725                                         HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Cannot fire %s due to weapons system damage", 489), missile_name);
5726                                 }
5727                         goto done_secondary;
5728                 }
5729         }
5730
5731         po = model_get( Ship_info[shipp->ship_info_index].modelnum );
5732         if ( po->n_missiles > 0 ) {
5733                 int check_ammo;         // used to tell if we should check ammo counts or not
5734                 int num_slots;
5735
5736                 if ( bank > po->n_missiles ) {
5737                         nprintf(("WARNING","WARNING ==> Tried to fire bank %d, but ship has only %d banks\n", bank+1, po->n_missiles));
5738                         return 0;               // we can make a quick out here!!!
5739                 }
5740
5741                 num_slots = po->missile_banks[bank].num_slots;
5742
5743                 // determine if there is enough ammo left to fire weapons on this bank.  As with primary
5744                 // weapons, we might or might not check ammo counts depending on game mode, who is firing,
5745                 // and if I am a client in multiplayer
5746                 check_ammo = 1;
5747                 if ( MULTIPLAYER_CLIENT && (obj != Player_obj) ){
5748                         check_ammo = 0;
5749                 }
5750
5751                 if ( check_ammo && ( swp->secondary_bank_ammo[bank] <= 0) ) {
5752                         if ( shipp->objnum == OBJ_INDEX(Player_obj) ) {
5753                                 if ( ship_maybe_play_secondary_fail_sound(wip) ) {
5754 //                                      HUD_sourced_printf(HUD_SOURCE_HIDDEN, "No %s missiles left in bank", Weapon_info[swp->secondary_bank_weapons[bank]].name);
5755                                 }
5756                         }
5757                         else {
5758                                 // TODO:  AI switch secondary weapon / re-arm?
5759                         }
5760                         goto done_secondary;
5761                 }
5762
5763                 int start_slot, end_slot;
5764
5765                 if ( shipp->flags & SF_SECONDARY_DUAL_FIRE ) {
5766                         start_slot = swp->secondary_next_slot[bank];
5767                         // AL 11-19-97: Ensure enough ammo remains when firing linked secondary weapons
5768                         if ( check_ammo && (swp->secondary_bank_ammo[bank] < 2) ) {
5769                                 end_slot = start_slot;
5770                         } else {
5771                                 end_slot = start_slot+1;
5772                         }
5773                 } else {
5774                         start_slot = swp->secondary_next_slot[bank];
5775                         end_slot = start_slot;
5776                 }
5777
5778                 int pnt_index=start_slot;
5779                 for ( j = start_slot; j <= end_slot; j++ ) {
5780                         int     weapon_num;
5781
5782                         swp->secondary_next_slot[bank]++;
5783                         if ( swp->secondary_next_slot[bank] > (num_slots-1) ){
5784                                 swp->secondary_next_slot[bank] = 0;
5785                         }
5786
5787                         if ( pnt_index >= num_slots ){
5788                                 pnt_index = 0;
5789                         }
5790                         pnt = po->missile_banks[bank].pnt[pnt_index++];
5791                         vm_vec_unrotate(&missile_point, &pnt, &obj->orient);
5792                         vm_vec_add(&firing_pos, &missile_point, &obj->pos);
5793
5794                         if ( Game_mode & GM_MULTIPLAYER ) {
5795                                 Assert( Weapon_info[weapon].subtype == WP_MISSILE );
5796                         }
5797
5798                         // create the weapon -- for multiplayer, the net_signature is assigned inside
5799                         // of weapon_create
5800                         weapon_num = weapon_create( &firing_pos, &obj->orient, weapon, OBJ_INDEX(obj), 0, -1, aip->current_target_is_locked);
5801                         weapon_set_tracking_info(weapon_num, OBJ_INDEX(obj), aip->target_objnum, aip->current_target_is_locked, aip->targeted_subsys);
5802
5803                         // create the muzzle flash effect
5804                         shipfx_flash_create( obj, shipp, &pnt, &obj->orient.v.fvec, 0, weapon );
5805
5806 /*
5807                         if ( weapon_num != -1 )
5808                                 Demo_fire_secondary_requests++; // testing for demo
5809 */
5810                         num_fired++;
5811                         swp->last_fired_weapon_index = weapon_num;
5812                         swp->detonate_weapon_time = timestamp(500);             //      Can detonate 1/2 second later.
5813                         if (weapon_num != -1) {
5814                                 swp->last_fired_weapon_signature = Objects[weapon_num].signature;
5815                         }
5816
5817                         // subtract the number of missiles fired
5818                         if ( Weapon_energy_cheat == 0 ){
5819                                 swp->secondary_bank_ammo[bank]--;
5820                         }
5821                 }
5822         }
5823
5824         if ( obj == Player_obj ) {
5825                 if ( Weapon_info[weapon].launch_snd != -1 ) {
5826                         weapon_info *wip;
5827                         ship_weapon *swp;
5828
5829                         snd_play( &Snds[Weapon_info[weapon].launch_snd], 0.0f, 1.0f, SND_PRIORITY_MUST_PLAY );
5830                         swp = &Player_ship->weapons;
5831                         if (swp->current_secondary_bank >= 0) {
5832                                 wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
5833                                 if (Player_ship->flags & SF_SECONDARY_DUAL_FIRE){
5834                                         joy_ff_play_secondary_shoot((int) (wip->cargo_size * 2.0f));
5835                                 } else {
5836                                         joy_ff_play_secondary_shoot((int) wip->cargo_size);
5837                                 }
5838                         }
5839                 }
5840
5841         } else {
5842                 if ( Weapon_info[weapon].launch_snd != -1 ) {
5843                         snd_play_3d( &Snds[Weapon_info[weapon].launch_snd], &obj->pos, &View_position );
5844                 }
5845         }
5846
5847 done_secondary:
5848
5849         if(num_fired > 0){
5850                 // if I am the master of a multiplayer game, send a secondary fired packet along with the
5851                 // first network signatures for the newly created weapons.  if nothing got fired, send a failed
5852                 // packet if 
5853                 if ( MULTIPLAYER_MASTER ) {                     
5854                         Assert(starting_sig != 0);
5855                         send_secondary_fired_packet( shipp, starting_sig, starting_bank_count, num_fired, allow_swarm );                        
5856                 }
5857
5858                 // STATS
5859                 if (obj->flags & OF_PLAYER_SHIP) {
5860                         // in multiplayer -- only the server needs to keep track of the stats.  Call the cool
5861                         // function to find the player given the object *.  It had better return a valid player
5862                         // or our internal structure as messed up.
5863                         if( Game_mode & GM_MULTIPLAYER ) {
5864                                 if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
5865                                         int player_num;
5866
5867                                         player_num = multi_find_player_by_object ( obj );
5868                                         Assert ( player_num != -1 );
5869
5870                                         Net_players[player_num].player->stats.ms_shots_fired += num_fired;
5871                                 }                               
5872                         } else
5873                                 Player->stats.ms_shots_fired += num_fired;
5874                 }
5875                 
5876                 // maybe announce a shockwave weapon
5877                 ai_maybe_announce_shockwave_weapon(obj, weapon);
5878         }
5879
5880         // AL 3-7-98: Move to next valid secondary bank if out of ammo
5881         if ( (obj->flags & OF_PLAYER_SHIP) && (swp->secondary_bank_ammo[bank] <= 0) ) {
5882                 int fire_wait = (int)(Weapon_info[weapon].fire_wait * 1000.0f);
5883                 if ( ship_select_next_valid_secondary_bank(swp) ) {
5884                         swp->next_secondary_fire_stamp[swp->current_secondary_bank] = max(timestamp(250),timestamp(fire_wait)); //      1/4 second delay until can fire
5885                         if ( obj == Player_obj ) {
5886                                 snd_play( &Snds[SND_SECONDARY_CYCLE] );
5887                         }
5888                 }
5889         }       
5890
5891         return num_fired;
5892 }
5893
5894 // ------------------------------------------------------------------------------
5895 // ship_select_next_primary()
5896 //
5897 //      Return true if a new index gets selected.
5898 //
5899 // parameters:          objp      => pointer to object for ship cycling primary
5900 //                direction => forward == CYCLE_PRIMARY_NEXT, backward == CYCLE_PRIMARY_PREV
5901 //
5902 // NOTE: This code can be called for any arbitrary ship.  HUD messages and sounds are only used
5903 //       for the player ship.
5904 int ship_select_next_primary(object *objp, int direction)
5905 {
5906         ship    *shipp;
5907         ship_weapon *swp;
5908
5909         Assert(objp != NULL);
5910         Assert(objp->type == OBJ_SHIP);
5911         Assert(objp->instance >= 0 && objp->instance < MAX_SHIPS);
5912
5913         shipp = &Ships[objp->instance];
5914         swp = &shipp->weapons;
5915
5916         Assert(direction == CYCLE_PRIMARY_NEXT || direction == CYCLE_PRIMARY_PREV);
5917
5918         switch ( swp->num_primary_banks ) {
5919
5920                 case 0:
5921                         if ( objp == Player_obj ) {
5922                                 HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "This ship has no primary weapons", 490));
5923                                 gamesnd_play_error_beep();
5924                         }
5925                         return 0;
5926                         break;          
5927
5928                 case 1:
5929                         if ( objp == Player_obj ) {
5930                                 HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "This ship has only one primary weapon: %s", 491),Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].name, swp->current_primary_bank + 1);
5931                                 gamesnd_play_error_beep();
5932                         }
5933                         return 0;
5934                         break;          
5935
5936                 case 2:
5937                         if ( shipp->flags & SF_PRIMARY_LINKED ) {
5938                                 shipp->flags &= ~SF_PRIMARY_LINKED;
5939                                 if ( direction == CYCLE_PRIMARY_NEXT ) {
5940                                         swp->current_primary_bank = 0;
5941                                 } else {
5942                                         swp->current_primary_bank = 1;
5943                                 }
5944                         } else {
5945                                 switch ( swp->current_primary_bank ) {
5946                                         case 0:
5947                                                 if ( direction == CYCLE_PRIMARY_NEXT ) {
5948                                                         swp->current_primary_bank = 1;
5949                                                 } else {
5950                                                         shipp->flags |= SF_PRIMARY_LINKED;
5951                                                 }
5952                                                 break;
5953
5954                                         case 1:
5955                                                 if ( direction == CYCLE_PRIMARY_NEXT ) {
5956                                                         shipp->flags |= SF_PRIMARY_LINKED;
5957                                                 } else {
5958                                                         swp->current_primary_bank = 0;
5959                                                 }
5960                                                 break;
5961
5962                                         default:
5963                                                 Int3(); // should never happen, get Alan if it does
5964                                                 return 0;
5965                                                 break;
5966                                 }
5967                         }
5968                         break;
5969                                         
5970                 default:
5971                         Int3();                         // should never happen, get Alan if it does
5972                         return 0;
5973                         break;
5974         }
5975
5976         if ( objp == Player_obj ) {
5977                 snd_play( &Snds[SND_PRIMARY_CYCLE], 0.0f );
5978         }
5979
5980         ship_primary_changed(shipp);
5981         return 1;
5982 }
5983
5984 // ------------------------------------------------------------------------------
5985 // ship_select_next_secondary() selects the next secondary bank with missles
5986 //
5987 //      returns:                1       => The secondary bank was switched
5988 //                                      0       => The secondary bank stayed the same
5989 //
5990 // If a secondary bank has no missles left, it is skipped.
5991 //
5992 // NOTE: This can be called for an arbitrary ship.  HUD messages and sounds are only used
5993 //                      for the player ship.
5994 int ship_select_next_secondary(object *objp)
5995 {
5996         Assert(objp != NULL);
5997         Assert(objp->type == OBJ_SHIP);
5998         Assert(objp->instance >= 0 && objp->instance < MAX_SHIPS);
5999
6000         int     original_bank, new_bank, i;
6001         ship    *shipp;
6002         ship_weapon *swp;
6003
6004         shipp = &Ships[objp->instance];
6005         swp = &shipp->weapons;
6006
6007         switch ( swp->num_secondary_banks ) {
6008                 case 0:
6009                         if ( objp == Player_obj ) {
6010                                 HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "This ship has no secondary weapons", 492));
6011                                 gamesnd_play_error_beep();
6012                         }
6013                         return 0;
6014                         break;
6015
6016                 case 1:
6017                         if ( objp == Player_obj ) {
6018                                 HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "This ship has only one secondary weapon: %s", 493), Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name, swp->current_secondary_bank + 1);
6019                                 gamesnd_play_error_beep();
6020                         }
6021                         return 0;
6022                         break;
6023
6024                 case 2:
6025                 case 3:
6026                         Assert(swp->current_secondary_bank < swp->num_secondary_banks);
6027                         original_bank = swp->current_secondary_bank;
6028
6029                         for ( i = 1; i < swp->num_secondary_banks; i++ ) {
6030                                 new_bank = (swp->current_secondary_bank+i) % swp->num_secondary_banks;
6031                                 if ( swp->secondary_bank_ammo[new_bank] <= 0 )
6032                                         continue;
6033                                 swp->current_secondary_bank = new_bank;
6034                                 break;
6035                         }
6036
6037                         if ( swp->current_secondary_bank != original_bank ) {
6038                                 if ( objp == Player_obj ) {
6039                                         snd_play( &Snds[SND_SECONDARY_CYCLE], 0.0f );
6040                                 }
6041                                 ship_secondary_changed(shipp);
6042                                 return 1;
6043                         }
6044
6045                         break;
6046
6047                 default:
6048                         Int3(); // should never happen, get Alan if it does
6049                         return 0;
6050                         break;
6051         } // end switch
6052
6053         // If we've reached this point, must have failed
6054         return 0;
6055 }
6056
6057 //      Stuff list of weapon indices for object *objp in list *outlist.
6058 //      Return number of weapons in list.
6059 int get_available_secondary_weapons(object *objp, int *outlist, int *outbanklist)
6060 {
6061         int     count = 0;
6062         int     i;
6063         ship    *shipp;
6064
6065         Assert(objp->type == OBJ_SHIP);
6066         Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
6067         shipp = &Ships[objp->instance];
6068
6069         for (i=0; i<shipp->weapons.num_secondary_banks; i++)
6070                 if (shipp->weapons.secondary_bank_ammo[i]) {
6071                         outbanklist[count] = i;
6072                         outlist[count++] = shipp->weapons.secondary_bank_weapons[i];
6073                 }
6074
6075         return count;
6076 }
6077
6078 //      Return the object index of the ship with name *name.
6079 int wing_name_lookup(char *name, int ignore_count)
6080 {
6081         int i, wing_limit;
6082
6083         if ( Fred_running )
6084                 wing_limit = MAX_WINGS;
6085         else
6086                 wing_limit = num_wings;
6087
6088         if (Fred_running || ignore_count ) {  // current_count not used for Fred..
6089                 for (i=0; i<wing_limit; i++)
6090                         if (Wings[i].wave_count && !stricmp(Wings[i].name, name))
6091                                 return i;
6092
6093         } else {
6094                 for (i=0; i<wing_limit; i++)
6095                         if (Wings[i].current_count && !stricmp(Wings[i].name, name))
6096                                 return i;
6097         }
6098
6099         return -1;
6100 }
6101
6102 // this function is needed in addition to wing_name_lookup because it does a straight lookup without
6103 // caring about how many ships are in the wing, etc.
6104 int wing_lookup(char *name)
6105 {
6106    int idx;
6107         for(idx=0;idx<num_wings;idx++)
6108                 if(strcmp(Wings[idx].name,name)==0)
6109                    return idx;
6110
6111         return -1;
6112 }
6113
6114 //      Return the index of Ship_info[].name that is *name.
6115 int ship_info_lookup(char *name)
6116 {
6117         int     i;
6118
6119         for (i=0; i < Num_ship_types; i++)
6120                 if (!stricmp(name, Ship_info[i].name))
6121                         return i;
6122
6123         return -1;
6124 }
6125
6126 //      Return the index of Ship_info[].name which is the *base* ship of a ship copy
6127 int ship_info_base_lookup(int si_index)
6128 {
6129         int     i;
6130         char name[NAME_LENGTH], *p;
6131
6132         strcpy( name, Ship_info[si_index].name );
6133         p = strchr( name, '#' );
6134         Assert( p );                                            // get allender -- something bogus with ship copy
6135         *p = '\0';
6136
6137         i = ship_info_lookup( name );
6138         Assert( i != -1 );                              // get allender -- there had better be a base ship!
6139
6140         return i;
6141 }
6142
6143 //      Return the ship index of the ship with name *name.
6144 int ship_name_lookup(char *name, int inc_players)
6145 {
6146         int     i;
6147
6148         // bogus
6149         if(name == NULL){
6150                 return -1;
6151         }
6152
6153         for (i=0; i<MAX_SHIPS; i++){
6154                 if (Ships[i].objnum >= 0){
6155                         if (Objects[Ships[i].objnum].type == OBJ_SHIP || (Objects[Ships[i].objnum].type == OBJ_START && inc_players)){
6156                                 if (!stricmp(name, Ships[i].ship_name)){
6157                                         return i;
6158                                 }
6159                         }
6160                 }
6161         }
6162         
6163         // couldn't find it
6164         return -1;
6165 }
6166
6167 int ship_type_name_lookup(char *name)
6168 {
6169         int idx;
6170
6171         // bogus
6172         if(name == NULL){
6173                 return -1;
6174         }
6175
6176         // look through the Ship_type_names array
6177         for(idx=0; idx<MAX_SHIP_TYPE_COUNTS; idx++){
6178                 if(!stricmp(name, Ship_type_names[idx])){
6179                         return idx;
6180                 }
6181         }
6182
6183         // couldn't find it
6184         return -1;
6185 }
6186
6187 // checks the (arrival & departure) state of a ship.  Return values:
6188 // -1: has yet to arrive in mission
6189 //  0: is currently in mission
6190 //  1: has been destroyed, departed, or never existsed
6191 int ship_query_state(char *name)
6192 {
6193         int i;
6194         p_object *objp;
6195
6196         // bogus
6197         if(name == NULL){
6198                 return -1;
6199         }
6200
6201         for (i=0; i<MAX_SHIPS; i++){
6202                 if (Ships[i].objnum >= 0){
6203                         if ((Objects[Ships[i].objnum].type == OBJ_SHIP) || (Objects[Ships[i].objnum].type == OBJ_START)){
6204                                 if (!stricmp(name, Ships[i].ship_name)){
6205                                         return 0;
6206                                 }
6207                         }
6208                 }
6209         }
6210
6211         objp = GET_FIRST(&ship_arrival_list);
6212         while (objp != END_OF_LIST(&ship_arrival_list)) {
6213                 if (!stricmp(name, objp->name)){
6214                         return -1;
6215                 }
6216
6217                 objp = GET_NEXT(objp);
6218         }
6219
6220         return 1;
6221 }
6222
6223 //      Note: This is not a general purpose routine.
6224 //      It is specifically used for targeting.
6225 //      It only returns a subsystem position if it has shields.
6226 //      Return true/false for subsystem found/not found.
6227 //      Stuff vector *pos with absolute position.
6228 // subsysp is a pointer to the subsystem.
6229 int get_subsystem_pos(vector *pos, object *objp, ship_subsys *subsysp)
6230 {
6231         matrix  m;
6232         model_subsystem *psub;
6233         vector  pnt;
6234         ship            *shipp;
6235
6236         Assert(objp->type == OBJ_SHIP);
6237         shipp = &Ships[objp->instance];
6238
6239         Assert ( subsysp != NULL );
6240
6241         psub = subsysp->system_info;
6242         vm_copy_transpose_matrix(&m, &objp->orient);
6243
6244         vm_vec_rotate(&pnt, &psub->pnt, &m);
6245         vm_vec_add2(&pnt, &objp->pos);
6246
6247         if ( pos ){
6248                 *pos = pnt;
6249         }
6250
6251         return 1;
6252 }
6253
6254 //=================================================
6255 // Takes all the angle info from the ship structure and stuffs it
6256 // into the model data so that the model code has all the correct
6257 // angles and stuff that it needs.    This is a poorly designed 
6258 // system that should be re-engineered so that all the model functions
6259 // accept a list of angles and everyone passes them through, but
6260 // that would require some major code revision.
6261 // So, anytime you are using a model that has rotating parts, you
6262 // need to do a ship_model_start before any model_ functions are
6263 // called and a ship_model_stop after you're done.   Even for 
6264 // collision detection and stuff, not just rendering.
6265 // See John for details.
6266
6267 void ship_model_start(object *objp)
6268 {
6269         model_subsystem *psub;
6270         ship            *shipp;
6271         ship_subsys     *pss;
6272
6273         shipp = &Ships[objp->instance];
6274
6275         // First clear all the angles in the model to zero
6276         model_clear_instance(shipp->modelnum);
6277
6278         // Go through all subsystems and bash the model angles for all 
6279         // the subsystems that need it.
6280         for ( pss = GET_FIRST(&shipp->subsys_list); pss != END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
6281                 psub = pss->system_info;
6282                 switch (psub->type) {
6283                 case SUBSYSTEM_RADAR:
6284                 case SUBSYSTEM_NAVIGATION:
6285                 case SUBSYSTEM_COMMUNICATION:
6286                 case SUBSYSTEM_UNKNOWN:
6287                 case SUBSYSTEM_ENGINE:
6288                 case SUBSYSTEM_SENSORS:
6289                 case SUBSYSTEM_WEAPONS:
6290                 case SUBSYSTEM_SOLAR:
6291                 case SUBSYSTEM_GAS_COLLECT:
6292                 case SUBSYSTEM_ACTIVATION:
6293                         break;
6294                 case SUBSYSTEM_TURRET:
6295                         Assert( !(psub->flags & MSS_FLAG_ROTATES) ); // Turrets can't rotate!!! See John!
6296                         break;
6297                 default:
6298                         Error(LOCATION, "Illegal subsystem type.\n");
6299                 }
6300
6301
6302                 if ( psub->subobj_num > -1 )    {
6303                         model_set_instance(shipp->modelnum, psub->subobj_num, &pss->submodel_info_1 );
6304                 }
6305
6306                 if ( (psub->subobj_num != psub->turret_gun_sobj) && (psub->turret_gun_sobj >-1) )               {
6307                         model_set_instance(shipp->modelnum, psub->turret_gun_sobj, &pss->submodel_info_2 );
6308                 }
6309
6310         }
6311 }
6312
6313 //==========================================================
6314 // Clears all the instance specific stuff out of the model info
6315 void ship_model_stop(object *objp)
6316 {
6317         ship            *shipp;
6318
6319         shipp = &Ships[objp->instance];
6320
6321         // Then, clear all the angles in the model to zero
6322         model_clear_instance(shipp->modelnum);
6323 }
6324
6325
6326 //==========================================================
6327 // Finds the number of crew points in a ship
6328 int ship_find_num_crewpoints(object *objp)
6329 {
6330         int n = 0;
6331         model_subsystem *psub;
6332         ship            *shipp;
6333         ship_subsys     *pss;
6334
6335         shipp = &Ships[objp->instance];
6336
6337         // Go through all subsystems and record the model angles for all 
6338         // the subsystems that need it.
6339         for ( pss = GET_FIRST(&shipp->subsys_list); pss != END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
6340                 psub = pss->system_info;
6341                 switch (psub->type) {
6342                 case SUBSYSTEM_TURRET:
6343                         if ( psub->flags & MSS_FLAG_CREWPOINT )
6344                                 n++; // fall through
6345
6346                 case SUBSYSTEM_RADAR:
6347                 case SUBSYSTEM_NAVIGATION:
6348                 case SUBSYSTEM_COMMUNICATION:
6349                 case SUBSYSTEM_UNKNOWN:
6350                 case SUBSYSTEM_ENGINE:
6351                 case SUBSYSTEM_GAS_COLLECT:
6352                 case SUBSYSTEM_ACTIVATION:
6353                         break;
6354                 default:
6355                         Error(LOCATION, "Illegal subsystem type.\n");
6356                 }
6357         }
6358         return n;
6359 }
6360
6361 //==========================================================
6362 // Finds the number of turrets in a ship
6363 int ship_find_num_turrets(object *objp)
6364 {
6365         int n = 0;
6366         model_subsystem *psub;
6367         ship            *shipp;
6368         ship_subsys     *pss;
6369
6370         shipp = &Ships[objp->instance];
6371
6372         // Go through all subsystems and record the model angles for all 
6373         // the subsystems that need it.
6374         for ( pss = GET_FIRST(&shipp->subsys_list); pss != END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
6375                 psub = pss->system_info;
6376                 switch (psub->type) {
6377                 case SUBSYSTEM_TURRET:
6378                         n++; // drop through
6379
6380                 case SUBSYSTEM_RADAR:
6381                 case SUBSYSTEM_NAVIGATION:
6382                 case SUBSYSTEM_COMMUNICATION:
6383                 case SUBSYSTEM_UNKNOWN:
6384                 case SUBSYSTEM_ENGINE:
6385                 case SUBSYSTEM_GAS_COLLECT:
6386                 case SUBSYSTEM_ACTIVATION:
6387                         break;
6388                 default:
6389                         Error(LOCATION, "Illegal subsystem type.\n");
6390                 }
6391         }
6392         return n;
6393 }
6394
6395 //      Modify the matrix orient by the slew angles a.
6396 void compute_slew_matrix(matrix *orient, angles *a)
6397 {
6398         matrix  tmp, tmp2;
6399         angles  t1, t2;
6400
6401         t1 = t2 = *a;
6402         t1.h = 0.0f;    t1.b = 0.0f;
6403         t2.p = 0.0f;    t2.b = 0.0f;
6404
6405         // put in p & b like normal
6406         vm_angles_2_matrix(&tmp, &t1 );
6407         vm_matrix_x_matrix( &tmp2, orient, &tmp);
6408
6409         // Put in heading separately
6410         vm_angles_2_matrix(&tmp, &t2 );
6411         vm_matrix_x_matrix( orient, &tmp2, &tmp );
6412
6413         vm_orthogonalize_matrix(orient);
6414 }
6415
6416 // calculates the eye position for this ship in the global reference frame.  Uses the
6417 // view_positions array in the model.  The 0th element is the noral viewing position.
6418 // the vector of the eye is returned in the parameter 'eye'.  The orientation of the
6419 // eye is returned in orient.  (NOTE: this is kind of bogus for now since non 0th element
6420 // eyes have no defined up vector)
6421 void ship_get_eye( vector *eye_pos, matrix *eye_orient, object *obj )
6422 {
6423         ship *shipp;
6424         polymodel *pm;
6425         eye *ep;
6426         // vector vec;
6427
6428         shipp = &Ships[obj->instance];
6429         pm = model_get( shipp->modelnum );
6430
6431         // check to be sure that we have a view eye to look at.....spit out nasty debug message
6432         if ( pm->n_view_positions == 0 ) {
6433 //              nprintf (("Warning", "No eye position found for model %s.  Find artist to get fixed.\n", pm->filename ));
6434                 *eye_pos = obj->pos;
6435                 *eye_orient = obj->orient;
6436                 return;
6437         }
6438         ep = &(pm->view_positions[0] );
6439
6440         // eye points are stored in an array -- the normal viewing position for a ship is the current_eye_index
6441         // element.
6442         model_find_world_point( eye_pos, &ep->pnt, shipp->modelnum, ep->parent, &obj->orient, &obj->pos );
6443         // if ( shipp->current_eye_index == 0 ) {
6444                 *eye_orient = obj->orient;
6445         //} else {
6446         //      model_find_world_dir( &vec, &ep->norm, shipp->modelnum, ep->parent, &obj->orient, &obj->pos );
6447                 // kind of bogus, but use the objects uvec to avoid totally stupid looking behavior.
6448         //      vm_vector_2_matrix(eye_orient,&vec,&obj->orient.v.uvec,NULL);
6449         //}
6450
6451         //      Modify the orientation based on head orientation.
6452         if ( Viewer_obj == obj ) {
6453                 if ( Viewer_mode & VM_PADLOCK_ANY ) {
6454                         player_get_padlock_orient(eye_orient);
6455                 } else {
6456                         compute_slew_matrix(eye_orient, &Viewer_slew_angles);
6457                 }
6458         }
6459 }
6460
6461 // of attackers to make this decision.
6462 //
6463 // NOTE: This function takes into account how many ships are attacking a subsystem, and will 
6464 //                      prefer an ignored subsystem over a subsystem that is in line of sight, if the in-sight
6465 //                      subsystem is attacked by more than MAX_SUBSYS_ATTACKERS
6466 // input:
6467 //                              sp                                      =>              ship pointer to parent of subsystem
6468 //                              subsys_type             =>              what kind of subsystem this is
6469 //                              attacker_pos    =>              the world coords of the attacker of this subsystem
6470 //
6471 // returns: pointer to subsystem if one found, NULL otherwise
6472 #define MAX_SUBSYS_ATTACKERS 3
6473 ship_subsys *ship_get_best_subsys_to_attack(ship *sp, int subsys_type, vector *attacker_pos)
6474 {
6475         ship_subsys     *ss;
6476         ship_subsys *best_in_sight_subsys, *lowest_attacker_subsys, *ss_return;
6477         int                     lowest_num_attackers, lowest_in_sight_attackers, num_attackers;
6478         vector          gsubpos;
6479         ship_obj                *sop;
6480
6481         lowest_in_sight_attackers = lowest_num_attackers = 1000;
6482         ss_return = best_in_sight_subsys = lowest_attacker_subsys = NULL;
6483
6484         for (ss = GET_FIRST(&sp->subsys_list); ss != END_OF_LIST(&sp->subsys_list); ss = GET_NEXT(ss) ) {
6485                 if ( (ss->system_info->type == subsys_type) && (ss->current_hits > 0) ) {
6486
6487                         // get world pos of subsystem
6488                         vm_vec_unrotate(&gsubpos, &ss->system_info->pnt, &Objects[sp->objnum].orient);
6489                         vm_vec_add2(&gsubpos, &Objects[sp->objnum].pos);
6490                         
6491                         // now find the number of ships attacking this subsystem by iterating through the ships list,
6492                         // and checking if aip->targeted_subsys matches the subsystem we're checking
6493                         num_attackers = 0;
6494                         sop = GET_FIRST(&Ship_obj_list);
6495                         while(sop != END_OF_LIST(&Ship_obj_list)){
6496                                 if ( Ai_info[Ships[Objects[sop->objnum].instance].ai_index].targeted_subsys == ss ) {
6497                                         num_attackers++;
6498                                 }
6499                                 sop = GET_NEXT(sop);
6500                         }
6501
6502                         if ( num_attackers < lowest_num_attackers ) {
6503                                 lowest_num_attackers = num_attackers;
6504                                 lowest_attacker_subsys = ss;
6505                         }
6506
6507                         if ( ship_subsystem_in_sight(&Objects[sp->objnum], ss, attacker_pos, &gsubpos) ) {
6508                                 if ( num_attackers < lowest_in_sight_attackers ) {
6509                                         lowest_in_sight_attackers = num_attackers;
6510                                         best_in_sight_subsys = ss;
6511                                 }
6512                         }
6513                 }
6514         }
6515
6516         if ( best_in_sight_subsys == NULL ) {
6517                 // no subsystems are in sight, so return the subsystem with the lowest # of attackers
6518                 ss_return =  lowest_attacker_subsys;
6519         } else {
6520                 if ( lowest_in_sight_attackers > MAX_SUBSYS_ATTACKERS ) {
6521                         ss_return = lowest_attacker_subsys;
6522                 } else {
6523                         ss_return =  best_in_sight_subsys;
6524                 }
6525         }
6526
6527         return ss_return;
6528 }
6529
6530 // function to return a pointer to the 'nth' ship_subsys structure in a ship's linked list
6531 // of ship_subsys'.
6532 // attacker_pos =>      world pos of attacker (default value NULL).  If value is non-NULL, try
6533 //                                                      to select the best subsystem to attack of that type (using line-of-sight)
6534 //                                                      and based on the number of ships already attacking the subsystem
6535 ship_subsys *ship_get_indexed_subsys( ship *sp, int index, vector *attacker_pos )
6536 {
6537         int count;
6538         ship_subsys *ss;
6539
6540         // first, special code to see if the index < 0.  If so, we are looking for one of several possible
6541         // engines or one of several possible turrets.  If we enter this if statement, we will always return
6542         // something.
6543         if ( index < 0 ) {
6544                 int subsys_type;
6545                 
6546                 subsys_type = -index;
6547                 if ( sp->subsys_info[subsys_type].current_hits == 0.0f )                // if there are no hits, no subsystem to attack.
6548                         return NULL;
6549
6550                 if ( attacker_pos != NULL ) {
6551                         ss = ship_get_best_subsys_to_attack(sp, subsys_type, attacker_pos);
6552                         return ss;
6553                 } else {
6554                         // next, scan the list of subsystems and search for the first subsystem of the particular
6555                         // type which has > 0 hits remaining.
6556                         for (ss = GET_FIRST(&sp->subsys_list); ss != END_OF_LIST(&sp->subsys_list); ss = GET_NEXT(ss) ) {
6557                                 if ( (ss->system_info->type == subsys_type) && (ss->current_hits > 0) )
6558                                         return ss;
6559                         }
6560                 }
6561                 
6562                 Int3();                         // maybe we shouldn't get here, but with possible floating point rounding, I suppose we could
6563                 return NULL;
6564         }
6565
6566
6567         count = 0;
6568         ss = GET_FIRST(&sp->subsys_list);
6569         while ( ss != END_OF_LIST( &sp->subsys_list ) ) {
6570                 if ( count == index )
6571                         return ss;
6572                 count++;
6573                 ss = GET_NEXT( ss );
6574         }
6575         Int3();                 // get allender -- turret ref didn't fixup correctly!!!!
6576         return NULL;
6577 }
6578
6579 //      Given a pointer to a subsystem and an associated object, return the index.
6580 int ship_get_index_from_subsys(ship_subsys *ssp, int objnum, int error_bypass)
6581 {
6582         if (ssp == NULL)
6583                 return -1;
6584         else {
6585                 int     count;
6586                 ship    *shipp;
6587                 ship_subsys     *ss;
6588
6589                 Assert(objnum >= 0);
6590                 Assert(Objects[objnum].instance >= 0);
6591
6592                 shipp = &Ships[Objects[objnum].instance];
6593
6594                 count = 0;
6595                 ss = GET_FIRST(&shipp->subsys_list);
6596                 while ( ss != END_OF_LIST( &shipp->subsys_list ) ) {
6597                         if ( ss == ssp)
6598                                 return count;
6599                         count++;
6600                         ss = GET_NEXT( ss );
6601                 }
6602                 if ( !error_bypass )
6603                         Int3();                 // get allender -- turret ref didn't fixup correctly!!!!
6604                 return -1;
6605         }
6606 }
6607
6608 // function which returns the index number of the ship_subsys parameter
6609 int ship_get_subsys_index(ship *sp, char *ss_name, int error_bypass)
6610 {
6611         int count;
6612         ship_subsys *ss;
6613
6614         count = 0;
6615         ss = GET_FIRST(&sp->subsys_list);
6616         while ( ss != END_OF_LIST( &sp->subsys_list ) ) {
6617                 if ( !stricmp(ss->system_info->subobj_name, ss_name) )
6618                         return count;
6619                 count++;
6620                 ss = GET_NEXT( ss );
6621         }
6622
6623         if (!error_bypass)
6624                 Int3();
6625
6626         return -1;
6627 }
6628
6629 // routine to return the strength of a subsystem.  We keep a total hit tally for all subsystems
6630 // which are similar (i.e. a total for all engines).  These routines will return a number between
6631 // 0.0 and 1.0 which is the relative combined strength of the given subsystem type.  The number
6632 // calculated for the engines is slightly different.  Once an engine reaches < 15% of it's hits, it's
6633 // output drops to that %.  A dead engine has no output.
6634 float ship_get_subsystem_strength( ship *shipp, int type )
6635 {
6636         float strength;
6637         ship_subsys *ssp;
6638
6639         Assert ( (type >= 0) && (type < SUBSYSTEM_MAX) );
6640         if ( shipp->subsys_info[type].total_hits == 0.0f )
6641                 return 1.0f;
6642
6643         //      For a dying ship, all subsystem strengths are zero.
6644         if (Objects[shipp->objnum].hull_strength <= 0.0f)
6645                 return 0.0f;
6646
6647         strength = shipp->subsys_info[type].current_hits / shipp->subsys_info[type].total_hits;
6648
6649         if ( strength == 0.0f )         // short circuit 0
6650                 return strength;
6651
6652         if ( (type == SUBSYSTEM_ENGINE) && (strength < 1.0f) ) {
6653                 float percent;
6654
6655                 percent = 0.0f;
6656                 ssp = GET_FIRST(&shipp->subsys_list);
6657                 while ( ssp != END_OF_LIST( &shipp->subsys_list ) ) {
6658
6659                         if ( ssp->system_info->type == SUBSYSTEM_ENGINE ) {
6660                                 float ratio;
6661
6662                                 ratio = ssp->current_hits / ssp->system_info->max_hits;
6663                                 if ( ratio < ENGINE_MIN_STR )
6664                                         ratio = ENGINE_MIN_STR;
6665
6666                                 percent += ratio;
6667                         }
6668                         ssp = GET_NEXT( ssp );
6669                 }
6670                 strength = percent / (float)shipp->subsys_info[type].num;
6671         }
6672
6673         return strength;
6674 }
6675
6676 // set the strength of a subsystem on a given ship.  The strength passed as a 
6677 // parameter is between 0.0 and 1.0
6678 //
6679 // NOTE: this function was made to be called by the debug function dcf_set_subsys().  If
6680 // you want to use this, be sure that you test it for all cases.
6681 void ship_set_subsystem_strength( ship *shipp, int type, float strength )
6682 {
6683         float total_current_hits, diff;
6684         ship_subsys *ssp;
6685
6686         Assert ( (type >= 0) && (type < SUBSYSTEM_MAX) );
6687         if ( shipp->subsys_info[type].total_hits == 0.0f )
6688                 return;
6689
6690         total_current_hits = 0.0f;
6691         ssp = GET_FIRST(&shipp->subsys_list);
6692         while ( ssp != END_OF_LIST( &shipp->subsys_list ) ) {
6693
6694                 if ( ssp->system_info->type == type ) {
6695                         ssp->current_hits = strength * ssp->system_info->max_hits;
6696                         total_current_hits += ssp->current_hits;
6697                 }
6698                 ssp = GET_NEXT( ssp );
6699         }
6700
6701         // update the objects integrity, needed since we've bashed the strength of a subsysem
6702         diff = total_current_hits - shipp->subsys_info[type].current_hits;
6703         Objects[shipp->objnum].hull_strength += diff;
6704         // fix up the shipp->subsys_info[type] current_hits value
6705         shipp->subsys_info[type].current_hits = total_current_hits;
6706 }
6707
6708 #define         SHIELD_REPAIR_RATE      0.20f                   //      Percent of shield repaired per second.
6709 #define         HULL_REPAIR_RATE                0.15f                   //      Percent of hull repaired per second.
6710 #define         SUBSYS_REPAIR_RATE      0.10f                   // Percent of subsystems repaired per second.
6711
6712 // ==================================================================================
6713 // ship_do_rearm_frame()
6714 //
6715 // function to rearm a ship.  This function gets called from the ai code ai_do_rearm_frame (or
6716 // some function of a similar name).  Returns 1 when ship is fully repaired and rearmed, 0 otherwise
6717 //
6718
6719 #define REARM_NUM_MISSILES_PER_BATCH 4          // how many missiles are dropped in per load sound
6720
6721 int ship_do_rearm_frame( object *objp, float frametime )
6722 {
6723         int                     i, banks_full, subsys_type, subsys_all_ok;
6724         float                   shield_str, repair_delta, repair_allocated;
6725         ship                    *shipp;
6726         ship_weapon     *swp;
6727         ship_info       *sip;
6728         ship_subsys     *ssp;
6729         ai_info         *aip;
6730
6731         shipp = &Ships[objp->instance];
6732         swp = &shipp->weapons;
6733         sip = &Ship_info[shipp->ship_info_index];
6734         aip = &Ai_info[shipp->ai_index];
6735
6736         // AL 10-31-97: Add missing primary weapons to the ship.  This is required since designers
6737         //              want to have ships that start with no primaries, but can get them through
6738         //                                       rearm/repair
6739         if ( swp->num_primary_banks < sip->num_primary_banks ) {
6740                 for ( i = swp->num_primary_banks; i < sip->num_primary_banks; i++ ) {
6741                         swp->primary_bank_weapons[i] = sip->primary_bank_weapons[i];
6742                 }
6743                 swp->num_primary_banks = sip->num_primary_banks;
6744         }
6745         
6746         // AL 12-30-97: Repair broken warp drive
6747         if ( shipp->flags & SF_WARP_BROKEN ) {
6748                 // TODO: maybe do something here like informing player warp is fixed?
6749                 shipp->flags &= ~SF_WARP_BROKEN;
6750         }
6751
6752         // AL 1-16-97: Replenish countermeasures
6753         shipp->cmeasure_count = sip->cmeasure_max;
6754
6755         // Do shield repair here
6756         if ( !(objp->flags & OF_NO_SHIELDS) ) {
6757                 shield_str = get_shield_strength(objp);
6758                 if ( shield_str < sip->shields ) {
6759                         if ( objp == Player_obj ) {
6760                                 player_maybe_start_repair_sound();
6761                         }
6762                         shield_str += sip->shields * frametime * SHIELD_REPAIR_RATE;
6763                         if ( shield_str > sip->shields ) {
6764                                  shield_str = sip->shields;
6765                         }
6766                         set_shield_strength(objp, shield_str);
6767                 }
6768         }
6769
6770         // Repair the ship integrity (subsystems + hull).  This works by applying the repair points
6771         // to the subsystems.  Ships integrity is stored is objp->hull_strength, so that always is 
6772         // incremented by repair_allocated
6773         repair_allocated = sip->initial_hull_strength * frametime * HULL_REPAIR_RATE;
6774
6775 /*
6776         AL 11-24-97: remove increase to hull integrity
6777
6778         objp->hull_strength += repair_allocated;
6779         if ( objp->hull_strength > sip->initial_hull_strength ) {
6780                 repair_allocated -= ( sip->initial_hull_strength - objp->hull_strength);
6781                 objp->hull_strength = sip->initial_hull_strength;
6782         }
6783 */
6784
6785         // check the subsystems of the ship.
6786         subsys_all_ok = 1;
6787         ssp = GET_FIRST(&shipp->subsys_list);
6788         while ( ssp != END_OF_LIST( &shipp->subsys_list ) ) {
6789
6790                 if ( ssp->current_hits < ssp->system_info->max_hits && repair_allocated > 0 ) {
6791                         subsys_all_ok = 0;
6792                         subsys_type = ssp->system_info->type;
6793
6794                         if ( objp == Player_obj ) {
6795                                 player_maybe_start_repair_sound();
6796                         }
6797                         
6798                         repair_delta = ssp->system_info->max_hits - ssp->current_hits;
6799                         if ( repair_delta > repair_allocated ) {
6800                                 repair_delta = repair_allocated;
6801                         }
6802                         repair_allocated -= repair_delta;
6803                         Assert(repair_allocated >= 0.0f);
6804
6805                         // add repair to current strength of single subsystem
6806                         ssp->current_hits += repair_delta;
6807                         if ( ssp->current_hits > ssp->system_info->max_hits ) {
6808                                 ssp->current_hits = ssp->system_info->max_hits;
6809                         }
6810
6811                         // add repair to aggregate strength of subsystems of that type
6812                         shipp->subsys_info[subsys_type].current_hits += repair_delta;
6813                         if ( shipp->subsys_info[subsys_type].current_hits > shipp->subsys_info[subsys_type].total_hits )
6814                                 shipp->subsys_info[subsys_type].current_hits = shipp->subsys_info[subsys_type].total_hits;
6815
6816                         if ( ssp->current_hits > ssp->system_info->max_hits )
6817                                 ssp->current_hits = ssp->system_info->max_hits;
6818
6819                         // check to see if this subsystem was totally non functional before -- if so, then
6820                         // reset the flags
6821                         if ( (ssp->system_info->type == SUBSYSTEM_ENGINE) && (shipp->flags & SF_DISABLED) ) {
6822                                 shipp->flags &= ~SF_DISABLED;
6823                                 ship_reset_disabled_physics(objp, shipp->ship_info_index);
6824                         }
6825                         break;
6826                 }
6827                 ssp = GET_NEXT( ssp );
6828         }
6829
6830         // now deal with rearming the player.  All secondary weapons have a certain rate at which
6831         // they can be rearmed.  We can rearm multiple banks at once.
6832         banks_full = 0;
6833         if ( subsys_all_ok ) {
6834                 for (i = 0; i < swp->num_secondary_banks; i++ ) {
6835                         if ( swp->secondary_bank_ammo[i] < swp->secondary_bank_start_ammo[i] ) {
6836                                 float rearm_time;
6837
6838                                 if ( objp == Player_obj ) {
6839                                         hud_gauge_popup_start(HUD_WEAPONS_GAUGE);
6840                                 }
6841
6842                                 if ( timestamp_elapsed(swp->secondary_bank_rearm_time[i]) ) {
6843
6844                                         // Have to do some gymnastics to play the sound effects properly.  There is a
6845                                         // one time sound effect which is the missile loading start, then for each missile
6846                                         // loaded there is a sound effect.  These are only played for the player.
6847                                         //
6848                                         rearm_time = Weapon_info[swp->secondary_bank_weapons[i]].rearm_rate;
6849                                         if ( aip->rearm_first_missile == TRUE ) {
6850                                                 rearm_time *= 3;
6851                                         }
6852
6853                                         swp->secondary_bank_rearm_time[i] = timestamp( (int)(rearm_time * 1000.f) );
6854
6855                                         // Acutal loading of missiles is preceded by a sound effect which is the missile
6856                                         // loading equipment moving into place
6857                                         if ( aip->rearm_first_missile == TRUE ) {
6858                                                 snd_play_3d( &Snds[SND_MISSILE_START_LOAD], &objp->pos, &View_position );
6859                                                 aip->rearm_first_missile = FALSE;
6860
6861                                         } else {
6862                                                 snd_play_3d( &Snds[SND_MISSILE_LOAD], &objp->pos, &View_position );
6863                                                 if (objp == Player_obj)
6864                                                         joy_ff_play_reload_effect();
6865
6866                                                 swp->secondary_bank_ammo[i] += REARM_NUM_MISSILES_PER_BATCH;
6867                                                 if ( swp->secondary_bank_ammo[i] > swp->secondary_bank_start_ammo[i] ) 
6868                                                         swp->secondary_bank_ammo[i] = swp->secondary_bank_start_ammo[i]; 
6869                                         }
6870                                 }
6871
6872                         } else
6873                                 banks_full++;
6874                 }
6875         } // end if (subsys_all_ok)
6876
6877         if ( banks_full == swp->num_secondary_banks ) {
6878                 aip->rearm_first_missile = TRUE;
6879         }
6880
6881         int shields_full = 0;
6882         if ( (objp->flags & OF_NO_SHIELDS) ) {
6883                 shields_full = 1;
6884         } else {
6885                 if ( get_shield_strength(objp) >= sip->shields ) 
6886                         shields_full = 1;
6887         }
6888
6889         // return 1 if at end of subsystem list, hull damage at 0, and shields full and all secondary banks full.
6890 //      if ( ((ssp = END_OF_LIST(&shipp->subsys_list)) != NULL )&&(objp->hull_strength == sip->initial_hull_strength)&&(shields_full) ) {
6891         if ( subsys_all_ok && shields_full ) {
6892
6893                 if ( objp == Player_obj ) {
6894                         player_stop_repair_sound();
6895                 }
6896
6897                 if (!aip->rearm_release_delay)
6898                         aip->rearm_release_delay = timestamp(1200);
6899
6900                 if ( banks_full == swp->num_secondary_banks ) {
6901
6902                         if ( timestamp_elapsed(aip->rearm_release_delay) )
6903                                 return 1;
6904                 }
6905                 else {
6906                         aip->rearm_release_delay = timestamp(1200);
6907                 }
6908         }
6909
6910         return 0;
6911 }
6912
6913 // function which is used to find a repair ship to repair requester_obj.  the way repair ships will work
6914 // is:
6915 // if repair ship present and available, return pointer to that object.
6916 // If repair ship present and busy, possibly return that object if he can satisfy the request soon enough.
6917 // If repair ship present and busy and cannot satisfy request, return NULL to warp a new one in if below max number
6918 // if no repair ship present, return NULL to force a new one to be warped in.
6919 #define MAX_SUPPORT_SHIPS_PER_TEAM              1
6920
6921 object *ship_find_repair_ship( object *requester_obj )
6922 {
6923         object *objp;
6924         ship *requester_ship;
6925         int     num_support_ships, num_available_support_ships;
6926         float   min_dist = 99999.0f;
6927         object  *nearest_support_ship = NULL;
6928         int             support_ships[MAX_SUPPORT_SHIPS_PER_TEAM];
6929
6930         Assert(requester_obj->type == OBJ_SHIP);
6931         Assert((requester_obj->instance >= 0) && (requester_obj->instance < MAX_OBJECTS));
6932
6933         // if support ships are not allowed, then no support ship can repair!
6934         if ( !is_support_allowed(requester_obj) )
6935                 return NULL;
6936
6937         num_support_ships = 0;
6938         num_available_support_ships = 0;
6939
6940         requester_ship = &Ships[requester_obj->instance];
6941         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
6942                 if ((objp->type == OBJ_SHIP) && !(objp->flags & OF_SHOULD_BE_DEAD)) {
6943                         ship                    *shipp;
6944                         ship_info       *sip;
6945                         float                   dist;
6946
6947                         Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
6948
6949                         shipp = &Ships[objp->instance];
6950                         sip = &Ship_info[shipp->ship_info_index];
6951
6952                         if ( shipp->team != requester_ship->team )
6953                                 continue;
6954
6955                         if ( !(sip->flags & SIF_SUPPORT) )
6956                                 continue;
6957
6958                         // don't deal with dying support ships
6959                         if ( shipp->flags & (SF_DYING | SF_DEPARTING) )
6960                                 continue;
6961
6962                         dist = vm_vec_dist_quick(&objp->pos, &requester_obj->pos);
6963                         support_ships[num_support_ships] = objp-Objects;
6964
6965                         if (!(Ai_info[shipp->ai_index].ai_flags & AIF_REPAIRING)) {
6966                                 num_available_support_ships++;
6967                                 if (dist < min_dist) {
6968                                         min_dist = dist;
6969                                         nearest_support_ship = objp;
6970                                 }
6971                         }
6972
6973                         if ( num_support_ships >= MAX_SUPPORT_SHIPS_PER_TEAM ) {
6974                                 mprintf(("Why is there more than %d support ships in this mission?\n",MAX_SUPPORT_SHIPS_PER_TEAM));
6975                                 break;
6976                         } else {
6977                                 support_ships[num_support_ships] = OBJ_INDEX(objp);
6978                                 num_support_ships++;
6979                         }
6980                 }
6981         }
6982
6983         if (nearest_support_ship != NULL)
6984                 return nearest_support_ship;
6985         else if (num_support_ships >= MAX_SUPPORT_SHIPS_PER_TEAM) {
6986                 Assert(&Objects[support_ships[0]] != NULL);
6987                 return &Objects[support_ships[0]];
6988         } else {
6989                 Assert(num_support_ships < MAX_SUPPORT_SHIPS_PER_TEAM);
6990                 return NULL;
6991         }
6992 }
6993
6994
6995
6996 // -------------------------------------------------------------------------------------------------
6997 // ship_close()
6998 //
6999 // called in game_shutdown() to free malloced memory
7000 //
7001 // NOTE: do not call this function.  It is only called from game_shutdown()
7002 void ship_close()
7003 {
7004         int i;
7005
7006         for (i=0; i<MAX_SHIPS; i++ )    {
7007                 if ( Ships[i].shield_integrity != NULL && Ships[i].objnum != -1 ) {
7008                         free( Ships[i].shield_integrity );
7009                         Ships[i].shield_integrity = NULL;
7010                 }
7011         }
7012
7013         // free memory alloced for subsystem storage
7014         for ( i = 0; i < Num_ship_types; i++ ) {
7015                 if ( Ship_info[i].subsystems != NULL ) {
7016                         free(Ship_info[i].subsystems);
7017                 }
7018         }
7019 }       
7020
7021 // -------------------------------------------------------------------------------------------------
7022 // ship_assign_sound()
7023 //
7024 //      Assign object-linked sound to a particular ship
7025 //
7026 void ship_assign_sound(ship *sp)
7027 {
7028         ship_info       *sip;   
7029         object *objp;
7030         vector engine_pos;
7031         ship_subsys *moveup;
7032
7033         Assert( sp->objnum >= 0 );
7034         if(sp->objnum < 0){
7035                 return;
7036         }
7037         objp = &Objects[sp->objnum];
7038         sip = &Ship_info[sp->ship_info_index];
7039
7040         if ( sip->engine_snd != -1 ) {
7041                 vm_vec_copy_scale(&engine_pos, &objp->orient.v.fvec, -objp->radius/2.0f);               
7042
7043                 obj_snd_assign(sp->objnum, sip->engine_snd, &engine_pos, 1);
7044         }
7045
7046         // if he's got any specific engine subsystems. go for it.       
7047         moveup = GET_FIRST(&sp->subsys_list);
7048         while(moveup != END_OF_LIST(&sp->subsys_list)){
7049                 // check the name of the subsystem
7050                 if(strstr(moveup->system_info->name, "enginelarge")){
7051                         obj_snd_assign(sp->objnum, SND_ENGINE_LOOP_LARGE, &moveup->system_info->pnt, 0);
7052                 } else if(strstr(moveup->system_info->name, "enginehuge")){
7053                         obj_snd_assign(sp->objnum, SND_ENGINE_LOOP_HUGE, &moveup->system_info->pnt, 0);
7054                 }
7055
7056                 // next
7057                 moveup = GET_NEXT(moveup);
7058         }       
7059 }
7060
7061 // -------------------------------------------------------------------------------------------------
7062 // ship_assign_sound_all()
7063 //
7064 //      Assign object-linked sounds to all ships currently in the obj_used_list
7065 //
7066 void ship_assign_sound_all()
7067 {
7068         object *objp;
7069         int idx, has_sounds;
7070
7071         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {           
7072                 if ( objp->type == OBJ_SHIP && Player_obj != objp) {
7073                         has_sounds = 0;
7074
7075                         // check to make sure this guy hasn't got sounds already assigned to him
7076                         for(idx=0; idx<MAX_OBJECT_SOUNDS; idx++){
7077                                 if(objp->objsnd_num[idx] != -1){
7078                                         // skip
7079                                         has_sounds = 1;
7080                                         break;
7081                                 }
7082                         }
7083
7084                         // actually assign the sound
7085                         if(!has_sounds){
7086                                 ship_assign_sound(&Ships[objp->instance]);
7087                         }
7088                 }
7089         }
7090 }
7091
7092
7093 // ---------------------------------------------------------------------------------------
7094 // dcf_set_shield()
7095 //
7096 // Debug console function to set the shield for the player ship
7097 //
7098 DCF(set_shield,"Change player ship shield strength")
7099 {
7100         ship_info       *sip;
7101         
7102         sip = &Ship_info[Ships[Player_obj->instance].ship_info_index];
7103         if ( Dc_command )       {
7104                 dc_get_arg(ARG_FLOAT|ARG_NONE);
7105
7106                 if ( Dc_arg_type & ARG_FLOAT ) {
7107                         if ( Dc_arg_float < 0 ) 
7108                                 Dc_arg_float = 0.0f;
7109                         if ( Dc_arg_float > 1.0 )
7110                                 Dc_arg_float = 1.0f;
7111                         set_shield_strength(Player_obj, Dc_arg_float * sip->shields);
7112                         dc_printf("Shields set to %.2f\n", get_shield_strength(Player_obj) );
7113                 }
7114         }
7115
7116         if ( Dc_help ) {
7117                 dc_printf ("Usage: set_shield [num]\n");
7118                 dc_printf ("[num] --  shield percentage 0.0 -> 1.0 of max\n");
7119                 dc_printf ("with no parameters, displays shield strength\n");
7120                 Dc_status = 0;
7121         }
7122
7123         if ( Dc_status )        {
7124                 dc_printf( "Shields are currently %.2f", get_shield_strength(Player_obj) );
7125         }
7126 }
7127
7128 // ---------------------------------------------------------------------------------------
7129 // dcf_set_hull()
7130 //
7131 // Debug console function to set the hull for the player ship
7132 //
7133 DCF(set_hull, "Change player ship hull strength")
7134 {
7135         ship_info       *sip;
7136         
7137         sip = &Ship_info[Ships[Player_obj->instance].ship_info_index];
7138         if ( Dc_command )       {
7139                 dc_get_arg(ARG_FLOAT|ARG_NONE);
7140
7141                 if ( Dc_arg_type & ARG_FLOAT ) {
7142                         if ( Dc_arg_float < 0 ) 
7143                                 Dc_arg_float = 0.0f;
7144                         if ( Dc_arg_float > 1.0 )
7145                                 Dc_arg_float = 1.0f;
7146                         Player_obj->hull_strength = Dc_arg_float * sip->initial_hull_strength;
7147                         dc_printf("Hull set to %.2f\n", Player_obj->hull_strength );
7148                 }
7149         }
7150
7151         if ( Dc_help ) {
7152                 dc_printf ("Usage: set_hull [num]\n");
7153                 dc_printf ("[num] --  hull percentage 0.0 -> 1.0 of max\n");
7154                 dc_printf ("with no parameters, displays hull strength\n");
7155                 Dc_status = 0;
7156         }
7157
7158         if ( Dc_status )        {
7159                 dc_printf( "Hull is currently %.2f", Player_obj->hull_strength );
7160         }
7161 }
7162
7163 // ---------------------------------------------------------------------------------------
7164 // dcf_set_subsys()
7165 //
7166 // Debug console function to set the strength of a particular subsystem
7167 //
7168 //XSTR:OFF
7169 DCF(set_subsys, "Set the strength of a particular subsystem on player ship" )
7170 {
7171         if ( Dc_command )       {
7172                 dc_get_arg(ARG_STRING);
7173                 if ( !stricmp( Dc_arg, "weapons" ))     {
7174                         dc_get_arg(ARG_FLOAT);
7175                         if ( (Dc_arg_float < 0.0f) || (Dc_arg_float > 1.0f) )   {
7176                                 Dc_help = 1;
7177                         } else {
7178                                 ship_set_subsystem_strength( Player_ship, SUBSYSTEM_WEAPONS, Dc_arg_float );
7179                         } 
7180                 } else if ( !stricmp( Dc_arg, "engine" ))       {
7181                         dc_get_arg(ARG_FLOAT);
7182                         if ( (Dc_arg_float < 0.0f) || (Dc_arg_float > 1.0f) )   {
7183                                 Dc_help = 1;
7184                         } else {
7185                                 ship_set_subsystem_strength( Player_ship, SUBSYSTEM_ENGINE, Dc_arg_float );
7186                                 if ( Dc_arg_float < ENGINE_MIN_STR )    {
7187                                         Player_ship->flags |= SF_DISABLED;                              // add the disabled flag
7188                                 } else {
7189                                         Player_ship->flags &= (~SF_DISABLED);                           // add the disabled flag
7190                                 }
7191                         } 
7192                 } else if ( !stricmp( Dc_arg, "sensors" ))      {
7193                         dc_get_arg(ARG_FLOAT);
7194                         if ( (Dc_arg_float < 0.0f) || (Dc_arg_float > 1.0f) )   {
7195                                 Dc_help = 1;
7196                         } else {
7197                                 ship_set_subsystem_strength( Player_ship, SUBSYSTEM_SENSORS, Dc_arg_float );
7198                         } 
7199                 } else if ( !stricmp( Dc_arg, "communication" ))        {
7200                         dc_get_arg(ARG_FLOAT);
7201                         if ( (Dc_arg_float < 0.0f) || (Dc_arg_float > 1.0f) )   {
7202                                 Dc_help = 1;
7203                         } else {
7204                                 ship_set_subsystem_strength( Player_ship, SUBSYSTEM_COMMUNICATION, Dc_arg_float );
7205                         } 
7206                 } else if ( !stricmp( Dc_arg, "navigation" ))   {
7207                         dc_get_arg(ARG_FLOAT);
7208                         if ( (Dc_arg_float < 0.0f) || (Dc_arg_float > 1.0f) )   {
7209                                 Dc_help = 1;
7210                         } else {
7211                                 ship_set_subsystem_strength( Player_ship, SUBSYSTEM_NAVIGATION, Dc_arg_float );
7212                         } 
7213                 } else if ( !stricmp( Dc_arg, "radar" ))        {
7214                         dc_get_arg(ARG_FLOAT);
7215                         if ( (Dc_arg_float < 0.0f) || (Dc_arg_float > 1.0f) )   {
7216                                 Dc_help = 1;
7217                         } else {
7218                                 ship_set_subsystem_strength( Player_ship, SUBSYSTEM_RADAR, Dc_arg_float );
7219                         } 
7220                 } else {
7221                         // print usage
7222                         Dc_help = 1;
7223                 }
7224         }
7225
7226         if ( Dc_help )  {
7227                 dc_printf( "Usage: set_subsys type X\nWhere X is value between 0 and 1.0, and type can be:\n" );
7228                 dc_printf( "weapons\n" );
7229                 dc_printf( "engine\n" );
7230                 dc_printf( "sensors\n" );
7231                 dc_printf( "communication\n" );
7232                 dc_printf( "navigation\n" );
7233                 dc_printf( "radar\n" );
7234                 Dc_status = 0;  // don't print status if help is printed.  Too messy.
7235         }
7236 }
7237 //XSTR:ON
7238
7239 // console function to toggle whether auto-repair for subsystems is active
7240 #ifndef NDEBUG
7241 DCF_BOOL( auto_repair, Ship_auto_repair );
7242 #endif
7243
7244 // two functions to keep track of counting ships of particular types.  Maybe we should be rolling this
7245 // thing into the stats section??  The first function adds a ship of a particular type to the overall
7246 // count of ships of that type (called from MissionParse.cpp).  The second function adds to the kill total
7247 // of ships of a particular type.  Note that we use the ship_info flags structure member to determine
7248 // what is happening.
7249 void ship_add_ship_type_count( int ship_info_flag, int num )
7250 {
7251         if ( ship_info_flag & SIF_CARGO )
7252                 Ship_counts[SHIP_TYPE_CARGO].total += num;
7253         else if ( (ship_info_flag & SIF_FIGHTER) || (ship_info_flag & SIF_BOMBER) )
7254                 Ship_counts[SHIP_TYPE_FIGHTER_BOMBER].total += num;
7255         else if ( ship_info_flag & SIF_CRUISER )
7256                 Ship_counts[SHIP_TYPE_CRUISER].total += num;
7257         else if ( ship_info_flag & SIF_CORVETTE )
7258                 Ship_counts[SHIP_TYPE_CORVETTE].total += num;
7259         else if ( ship_info_flag & SIF_GAS_MINER )
7260                 Ship_counts[SHIP_TYPE_GAS_MINER].total += num;
7261         else if ( ship_info_flag & SIF_AWACS )
7262                 Ship_counts[SHIP_TYPE_AWACS].total += num;
7263         else if ( ship_info_flag & SIF_FREIGHTER )
7264                 Ship_counts[SHIP_TYPE_FREIGHTER].total += num;
7265         else if ( ship_info_flag & SIF_CAPITAL )
7266                 Ship_counts[SHIP_TYPE_CAPITAL].total += num;
7267         else if ( ship_info_flag & SIF_TRANSPORT )
7268                 Ship_counts[SHIP_TYPE_TRANSPORT].total += num;
7269         else if ( ship_info_flag & SIF_SUPPORT )
7270                 Ship_counts[SHIP_TYPE_REPAIR_REARM].total += num;
7271         else if ( ship_info_flag & SIF_NO_SHIP_TYPE )
7272                 Ship_counts[SHIP_TYPE_NONE].total += num;
7273         else if ( ship_info_flag & SIF_NAVBUOY ) {
7274                 Ship_counts[SHIP_TYPE_NAVBUOY].total += num;
7275         } else if ( ship_info_flag & SIF_SENTRYGUN ) {
7276                 Ship_counts[SHIP_TYPE_SENTRYGUN].total += num;
7277         } else if ( ship_info_flag & SIF_ESCAPEPOD ) {
7278                 Ship_counts[SHIP_TYPE_ESCAPEPOD].total += num;
7279         } else if ( ship_info_flag & SIF_SUPERCAP ) {
7280                 Ship_counts[SHIP_TYPE_SUPERCAP].total += num;
7281         } else if ( ship_info_flag & SIF_DRYDOCK ) {
7282                 Ship_counts[SHIP_TYPE_DRYDOCK].total += num;
7283         } else if ( ship_info_flag & SIF_KNOSSOS_DEVICE){
7284                 Ship_counts[SHIP_TYPE_KNOSSOS_DEVICE].total += num;
7285         }
7286         else
7287                 Int3();         //get allender -- unknown ship type
7288 }
7289
7290 void ship_add_ship_type_kill_count( int ship_info_flag )
7291 {
7292         if ( ship_info_flag & SIF_CARGO )
7293                 Ship_counts[SHIP_TYPE_CARGO].killed++;
7294         else if ( (ship_info_flag & SIF_FIGHTER) || (ship_info_flag & SIF_BOMBER) )
7295                 Ship_counts[SHIP_TYPE_FIGHTER_BOMBER].killed++;
7296         else if ( ship_info_flag & SIF_CRUISER )
7297                 Ship_counts[SHIP_TYPE_CRUISER].killed++;
7298         else if ( ship_info_flag & SIF_CORVETTE )
7299                 Ship_counts[SHIP_TYPE_CORVETTE].killed++;
7300         else if ( ship_info_flag & SIF_AWACS )
7301                 Ship_counts[SHIP_TYPE_AWACS].killed++;
7302         else if ( ship_info_flag & SIF_GAS_MINER )
7303                 Ship_counts[SHIP_TYPE_GAS_MINER].killed++;
7304         else if ( ship_info_flag & SIF_FREIGHTER )
7305                 Ship_counts[SHIP_TYPE_FREIGHTER].killed++;
7306         else if ( ship_info_flag & SIF_CAPITAL )
7307                 Ship_counts[SHIP_TYPE_CAPITAL].killed++;
7308         else if ( ship_info_flag & SIF_TRANSPORT )
7309                 Ship_counts[SHIP_TYPE_TRANSPORT].killed++;
7310         else if ( ship_info_flag & SIF_SUPPORT )
7311                 Ship_counts[SHIP_TYPE_REPAIR_REARM].killed++;
7312         else if ( ship_info_flag & SIF_SENTRYGUN )
7313                 Ship_counts[SHIP_TYPE_SENTRYGUN].killed++;
7314         else if ( ship_info_flag & SIF_ESCAPEPOD )
7315                 Ship_counts[SHIP_TYPE_ESCAPEPOD].killed++;
7316         else if ( ship_info_flag & SIF_NO_SHIP_TYPE )
7317                 Ship_counts[SHIP_TYPE_NONE].killed++;
7318         else if ( ship_info_flag & SIF_SUPERCAP ) 
7319                 Ship_counts[SHIP_TYPE_SUPERCAP].killed++;
7320         else if ( ship_info_flag & SIF_DRYDOCK ) 
7321                 Ship_counts[SHIP_TYPE_DRYDOCK].killed++;
7322         else if ( ship_info_flag & SIF_KNOSSOS_DEVICE )
7323                 Ship_counts[SHIP_TYPE_KNOSSOS_DEVICE].killed++;
7324         else
7325                 Int3();         //get allender -- unknown ship type
7326 }
7327
7328 int ship_query_general_type(int ship)
7329 {
7330         return ship_query_general_type(&Ships[ship]);
7331 }
7332
7333 int ship_query_general_type(ship *shipp)
7334 {
7335         int flags;
7336
7337         flags = Ship_info[shipp->ship_info_index].flags;
7338         switch (flags & SIF_ALL_SHIP_TYPES) {
7339                 case SIF_CARGO:
7340                         return SHIP_TYPE_CARGO;
7341
7342                 case SIF_FIGHTER:
7343                 case SIF_BOMBER:
7344                         return SHIP_TYPE_FIGHTER_BOMBER;
7345
7346                 case SIF_CRUISER:
7347                         return SHIP_TYPE_CRUISER;
7348
7349                 case SIF_FREIGHTER:
7350                         return SHIP_TYPE_FREIGHTER;
7351
7352                 case SIF_CAPITAL:
7353                         return SHIP_TYPE_CAPITAL;
7354
7355                 case SIF_TRANSPORT:
7356                         return SHIP_TYPE_TRANSPORT;
7357
7358                 case SIF_NO_SHIP_TYPE:
7359                         return SHIP_TYPE_NONE;
7360
7361                 case SIF_SUPPORT:
7362                         return SHIP_TYPE_REPAIR_REARM;
7363
7364                 case SIF_NAVBUOY:
7365                         return SHIP_TYPE_NAVBUOY;
7366
7367                 case SIF_SENTRYGUN:
7368                         return SHIP_TYPE_SENTRYGUN;
7369
7370                 case SIF_ESCAPEPOD:
7371                         return SHIP_TYPE_ESCAPEPOD;
7372
7373                 case SIF_SUPERCAP:
7374                         return SHIP_TYPE_SUPERCAP;
7375
7376                 case SIF_DRYDOCK:
7377                         return SHIP_TYPE_DRYDOCK;
7378
7379                 case SIF_CORVETTE:
7380                         return SHIP_TYPE_CORVETTE;
7381                 
7382                 case SIF_AWACS:
7383                         return SHIP_TYPE_AWACS;
7384
7385                 case SIF_GAS_MINER:
7386                         return SHIP_TYPE_GAS_MINER;
7387
7388                 case SIF_KNOSSOS_DEVICE:
7389                         return SHIP_TYPE_KNOSSOS_DEVICE;
7390         }
7391
7392         Error(LOCATION, "Ship type flag is unknown.  Flags value is 0x%x", flags);
7393         return SHIP_TYPE_NONE;
7394 }
7395
7396 // returns true if the docker can (is allowed) to dock with dockee
7397 int ship_docking_valid(int docker, int dockee)
7398 {
7399         int docker_type, dockee_type;
7400
7401         Assert(docker >= 0 && docker < MAX_SHIPS);
7402         Assert(dockee >= 0 && dockee < MAX_SHIPS);
7403         docker_type = ship_query_general_type(docker);
7404         dockee_type = ship_query_general_type(dockee);
7405
7406         // escape pods can dock with transports, freighters, cruisers.
7407         if ( docker_type == SHIP_TYPE_ESCAPEPOD ) {
7408                 if ( (dockee_type == SHIP_TYPE_TRANSPORT) || (dockee_type == SHIP_TYPE_CRUISER) || (dockee_type == SHIP_TYPE_FREIGHTER) || (dockee_type == SHIP_TYPE_DRYDOCK) || (dockee_type == SHIP_TYPE_CORVETTE) || (dockee_type == SHIP_TYPE_GAS_MINER) || (dockee_type == SHIP_TYPE_AWACS)){
7409                         return 1;
7410                 }
7411         }
7412
7413         // docket == freighter
7414         if (docker_type == SHIP_TYPE_FREIGHTER) {
7415                 if ( (dockee_type == SHIP_TYPE_CARGO) || (dockee_type == SHIP_TYPE_CRUISER) || (dockee_type == SHIP_TYPE_CAPITAL) || (dockee_type == SHIP_TYPE_SUPERCAP) || (dockee_type == SHIP_TYPE_DRYDOCK) || (dockee_type == SHIP_TYPE_CORVETTE) || (dockee_type == SHIP_TYPE_GAS_MINER) || (dockee_type == SHIP_TYPE_AWACS)){
7416                         return 1;
7417                 }
7418         }
7419
7420         // docker == cruiser
7421         if ( (docker_type == SHIP_TYPE_CRUISER) || (docker_type == SHIP_TYPE_CORVETTE) || (docker_type == SHIP_TYPE_GAS_MINER) || (docker_type == SHIP_TYPE_AWACS)){
7422                 if ( (dockee_type == SHIP_TYPE_CARGO) || (dockee_type == SHIP_TYPE_CRUISER) || (dockee_type == SHIP_TYPE_CAPITAL) || (dockee_type == SHIP_TYPE_SUPERCAP) || (dockee_type == SHIP_TYPE_DRYDOCK) || (dockee_type == SHIP_TYPE_CORVETTE) || (dockee_type == SHIP_TYPE_GAS_MINER) || (dockee_type == SHIP_TYPE_AWACS)){
7423                         return 1;
7424                 }
7425         }
7426
7427         if (docker_type == SHIP_TYPE_TRANSPORT) {
7428                 if ( (dockee_type == SHIP_TYPE_CARGO) || (dockee_type == SHIP_TYPE_CRUISER)
7429                         || (dockee_type == SHIP_TYPE_FREIGHTER) || (dockee_type == SHIP_TYPE_TRANSPORT)
7430                         || (dockee_type == SHIP_TYPE_CAPITAL) || (dockee_type == SHIP_TYPE_ESCAPEPOD) 
7431                         || (dockee_type == SHIP_TYPE_SUPERCAP) || (dockee_type == SHIP_TYPE_DRYDOCK) || (dockee_type == SHIP_TYPE_CORVETTE) || (dockee_type == SHIP_TYPE_GAS_MINER) || (dockee_type == SHIP_TYPE_AWACS)){
7432                                 return 1;
7433                 }
7434         }
7435
7436         if (docker_type == SHIP_TYPE_REPAIR_REARM) {
7437                 if ((dockee_type == SHIP_TYPE_FIGHTER_BOMBER) || (dockee_type == SHIP_TYPE_STEALTH)){
7438                         return 1;
7439                 }
7440         }
7441
7442         return 0;
7443 }
7444
7445 // function to return a random ship in a starting player wing.  Returns -1 if a suitable
7446 // one cannot be found
7447 // input:       max_dist        =>      OPTIONAL PARAMETER (default value 0.0f) max range ship can be from player
7448 // input:   persona  => OPTIONAL PARAMETER (default to -1) which persona to get
7449 int ship_get_random_player_wing_ship( int flags, float max_dist, int persona_index, int get_first, int multi_team )
7450 {
7451         int i, j, ship_index, count;
7452         int slist[MAX_SHIPS_PER_WING * MAX_STARTING_WINGS], which_one;
7453
7454         // iterate through starting wings of player.  Add ship indices of ships which meet
7455         // given criteria
7456         count = 0;
7457         for (i = 0; i < num_wings; i++ ) {
7458                 int wingnum;
7459
7460                 wingnum = -1;
7461
7462                 // multi-team?
7463                 if(multi_team >= 0){
7464                         if(!stricmp(Wings[i].name, multi_team == 0 ? "alpha" : "zeta")){
7465                                 wingnum = i;
7466                         } else {
7467                                 continue;
7468                         }
7469                 } else {
7470                         // first check for a player starting wing (alpha, beta, gamma)
7471                         for ( j = 0; j < MAX_PLAYER_WINGS; j++ ) {
7472                                 if ( i == Starting_wings[j] ) {
7473                                         wingnum = i;
7474                                         break;
7475                                 }
7476                         }
7477
7478                         // if not found, the delta and epsilon count too
7479                         if ( wingnum == -1 ) {
7480                                 if ( !stricmp(Wings[i].name, NOX("delta")) || !stricmp(Wings[i].name, NOX("epsilon")) ) {
7481                                         wingnum = i;
7482                                 }
7483                         }
7484
7485                         if ( wingnum == -1 ){
7486                                 continue;
7487                         }
7488                 }
7489
7490                 for ( j = 0; j < Wings[wingnum].current_count; j++ ) {
7491                         ship_index = Wings[wingnum].ship_index[j];
7492                         Assert( ship_index != -1 );
7493
7494                         if ( Ships[ship_index].flags & SF_DYING ) {
7495                                 continue;
7496                         }
7497
7498                         // see if ship meets our criterea
7499                         if ( (flags == SHIP_GET_NO_PLAYERS) && (Objects[Ships[ship_index].objnum].flags & OF_PLAYER_SHIP) ){
7500                                 continue;
7501                         }
7502
7503                         // don't process ships on a different team
7504                         if(multi_team < 0){
7505                                 if ( Player_ship->team != Ships[ship_index].team ){
7506                                         continue;
7507                                 }
7508                         }
7509
7510                         // see if ship is within max_dist units
7511                         if ( (max_dist > 0) && (multi_team < 0) ) {
7512                                 float dist;
7513                                 dist = vm_vec_dist_quick(&Objects[Ships[ship_index].objnum].pos, &Player_obj->pos);
7514                                 if ( dist > max_dist ) {
7515                                         continue;
7516                                 }
7517                         }
7518
7519                         // if we should be checking persona's, then don't add ships that don't have the proper persona
7520                         if ( persona_index != -1 ) {
7521                                 if ( Ships[ship_index].persona_index != persona_index ){
7522                                         continue;
7523                                 }
7524                         }
7525
7526                         // return the first ship with correct persona
7527                         if (get_first) {
7528                                 return ship_index;
7529                         }
7530
7531                         slist[count] = ship_index;
7532                         count++;
7533                 }
7534         }
7535
7536         if ( count == 0 ){
7537                 return -1;
7538         }
7539
7540         // now get a random one from the list
7541         which_one = (rand() % count);
7542         ship_index = slist[which_one];
7543
7544         Assert ( Ships[ship_index].objnum != -1 );
7545
7546         return ship_index;
7547 }
7548
7549 // like above function, but returns a random ship in the given wing -- no restrictions
7550 // input:       max_dist        =>      OPTIONAL PARAMETER (default value 0.0f) max range ship can be from player
7551 int ship_get_random_ship_in_wing(int wingnum, int flags, float max_dist, int get_first)
7552 {
7553         int i, ship_index, slist[MAX_SHIPS_PER_WING], count, which_one;
7554
7555         count = 0;
7556         for ( i = 0; i < Wings[wingnum].current_count; i++ ) {
7557                 ship_index = Wings[wingnum].ship_index[i];
7558                 Assert( ship_index != -1 );
7559
7560                 if ( Ships[ship_index].flags & SF_DYING ) {
7561                         continue;
7562                 }
7563
7564                 // see if ship meets our criterea
7565                 if ( (flags == SHIP_GET_NO_PLAYERS) && (Objects[Ships[ship_index].objnum].flags & OF_PLAYER_SHIP) )
7566                         continue;
7567
7568                 // see if ship is within max_dist units
7569                 if ( max_dist > 0 ) {
7570                         float dist;
7571                         dist = vm_vec_dist_quick(&Objects[Ships[ship_index].objnum].pos, &Player_obj->pos);
7572                         if ( dist > max_dist ) {
7573                                 continue;
7574                         }
7575                 }
7576
7577                 // return the first ship in wing
7578                 if (get_first) {
7579                         return ship_index;
7580                 }
7581
7582                 slist[count] = ship_index;
7583                 count++;
7584         }
7585
7586         if ( count == 0 ) {
7587                 return -1;
7588         }
7589
7590         // now get a random one from the list
7591         which_one = (rand() % count);
7592         ship_index = slist[which_one];
7593
7594         Assert ( Ships[ship_index].objnum != -1 );
7595
7596         return ship_index;
7597 }
7598
7599
7600 // this function returns a random index into the Ship array of a ship of the given team
7601 // cargo containers are not counted as ships for the purposes of this function.  Why???
7602 // because now it is only used for getting a random ship for a message and cargo containers
7603 // can't send mesages.  This function is an example of kind of bad coding :-(
7604 // input:       max_dist        =>      OPTIONAL PARAMETER (default value 0.0f) max range ship can be from player
7605 int ship_get_random_team_ship( int team, int flags, float max_dist )
7606 {
7607         int num, which_one;
7608         object *objp, *obj_list[MAX_SHIPS];
7609
7610         // for any allied, go through the ships list and find all of the ships on that team
7611         num = 0;
7612         for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
7613                 if ( objp->type != OBJ_SHIP )
7614                         continue;
7615
7616                 // series of conditionals one per line for easy reading
7617                 // don't process ships on wrong team
7618                 // don't process cargo's or navbuoys
7619                 // don't process player ships if flags are set
7620                 if ( Ships[objp->instance].team != team )
7621                         continue;
7622                 else if ( Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_NOT_FLYABLE )
7623                         continue;
7624                 else if ( (flags == SHIP_GET_NO_PLAYERS) && (objp->flags & OF_PLAYER_SHIP) )
7625                         continue;
7626                 else if ( (flags == SHIP_GET_ONLY_PLAYERS) && !(objp->flags & OF_PLAYER_SHIP) )
7627                         continue;
7628
7629                 if ( Ships[objp->instance].flags & SF_DYING ) {
7630                         continue;
7631                 }
7632
7633                 // see if ship is within max_dist units
7634                 if ( max_dist > 0 ) {
7635                         float dist;
7636                         dist = vm_vec_dist_quick(&objp->pos, &Player_obj->pos);
7637                         if ( dist > max_dist ) {
7638                                 continue;
7639                         }
7640                 }
7641
7642                 obj_list[num] = objp;
7643                 num++;
7644         }
7645
7646         if ( num == 0 )
7647                 return -1;
7648
7649         which_one = (rand() % num);
7650         objp = obj_list[which_one];
7651
7652         Assert ( objp->instance != -1 );
7653
7654         return objp->instance;
7655 }
7656
7657 // -----------------------------------------------------------------------
7658 // ship_secondary_bank_has_ammo()
7659 //
7660 // check if currently selected secondary bank has ammo
7661 //
7662 // input:       shipnum =>      index into Ships[] array for ship to check
7663 //
7664 int ship_secondary_bank_has_ammo(int shipnum)
7665 {
7666         ship_weapon     *swp;
7667
7668         Assert(shipnum >= 0 && shipnum < MAX_SHIPS);
7669         swp = &Ships[shipnum].weapons;
7670         
7671         if ( swp->current_secondary_bank == -1 )
7672                 return 0;
7673
7674         Assert(swp->current_secondary_bank >= 0 && swp->current_secondary_bank < MAX_SECONDARY_BANKS );
7675         if ( swp->secondary_bank_ammo[swp->current_secondary_bank] <= 0 )
7676                 return 0;
7677
7678         return 1;
7679 }
7680
7681 // see if there is enough engine power to allow the ship to warp
7682 // returns 1 if ship is able to warp, otherwise return 0
7683 int ship_can_warp(ship *sp)
7684 {
7685         float   engine_str;
7686
7687         engine_str = ship_get_subsystem_strength( sp, SUBSYSTEM_ENGINE );
7688         // Note that ship can always warp at lowest skill level
7689         if ( (Game_skill_level > 0) && (engine_str >= SHIP_MIN_ENGINES_TO_WARP) ){
7690                 return 1;
7691         } else {
7692                 return 0;
7693         }
7694 }
7695
7696
7697 // Calculate the normal vector from a subsystem position and it's first path point
7698 // input:       sp      =>      pointer to ship that is parent of subsystem
7699 //                              ss =>   pointer to subsystem of interest
7700 //                              norm    => output parameter... vector from subsys to first path point
7701 //
7702 //      exit:           0       =>      a valid vector was placed in norm
7703 //                              !0      => an path normal could not be calculated
7704 //                              
7705 int ship_return_subsys_path_normal(ship *sp, ship_subsys *ss, vector *gsubpos, vector *norm)
7706 {
7707         if ( ss->system_info->path_num >= 0 ) {
7708                 polymodel       *pm;
7709                 model_path      *mp;
7710                 vector          *path_point;
7711                 vector          gpath_point;
7712                 pm = model_get(sp->modelnum);
7713                 mp = &pm->paths[ss->system_info->path_num];
7714                 if ( mp->nverts >= 2 ) {
7715 //                      path_point = &mp->verts[mp->nverts-1].pos;
7716                         path_point = &mp->verts[0].pos;
7717                         // get path point in world coords
7718                         vm_vec_unrotate(&gpath_point, path_point, &Objects[sp->objnum].orient);
7719                         vm_vec_add2(&gpath_point, &Objects[sp->objnum].pos);
7720                         // get unit vector pointing from subsys pos to first path point
7721                         vm_vec_normalized_dir(norm, &gpath_point, gsubpos);
7722                         return 0;
7723                 }
7724         }
7725         return 1;
7726 }
7727
7728
7729 //      Determine if the subsystem can be viewed from eye_pos.  The method is to check where the
7730 // vector from eye_pos to the subsystem hits the ship.  If distance from the hit position and
7731 // the center of the subsystem is within a range (currently the subsystem radius) it is considered
7732 // in view (return true).  If not in view, return false.
7733 //
7734 // input:       objp            =>              object that is the ship with the subsystem on it
7735 //                              subsys  =>              pointer to the subsystem of interest
7736 //                              eye_pos =>              world coord for the eye looking at the subsystem
7737 //                              subsys_pos                      =>      world coord for the center of the subsystem of interest
7738 //                              do_facing_check =>      OPTIONAL PARAMETER (default value is 1), do a dot product check to see if subsystem fvec is facing
7739 //                                                                                      towards the eye position        
7740 //                              dot_out =>              OPTIONAL PARAMETER, output parameter, will return dot between subsys fvec and subsys_to_eye_vec
7741 //                                                                      (only filled in if do_facing_check is true)
7742 //                              vec_out =>              OPTIONAL PARAMETER, vector from eye_pos to absolute subsys_pos.  (only filled in if do_facing_check is true)
7743 int ship_subsystem_in_sight(object* objp, ship_subsys* subsys, vector *eye_pos, vector* subsys_pos, int do_facing_check, float *dot_out, vector *vec_out)
7744 {
7745         float           dist, dot;
7746         mc_info mc;
7747         vector  terminus, eye_to_pos, subsys_fvec, subsys_to_eye_vec;
7748
7749         if (objp->type != OBJ_SHIP)
7750                 return 0;
7751
7752         // See if we are at least facing the subsystem
7753         if ( do_facing_check ) {
7754                 if ( ship_return_subsys_path_normal(&Ships[objp->instance], subsys, subsys_pos, &subsys_fvec) ) {
7755                         // non-zero return value means that we couldn't generate a normal from path info... so use inaccurate method
7756                         vm_vec_normalized_dir(&subsys_fvec, subsys_pos, &objp->pos);
7757                 }
7758
7759                 vm_vec_normalized_dir(&subsys_to_eye_vec, eye_pos, subsys_pos);
7760                 dot = vm_vec_dot(&subsys_fvec, &subsys_to_eye_vec);
7761                 if ( dot_out ) {
7762                         *dot_out = dot;
7763                 }
7764
7765                 if (vec_out) {
7766                         *vec_out = subsys_to_eye_vec;
7767                         vm_vec_negate(vec_out);
7768                 }
7769
7770                 if ( dot < 0 )
7771                         return 0;
7772         }
7773
7774         // See if ray from eye to subsystem actually hits close enough to the subsystem position
7775         vm_vec_normalized_dir(&eye_to_pos, subsys_pos, eye_pos);
7776         vm_vec_scale_add(&terminus, eye_pos, &eye_to_pos, 100000.0f);
7777
7778         ship_model_start(objp);
7779
7780         mc.model_num = Ships[objp->instance].modelnum;                  // Fill in the model to check
7781         mc.orient = &objp->orient;                                                                              // The object's orientation
7782         mc.pos = &objp->pos;                                                                                            // The object's position
7783         mc.p0 = eye_pos;                                                                                                        // Point 1 of ray to check
7784         mc.p1 = &terminus;                                                                                              // Point 2 of ray to check
7785         mc.flags = MC_CHECK_MODEL;      
7786
7787         model_collide(&mc);
7788
7789         ship_model_stop(objp);
7790
7791         if ( !mc.num_hits ) {
7792                 return 0;
7793         }       
7794
7795         // determine if hitpos is close enough to subsystem
7796         dist = vm_vec_dist(&mc.hit_point_world, subsys_pos);
7797
7798         if ( dist <= subsys->system_info->radius ) {
7799                 return 1;
7800         }
7801         
7802         return 0;
7803 }
7804
7805 // try to find a subsystem matching 'type' inside the ship, and that is 
7806 // not destroyed.  If cannot find one, return NULL.
7807 ship_subsys *ship_return_next_subsys(ship *shipp, int type, vector *attacker_pos)
7808 {
7809         ship_subsys     *ssp;
7810
7811         Assert ( type >= 0 && type < SUBSYSTEM_MAX );
7812
7813         // If aggregate total is 0, that means no subsystem is alive of that type
7814         if ( shipp->subsys_info[type].total_hits <= 0.0f )
7815                 return NULL;
7816
7817         // loop through all the subsystems, if we find a match that has some strength, return it
7818         ssp = ship_get_best_subsys_to_attack(shipp, type, attacker_pos);
7819
7820         return ssp;
7821 }
7822
7823 // Return the shield strength in the quadrant hit on hit_objp, based on global hitpos
7824 //
7825 // input:       hit_objp        =>      object pointer to ship getting hit
7826 //                              hitpos  => global position of impact
7827 //
7828 // exit:                strength of shields in the quadrant that was hit as a percentage, between 0 and 1.0
7829 //
7830 // Assumes: that hitpos is a valid global hit position
7831 float ship_quadrant_shield_strength(object *hit_objp, vector *hitpos)
7832 {
7833         int                     quadrant_num, i;
7834         float                   max_quadrant;
7835         vector          tmpv1, tmpv2;
7836
7837         // If ship doesn't have shield mesh, then return
7838         if ( hit_objp->flags & OF_NO_SHIELDS ) {
7839                 return 0.0f;
7840         }
7841
7842         // Check if all the shield quadrants are all already 0, if so return 0
7843         for ( i = 0; i < 4; i++ ) {
7844                 if ( hit_objp->shields[i] > 0 )
7845                         break;
7846         }
7847
7848         if ( i == 4 ) {
7849                 return 0.0f;
7850         }
7851
7852         // convert hitpos to position in model coordinates
7853         vm_vec_sub(&tmpv1, hitpos, &hit_objp->pos);
7854         vm_vec_rotate(&tmpv2, &tmpv1, &hit_objp->orient);
7855         quadrant_num = get_quadrant(&tmpv2);
7856         //nprintf(("Alan","Quadrant hit: %d\n", quadrant_num));
7857
7858         if ( quadrant_num < 0 )
7859                 quadrant_num = 0;
7860
7861         max_quadrant = Ship_info[Ships[hit_objp->instance].ship_info_index].shields / 4.0f;
7862         if ( max_quadrant <= 0 ) {
7863                 return 0.0f;
7864         }
7865
7866         Assert(hit_objp->shields[quadrant_num] <= max_quadrant);
7867
7868         return hit_objp->shields[quadrant_num]/max_quadrant;
7869 }
7870
7871 // Determine if a ship is threatened by any dumbfire projectiles (laser or missile)
7872 // input:       sp      =>      pointer to ship that might be threatened
7873 // exit:                0 =>    no dumbfire threats
7874 //                              1 =>    at least one dumbfire threat
7875 //
7876 // NOTE: Currently this function is only called periodically from the HUD code for the 
7877 //       player ship.
7878 int ship_dumbfire_threat(ship *sp)
7879 {
7880         if ( (Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER) ) {
7881                 return 0;
7882         }
7883
7884         if (ai_endangered_by_weapon(&Ai_info[sp->ai_index]) > 0) {
7885                 return 1;
7886         } 
7887
7888         return 0;
7889 }
7890
7891 // Return !0 if there is a missile in the air homing on shipp
7892 int ship_has_homing_missile_locked(ship *shipp)
7893 {
7894         object          *locked_objp, *A;
7895         weapon          *wp;
7896         weapon_info     *wip;
7897         missile_obj     *mo;
7898
7899         Assert(shipp->objnum >= 0 && shipp->objnum < MAX_OBJECTS);
7900         locked_objp = &Objects[shipp->objnum];
7901
7902         // check for currently locked missiles (highest precedence)
7903         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
7904                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
7905                 A = &Objects[mo->objnum];
7906
7907                 if (A->type != OBJ_WEAPON)
7908                         continue;
7909
7910                 Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
7911                 wp = &Weapons[A->instance];
7912                 wip = &Weapon_info[wp->weapon_info_index];
7913
7914                 if ( wip->subtype != WP_MISSILE )
7915                         continue;
7916
7917                 if ( !(wip->wi_flags & (WIF_HOMING_ASPECT|WIF_HOMING_HEAT) ) )
7918                         continue;
7919
7920                 if (wp->homing_object == locked_objp) {
7921                         return 1;
7922                 }
7923         }       // end for 
7924
7925         return 0;
7926 }
7927
7928 // Return !0 if there is some ship attempting to lock onto shipp
7929 int ship_is_getting_locked(ship *shipp)
7930 {
7931         ship_obj        *so;
7932         object  *objp;
7933         ai_info *aip;
7934
7935         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
7936                 objp = &Objects[so->objnum];
7937                 aip = &Ai_info[Ships[objp->instance].ai_index];
7938
7939                 if ( aip->target_objnum == shipp->objnum ) {
7940                         if ( aip->aspect_locked_time > 0.1f ) {
7941                                 float dist, wep_range;
7942                                 dist = vm_vec_dist_quick(&objp->pos, &Objects[shipp->objnum].pos);
7943                                 wep_range = ship_get_secondary_weapon_range(&Ships[objp->instance]);
7944                                 if ( wep_range > dist ) {
7945                                         nprintf(("Alan","AI ship is seeking lock\n"));
7946                                         return 1;
7947                                 }
7948                         }
7949                 }
7950         }
7951
7952         return 0;
7953 }
7954
7955 // Determine if a ship is threatened by attempted lock or actual lock
7956 // input:       sp      =>      pointer to ship that might be threatened
7957 // exit:                0 =>    no lock threats of any kind
7958 //                              1 =>    at least one attempting lock (no actual locks)
7959 //                              2 =>    at least one lock (possible other attempting locks)
7960 //
7961 // NOTE: Currently this function is only called periodically from the HUD code for the 
7962 //       player ship.
7963 int ship_lock_threat(ship *sp)
7964 {
7965         if ( ship_has_homing_missile_locked(sp) ) {
7966                 return 2;
7967         }
7968
7969         if ( ship_is_getting_locked(sp) ) {
7970                 return 1;
7971         }
7972
7973         return 0;
7974 }
7975
7976 // converts a bitmask, such as 0x08, into the bit number this would be (3 in this case)
7977 // NOTE: Should move file to something like Math_utils.
7978 int bitmask_2_bitnum(int num)
7979 {
7980         int i;
7981
7982         for (i=0; i<32; i++)
7983                 if (num & (1 << i))
7984                         return i;
7985
7986         return -1;
7987 }
7988
7989 // Get a text description of a ships orders. 
7990 //
7991 //      input:  outbuf  =>              buffer to hold orders string
7992 //                              sp                      =>              ship pointer to extract orders from
7993 //
7994 // exit:                NULL            =>              printable orders are not applicable
7995 //                              non-NULL        =>              pointer to string that was passed in originally
7996 //
7997 // This function is called from HUD code to get a text description
7998 // of what a ship's orders are.  Feel free to use this function if 
7999 // it suits your needs for something.
8000 //
8001 char *ship_return_orders(char *outbuf, ship *sp)
8002 {
8003         ai_info *aip;
8004         ai_goal *aigp;
8005         char            *order_text;
8006         
8007         Assert(sp->ai_index >= 0);
8008         aip = &Ai_info[sp->ai_index];
8009
8010         // The active goal is always in the first element of aip->goals[]
8011         aigp = &aip->goals[0];
8012
8013         if ( aigp->ai_mode < 0 ) 
8014                 return NULL;
8015
8016         order_text = Ai_goal_text(bitmask_2_bitnum(aigp->ai_mode));
8017         if ( order_text == NULL )
8018                 return NULL;
8019
8020         strcpy(outbuf, order_text);
8021         switch (aigp->ai_mode ) {
8022
8023                 case AI_GOAL_FORM_ON_WING:
8024                 case AI_GOAL_GUARD_WING:
8025                 case AI_GOAL_CHASE_WING:
8026                         if ( aigp->ship_name ) {
8027                                 strcat(outbuf, aigp->ship_name);
8028                                 strcat(outbuf, XSTR( " Wing", 494));
8029                         } else {
8030                                 strcpy(outbuf, XSTR( "no orders", 495));
8031                         }
8032                         break;
8033         
8034                 case AI_GOAL_CHASE:
8035                 case AI_GOAL_DOCK:
8036                 case AI_GOAL_UNDOCK:
8037                 case AI_GOAL_GUARD:
8038                 case AI_GOAL_DISABLE_SHIP:
8039                 case AI_GOAL_DISARM_SHIP:
8040                 case AI_GOAL_EVADE_SHIP:
8041                 case AI_GOAL_REARM_REPAIR:
8042                         if ( aigp->ship_name ) {
8043                                 strcat(outbuf, aigp->ship_name);
8044                         } else {
8045                                 strcpy(outbuf, XSTR( "no orders", 495));
8046                         }
8047                         break;
8048
8049                 case AI_GOAL_DESTROY_SUBSYSTEM: {
8050                         char name[NAME_LENGTH];
8051                         if ( aip->targeted_subsys != NULL ) {
8052                                 sprintf(outbuf, XSTR( "atk %s %s", 496), aigp->ship_name, hud_targetbox_truncate_subsys_name(aip->targeted_subsys->system_info->name));
8053                                 strcat(outbuf, name);
8054                         } else {
8055                                 strcpy(outbuf, XSTR( "no orders", 495) );
8056                         }
8057                         break;
8058                 }
8059
8060                 case AI_GOAL_WAYPOINTS:
8061                 case AI_GOAL_WAYPOINTS_ONCE:
8062                         // don't do anything, all info is in order_text
8063                         break;
8064
8065                 default:
8066                         return NULL;
8067         }
8068
8069         return outbuf;
8070 }
8071
8072 // return the amount of time until ship reaches it's goal (in MM:SS format)
8073 //      input:  outbuf  =>              buffer to hold orders string
8074 //                              sp                      =>              ship pointer to extract orders from
8075 //
8076 // exit:                NULL            =>              printable orders are not applicable
8077 //                              non-NULL        =>              pointer to string that was passed in originally
8078 //
8079 // This function is called from HUD code to get a text description
8080 // of what a ship's orders are.  Feel free to use this function if 
8081 // it suits your needs for something.
8082 char *ship_return_time_to_goal(char *outbuf, ship *sp)
8083 {
8084         ai_info *aip;
8085         int             time, seconds, minutes;
8086         float           dist = 0.0f;
8087         object  *objp;  
8088         float           min_speed;
8089
8090         objp = &Objects[sp->objnum];
8091         aip = &Ai_info[sp->ai_index];
8092
8093         min_speed = objp->phys_info.speed;
8094
8095         if ( aip->mode == AIM_WAYPOINTS ) {
8096                 waypoint_list   *wpl;
8097                 min_speed = 0.9f * sp->current_max_speed;
8098                 if (aip->wp_list >= 0) {
8099                         wpl = &Waypoint_lists[aip->wp_list];
8100                         dist += vm_vec_dist_quick(&objp->pos, &wpl->waypoints[aip->wp_index]);
8101                         for (int i=aip->wp_index; i<wpl->count-1; i++) {
8102                                 dist += vm_vec_dist_quick(&wpl->waypoints[i], &wpl->waypoints[i+1]);
8103                         }
8104                 }
8105
8106                 if ( dist < 1.0f) {
8107                         return NULL;
8108                 }       
8109
8110                 if ( (Objects[sp->objnum].phys_info.speed <= 0) || (sp->current_max_speed <= 0.0f) ) {
8111                         time = -1;
8112                 } else {
8113                         float   speed;
8114
8115                         speed = objp->phys_info.speed;
8116
8117                         if (speed < min_speed)
8118                                 speed = min_speed;
8119                         time = fl2i(dist/speed);
8120                 }
8121
8122         } else if ( (aip->mode == AIM_DOCK) && (aip->submode < AIS_DOCK_4) ) {
8123                 time = hud_support_get_dock_time( OBJ_INDEX(objp) );
8124         } else {
8125                 // don't return anytime for time to except for waypoints and actual docking.
8126                 return NULL;
8127         }
8128
8129 /*
8130         } else if ( aip->goal_objnum >= 0 ) {
8131                 dist = vm_vec_dist_quick(&Objects[aip->goal_objnum].pos, &objp->pos);
8132                 min_speed = sip->max_speed/4.0f;
8133         } else if ( aip->target_objnum >= 0 ) {
8134                 if ( aip->guard_objnum < 0 ) {
8135                         dist = vm_vec_dist_quick(&Objects[aip->target_objnum].pos, &objp->pos);
8136                         min_speed = sip->max_speed/4.0f;
8137                 }
8138         }
8139 */
8140
8141         if ( time >= 0 ) {
8142                 minutes = time/60;
8143                 seconds = time%60;
8144                 if ( minutes > 99 ) {
8145                         minutes = 99;
8146                         seconds = 99;
8147                 }
8148                 sprintf(outbuf, NOX("%02d:%02d"), minutes, seconds);
8149         } else {
8150                 sprintf( outbuf, XSTR( "Unknown", 497) );
8151         }
8152
8153         return outbuf;
8154 }
8155
8156
8157 // Called to check if any AI ships might reveal the cargo of any cargo containers.
8158 //
8159 // This is called once a frame, but a global timer 'Ship_cargo_check_timer' will limit this
8160 // function to being called every SHIP_CARGO_CHECK_INTERVAL ms.  I think that should be sufficient.
8161 //
8162 // NOTE: This function uses CARGO_REVEAL_DISTANCE from the HUD code... which is a multiple of
8163 //       the ship radius that is used to determine when cargo is detected.  AI ships do not 
8164 //       have to have the ship targeted to reveal cargo.  The player is ignored in this function.
8165 #define SHIP_CARGO_CHECK_INTERVAL       1000
8166 void ship_check_cargo_all()
8167 {
8168         object  *cargo_objp;
8169         ship_obj        *cargo_so, *ship_so;
8170         ship            *cargo_sp, *ship_sp;
8171         float           dist_squared, limit_squared;
8172
8173         // I don't want to do this check every frame, so I made a global timer to limit check to
8174         // every SHIP_CARGO_CHECK_INTERVAL ms.
8175         if ( !timestamp_elapsed(Ship_cargo_check_timer) ) {
8176                 return;
8177         } else {
8178                 Ship_cargo_check_timer = timestamp(SHIP_CARGO_CHECK_INTERVAL);
8179         }
8180
8181         // Check all friendly fighter/bombers against all non-friendly cargo containers that don't have
8182         // cargo revealed
8183
8184         // for now just locate a captial ship on the same team:
8185         cargo_so = GET_FIRST(&Ship_obj_list);
8186         while(cargo_so != END_OF_LIST(&Ship_obj_list)){
8187                 cargo_sp = &Ships[Objects[cargo_so->objnum].instance];
8188                 if ( (Ship_info[cargo_sp->ship_info_index].flags & SIF_CARGO) && !(cargo_sp->team & TEAM_FRIENDLY) ) {
8189                         
8190                         // If the cargo is revealed, continue on to next hostile cargo
8191                         if ( cargo_sp->flags & SF_CARGO_REVEALED ) {
8192                                 goto next_cargo;
8193                         }
8194
8195                         // check against friendly fighter/bombers + cruiser/freighter/transport
8196                         // IDEA: could cull down to fighter/bomber if we want this to run a bit quicker
8197                         for ( ship_so=GET_FIRST(&Ship_obj_list); ship_so != END_OF_LIST(&Ship_obj_list); ship_so=GET_NEXT(ship_so) ) {
8198                                 ship_sp = &Ships[Objects[ship_so->objnum].instance];
8199                                 // only consider friendly ships
8200                                 if ( !(ship_sp->team & TEAM_FRIENDLY) ) {
8201                                         continue;
8202                                 }
8203
8204                                 // ignore the player
8205                                 if ( ship_so->objnum == OBJ_INDEX(Player_obj) ) {
8206                                         continue;
8207                                 }
8208
8209                                 // if this ship is a small or big ship
8210                                 if ( Ship_info[ship_sp->ship_info_index].flags & (SIF_SMALL_SHIP|SIF_BIG_SHIP) ) {
8211                                         cargo_objp = &Objects[cargo_sp->objnum];
8212                                         // use square of distance, faster than getting real distance (which will use sqrt)
8213                                         dist_squared = vm_vec_dist_squared(&cargo_objp->pos, &Objects[ship_sp->objnum].pos);
8214                                         limit_squared = (cargo_objp->radius+CARGO_RADIUS_DELTA)*(cargo_objp->radius+CARGO_RADIUS_DELTA);
8215                                         if ( dist_squared <= max(limit_squared, CARGO_REVEAL_MIN_DIST*CARGO_REVEAL_MIN_DIST) ) {
8216                                                 ship_do_cargo_revealed( cargo_sp );
8217                                                 break;  // break out of for loop, move on to next hostile cargo
8218                                         }
8219                                 }
8220                         } // end for
8221                 }
8222 next_cargo:
8223                 cargo_so = GET_NEXT(cargo_so);
8224         } // end while
8225 }
8226
8227
8228 // Maybe warn player about this attacking ship.  This is called once per frame, and the
8229 // information about the closest attacking ship comes for free, since this function is called
8230 // from HUD code which has already determined the closest enemy attacker and the distance.
8231 //
8232 // input:       enemy_sp        =>      ship pointer to the TEAM_ENEMY ship attacking the player
8233 //                              dist            =>      the distance of the enemy to the player
8234 //
8235 // NOTE: there are no filters on enemy_sp, so it could be any ship type
8236 //
8237 #define PLAYER_ALLOW_WARN_INTERVAL              60000           // minimum time between warnings
8238 #define PLAYER_CHECK_WARN_INTERVAL              300             // how often we check for warnings
8239 #define PLAYER_MAX_WARNINGS                             2                       // max number of warnings player can receive in a mission
8240 #define PLAYER_MIN_WARN_DIST                            100             // minimum distance attacking ship can be from player and still allow warning
8241 #define PLAYER_MAX_WARN_DIST                            1000            // maximum distance attacking ship can be from plyaer and still allow warning
8242
8243 void ship_maybe_warn_player(ship *enemy_sp, float dist)
8244 {
8245         float           fdot; //, rdot, udot;
8246         vector  vec_to_target;
8247         int             msg_type; //, on_right;
8248
8249         // First check if the player has reached the maximum number of warnings for a mission
8250         if ( Player->warn_count >= PLAYER_MAX_WARNINGS ) {
8251                 return;
8252         }
8253
8254         // Check if enough time has elapsed since last warning, if not - leave
8255         if ( !timestamp_elapsed(Player->allow_warn_timestamp) ) {
8256                 return;
8257         }
8258
8259         // Check to see if check timer has elapsed.  Necessary, since we don't want to check each frame
8260         if ( !timestamp_elapsed(Player->check_warn_timestamp ) ) {
8261                 return;
8262         }
8263         Player->check_warn_timestamp = timestamp(PLAYER_CHECK_WARN_INTERVAL);
8264
8265         // only allow warnings if within a certain distance range
8266         if ( dist < PLAYER_MIN_WARN_DIST || dist > PLAYER_MAX_WARN_DIST ) {
8267                 return;
8268         }
8269
8270         // only warn if a fighter or bomber is attacking the player
8271         if ( !(Ship_info[enemy_sp->ship_info_index].flags & SIF_SMALL_SHIP) ) {
8272                 return;
8273         }
8274
8275         // get vector from player to target
8276         vm_vec_normalized_dir(&vec_to_target, &Objects[enemy_sp->objnum].pos, &Eye_position);
8277
8278         // ensure that enemy fighter is oriented towards player
8279         fdot = vm_vec_dot(&Objects[enemy_sp->objnum].orient.v.fvec, &vec_to_target);
8280         if ( fdot > -0.7 ) {
8281                 return;
8282         }
8283
8284         fdot = vm_vec_dot(&Player_obj->orient.v.fvec, &vec_to_target);
8285
8286         msg_type = -1;
8287
8288         // check if attacking ship is on six.  return if not far enough behind player.
8289         if ( fdot > -0.7 )
8290                 return;
8291
8292         msg_type = MESSAGE_CHECK_6;
8293 /*
8294                 goto warn_player_done;
8295         }
8296
8297         // see if attacking ship is in front of ship (then do nothing)
8298         if ( fdot > 0.7 ) {
8299                 return;
8300         }
8301
8302         // ok, ship is on 3 or 9.  Find out which
8303         rdot = vm_vec_dot(&Player_obj->orient.v.rvec, &vec_to_target);
8304         if ( rdot > 0 ) {
8305                 on_right = 1;
8306         } else {
8307                 on_right = 0;
8308         }
8309
8310         // now determine if ship is high or low
8311         udot = vm_vec_dot(&Player_obj->orient.v.uvec, &vec_to_target);
8312         if ( udot < -0.8 ) {
8313                 return; // if ship is attacking from directly below, no warning given
8314         }
8315
8316         if ( udot > 0 ) {
8317                 if ( on_right ) {
8318                         msg_type = MESSAGE_CHECK_3_HIGH;
8319                 } else {
8320                         msg_type = MESSAGE_CHECK_9_HIGH;
8321                 }
8322         } else {
8323                 if ( on_right ) {
8324                         msg_type = MESSAGE_CHECK_3_LOW;
8325                 } else {
8326                         msg_type = MESSAGE_CHECK_9_LOW;
8327                 }
8328         }
8329
8330 warn_player_done:
8331 */
8332
8333         if ( msg_type != -1 ) {
8334                 int ship_index;
8335
8336                 // multiplayer tvt - this is client side.
8337                 if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM) && (Net_player != NULL)){
8338                         ship_index = ship_get_random_player_wing_ship( SHIP_GET_NO_PLAYERS, 0.0f, -1, 0, Net_player->p_info.team );
8339                 } else {
8340                         ship_index = ship_get_random_player_wing_ship( SHIP_GET_NO_PLAYERS );
8341                 }
8342
8343                 if ( ship_index >= 0 ) {
8344                         // multiplayer - make sure I just send to myself
8345                         if(Game_mode & GM_MULTIPLAYER){
8346                                 message_send_builtin_to_player(msg_type, &Ships[ship_index], MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, MY_NET_PLAYER_NUM, -1);
8347                         } else {
8348                                 message_send_builtin_to_player(msg_type, &Ships[ship_index], MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
8349                         }
8350                         Player->allow_warn_timestamp = timestamp(PLAYER_ALLOW_WARN_INTERVAL);
8351                         Player->warn_count++;
8352 //                      nprintf(("Alan","Warning given for ship name: %s\n", enemy_sp->ship_name));
8353                 }
8354         }
8355 }
8356
8357 // player has just killed a ship, maybe offer send a 'good job' message
8358 #define PLAYER_MAX_PRAISES                                      10                      // max number of praises player can receive in a mission
8359 void ship_maybe_praise_player(ship *deader_sp)
8360 {
8361         if ( myrand()&1 ) {
8362                 return;
8363         }
8364
8365         // First check if the player has reached the maximum number of praises for a mission
8366         if ( Player->praise_count >= PLAYER_MAX_PRAISES ) {
8367                 return;
8368         }
8369
8370         // Check if enough time has elapsed since last praise, if not - leave
8371         if ( !timestamp_elapsed(Player->allow_praise_timestamp) ) {
8372                 return;
8373         }
8374
8375         if ( !(Player_ship->team & TEAM_FRIENDLY) ) {
8376                 return;
8377         }
8378
8379         if ( deader_sp->team == Player_ship->team ) {   // only praise if killing an enemy!
8380                 return;
8381         }
8382
8383         // don't praise the destruction of navbuoys, cargo or other non-flyable ship types
8384         if ( Ship_info[deader_sp->ship_info_index].flags & SIF_NOT_FLYABLE ) {
8385                 return;
8386         }
8387
8388         // There is already a praise pending
8389         if ( Player->praise_delay_timestamp ) {
8390                 return;
8391         }
8392
8393         // We don't want to praise the player right away.. it is more realistic to wait a moment
8394         Player->praise_delay_timestamp = timestamp_rand(1000, 2000);
8395 }
8396
8397 // player has just killed a ship, maybe offer send a 'good job' message
8398 #define PLAYER_ASK_HELP_INTERVAL                        60000           // minimum time between praises
8399 #define PLAYER_MAX_ASK_HELP                             10                      // max number of warnings player can receive in a mission
8400 #define ASK_HELP_SHIELD_PERCENT                 0.1             // percent shields at which ship will ask for help
8401 #define ASK_HELP_HULL_PERCENT                           0.3             // percent hull at which ship will ask for help
8402 #define AWACS_HELP_HULL_HI                                      0.75            // percent hull at which ship will ask for help
8403 #define AWACS_HELP_HULL_LOW                             0.25            // percent hull at which ship will ask for help
8404
8405 // -----------------------------------------------------------------------------
8406 void awacs_maybe_ask_for_help(ship *sp, int multi_team_filter)
8407 {
8408         object *objp;
8409         int message = -1;
8410         objp = &Objects[sp->objnum];
8411
8412         if ( objp->hull_strength < ( (AWACS_HELP_HULL_LOW + 0.01f *(static_rand(objp-Objects) & 5)) * Ship_info[sp->ship_info_index].initial_hull_strength) ) {
8413                 // awacs ship below 25 + (0-4) %
8414                 if (!(sp->awacs_warning_flag & AWACS_WARN_25)) {
8415                         message = MESSAGE_AWACS_25;
8416                         sp->awacs_warning_flag |=  AWACS_WARN_25;
8417                 }
8418         } else if ( objp->hull_strength < ( (AWACS_HELP_HULL_HI + 0.01f*(static_rand(objp-Objects) & 5)) * Ship_info[sp->ship_info_index].initial_hull_strength) ) {
8419                 // awacs ship below 75 + (0-4) %
8420                 if (!(sp->awacs_warning_flag & AWACS_WARN_75)) {
8421                         message = MESSAGE_AWACS_75;
8422                         sp->awacs_warning_flag |=  AWACS_WARN_75;
8423                 }
8424         }
8425
8426         if (message >= 0) {
8427                 message_send_builtin_to_player(message, sp, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, multi_team_filter);
8428                 Player->allow_ask_help_timestamp = timestamp(PLAYER_ASK_HELP_INTERVAL);
8429                 Player->ask_help_count++;
8430         }
8431 }
8432
8433 // -----------------------------------------------------------------------------
8434 void ship_maybe_ask_for_help(ship *sp)
8435 {
8436         object *objp;
8437         int multi_team_filter = -1;
8438
8439         // First check if the player has reached the maximum number of ask_help's for a mission
8440         if ( Player->ask_help_count >= PLAYER_MAX_ASK_HELP ) {
8441                 return;
8442         }
8443
8444         // Check if enough time has elapsed since last help request, if not - leave
8445         if ( !timestamp_elapsed(Player->allow_ask_help_timestamp) ) {
8446                 return;
8447         }
8448
8449         if ( !(Player_ship->team & TEAM_FRIENDLY) ) {
8450                 return;
8451         }
8452
8453         Assert(sp->team & TEAM_FRIENDLY );
8454         objp = &Objects[sp->objnum];
8455
8456         if ( objp->flags & OF_PLAYER_SHIP )     {// don't let the player ask for help!
8457                 return;
8458         }
8459
8460         // determine team filter if TvT
8461         if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)){
8462                 if(sp->team == TEAM_FRIENDLY){
8463                         multi_team_filter = 0;
8464                 } else if(sp->team == TEAM_HOSTILE){
8465                         multi_team_filter = 1;
8466                 }
8467         }
8468
8469         // handle awacs ship as a special case
8470         if (Ship_info[sp->ship_info_index].flags & SIF_HAS_AWACS) {
8471                 awacs_maybe_ask_for_help(sp, multi_team_filter);
8472                 return;
8473         }
8474
8475         // for now, only have wingman ships request help
8476         if ( !(sp->flags & SF_FROM_PLAYER_WING) ) {
8477                 return;
8478         }
8479
8480         // first check if hull is at a critical level
8481         if ( objp->hull_strength < ASK_HELP_HULL_PERCENT * Ship_info[sp->ship_info_index].initial_hull_strength ) {
8482                 goto play_ask_help;
8483         }
8484
8485         // check if shields are near critical level
8486         if ( objp->flags & OF_NO_SHIELDS ) {
8487                 return; // no shields on ship, no don't check shield levels
8488         }
8489
8490         if ( get_shield_strength(objp) > (ASK_HELP_SHIELD_PERCENT * Ship_info[sp->ship_info_index].shields) ) {
8491                 return;
8492         }
8493
8494 play_ask_help:
8495
8496         Assert(Ship_info[sp->ship_info_index].flags & (SIF_FIGHTER|SIF_BOMBER) );       // get Alan
8497         message_send_builtin_to_player(MESSAGE_HELP, sp, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, multi_team_filter);
8498         Player->allow_ask_help_timestamp = timestamp(PLAYER_ASK_HELP_INTERVAL);
8499
8500         if ( timestamp_until(Player->allow_scream_timestamp) < 15000 ) {
8501                 Player->allow_scream_timestamp = timestamp(15000);      // prevent overlap with death message
8502         }
8503
8504         Player->ask_help_count++;
8505 }
8506
8507 // The player has just entered death roll, maybe have wingman mourn the loss of the player
8508 void ship_maybe_lament()
8509 {
8510         int ship_index;
8511
8512         // no. because in multiplayer, its funny
8513         if(Game_mode & GM_MULTIPLAYER){
8514                 return;
8515         }
8516
8517         if ( rand()%4 == 0 ) {
8518                 ship_index = ship_get_random_player_wing_ship( SHIP_GET_NO_PLAYERS );
8519                 if ( ship_index >= 0 ) {
8520                         message_send_builtin_to_player(MESSAGE_PLAYED_DIED, &Ships[ship_index], MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
8521                 }
8522         }
8523 }
8524
8525 #define PLAYER_SCREAM_INTERVAL          60000
8526 #define PLAYER_MAX_SCREAMS                              10
8527
8528 // play a death scream for a ship
8529 void ship_scream(ship *sp)
8530 {
8531         int multi_team_filter = -1;
8532
8533         // bogus
8534         if(sp == NULL){
8535                 return;
8536         }
8537
8538         // multiplayer tvt
8539         if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)){
8540                 if(sp->team == TEAM_FRIENDLY){
8541                         multi_team_filter = 0;
8542                 } else if(sp->team == TEAM_HOSTILE){
8543                         multi_team_filter = 1;
8544                 }
8545         }
8546
8547         message_send_builtin_to_player(MESSAGE_WINGMAN_SCREAM, sp, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, multi_team_filter);
8548         Player->allow_scream_timestamp = timestamp(PLAYER_SCREAM_INTERVAL);
8549         Player->scream_count++;
8550         sp->flags |= SF_SHIP_HAS_SCREAMED;
8551
8552         // prevent overlap with help messages
8553         if ( timestamp_until(Player->allow_ask_help_timestamp) < 15000 ) {
8554                 Player->allow_ask_help_timestamp = timestamp(15000);    // prevent overlap with death message
8555         }
8556 }
8557
8558 // ship has just died, maybe play a scream.
8559 //
8560 // NOTE: this is only called for ships that are in a player wing (and not player ship)
8561 void ship_maybe_scream(ship *sp)
8562 {
8563         if ( rand()&1 )
8564                 return;
8565
8566         // First check if the player has reached the maximum number of screams for a mission
8567         if ( Player->scream_count >= PLAYER_MAX_SCREAMS ) {
8568                 return;
8569         }
8570
8571         // if on different teams (i.e. team v. team games in multiplayer), no scream
8572         if ( sp->team != Player_ship->team ) {
8573                 return;
8574         }
8575
8576         // Check if enough time has elapsed since last scream, if not - leave
8577         if ( !timestamp_elapsed(Player->allow_scream_timestamp) ) {
8578                 return;
8579         }
8580
8581         ship_scream(sp);
8582 }
8583
8584 // maybe tell player that we've requested a support ship
8585 #define PLAYER_REQUEST_REPAIR_MSG_INTERVAL      240000
8586 void ship_maybe_tell_about_rearm(ship *sp)
8587 {
8588         if ( !timestamp_elapsed(Player->request_repair_timestamp) ) {
8589                 return;
8590         }
8591
8592         if ( !(Player_ship->team & TEAM_FRIENDLY) ) {
8593                 return;
8594         }
8595
8596         // AL 1-4-98:   If ship integrity is low, tell player you want to get repaired.  Otherwise, tell
8597         // the player you want to get re-armed.
8598
8599         int message_type = -1;
8600         int heavily_damaged = 0;
8601         if ( Objects[sp->objnum].hull_strength/Ship_info[sp->ship_info_index].initial_hull_strength < 0.4 ) {
8602                 heavily_damaged = 1;
8603         }
8604
8605         if ( heavily_damaged || (sp->flags & SF_DISABLED) ) {
8606                 message_type = MESSAGE_REPAIR_REQUEST;
8607         } else {
8608                 int i;
8609                 ship_weapon *swp;
8610
8611                 swp = &sp->weapons;
8612                 for ( i = 0; i < swp->num_secondary_banks; i++ ) {
8613                         if (swp->secondary_bank_start_ammo[i] > 0) {
8614                                 if ( swp->secondary_bank_ammo[i]/swp->secondary_bank_start_ammo[i] < 0.5f ) {
8615                                         message_type = MESSAGE_REARM_REQUEST;
8616                                         break;
8617                                 }
8618                         }
8619                 }
8620         }
8621
8622         int multi_team_filter = -1;
8623
8624         // multiplayer tvt
8625         if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)){
8626                 if(sp->team == TEAM_FRIENDLY){
8627                         multi_team_filter = 0;
8628                 } else if(sp->team == TEAM_HOSTILE){
8629                         multi_team_filter = 1;
8630                 }
8631         }
8632
8633         if ( message_type >= 0 ) {
8634                 if ( rand() & 1 ) {
8635                         message_send_builtin_to_player(message_type, sp, MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, -1, multi_team_filter);
8636                 }
8637                 Player->request_repair_timestamp = timestamp(PLAYER_REQUEST_REPAIR_MSG_INTERVAL);
8638         }
8639 }
8640
8641 // The current primary weapon or link status for a ship has changed.. notify clients if multiplayer
8642 //
8643 // input:       sp                      =>      pointer to ship that modified primaries
8644 void ship_primary_changed(ship *sp)
8645 {
8646 #if 0
8647         ship_weapon     *swp;
8648
8649         // we only need to deal with multiplayer issues for now, so bail it not multiplayer
8650         if ( !(Game_mode & GM_MULTIPLAYER) )
8651                 return;
8652
8653         Assert(sp);
8654         swp = &sp->weapons;
8655
8656         
8657         if ( MULTIPLAYER_MASTER )
8658                 send_ship_weapon_change( sp, MULTI_PRIMARY_CHANGED, swp->current_primary_bank, (sp->flags & SF_PRIMARY_LINKED)?1:0 );
8659 #endif
8660 }
8661
8662 // The current secondary weapon or dual-fire status for a ship has changed.. notify clients if multiplayer
8663 //
8664 // input:       sp                                      =>      pointer to ship that modified secondaries
8665 void ship_secondary_changed(ship *sp)
8666 {
8667 #if 0
8668         ship_weapon     *swp;
8669
8670         // we only need to deal with multiplayer issues for now, so bail it not multiplayer
8671         if ( !(Game_mode & GM_MULTIPLAYER) ){
8672                 return;
8673         }
8674
8675         Assert(sp);
8676         swp = &sp->weapons;
8677
8678         if ( MULTIPLAYER_MASTER )
8679                 send_ship_weapon_change( sp, MULTI_SECONDARY_CHANGED, swp->current_secondary_bank, (sp->flags & SF_SECONDARY_DUAL_FIRE)?1:0 );
8680 #endif
8681 }
8682
8683 int ship_get_SIF(ship *shipp)
8684 {
8685         return Ship_info[shipp->ship_info_index].flags;
8686 }
8687
8688 int ship_get_SIF(int sh)
8689 {
8690         return Ship_info[Ships[sh].ship_info_index].flags;
8691 }
8692
8693 int ship_get_by_signature(int signature)
8694 {
8695         ship_obj *so;
8696                 
8697         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {          
8698                 // if we found a matching ship object signature
8699                 if((Objects[so->objnum].signature == signature) && (Objects[so->objnum].type == OBJ_SHIP)){
8700                         return Objects[so->objnum].instance;
8701                 }
8702         }
8703
8704         // couldn't find the ship
8705         return -1;
8706 }
8707
8708 // function which gets called when the cargo of a ship is revealed.  Happens at two different locations
8709 // (at least when this function was written), one for the player, and one for AI ships.  Need to send stuff
8710 // to clients in multiplayer game.
8711 void ship_do_cargo_revealed( ship *shipp, int from_network )
8712 {
8713         // don't do anything if we already know the cargo
8714         if ( shipp->flags & SF_CARGO_REVEALED ){
8715                 return;
8716         }
8717         
8718         nprintf(("Network", "Revealing cargo for %s\n", shipp->ship_name));
8719
8720         // send the packet if needed
8721         if ( (Game_mode & GM_MULTIPLAYER) && !from_network ){
8722                 send_cargo_revealed_packet( shipp );            
8723         }
8724
8725         shipp->flags |= SF_CARGO_REVEALED;
8726         shipp->time_cargo_revealed = Missiontime;       
8727
8728         // if the cargo is something other than "nothing", then make a log entry
8729         if ( stricmp(Cargo_names[shipp->cargo1 & CARGO_INDEX_MASK], NOX("nothing")) ){
8730                 mission_log_add_entry(LOG_CARGO_REVEALED, shipp->ship_name, NULL, (shipp->cargo1 & CARGO_INDEX_MASK) );
8731         }       
8732 }
8733
8734 void ship_do_cap_subsys_cargo_revealed( ship *shipp, ship_subsys *subsys, int from_network )
8735 {
8736         if ( subsys->subsys_cargo_revealed ) {
8737                 return;
8738         }
8739
8740         
8741         nprintf(("Network", "Revealing cap ship subsys cargo for %s\n", shipp->ship_name));
8742
8743         // send the packet if needed
8744         if ( (Game_mode & GM_MULTIPLAYER) && !from_network ){
8745                 int subsystem_index = ship_get_index_from_subsys(subsys, shipp->objnum);
8746                 send_subsystem_cargo_revealed_packet( shipp, subsystem_index );         
8747         }
8748
8749         subsys->subsys_cargo_revealed = 1;
8750
8751         // if the cargo is something other than "nothing", then make a log entry
8752         if ( (subsys->subsys_cargo_name > 0) && stricmp(Cargo_names[subsys->subsys_cargo_name], NOX("nothing")) ){
8753                 mission_log_add_entry(LOG_CAP_SUBSYS_CARGO_REVEALED, shipp->ship_name, subsys->system_info->name, subsys->subsys_cargo_name );
8754         }       
8755 }
8756
8757
8758 // Return the range of the currently selected secondary weapon
8759 // NOTE: If there is no missiles left in the current bank, range returned is 0
8760 float ship_get_secondary_weapon_range(ship *shipp)
8761 {
8762         float srange=0.0f;
8763
8764         ship_weapon     *swp;
8765         swp = &shipp->weapons;
8766         if ( swp->current_secondary_bank >= 0 ) {
8767                 weapon_info     *wip;
8768                 int bank=swp->current_secondary_bank;
8769                 wip = &Weapon_info[swp->secondary_bank_weapons[bank]];
8770                 if ( swp->secondary_bank_ammo[bank] > 0 ) {
8771                         srange = wip->max_speed * wip->lifetime;
8772                 }
8773         }
8774
8775         return srange;
8776 }
8777
8778 // Determine the number of secondary ammo units (missile/bomb) allowed max for a ship
8779 //
8780 int get_max_ammo_count_for_bank(int ship_class, int bank, int ammo_type)
8781 {
8782         float capacity, size;
8783
8784         capacity = (float) Ship_info[ship_class].secondary_bank_ammo_capacity[bank];
8785         size = (float) Weapon_info[ammo_type].cargo_size;
8786         return (int) (capacity / size);
8787 }
8788
8789
8790
8791 // Page in bitmaps for all the ships in this level
8792 void ship_page_in()
8793 {
8794         int i,j;
8795         int num_subsystems_needed = 0;
8796
8797         int ship_class_used[MAX_SHIP_TYPES];
8798
8799         // Mark all ship classes as not used
8800         for (i=0; i<MAX_SHIP_TYPES; i++ )       {
8801                 ship_class_used[i] = 0;
8802         }
8803
8804         // Mark any support ship types as used
8805         // 
8806         for (i=0; i<Num_ship_types; i++ )       {
8807                 if ( Ship_info[i].flags & SIF_SUPPORT ) {
8808                         nprintf(( "Paging", "Found support ship '%s'\n", Ship_info[i].name ));
8809                         ship_class_used[i]++;
8810
8811                         num_subsystems_needed += Ship_info[i].n_subsystems;
8812                 }
8813         }
8814         
8815         // Mark any ships in the mission as used
8816         //
8817         for (i=0; i<MAX_SHIPS; i++)     {
8818                 if (Ships[i].objnum > -1)       {
8819                         nprintf(( "Paging","Found ship '%s'\n", Ships[i].ship_name ));
8820                         ship_class_used[Ships[i].ship_info_index]++;
8821
8822                         num_subsystems_needed += Ship_info[Ships[i].ship_info_index].n_subsystems;
8823                 }
8824         }
8825
8826         // Mark any ships that might warp in in the future as used
8827         //
8828         p_object * p_objp;
8829         for( p_objp = GET_FIRST(&ship_arrival_list); p_objp != END_OF_LIST(&ship_arrival_list); p_objp = GET_NEXT(p_objp) )     {
8830                 nprintf(( "Paging","Found future arrival ship '%s'\n", p_objp->name ));
8831                 ship_class_used[p_objp->ship_class]++;
8832
8833                 num_subsystems_needed += Ship_info[p_objp->ship_class].n_subsystems;
8834         }
8835
8836
8837         // Page in all the ship classes that are used on this level
8838         //
8839         int num_ship_types_used = 0;
8840
8841         for (i=0; i<MAX_SHIP_TYPES; i++ )       {
8842                 if ( ship_class_used[i]  )      {
8843                         ship_info *si = &Ship_info[i];
8844
8845                         num_ship_types_used++;
8846
8847                         // Page in the small hud icons for each ship
8848                         {
8849                                 extern void hud_ship_icon_page_in(ship_info *sip);
8850
8851                                 hud_ship_icon_page_in(si);
8852
8853                         }
8854
8855                         // See if this model was previously loaded by another ship
8856                         int model_previously_loaded = -1;
8857                         int ship_previously_loaded = -1;
8858                         for (j=0; j<MAX_SHIP_TYPES; j++ )       {
8859                                 if ( (Ship_info[j].modelnum > -1) && !stricmp(si->pof_file, Ship_info[j].pof_file) )    {
8860                                         // Model already loaded
8861                                         model_previously_loaded = Ship_info[j].modelnum;
8862                                         ship_previously_loaded = j;
8863                                         break;
8864                                 }
8865                         }
8866
8867                         // If the model is previously loaded...
8868                         if ( model_previously_loaded > -1 )     {
8869
8870                                 // If previously loaded model isn't the same ship class...)
8871                                 if ( ship_previously_loaded != i )      {
8872
8873                                         // update the model number.
8874                                         si->modelnum = model_previously_loaded;
8875
8876                                         for ( j = 0; j < si->n_subsystems; j++ )        {
8877                                                 si->subsystems[j].model_num = -1;
8878                                         }
8879
8880                                         ship_copy_subsystem_fixup(si);
8881
8882                                         #ifndef NDEBUG
8883                                                 for ( j = 0; j < si->n_subsystems; j++ )        {
8884                                                         Assert( si->subsystems[j].model_num == si->modelnum );
8885                                                 }
8886                                         #endif
8887
8888                                 } else {
8889                                         // Just to be safe (I mean to check that my code works...)
8890                                         Assert( si->modelnum > -1 );
8891                                         Assert( si->modelnum == model_previously_loaded );
8892
8893                                         #ifndef NDEBUG
8894                                                 for ( j = 0; j < si->n_subsystems; j++ )        {
8895                                                         Assert( si->subsystems[j].model_num == si->modelnum );
8896                                                 }
8897                                         #endif
8898                                 }
8899                         } else {
8900                                 // Model not loaded... so load it and page in its textures
8901                                 si->modelnum = model_load(si->pof_file, si->n_subsystems, &si->subsystems[0]);
8902
8903                                 Assert( si->modelnum > -1 );
8904
8905                                 // Verify that all the subsystem model numbers are updated
8906                                 #ifndef NDEBUG
8907                                         for ( j = 0; j < si->n_subsystems; j++ )        {
8908                                                 Assert( si->subsystems[j].model_num == si->modelnum );  // JAS
8909                                         }
8910                                 #endif
8911
8912                         }
8913         
8914                 }
8915         }
8916
8917         for (i=0; i<MAX_SHIP_TYPES; i++ )       {
8918                 if ( ship_class_used[i]  )      {
8919                         ship_info *si = &Ship_info[i];
8920
8921                         if ( si->modelnum > -1 )        {
8922                                 polymodel *pm = model_get(si->modelnum);
8923                                 
8924                                 nprintf(( "Paging", "Paging in textures for model '%s'\n", si->pof_file ));
8925
8926                                 for (j=0; j<pm->n_textures; j++ )       {
8927                                         int bitmap_num = pm->original_textures[j];
8928
8929                                         if ( bitmap_num > -1 )  {
8930                                                 // if we're in Glide (and maybe later with D3D), use nondarkening textures
8931                                                 if(gr_screen.mode == GR_GLIDE){
8932                                                         bm_page_in_nondarkening_texture( bitmap_num );
8933                                                 } else {
8934                                                         bm_page_in_texture( bitmap_num );
8935                                                 }
8936                                         }
8937                                 }
8938
8939                         } else {
8940                                 nprintf(( "Paging", "Couldn't load model '%s'\n", si->pof_file ));
8941                         }
8942                 }
8943         }
8944
8945         nprintf(( "Paging", "There are %d ship classes used in this mission.\n", num_ship_types_used ));
8946         mprintf(( "This mission requires %d Ship_subsystems. See #define MAX_SHIP_SUBOBJECTS.\n", num_subsystems_needed ));
8947
8948         // JAS: If you hit this, then MAX_SHIP_SUBOBJECTS is set too low.
8949         // I added this code in to detect an error that wasn't getting detected any other
8950         // way.
8951         Assert(num_subsystems_needed < MAX_SHIP_SUBOBJECTS );   
8952
8953         // Page in the thruster effects
8954         //
8955
8956         // Make sure thrusters are loaded
8957         if ( !Thrust_anim_inited )      ship_init_thrusters();
8958
8959         for ( i = 0; i < NUM_THRUST_ANIMS; i++ ) {
8960                 thrust_anim     *ta = &Thrust_anims[i];
8961                 for ( j = 0; j<ta->num_frames; j++ )    {
8962                         bm_page_in_texture( ta->first_frame + j );
8963                 }
8964         }
8965
8966         for ( i = 0; i < NUM_THRUST_GLOW_ANIMS; i++ ) {
8967                 thrust_anim     *ta = &Thrust_glow_anims[i];
8968                 // glows are really not anims
8969                 bm_page_in_texture( ta->first_frame );
8970         }
8971
8972         // page in insignia bitmaps
8973         if(Game_mode & GM_MULTIPLAYER){
8974                 for(i=0; i<MAX_PLAYERS; i++){
8975                         if(MULTI_CONNECTED(Net_players[i]) && (Net_players[i].player != NULL) && (Net_players[i].player->insignia_texture >= 0)){
8976                                 bm_page_in_xparent_texture(Net_players[i].player->insignia_texture);
8977                         }
8978                 }
8979         } else {
8980                 if((Player != NULL) && (Player->insignia_texture >= 0)){
8981                         bm_page_in_xparent_texture(Player->insignia_texture);
8982                 }
8983         }
8984 }
8985
8986 // function to return true if support ships are allowed in the mission for the given object.
8987 //      In single player, must be friendly and not Shivan.
8988 //      In multiplayer -- to be coded by Mark Allender after 5/4/98 -- MK, 5/4/98
8989 int is_support_allowed(object *objp)
8990 {
8991         if (The_mission.disallow_support){
8992                 return 0;
8993         }
8994
8995         if ( Game_mode & GM_NORMAL ) {
8996                 if (Ships[objp->instance].team != TEAM_FRIENDLY){
8997                         return 0;
8998                 }
8999
9000                 switch (Ship_info[Ships[objp->instance].ship_info_index].species) {
9001                 case SPECIES_TERRAN:
9002                         break;
9003                 case SPECIES_VASUDAN:
9004                         break;
9005                 case SPECIES_SHIVAN:
9006                         return 0;
9007                 case SPECIES_NONE:
9008                         break;
9009                 }
9010
9011                 return 1;
9012         } else {
9013                 // multiplayer version behaves differently.  Depending on mode:
9014                 // 1) coop mode -- only available to friendly
9015                 // 2) team v team mode -- availble to either side
9016                 // 3) dogfight -- never
9017
9018                 if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
9019                         return 0;
9020                 }
9021
9022                 if ( IS_MISSION_MULTI_COOP ) {
9023                         if ( Ships[objp->instance].team != TEAM_FRIENDLY ){
9024                                 return 0;
9025                         }
9026                 }
9027
9028                 return 1;
9029         }
9030
9031 }
9032
9033 // return ship index
9034 int ship_get_random_ship()
9035 {
9036         int num_ships;
9037         int rand_ship;
9038         int idx;
9039         ship_obj *so;
9040
9041         // get the # of ships on the list
9042         num_ships = ship_get_num_ships();
9043
9044         // get a random ship on the list
9045         rand_ship = (int)frand_range(0.0f, (float)(num_ships - 1));
9046         if(rand_ship < 0){
9047                 rand_ship = 0;
9048         } 
9049         if(rand_ship > num_ships){
9050                 rand_ship = num_ships;
9051         }
9052
9053         // find this guy
9054         so = GET_FIRST(&Ship_obj_list);
9055         for(idx=0; idx<rand_ship; idx++) {
9056                 so = GET_NEXT(so);
9057         }
9058
9059         return Objects[so->objnum].instance;
9060 }
9061
9062 // forcible jettison cargo from a ship
9063 void ship_jettison_cargo(ship *shipp)
9064 {
9065         object *objp;
9066         object *cargo_objp;
9067         vector impulse, pos;
9068
9069         // make sure we are docked with a valid object
9070         if(shipp->objnum < 0){
9071                 return;
9072         }
9073         objp = &Objects[shipp->objnum];
9074         if(Ai_info[shipp->ai_index].dock_objnum == -1){
9075                 return;
9076         }
9077         if(Objects[Ai_info[shipp->ai_index].dock_objnum].type != OBJ_SHIP){
9078                 Int3();
9079                 return;
9080         }
9081         if(Ai_info[Ships[Objects[Ai_info[shipp->ai_index].dock_objnum].instance].ai_index].dock_objnum != OBJ_INDEX(objp)){
9082                 return;
9083         }
9084         cargo_objp = &Objects[Ai_info[shipp->ai_index].dock_objnum];
9085
9086         // undock the objects
9087         ai_do_objects_undocked_stuff( objp, cargo_objp );
9088         
9089         // physics stuff
9090         vm_vec_sub(&pos, &cargo_objp->pos, &objp->pos);
9091         impulse = pos;
9092         vm_vec_scale(&impulse, 100.0f);
9093         vm_vec_normalize(&pos);
9094
9095         // whack the ship
9096         physics_apply_whack(&impulse, &pos, &cargo_objp->phys_info, &cargo_objp->orient, cargo_objp->phys_info.mass);
9097 }
9098
9099 float ship_get_exp_damage(object* objp)
9100 {
9101         Assert(objp->type == OBJ_SHIP);
9102         float damage; 
9103
9104         ship *shipp = &Ships[objp->instance];
9105
9106         if (shipp->special_exp_index != -1) {
9107                 damage = (float) atoi(Sexp_variables[shipp->special_exp_index+DAMAGE].text);
9108         } else {
9109                 damage = Ship_info[shipp->ship_info_index].damage;
9110         }
9111
9112         return damage;
9113 }
9114
9115 int ship_get_exp_propagates(ship *sp)
9116 {
9117         return Ship_info[sp->ship_info_index].explosion_propagates;
9118 }
9119
9120 float ship_get_exp_outer_rad(object *ship_objp)
9121 {
9122         float outer_rad;
9123         Assert(ship_objp->type == OBJ_SHIP);
9124
9125         if (Ships[ship_objp->instance].special_exp_index == -1) {
9126                 outer_rad = Ship_info[Ships[ship_objp->instance].ship_info_index].outer_rad;
9127         } else {
9128                 outer_rad = (float) atoi(Sexp_variables[Ships[ship_objp->instance].special_exp_index+OUTER_RAD].text);
9129         }
9130
9131         return outer_rad;
9132 }
9133
9134 int valid_cap_subsys_cargo_list(char *subsys)
9135 {
9136         if (strstr(subsys, "nav")
9137                 || strstr(subsys, "comm")
9138                 || strstr(subsys, "engines")
9139                 || strstr(subsys, "fighter")    // fighter bays
9140                 || strstr(subsys, "sensors")
9141                 || strstr(subsys, "weapons")) {
9142
9143                 return 1;
9144         }
9145
9146         return 0;
9147 }
9148
9149 // determine turret status of a given subsystem, returns 0 for no turret, 1 for "fixed turret", 2 for "rotating" turret
9150 int ship_get_turret_type(ship_subsys *subsys)
9151 {
9152         // not a turret at all
9153         if(subsys->system_info->type != SUBSYSTEM_TURRET){
9154                 return 0;
9155         }
9156
9157         // if it rotates
9158         if(subsys->system_info->turret_turning_rate > 0.0f){
9159                 return 2;
9160         }
9161
9162         // if its fixed
9163         return 1;
9164 }
9165
9166 ship_subsys *ship_get_subsys(ship *shipp, char *subsys_name)
9167 {
9168         ship_subsys *lookup;
9169
9170         // sanity checks
9171         if((shipp == NULL) || (subsys_name == NULL)){
9172                 return NULL;
9173         }
9174
9175         lookup = GET_FIRST(&shipp->subsys_list);
9176         while(lookup != END_OF_LIST(&shipp->subsys_list)){
9177                 // turret
9178                 if(!strcmp(lookup->system_info->subobj_name, subsys_name)){
9179                         return lookup;
9180                 }
9181
9182                 // next
9183                 lookup = GET_NEXT(lookup);
9184         }
9185
9186         // didn't find it
9187         return NULL;
9188 }
9189
9190 // returns 0 if no conflict, 1 if conflict, -1 on some kind of error with wing struct
9191 int wing_has_conflicting_teams(int wing_index)
9192 {
9193         int first_team, idx;
9194
9195         // sanity checks
9196         Assert((wing_index >= 0) && (wing_index < num_wings) && (Wings[wing_index].current_count > 0));
9197         if((wing_index < 0) || (wing_index >= num_wings) || (Wings[wing_index].current_count <= 0)){
9198                 return -1;
9199         }
9200
9201         // check teams
9202         Assert(Wings[wing_index].ship_index[0] >= 0);
9203         if(Wings[wing_index].ship_index[0] < 0){
9204                 return -1;
9205         }
9206         first_team = Ships[Wings[wing_index].ship_index[0]].team;
9207         for(idx=1; idx<Wings[wing_index].current_count; idx++){
9208                 // more sanity checks
9209                 Assert(Wings[wing_index].ship_index[idx] >= 0);
9210                 if(Wings[wing_index].ship_index[idx] < 0){
9211                         return -1;
9212                 }
9213
9214                 // if we've got a team conflict
9215                 if(first_team != Ships[Wings[wing_index].ship_index[idx]].team){
9216                         return 1;
9217                 }
9218         }
9219
9220         // no conflict
9221         return 0;
9222 }
9223
9224 // get the team of a reinforcement item
9225 int ship_get_reinforcement_team(int r_index)
9226 {
9227         int wing_index;
9228         p_object *objp;
9229
9230         // sanity checks
9231         Assert((r_index >= 0) && (r_index < Num_reinforcements));
9232         if((r_index < 0) || (r_index >= Num_reinforcements)){
9233                 return -1;
9234         }
9235
9236         // if the reinforcement is a ship       
9237         objp = mission_parse_get_arrival_ship( Reinforcements[r_index].name );
9238         if(objp != NULL){
9239                 return objp->team;
9240         }
9241
9242         // if the reinforcement is a ship
9243         wing_index = wing_lookup(Reinforcements[r_index].name);
9244         if(wing_index >= 0){            
9245                 // go through the ship arrival list and find the first ship in this wing
9246                 objp = GET_FIRST(&ship_arrival_list);
9247                 while( objp != END_OF_LIST(&ship_arrival_list) )        {
9248                         // check by wingnum                     
9249                         if (objp->wingnum == wing_index) {
9250                                 return objp->team;
9251                         }
9252
9253                         // next
9254                         objp = GET_NEXT(objp);
9255                 }
9256         }
9257
9258         // no team ?
9259         return -1;
9260 }
9261
9262 // determine if the given texture is used by a ship type. return ship info index, or -1 if not used by a ship
9263 int ship_get_texture(int bitmap)
9264 {
9265         int idx;
9266
9267         // check all ship types
9268         for(idx=0; idx<Num_ship_types; idx++){
9269                 if((Ship_info[idx].modelnum >= 0) && model_find_texture(Ship_info[idx].modelnum, bitmap) == 1){
9270                         return idx;
9271                 }
9272         }
9273
9274         // couldn't find the texture
9275         return -1;
9276 }
9277
9278 extern void ssm_create(vector *target, vector *start, int ssm_index, ssm_firing_info *override);
9279
9280 // update artillery lock info
9281 #define CLEAR_ARTILLERY_AND_CONTINUE()  { if(aip != NULL){ aip->artillery_objnum = -1; aip->artillery_sig = -1; aip->artillery_lock_time = 0.0f;} continue; } 
9282 float artillery_dist = 10.0f;
9283 DCF(art, "")
9284 {
9285         dc_get_arg(ARG_FLOAT);
9286         artillery_dist = Dc_arg_float;
9287 }
9288 void ship_update_artillery_lock()
9289 {
9290 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO) || defined(FS1_DEMO)
9291         return;
9292 #else
9293         ai_info *aip = NULL;
9294         weapon_info *tlaser = NULL;
9295         mc_info *cinfo = NULL;
9296         int c_objnum;
9297         vector temp, local_hit;
9298         ship *shipp;
9299         ship_obj *so;
9300
9301         // update all ships
9302         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ){
9303                 // get the ship
9304                 if((so->objnum >= 0) && (Objects[so->objnum].type == OBJ_SHIP) && (Objects[so->objnum].instance >= 0)){
9305                         shipp = &Ships[Objects[so->objnum].instance];
9306                 } else {
9307                         continue;
9308                 }               
9309
9310                 // get ai info
9311                 if(shipp->ai_index >= 0){
9312                         aip = &Ai_info[shipp->ai_index];
9313                 }
9314
9315                 // if the ship has no targeting laser firing
9316                 if((shipp->targeting_laser_objnum < 0) || (shipp->targeting_laser_bank < 0)){
9317                         CLEAR_ARTILLERY_AND_CONTINUE();
9318                 }
9319
9320                 // if he didn't hit any objects this frame
9321                 if(beam_get_num_collisions(shipp->targeting_laser_objnum) <= 0){
9322                         CLEAR_ARTILLERY_AND_CONTINUE();
9323                 }
9324
9325                 // get weapon info for the targeting laser he's firing
9326                 Assert((shipp->weapons.current_primary_bank >= 0) && (shipp->weapons.current_primary_bank < 2));
9327                 if((shipp->weapons.current_primary_bank < 0) || (shipp->weapons.current_primary_bank >= 2)){
9328                         continue;
9329                 }
9330                 Assert(shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank] >= 0);
9331                 if(shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank] < 0){
9332                         continue;
9333                 }
9334                 Assert((Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].wi_flags & WIF_BEAM) && (Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].b_info.beam_type == BEAM_TYPE_C));
9335                 if(!(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].wi_flags & WIF_BEAM) || (Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].b_info.beam_type != BEAM_TYPE_C)){
9336                         continue;
9337                 }
9338                 tlaser = &Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]];        
9339
9340                 // get collision info
9341                 if(!beam_get_collision(shipp->targeting_laser_objnum, 0, &c_objnum, &cinfo)){
9342                         CLEAR_ARTILLERY_AND_CONTINUE();
9343                 }
9344                 if((c_objnum < 0) || (cinfo == NULL)){
9345                         CLEAR_ARTILLERY_AND_CONTINUE();
9346                 }
9347
9348                 // get the position we hit this guy with in his local coords
9349                 vm_vec_sub(&temp, &cinfo->hit_point_world, &Objects[c_objnum].pos);
9350                 vm_vec_rotate(&local_hit, &temp, &Objects[c_objnum].orient);
9351
9352                 // if we are hitting a different guy now, reset the lock
9353                 if((c_objnum != aip->artillery_objnum) || (Objects[c_objnum].signature != aip->artillery_sig)){
9354                         aip->artillery_objnum = c_objnum;
9355                         aip->artillery_sig = Objects[c_objnum].signature;
9356                         aip->artillery_lock_time = 0.0f;
9357                         aip->artillery_lock_pos = local_hit;
9358
9359                         // done
9360                         continue;
9361                 }       
9362
9363                 // otherwise we're hitting the same guy. check to see if we've strayed too far
9364                 if(vm_vec_dist_quick(&local_hit, &aip->artillery_lock_pos) > artillery_dist){
9365                         // hmmm. reset lock time, but don't reset the lock itself
9366                         aip->artillery_lock_time = 0.0f;
9367                         continue;
9368                 }
9369
9370                 // finally - just increment the lock time
9371                 aip->artillery_lock_time += flFrametime;
9372
9373                 // TEST CODE
9374                 if(aip->artillery_lock_time >= 2.0f){
9375
9376                         HUD_printf("Firing artillery");
9377
9378                         vector temp;
9379                         vm_vec_unrotate(&temp, &aip->artillery_lock_pos, &Objects[aip->artillery_objnum].orient);
9380                         vm_vec_add2(&temp, &Objects[aip->artillery_objnum].pos);                        
9381                         ssm_create(&temp, &Objects[so->objnum].pos, 0, NULL);                           
9382
9383                         // reset the artillery                  
9384                         aip->artillery_lock_time = 0.0f;                        
9385                 }
9386         }
9387 #endif
9388 }
9389
9390 // checks if a world point is inside the extended bounding box of a ship
9391 // may not work if delta box is large and negative (ie, adjusted box crosses over on itself - min > max)
9392 int check_world_pt_in_expanded_ship_bbox(vector *world_pt, object *objp, float delta_box)
9393 {
9394         Assert(objp->type == OBJ_SHIP);
9395
9396         vector temp, ship_pt;
9397         polymodel *pm;
9398         vm_vec_sub(&temp, world_pt, &objp->pos);
9399         vm_vec_rotate(&ship_pt, &temp, &objp->orient);
9400
9401         pm = model_get(Ships[objp->instance].modelnum);
9402
9403         return (
9404                         (ship_pt.xyz.x > pm->mins.xyz.x - delta_box) && (ship_pt.xyz.x < pm->maxs.xyz.x + delta_box)
9405                 && (ship_pt.xyz.y > pm->mins.xyz.y - delta_box) && (ship_pt.xyz.y < pm->maxs.xyz.y + delta_box)
9406                 && (ship_pt.xyz.z > pm->mins.xyz.z - delta_box) && (ship_pt.xyz.z < pm->maxs.xyz.z + delta_box)
9407         );
9408 }
9409
9410
9411 // returns true when objp is ship and is tagged
9412 int ship_is_tagged(object *objp)
9413 {
9414         ship *shipp;
9415         if (objp->type == OBJ_SHIP) {
9416                 shipp = &Ships[objp->instance];
9417                 if ( (shipp->tag_left > 0) || (shipp->level2_tag_left > 0) ) {
9418                         return 1;
9419                 }
9420         }
9421
9422         return 0;
9423 }
9424
9425 // get maximum ship speed (when not warping in or out)
9426 float ship_get_max_speed(ship *shipp)
9427 {
9428         float max_speed;
9429
9430         int ship_info_index = shipp->ship_info_index;
9431
9432         // max overclodk
9433         max_speed = Ship_info[ship_info_index].max_overclocked_speed;
9434
9435         // normal max speed
9436         max_speed = max(max_speed, Ship_info[ship_info_index].max_vel.xyz.z);
9437
9438         // afterburn
9439         max_speed = max(max_speed, Ship_info[ship_info_index].afterburner_max_vel.xyz.z);
9440
9441         return max_speed;
9442 }
9443
9444 // determin warp speed of ship
9445 float ship_get_warp_speed(object *objp)
9446 {
9447         Assert(objp->type == OBJ_SHIP);
9448         float shipfx_calculate_warp_speed(object *);
9449         return shipfx_calculate_warp_speed(objp);
9450 }
9451
9452 // returns true if ship is beginning to speed up in warpout 
9453 int ship_is_beginning_warpout_speedup(object *objp)
9454 {
9455         Assert(objp->type == OBJ_SHIP);
9456
9457         ai_info *aip;
9458
9459         aip = &Ai_info[Ships[objp->instance].ai_index];
9460
9461         if (aip->mode == AIM_WARP_OUT) {
9462                 if ( (aip->submode == AIS_WARP_3) || (aip->submode == AIS_WARP_4) || (aip->submode == AIS_WARP_5) ) {
9463                         return 1;
9464                 }
9465         }
9466
9467         return 0;
9468 }
9469
9470 // given a ship info type, return a species
9471 int ship_get_species_by_type(int ship_info_index)
9472 {
9473         // sanity
9474         if((ship_info_index < 0) || (ship_info_index >= Num_ship_types)){
9475                 return -1;
9476         }
9477
9478         // return species
9479         return Ship_info[ship_info_index].species;
9480 }
9481
9482 // return the length of a ship
9483 float ship_get_length(ship* shipp)
9484 {
9485         polymodel *pm = model_get(shipp->modelnum);
9486         return (pm->maxs.xyz.z - pm->mins.xyz.z);
9487 }
9488