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