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