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