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