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