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