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