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