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