]> icculus.org git repositories - taylor/freespace2.git/blob - src/weapon/weapons.cpp
merge fixes
[taylor/freespace2.git] / src / weapon / weapons.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/Weapon/Weapons.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * Code to handle the weapon systems
16  *
17  * $Log$
18  * Revision 1.13  2005/10/01 22:04:58  taylor
19  * fix FS1 (de)briefing voices, the directory names are different in FS1
20  * hard code the table values so that the fs1.vp file isn't needed
21  * hard code a mission fix for sm2-08a since a have no idea how to fix it otherwise
22  * generally cleanup some FS1 code
23  * fix volume sliders in the options screen that never went all the way up
24  *
25  * Revision 1.12  2005/08/12 08:48:11  taylor
26  * don't scale weapon damage for FS1, this way you can still kill a capship with lasers rather than bombs
27  *
28  * Revision 1.11  2004/09/20 01:31:45  theoddone33
29  * GCC 3.4 fixes.
30  *
31  * Revision 1.10  2003/06/11 18:30:33  taylor
32  * plug memory leaks
33  *
34  * Revision 1.9  2003/05/25 02:30:44  taylor
35  * Freespace 1 support
36  *
37  * Revision 1.8  2002/06/21 03:04:12  relnev
38  * nothing important
39  *
40  * Revision 1.7  2002/06/17 06:33:11  relnev
41  * ryan's struct patch for gcc 2.95
42  *
43  * Revision 1.6  2002/06/09 04:41:30  relnev
44  * added copyright header
45  *
46  * Revision 1.5  2002/06/05 08:05:29  relnev
47  * stub/warning removal.
48  *
49  * reworked the sound code.
50  *
51  * Revision 1.4  2002/06/01 03:32:00  relnev
52  * fix texture loading mistake.
53  *
54  * enable some d3d stuff for opengl also
55  *
56  * Revision 1.3  2002/05/28 08:52:03  relnev
57  * implemented two assembly stubs.
58  *
59  * cleaned up a few warnings.
60  *
61  * added a little demo hackery to make it progress a little farther.
62  *
63  * Revision 1.2  2002/05/07 03:16:53  theoddone33
64  * The Great Newline Fix
65  *
66  * Revision 1.1.1.1  2002/05/03 03:28:11  root
67  * Initial import.
68  *
69  * 
70  * 69    9/14/99 3:26a Dave
71  * Fixed laser fogging problem in nebula (D3D)> Fixed multiplayer
72  * respawn-too-early problem. Made a few crash points safe.
73  * 
74  * 68    9/14/99 1:32a Andsager
75  * Better LOD for weapon explosions when behind.  Move point ahead to get
76  * vertex and then find size.
77  * 
78  * 67    9/07/99 1:10p Mikek
79  * Fix code I busted due to adding lifeleft to missiles.
80  * 
81  * 66    9/07/99 12:20a Andsager
82  * LOD less agressive at lower hardware detail level
83  * 
84  * 65    9/06/99 7:21p Dave
85  * Commented out bad lifeleft scaling code in weapon_set_tracking_info()
86  * 
87  * 64    9/06/99 3:23p Andsager
88  * Make fireball and weapon expl ani LOD choice look at resolution of the
89  * bitmap
90  * 
91  * 63    9/06/99 12:46a Andsager
92  * Add weapon_explosion_ani LOD
93  * 
94  * 62    9/05/99 11:24p Mikek
95  * Fixed problems caused by earlier checkin (that was rolled back).
96  * Problem was wp->target_pos was not set for swarmers.
97  * 
98  * More tweaking of missile behavior.  Also add 20% to lifetime of a
99  * locked missile.
100  * 
101  * [Rolled back -- MK] 63    9/05/99 2:23p Mikek
102  * Make aspect seekers a little less likely to miss their target.
103  * Mysterious why they do it so often.  Maybe fix for FS3...
104  * 
105  * [Rolled back -- MK] 62    9/04/99 12:09p Mikek
106  * Limit number of spawned weapons that can home on player based on skill
107  * level.  Works same as for non-spawned weapons.  Only do in single
108  * player.
109  * 
110  * 61    8/27/99 1:34a Andsager
111  * Modify damage by shockwaves for BIG|HUGE ships.  Modify shockwave damge
112  * when weapon blows up.
113  * 
114  * 60    8/24/99 10:47a Jefff
115  * tech room weapon anims.  added tech anim field to weapons.tbl
116  * 
117  * 59    8/16/99 11:58p Andsager
118  * Disable collision on proximity for ships with SIF_DONT_COLLIDE_INVIS
119  * hulls.
120  * 
121  * 58    8/10/99 5:30p Jefff
122  * Added tech_title string to weapon_info.  Changed parser accordingly.
123  * 
124  * 57    8/05/99 2:06a Dave
125  * Whee.
126  * 
127  * 56    8/02/99 5:16p Dave
128  * Bumped up weapon title string length from 32 to 48
129  * 
130  * 55    7/29/99 5:41p Jefff
131  * Sound hooks for cmeasure success
132  * 
133  * 54    7/24/99 1:54p Dave
134  * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
135  * missions.
136  * 
137  * 53    7/19/99 7:20p Dave
138  * Beam tooling. Specialized player-killed-self messages. Fixed d3d nebula
139  * pre-rendering.
140  * 
141  * 52    7/18/99 12:32p Dave
142  * Randomly oriented shockwaves.
143  * 
144  * 51    7/16/99 1:50p Dave
145  * 8 bit aabitmaps. yay.
146  * 
147  * 50    7/15/99 9:20a Andsager
148  * FS2_DEMO initial checkin
149  * 
150  * 49    7/08/99 10:53a Dave
151  * New multiplayer interpolation scheme. Not 100% done yet, but still
152  * better than the old way.
153  * 
154  * 48    7/02/99 4:31p Dave
155  * Much more sophisticated lightning support.
156  * 
157  * 47    7/01/99 5:57p Johnson
158  * Oops. Fixed big ship damage.
159  * 
160  * 46    7/01/99 4:23p Dave
161  * Full support for multiple linked ambient engine sounds. Added "big
162  * damage" flag.
163  * 
164  * 45    6/30/99 5:53p Dave
165  * Put in new anti-camper code.
166  * 
167  * 44    6/22/99 3:24p Danw
168  * Fixed incorrect weapon hit sound culling.
169  * 
170  * 43    6/21/99 7:25p Dave
171  * netplayer pain packet. Added type E unmoving beams.
172  * 
173  * 42    6/14/99 10:45a Dave
174  * Made beam weapons specify accuracy by skill level in the weapons.tbl
175  * 
176  * 41    6/11/99 11:13a Dave
177  * last minute changes before press tour build.
178  * 
179  * 40    6/04/99 2:16p Dave
180  * Put in shrink effect for beam weapons.
181  * 
182  * 39    6/01/99 8:35p Dave
183  * Finished lockarm weapons. Added proper supercap weapons/damage. Added
184  * awacs-set-radius sexpression.
185  * 
186  * 38    6/01/99 3:52p Dave
187  * View footage screen. Fixed xstrings to not display the & symbol. Popup,
188  * dead popup, pxo find player popup, pxo private room popup.
189  * 
190  * 37    5/27/99 6:17p Dave
191  * Added in laser glows.
192  * 
193  * 36    5/20/99 7:00p Dave
194  * Added alternate type names for ships. Changed swarm missile table
195  * entries.
196  * 
197  * 35    5/08/99 8:25p Dave
198  * Upped object pairs. First run of nebula lightning.
199  * 
200  * 34    4/28/99 11:13p Dave
201  * Temporary checkin of artillery code.
202  * 
203  * 33    4/28/99 3:11p Andsager
204  * Stagger turret weapon fire times.  Make turrets smarter when target is
205  * protected or beam protected.  Add weaopn range to weapon info struct.
206  * 
207  * 32    4/22/99 11:06p Dave
208  * Final pass at beam weapons. Solidified a lot of stuff. All that remains
209  * now is to tweak and fix bugs as they come up. No new beam weapon
210  * features.
211  * 
212  * 31    4/19/99 11:01p Dave
213  * More sophisticated targeting laser support. Temporary checkin.
214  * 
215  * 30    4/16/99 5:54p Dave
216  * Support for on/off style "stream" weapons. Real early support for
217  * target-painting lasers.
218  * 
219  * 29    4/07/99 6:22p Dave
220  * Fred and Freespace support for multiple background bitmaps and suns.
221  * Fixed link errors on all subprojects. Moved encrypt_init() to
222  * cfile_init() and lcl_init(), since its safe to call twice.
223  * 
224  * 28    4/02/99 1:35p Dave
225  * Removed weapon hit packet. No good for causing pain.
226  * 
227  * 27    4/02/99 9:55a Dave
228  * Added a few more options in the weapons.tbl for beam weapons. Attempt
229  * at putting "pain" packets into multiplayer.
230  * 
231  * 26    3/31/99 9:26p Dave
232  * Don't load beam textures when in Fred.
233  * 
234  * 25    3/31/99 8:24p Dave
235  * Beefed up all kinds of stuff, incluging beam weapons, nebula effects
236  * and background nebulae. Added per-ship non-dimming pixel colors.
237  * 
238  * 24    3/23/99 2:29p Andsager
239  * Fix shockwaves for kamikazi and Fred defined.  Collect together
240  * shockwave_create_info struct.
241  * 
242  * 23    3/23/99 11:03a Dave
243  * Added a few new fields and fixed parsing code for new weapon stuff.
244  * 
245  * 22    3/19/99 9:52a Dave
246  * Checkin to repair massive source safe crash. Also added support for
247  * pof-style nebulae, and some new weapons code.
248  * 
249  * 24    3/15/99 6:45p Daveb
250  * Put in rough nebula bitmap support.
251  * 
252  * 23    3/12/99 3:19p Enricco
253  * Remove spurious Int3()
254  * 
255  * 22    3/11/99 5:53p Dave
256  * More network optimization. Spliced in Dell OEM planet bitmap crap.
257  * 
258  * 21    3/10/99 6:51p Dave
259  * Changed the way we buffer packets for all clients. Optimized turret
260  * fired packets. Did some weapon firing optimizations.
261  * 
262  * 20    2/24/99 4:02p Dave
263  * Fixed weapon locking and homing problems for multiplayer dogfight mode.
264  * 
265  * 19    2/05/99 12:52p Dave
266  * Fixed Glide nondarkening textures.
267  * 
268  * 18    1/29/99 12:47a Dave
269  * Put in sounds for beam weapon. A bunch of interface screens (tech
270  * database stuff).
271  * 
272  * 17    1/27/99 9:56a Dave
273  * Temporary checkin of beam weapons for Dan to make cool sounds.
274  * 
275  * 16    1/25/99 5:03a Dave
276  * First run of stealth, AWACS and TAG missile support. New mission type
277  * :)
278  * 
279  * 15    1/24/99 11:37p Dave
280  * First full rev of beam weapons. Very customizable. Removed some bogus
281  * Int3()'s in low level net code.
282  * 
283  * 14    1/21/99 10:45a Dave
284  * More beam weapon stuff. Put in warmdown time.
285  * 
286  * 13    1/12/99 5:45p Dave
287  * Moved weapon pipeline in multiplayer to almost exclusively client side.
288  * Very good results. Bandwidth goes down, playability goes up for crappy
289  * connections. Fixed object update problem for ship subsystems.
290  * 
291  * 12    1/08/99 2:08p Dave
292  * Fixed software rendering for pofview. Super early support for AWACS and
293  * beam weapons.
294  * 
295  * 11    1/06/99 2:24p Dave
296  * Stubs and release build fixes.
297  * 
298  * 10    12/01/98 6:12p Johnson
299  * Make sure to page in weapon impact animations as xparent textures.
300  * 
301  * 9     11/20/98 4:08p Dave
302  * Fixed flak effect in multiplayer.
303  * 
304  * 8     11/14/98 5:33p Dave
305  * Lots of nebula work. Put in ship contrails.
306  * 
307  * 7     11/05/98 4:18p Dave
308  * First run nebula support. Beefed up localization a bit. Removed all
309  * conditional compiles for foreign versions. Modified mission file
310  * format.
311  * 
312  * 6     10/26/98 9:42a Dave
313  * Early flak gun support.
314  * 
315  * 5     10/23/98 3:51p Dave
316  * Full support for tstrings.tbl and foreign languages. All that remains
317  * is to make it active in Fred.
318  * 
319  * 4     10/07/98 6:27p Dave
320  * Globalized mission and campaign file extensions. Removed Silent Threat
321  * special code. Moved \cache \players and \multidata into the \data
322  * directory.
323  * 
324  * 3     10/07/98 4:49p Andsager
325  * don't do weapon swap (was needed for mission disk)
326  * 
327  * 2     10/07/98 10:54a Dave
328  * Initial checkin.
329  * 
330  * 1     10/07/98 10:51a Dave
331  * 
332  * 314   9/21/98 11:19p Dave
333  * Weapon name fix.
334  * 
335  * 313   9/19/98 4:33p Adam
336  * Changed default values for particle spew (used on Leech Cannon)
337  * 
338  * 312   9/13/98 10:51p Dave
339  * Put in newfangled icons for mission simulator room. New mdisk.vp
340  * checksum and file length.
341  * 
342  * 311   9/13/98 4:29p Andsager
343  * Maintain Weapon_info compataiblity with mission disk
344  * 
345  * 310   9/13/98 4:26p Andsager
346  * 
347  * 309   9/01/98 4:25p Dave
348  * Put in total (I think) backwards compatibility between mission disk
349  * freespace and non mission disk freespace, including pilot files and
350  * campaign savefiles.
351  * 
352  * 308   8/28/98 3:29p Dave
353  * EMP effect done. AI effects may need some tweaking as required.
354  * 
355  * 307   8/25/98 1:49p Dave
356  * First rev of EMP effect. Player side stuff basically done. Next comes
357  * AI code.
358  * 
359  * 306   8/18/98 10:15a Dave
360  * Touchups on the corkscrew missiles. Added particle spewing weapons.
361  * 
362  * 305   8/17/98 5:07p Dave
363  * First rev of corkscrewing missiles.
364  * 
365  * 304   6/30/98 2:23p Dave
366  * Revised object update system. Removed updates for all weapons. Put
367  * button info back into control info packet.
368  * 
369  * 303   6/22/98 8:36a Allender
370  * revamping of homing weapon system.  don't send as object updates
371  * anymore
372  * 
373  * 302   5/24/98 2:25p Allender
374  * be sure that homing missiles die on client when lifeleft gets too
375  * negative (lost packets)
376  * 
377  * 301   5/20/98 5:47p Sandeep
378  * 
379  * 300   5/18/98 1:58a Mike
380  * Make Phoenix not be fired at fighters (but yes bombers).
381  * Improve positioning of ships in guard mode.
382  * Make turrets on player ship not fire near end of support ship docking.
383  * 
384  * $NoKeywords: $
385  */
386
387 #include <stdlib.h>
388
389 #include "pstypes.h"
390 #include "systemvars.h"
391 #include "vecmat.h"
392 #include "tmapper.h"
393 #include "2d.h"
394 #include "3d.h"
395 #include "bmpman.h"
396 #include "model.h"
397 #include "key.h"
398 #include "physics.h"
399 #include "floating.h"
400 #include "model.h"
401 #include "lighting.h"
402 #include "object.h"
403 #include "weapon.h"
404 #include "ship.h"
405 #include "fireballs.h"
406 #include "player.h"
407 #include "hudtarget.h"
408 #include "freespace.h"
409 #include "radar.h"
410 #include "ai.h"
411 #include "sound.h"
412 #include "multi.h"
413 #include "multimsgs.h"
414 #include "linklist.h"
415 #include "timer.h"
416 #include "gamesnd.h"
417 #include "cmeasure.h"
418 #include "shockwave.h"
419 #include "model.h"
420 #include "staticrand.h"
421 #include "swarm.h"
422 #include "multiutil.h"
423 #include "shiphit.h"
424 #include "trails.h"
425 #include "hud.h"
426 #include "objcollide.h"
427 #include "aibig.h"
428 #include "particle.h"
429 #include "asteroid.h"
430 #include "joy_ff.h"
431 #include "multi_obj.h"
432 #include "corkscrew.h"
433 #include "emp.h"
434 #include "localize.h"
435 #include "flak.h"
436 #include "muzzleflash.h"
437
438 #ifndef NDEBUG
439 int Weapon_flyby_sound_enabled = 1;
440 DCF_BOOL( weapon_flyby, Weapon_flyby_sound_enabled )
441 #endif
442
443 static int Weapon_flyby_sound_timer;    
444
445 weapon Weapons[MAX_WEAPONS];
446 weapon_info Weapon_info[MAX_WEAPON_TYPES];
447
448 #define         MISSILE_OBJ_USED        (1<<0)                  // flag used in missile_obj struct
449 #define         MAX_MISSILE_OBJS        MAX_WEAPONS             // max number of missiles tracked in missile list
450 missile_obj Missile_objs[MAX_MISSILE_OBJS];     // array used to store missile object indexes
451 missile_obj Missile_obj_list;                                           // head of linked list of missile_obj structs
452
453
454 // WEAPON EXPLOSION INFO
455 #define MAX_weapon_expl_lod                                             4
456 #define MAX_Weapon_expl_info                                    3
457
458 typedef struct weapon_expl_lod {
459         char    filename[MAX_FILENAME_LEN];
460         int     bitmap_id;
461         int     num_frames;
462         int     fps;
463 } weapon_expl_lod;
464
465 typedef struct weapon_expl_info {
466         int                                     lod_count;      
467         weapon_expl_lod         lod[MAX_weapon_expl_lod];
468 } weapon_expl_info;
469
470 weapon_expl_info Weapon_expl_info[MAX_Weapon_expl_info];
471
472 int Num_weapon_expl = 0;
473
474 int Num_weapon_types = 0;
475
476 int Num_weapons = 0;
477 int Weapons_inited = 0;
478
479 int laser_model_inner = -1;
480 int laser_model_outer = -1;
481
482 int missile_model = -1;
483
484 char    *Weapon_names[MAX_WEAPON_TYPES];
485
486 int     First_secondary_index = -1;
487
488 #define MAX_SPAWN_WEAPONS       10                      //      Up to 10 weapons can spawn weapons.
489
490 int     Num_spawn_types;
491 char    Spawn_names[MAX_SPAWN_WEAPONS][NAME_LENGTH];
492
493 int Num_player_weapon_precedence;                               // Number of weapon types in Player_weapon_precedence
494 int Player_weapon_precedence[MAX_WEAPON_TYPES]; // Array of weapon types, precedence list for player weapon selection
495
496 // Used to avoid playing too many impact sounds in too short a time interval.
497 // This will elimate the odd "stereo" effect that occurs when two weapons impact at 
498 // nearly the same time, like from a double laser (also saves sound channels!)
499 #define IMPACT_SOUND_DELTA      50              // in milliseconds
500 int             Weapon_impact_timer;                    // timer, initalized at start of each mission
501
502 // energy suck defines
503 #define ESUCK_DEFAULT_WEAPON_REDUCE                             (10.0f)
504 #define ESUCK_DEFAULT_AFTERBURNER_REDUCE                (10.0f)
505
506 // scale factor for supercaps taking damage from weapons which are not "supercap" weapons
507 #define SUPERCAP_DAMAGE_SCALE                   0.25f
508
509 // scale factor for big ships getting hit by flak
510 #define FLAK_DAMAGE_SCALE                               0.05f
511
512 extern int Max_allowed_player_homers[];
513 extern int compute_num_homing_objects(object *target_objp);
514
515 // 
516 void parse_weapon_expl_tbl()
517 {
518 #ifndef MAKE_FS1
519         int     idx;
520         char base_filename[256] = "";
521
522         // open localization
523         lcl_ext_open();
524
525         try {
526                 read_file_text(NOX("weapon_expl.tbl"));
527                 reset_parse();
528
529                 Num_weapon_expl = 0;
530                 required_string("#Start");
531                 while (required_string_either("#End","$Name:")) {
532                         SDL_assert( Num_weapon_expl < MAX_Weapon_expl_info);
533
534                         // base filename
535                         required_string("$Name:");
536                         stuff_string(base_filename, F_NAME, NULL);
537
538                         // # of lod levels - make sure old fireball.tbl is compatible
539                         Weapon_expl_info[Num_weapon_expl].lod_count = 1;
540                         if(optional_string("$LOD:")){
541                                 stuff_int(&Weapon_expl_info[Num_weapon_expl].lod_count);
542                         }
543
544                         // stuff default filename
545                         SDL_strlcpy(Weapon_expl_info[Num_weapon_expl].lod[0].filename, base_filename, MAX_FILENAME_LEN);
546
547                         // stuff LOD level filenames
548                         for(idx=1; idx<Weapon_expl_info[Num_weapon_expl].lod_count; idx++){
549                                 if(idx >= MAX_weapon_expl_lod){
550                                         break;
551                                 }
552
553                                 SDL_snprintf(Weapon_expl_info[Num_weapon_expl].lod[idx].filename, MAX_FILENAME_LEN, "%s_%d", base_filename, idx);
554                         }
555
556                         Num_weapon_expl++;
557                 }
558                 required_string("#End");
559         } catch (parse_error_t rval) {
560                 Error(LOCATION, "Unable to parse weapon_expl.tbl!  Code = %i.\n", (int)rval);
561         }
562
563         // close localization
564         lcl_ext_close();
565 #else
566         // hard coded value for FS1
567         Num_weapon_expl = 0;
568
569         Weapon_expl_info[Num_weapon_expl].lod_count = 1;
570         SDL_strlcpy(Weapon_expl_info[Num_weapon_expl].lod[0].filename, "ExpMissileHit1", MAX_FILENAME_LEN);
571
572         Num_weapon_expl++;
573 #endif
574 }
575
576 int get_weapon_expl_info_index(char *filename)
577 {
578         for (int i=0; i<MAX_Weapon_expl_info; i++) {
579                 if ( SDL_strcasecmp(Weapon_expl_info[i].lod[0].filename, filename) == 0) {
580                         return i;
581                 }
582         }
583
584         return -1;
585 }
586 // ----------------------------------------------------------------------
587 // missile_obj_list_init()
588 //
589 // Clear out the Missile_obj_list
590 //
591 void missile_obj_list_init()
592 {
593         int i;
594
595         list_init(&Missile_obj_list);
596         for ( i = 0; i < MAX_MISSILE_OBJS; i++ ) {
597                 Missile_objs[i].flags = 0;
598         }
599 }
600
601 // ---------------------------------------------------
602 // missile_obj_list_add()
603 //
604 // Function to add a node from the Missile_obj_list.  Only
605 // called from weapon_create()
606 int missile_obj_list_add(int objnum)
607 {
608         int i;
609
610         for ( i = 0; i < MAX_MISSILE_OBJS; i++ ) {
611                 if ( !(Missile_objs[i].flags & MISSILE_OBJ_USED) )
612                         break;
613         }
614         if ( i == MAX_MISSILE_OBJS ) {
615                 Error(LOCATION, "Fatal Error: Ran out of missile object nodes\n");
616                 return -1;
617         }
618         
619         Missile_objs[i].flags = 0;
620         Missile_objs[i].objnum = objnum;
621         list_append(&Missile_obj_list, &Missile_objs[i]);
622         Missile_objs[i].flags |= MISSILE_OBJ_USED;
623
624         return i;
625 }
626
627 // ---------------------------------------------------
628 // missle_obj_list_remove()
629 //
630 // Function to remove a node from the Missile_obj_list.  Only
631 // called from weapon_delete()
632 void missle_obj_list_remove(int index)
633 {
634         SDL_assert(index >= 0 && index < MAX_MISSILE_OBJS);
635         list_remove(&Missile_obj_list, &Missile_objs[index]);   
636         Missile_objs[index].flags = 0;
637 }
638
639 // ---------------------------------------------------
640 // missile_obj_list_rebuild()
641 //
642 // Called by the save/restore code to rebuild Missile_obj_list
643 //
644 void missile_obj_list_rebuild()
645 {
646         object *objp;
647
648         missile_obj_list_init();
649
650         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
651                 if ( objp->type == OBJ_WEAPON && Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE ) {
652                         Weapons[objp->instance].missile_list_index = missile_obj_list_add(OBJ_INDEX(objp));
653                 }
654         }
655 }
656
657 // If this is a player countermeasure, let the player know he evaded a missile
658 void weapon_maybe_alert_cmeasure_success(object *objp)
659 {
660         if ( objp->type == OBJ_CMEASURE ) {
661                 cmeasure *cmp;
662                 cmp = &Cmeasures[objp->instance];
663                 if ( cmp->source_objnum == OBJ_INDEX(Player_obj) ) {
664                         hud_start_text_flash(XSTR("Evaded", 1430), 800);
665                         snd_play(&Snds[SND_MISSILE_EVADED_POPUP]);
666                 } else if ( Objects[cmp->source_objnum].flags & OF_PLAYER_SHIP ) {
667                         send_countermeasure_success_packet( cmp->source_objnum );
668                 }
669         }
670 }
671
672 // ---------------------------------------------------
673 // missile_obj_return_address()
674 //
675 // Called externally to generate an address from an index into
676 // the Missile_objs[] array
677 //
678 missile_obj *missile_obj_return_address(int index)
679 {
680         SDL_assert(index >= 0 && index < MAX_MISSILE_OBJS);
681         return &Missile_objs[index];
682 }
683
684 //      Return the index of Weapon_info[].name that is *name.
685 int weapon_info_lookup(const char *name)
686 {
687         int     i;
688
689 #ifdef MAKE_FS1
690         // fix the stupid table stuff - it's non-fatal but annoying error messages
691         if (!strcmp(name, "Disruptor Missile")) {
692                 name = "D-Missile";
693         // this one fixes the same issue with a SilentThreat mission
694         } else if (!strcmp(name, "Shield Breaker")) {
695                 name = "S-Breaker";
696         }
697 #endif
698
699         for (i=0; i<Num_weapon_types; i++)
700                 if (!SDL_strcasecmp(name, Weapon_info[i].name))
701                         return i;
702
703         return -1;
704 }
705
706 #define DEFAULT_WEAPON_SPAWN_COUNT      10
707
708 //      Parse the weapon flags.
709 void parse_wi_flags(weapon_info *weaponp)
710 {
711         char    weapon_strings[MAX_WEAPON_FLAGS][NAME_LENGTH];
712         int     num_strings;
713
714         required_string("$Flags:");
715
716         num_strings = stuff_string_list(weapon_strings, MAX_WEAPON_FLAGS);
717         
718         for (int i=0; i<num_strings; i++) {
719                 if (!SDL_strcasecmp(NOX("Electronics"), weapon_strings[i]))
720                         weaponp->wi_flags |= WIF_ELECTRONICS;           
721                 else if (!SDL_strncasecmp(NOX("Spawn"), weapon_strings[i], 5)) {
722                         if (weaponp->spawn_type == -1) {
723                                 int     skip_length, name_length;
724                                 char    *temp_string;
725
726                                 temp_string = weapon_strings[i];
727
728                                 weaponp->wi_flags |= WIF_SPAWN;
729                                 weaponp->spawn_type = (short)Num_spawn_types;
730                                 skip_length = strlen(NOX("Spawn")) + strspn(&temp_string[strlen(NOX("Spawn"))], NOX(" \t"));
731                                 char *num_start = SDL_strchr(&temp_string[skip_length], ',');
732                                 if (num_start == NULL) {
733                                         weaponp->spawn_count = DEFAULT_WEAPON_SPAWN_COUNT;
734                                         name_length = NAME_LENGTH;
735                                 } else {
736                                         weaponp->spawn_count = (short)atoi(num_start+1);
737                                         name_length = SDL_min(num_start - temp_string - skip_length + 1, NAME_LENGTH);
738                                 }
739
740                                 SDL_strlcpy(Spawn_names[Num_spawn_types++], &(weapon_strings[i][skip_length]), name_length);
741                                 SDL_assert(Num_spawn_types < MAX_SPAWN_WEAPONS);
742                         } else
743                                 Warning(LOCATION, "Illegal to have two spawn types for one weapon.\n"
744                                                                                 "Ignoring weapon %s", weapon_strings[i]);
745                 } else if (!SDL_strcasecmp(NOX("Remote Detonate"), weapon_strings[i]))
746                         weaponp->wi_flags |= WIF_REMOTE;
747                 else if (!SDL_strcasecmp(NOX("Puncture"), weapon_strings[i]))
748                         weaponp->wi_flags |= WIF_PUNCTURE;              
749                 else if (!SDL_strcasecmp(NOX("Big Ship"), weapon_strings[i]))
750                         weaponp->wi_flags |= WIF_BIG_ONLY;
751                 else if (!SDL_strcasecmp(NOX("Huge"), weapon_strings[i]))
752                         weaponp->wi_flags |= WIF_HUGE;
753                 else if (!SDL_strcasecmp(NOX("Bomber+"), weapon_strings[i]))
754                         weaponp->wi_flags |= WIF_BOMBER_PLUS;
755                 else if (!SDL_strcasecmp(NOX("child"), weapon_strings[i]))
756                         weaponp->wi_flags |= WIF_CHILD;
757                 else if (!SDL_strcasecmp(NOX("Bomb"), weapon_strings[i]))
758                         weaponp->wi_flags |= WIF_BOMB;
759                 else if (!SDL_strcasecmp(NOX("No Dumbfire"), weapon_strings[i]))
760                         weaponp->wi_flags |= WIF_NO_DUMBFIRE;
761                 else if (!SDL_strcasecmp(NOX("In tech database"), weapon_strings[i]))
762                         weaponp->wi_flags |= WIF_IN_TECH_DATABASE;
763                 else if (!SDL_strcasecmp(NOX("Player allowed"), weapon_strings[i]))
764                         weaponp->wi_flags |= WIF_PLAYER_ALLOWED;                
765                 else if (!SDL_strcasecmp(NOX("Particle Spew"), weapon_strings[i]))
766                         weaponp->wi_flags |= WIF_PARTICLE_SPEW;
767                 else if (!SDL_strcasecmp(NOX("EMP"), weapon_strings[i]))
768                         weaponp->wi_flags |= WIF_EMP;
769                 else if (!SDL_strcasecmp(NOX("Esuck"), weapon_strings[i]))
770                         weaponp->wi_flags |= WIF_ENERGY_SUCK;
771                 else if (!SDL_strcasecmp(NOX("Flak"), weapon_strings[i]))
772                         weaponp->wi_flags |= WIF_FLAK;
773                 else if (!SDL_strcasecmp(NOX("Corkscrew"), weapon_strings[i]))
774                         weaponp->wi_flags |= WIF_CORKSCREW;
775                 else if (!SDL_strcasecmp(NOX("Shudder"), weapon_strings[i]))
776                         weaponp->wi_flags |= WIF_SHUDDER;               
777                 else if (!SDL_strcasecmp(NOX("lockarm"), weapon_strings[i]))
778                         weaponp->wi_flags |= WIF_LOCKARM;               
779                 else if (!SDL_strcasecmp(NOX("beam"), weapon_strings[i]))
780                         weaponp->wi_flags |= WIF_BEAM;
781                 else if (!SDL_strcasecmp(NOX("stream"), weapon_strings[i]))
782                         weaponp->wi_flags |= WIF_STREAM;
783                 else if (!SDL_strcasecmp(NOX("supercap"), weapon_strings[i]))
784                         weaponp->wi_flags |= WIF_SUPERCAP;
785 #ifdef MAKE_FS1
786                 else if (!SDL_strcasecmp(NOX("Swarm"), weapon_strings[i])) {
787                         weaponp->wi_flags |= WIF_SWARM;
788                         weaponp->swarm_count = SWARM_DEFAULT_NUM_MISSILES_FIRED;
789                 } else if (!SDL_strcasecmp(NOX("No Ship"), weapon_strings[i]))
790                         weaponp->wi_flags |= WIF_CHILD;
791 #endif 
792                 else
793                         Warning(LOCATION, "Bogus string in weapon flags: %s\n", weapon_strings[i]);
794         }       
795
796         // SWARM, CORKSCREW and FLAK should be mutually exclusive
797         if(weaponp->wi_flags & WIF_FLAK){
798                 SDL_assert(!(weaponp->wi_flags & WIF_CORKSCREW) && !(weaponp->wi_flags & WIF_SWARM));
799         }
800         if(weaponp->wi_flags & WIF_CORKSCREW){
801                 SDL_assert(!(weaponp->wi_flags & WIF_FLAK) && !(weaponp->wi_flags & WIF_SWARM));
802         }
803         if(weaponp->wi_flags & WIF_SWARM){
804                 SDL_assert(!(weaponp->wi_flags & WIF_CORKSCREW) && !(weaponp->wi_flags & WIF_FLAK));
805         }
806
807         // make sure flak guns are only placed on turrets
808         if(weaponp->wi_flags & WIF_FLAK){
809                 SDL_assert(weaponp->wi_flags & WIF_BIG_ONLY);
810         }
811 }
812
813 // function to parse the information for a specific weapon type.        
814 // return 0 if successful, otherwise return -1
815 #define WEAPONS_MULTITEXT_LENGTH 2048
816
817 int parse_weapon()
818 {
819         char buf[WEAPONS_MULTITEXT_LENGTH];
820         weapon_info *wip;
821         char fname[255] = "";
822         int idx;
823
824         wip = &Weapon_info[Num_weapon_types];
825
826         wip->wi_flags = 0;
827
828         required_string("$Name:");
829         stuff_string(wip->name, F_NAME, NULL);
830         diag_printf ("Weapon name -- %s\n", wip->name);
831
832         // AL 28-3-98: If this is a demo build, we only want to parse weapons that are preceded with
833         //             the '@' symbol
834         #ifdef DEMO // not needed FS2_DEMO (separate table file)
835                 if ( wip->name[0] != '@' ) {
836                         // advance to next weapon, and return -1
837
838                         if ( skip_to_start_of_strings("$Name:", "#End") != 1 ) {
839                                 Int3();
840                         }
841                         return -1;
842                 }
843         #endif
844
845         if ( wip->name[0] == '@' ) {
846                 char old_name[NAME_LENGTH];
847                 SDL_strlcpy(old_name, wip->name, SDL_arraysize(old_name));
848                 SDL_strlcpy(wip->name, old_name+1, SDL_arraysize(wip->name));
849         }
850
851         wip->title[0] = 0;
852         if (optional_string("+Title:")) {
853                 stuff_string(wip->title, F_NAME, NULL, WEAPON_TITLE_LEN);
854         }
855
856         wip->desc = NULL;
857         if (optional_string("+Description:")) {
858                 stuff_string(buf, F_MULTITEXT, NULL);
859                 wip->desc = strdup(buf);
860         }
861
862         wip->tech_title[0] = 0;
863         if (optional_string("+Tech Title:")) {
864                 stuff_string(wip->tech_title, F_NAME, NULL, NAME_LENGTH);
865         }
866
867         wip->tech_anim_filename[0] = 0;
868         if (optional_string("+Tech Anim:")) {
869                 stuff_string(wip->tech_anim_filename, F_NAME, NULL, NAME_LENGTH);
870         }
871
872         wip->tech_desc = NULL;
873         if (optional_string("+Tech Description:")) {
874                 stuff_string(buf, F_MULTITEXT, NULL, WEAPONS_MULTITEXT_LENGTH);
875                 wip->tech_desc = strdup(buf);
876         }
877
878         //      Read the model file.  It can be a POF file or none.
879         //      If there is no model file (Model file: = "none") then we use our special
880         //      laser renderer which requires inner, middle and outer information.
881         required_string("$Model file:");
882         stuff_string(wip->pofbitmap_name, F_NAME, NULL);
883         diag_printf ("Model pof file -- %s\n", wip->pofbitmap_name );
884         if ( SDL_strcasecmp(wip->pofbitmap_name, NOX("none")) ) {
885                 wip->model_num = -1;                            
886                 wip->render_type = WRT_POF;
887                 wip->laser_bitmap = -1;
888         } else {
889                 //      No POF or AVI file specified, render as special laser type.
890                 ubyte r,g,b;
891
892                 wip->render_type = WRT_LASER;
893                 wip->model_num = -1;
894
895                 // laser bitmap itself
896                 required_string("@Laser Bitmap:");
897                 stuff_string(wip->pofbitmap_name, F_NAME, NULL);
898                 wip->laser_bitmap = -1;
899                 if(!Fred_running){
900                         wip->laser_bitmap = bm_load( wip->pofbitmap_name );
901                 }
902
903                 // optional laser glow
904                 wip->laser_glow_bitmap = -1;
905                 if(optional_string("@Laser Glow:")){
906                         stuff_string(fname, F_NAME, NULL);              
907                         if(!Fred_running){
908                                 wip->laser_glow_bitmap = bm_load( fname );
909
910                                 // might as well lock it down as an aabitmap now
911                                 if(wip->laser_glow_bitmap >= 0){
912                                         bm_lock(wip->laser_glow_bitmap, 8, BMP_AABITMAP);
913                                         bm_unlock(wip->laser_glow_bitmap);
914                                 }
915                         }
916                 }
917                 
918                 required_string("@Laser Color:");
919                 stuff_byte(&r); stuff_byte(&g); stuff_byte(&b);
920                 gr_init_color( &wip->laser_color_1, r, g, b );
921
922                 // optional string for cycling laser colors
923                 gr_init_color(&wip->laser_color_2, 0, 0, 0);
924                 if(optional_string("@Laser Color2:")){
925                         stuff_byte(&r); stuff_byte(&g); stuff_byte(&b);
926                         gr_init_color( &wip->laser_color_2, r, g, b );
927                 }
928
929                 required_string("@Laser Length:");
930                 stuff_float(&wip->laser_length);
931                 
932                 required_string("@Laser Head Radius:");
933                 stuff_float(&wip->laser_head_radius);
934
935                 required_string("@Laser Tail Radius:");
936                 stuff_float(&wip->laser_tail_radius );
937         }
938
939         required_string("$Mass:");
940         stuff_float( &(wip->mass) );
941         diag_printf ("Weapon mass -- %7.3f\n", wip->mass);
942
943         required_string("$Velocity:");
944         stuff_float( &(wip->max_speed) );
945         diag_printf ("Weapon mass -- %7.3f\n", wip->max_speed);
946
947         required_string("$Fire Wait:");
948         stuff_float( &(wip->fire_wait) );
949         diag_printf ("Weapon fire wait -- %7.3f\n", wip->fire_wait);
950
951         required_string("$Damage:");
952         stuff_float(&wip->damage);
953
954         // secondary weapons require these values
955         if (First_secondary_index != -1) {
956                 required_string("$Blast Force:");
957                 stuff_float( &(wip->blast_force) );
958                 diag_printf ("Weapon blast force -- %7.3f\n", wip->blast_force);
959
960                 required_string("$Inner Radius:");
961                 stuff_float( &(wip->inner_radius) );
962                 if ( wip->inner_radius != 0 ) {
963                         wip->wi_flags |= WIF_AREA_EFFECT;
964                 }
965                 diag_printf ("Weapon inner blast radius -- %7.3f\n", wip->inner_radius);
966
967                 required_string("$Outer Radius:");
968                 stuff_float( &(wip->outer_radius) );
969                 if ( wip->outer_radius != 0 ) {
970                         wip->wi_flags |= WIF_AREA_EFFECT;
971                 }
972                 diag_printf ("Weapon outer blast radius -- %7.3f\n", wip->outer_radius);
973
974                 required_string("$Shockwave Speed:");
975                 stuff_float( &(wip->shockwave_speed) );
976                 if ( wip->shockwave_speed != 0 ) {
977                         wip->wi_flags |= WIF_SHOCKWAVE;
978                 }
979                 diag_printf ("Shockwave speed -- %7.3f\n", wip->shockwave_speed);
980         } 
981         // for primary weapons they're optional
982         else {
983                 if(optional_string("$Blast Force:")){
984                         stuff_float( &(wip->blast_force) );
985                         diag_printf ("Weapon blast force -- %7.3f\n", wip->blast_force);
986                 }
987
988                 if(optional_string("$Inner Radius:")){
989                         stuff_float( &(wip->inner_radius) );
990                         if ( wip->inner_radius != 0 ) {
991                                 wip->wi_flags |= WIF_AREA_EFFECT;
992                         }
993                         diag_printf ("Weapon inner blast radius -- %7.3f\n", wip->inner_radius);
994                 }
995
996                 if(optional_string("$Outer Radius:")){
997                         stuff_float( &(wip->outer_radius) );
998                         if ( wip->outer_radius != 0 ) {
999                                 wip->wi_flags |= WIF_AREA_EFFECT;
1000                         }
1001                         diag_printf ("Weapon outer blast radius -- %7.3f\n", wip->outer_radius);
1002                 }
1003
1004                 if(optional_string("$Shockwave Speed:")){
1005                         stuff_float( &(wip->shockwave_speed) );
1006                         if ( wip->shockwave_speed != 0 ) {
1007                                 wip->wi_flags |= WIF_SHOCKWAVE;
1008                         }
1009                         diag_printf ("Shockwave speed -- %7.3f\n", wip->shockwave_speed);
1010                 }
1011         }
1012
1013         required_string("$Armor Factor:");
1014         stuff_float(&wip->armor_factor);
1015
1016         required_string("$Shield Factor:");
1017         stuff_float(&wip->shield_factor);
1018
1019         required_string("$Subsystem Factor:");
1020         stuff_float(&wip->subsystem_factor);
1021
1022         required_string("$Lifetime:");
1023         stuff_float(&wip->lifetime);
1024
1025         required_string("$Energy Consumed:");
1026         stuff_float(&wip->energy_consumed);
1027
1028         required_string("$Cargo Size:");
1029         stuff_float(&wip->cargo_size);
1030
1031         int is_homing=0;
1032         required_string("$Homing:");
1033         stuff_boolean(&is_homing);
1034
1035         if (is_homing == 1) {
1036                 char    temp_type[128];
1037
1038                 // the following five items only need to be recorded if the weapon is a homing weapon
1039                 required_string("+Type:");
1040                 stuff_string(temp_type, F_NAME, NULL);
1041
1042                 if (!SDL_strcasecmp(temp_type, NOX("HEAT"))) {
1043                         float   view_cone_angle;
1044
1045                         wip->wi_flags |= WIF_HOMING_HEAT | WIF_TURNS;
1046
1047                         required_string("+Turn Time:");                 
1048                         stuff_float(&wip->turn_time);
1049
1050                         required_string("+View Cone:");
1051                         stuff_float(&view_cone_angle);
1052
1053                         wip->fov = (float)cos((float)(ANG_TO_RAD(view_cone_angle/2.0f)));
1054
1055                 } else if (!SDL_strcasecmp(temp_type, NOX("ASPECT"))) {
1056                         wip->wi_flags |= WIF_HOMING_ASPECT | WIF_TURNS;
1057
1058                         required_string("+Turn Time:");                 
1059                         stuff_float(&wip->turn_time);
1060
1061                         required_string("+Min Lock Time:");                     // minimum time (in seconds) to achieve lock
1062                         stuff_float(&wip->min_lock_time);
1063
1064                         required_string("+Lock Pixels/Sec:");           // pixels/sec moved while locking
1065                         stuff_int(&wip->lock_pixels_per_sec);
1066
1067                         required_string("+Catch-up Pixels/Sec:");       // pixels/sec moved while catching-up for a lock
1068                         stuff_int(&wip->catchup_pixels_per_sec);
1069
1070                         required_string("+Catch-up Penalty:");          // number of extra pixels to move while locking as a penalty for catching up for a lock
1071                         stuff_int(&wip->catchup_pixel_penalty);
1072                 } else
1073                         Error(LOCATION, "Illegal homing type = %s.\nMust be HEAT or ASPECT.\n", temp_type);
1074
1075         }
1076
1077         // swarm missiles
1078         int s_count;
1079         wip->swarm_count = -1;
1080         if(optional_string("$Swarm:")){
1081                 wip->swarm_count = SWARM_DEFAULT_NUM_MISSILES_FIRED;
1082                 stuff_int(&s_count);
1083                 wip->swarm_count = (short)s_count;
1084
1085                 // flag as being a swarm weapon
1086                 wip->wi_flags |= WIF_SWARM;
1087         }
1088
1089         required_string("$LaunchSnd:");
1090         stuff_int(&wip->launch_snd);
1091
1092         required_string("$ImpactSnd:");
1093         stuff_int(&wip->impact_snd);
1094
1095         if (First_secondary_index != -1) {
1096                 required_string("$FlyBySnd:");
1097                 stuff_int(&wip->flyby_snd);
1098         }
1099
1100         //      Secondary weapons are required to have a rearm rate.
1101         if (First_secondary_index != -1) {
1102                 required_string( "$Rearm Rate:");
1103                 stuff_float( &wip->rearm_rate );
1104                 if (wip->rearm_rate > 0.1f)
1105                         wip->rearm_rate = 1.0f/wip->rearm_rate;
1106                 else
1107                         wip->rearm_rate = 1.0f;
1108         }
1109
1110         wip->weapon_range = 999999999.9f;
1111         if (optional_string("+Weapon Range:")) {
1112                 stuff_float(&wip->weapon_range);
1113         }
1114
1115         wip->spawn_type = -1;
1116         parse_wi_flags(wip);
1117
1118         char trail_name[MAX_FILENAME_LEN] = "";
1119         trail_info *ti = &wip->tr_info;
1120         memset(ti, 0, sizeof(trail_info));
1121 #ifndef MAKE_FS1
1122         if(optional_string("$Trail:")){ 
1123                 wip->wi_flags |= WIF_TRAIL;             // missile leaves a trail
1124
1125                 required_string("+Start Width:");
1126                 stuff_float(&ti->w_start);
1127
1128                 required_string("+End Width:");
1129                 stuff_float(&ti->w_end);
1130
1131                 required_string("+Start Alpha:");
1132                 stuff_float(&ti->a_start);
1133
1134                 required_string("+End Alpha:");
1135                 stuff_float(&ti->a_end);                
1136
1137                 required_string("+Max Life:");
1138                 stuff_float(&ti->max_life);
1139
1140                 ti->stamp = fl2i(1000.0f*ti->max_life)/(NUM_TRAIL_SECTIONS+1);
1141
1142                 required_string("+Bitmap:");
1143                 stuff_string(trail_name, F_NAME, NULL);
1144                 ti->bitmap = bm_load(trail_name);
1145                 // wip->delta_time = fl2i(1000.0f*wip->max_life)/(NUM_TRAIL_SECTIONS+1);                // time between sections.  max_life / num_sections basically.
1146         }
1147 #else
1148         // seemed easier to separate this out from above
1149         int has_trail=0;
1150         required_string("$Trail:");
1151         stuff_boolean(&has_trail);
1152
1153         if (has_trail == 1) {
1154                 wip->wi_flags |= WIF_TRAIL;     // missile leaves a trail
1155
1156                 required_string("+Head Width:");
1157                 stuff_float(&ti->w_start);
1158
1159                 required_string("+Tail Width:");
1160                 stuff_float(&ti->w_end);
1161
1162                 ti->a_start = 1.0;
1163                 ti->a_end = 0.0;
1164
1165                 required_string("+Life:");
1166                 stuff_float(&ti->max_life);
1167
1168                 ti->stamp = fl2i(1000.0f*ti->max_life)/(NUM_TRAIL_SECTIONS+1);
1169
1170                 required_string("+Bitmap:");
1171                 stuff_string(trail_name, F_NAME, NULL);
1172                 ti->bitmap = bm_load(trail_name);
1173         }
1174 #endif
1175
1176         // read in filename for icon that is used in weapons selection
1177         wip->icon_filename[0] = 0;
1178         if ( optional_string("$Icon:") ) {
1179                 stuff_string(wip->icon_filename, F_NAME, NULL);
1180         }
1181
1182         // read in filename for animation that is used in weapons selection
1183         wip->anim_filename[0] = 0;
1184         if ( optional_string("$Anim:") ) {
1185                 stuff_string(wip->anim_filename, F_NAME, NULL);
1186         }
1187
1188         wip->impact_weapon_expl_index = -1;
1189         if ( optional_string("$Impact Explosion:") ) {
1190                 char impact_ani_file[FILESPEC_LENGTH];
1191                 stuff_string(impact_ani_file, F_NAME, NULL);
1192                 if ( SDL_strcasecmp(impact_ani_file,NOX("none")))       {
1193                         wip->impact_weapon_expl_index = get_weapon_expl_info_index(impact_ani_file);
1194                         //int num_frames, fps;
1195                         //wip->impact_explosion_ani = bm_load_animation( impact_ani_file, &num_frames, &fps, 1 );
1196
1197                         required_string("$Impact Explosion Radius:");
1198                         stuff_float(&wip->impact_explosion_radius);
1199                 }
1200         }
1201
1202         // muzzle flash
1203         char mflash_string[255] = "";
1204         wip->muzzle_flash = -1;
1205         if( optional_string("$Muzzleflash:") ){
1206                 stuff_string(mflash_string, F_NAME, NULL);
1207
1208                 // look it up
1209                 wip->muzzle_flash = mflash_lookup(mflash_string);
1210
1211                 if(wip->muzzle_flash >= 0){                     
1212                         wip->wi_flags |= WIF_MFLASH;
1213                 }
1214         }
1215
1216         // EMP optional stuff (if WIF_EMP is not set, none of this matters, anyway)
1217         if( optional_string("$EMP Intensity:") ){
1218                 stuff_float(&wip->emp_intensity);
1219         } else {
1220                 wip->emp_intensity = EMP_DEFAULT_INTENSITY;
1221         }
1222         if( optional_string("$EMP Time:") ){
1223                 stuff_float(&wip->emp_time);
1224         } else {
1225                 wip->emp_intensity = EMP_DEFAULT_TIME;
1226         }
1227
1228         // Energy suck optional stuff (if WIF_ENERGY_SUCK is not set, none of this matters anyway)
1229         if( optional_string("$Leech Weapon:") ){
1230                 stuff_float(&wip->weapon_reduce);
1231         } else {
1232                 wip->weapon_reduce = ESUCK_DEFAULT_WEAPON_REDUCE;
1233         }
1234         if( optional_string("$Leech Afterburner:") ){
1235                 stuff_float(&wip->afterburner_reduce);
1236         } else {
1237                 wip->afterburner_reduce = ESUCK_DEFAULT_AFTERBURNER_REDUCE;
1238         }
1239
1240         // beam weapon optional stuff
1241         wip->b_info.beam_type = -1;
1242         wip->b_info.beam_life = -1.0f;
1243         wip->b_info.beam_warmup = -1;
1244         wip->b_info.beam_warmdown = -1;
1245         wip->b_info.beam_muzzle_radius = 0.0f;
1246         wip->b_info.beam_particle_count = -1;
1247         wip->b_info.beam_particle_radius = 0.0f;
1248         wip->b_info.beam_particle_angle = 0.0f;
1249         wip->b_info.beam_particle_ani = -1;     
1250         wip->b_info.beam_loop_sound = -1;
1251         wip->b_info.beam_warmup_sound = -1;
1252         wip->b_info.beam_warmdown_sound = -1;
1253         wip->b_info.beam_num_sections = 0;
1254         wip->b_info.beam_glow_bitmap = -1;
1255         wip->b_info.beam_shots = 0;
1256         wip->b_info.beam_shrink_factor = 0.0f;
1257         wip->b_info.beam_shrink_pct = 0.0f;
1258         if( optional_string("$BeamInfo:")){
1259                 // beam type
1260                 required_string("+Type:");
1261                 stuff_int(&wip->b_info.beam_type);
1262
1263                 // how long it lasts
1264                 required_string("+Life:");
1265                 stuff_float(&wip->b_info.beam_life);
1266
1267                 // warmup time
1268                 required_string("+Warmup:");
1269                 stuff_int(&wip->b_info.beam_warmup);
1270
1271                 // warmdowm time
1272                 required_string("+Warmdown:");
1273                 stuff_int(&wip->b_info.beam_warmdown);
1274
1275                 // muzzle glow radius
1276                 required_string("+Radius:");
1277                 stuff_float(&wip->b_info.beam_muzzle_radius);
1278
1279                 // particle spew count
1280                 required_string("+PCount:");
1281                 stuff_int(&wip->b_info.beam_particle_count);
1282
1283                 // particle radius
1284                 required_string("+PRadius:");
1285                 stuff_float(&wip->b_info.beam_particle_radius);
1286
1287                 // angle off turret normal
1288                 required_string("+PAngle:");
1289                 stuff_float(&wip->b_info.beam_particle_angle);
1290
1291                 // particle bitmap/ani          
1292                 required_string("+PAni:");
1293                 stuff_string(fname, F_NAME, NULL);
1294                 if(!Fred_running){
1295                         int num_frames, fps;
1296                         wip->b_info.beam_particle_ani = bm_load_animation(fname, &num_frames, &fps, 1);
1297                 }
1298
1299                 // magic miss #
1300                 required_string("+Miss Factor:");               
1301                 for(idx=0; idx<NUM_SKILL_LEVELS; idx++){
1302                         wip->b_info.beam_miss_factor[idx] = 0.00001f;
1303                         stuff_float(&wip->b_info.beam_miss_factor[idx]);
1304                 }
1305
1306                 // beam fire sound
1307                 required_string("+BeamSound:");
1308                 stuff_int(&wip->b_info.beam_loop_sound);
1309
1310                 // warmup sound
1311                 required_string("+WarmupSound:");
1312                 stuff_int(&wip->b_info.beam_warmup_sound);
1313
1314                 // warmdown sound
1315                 required_string("+WarmdownSound:");
1316                 stuff_int(&wip->b_info.beam_warmdown_sound);
1317
1318                 // glow bitmap
1319                 required_string("+Muzzleglow:");
1320                 stuff_string(fname, F_NAME, NULL);
1321                 if(!Fred_running){
1322                         wip->b_info.beam_glow_bitmap = bm_load(fname);
1323                 }
1324
1325                 // # of shots (only used for type D beams)
1326                 required_string("+Shots:");
1327                 stuff_int(&wip->b_info.beam_shots);
1328
1329                 // shrinkage
1330                 required_string("+ShrinkFactor:");
1331                 stuff_float(&wip->b_info.beam_shrink_factor);
1332                 required_string("+ShrinkPct:");
1333                 stuff_float(&wip->b_info.beam_shrink_pct);
1334
1335                 // beam sections
1336                 while( optional_string("$Section:") ){
1337                         beam_weapon_section_info i;
1338                         char tex_name[255] = "";
1339                         
1340                         // section width
1341                         required_string("+Width:");
1342                         stuff_float(&i.width);
1343
1344                         // texture
1345                         required_string("+Texture:");
1346                         stuff_string(tex_name, F_NAME, NULL);
1347                         i.texture = -1;
1348                         if(!Fred_running){
1349                                 i.texture = bm_load(tex_name);
1350                                 if(i.texture >= 0){
1351                                         bm_lock(i.texture, 16, BMP_TEX_OTHER);
1352                                         bm_unlock(i.texture);
1353                                 }
1354                         }
1355
1356                         // rgba inner
1357                         required_string("+RGBA Inner:");
1358                         stuff_byte(&i.rgba_inner[0]);
1359                         stuff_byte(&i.rgba_inner[1]);
1360                         stuff_byte(&i.rgba_inner[2]);
1361                         stuff_byte(&i.rgba_inner[3]);
1362
1363                         // rgba outer
1364                         required_string("+RGBA Outer:");
1365                         stuff_byte(&i.rgba_outer[0]);
1366                         stuff_byte(&i.rgba_outer[1]);
1367                         stuff_byte(&i.rgba_outer[2]);
1368                         stuff_byte(&i.rgba_outer[3]);
1369
1370                         // flicker
1371                         required_string("+Flicker:");
1372                         stuff_float(&i.flicker);                        
1373
1374                         // zadd
1375                         required_string("+Zadd:");
1376                         stuff_float(&i.z_add);
1377
1378                         // maybe copy it
1379                         if(wip->b_info.beam_num_sections < MAX_BEAM_SECTIONS - 1){
1380                                 wip->b_info.sections[wip->b_info.beam_num_sections++] = i;
1381                         }
1382                 }               
1383         }
1384
1385         // tag weapon optional stuff
1386         wip->tag_level = -1;
1387         wip->tag_time = -1.0f;
1388         if( optional_string("$Tag:")){
1389                 stuff_int(&wip->tag_level);
1390                 stuff_float(&wip->tag_time);            
1391                 wip->wi_flags |= WIF_TAG;
1392         }       
1393
1394         return 0;
1395 }
1396
1397 // function to parse the information for a specific ship type.  
1398 void parse_cmeasure()
1399 {
1400         cmeasure_info *cmeasurep;
1401
1402         cmeasurep = &Cmeasure_info[Num_cmeasure_types];
1403
1404         required_string("$Name:");
1405         stuff_string(cmeasurep->cmeasure_name, F_NAME, NULL);
1406
1407 /*$Name:                                        Type One
1408 $Velocity:                              20.0                            ;; speed relative to ship, rear-fired until POF info added, MK, 5/22/97
1409 $Fire Wait:                             0.5
1410 $Lifetime Min:                  1.0                             ;; Minimum lifetime
1411 $Lifetime Max:                  2.0                             ;; Maximum lifetime.  Actual lifetime is rand(min..max).
1412 $LaunchSnd:                             counter_1.wav,  .8, 10, 300     ;; countermeasure 1 fired (sound is 3d)
1413 */
1414
1415         required_string("$Velocity:");
1416         stuff_float( &(cmeasurep->max_speed) );
1417
1418         required_string("$Fire Wait:");
1419         stuff_float( &(cmeasurep->fire_wait) );
1420
1421         required_string("$Lifetime Min:");
1422         stuff_float(&cmeasurep->life_min);
1423
1424         required_string("$Lifetime Max:");
1425         stuff_float(&cmeasurep->life_max);
1426
1427         required_string("$LaunchSnd:");
1428         stuff_int(&cmeasurep->launch_sound);
1429
1430         required_string("$Model:");
1431         stuff_string(cmeasurep->pof_name, F_NAME, NULL);
1432         cmeasurep->model_num = -1;              
1433 }
1434
1435
1436 //      For all weapons that spawn weapons, given an index at weaponp->spawn_type,
1437 // convert the strings in Spawn_names to indices in the Weapon_types array.
1438 void translate_spawn_types()
1439 {
1440         int     i,j;
1441
1442         for (i=0; i<Num_weapon_types; i++)
1443                 if (Weapon_info[i].spawn_type != -1) {
1444                         int     spawn_type = Weapon_info[i].spawn_type;
1445
1446                         for (j=0; j<Num_weapon_types; j++)
1447                                 if (!SDL_strcasecmp(Spawn_names[spawn_type], Weapon_info[j].name)) {
1448                                         Weapon_info[i].spawn_type = (short)j;
1449                                         if (i == j){
1450                                                 Warning(LOCATION, "Weapon %s spawns itself.  Infinite recursion?\n", Weapon_info[i].name);
1451                                         }
1452                                 }
1453                 }
1454 }
1455
1456 void parse_weaponstbl()
1457 {
1458         // open localization
1459         lcl_ext_open();
1460
1461         read_file_text("weapons.tbl");
1462         reset_parse();
1463
1464         Num_weapon_types = 0;
1465         First_secondary_index = -1;
1466         Num_spawn_types = 0;
1467         
1468         required_string("#Primary Weapons");
1469         while (required_string_either("#End", "$Name:")) {
1470                 SDL_assert( Num_weapon_types < MAX_WEAPON_TYPES );
1471                 // AL 28-3-98: If parse_weapon() fails, try next .tbl weapon
1472                 if ( parse_weapon() ) {
1473                         continue;
1474                 }
1475                 Weapon_info[Num_weapon_types].subtype = WP_LASER;
1476                 Num_weapon_types++;
1477         }
1478         required_string("#End");
1479
1480         required_string("#Secondary Weapons");
1481         First_secondary_index = Num_weapon_types;
1482         while (required_string_either("#End", "$Name:")) {
1483                 SDL_assert( Num_weapon_types < MAX_WEAPON_TYPES );
1484                 // AL 28-3-98: If parse_weapon() fails, try next .tbl weapon
1485                 if ( parse_weapon() ) {
1486                         continue;
1487                 }
1488                 Weapon_info[Num_weapon_types].subtype = WP_MISSILE;
1489                 Num_weapon_types++;
1490         }
1491         required_string("#End");
1492
1493         required_string("#Beam Weapons");
1494         while (required_string_either("#End", "$Name:")) {
1495                 SDL_assert( Num_weapon_types < MAX_WEAPON_TYPES );
1496                 // AL 28-3-98: If parse_weapon() fails, try next .tbl weapon
1497                 if ( parse_weapon() ) {
1498                         continue;
1499                 }
1500                 Weapon_info[Num_weapon_types].subtype = WP_BEAM;
1501                 Num_weapon_types++;
1502         }
1503         required_string("#End");
1504
1505         required_string("#Countermeasures");
1506         while (required_string_either("#End", "$Name:")) {
1507                 SDL_assert( Num_cmeasure_types < MAX_CMEASURE_TYPES );
1508                 parse_cmeasure();
1509                 Num_cmeasure_types++;
1510         }
1511
1512         required_string("#End");
1513
1514         // Read in a list of weapon_info indicies that are an ordering of the player weapon precedence.
1515         // This list is used to select an alternate weapon when a particular weapon is not available
1516         // during weapon selection.
1517         required_string("$Player Weapon Precedence:");
1518         Num_player_weapon_precedence = stuff_int_list(Player_weapon_precedence, MAX_WEAPON_TYPES, WEAPON_LIST_TYPE);
1519
1520         translate_spawn_types();
1521
1522         // close localization
1523         lcl_ext_close();
1524 }
1525
1526 void create_weapon_names()
1527 {
1528         int     i;
1529
1530         for (i=0; i<Num_weapon_types; i++)
1531                 Weapon_names[i] = Weapon_info[i].name;
1532 }
1533
1534 // This will get called once at game startup
1535 void weapon_init()
1536 {
1537         if ( !Weapons_inited ) {
1538 #if !(defined(FS2_DEMO) || defined(FS1_DEMO))
1539                 // parse weapon_exp.tbl
1540                 parse_weapon_expl_tbl();
1541 #endif
1542                 // parse weapons.tbl
1543                 try {
1544                         parse_weaponstbl();
1545                         create_weapon_names();
1546                         Weapons_inited = 1;
1547                 } catch (parse_error_t rval) {
1548                         Error(LOCATION, "Error parsing 'weapons.tbl'\r\nError code = %i.\r\n", (int)rval);
1549                 }
1550         }
1551
1552         weapon_level_init();
1553 }
1554
1555
1556 // This will get called at the start of each level.
1557 void weapon_level_init()
1558 {
1559         int i;
1560
1561         // Reset everything between levels
1562         Num_weapons = 0;
1563         for (i=0; i<MAX_WEAPONS; i++)   {
1564                 Weapons[i].objnum = -1;
1565                 Weapons[i].weapon_info_index = -1;
1566         }
1567
1568         trail_level_init();             // reset all missile trails
1569
1570         swarm_level_init();
1571         missile_obj_list_init();
1572         
1573         cscrew_level_init();
1574
1575         // emp effect
1576         emp_level_init();
1577
1578         Weapon_flyby_sound_timer = timestamp(0);
1579         Weapon_impact_timer = 1;        // inited each level, used to reduce impact sounds
1580 }
1581
1582 MONITOR( NumWeaponsRend );      
1583
1584 static const float weapon_glow_scale_f = 2.3f;
1585 static const float weapon_glow_scale_r = 2.3f;
1586 static const float weapon_glow_scale_l = 1.5f;
1587 static const float weapon_glow_alpha = 0.85f;
1588
1589 void weapon_render(object *obj)
1590 {
1591         int num;
1592         weapon_info *wip;
1593         weapon *wp;
1594         color c;
1595
1596         MONITOR_INC(NumWeaponsRend, 1);
1597
1598         SDL_assert(obj->type == OBJ_WEAPON);
1599
1600         num = obj->instance;
1601         wp = &Weapons[num];
1602         wip = &Weapon_info[Weapons[num].weapon_info_index];
1603
1604         switch (wip->render_type) {
1605                 case WRT_LASER: {
1606                         // turn off fogging for good measure
1607                         gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0, -1.0f, -1.0f);
1608
1609                         if (wip->laser_bitmap >= 0) {                                   
1610                                 gr_set_color_fast(&wip->laser_color_1);
1611                                 gr_set_bitmap(wip->laser_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 0.99999f, -1, -1);
1612
1613                                 vector headp;
1614                                 vm_vec_scale_add(&headp, &obj->pos, &obj->orient.v.fvec, wip->laser_length);
1615                                 wp->weapon_flags &= ~WF_CONSIDER_FOR_FLYBY_SOUND;
1616                                 if ( g3_draw_laser(&headp, wip->laser_head_radius, &obj->pos, wip->laser_tail_radius) ) {
1617                                         wp->weapon_flags |= WF_CONSIDER_FOR_FLYBY_SOUND;
1618                                 }
1619                         }                       
1620
1621                         // maybe draw laser glow bitmap
1622 #ifndef MAKE_FS1
1623                         if(wip->laser_glow_bitmap >= 0){
1624 #else
1625                         if ( (Detail.weapon_detail >= 1) && (wip->laser_glow_bitmap >= 0) ) {
1626 #endif
1627                                 // get the laser color
1628                                 weapon_get_laser_color(&c, obj);
1629
1630                                 vector headp2 = ZERO_VECTOR;
1631                                 vm_vec_scale_add(&headp2, &obj->pos, &obj->orient.v.fvec, wip->laser_length * weapon_glow_scale_l);
1632                                 gr_set_bitmap(wip->laser_glow_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, weapon_glow_alpha, -1, -1);
1633                                 g3_draw_laser_rgb(&headp2, wip->laser_head_radius * weapon_glow_scale_f, &obj->pos, wip->laser_tail_radius * weapon_glow_scale_r, c.red, c.green, c.blue);
1634                         }                                               
1635                         break;
1636                 }
1637
1638                 case WRT_POF:   {
1639                                 uint render_flags = MR_NORMAL|MR_IS_MISSILE|MR_NO_LIGHTING;
1640
1641                                 model_clear_instance(wip->model_num);
1642
1643                                 if ( (wip->wi_flags & WIF_THRUSTER) && (wp->thruster_bitmap > -1) ) {
1644                                         float   ft;
1645
1646                                         //      Add noise to thruster geometry.
1647                                         //ft = obj->phys_info.forward_thrust;                                   
1648                                         ft = 1.0f;              // Always use 1.0f for missiles                                 
1649                                         ft *= (1.0f + frand()/5.0f - 1.0f/10.0f);
1650                                         if (ft > 1.0f)
1651                                                 ft = 1.0f;
1652
1653                                         model_set_thrust( wip->model_num, ft, wp->thruster_bitmap, wp->thruster_glow_bitmap, wp->thruster_glow_noise);
1654                                         render_flags |= MR_SHOW_THRUSTERS;
1655                                 }
1656
1657                                 model_render(wip->model_num, &obj->orient, &obj->pos, render_flags);
1658
1659                                 // render a missile plume as well
1660                                 /*
1661                                 static int plume = -1;  
1662                                 extern float Interp_thrust_twist;
1663                                 extern float Interp_thrust_twist2;
1664                                 if(plume == -1){
1665                                         plume = model_load("plume01.pof", -1, NULL);
1666                                 }
1667                                 if(plume != -1){
1668                                         Interp_thrust_twist = tw;
1669                                         Interp_thrust_twist2 = tw2;
1670                                         model_set_alpha(plume_alpha);
1671                                         model_render(plume, &obj->orient, &obj->pos, MR_ALL_XPARENT);
1672                                         Interp_thrust_twist = -1.0f;
1673                                         Interp_thrust_twist2 = -1.0f;
1674                                 }
1675                                 */
1676                         }
1677                         break;
1678
1679                 default:
1680                         Warning(LOCATION, "Unknown weapon rendering type = %i\n", wip->render_type);
1681         }
1682 }
1683
1684 void weapon_delete(object *obj)
1685 {
1686         weapon *wp;
1687         int num;
1688
1689         num = obj->instance;
1690
1691         SDL_assert( Weapons[num].objnum == OBJ_INDEX(obj));
1692         wp = &Weapons[num];
1693
1694         SDL_assert(wp->weapon_info_index >= 0);
1695         wp->weapon_info_index = -1;
1696         if (wp->swarm_index >= 0) {
1697                 swarm_delete(wp->swarm_index);
1698                 wp->swarm_index = -1;
1699         }
1700
1701         if(wp->cscrew_index >= 0) {
1702                 cscrew_delete(wp->cscrew_index);
1703                 wp->cscrew_index = -1;
1704         }
1705
1706         if (wp->missile_list_index >= 0) {
1707                 missle_obj_list_remove(wp->missile_list_index);
1708                 wp->missile_list_index = -1;
1709         }
1710
1711         if (wp->flak_index >= 0){
1712                 flak_delete(wp->flak_index);
1713                 wp->flak_index = -1;
1714         }
1715
1716         if (wp->trail_num > -1) {
1717                 trail_object_died(wp->trail_num);
1718         }
1719
1720         wp->objnum = -1;
1721         Num_weapons--;
1722         SDL_assert(Num_weapons >= 0);
1723 }
1724
1725 // Check if missile is newly locked onto the Player, maybe play a launch warning
1726 void weapon_maybe_play_warning(weapon *wp)
1727 {
1728         if ( wp->homing_object == Player_obj ) {
1729                 if ( !(wp->weapon_flags & WF_LOCK_WARNING_PLAYED) ) {
1730                         wp->weapon_flags |= WF_LOCK_WARNING_PLAYED;
1731                         if ( Weapon_info[wp->weapon_info_index].wi_flags & WIF_HOMING_HEAT ) {
1732                                 snd_play(&Snds[SND_HEATLOCK_WARN]);
1733                         } else {
1734                                 SDL_assert(Weapon_info[wp->weapon_info_index].wi_flags & WIF_HOMING_ASPECT);
1735                                 snd_play(&Snds[SND_ASPECTLOCK_WARN]);
1736                         }
1737                 }
1738         }
1739 }
1740
1741 #define CMEASURE_DETONATE_DISTANCE              40.0f
1742
1743 //      Detonate all missiles near this countermeasure.
1744 void detonate_nearby_missiles(cmeasure *cmp)
1745 {
1746         missile_obj     *mop;
1747         vector          cmeasure_pos;
1748
1749         cmeasure_pos = Objects[cmp->objnum].pos;
1750
1751         mop = GET_FIRST(&Missile_obj_list);
1752         while(mop != END_OF_LIST(&Missile_obj_list)) {
1753                 object  *objp;
1754                 weapon  *wp;
1755
1756                 objp = &Objects[mop->objnum];
1757                 wp = &Weapons[objp->instance];
1758
1759                 if (wp->team != cmp->team) {
1760                         if ( Missiontime - wp->creation_time > F1_0/2) {
1761                                 if (vm_vec_dist_quick(&cmeasure_pos, &objp->pos) < CMEASURE_DETONATE_DISTANCE) {
1762                                         if (wp->lifeleft > 0.2f) { 
1763                                                 //nprintf(("Jim", "Frame %i: Cmeasure #%i detonating weapon #%i\n", Framecount, cmp-Cmeasures, wp-Weapons));
1764                                                 wp->lifeleft = 0.2f;
1765                                                 // nprintf(("AI", "Frame %i: Flagging weapon %i for detonation.\n", Framecount, wp-Weapons));
1766                                         }
1767                                 }
1768                         }
1769                 }
1770
1771                 mop = mop->next;
1772         }
1773 }
1774
1775 //      Find an object for weapon #num (object *weapon_objp) to home on due to heat.
1776 void find_homing_object(object *weapon_objp, int num)
1777 {
1778         object          *objp, *old_homing_objp;
1779         weapon_info     *wip;
1780         weapon          *wp;
1781         float                   best_dist;
1782
1783         wp = &Weapons[num];
1784
1785         wip = &Weapon_info[Weapons[num].weapon_info_index];
1786
1787         best_dist = 99999.9f;
1788
1789         // save the old homing object so that multiplayer servers can give the right information
1790         // to clients if the object changes
1791         old_homing_objp = wp->homing_object;
1792
1793         wp->homing_object = &obj_used_list;
1794
1795         //      Scan all objects, find a weapon to home on.
1796         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
1797                 if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_CMEASURE)) {
1798                         if (objp->type == OBJ_CMEASURE)
1799                                 if (Cmeasures[objp->instance].flags & CMF_DUD_HEAT)
1800                                         continue;
1801
1802                         int homing_object_team = obj_team(objp);
1803                         if ( (homing_object_team != wp->team) || (homing_object_team == TEAM_TRAITOR) ) {
1804                                 float           dist;
1805                                 float           dot;
1806                                 vector  vec_to_object;
1807
1808                                 // AL 2-17-98: If ship is immune to sensors, can't home on it (Sandeep says so)!
1809                                 if ( objp->type == OBJ_SHIP ) {
1810                                         if ( Ships[objp->instance].flags & SF_HIDDEN_FROM_SENSORS ) {
1811                                                 continue;
1812                                         }
1813
1814                                         //      MK, 9/4/99.
1815                                         //      If this is a player object, make sure there aren't already too many homers.
1816                                         //      Only in single player.  In multiplayer, we don't want to restrict it in dogfight on team vs. team.
1817                                         //      For co-op, it's probably also OK.
1818                                         if (!( Game_mode & GM_MULTIPLAYER )) {
1819                                                 int     num_homers = compute_num_homing_objects(objp);
1820                                                 if (Max_allowed_player_homers[Game_skill_level] < num_homers)
1821                                                         continue;
1822                                         }
1823                                 }
1824
1825                                 dist = vm_vec_normalized_dir(&vec_to_object, &objp->pos, &weapon_objp->pos);
1826
1827                                 if (objp->type == OBJ_CMEASURE)
1828                                         dist *= 0.5f;
1829
1830                                 dot = vm_vec_dot(&vec_to_object, &weapon_objp->orient.v.fvec);
1831
1832                                 if (dot > wip->fov) {
1833                                         if (dist < best_dist) {
1834                                                 best_dist = dist;
1835                                                 wp->homing_object = objp;
1836                                                 wp->target_sig = objp->signature;
1837
1838                                                 weapon_maybe_alert_cmeasure_success(objp);
1839                                         }
1840                                 }
1841                         }
1842                 }
1843         }
1844
1845 //      if (wp->homing_object->type == OBJ_CMEASURE)
1846 //              nprintf(("AI", "Frame %i: Weapon #%i homing on cmeasure #%i\n", Framecount, num, objp-Objects));
1847
1848         if (wp->homing_object == Player_obj)
1849                 weapon_maybe_play_warning(wp);
1850
1851         // if the old homing object is different that the new one, send a packet to clients
1852         if ( MULTIPLAYER_MASTER && (old_homing_objp != wp->homing_object) ) {
1853                 send_homing_weapon_info( num );
1854         }
1855 }
1856
1857 //      Scan all countermeasures.  Maybe make weapon_objp home on it.
1858 void find_homing_object_cmeasures_1(object *weapon_objp)
1859 {
1860         object  *objp;
1861         weapon  *wp;
1862         weapon_info     *wip;
1863         float           best_dot, dist, dot;
1864
1865         wp = &Weapons[weapon_objp->instance];
1866         wip = &Weapon_info[wp->weapon_info_index];
1867
1868         best_dot = wip->fov;                    //      Note, setting to this avoids comparison below.
1869
1870         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
1871                 if (objp->type == OBJ_CMEASURE) {
1872                         vector  vec_to_object;
1873                         dist = vm_vec_normalized_dir(&vec_to_object, &objp->pos, &weapon_objp->pos);
1874
1875                         if (dist < MAX_CMEASURE_TRACK_DIST) {
1876                                 float   chance;
1877                                 if (wip->wi_flags & WIF_HOMING_ASPECT) {
1878                                         chance = 1.0f/2.0f;     //      aspect seeker this likely to chase a countermeasure
1879                                 } else {
1880                                         chance = 1.0f/1.5f;     //      heat seeker this likely to chase a countermeasure
1881                                 }
1882                                 if ((objp->signature != wp->cmeasure_ignore_objnum) && (objp->signature != wp->cmeasure_chase_objnum)) {
1883                                         if (frand() < chance) {
1884                                                 wp->cmeasure_ignore_objnum = objp->signature;   //      Don't process this countermeasure again.
1885                                                 //nprintf(("Jim", "Frame %i: Weapon #%i ignoring cmeasure #%i\n", Framecount, OBJ_INDEX(weapon_objp), objp->signature));
1886                                         } else  {
1887                                                 wp->cmeasure_chase_objnum = objp->signature;    //      Don't process this countermeasure again.
1888                                                 //nprintf(("Jim", "Frame %i: Weapon #%i CHASING cmeasure #%i\n", Framecount, OBJ_INDEX(weapon_objp), objp->signature));
1889                                         }
1890                                 }
1891                                 
1892                                 if (objp->signature != wp->cmeasure_ignore_objnum) {
1893
1894                                         dot = vm_vec_dot(&vec_to_object, &weapon_objp->orient.v.fvec);
1895
1896                                         if (dot > best_dot) {
1897                                                 //nprintf(("Jim", "Frame %i: Weapon #%i homing on cmeasure #%i\n", Framecount, weapon_objp-Objects, objp->signature));
1898                                                 best_dot = dot;
1899                                                 wp->homing_object = objp;
1900                                                 weapon_maybe_alert_cmeasure_success(objp);
1901                                         }
1902                                 }
1903                         }
1904                 }
1905         }
1906 }
1907
1908
1909 //      Someone launched countermeasures.
1910 //      For all heat-seeking homing objects, see if should favor tracking a countermeasure instead.
1911 void find_homing_object_cmeasures()
1912 {
1913         object  *weapon_objp;
1914
1915         // nprintf(("AI", "Scanning for countermeasures in frame %i\n", Framecount));
1916
1917         if (Cmeasures_homing_check == 0)
1918                 return;
1919
1920         if (Cmeasures_homing_check <= 0)
1921                 Cmeasures_homing_check = 1;
1922
1923         Cmeasures_homing_check--;
1924
1925         for (weapon_objp = GET_FIRST(&obj_used_list); weapon_objp != END_OF_LIST(&obj_used_list); weapon_objp = GET_NEXT(weapon_objp) ) {
1926                 if (weapon_objp->type == OBJ_WEAPON) {
1927                         weapon_info     *wip = &Weapon_info[Weapons[weapon_objp->instance].weapon_info_index];
1928
1929                         if (wip->wi_flags & WIF_HOMING)
1930                                 find_homing_object_cmeasures_1(weapon_objp);
1931                 }
1932         }
1933
1934 }
1935
1936 //      Find object with signature "sig" and make weapon home on it.
1937 void find_homing_object_by_sig(object *weapon_objp, int sig)
1938 {
1939         ship_obj                *sop;
1940         weapon          *wp;
1941         object          *old_homing_objp;
1942
1943         wp = &Weapons[weapon_objp->instance];
1944
1945         // save the old object so that multiplayer masters know whether to send a homing update packet
1946         old_homing_objp = wp->homing_object;
1947
1948         sop = GET_FIRST(&Ship_obj_list);
1949         while(sop != END_OF_LIST(&Ship_obj_list)) {
1950                 object  *objp;
1951
1952                 objp = &Objects[sop->objnum];
1953                 if (objp->signature == sig) {
1954                         wp->homing_object = objp;
1955                         wp->target_sig = objp->signature;
1956                         break;
1957                 }
1958
1959                 sop = sop->next;
1960         }
1961
1962         // if the old homing object is different that the new one, send a packet to clients
1963         if ( MULTIPLAYER_MASTER && (old_homing_objp != wp->homing_object) ) {
1964                 send_homing_weapon_info( weapon_objp->instance );
1965         }
1966
1967 }
1968
1969 //      Make weapon num home.  It's also object *obj.
1970 void weapon_home(object *obj, int num, float frame_time)
1971 {
1972         weapon          *wp;
1973         weapon_info     *wip;
1974         object          *hobjp;
1975
1976         SDL_assert(obj->type == OBJ_WEAPON);
1977         SDL_assert(obj->instance == num);
1978         wp = &Weapons[num];
1979         wip = &Weapon_info[wp->weapon_info_index];
1980         hobjp = Weapons[num].homing_object;
1981
1982         //      If not 1/2 second gone by, don't home yet.
1983         if ((hobjp == &obj_used_list) || ( f2fl(Missiontime - wp->creation_time) < 0.25f )) {
1984                 //      If this is a heat seeking homing missile and 1/2 second has elapsed since firing
1985                 //      and we don't have a target (else we wouldn't be inside the IF), find a new target.
1986                 if (wip->wi_flags & WIF_HOMING_HEAT)
1987                         if ( f2fl(Missiontime - wp->creation_time) > 0.5f )
1988                                 find_homing_object(obj, num);
1989
1990                 if (obj->phys_info.speed > wip->max_speed) {
1991                         obj->phys_info.speed -= frame_time * 4;
1992                         vm_vec_copy_scale( &obj->phys_info.desired_vel, &obj->orient.v.fvec, obj->phys_info.speed);
1993                 } else if ((obj->phys_info.speed < wip->max_speed/4) && (wip->wi_flags & WIF_HOMING_HEAT)) {
1994                         obj->phys_info.speed = wip->max_speed/4;
1995                         vm_vec_copy_scale( &obj->phys_info.desired_vel, &obj->orient.v.fvec, obj->phys_info.speed);
1996                 }
1997
1998 /*      Removed code that makes bombs drop for a bit.  They looked odd and it was confusing.  People wondered where their weapons went.
1999                 //      Make bombs drop down for first second of life.
2000                 if (wip->wi_flags & WIF_BOMB) {
2001                         if (wip->lifetime - wp->lifeleft < 0.5f) {
2002                                 float   time_scale = wip->lifetime - wp->lifeleft;
2003                                 vm_vec_scale_add2(&obj->phys_info.desired_vel, &obj->orient.uvec, (time_scale - 0.5f) * SDL_max(10.0f, obj->phys_info.speed/2.0f));
2004                         }
2005                 }
2006 */
2007                 return;
2008         }
2009
2010         // AL 4-8-98: If orgiginal target for aspect lock missile is lost, stop homing
2011         if (wip->wi_flags & WIF_HOMING_ASPECT) {
2012                 if ( wp->target_sig > 0 ) {
2013                         if ( wp->homing_object->signature != wp->target_sig ) {
2014                                 wp->homing_object = &obj_used_list;
2015                                 return;
2016                         }
2017                 }
2018         }
2019
2020         // AL 4-13-98: Stop homing on a subsystem if parent ship has changed
2021         if (wip->wi_flags & WIF_HOMING_HEAT) {
2022                 if ( wp->target_sig > 0 ) {
2023                         if ( wp->homing_object->signature != wp->target_sig ) {
2024                                 wp->homing_subsys = NULL;
2025                         }
2026                 }
2027         }
2028
2029 /*
2030         if (hobjp->type == OBJ_NONE) {
2031                 find_homing_object(obj, num);
2032                 return;
2033         }
2034 */
2035
2036         switch (hobjp->type) {
2037         case OBJ_NONE:
2038                 if (wip->wi_flags & WIF_HOMING_ASPECT)
2039                         find_homing_object_by_sig(obj, wp->target_sig);
2040                 else
2041                         find_homing_object(obj, num);
2042                 return;
2043                 break;
2044         case OBJ_SHIP:
2045                 if (hobjp->signature != wp->target_sig) {
2046                         if (wip->wi_flags & WIF_HOMING_ASPECT)
2047                                 find_homing_object_by_sig(obj, wp->target_sig);
2048                         else
2049                                 find_homing_object(obj, num);
2050                         return;
2051                 }
2052                 break;
2053         case OBJ_WEAPON:
2054                 // only allowed to home on bombs
2055                 SDL_assert(Weapon_info[Weapons[hobjp->instance].weapon_info_index].wi_flags & WIF_BOMB);
2056                 if (wip->wi_flags & WIF_HOMING_ASPECT)
2057                         find_homing_object_by_sig(obj, wp->target_sig);
2058                 else
2059                         find_homing_object(obj, num);
2060                 break;
2061         case OBJ_CMEASURE:
2062                 break;
2063         default:
2064                 return;
2065         }
2066
2067         //      See if this weapon is the nearest homing object to the object it is homing on.
2068         //      If so, update some fields in the target object's ai_info.
2069         if (hobjp != &obj_used_list) {
2070                 float   dist;
2071
2072                 dist = vm_vec_dist_quick(&obj->pos, &hobjp->pos);
2073
2074                 if (hobjp->type == OBJ_SHIP) {
2075                         ai_info *aip;
2076
2077                         aip = &Ai_info[Ships[hobjp->instance].ai_index];
2078
2079                         if ((aip->nearest_locked_object == -1) || (dist < aip->nearest_locked_distance)) {
2080                                 aip->nearest_locked_object = obj-Objects;
2081                                 aip->nearest_locked_distance = dist;
2082                         }
2083                 }
2084         }
2085
2086         //      If the object it is homing on is still valid, home some more!
2087         if (hobjp != &obj_used_list) {
2088                 float           old_dot, vel;
2089                 vector  vec_to_goal;
2090                 vector  target_pos;     // position of what the homing missile is seeking
2091
2092                 vm_vec_zero(&target_pos);
2093
2094                 // the homing missile may be seeking a subsystem on a ship.  If so, we need to calculate the
2095                 // world coordinates of that subsystem so the homing missile can seek it out.
2096                 //      For now, March 7, 1997, MK, heat seeking homing missiles will be able to home on
2097                 //      any subsystem.  Probably makes sense for them to only home on certain kinds of subsystems.
2098                 if ( wp->homing_subsys != NULL ) {
2099                         get_subsystem_world_pos(hobjp, Weapons[num].homing_subsys, &target_pos);
2100                         wp->homing_pos = target_pos;    // store the homing position in weapon data
2101                         SDL_assert( !vm_is_vec_nan(&wp->homing_pos) );
2102                 } else {
2103                         float   fov;
2104                         float   dist;
2105
2106                         dist = vm_vec_dist_quick(&obj->pos, &hobjp->pos);
2107                         if (hobjp->type == OBJ_CMEASURE) {
2108                                 if (dist < CMEASURE_DETONATE_DISTANCE) {
2109                                         cmeasure        *cmp;
2110
2111                                         cmp = &Cmeasures[hobjp->instance];
2112
2113                                         //      Make this missile detonate soon.  Not right away, not sure why.  Seems better.
2114                                         if (cmp->team != wp->team) {
2115                                                 detonate_nearby_missiles(cmp);
2116                                                 //nprintf(("AI", "Frame %i: Weapon %i hit cmeasure, will die!\n", Framecount, wp-Weapons));
2117                                                 return;
2118                                         }
2119                                 }
2120                         }
2121
2122                         fov = 0.8f;
2123                         if (wip->fov > 0.8f)
2124                                 fov = wip->fov;
2125
2126                         int pick_homing_point = 0;
2127                         if ( IS_VEC_NULL(&wp->homing_pos) ) {
2128                                 pick_homing_point = 1;
2129                         }
2130
2131                         //      Update homing position if it hasn't been set, you're within 500 meters, or every half second, approximately.
2132                         //      For large objects, don't lead them.
2133                         if (hobjp->radius < 40.0f) {
2134                                 target_pos = hobjp->pos;
2135                                 wp->homing_pos = target_pos;
2136                         } else if ( pick_homing_point || (dist < 500.0f) || (rand_chance(flFrametime, 2.0f)) ) {
2137
2138                                 if (hobjp->type == OBJ_SHIP) {
2139                                         if ( !pick_homing_point ) {
2140                                                 // ensure that current attack point is only updated in world coords (ie not pick a different vertex)
2141                                                 wp->pick_big_attack_point_timestamp = 0;
2142                                         }
2143
2144                                         if ( pick_homing_point ) {
2145                                                 // If *any* player is parent of homing missile, then use position where lock indicator is
2146                                                 if ( Objects[obj->parent].flags & OF_PLAYER_SHIP ) {
2147                                                         player *pp;
2148
2149                                                         // determine the player
2150                                                         pp = Player;
2151                                                         if ( Game_mode & GM_MULTIPLAYER ) {
2152                                                                 int pnum;
2153
2154                                                                 pnum = multi_find_player_by_object( &Objects[obj->parent] );
2155                                                                 if ( pnum != -1 ){
2156                                                                         pp = Net_players[pnum].player;
2157                                                                 }
2158                                                         }
2159
2160                                                         // If player has apect lock, we don't want to find a homing point on the closest
2161                                                         // octant... setting the timestamp to 0 ensures this.
2162                                                         if (wip->wi_flags & WIF_HOMING_ASPECT) {
2163                                                                 wp->pick_big_attack_point_timestamp = 0;
2164                                                         } else {
2165                                                                 wp->pick_big_attack_point_timestamp = 1;
2166                                                         }
2167
2168                                                         if ( pp && pp->locking_subsys ) {
2169                                                                 wp->big_attack_point = pp->locking_subsys->system_info->pnt;
2170                                                         } else {
2171                                                                 vm_vec_zero(&wp->big_attack_point);
2172                                                         }
2173                                                 }
2174                                         }
2175
2176                                         ai_big_pick_attack_point(hobjp, obj, &target_pos, fov);
2177
2178                                 } else {
2179                                         target_pos = hobjp->pos;
2180                                 }
2181
2182                                 wp->homing_pos = target_pos;
2183                                 SDL_assert( !vm_is_vec_nan(&wp->homing_pos) );
2184                                 // nprintf(("AI", "Attack point = %7.3f %7.3f %7.3f\n", target_pos.xyz.x, target_pos.xyz.y, target_pos.xyz.z));
2185                         } else
2186                                 target_pos = wp->homing_pos;
2187                 }
2188
2189                 //      Couldn't find a lock.
2190                 if (IS_VEC_NULL(&target_pos))
2191                         return;
2192
2193                 //      Cause aspect seeking weapon to home at target's predicted position.
2194                 //      But don't use predicted position if dot product small or negative.
2195                 //      If do this, with a ship headed towards missile, could choose a point behind missile.``1
2196                 float   dist_to_target, time_to_target;
2197                 
2198                 dist_to_target = vm_vec_normalized_dir(&vec_to_goal, &target_pos, &obj->pos);
2199                 time_to_target = dist_to_target/wip->max_speed;
2200
2201                 vector  tvec;
2202                 tvec = obj->phys_info.vel;
2203                 vm_vec_normalize(&tvec);
2204
2205                 old_dot = vm_vec_dot(&tvec, &vec_to_goal);
2206
2207                 //      If a weapon has missed its target, detonate it.
2208                 //      This solves the problem of a weapon circling the center of a subsystem that has been blown away.
2209                 //      Problem: It does not do impact damage, just proximity damage.
2210                 if ((dist_to_target < flFrametime * obj->phys_info.speed * 4.0f + 10.0f) && (old_dot < 0.0f)) {
2211                         int kill_missile = TRUE;
2212                         if (wp->homing_object) {
2213                                 if (wp->homing_object->type == OBJ_SHIP) {
2214                                         ship *shipp = &Ships[wp->homing_object->instance];
2215                                         if (Ship_info[shipp->ship_info_index].flags & SIF_DONT_COLLIDE_INVIS) {
2216                                                 kill_missile = FALSE;
2217                                         }
2218                                 }
2219                         }
2220                         
2221                         if (kill_missile && (wp->lifeleft > 0.01f)) {
2222                                 wp->lifeleft = 0.01f;
2223                         }
2224                 }
2225
2226                 //      Only lead target if more than one second away.  Otherwise can miss target.  I think this
2227                 //      is what's causing Harbingers to miss the super destroyer. -- MK, 4/15/98
2228                 if ((wip->wi_flags & WIF_HOMING_ASPECT) && (old_dot > 0.1f) && (time_to_target > 0.1f))
2229                         vm_vec_scale_add2(&target_pos, &hobjp->phys_info.vel, SDL_min(time_to_target, 2.0f));
2230
2231                 //nprintf(("AI", "Dot = %7.3f, dist = %7.3f, time_to = %6.3f, deg/sec = %7.3f\n", old_dot, dist_to_target, time_to_target, angles/flFrametime));
2232
2233                 // nprintf(("AI", "Weapon %i, lifeleft = %7.3f, dist = %7.3f, dot = %6.3f\n", num, Weapons[num].lifeleft, vm_vec_dist_quick(&obj->pos, &Weapons[num].homing_object->pos), old_dot));
2234
2235                 //      If a HEAT seeking (rather than ASPECT seeking) homing missile, verify that target is in viewcone.
2236                 if (wip->wi_flags & WIF_HOMING_HEAT) {
2237                         if ((old_dot < wip->fov) && (dist_to_target > wip->inner_radius*1.1f)) {        //      Delay finding new target one frame to allow detonation.
2238                                 find_homing_object(obj, num);
2239                                 return;                 //      Maybe found a new homing object.  Return, process more next frame.
2240                         } else  //      Subtract out life based on how far from target this missile points.
2241                                 if (wip->fov < 0.95f) {
2242                                         wp->lifeleft -= flFrametime * (0.95f - old_dot);
2243                                         //Should only happen when time is compressed.
2244                                         //if (flFrametime * (1.0f - old_dot) > 1.0f)
2245                                         //      Int3();
2246                                 }
2247                 } else if (wip->wi_flags & WIF_HOMING_ASPECT) { //      subtract life as if max turn is 90 degrees.
2248                         if (wip->fov < 0.95f)
2249                                 wp->lifeleft -= flFrametime * (0.95f - old_dot);
2250                 } else
2251                         SDL_assert(0);  //      Hmm, a homing missile, but not aspect or heat?
2252
2253
2254                 //      Control speed based on dot product to goal.  If close to straight ahead, move
2255                 //      at max speed, else move slower based on how far from ahead.
2256                 if (old_dot < 0.90f) {
2257                         obj->phys_info.speed = SDL_max(0.2f, old_dot* (float) fabs(old_dot));
2258                         if (obj->phys_info.speed < wip->max_speed*0.75f)
2259                                 obj->phys_info.speed = wip->max_speed*0.75f;
2260                 } else
2261                         obj->phys_info.speed = wip->max_speed;
2262
2263                 //      For first second of weapon's life, it doesn't fly at top speed.  It ramps up.
2264                 if (Missiontime - wp->creation_time < i2f(1)) {
2265                         float   t;
2266
2267                         t = f2fl(Missiontime - wp->creation_time);
2268                         obj->phys_info.speed *= t*t;
2269                 }
2270
2271                 SDL_assert( obj->phys_info.speed > 0.0f );
2272
2273                 vm_vec_copy_scale( &obj->phys_info.desired_vel, &obj->orient.v.fvec, obj->phys_info.speed);
2274
2275                 // turn the missile towards the target only if non-swarm.  Homing swarm missiles choose
2276                 // a different vector to turn towards, this is done in swarm_update_direction().
2277 //              if ( !(wip->wi_flags & WIF_SWARM) ) {
2278                 if ( wp->swarm_index < 0 ) {
2279                         // nprintf(("AI", "Dot, dist = %7.3f, %7.3f, target pos = %7.3f %7.3f %7.3f\n", old_dot, vm_vec_dist_quick(&obj->pos, &target_pos), target_pos.xyz.x, target_pos.xyz.y, target_pos.xyz.z));
2280                         ai_turn_towards_vector(&target_pos, obj, frame_time, wip->turn_time, NULL, NULL, 0.0f, 0, NULL);
2281                         vel = vm_vec_mag(&obj->phys_info.desired_vel);
2282
2283                         vm_vec_copy_scale(&obj->phys_info.desired_vel, &obj->orient.v.fvec, vel);
2284
2285                 }
2286
2287 /*              //      If this weapon shot past its target, make it detonate.
2288                 if ((old_dot < 0.0f) && (dist_to_target < 50.0f)) {
2289                         if (wp->lifeleft > 0.01f)
2290                                 wp->lifeleft = 0.01f;
2291                 }
2292 */      }
2293 }
2294
2295 // as Mike K did with ships -- break weapon into process_pre and process_post for code to execute
2296 // before and after physics movement
2297
2298 void weapon_process_pre( object *obj, float frame_time)
2299 {
2300         // if the object is a corkscrew style weapon, process it now
2301         if((obj->type == OBJ_WEAPON) && (Weapons[obj->instance].cscrew_index >= 0)){
2302                 cscrew_process_pre(obj);
2303         }
2304
2305         // if the weapon is a flak weapon, maybe detonate it early
2306         if((obj->type == OBJ_WEAPON) && (Weapon_info[Weapons[obj->instance].weapon_info_index].wi_flags & WIF_FLAK) && (Weapons[obj->instance].flak_index >= 0)){
2307                 flak_maybe_detonate(obj);               
2308         }
2309 }
2310
2311 int     Homing_hits = 0, Homing_misses = 0;
2312
2313
2314 MONITOR( NumWeapons );  
2315
2316 // maybe play a "whizz sound" if close enough to view position
2317 void weapon_maybe_play_flyby_sound(object *weapon_objp, weapon *wp)
2318 {       
2319         // do a quick out if not a laser
2320         if ( Weapon_info[wp->weapon_info_index].subtype != WP_LASER ) {
2321                 return;
2322         }
2323
2324         // don't play flyby sounds too close together
2325         if ( !timestamp_elapsed(Weapon_flyby_sound_timer) ) {
2326                 return;
2327         }
2328
2329         if ( !(wp->weapon_flags & WF_PLAYED_FLYBY_SOUND) && (wp->weapon_flags & WF_CONSIDER_FOR_FLYBY_SOUND) ) {
2330                 float           dist, dot, radius;
2331
2332                 dist = vm_vec_dist_quick(&weapon_objp->pos, &Eye_position);
2333
2334                 if ( Viewer_obj ) {
2335                         radius = Viewer_obj->radius;
2336                 } else {
2337                         radius = 0.0f;
2338                 }
2339
2340                 if ( (dist > radius) && (dist < 55) ) {
2341                         vector  vec_to_weapon;
2342
2343                         vm_vec_sub(&vec_to_weapon, &weapon_objp->pos, &Eye_position);
2344                         vm_vec_normalize(&vec_to_weapon);
2345
2346                         // ensure laser is in front of eye
2347                         dot = vm_vec_dot(&vec_to_weapon, &Eye_matrix.v.fvec);
2348                         if ( dot < 0.1 ) {
2349                                 return;
2350                         }
2351
2352                         // ensure that laser is moving in similar direction to fvec
2353                         dot = vm_vec_dot(&vec_to_weapon, &weapon_objp->orient.v.fvec);
2354                         
2355 //                      nprintf(("Alan", "Weapon dot: %.2f\n", dot));
2356                         if ( (dot < -0.80) && (dot > -0.98) ) {
2357                                 snd_play_3d( &Snds[SND_WEAPON_FLYBY], &weapon_objp->pos, &Eye_position );
2358                                 Weapon_flyby_sound_timer = timestamp(200);
2359                                 wp->weapon_flags |= WF_PLAYED_FLYBY_SOUND;
2360                         }
2361                 }
2362         }
2363 }
2364
2365 // process a weapon after physics movement.  MWA reorders some of the code on 8/13 for multiplayer.  When
2366 // adding something to this function, decide whether or not a client in a multiplayer game needs to do
2367 // what is normally done in a single player game.  Things like plotting an object on a radar, effect
2368 // for exhaust are things that are done on all machines.  Things which calculate weapon targets, new
2369 // velocities, etc, are server only functions and should go after the if ( !MULTIPLAYER_MASTER ) statement
2370 // See Allender if you cannot decide what to do.
2371 void weapon_process_post(object * obj, float frame_time)
2372 {
2373         int                     num;    
2374         weapon_info     *wip;
2375         weapon          *wp;
2376
2377         MONITOR_INC( NumWeapons, 1 );   
2378         
2379         SDL_assert(obj->type == OBJ_WEAPON);
2380
2381         num = obj->instance;
2382
2383 #ifndef NDEBUG
2384         int objnum = OBJ_INDEX(obj);
2385         SDL_assert( Weapons[num].objnum == objnum );
2386 #endif
2387
2388         wp = &Weapons[num];
2389
2390         wp->lifeleft -= frame_time;
2391         wip = &Weapon_info[wp->weapon_info_index];
2392
2393         // check life left.  Multiplayer client code will go through here as well.  We must be careful in weapon_hit
2394         // when killing a missile that spawn child weapons!!!!
2395         if ( wp->lifeleft < 0.0f ) {
2396                 if ( wip->subtype & WP_MISSILE ) {
2397                         if(Game_mode & GM_MULTIPLAYER){                         
2398                                 if ( !MULTIPLAYER_CLIENT || (MULTIPLAYER_CLIENT && (wp->lifeleft < -2.0f)) || (MULTIPLAYER_CLIENT && (wip->wi_flags & WIF_CHILD))) {                                    // don't call this function multiplayer client -- host will send this packet to us
2399                                         // nprintf(("AI", "Frame %i: Weapon %i detonated, dist = %7.3f!\n", Framecount, obj-Objects));
2400                                         weapon_detonate(obj);                                   
2401                                 }
2402                         } else {
2403                                 // nprintf(("AI", "Frame %i: Weapon %i detonated, dist = %7.3f!\n", Framecount, obj-Objects));
2404                                 weapon_detonate(obj);                                                                   
2405                         }
2406                         if (wip->wi_flags & WIF_HOMING) {
2407                                 Homing_misses++;
2408                                 // nprintf(("AI", "Miss!  Hits = %i/%i\n", Homing_hits, (Homing_hits + Homing_misses)));
2409                         }
2410                 } else {
2411                         obj->flags |= OF_SHOULD_BE_DEAD;
2412 //                      demo_do_flag_dead(OBJ_INDEX(obj));
2413                 }
2414
2415                 return;
2416         }
2417
2418         // plot homing missiles on the radar
2419         if (wip->wi_flags & WIF_HOMING) {
2420                 if ( hud_gauge_active(HUD_RADAR) ) {
2421                         radar_plot_object( obj );
2422                 }
2423         }
2424
2425         // trail missiles
2426         if ((wip->wi_flags & WIF_TRAIL) && !(wip->wi_flags & WIF_CORKSCREW)) {
2427                 if ( wp->trail_num > -1 )       {
2428                         if (trail_stamp_elapsed(wp->trail_num)) {
2429
2430                                 trail_add_segment( wp->trail_num, &obj->pos );
2431                                 
2432                                 trail_set_stamp(wp->trail_num);
2433                         } else {
2434                                 trail_set_segment( wp->trail_num, &obj->pos );
2435                         }
2436
2437                 }
2438         }
2439
2440         if ( wip->wi_flags & WIF_THRUSTER )     {
2441                 ship_do_weapon_thruster_frame( wp, obj, flFrametime );  
2442         }
2443
2444         // maybe play a "whizz sound" if close enough to view position
2445         #ifndef NDEBUG
2446         if ( Weapon_flyby_sound_enabled ) {
2447                 weapon_maybe_play_flyby_sound(obj, wp);
2448         }
2449         #else
2450                 weapon_maybe_play_flyby_sound(obj, wp);
2451         #endif  
2452         
2453         //      If our target is still valid, then update some info.
2454         if (wp->target_num != -1) {
2455                 if (Objects[wp->target_num].signature == wp->target_sig) {
2456                         float           cur_dist;
2457                         vector  v0;
2458
2459                         vm_vec_avg(&v0, &obj->pos, &obj->last_pos);
2460
2461                         cur_dist = vm_vec_dist_quick(&v0, &Objects[wp->target_num].pos);
2462
2463                         if (cur_dist < wp->nearest_dist) {
2464                                 wp->nearest_dist = cur_dist;
2465                         } else if (cur_dist > wp->nearest_dist + 1.0f) {
2466                                 float           dot;
2467                                 vector  tvec;
2468                                 ai_info *parent_aip;
2469                         //      float           lead_scale = 0.0f;
2470
2471                                 parent_aip = NULL;
2472                                 if (obj->parent != Player_obj-Objects) {
2473                                         parent_aip = &Ai_info[Ships[Objects[obj->parent].instance].ai_index];
2474                                 //      lead_scale = parent_aip->lead_scale;
2475                                 }
2476
2477                                 vm_vec_normalized_dir(&tvec, &v0, &Objects[wp->target_num].pos);
2478                                 dot = vm_vec_dot(&tvec, &Objects[wp->target_num].orient.v.fvec);
2479                                 // nprintf(("AI", "Miss dot = %7.3f, dist = %7.3f, lead_scale = %7.3f\n", dot, cur_dist, lead_scale));
2480                                 wp->target_num = -1;
2481
2482                                 //      Learn!  If over-shooting or under-shooting, compensate.
2483                                 //      Really need to compensate for left/right errors.  This does no good against someone circling
2484                                 //      in a plane perpendicular to the attacker's forward vector.
2485                                 if (parent_aip != NULL) {
2486                                         if (cur_dist > 100.0f)
2487                                                 parent_aip->lead_scale = 0.0f;
2488
2489                                         if (dot < -0.1f){
2490                                                 parent_aip->lead_scale += cur_dist/2000.0f;
2491                                         } else if (dot > 0.1f) {
2492                                                 parent_aip->lead_scale -= cur_dist/2000.0f;
2493                                         }
2494                                         
2495                                         if (fl_abs(parent_aip->lead_scale) > 1.0f){
2496                                                 parent_aip->lead_scale *= 0.9f;
2497                                         }
2498                                 }
2499                         }
2500                 }
2501         }
2502
2503         if(wip->wi_flags & WIF_PARTICLE_SPEW){
2504                 weapon_maybe_spew_particle(obj);
2505         }
2506
2507         // a single player or multiplayer server function -- it affects actual weapon movement.
2508         if (wip->wi_flags & WIF_HOMING) {
2509                 weapon_home(obj, num, frame_time);
2510
2511 /*              if (wip->wi_flags & WIF_BOMB) {
2512                         if (wip->lifetime - obj->lifeleft < 1.0f) {
2513                                 
2514                         }
2515                 }
2516 */              
2517                 // If this is a swarm type missile, 
2518 //              if ( wip->wi_flags & WIF_SWARM ) 
2519                 if ( wp->swarm_index >= 0 ) {
2520                         swarm_update_direction(obj, frame_time);
2521                 }
2522
2523                 if( wp->cscrew_index >= 0) {
2524                         cscrew_process_post(obj);                       
2525                 }
2526         }
2527 }
2528
2529 //      Update weapon tracking information.
2530 void weapon_set_tracking_info(int weapon_objnum, int parent_objnum, int target_objnum, int target_is_locked, ship_subsys *target_subsys)
2531 {
2532         int                     ai_index;
2533         object          *parent_objp;
2534         weapon          *wp;
2535         weapon_info     *wip;
2536         int targeting_same = 0;
2537
2538         if ( weapon_objnum < 0 ) {
2539                 return;
2540         }
2541
2542         SDL_assert(Objects[weapon_objnum].type == OBJ_WEAPON);
2543
2544         wp = &Weapons[Objects[weapon_objnum].instance];
2545         wip = &Weapon_info[wp->weapon_info_index];
2546         parent_objp = &Objects[parent_objnum];
2547
2548         SDL_assert(parent_objp->type == OBJ_SHIP);
2549         ai_index = Ships[parent_objp->instance].ai_index;
2550
2551         if ( ai_index >= 0 ) {
2552                 int target_team = -1;
2553                 if ( target_objnum >= 0 ) {
2554                         int obj_type = Objects[target_objnum].type;
2555                         if ( (obj_type == OBJ_SHIP) || (obj_type == OBJ_WEAPON) ) {
2556                                 target_team = obj_team(&Objects[target_objnum]);
2557                         }
2558                 }
2559         
2560                 // determining if we're targeting the same team
2561                 if(Ships[parent_objp->instance].team == target_team){
2562                         targeting_same = 1;
2563                 } else {
2564                         targeting_same = 0;
2565                 }
2566
2567                 if ((target_objnum != -1) && (!targeting_same || ((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT) && (target_team == TEAM_TRAITOR))) ) {
2568                         wp->target_num = target_objnum;
2569                         wp->target_sig = Objects[target_objnum].signature;
2570                         wp->nearest_dist = 99999.0f;
2571                         if ( (wip->wi_flags & WIF_HOMING_ASPECT) && target_is_locked) {
2572                                 wp->homing_object = &Objects[target_objnum];
2573                                 wp->homing_subsys = target_subsys;
2574                                 weapon_maybe_play_warning(wp);
2575                         } else if ( wip->wi_flags & WIF_HOMING_HEAT ) {
2576                                 //      Make a heat seeking missile try to home.  If the target is outside the view cone, it will
2577                                 //      immediately drop it and try to find one in its view cone.
2578                                 if (target_objnum != -1) {
2579                                         wp->homing_object = &Objects[target_objnum];
2580                                         weapon_maybe_play_warning(wp);
2581                                 } else
2582                                         wp->homing_object = &obj_used_list;
2583
2584                                 wp->homing_subsys = target_subsys;
2585                         }
2586                 } else {
2587                         wp->target_num = -1;
2588                         wp->target_sig = -1;
2589                 }
2590
2591                 //      If missile is locked on target, increase its lifetime by 20% since missiles can be fired at limit of range
2592                 //      as defined by velocity*lifeleft, but missiles often slow down a bit, plus can be fired at a moving away target.
2593                 //      Confusing to many players when their missiles run out of gas before getting to target.  
2594                 // DB - removed 7:14 pm 9/6/99. was totally messing up lifetimes for all weapons.
2595                 //      MK, 7:11 am, 9/7/99.  Put it back in, but with a proper check here to make sure it's an aspect seeker and
2596                 //      put a sanity check in the color changing laser code that was broken by this code.
2597                 if (target_is_locked && (wp->target_num != -1) && (wip->wi_flags & WIF_HOMING_ASPECT) ) {
2598                         wp->lifeleft *= 1.2f;
2599                 }
2600
2601                 ai_update_danger_weapon(target_objnum, weapon_objnum);          
2602         }
2603 }
2604
2605
2606 // weapon_create() will create a weapon object
2607 //
2608 // Returns:  index of weapon in the Objects[] array, -1 if the weapon object was not created
2609 int Weapons_created = 0;
2610 int weapon_create( vector * pos, matrix * orient, int weapon_id, int parent_objnum, int secondary_flag, int group_id, int is_locked )
2611 {
2612         int                     n, objnum;
2613         int num_deleted;
2614         object          *objp, *parent_objp;
2615         weapon          *wp;
2616         weapon_info     *wip;
2617
2618         SDL_assert(weapon_id >= 0 && weapon_id < Num_weapon_types);
2619
2620         // beam weapons should never come through here!
2621         SDL_assert(!(Weapon_info[weapon_id].wi_flags & WIF_BEAM));
2622
2623         num_deleted = 0;
2624         if (Num_weapons >= MAX_WEAPONS-5) {
2625
2626                 //No, do remove for AI ships -- MK, 3/12/98  // don't need to try and delete weapons for ai ships
2627                 //if ( !(Objects[parent_objnum].flags & OF_PLAYER_SHIP) )
2628                 //      return -1;
2629
2630                 num_deleted = collide_remove_weapons();
2631                 nprintf(("WARNING", "Deleted %d weapons because of lack of slots\n", num_deleted));
2632                 if (num_deleted == 0){
2633                         return -1;
2634                 }
2635         }
2636
2637         for (n=0; n<MAX_WEAPONS; n++ ){
2638                 if (Weapons[n].weapon_info_index < 0){
2639                         break;
2640                 }
2641         }
2642
2643         if (n == MAX_WEAPONS) {
2644                 // if we supposedly deleted weapons above, what happened here!!!!
2645                 if (num_deleted){
2646                         Int3();                         // get allender -- something funny is going on!!!
2647                 }
2648
2649                 return -1;
2650         }
2651
2652         Weapons_created++;
2653         objnum = obj_create( OBJ_WEAPON, parent_objnum, n, orient, pos, 2.0f, OF_RENDERS | OF_COLLIDES | OF_PHYSICS );
2654         SDL_assert(objnum >= 0);
2655         SDL_assert(First_secondary_index != -1);
2656         objp = &Objects[objnum];
2657
2658         parent_objp = NULL;
2659         if(parent_objnum >= 0){
2660                 parent_objp = &Objects[parent_objnum];
2661         }
2662
2663         // Create laser n!
2664         wp = &Weapons[n];
2665         wip = &Weapon_info[weapon_id];
2666
2667         // check if laser or dumbfire missile
2668         // set physics flag to allow optimization
2669         if ((wip->subtype == WP_LASER) || ((wip->subtype == WP_MISSILE) && !(wip->wi_flags & WIF_HOMING))) {
2670                 // set physics flag
2671                 objp->phys_info.flags |= PF_CONST_VEL;
2672         }
2673
2674         wp->weapon_info_index = weapon_id;
2675         wp->lifeleft = wip->lifetime;
2676
2677         wp->objnum = objnum;
2678         wp->homing_object = &obj_used_list;             //      Assume not homing on anything.
2679         wp->homing_subsys = NULL;
2680         wp->creation_time = Missiontime;
2681         wp->group_id = group_id;
2682
2683         // we don't necessarily need a parent
2684         if(parent_objp != NULL){
2685                 SDL_assert(parent_objp->type == OBJ_SHIP);      //      Get Mike, a non-ship has fired a weapon!
2686                 SDL_assert((parent_objp->instance >= 0) && (parent_objp->instance < MAX_SHIPS));
2687                 wp->team = Ships[parent_objp->instance].team;
2688                 wp->species = Ship_info[Ships[parent_objp->instance].ship_info_index].species;
2689         } else {
2690                 wp->team = 0;
2691                 wp->species = 0;
2692         }
2693         wp->turret_subsys = NULL;
2694         vm_vec_zero(&wp->homing_pos);
2695         wp->weapon_flags = 0;
2696         wp->target_sig = -1;
2697         wp->cmeasure_ignore_objnum = -1;
2698         wp->cmeasure_chase_objnum = -1;
2699
2700         // Init the thruster info
2701         wp->thruster_bitmap = -1;
2702         wp->thruster_frame = 0.0f;
2703         wp->thruster_glow_bitmap = -1;
2704         wp->thruster_glow_noise = 1.0f;
2705         wp->thruster_glow_frame = 0.0f;
2706
2707         if ( wip->wi_flags & WIF_SWARM ) {
2708                 wp->swarm_index = (short)swarm_create();
2709         } else {
2710                 wp->swarm_index = -1;
2711         }               
2712
2713         // if this is a particle spewing weapon, setup some stuff
2714         if(wip->wi_flags & WIF_PARTICLE_SPEW){
2715                 wp->particle_spew_time = -1;            
2716         }
2717
2718         // assign the network signature.  The starting sig is sent to all clients, so this call should
2719         // result in the same net signature numbers getting assigned to every player in the game
2720         if ( Game_mode & GM_MULTIPLAYER ) {
2721                 if(wip->subtype == WP_MISSILE){
2722                         Objects[objnum].net_signature = multi_assign_network_signature( MULTI_SIG_NON_PERMANENT );
2723
2724                         // for weapons that respawn, add the number of respawnable weapons to the net signature pool
2725                         // to reserve N signatures for the spawned weapons
2726                         if ( wip->wi_flags & WIF_SPAWN ){
2727                                 multi_set_network_signature( (ushort)(Objects[objnum].net_signature + wip->spawn_count), MULTI_SIG_NON_PERMANENT );
2728                         }
2729                 } else {
2730                         Objects[objnum].net_signature = multi_assign_network_signature( MULTI_SIG_NON_PERMANENT );
2731                 }
2732                 // for multiplayer clients, when creating lasers, add some more life to the lasers.  This helps
2733                 // to overcome some problems associated with lasers dying on client machine before they get message
2734                 // from server saying it hit something.
2735                 // removed 1/13/98 -- MWA if ( MULTIPLAYER_CLIENT && (wip->subtype == WP_LASER) )
2736                 //      removed 1/13/98 -- MWA  wp->lifeleft += 1.5f;
2737         }
2738
2739         //      Make remote detonate missiles look like they're getting detonated by firer simply by giving them variable lifetimes.
2740         if (!(Objects[parent_objnum].flags & OF_PLAYER_SHIP) && (wip->wi_flags & WIF_REMOTE)) {
2741                 float rand_val;
2742
2743                 if ( Game_mode & GM_NORMAL ){
2744                         rand_val = frand();
2745                 } else {
2746                         rand_val = static_randf(Objects[objnum].net_signature);
2747                 }
2748
2749                 wp->lifeleft = wp->lifeleft/2.0f + rand_val * wp->lifeleft/2.0f;
2750         }
2751
2752         objp->phys_info.mass = wip->mass;
2753         objp->phys_info.side_slip_time_const = 0.0f;
2754         objp->phys_info.rotdamp = 0.0f;
2755         vm_vec_zero(&objp->phys_info.max_vel);
2756         objp->phys_info.max_vel.xyz.z = wip->max_speed;
2757         vm_vec_zero(&objp->phys_info.max_rotvel);
2758         objp->shields[0] = wip->damage;
2759         if (wip->wi_flags & WIF_BOMB){
2760                 objp->hull_strength = 50.0f;
2761         } else {
2762                 objp->hull_strength = 0.0f;
2763         }
2764
2765         if ( wip->subtype == WP_MISSILE ){
2766                 objp->radius = model_get_radius(wip->model_num);
2767         } else if ( wip->subtype == WP_LASER ) {
2768                 objp->radius = wip->laser_head_radius;
2769         }
2770
2771         //      Set desired velocity and initial velocity.
2772         //      For lasers, velocity is always the same.
2773         //      For missiles, it is a small amount plus the firing ship's velocity.
2774         //      For missiles, the velocity trends towards some goal.
2775         //      Note: If you change how speed works here, such as adding in speed of parent ship, you'll need to change the AI code
2776         //      that predicts collision points.  See Mike Kulas or Dave Andsager.  (Or see ai_get_weapon_speed().)
2777         if (!(wip->wi_flags & WIF_HOMING)) {
2778                 vm_vec_copy_scale(&objp->phys_info.desired_vel, &objp->orient.v.fvec, objp->phys_info.max_vel.xyz.z );
2779                 objp->phys_info.vel = objp->phys_info.desired_vel;
2780                 objp->phys_info.speed = vm_vec_mag(&objp->phys_info.desired_vel);
2781         } else {                
2782                 //      For weapons that home, set velocity to sum of forward component of parent's velocity and 1/4 weapon's max speed.
2783                 //      Note that it is important to extract the forward component of the parent's velocity to factor out sliding, else
2784                 //      the missile will not be moving forward.
2785                 if(parent_objp != NULL){
2786                         vm_vec_copy_scale(&objp->phys_info.desired_vel, &objp->orient.v.fvec, vm_vec_dot(&parent_objp->phys_info.vel, &parent_objp->orient.v.fvec) + objp->phys_info.max_vel.xyz.z/4 );
2787                 } else {
2788                         vm_vec_copy_scale(&objp->phys_info.desired_vel, &objp->orient.v.fvec, objp->phys_info.max_vel.xyz.z/4 );
2789                 }
2790                 objp->phys_info.vel = objp->phys_info.desired_vel;
2791                 objp->phys_info.speed = vm_vec_mag(&objp->phys_info.vel);
2792         }
2793
2794         // create the corkscrew
2795         if ( wip->wi_flags & WIF_CORKSCREW ) {
2796                 wp->cscrew_index = (short)cscrew_create(objp);
2797         } else {
2798                 wp->cscrew_index = -1;
2799         }
2800
2801         // if this is a flak weapon shell, make it so
2802         // NOTE : this function will change some fundamental things about the weapon object
2803         if ( wip->wi_flags & WIF_FLAK ){
2804                 flak_create(wp);
2805         } else {
2806                 wp->flak_index = -1;
2807         }       
2808
2809         wp->missile_list_index = -1;
2810         // If this is a missile, then add it to the Missile_obj_list
2811         if ( wip->subtype == WP_MISSILE ) {
2812                 wp->missile_list_index = missile_obj_list_add(objnum);
2813         }
2814
2815         if (wip->wi_flags & WIF_TRAIL /*&& !(wip->wi_flags & WIF_CORKSCREW) */) {
2816                 wp->trail_num = trail_create(wip->tr_info);             
2817
2818                 if ( wp->trail_num > -1 )       {
2819                         // Add two segments.  One to stay at launch pos, one to move.
2820                         trail_add_segment( wp->trail_num, &objp->pos );
2821                         trail_add_segment( wp->trail_num, &objp->pos );
2822                 }
2823         }
2824
2825         // Ensure weapon flyby sound doesn't get played for player lasers
2826         if ( parent_objp == Player_obj ) {
2827                 wp->weapon_flags |= WF_PLAYED_FLYBY_SOUND;
2828         }
2829
2830         wp->pick_big_attack_point_timestamp = timestamp(1);
2831
2832         //      Set detail levels for POF-type weapons.
2833         if (Weapon_info[wp->weapon_info_index].model_num != -1) {
2834                 polymodel * pm;
2835                 int     i;
2836                 pm = model_get(Weapon_info[wp->weapon_info_index].model_num);
2837
2838                 for (i=0; i<pm->n_detail_levels; i++){
2839                         pm->detail_depth[i] = (objp->radius*20.0f + 20.0f) * i;
2840                 }
2841         }
2842
2843                 // if the weapon was fired locked
2844         if(is_locked){
2845                 wp->weapon_flags |= WF_LOCKED_WHEN_FIRED;
2846         }
2847
2848         Num_weapons++;
2849         return objnum;
2850 }
2851
2852 //      Spawn child weapons from object *objp.
2853 void spawn_child_weapons(object *objp)
2854 {
2855         int     i;
2856         int     child_id;
2857         int     parent_num;
2858         ushort starting_sig;
2859         weapon  *wp;
2860         weapon_info     *wip;
2861
2862         SDL_assert(objp->type == OBJ_WEAPON);
2863         SDL_assert((objp->instance >= 0) && (objp->instance < MAX_WEAPONS));
2864
2865         wp = &Weapons[objp->instance];
2866         SDL_assert((wp->weapon_info_index >= 0) && (wp->weapon_info_index < MAX_WEAPON_TYPES));
2867         wip = &Weapon_info[wp->weapon_info_index];
2868
2869         child_id = wip->spawn_type;
2870
2871         parent_num = objp->parent;
2872
2873         if ((Objects[parent_num].type != objp->parent_type) || (Objects[parent_num].signature != objp->parent_sig)) {
2874                 mprintf(("Warning: Parent of spawn weapon does not exist.  Not spawning.\n"));
2875                 return;
2876         }
2877
2878         starting_sig = 0;
2879         if ( Game_mode & GM_MULTIPLAYER ) {             
2880                 // get the next network signature and save it.  Set the next usable network signature to be
2881                 // the passed in objects signature + 1.  We "reserved" N of these slots when we created objp
2882                 // for it's spawned children.
2883                 starting_sig = multi_get_next_network_signature( MULTI_SIG_NON_PERMANENT );
2884                 multi_set_network_signature( objp->net_signature, MULTI_SIG_NON_PERMANENT );
2885         }
2886
2887         for (i=0; i<wip->spawn_count; i++) {
2888                 int             weapon_objnum;
2889                 vector  tvec, pos;
2890                 matrix  orient;
2891
2892                 // for multiplayer, use the static randvec functions based on the network signatures to provide
2893                 // the randomness so that it is the same on all machines.
2894                 if ( Game_mode & GM_MULTIPLAYER ){
2895                         static_randvec(objp->net_signature + i, &tvec);
2896                 } else {
2897                         vm_vec_rand_vec_quick(&tvec);
2898                 }
2899                 vm_vec_scale_add(&pos, &objp->pos, &tvec, objp->radius);
2900
2901                 vm_vector_2_matrix(&orient, &tvec, NULL, NULL);
2902                 weapon_objnum = weapon_create(&pos, &orient, child_id, parent_num, 1, -1, wp->weapon_flags & WF_LOCKED_WHEN_FIRED);
2903
2904                 //      Assign a little randomness to lifeleft so they don't all disappear at the same time.
2905                 if (weapon_objnum != -1) {
2906                         float rand_val;
2907
2908                         if ( Game_mode & GM_NORMAL ){
2909                                 rand_val = frand();
2910                         } else {
2911                                 rand_val = static_randf(objp->net_signature + i);
2912                         }
2913
2914                         Weapons[Objects[weapon_objnum].instance].lifeleft *= rand_val*0.4f + 0.8f;
2915                 }
2916
2917         }
2918
2919         // in multiplayer, reset the next network signature to the one that was saved.
2920         if ( Game_mode & GM_MULTIPLAYER ){
2921                 multi_set_network_signature( starting_sig, MULTI_SIG_NON_PERMANENT );
2922         }
2923 }
2924
2925 // -----------------------------------------------------------------------
2926 // weapon_hit_do_sound()
2927 //
2928 // Play a sound effect when a weapon hits a ship
2929 //
2930 // To elimate the "stereo" effect of two lasers hitting at nearly
2931 // the same time, and to reduce the number of sound channels used,
2932 // only play one impact sound if IMPACT_SOUND_DELTA has elapsed
2933 //
2934 // Note: Uses Weapon_impact_timer global for timer variable
2935 //
2936 void weapon_hit_do_sound(object *hit_obj, weapon_info *wip, vector *hitpos)
2937 {
2938         int     is_hull_hit;
2939         float shield_str;
2940
2941         // If non-missiles (namely lasers) expire without hitting a ship, don't play impact sound
2942         if      ( wip->subtype != WP_MISSILE ) {                
2943                 if ( !hit_obj ) {
2944                         // flak weapons make sounds             
2945                         if(wip->wi_flags & WIF_FLAK){
2946                                 snd_play_3d( &Snds[wip->impact_snd], hitpos, &Eye_position );                           
2947                         }
2948                         return;
2949                 }
2950
2951                 switch(hit_obj->type) {
2952                 case OBJ_SHIP:
2953                         // do nothing
2954                         break;
2955
2956                 case OBJ_ASTEROID:
2957                         if ( timestamp_elapsed(Weapon_impact_timer) ) {
2958                                 snd_play_3d( &Snds[wip->impact_snd], hitpos, &Eye_position );
2959                                 Weapon_impact_timer = timestamp(IMPACT_SOUND_DELTA);
2960                         }
2961                         return;
2962                         break;
2963
2964                 default:
2965                         return;
2966                 }
2967         }
2968
2969         if ( hit_obj == NULL ) {
2970                 snd_play_3d( &Snds[wip->impact_snd], hitpos, &Eye_position );
2971                 return;
2972         }
2973
2974         if ( timestamp_elapsed(Weapon_impact_timer) ) {
2975
2976                 is_hull_hit = 1;
2977                 if ( hit_obj->type == OBJ_SHIP ) {
2978                         shield_str = ship_quadrant_shield_strength(hit_obj, hitpos);
2979                 } else {
2980                         shield_str = 0.0f;
2981                 }
2982
2983                 // play a shield hit if shields are above 10% max in this quadrant
2984                 if ( shield_str > 0.1f ) {
2985                         is_hull_hit = 0;
2986                 }
2987
2988                 if ( !is_hull_hit ) {
2989                         // Play a shield impact sound effect
2990                         if ( hit_obj == Player_obj ) {
2991                                 snd_play_3d( &Snds[SND_SHIELD_HIT_YOU], hitpos, &Eye_position );
2992                                 // AL 12-15-97: Add missile impact sound even when shield is hit
2993                                 if ( wip->subtype == WP_MISSILE ) {
2994                                         snd_play_3d( &Snds[SND_PLAYER_HIT_MISSILE], hitpos, &Eye_position);
2995                                 }
2996                         } else {
2997                                 snd_play_3d( &Snds[SND_SHIELD_HIT], hitpos, &Eye_position );
2998                         }
2999                 } else {
3000                         // Play a hull impact sound effect
3001                         switch ( wip->subtype ) {
3002                                 case WP_LASER:
3003                                         if ( hit_obj == Player_obj )
3004                                                 snd_play_3d( &Snds[SND_PLAYER_HIT_LASER], hitpos, &Eye_position );
3005                                         else {
3006                                                 if ( wip->impact_snd != -1 ) {
3007                                                         snd_play_3d( &Snds[wip->impact_snd], hitpos, &Eye_position );
3008                                                 }
3009                                         }
3010                                         break;
3011                                 case WP_MISSILE:
3012                                         if ( hit_obj == Player_obj ) 
3013                                                 snd_play_3d( &Snds[SND_PLAYER_HIT_MISSILE], hitpos, &Eye_position);
3014                                         else {
3015                                                 if ( wip->impact_snd != -1 ) {
3016                                                         snd_play_3d( &Snds[wip->impact_snd], hitpos, &Eye_position );
3017                                                 }
3018                                         }
3019                                         break;
3020                                 default:        
3021                                         nprintf(("Warning","WARNING ==> Cannot determine sound to play for weapon impact\n"));
3022                                         break;
3023                         } // end switch
3024                 }
3025
3026                 Weapon_impact_timer = timestamp(IMPACT_SOUND_DELTA);
3027         }
3028 }
3029
3030 // distrupt any subsystems that fall into damage sphere of this Electronics missile
3031 //
3032 // input:       ship_obj                =>              pointer to ship that holds subsystem
3033 //                              blast_pos       =>              world pos of weapon blast
3034 //                              wi_index                =>              weapon info index of weapon causing blast
3035 void weapon_do_electronics_affect(object *ship_objp, vector *blast_pos, int wi_index)
3036 {
3037         weapon_info                     *wip;
3038         ship                                    *shipp;
3039         ship_subsys                     *ss;
3040         model_subsystem *psub;
3041         vector                          subsys_world_pos;
3042         float                                   dist;
3043
3044         shipp = &Ships[ship_objp->instance];
3045         wip = &Weapon_info[wi_index];
3046
3047         for ( ss = GET_FIRST(&shipp->subsys_list); ss != END_OF_LIST(&shipp->subsys_list); ss = GET_NEXT(ss) ) {
3048                 psub = ss->system_info;
3049
3050                 // convert subsys point to world coords
3051                 vm_vec_unrotate(&subsys_world_pos, &psub->pnt, &ship_objp->orient);
3052                 vm_vec_add2(&subsys_world_pos, &ship_objp->pos);
3053
3054                 // see if subsys point is within damage sphere
3055                 dist = vm_vec_dist_quick(blast_pos, &subsys_world_pos); 
3056                 if ( dist < wip->outer_radius ) {
3057                         ship_subsys_set_disrupted(ss, fl2i(6000.0f + frand()*4000.0f));
3058                 }
3059         }
3060 }
3061
3062 //      ----------------------------------------------------------------------
3063 //      weapon_area_calc_damage()
3064 //
3065 // Calculate teh damage for an object based on the location of an area-effect
3066 // explosion.
3067 //
3068 // input:               objp                    =>              object pointer ship receiving blast effect
3069 //                                      pos                     =>              world pos of blast center
3070 //                                      inner_rad       =>              smallest radius at which full damage is done
3071 //                                      outer_rad       =>              radius at which no damage is done
3072 //                                      max_blast       =>              maximum blast possible from explosion
3073 //                                      max_damage      =>              maximum damage possible from explosion
3074 //                                      blast                   =>              OUTPUT PARAMETER: receives blast value from explosion
3075 //                                      damage          =>              OUTPUT PARAMETER: receives damage value from explosion
3076 //                                      limit                   =>              a limit on the area, needed for shockwave damage
3077 //
3078 //      returns:                no damage occurred      =>              -1
3079 //                                      damage occured                  =>              0
3080 //
3081 int weapon_area_calc_damage(object *objp, vector *pos, float inner_rad, float outer_rad, float max_blast, float max_damage, float *blast, float *damage, float limit)
3082 {
3083         float                   dist, max_dist, min_dist;
3084
3085         // only blast ships and asteroids
3086         if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID)) {
3087                 return -1;
3088         }
3089
3090         max_dist = objp->radius + outer_rad;
3091         dist = vm_vec_dist_quick(&objp->pos, pos);      
3092         if ( (dist > max_dist) || (dist > (limit+objp->radius)) ) {
3093                 return -1;      // spheres don't intersect at all
3094         }
3095
3096         if ( dist < (inner_rad+objp->radius) ) {
3097                 // damage is maximum within inner radius
3098                 *damage = max_damage;
3099                 *blast = max_blast;
3100         } else {
3101                 float dist_to_outer_rad_squared, total_dist_squared;
3102                 min_dist = dist - objp->radius;
3103                 SDL_assert(min_dist < outer_rad);
3104                 dist_to_outer_rad_squared = (outer_rad-min_dist)*(outer_rad-min_dist);
3105                 total_dist_squared = (inner_rad-outer_rad)*(inner_rad-outer_rad);
3106                 // AL 2-24-98: drop off damage relative to square of distance
3107                 SDL_assert(dist_to_outer_rad_squared <= total_dist_squared);
3108                 *damage = max_damage * dist_to_outer_rad_squared/total_dist_squared;
3109
3110
3111 //              *damage = (min_dist - outer_rad) * max_damage/(inner_rad - outer_rad);
3112                 *blast =  (min_dist - outer_rad) * max_blast /(inner_rad - outer_rad);
3113         }
3114
3115         // nprintf(("AI", "Frame %i: Damage = %7.3f, %7.3f meters away.\n", Framecount, *damage, dist));
3116
3117         return 0;
3118 }
3119
3120 //      ----------------------------------------------------------------------
3121 //      weapon_area_apply_blast()
3122 //
3123 // Apply the blast effects of an explosion to a ship
3124 //
3125 // input:       force_apply_pos =>              world pos of where force is applied to object
3126 //                              ship_obj                                =>              object pointer of ship receiving the blast
3127 //                              blast_pos                       =>              world pos of blast center
3128 //                              blast                                   =>              force of blast
3129 //                              make_shockwave          =>              boolean, whether to create a shockwave or not
3130 //
3131 void weapon_area_apply_blast(vector *force_apply_pos, object *ship_obj, vector *blast_pos, float blast, int make_shockwave)
3132 {
3133         #define SHAKE_CONST 3000
3134         vector          force, vec_blast_to_ship, vec_ship_to_impact;
3135         polymodel               *pm;
3136
3137         // apply blast force based on distance from center of explosion
3138         vm_vec_sub(&vec_blast_to_ship, &ship_obj->pos, blast_pos);
3139         vm_vec_normalize_safe(&vec_blast_to_ship);
3140         vm_vec_copy_scale(&force, &vec_blast_to_ship, blast );
3141
3142
3143         vm_vec_sub(&vec_ship_to_impact, blast_pos, &ship_obj->pos);
3144
3145         pm = model_get(Ships[ship_obj->instance].modelnum);
3146         SDL_assert ( pm != NULL );
3147
3148         if (make_shockwave) {
3149                 physics_apply_shock (&force, blast, &ship_obj->phys_info, &ship_obj->orient, &pm->mins, &pm->maxs, pm->rad);
3150                 if (ship_obj == Player_obj) {
3151                         joy_ff_play_vector_effect(&vec_blast_to_ship, blast * 2.0f);
3152                 }
3153         } else {
3154                 ship_apply_whack( &force, &vec_ship_to_impact, ship_obj);
3155         }
3156 }
3157
3158 //      ----------------------------------------------------------------------
3159 //      weapon_do_area_effect()
3160 //
3161 // Do the area effect for a weapon
3162 //
3163 // input:       wobjp                   =>              object pointer to weapon causing explosion
3164 //                              pos                     =>              world pos of explosion center
3165 //                              other_obj       =>              object pointer to ship that weapon impacted on (can be NULL)
3166 //
3167 void weapon_do_area_effect(object *wobjp, vector *pos, object *other_obj)
3168 {
3169         weapon_info     *wip;
3170         object          *objp;
3171         float                   damage, blast;
3172
3173         wip = &Weapon_info[Weapons[wobjp->instance].weapon_info_index]; 
3174
3175         SDL_assert(wip->inner_radius != 0);
3176
3177         // only blast ships and asteroids
3178         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
3179                 if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID) ) {
3180                         continue;
3181                 }
3182         
3183                 if ( objp->type == OBJ_SHIP ) {
3184                         // don't blast navbuoys
3185                         if ( ship_get_SIF(objp->instance) & SIF_NAVBUOY ) {
3186                                 continue;
3187                         }
3188                 }
3189
3190                 if ( weapon_area_calc_damage(objp, pos, wip->inner_radius, wip->outer_radius, wip->blast_force, wip->damage, &blast, &damage, wip->outer_radius) == -1 ){
3191                         continue;
3192                 }
3193
3194                 // scale damage
3195                 damage *= weapon_get_damage_scale(wip, wobjp, other_obj);               
3196
3197                 switch ( objp->type ) {
3198                 case OBJ_SHIP:
3199                         ship_apply_global_damage(objp, wobjp, pos, damage);
3200                         weapon_area_apply_blast(NULL, objp, pos, blast, 0);                     
3201                         break;
3202                 case OBJ_ASTEROID:
3203                         asteroid_hit(objp, NULL, NULL, damage);
3204                         break;
3205                 default:
3206                         Int3();
3207                         break;
3208                 }       
3209
3210         }       // end for
3211
3212         // if this weapon has the "Electronics" flag set, then disrupt subsystems in sphere
3213         if ( (other_obj != NULL) && (wip->wi_flags & WIF_ELECTRONICS) ) {
3214                 if ( other_obj->type == OBJ_SHIP ) {
3215                         weapon_do_electronics_affect(other_obj, pos, Weapons[wobjp->instance].weapon_info_index);
3216                 }
3217         }
3218 }
3219
3220
3221 //      ----------------------------------------------------------------------
3222 //      weapon_hit()
3223 //
3224 // This function is called when a weapon hits something (or, in the case of
3225 // missiles explodes for any particular reason)
3226 //
3227 void weapon_hit( object * weapon_obj, object * other_obj, vector * hitpos )
3228 {
3229         SDL_assert(weapon_obj != NULL);
3230         if(weapon_obj == NULL){
3231                 return;
3232         }
3233         SDL_assert((weapon_obj->type == OBJ_WEAPON) && (weapon_obj->instance >= 0) && (weapon_obj->instance < MAX_WEAPONS));
3234         if((weapon_obj->type != OBJ_WEAPON) || (weapon_obj->instance < 0) || (weapon_obj->instance >= MAX_WEAPONS)){
3235                 return;
3236         }
3237
3238         int                     num = weapon_obj->instance;
3239         int                     weapon_type = Weapons[num].weapon_info_index;
3240         weapon_info     *wip;
3241         // int np_index;
3242
3243         SDL_assert((weapon_type >= 0) && (weapon_type < MAX_WEAPONS));
3244         if((weapon_type < 0) || (weapon_type >= MAX_WEAPONS)){
3245                 return;
3246         }
3247         wip = &Weapon_info[weapon_type];
3248
3249         // if this is the player ship, and is a laser hit, skip it. wait for player "pain" to take care of it
3250         // if( ((wip->subtype != WP_LASER) || !MULTIPLAYER_CLIENT) && (Player_obj != NULL) && (other_obj == Player_obj) ){
3251         if((other_obj != Player_obj) || (wip->subtype != WP_LASER) || !MULTIPLAYER_CLIENT){
3252                 weapon_hit_do_sound(other_obj, wip, hitpos);
3253         }
3254
3255         if ( wip->impact_weapon_expl_index > -1 )       {
3256                 int expl_ani_handle = weapon_get_expl_handle(wip->impact_weapon_expl_index, hitpos, wip->impact_explosion_radius);
3257                 particle_create( hitpos, &vmd_zero_vector, 0.0f, wip->impact_explosion_radius, PARTICLE_BITMAP_PERSISTENT, expl_ani_handle );
3258         }
3259
3260         weapon_obj->flags |= OF_SHOULD_BE_DEAD;
3261
3262         int sw_flag = SW_WEAPON;
3263
3264         // check if this is an area effect weapon
3265         if ( wip->wi_flags & WIF_AREA_EFFECT ) {
3266         // if ( wip->subtype & WP_MISSILE && wip->wi_flags & WIF_AREA_EFFECT ) {
3267                 if ( wip->wi_flags & WIF_SHOCKWAVE ) {
3268                         float actual_damage = wip->damage;
3269                         // Shockwaves caused by weapons hitting weapons are 1/4 as powerful
3270                         if ( ((other_obj) && (other_obj->type == OBJ_WEAPON)) || (Weapons[num].weapon_flags & WF_DESTROYED_BY_WEAPON)) {
3271                                 actual_damage /= 4.0f;
3272                                 sw_flag |= SW_WEAPON_KILL;
3273                         }
3274                         shockwave_create_info sci;
3275                         sci.blast = wip->blast_force;
3276                         sci.damage = actual_damage;
3277                         sci.inner_rad = wip->inner_radius;
3278                         sci.outer_rad = wip->outer_radius;
3279                         sci.speed = wip->shockwave_speed;
3280                         sci.rot_angle = 0.0f;
3281
3282                         shockwave_create(OBJ_INDEX(weapon_obj), hitpos, &sci, sw_flag);
3283 //                      snd_play_3d( &Snds[SND_SHOCKWAVE_IMPACT], hitpos, &Eye_position );
3284                 }
3285                 else {
3286                         weapon_do_area_effect(weapon_obj, hitpos, other_obj);
3287                 }
3288         }
3289
3290         // check if this is an EMP weapon
3291         if(wip->wi_flags & WIF_EMP){
3292                 emp_apply(&weapon_obj->pos, wip->inner_radius, wip->outer_radius, wip->emp_intensity, wip->emp_time);
3293         }       
3294
3295         // spawn weapons - note the change from FS 1 multiplayer.
3296         if (wip->wi_flags & WIF_SPAWN){
3297                 spawn_child_weapons(weapon_obj);
3298         }       
3299 }
3300
3301 void weapon_detonate(object *objp)
3302 {
3303         SDL_assert(objp != NULL);
3304         if(objp == NULL){
3305                 return;
3306         }
3307         SDL_assert((objp->type == OBJ_WEAPON) && (objp->instance >= 0));
3308         if((objp->type != OBJ_WEAPON) || (objp->instance < 0)){
3309                 return;
3310         }       
3311
3312         // send a detonate packet in multiplayer
3313         if(MULTIPLAYER_MASTER){
3314                 send_weapon_detonate_packet(objp);
3315         }
3316
3317         // call weapon hit
3318         weapon_hit(objp, NULL, &objp->pos);
3319 }
3320
3321 //      Return the Weapon_info[] index of the weapon with name *name.
3322 int weapon_name_lookup(const char *name)
3323 {
3324         int     i;
3325
3326         for ( i=0; i < Num_weapon_types; i++) {
3327                 if (!SDL_strcasecmp(name, Weapon_info[i].name)) {
3328                         return i;
3329                 }
3330         }
3331
3332         return -1;
3333 }
3334
3335 // Group_id:  If you should quad lasers, they should all have the same group id.  
3336 // This will be used to optimize lighting, since each group only needs to cast one light.
3337 // Call this to get a new group id, then pass it to each weapon_create call for all the
3338 // weapons in the group.   Number will be between 0 and WEAPON_MAX_GROUP_IDS and will
3339 // get reused.
3340 int weapon_create_group_id()
3341 {
3342         static int current_id = 0;
3343
3344         int n = current_id;
3345         
3346         current_id++;
3347         if ( current_id >= WEAPON_MAX_GROUP_IDS )       {
3348                 current_id = 0;
3349         }
3350         return n;
3351 }
3352
3353
3354 void weapons_page_in()
3355 {
3356         int i, j, idx;
3357
3358         // Page in bitmaps for all weapons
3359         for (i=0; i<Num_weapon_types; i++ )     {
3360                 weapon_info *wip = &Weapon_info[i];
3361
3362                 wip->wi_flags &= (~WIF_THRUSTER);               // Assume no thrusters
3363                 
3364                 switch( wip->render_type )      {
3365                         case WRT_POF:
3366                                 {
3367                                         wip->model_num = model_load( wip->pofbitmap_name, 0, NULL );
3368
3369                                         polymodel *pm = model_get( wip->model_num );
3370
3371                                         // If it has a model, and the model pof has thrusters, then set
3372                                         // the flags
3373                                         if ( pm->n_thrusters > 0 )      {
3374                                                 //mprintf(( "Weapon %s has thrusters!\n", wip->name ));
3375                                                 wip->wi_flags |= WIF_THRUSTER;
3376                                         }
3377                 
3378                                         for (j=0; j<pm->n_textures; j++ )       {
3379                                                 int bitmap_num = pm->original_textures[j];
3380
3381                                                 if ( bitmap_num > -1 )  {
3382                                                         bm_page_in_texture( bitmap_num );
3383                                                 }
3384                                         }
3385                                 }
3386                                 break;
3387
3388                         case WRT_LASER:
3389                                 {
3390                                         bm_page_in_texture( wip->laser_bitmap );
3391
3392                                         if(wip->laser_glow_bitmap >= 0){
3393                                                 bm_page_in_texture(wip->laser_glow_bitmap);
3394                                         }
3395                                 }
3396                                 break;
3397
3398                         default:
3399                                 Int3(); // Invalid weapon rendering type.
3400                 }
3401
3402                 // If this has an impact vclip page it in.
3403 //              if ( wip->impact_explosion_ani > -1 )   {
3404 //                      int nframes, fps;
3405 //                      bm_get_info( wip->impact_explosion_ani, NULL, NULL, NULL, &nframes, &fps );
3406 //                      bm_page_in_xparent_texture( wip->impact_explosion_ani, nframes );
3407 //              }
3408
3409                 // trail bitmaps
3410                 if ( (wip->wi_flags & WIF_TRAIL) && (wip->tr_info.bitmap > -1) )        {
3411                         bm_page_in_texture( wip->tr_info.bitmap );
3412                 }
3413
3414                 // if this is a beam weapon, page in its stuff
3415                 if(wip->wi_flags & WIF_BEAM){
3416                         // all beam sections
3417                         for(idx=0; idx<wip->b_info.beam_num_sections; idx++){
3418                                 if((idx < MAX_BEAM_SECTIONS) && (wip->b_info.sections[idx].texture >= 0)){
3419                                         bm_page_in_texture(wip->b_info.sections[idx].texture);
3420                                 }
3421                         }
3422
3423                         // muzzle glow
3424                         if(wip->b_info.beam_glow_bitmap >= 0){
3425                                 bm_page_in_texture(wip->b_info.beam_glow_bitmap);
3426                         }
3427
3428                         // particle ani
3429                         if(wip->b_info.beam_particle_ani >= 0){
3430                                 int nframes, fps;
3431                                 bm_get_info( wip->b_info.beam_particle_ani, NULL, NULL, NULL, &nframes, &fps );
3432                                 bm_page_in_texture( wip->b_info.beam_particle_ani, nframes );
3433                         }
3434                 }
3435         }
3436
3437         // explosion ani's
3438         for (i=0; i<Num_weapon_expl; i++) {
3439                 int bitmap_handle, nframes, fps;
3440
3441                 for (j=0; j<Weapon_expl_info[i].lod_count; j++) {
3442                         //load ani
3443                         bitmap_handle = bm_load_animation(Weapon_expl_info[i].lod[j].filename, &nframes, &fps, 1);
3444                         Weapon_expl_info[i].lod[j].bitmap_id = bitmap_handle;
3445                         Weapon_expl_info[i].lod[j].fps = fps;
3446                         Weapon_expl_info[i].lod[j].num_frames = nframes;
3447
3448                         // page it in
3449                         bm_page_in_xparent_texture(bitmap_handle, nframes);
3450                 }
3451         }
3452
3453         // Counter measures
3454         for (i=0; i<Num_cmeasure_types; i++ )   {
3455                 cmeasure_info *cmeasurep;
3456
3457                 cmeasurep = &Cmeasure_info[i];
3458         
3459                 cmeasurep->model_num = model_load( cmeasurep->pof_name, 0, NULL );
3460
3461                 polymodel *pm = model_get( cmeasurep->model_num );
3462
3463                 for (j=0; j<pm->n_textures; j++ )       {
3464                         int bitmap_num = pm->original_textures[j];
3465
3466                         if ( bitmap_num > -1 )  {
3467                                 bm_page_in_texture( bitmap_num );
3468                         }
3469                 }
3470                 SDL_assert( cmeasurep->model_num > -1 );
3471         }
3472
3473 }
3474
3475 // call to get the "color" of the laser at the given moment (since glowing lasers can cycle colors)
3476 void weapon_get_laser_color(color *c, object *objp)
3477 {
3478         weapon *wep;
3479         weapon_info *winfo;
3480         float pct;
3481
3482         // sanity
3483         if(c == NULL){
3484                 return;
3485         }
3486
3487         // sanity
3488         SDL_assert(objp->type == OBJ_WEAPON);
3489         SDL_assert(objp->instance >= 0);
3490         SDL_assert(Weapons[objp->instance].weapon_info_index >= 0);
3491         if((objp->type != OBJ_WEAPON) || (objp->instance < 0) || (Weapons[objp->instance].weapon_info_index < 0)){
3492                 return;
3493         }
3494         wep = &Weapons[objp->instance];
3495         winfo = &Weapon_info[wep->weapon_info_index];
3496
3497         // if we're a one-color laser
3498         if((winfo->laser_color_2.red == 0) && (winfo->laser_color_2.green == 0) && (winfo->laser_color_2.blue == 0)){
3499                 *c = winfo->laser_color_1;
3500         }
3501
3502         // lifetime pct
3503         pct = 1.0f - (wep->lifeleft / winfo->lifetime);
3504         if(pct > 0.5f){
3505                 pct = 0.5f;
3506         } else if (pct < 0.0f)
3507                 pct = 0.0f;
3508
3509         pct *= 2.0f;
3510         
3511         // otherwise interpolate between the colors
3512         gr_init_color( c, (int)((float)winfo->laser_color_1.red + (((float)winfo->laser_color_2.red - (float)winfo->laser_color_1.red) * pct)), 
3513                                                         (int)((float)winfo->laser_color_1.green + (((float)winfo->laser_color_2.green - (float)winfo->laser_color_1.green) * pct)), 
3514                                                         (int)((float)winfo->laser_color_1.blue + (((float)winfo->laser_color_2.blue - (float)winfo->laser_color_1.blue) * pct)) );
3515 }
3516
3517 // default weapon particle spew data
3518 int Weapon_particle_spew_count = 1;
3519 int Weapon_particle_spew_time = 25;
3520 float Weapon_particle_spew_vel = 0.4f;
3521 float Weapon_particle_spew_radius = 2.0f;
3522 float Weapon_particle_spew_lifetime = 0.15f;
3523 float Weapon_particle_spew_scale = 0.8f;
3524
3525 // for weapons flagged as particle spewers, spew particles. wheee
3526 void weapon_maybe_spew_particle(object *obj)
3527 {
3528         weapon *wp;
3529         int idx;
3530         vector direct, direct_temp, particle_pos;
3531         vector null_vec = ZERO_VECTOR;
3532         vector vel;
3533         float ang;
3534
3535         // check some stuff
3536         SDL_assert(obj->type == OBJ_WEAPON);
3537         SDL_assert(obj->instance >= 0);
3538         SDL_assert(Weapons[obj->instance].weapon_info_index >= 0);
3539         SDL_assert(Weapon_info[Weapons[obj->instance].weapon_info_index].wi_flags & WIF_PARTICLE_SPEW);
3540         
3541         wp = &Weapons[obj->instance];   
3542
3543         // if the weapon's particle timestamp has elapse`d
3544         if((wp->particle_spew_time == -1) || timestamp_elapsed(wp->particle_spew_time)){
3545                 // reset the timestamp
3546                 wp->particle_spew_time = timestamp(Weapon_particle_spew_time);
3547
3548                 // spew some particles
3549                 for(idx=0; idx<Weapon_particle_spew_count; idx++){
3550                         // get the backward vector of the weapon
3551                         direct = obj->orient.v.fvec;
3552                         vm_vec_negate(&direct);
3553
3554                         //      randomly perturb x, y and z
3555                         
3556                         // uvec
3557                         ang = fl_radian(frand_range(-90.0f, 90.0f));
3558                         vm_rot_point_around_line(&direct_temp, &direct, ang, &null_vec, &obj->orient.v.fvec);                   
3559                         direct = direct_temp;
3560                         vm_vec_scale(&direct, Weapon_particle_spew_scale);
3561
3562                         // rvec
3563                         ang = fl_radian(frand_range(-90.0f, 90.0f));
3564                         vm_rot_point_around_line(&direct_temp, &direct, ang, &null_vec, &obj->orient.v.rvec);                   
3565                         direct = direct_temp;
3566                         vm_vec_scale(&direct, Weapon_particle_spew_scale);
3567
3568                         // fvec
3569                         ang = fl_radian(frand_range(-90.0f, 90.0f));
3570                         vm_rot_point_around_line(&direct_temp, &direct, ang, &null_vec, &obj->orient.v.uvec);                   
3571                         direct = direct_temp;
3572                         vm_vec_scale(&direct, Weapon_particle_spew_scale);
3573
3574                         // get a velovity vector of some percentage of the weapon's velocity
3575                         vel = obj->phys_info.vel;
3576                         vm_vec_scale(&vel, Weapon_particle_spew_vel);
3577
3578                         // emit the particle
3579                         vm_vec_add(&particle_pos, &obj->pos, &direct);
3580                         particle_create(&particle_pos, &vel, Weapon_particle_spew_lifetime, Weapon_particle_spew_radius, PARTICLE_SMOKE);
3581                 }
3582         }
3583 }
3584
3585 // debug console functionality
3586 void pspew_display_dcf()
3587 {
3588         dc_printf("Particle spew settings\n\n");
3589         dc_printf("Particle spew count (pspew_count) : %d\n", Weapon_particle_spew_count);
3590         dc_printf("Particle spew time (pspew_time) : %d\n", Weapon_particle_spew_time);
3591         dc_printf("Particle spew velocity (pspew_vel) : %f\n", Weapon_particle_spew_vel);
3592         dc_printf("Particle spew size (pspew_size) : %f\n", Weapon_particle_spew_radius);
3593         dc_printf("Particle spew lifetime (pspew_life) : %f\n", Weapon_particle_spew_lifetime);
3594         dc_printf("Particle spew scale (psnew_scale) : %f\n", Weapon_particle_spew_scale);
3595 }
3596
3597 DCF(pspew_count, "Number of particles spewed at a time")
3598 {       
3599         dc_get_arg(ARG_INT);
3600         if(Dc_arg_type & ARG_INT){
3601                 Weapon_particle_spew_count = Dc_arg_int;
3602         }
3603
3604         pspew_display_dcf();
3605 }
3606
3607 DCF(pspew_time, "Time between particle spews")
3608 {       
3609         dc_get_arg(ARG_INT);
3610         if(Dc_arg_type & ARG_INT){
3611                 Weapon_particle_spew_time = Dc_arg_int;
3612         }
3613
3614         pspew_display_dcf();
3615 }
3616
3617 DCF(pspew_vel, "Relative velocity of particles (0.0 - 1.0)")
3618 {       
3619         dc_get_arg(ARG_FLOAT);
3620         if(Dc_arg_type & ARG_FLOAT){
3621                 Weapon_particle_spew_vel = Dc_arg_float;
3622         }
3623
3624         pspew_display_dcf();
3625 }
3626
3627 DCF(pspew_size, "Size of spewed particles")
3628 {       
3629         dc_get_arg(ARG_FLOAT);
3630         if(Dc_arg_type & ARG_FLOAT){
3631                 Weapon_particle_spew_radius = Dc_arg_float;
3632         }
3633
3634         pspew_display_dcf();
3635 }
3636
3637 DCF(pspew_life, "Lifetime of spewed particles")
3638 {       
3639         dc_get_arg(ARG_FLOAT);
3640         if(Dc_arg_type & ARG_FLOAT){
3641                 Weapon_particle_spew_lifetime = Dc_arg_float;
3642         }
3643
3644         pspew_display_dcf();
3645 }
3646
3647 DCF(pspew_scale, "How far away particles are from the weapon path")
3648 {       
3649         dc_get_arg(ARG_FLOAT);
3650         if(Dc_arg_type & ARG_FLOAT){
3651                 Weapon_particle_spew_scale = Dc_arg_float;
3652         }
3653
3654         pspew_display_dcf();
3655 }
3656
3657 // return a scale factor for damage which should be applied for 2 collisions
3658 float weapon_get_damage_scale(weapon_info *wip, object *wep, object *target)
3659 {
3660 #ifdef MAKE_FS1
3661         // don't do special damage scaling for capships in FS1
3662         return 1.0f;
3663
3664 #else
3665
3666         weapon *wp;
3667         int from_player = 0;
3668         float total_scale = 1.0f;
3669         float hull_pct;
3670         int is_big_damage_ship = 0;
3671
3672         // sanity
3673         if((wip == NULL) || (wep == NULL) || (target == NULL)){
3674                 return 1.0f;
3675         }
3676
3677         // don't scale any damage if its not a weapon   
3678         if((wep->type != OBJ_WEAPON) || (wep->instance < 0) || (wep->instance >= MAX_WEAPONS)){
3679                 return 1.0f;
3680         }
3681         wp = &Weapons[wep->instance];
3682
3683         // was the weapon fired by the player
3684         from_player = 0;
3685         if((wep->parent >= 0) && (wep->parent < MAX_OBJECTS) && (Objects[wep->parent].flags & OF_PLAYER_SHIP)){
3686                 from_player = 1;
3687         }
3688                 
3689         // if this is a lockarm weapon, and it was fired unlocked
3690         if((wip->wi_flags & WIF_LOCKARM) && !(wp->weapon_flags & WF_LOCKED_WHEN_FIRED)){                
3691                 total_scale *= 0.1f;
3692         }
3693
3694         // if the hit object was a ship
3695         if(target->type == OBJ_SHIP){
3696                 ship_info *sip;
3697
3698                 // get some info on the ship
3699                 SDL_assert((target->instance >= 0) && (target->instance < MAX_SHIPS));
3700                 if((target->instance < 0) || (target->instance >= MAX_SHIPS)){
3701                         return total_scale;
3702                 }
3703
3704                 sip = &Ship_info[Ships[target->instance].ship_info_index];
3705
3706                 // get hull pct of the ship currently
3707                 hull_pct = target->hull_strength / sip->initial_hull_strength;
3708
3709                 // if it has hit a supercap ship and is not a supercap class weapon
3710                 if((sip->flags & SIF_SUPERCAP) && !(wip->wi_flags & WIF_SUPERCAP)){
3711                         // if the supercap is around 3/4 damage, apply nothing
3712                         if(hull_pct <= 0.75f){
3713                                 return 0.0f;
3714                         } else {
3715                                 total_scale *= SUPERCAP_DAMAGE_SCALE;
3716                         }
3717                 }
3718
3719                 // determine if this is a big damage ship
3720                 is_big_damage_ship = (sip->flags & SIF_BIG_DAMAGE);
3721
3722                 // if this is a large ship, and is being hit by flak
3723                 if(is_big_damage_ship && (wip->wi_flags & WIF_FLAK)){
3724                         total_scale *= FLAK_DAMAGE_SCALE;
3725                 }
3726                 
3727                 // if the player is firing small weapons at a big ship
3728                 if( from_player && is_big_damage_ship && !(wip->wi_flags & (WIF_HURTS_BIG_SHIPS)) ){
3729
3730                         // if its a laser weapon
3731                         if(wip->subtype == WP_LASER){
3732                                 total_scale *= 0.01f;
3733                         } else {
3734                                 total_scale *= 0.05f;
3735                         }
3736                 }
3737
3738                 // if the weapon is a small weapon being fired at a big ship
3739                 if( is_big_damage_ship && !(wip->wi_flags & (WIF_HURTS_BIG_SHIPS)) ){
3740                         if(hull_pct > 0.1f){
3741                                 total_scale *= hull_pct;
3742                         } else {
3743                                 return 0.0f;
3744                         }
3745                 }
3746         }
3747         
3748         return total_scale;
3749 #endif
3750 }
3751
3752 int weapon_get_expl_handle(int weapon_expl_index, vector *pos, float size)
3753 {
3754         weapon_expl_info *wei = &Weapon_expl_info[weapon_expl_index];
3755
3756         if (wei->lod_count == 1) {
3757                 return wei->lod[0].bitmap_id;
3758         }
3759
3760         // now we have to do some work
3761         vertex v;
3762         int x, y, w, h, bm_size;
3763         int must_stop = 0;
3764         int best_lod = 1;
3765         int behind = 0;
3766
3767         // start the frame
3768         extern float Viewer_zoom;
3769         extern int G3_count;
3770
3771         if(!G3_count){
3772                 g3_start_frame(1);
3773                 must_stop = 1;
3774         }
3775         g3_set_view_matrix(&Eye_position, &Eye_matrix, Viewer_zoom);
3776
3777         // get extents of the rotated bitmap
3778         g3_rotate_vertex(&v, pos);
3779
3780         // if vertex is behind, find size if in front, then drop down 1 LOD
3781         if (v.codes & CC_BEHIND) {
3782                 float dist = vm_vec_dist_quick(&Eye_position, pos);
3783                 vector temp;
3784
3785                 behind = 1;
3786                 vm_vec_scale_add(&temp, &Eye_position, &Eye_matrix.v.fvec, dist);
3787                 g3_rotate_vertex(&v, &temp);
3788
3789                 // if still behind, bail and go with default
3790                 if (v.codes & CC_BEHIND) {
3791                         behind = 0;
3792                 }
3793         }
3794
3795         if (!g3_get_bitmap_dims(wei->lod[0].bitmap_id, &v, size, &x, &y, &w, &h, &bm_size)) {
3796                 if (Detail.hardware_textures == 4) {
3797                         // straight LOD
3798                         if(w <= bm_size/8){
3799                                 best_lod = 3;
3800                         } else if(w <= bm_size/2){
3801                                 best_lod = 2;
3802                         } else if(w <= 1.3f*bm_size){
3803                                 best_lod = 1;
3804                         } else {
3805                                 best_lod = 0;
3806                         }
3807                 } else {
3808                         // less aggressive LOD for lower detail settings
3809                         if(w <= bm_size/8){
3810                                 best_lod = 3;
3811                         } else if(w <= bm_size/3){
3812                                 best_lod = 2;
3813                         } else if(w <= (1.15f*bm_size)){
3814                                 best_lod = 1;
3815                         } else {
3816                                 best_lod = 0;
3817                         }               
3818                 }
3819         }
3820
3821         // if it's behind, bump up LOD by 1
3822         if (behind) {
3823                 best_lod++;
3824         }
3825
3826         // end the frame
3827         if(must_stop){
3828                 g3_end_frame();
3829         }
3830
3831         best_lod = SDL_min(best_lod, wei->lod_count - 1);
3832         return wei->lod[best_lod].bitmap_id;
3833 }
3834
3835 // -------------------------------------------------------------------------------------------------
3836 // weapon_close()
3837 //
3838 // called in game_shutdown() to free malloced memory
3839 //
3840 // NOTE: do not call this function.  It is only called from game_shutdown()
3841 void weapon_close()
3842 {
3843         int i;
3844         
3845         // free info from parsed table data
3846         for (i=0; i<MAX_WEAPON_TYPES; i++) {
3847                 if ( Weapon_info[i].desc != NULL ) {
3848                         free(Weapon_info[i].desc);
3849                         Weapon_info[i].desc = NULL;
3850                 }
3851                 if ( Weapon_info[i].tech_desc != NULL ) {
3852                         free(Weapon_info[i].tech_desc);
3853                         Weapon_info[i].tech_desc = NULL;
3854                 }
3855         }
3856 }