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