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