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