added -udp documentation from d1x
[btb/d2x.git] / main / weapon.c
1 /* $Id: weapon.c,v 1.9 2003-10-11 09:28:38 btb Exp $ */
2 /*
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
13 */
14
15 /*
16  *
17  * Functions for weapons...
18  *
19  * Old Log:
20  * Revision 1.2  1995/10/31  10:17:39  allender
21  * new shareware stuff
22  *
23  * Revision 1.1  1995/05/16  15:32:16  allender
24  * Initial revision
25  *
26  * Revision 2.1  1995/03/21  14:38:43  john
27  * Ifdef'd out the NETWORK code.
28  *
29  * Revision 2.0  1995/02/27  11:27:25  john
30  * New version 2.0, which has no anonymous unions, builds with
31  * Watcom 10.0, and doesn't require parsing BITMAPS.TBL.
32  *
33  * Revision 1.54  1995/02/15  15:21:48  mike
34  * make smart missile select if mega missiles used up.
35  *
36  *
37  * Revision 1.53  1995/02/12  02:12:30  john
38  * Fixed bug with state restore making weapon beeps.
39  *
40  * Revision 1.52  1995/02/09  20:42:15  mike
41  * change weapon autoselect, always autoselect smart, mega.
42  *
43  * Revision 1.51  1995/02/07  20:44:26  mike
44  * autoselect mega, smart when you pick them up.
45  *
46  * Revision 1.50  1995/02/07  13:32:25  rob
47  * Added include of multi.h
48  *
49  * Revision 1.49  1995/02/07  13:21:33  yuan
50  * Fixed 2nd typo
51  *
52  * Revision 1.48  1995/02/07  13:16:39  yuan
53  * Fixed typo.
54  *
55  * Revision 1.47  1995/02/07  12:53:12  rob
56  * Added network sound prop. to weapon switch.
57  *
58  * Revision 1.46  1995/02/06  15:53:17  mike
59  * don't autoselect smart or mega missile when you pick it up.
60  *
61  * Revision 1.45  1995/02/02  21:43:34  mike
62  * make autoselection better.
63  *
64  * Revision 1.44  1995/02/02  16:27:21  mike
65  * make concussion missiles trade up.
66  *
67  * Revision 1.43  1995/02/01  23:34:57  adam
68  * messed with weapon change sounds
69  *
70  * Revision 1.42  1995/02/01  17:12:47  mike
71  * Make smart missile, mega missile not auto-select.
72  *
73  * Revision 1.41  1995/02/01  15:50:54  mike
74  * fix bogus weapon selection sound code.
75  *
76  * Revision 1.40  1995/01/31  16:16:31  mike
77  * Separate smart blobs for robot and player.
78  *
79  * Revision 1.39  1995/01/30  21:12:11  mike
80  * Use new weapon selection sounds, different for primary and secondary.
81  *
82  * Revision 1.38  1995/01/29  13:46:52  mike
83  * Don't auto-select fusion cannon when you run out of energy.
84  *
85  * Revision 1.37  1995/01/20  11:11:13  allender
86  * record weapon changes again.  (John somehow lost my 1.35 changes).
87  *
88  * Revision 1.36  1995/01/19  17:00:46  john
89  * Made save game work between levels.
90  *
91  * Revision 1.34  1995/01/09  17:03:48  mike
92  * fix autoselection of weapons.
93  *
94  * Revision 1.33  1995/01/05  15:46:31  john
95  * Made weapons not rearm when starting a saved game.
96  *
97  * Revision 1.32  1995/01/03  12:34:23  mike
98  * autoselect next lower weapon if run out of smart or mega missile.
99  *
100  * Revision 1.31  1994/12/12  21:39:37  matt
101  * Changed vulcan ammo: 10K max, 5K w/weapon, 1250 per powerup
102  *
103  * Revision 1.30  1994/12/09  19:55:04  matt
104  * Added weapon name in "not available in shareware" message
105  *
106  * Revision 1.29  1994/12/06  13:50:24  adam
107  * added shareware msg. when choosing 4 top weapons
108  *
109  * Revision 1.28  1994/12/02  22:07:13  mike
110  * if you gots 19 concussion missiles and you runs over 4, say you picks up 1, not 4, we do the math, see?
111  *
112  * Revision 1.27  1994/12/02  20:06:24  matt
113  * Made vulcan ammo print at approx 25 times actual
114  *
115  * Revision 1.26  1994/12/02  15:05:03  matt
116  * Fixed bogus weapon constants and arrays
117  *
118  * Revision 1.25  1994/12/02  10:50:34  yuan
119  * Localization
120  *
121  * Revision 1.24  1994/11/29  15:48:28  matt
122  * selecting weapon now makes sound
123  *
124  * Revision 1.23  1994/11/28  11:26:58  matt
125  * Cleaned up hud message printing for picking up weapons
126  *
127  * Revision 1.22  1994/11/27  23:13:39  matt
128  * Made changes for new mprintf calling convention
129  *
130  * Revision 1.21  1994/11/12  16:38:34  mike
131  * clean up default ammo stuff.
132  *
133  * Revision 1.20  1994/11/07  17:41:18  mike
134  * messages for when you try to fire a weapon you don't have or don't have ammo for.
135  *
136  * Revision 1.19  1994/10/21  20:40:05  mike
137  * fix double vulcan ammo.
138  *
139  * Revision 1.18  1994/10/20  09:49:05  mike
140  * kill messages no one liked...*sniff* *sniff*
141  *
142  * Revision 1.17  1994/10/19  11:17:07  mike
143  * Limit amount of player ammo.
144  *
145  * Revision 1.16  1994/10/12  08:04:18  mike
146  * Fix proximity/homing confusion.
147  *
148  * Revision 1.15  1994/10/11  18:27:58  matt
149  * Changed auto selection of secondary weapons
150  *
151  * Revision 1.14  1994/10/08  23:37:54  matt
152  * Don't pick up weapons you already have; also fixed auto_select bug
153  * for seconary weapons
154  *
155  * Revision 1.13  1994/10/08  14:55:47  matt
156  * Fixed bug that selected vulcan cannon when picked up ammo, even though
157  * you didn't have the weapon.
158  *
159  * Revision 1.12  1994/10/08  12:50:32  matt
160  * Fixed bug that let you select weapons you don't have
161  *
162  * Revision 1.11  1994/10/07  23:37:56  matt
163  * Made weapons select when pick up better one
164  *
165  * Revision 1.10  1994/10/07  16:02:08  matt
166  * Fixed problem with weapon auto-select
167  *
168  * Revision 1.9  1994/10/05  17:00:20  matt
169  * Made player_has_weapon() public and moved constants to header file
170  *
171  * Revision 1.8  1994/09/26  11:27:13  mike
172  * Fix auto selection of weapon when you run out of ammo.
173  *
174  * Revision 1.7  1994/09/13  16:40:45  mike
175  * Add rearm delay and missile firing delay.
176  *
177  * Revision 1.6  1994/09/13  14:43:12  matt
178  * Added cockpit weapon displays
179  *
180  * Revision 1.5  1994/09/03  15:23:06  mike
181  * Auto select next weaker weapon when one runs out, clean up code.
182  *
183  * Revision 1.4  1994/09/02  16:38:19  mike
184  * Eliminate a pile of arrays, associate weapon data with Weapon_info.
185  *
186  * Revision 1.3  1994/09/02  11:57:10  mike
187  * Add a bunch of stuff, I forget what.
188  *
189  * Revision 1.2  1994/06/03  16:26:32  john
190  * Initial version.
191  *
192  * Revision 1.1  1994/06/03  14:40:43  john
193  * Initial revision
194  *
195  *
196  */
197
198 #ifdef HAVE_CONFIG_H
199 #include <conf.h>
200 #endif
201
202 #ifdef RCS
203 static char rcsid[] = "$Id: weapon.c,v 1.9 2003-10-11 09:28:38 btb Exp $";
204 #endif
205
206 #include <stdlib.h>
207 #include <stdio.h>
208 #include <string.h>
209
210 #include "game.h"
211 #include "laser.h"
212 #include "weapon.h"
213 #include "mono.h"
214 #include "player.h"
215 #include "gauges.h"
216 #include "error.h"
217 #include "sounds.h"
218 #include "text.h"
219 #include "powerup.h"
220 #include "fireball.h"
221 #include "newdemo.h"
222 #include "multi.h"
223 #include "newmenu.h"
224 #include "ai.h"
225 #include "args.h"
226
227 #if defined (TACTILE)
228 #include "tactile.h"
229 #endif
230
231 int POrderList (int num);
232 int SOrderList (int num);
233 //      Note, only Vulcan cannon requires ammo.
234 // NOTE: Now Vulcan and Gauss require ammo. -5/3/95 Yuan
235 //ubyte Default_primary_ammo_level[MAX_PRIMARY_WEAPONS] = {255, 0, 255, 255, 255};
236 //ubyte Default_secondary_ammo_level[MAX_SECONDARY_WEAPONS] = {3, 0, 0, 0, 0};
237
238 //      Convert primary weapons to indices in Weapon_info array.
239 ubyte Primary_weapon_to_weapon_info[MAX_PRIMARY_WEAPONS] = {LASER_ID, VULCAN_ID, SPREADFIRE_ID, PLASMA_ID, FUSION_ID, SUPER_LASER_ID, GAUSS_ID, HELIX_ID, PHOENIX_ID, OMEGA_ID};
240 ubyte Secondary_weapon_to_weapon_info[MAX_SECONDARY_WEAPONS] = {CONCUSSION_ID, HOMING_ID, PROXIMITY_ID, SMART_ID, MEGA_ID, FLASH_ID, GUIDEDMISS_ID, SUPERPROX_ID, MERCURY_ID, EARTHSHAKER_ID};
241
242 //for each Secondary weapon, which gun it fires out of
243 ubyte Secondary_weapon_to_gun_num[MAX_SECONDARY_WEAPONS] = {4,4,7,7,7,4,4,7,4,7};
244
245 int Primary_ammo_max[MAX_PRIMARY_WEAPONS] = {0, VULCAN_AMMO_MAX, 0, 0, 0, 0, VULCAN_AMMO_MAX, 0, 0, 0};
246 ubyte Secondary_ammo_max[MAX_SECONDARY_WEAPONS] = {20, 10, 10, 5, 5, 20, 20, 15, 10, 10};
247
248 //for each primary weapon, what kind of powerup gives weapon
249 ubyte Primary_weapon_to_powerup[MAX_PRIMARY_WEAPONS] = {POW_LASER,POW_VULCAN_WEAPON,POW_SPREADFIRE_WEAPON,POW_PLASMA_WEAPON,POW_FUSION_WEAPON,POW_LASER,POW_GAUSS_WEAPON,POW_HELIX_WEAPON,POW_PHOENIX_WEAPON,POW_OMEGA_WEAPON};
250
251 //for each Secondary weapon, what kind of powerup gives weapon
252 ubyte Secondary_weapon_to_powerup[MAX_SECONDARY_WEAPONS] = {POW_MISSILE_1,POW_HOMING_AMMO_1,POW_PROXIMITY_WEAPON,POW_SMARTBOMB_WEAPON,POW_MEGA_WEAPON,POW_SMISSILE1_1,POW_GUIDED_MISSILE_1,POW_SMART_MINE,POW_MERCURY_MISSILE_1,POW_EARTHSHAKER_MISSILE};
253
254 weapon_info Weapon_info[MAX_WEAPON_TYPES];
255 int     N_weapon_types=0;
256 sbyte   Primary_weapon, Secondary_weapon;
257
258 // autoselect ordering
259
260 ubyte PrimaryOrder[]={9,8,7,6,5,4,3,2,1,0,255};
261 ubyte SecondaryOrder[]={9,8,4,3,1,5,0,255,7,6,2};
262
263 ubyte DefaultPrimaryOrder[]={9,8,7,6,5,4,3,2,1,0,255};
264 ubyte DefaultSecondaryOrder[]={9,8,4,3,1,5,0,255,7,6,2};
265
266 // Cycling weapon key pressed?
267
268 ubyte Cycling=0;
269
270 //allow player to reorder menus?
271 extern ubyte MenuReordering;
272
273 //char  *Primary_weapon_names[MAX_PRIMARY_WEAPONS] = {
274 //      "Laser Cannon",
275 //      "Vulcan Cannon",
276 //      "Spreadfire Cannon",
277 //      "Plasma Cannon",
278 //      "Fusion Cannon"
279 //};
280
281 //char  *Secondary_weapon_names[MAX_SECONDARY_WEAPONS] = {
282 //      "Concussion Missile",
283 //      "Homing Missile",
284 //      "Proximity Bomb",
285 //      "Smart Missile",
286 //      "Mega Missile"
287 //};
288
289 //char  *Primary_weapon_names_short[MAX_PRIMARY_WEAPONS] = {
290 //      "Laser",
291 //      "Vulcan",
292 //      "Spread",
293 //      "Plasma",
294 //      "Fusion"
295 //};
296
297 //char  *Secondary_weapon_names_short[MAX_SECONDARY_WEAPONS] = {
298 //      "Concsn\nMissile",
299 //      "Homing\nMissile",
300 //      "Proxim.\nBomb",
301 //      "Smart\nMissile",
302 //      "Mega\nMissile"
303 //};
304
305 sbyte   Weapon_is_energy[MAX_WEAPON_TYPES] = {
306         1, 1, 1, 1, 1,
307         1, 1, 1, 0, 1,
308         1, 0, 1, 1, 1,
309         0, 1, 0, 0, 1,
310         1, 0, 0, 1, 1,
311         1, 1, 1, 0, 1,
312         1, 1, 0, 1, 1,
313         1
314 };
315
316 // ; (0) Laser Level 1
317 // ; (1) Laser Level 2
318 // ; (2) Laser Level 3
319 // ; (3) Laser Level 4
320 // ; (4) Unknown Use
321 // ; (5) Josh Blobs
322 // ; (6) Unknown Use
323 // ; (7) Unknown Use
324 // ; (8) ---------- Concussion Missile ----------
325 // ; (9) ---------- Flare ----------
326 // ; (10) ---------- Blue laser that blue guy shoots -----------
327 // ; (11) ---------- Vulcan Cannon ----------
328 // ; (12) ---------- Spreadfire Cannon ----------
329 // ; (13) ---------- Plasma Cannon ----------
330 // ; (14) ---------- Fusion Cannon ----------
331 // ; (15) ---------- Homing Missile ----------
332 // ; (16) ---------- Proximity Bomb ----------
333 // ; (17) ---------- Smart Missile ----------
334 // ; (18) ---------- Mega Missile ----------
335 // ; (19) ---------- Children of the PLAYER'S Smart Missile ----------
336 // ; (20) ---------- Bad Guy Spreadfire Laser ----------
337 // ; (21) ---------- SuperMech Homing Missile ----------
338 // ; (22) ---------- Regular Mech's missile -----------
339 // ; (23) ---------- Silent Spreadfire Laser ----------
340 // ; (24) ---------- Red laser that baby spiders shoot -----------
341 // ; (25) ---------- Green laser that rifleman shoots -----------
342 // ; (26) ---------- Plasma gun that 'plasguy' fires ------------
343 // ; (27) ---------- Blobs fired by Red Spiders -----------
344 // ; (28) ---------- Final Boss's Mega Missile ----------
345 // ; (29) ---------- Children of the ROBOT'S Smart Missile ----------
346 // ; (30) Laser Level 5
347 // ; (31) Laser Level 6
348 // ; (32) ---------- Super Vulcan Cannon ----------
349 // ; (33) ---------- Super Spreadfire Cannon ----------
350 // ; (34) ---------- Super Plasma Cannon ----------
351 // ; (35) ---------- Super Fusion Cannon ----------
352
353 //      ------------------------------------------------------------------------------------
354 //      Return:
355 // Bits set:
356 //              HAS_WEAPON_FLAG
357 //              HAS_ENERGY_FLAG
358 //              HAS_AMMO_FLAG   
359 // See weapon.h for bit values
360 int player_has_weapon(int weapon_num, int secondary_flag)
361 {
362         int     return_value = 0;
363         int     weapon_index;
364
365         //      Hack! If energy goes negative, you can't fire a weapon that doesn't require energy.
366         //      But energy should not go negative (but it does), so find out why it does!
367         if (Players[Player_num].energy < 0)
368                 Players[Player_num].energy = 0;
369
370         if (!secondary_flag) {
371                 weapon_index = Primary_weapon_to_weapon_info[weapon_num];
372
373                 if (Players[Player_num].primary_weapon_flags & (1 << weapon_num))
374                         return_value |= HAS_WEAPON_FLAG;
375
376                 // Special case: Gauss cannon uses vulcan ammo.         
377                 if (weapon_num == GAUSS_INDEX) {
378                         if (Weapon_info[weapon_index].ammo_usage <= Players[Player_num].primary_ammo[VULCAN_INDEX])
379                                 return_value |= HAS_AMMO_FLAG;
380                 } else
381                         if (Weapon_info[weapon_index].ammo_usage <= Players[Player_num].primary_ammo[weapon_num])
382                                 return_value |= HAS_AMMO_FLAG;
383
384                 if (weapon_num == OMEGA_INDEX) {        // Hack: Make sure player has energy to omega
385                         if (Players[Player_num].energy || Omega_charge)
386                                 return_value |= HAS_ENERGY_FLAG;
387                 } else          
388                         if (Weapon_info[weapon_index].energy_usage <= Players[Player_num].energy)
389                                 return_value |= HAS_ENERGY_FLAG;
390
391         } else {
392                 weapon_index = Secondary_weapon_to_weapon_info[weapon_num];
393
394                 if (Players[Player_num].secondary_weapon_flags & (1 << weapon_num))
395                         return_value |= HAS_WEAPON_FLAG;
396
397                 if (Weapon_info[weapon_index].ammo_usage <= Players[Player_num].secondary_ammo[weapon_num])
398                         return_value |= HAS_AMMO_FLAG;
399
400                 if (Weapon_info[weapon_index].energy_usage <= Players[Player_num].energy)
401                         return_value |= HAS_ENERGY_FLAG;
402         }
403
404         return return_value;
405 }
406
407 void InitWeaponOrdering ()
408  {
409   // short routine to setup default weapon priorities for new pilots
410
411   int i;
412
413   for (i=0;i<MAX_PRIMARY_WEAPONS+1;i++)
414         PrimaryOrder[i]=DefaultPrimaryOrder[i];
415   for (i=0;i<MAX_SECONDARY_WEAPONS+1;i++)
416         SecondaryOrder[i]=DefaultSecondaryOrder[i];
417  }      
418
419 void CyclePrimary ()
420 {
421         mprintf ((0,"Cycling primary!\n"));
422         Cycling=1;
423         auto_select_weapon (0);
424         Cycling=0;
425 }
426
427 void CycleSecondary ()
428 {
429         mprintf ((0,"Cycling secondary!\n"));
430         Cycling=1;
431         auto_select_weapon (1);
432         Cycling=0;
433 }
434
435
436 //      ------------------------------------------------------------------------------------
437 //if message flag set, print message saying selected
438 void select_weapon(int weapon_num, int secondary_flag, int print_message, int wait_for_rearm)
439 {
440         char    *weapon_name;
441
442         if (Newdemo_state==ND_STATE_RECORDING )
443                 newdemo_record_player_weapon(secondary_flag, weapon_num);
444
445         if (!secondary_flag) {
446                 if (Primary_weapon != weapon_num) {
447                         if (wait_for_rearm) digi_play_sample_once( SOUND_GOOD_SELECTION_PRIMARY, F1_0 );
448 #ifdef NETWORK
449                         if (Game_mode & GM_MULTI)       {
450                                 if (wait_for_rearm) multi_send_play_sound(SOUND_GOOD_SELECTION_PRIMARY, F1_0);
451                         }
452 #endif
453                         if (wait_for_rearm)
454                                 Next_laser_fire_time = GameTime + REARM_TIME;
455                         else
456                                 Next_laser_fire_time = 0;
457                         Global_laser_firing_count = 0;
458                 } else  {
459                         // Select super version if available.
460                         if (wait_for_rearm)
461                         {
462                          if (!Cycling)
463                                 ; // -- MK, only plays when can't fire weapon anyway, fixes bug -- digi_play_sample_once( SOUND_ALREADY_SELECTED, F1_0 );
464                          else
465                                 digi_play_sample_once( SOUND_BAD_SELECTION, F1_0 );
466                         }
467                 }
468                 Primary_weapon = weapon_num;
469                 weapon_name = PRIMARY_WEAPON_NAMES(weapon_num);
470       #if defined (TACTILE)
471                 tactile_set_button_jolt();
472                 #endif
473
474
475                 //save flag for whether was super version
476                 Primary_last_was_super[weapon_num % SUPER_WEAPON] = (weapon_num >= SUPER_WEAPON);
477
478         } else {
479
480                 if (Secondary_weapon != weapon_num) {
481                         if (wait_for_rearm) digi_play_sample_once( SOUND_GOOD_SELECTION_SECONDARY, F1_0 );
482 #ifdef NETWORK
483                         if (Game_mode & GM_MULTI)       {
484                                 if (wait_for_rearm) multi_send_play_sound(SOUND_GOOD_SELECTION_PRIMARY, F1_0);
485                         }
486 #endif
487                         if (wait_for_rearm)
488                                 Next_missile_fire_time = GameTime + REARM_TIME;
489                         else
490                                 Next_missile_fire_time = 0;
491                         Global_missile_firing_count = 0;
492                 } else  {
493                         if (wait_for_rearm)
494                         {
495                          if (!Cycling)
496                                 digi_play_sample_once( SOUND_ALREADY_SELECTED, F1_0 );
497                          else
498                                 digi_play_sample_once( SOUND_BAD_SELECTION, F1_0 );
499                         }
500                                 
501                 }
502                 Secondary_weapon = weapon_num;
503                 weapon_name = SECONDARY_WEAPON_NAMES(weapon_num);
504
505                 //save flag for whether was super version
506                 Secondary_last_was_super[weapon_num % SUPER_WEAPON] = (weapon_num >= SUPER_WEAPON);
507         }
508
509         if (print_message)
510         {
511                 if (weapon_num == LASER_INDEX && !secondary_flag)
512                         HUD_init_message("%s Level %d %s", weapon_name, Players[Player_num].laser_level+1, TXT_SELECTED);
513                 else
514                         HUD_init_message("%s %s", weapon_name, TXT_SELECTED);
515         }
516
517 }
518
519 //flags whether the last time we use this weapon, it was the 'super' version
520 ubyte Primary_last_was_super[MAX_PRIMARY_WEAPONS];
521 ubyte Secondary_last_was_super[MAX_SECONDARY_WEAPONS];
522
523 //      ------------------------------------------------------------------------------------
524 //      Select a weapon, primary or secondary.
525 void do_weapon_select(int weapon_num, int secondary_flag)
526 {
527         int     weapon_num_save=weapon_num;
528         int     weapon_status,current,has_flag;
529         ubyte   last_was_super;
530
531         if (!secondary_flag) {
532                 current = Primary_weapon;
533                 last_was_super = Primary_last_was_super[weapon_num];
534                 has_flag = HAS_WEAPON_FLAG;
535         }
536         else {
537                 current = Secondary_weapon;
538                 last_was_super = Secondary_last_was_super[weapon_num];
539                 has_flag = HAS_WEAPON_FLAG+HAS_AMMO_FLAG;
540         }
541
542         if (current == weapon_num || current == weapon_num+SUPER_WEAPON) {
543
544                 //already have this selected, so toggle to other of normal/super version
545
546                 weapon_num += weapon_num+SUPER_WEAPON - current;
547                 weapon_status = player_has_weapon(weapon_num, secondary_flag);
548         }
549         else {
550
551                 //go to last-select version of requested missile
552
553                 if (last_was_super)
554                         weapon_num += SUPER_WEAPON;
555
556                 weapon_status = player_has_weapon(weapon_num, secondary_flag);
557
558                 //if don't have last-selected, try other version
559
560                 if ((weapon_status & has_flag) != has_flag) {
561                         weapon_num = 2*weapon_num_save+SUPER_WEAPON - weapon_num;
562                         weapon_status = player_has_weapon(weapon_num, secondary_flag);
563                         if ((weapon_status & has_flag) != has_flag)
564                                 weapon_num = 2*weapon_num_save+SUPER_WEAPON - weapon_num;
565                 }
566         }
567
568         //if we don't have the weapon we're switching to, give error & bail
569         if ((weapon_status & has_flag) != has_flag) {
570                 if (!secondary_flag) {
571                         if (weapon_num==SUPER_LASER_INDEX)
572                                 return;                 //no such thing as super laser, so no error
573                         HUD_init_message("%s %s!", TXT_DONT_HAVE, PRIMARY_WEAPON_NAMES(weapon_num));
574                 }
575                 else
576                         HUD_init_message("%s %s%s",TXT_HAVE_NO, SECONDARY_WEAPON_NAMES(weapon_num), TXT_SX);
577                 digi_play_sample( SOUND_BAD_SELECTION, F1_0 );
578                 return;
579         }
580
581         //now actually select the weapon
582         select_weapon(weapon_num, secondary_flag, 1, 1);
583 }
584
585 //      ----------------------------------------------------------------------------------------
586 //      Automatically select next best weapon if unable to fire current weapon.
587 // Weapon type: 0==primary, 1==secondary
588 void auto_select_weapon(int weapon_type)
589 {
590         int     r;
591         int cutpoint;
592         int looped=0;
593
594         if (weapon_type==0) {
595                 r = player_has_weapon(Primary_weapon, 0);
596                 if (r != HAS_ALL || Cycling) {
597                         int     cur_weapon;
598                         int     try_again = 1;
599         
600                         cur_weapon = POrderList(Primary_weapon);
601                         cutpoint = POrderList (255);
602         
603                         while (try_again) {
604                                 cur_weapon++;
605
606                                 if (cur_weapon>=cutpoint)
607                                 {
608                                         if (looped)
609                                         {
610                                                 if (!Cycling)
611                                                 {
612                                                         HUD_init_message(TXT_NO_PRIMARY);
613                                                         #ifdef TACTILE
614                                                         if (TactileStick)
615                                                          ButtonReflexClear(0);
616                                                         #endif
617                                                         select_weapon(0, 0, 0, 1);
618                                                 }
619                                                 else
620                                                         select_weapon (Primary_weapon,0,0,1);
621
622                                                 try_again = 0;
623                                                 continue;
624                                         }
625                                         cur_weapon=0;
626                                         looped=1;
627                                 }
628
629
630                                 if (cur_weapon==MAX_PRIMARY_WEAPONS)
631                                         cur_weapon = 0;
632         
633                                 //      Hack alert!  Because the fusion uses 0 energy at the end (it's got the weird chargeup)
634                                 //      it looks like it takes 0 to fire, but it doesn't, so never auto-select.
635                                 // if (PrimaryOrder[cur_weapon] == FUSION_INDEX)
636                                 //      continue;
637
638                                 if (PrimaryOrder[cur_weapon] == Primary_weapon) {
639                                         if (!Cycling)
640                                         {
641                                                 HUD_init_message(TXT_NO_PRIMARY);
642                                                 #ifdef TACTILE
643                                                 if (TactileStick)
644                                                  ButtonReflexClear(0);
645                                                 #endif
646
647                                                 //      if (POrderList(0)<POrderList(255))
648                                                 select_weapon(0, 0, 0, 1);
649                                         }
650                                         else
651                                                 select_weapon (Primary_weapon,0,0,1);
652
653                                         try_again = 0;                  // Tried all weapons!
654
655                                 } else if (PrimaryOrder[cur_weapon]!=255 && player_has_weapon(PrimaryOrder[cur_weapon], 0) == HAS_ALL) {
656                                         select_weapon(PrimaryOrder[cur_weapon], 0, 1, 1 );
657                                         try_again = 0;
658                                 }
659                         }
660                 }
661
662         } else {
663
664                 Assert(weapon_type==1);
665                 r = player_has_weapon(Secondary_weapon, 1);
666                 if (r != HAS_ALL || Cycling) {
667                         int     cur_weapon;
668                         int     try_again = 1;
669         
670                         cur_weapon = SOrderList(Secondary_weapon);
671                         cutpoint = SOrderList (255);
672
673         
674                         while (try_again) {
675                                 cur_weapon++;
676
677                                 if (cur_weapon>=cutpoint)
678                                 {
679                                         if (looped)
680                                         {
681                                                 if (!Cycling)
682                                                         HUD_init_message("No secondary weapons selected!");
683                                                 else
684                                                         select_weapon (Secondary_weapon,1,0,1);
685                                                 try_again = 0;
686                                                 continue;
687                                         }
688                                         cur_weapon=0;
689                                         looped=1;
690                                 }
691
692                                 if (cur_weapon==MAX_SECONDARY_WEAPONS)
693                                         cur_weapon = 0;
694
695                                 if (SecondaryOrder[cur_weapon] == Secondary_weapon) {
696                                         if (!Cycling)
697                                                 HUD_init_message("No secondary weapons available!");
698                                         else
699                                                 select_weapon (Secondary_weapon,1,0,1);
700
701                                         try_again = 0;                          // Tried all weapons!
702                                 } else if (player_has_weapon(SecondaryOrder[cur_weapon], 1) == HAS_ALL) {
703                                         select_weapon(SecondaryOrder[cur_weapon], 1, 1, 1 );
704                                         try_again = 0;
705                                 }
706                         }
707                 }
708
709
710         }
711
712 }
713
714 #ifndef RELEASE
715
716 //      ----------------------------------------------------------------------------------------
717 //      Show player which weapons he has, how much ammo...
718 //      Looks like a debug screen now because it writes to mono screen, but that will change...
719 void show_weapon_status(void)
720 {
721         int     i;
722
723         for (i=0; i<MAX_PRIMARY_WEAPONS; i++) {
724                 if (Players[Player_num].primary_weapon_flags & (1 << i))
725                         mprintf((0, "HAVE"));
726                 else
727                         mprintf((0, "    "));
728
729                 mprintf((0, "  Weapon: %20s, charges: %4i\n", PRIMARY_WEAPON_NAMES(i), Players[Player_num].primary_ammo[i]));
730         }
731
732         mprintf((0, "\n"));
733         for (i=0; i<MAX_SECONDARY_WEAPONS; i++) {
734                 if (Players[Player_num].secondary_weapon_flags & (1 << i))
735                         mprintf((0, "HAVE"));
736                 else
737                         mprintf((0, "    "));
738
739                 mprintf((0, "  Weapon: %20s, charges: %4i\n", SECONDARY_WEAPON_NAMES(i), Players[Player_num].secondary_ammo[i]));
740         }
741
742         mprintf((0, "\n"));
743         mprintf((0, "\n"));
744
745 }
746
747 #endif
748
749 //      ---------------------------------------------------------------------
750 //called when one of these weapons is picked up
751 //when you pick up a secondary, you always get the weapon & ammo for it
752 //      Returns true if powerup picked up, else returns false.
753 int pick_up_secondary(int weapon_index,int count)
754 {
755         int max;
756         int     num_picked_up;
757         int cutpoint;
758
759         max = Secondary_ammo_max[weapon_index];
760
761         if (Players[Player_num].flags & PLAYER_FLAGS_AMMO_RACK)
762                 max *= 2;
763
764         if (Players[Player_num].secondary_ammo[weapon_index] >= max) {
765                 HUD_init_message("%s %i %ss!", TXT_ALREADY_HAVE, Players[Player_num].secondary_ammo[weapon_index],SECONDARY_WEAPON_NAMES(weapon_index));
766                 return 0;
767         }
768
769         Players[Player_num].secondary_weapon_flags |= (1<<weapon_index);
770         Players[Player_num].secondary_ammo[weapon_index] += count;
771
772         num_picked_up = count;
773         if (Players[Player_num].secondary_ammo[weapon_index] > max) {
774                 num_picked_up = count - (Players[Player_num].secondary_ammo[weapon_index] - max);
775                 Players[Player_num].secondary_ammo[weapon_index] = max;
776         }
777
778         cutpoint=SOrderList (255);
779         if (SOrderList (weapon_index)<cutpoint && ((SOrderList (weapon_index) < SOrderList(Secondary_weapon)) || (Players[Player_num].secondary_ammo[Secondary_weapon] == 0))   )
780                 select_weapon(weapon_index,1, 0, 1);
781         else {
782                 //if we don't auto-select this weapon, but it's a proxbomb or smart mine,
783                 //we want to do a mini-auto-selection that applies to the drop bomb key
784
785                 if ((weapon_index == PROXIMITY_INDEX || weapon_index == SMART_MINE_INDEX) &&
786                                 !(Secondary_weapon == PROXIMITY_INDEX || Secondary_weapon == SMART_MINE_INDEX)) {
787                         int cur;
788
789                         cur = Secondary_last_was_super[PROXIMITY_INDEX]?SMART_MINE_INDEX:PROXIMITY_INDEX;
790
791                         if (SOrderList (weapon_index) < SOrderList(cur))
792                                 Secondary_last_was_super[PROXIMITY_INDEX] = (weapon_index == SMART_MINE_INDEX);
793                 }
794         }
795
796         //note: flash for all but concussion was 7,14,21
797         if (count>1) {
798                 PALETTE_FLASH_ADD(15,15,15);
799                 HUD_init_message("%d %s%s",num_picked_up,SECONDARY_WEAPON_NAMES(weapon_index), TXT_SX);
800         }
801         else {
802                 PALETTE_FLASH_ADD(10,10,10);
803                 HUD_init_message("%s!",SECONDARY_WEAPON_NAMES(weapon_index));
804         }
805
806         return 1;
807 }
808
809 void ReorderPrimary ()
810 {
811         newmenu_item m[MAX_PRIMARY_WEAPONS+1];
812         int i;
813
814         for (i=0;i<MAX_PRIMARY_WEAPONS+1;i++)
815         {
816                 m[i].type=NM_TYPE_MENU;
817                 if (PrimaryOrder[i]==255)
818                         m[i].text="\88\88\88\88\88\88\88 Never autoselect \88\88\88\88\88\88\88";
819                 else
820                         m[i].text=(char *)PRIMARY_WEAPON_NAMES(PrimaryOrder[i]);
821                 m[i].value=PrimaryOrder[i];
822         }
823         MenuReordering=1;
824         i = newmenu_do("Reorder Primary","Shift+Up/Down arrow to move item", i, m, NULL);
825         MenuReordering=0;
826         
827         for (i=0;i<MAX_PRIMARY_WEAPONS+1;i++)
828                 PrimaryOrder[i]=m[i].value;
829 }
830
831 void ReorderSecondary ()
832 {
833         newmenu_item m[MAX_SECONDARY_WEAPONS+1];
834         int i;
835
836         for (i=0;i<MAX_SECONDARY_WEAPONS+1;i++)
837         {
838                 m[i].type=NM_TYPE_MENU;
839                 if (SecondaryOrder[i]==255)
840                         m[i].text="\88\88\88\88\88\88\88 Never autoselect \88\88\88\88\88\88\88";
841                 else
842                         m[i].text=(char *)SECONDARY_WEAPON_NAMES(SecondaryOrder[i]);
843                 m[i].value=SecondaryOrder[i];
844         }
845         MenuReordering=1;
846         i = newmenu_do("Reorder Secondary","Shift+Up/Down arrow to move item", i, m, NULL);
847         MenuReordering=0;
848         for (i=0;i<MAX_SECONDARY_WEAPONS+1;i++)
849                 SecondaryOrder[i]=m[i].value;
850 }
851
852 int POrderList (int num)
853 {
854         int i;
855
856         for (i=0;i<MAX_PRIMARY_WEAPONS+1;i++)
857         if (PrimaryOrder[i]==num)
858         {
859                 mprintf ((0,"Primary %d has priority of %d!\n",num,i));
860                 return (i);
861         }
862         Error ("Primary Weapon is not in order list!!!");
863 }
864
865 int SOrderList (int num)
866 {
867         int i;
868
869         for (i=0;i<MAX_SECONDARY_WEAPONS+1;i++)
870                 if (SecondaryOrder[i]==num)
871                 {
872                         mprintf ((0,"Secondary %d has priority of %d!\n",num,i));
873                         return (i);
874                 }
875         mprintf ((0,"Error! Secondary Num=%d\n",num));
876         Error ("Secondary Weapon is not in order list!!!");
877 }
878
879
880 //called when a primary weapon is picked up
881 //returns true if actually picked up
882 int pick_up_primary(int weapon_index)
883 {
884         //ushort old_flags = Players[Player_num].primary_weapon_flags;
885         ushort flag = 1<<weapon_index;
886         int cutpoint;
887         int supposed_weapon=Primary_weapon;
888
889         if (weapon_index!=LASER_INDEX && Players[Player_num].primary_weapon_flags & flag) {             //already have
890                 HUD_init_message("%s %s!", TXT_ALREADY_HAVE_THE, PRIMARY_WEAPON_NAMES(weapon_index));
891                 return 0;
892         }
893
894         Players[Player_num].primary_weapon_flags |= flag;
895
896         cutpoint=POrderList (255);
897
898         if (Primary_weapon==LASER_INDEX && Players[Player_num].laser_level>=4)  
899                 supposed_weapon=SUPER_LASER_INDEX;  // allotment for stupid way of doing super laser
900
901
902         if (POrderList(weapon_index)<cutpoint && POrderList(weapon_index)<POrderList(supposed_weapon))
903                 select_weapon(weapon_index,0,0,1);
904
905         PALETTE_FLASH_ADD(7,14,21);
906         mprintf ((0,"Weapon index: %d\n",weapon_index));
907         
908    if (weapon_index!=LASER_INDEX)
909         HUD_init_message("%s!",PRIMARY_WEAPON_NAMES(weapon_index));
910
911         return 1;
912 }
913 int check_to_use_primary(int weapon_index)
914 {
915         ushort old_flags = Players[Player_num].primary_weapon_flags;
916         ushort flag = 1<<weapon_index;
917         int cutpoint;
918
919         cutpoint=POrderList (255);
920
921         if (!(old_flags & flag) && POrderList(weapon_index)<cutpoint && POrderList(weapon_index)<POrderList(Primary_weapon))
922         {
923                 if (weapon_index==SUPER_LASER_INDEX)
924                         select_weapon(LASER_INDEX,0,0,1);
925                 else
926                         select_weapon(weapon_index,0,0,1);
927         }
928                 
929         PALETTE_FLASH_ADD(7,14,21);
930
931         return 1;
932 }
933
934
935
936 //called when ammo (for the vulcan cannon) is picked up
937 //      Returns the amount picked up
938 int pick_up_ammo(int class_flag,int weapon_index,int ammo_count)
939 {
940         int max,cutpoint,supposed_weapon=Primary_weapon;
941         int old_ammo=class_flag;                //kill warning
942
943         Assert(class_flag==CLASS_PRIMARY && weapon_index==VULCAN_INDEX);
944
945         max = Primary_ammo_max[weapon_index];
946         if (Players[Player_num].flags & PLAYER_FLAGS_AMMO_RACK)
947                 max *= 2;
948
949         if (Players[Player_num].primary_ammo[weapon_index] == max)
950                 return 0;
951
952         old_ammo = Players[Player_num].primary_ammo[weapon_index];
953
954         Players[Player_num].primary_ammo[weapon_index] += ammo_count;
955
956         if (Players[Player_num].primary_ammo[weapon_index] > max) {
957                 ammo_count += (max - Players[Player_num].primary_ammo[weapon_index]);
958                 Players[Player_num].primary_ammo[weapon_index] = max;
959         }
960         cutpoint=POrderList (255);
961
962         if (Primary_weapon==LASER_INDEX && Players[Player_num].laser_level>=4)  
963                 supposed_weapon=SUPER_LASER_INDEX;  // allotment for stupid way of doing super laser
964
965
966         if (Players[Player_num].primary_weapon_flags&(1<<weapon_index) && weapon_index>Primary_weapon && old_ammo==0 &&
967                 POrderList(weapon_index)<cutpoint && POrderList(weapon_index)<POrderList(supposed_weapon))
968                 select_weapon(weapon_index,0,0,1);
969
970         return ammo_count;      //return amount used
971 }
972
973 #define SMEGA_SHAKE_TIME                (F1_0*2)
974 #define MAX_SMEGA_DETONATES     4
975 fix     Smega_detonate_times[MAX_SMEGA_DETONATES];
976
977 //      Call this to initialize for a new level.
978 //      Sets all super mega missile detonation times to 0 which means there aren't any.
979 void init_smega_detonates(void)
980 {
981         int     i;
982
983         for (i=0; i<MAX_SMEGA_DETONATES; i++)
984                 Smega_detonate_times[i] = 0;    
985 }
986
987 fix     Seismic_tremor_magnitude;
988 fix     Next_seismic_sound_time;
989 int     Seismic_sound_playing = 0;
990 int     Seismic_tremor_volume;
991
992 int     Seismic_sound = SOUND_SEISMIC_DISTURBANCE_START;
993
994 //      If a smega missile been detonated, rock the mine!
995 //      This should be called every frame.
996 //      Maybe this should affect all robots, being called when they get their physics done.
997 void rock_the_mine_frame(void)
998 {
999         int     i;
1000
1001         for (i=0; i<MAX_SMEGA_DETONATES; i++) {
1002
1003                 if (Smega_detonate_times[i] != 0) {
1004                         fix     delta_time;
1005                         delta_time = GameTime - Smega_detonate_times[i];
1006
1007                         if (!Seismic_sound_playing) {
1008                                 digi_play_sample_looping(Seismic_sound, F1_0, -1, -1);
1009                                 Seismic_sound_playing = 1;
1010                                 Next_seismic_sound_time = GameTime + d_rand()/2;
1011                         }
1012
1013                         if (delta_time < SMEGA_SHAKE_TIME) {
1014
1015                                 //      Control center destroyed, rock the player's ship.
1016                                 int     fc, rx, rz;
1017                                 // -- fc = abs(delta_time - SMEGA_SHAKE_TIME/2);
1018                                 //      Changed 10/23/95 to make decreasing for super mega missile.
1019                                 fc = (SMEGA_SHAKE_TIME - delta_time)/2;
1020                                 fc /= SMEGA_SHAKE_TIME/32;
1021                                 if (fc > 16)
1022                                         fc = 16;
1023
1024                                 if (fc == 0)
1025                                         fc = 1;
1026
1027                                 Seismic_tremor_volume += fc;
1028
1029                                 rx = fixmul(d_rand() - 16384, 3*F1_0/16 + (F1_0*(16-fc))/32);
1030                                 rz = fixmul(d_rand() - 16384, 3*F1_0/16 + (F1_0*(16-fc))/32);
1031
1032                                 ConsoleObject->mtype.phys_info.rotvel.x += rx;
1033                                 ConsoleObject->mtype.phys_info.rotvel.z += rz;
1034
1035                                 //      Shake the buddy!
1036                                 if (Buddy_objnum != -1) {
1037                                         Objects[Buddy_objnum].mtype.phys_info.rotvel.x += rx*4;
1038                                         Objects[Buddy_objnum].mtype.phys_info.rotvel.z += rz*4;
1039                                 }
1040
1041                                 //      Shake a guided missile!
1042                                 Seismic_tremor_magnitude += rx;
1043
1044                         } else
1045                                 Smega_detonate_times[i] = 0;
1046
1047                 }
1048         }
1049
1050         //      Hook in the rumble sound effect here.
1051 }
1052
1053 extern  int     Level_shake_frequency, Level_shake_duration;
1054 #ifdef NETWORK
1055 extern void multi_send_seismic (fix,fix);
1056 #endif
1057
1058 #define SEISMIC_DISTURBANCE_DURATION    (F1_0*5)
1059 fix     Seismic_disturbance_start_time = 0, Seismic_disturbance_end_time;
1060
1061 int Seismic_level=0;
1062
1063 int     on_seismic_level(void)
1064 {
1065         return Seismic_level;
1066 }
1067
1068 void init_seismic_disturbances(void)
1069 {
1070         Seismic_disturbance_start_time = 0;
1071         Seismic_disturbance_end_time = 0;
1072 }
1073
1074 //      Return true if time to start a seismic disturbance.
1075 int start_seismic_disturbance(void)
1076 {
1077         int     rval;
1078
1079         if (Level_shake_duration < 1)
1080                 return 0;
1081
1082         rval =  (2 * fixmul(d_rand(), Level_shake_frequency)) < FrameTime;
1083
1084         if (rval) {
1085                 Seismic_disturbance_start_time = GameTime;
1086                 Seismic_disturbance_end_time = GameTime + Level_shake_duration;
1087                 if (!Seismic_sound_playing) {
1088                         digi_play_sample_looping(Seismic_sound, F1_0, -1, -1);
1089                         Seismic_sound_playing = 1;
1090                         Next_seismic_sound_time = GameTime + d_rand()/2;
1091                 }
1092
1093 #ifdef NETWORK
1094                 if (Game_mode & GM_MULTI)
1095                         multi_send_seismic (Seismic_disturbance_start_time,Seismic_disturbance_end_time);
1096 #endif
1097         }
1098
1099         return rval;
1100 }
1101
1102 void seismic_disturbance_frame(void)
1103 {
1104         if (Level_shake_frequency) {
1105                 if (((Seismic_disturbance_start_time < GameTime) && (Seismic_disturbance_end_time > GameTime)) || start_seismic_disturbance()) {
1106                         fix     delta_time;
1107                         int     fc, rx, rz;
1108
1109                         delta_time = GameTime - Seismic_disturbance_start_time;
1110
1111                         fc = abs(delta_time - (Seismic_disturbance_end_time - Seismic_disturbance_start_time)/2);
1112                         fc /= F1_0/16;
1113                         if (fc > 16)
1114                                 fc = 16;
1115
1116                         if (fc == 0)
1117                                 fc = 1;
1118
1119                         Seismic_tremor_volume += fc;
1120
1121                         rx = fixmul(d_rand() - 16384, 3*F1_0/16 + (F1_0*(16-fc))/32);
1122                         rz = fixmul(d_rand() - 16384, 3*F1_0/16 + (F1_0*(16-fc))/32);
1123
1124                         ConsoleObject->mtype.phys_info.rotvel.x += rx;
1125                         ConsoleObject->mtype.phys_info.rotvel.z += rz;
1126
1127                         //      Shake the buddy!
1128                         if (Buddy_objnum != -1) {
1129                                 Objects[Buddy_objnum].mtype.phys_info.rotvel.x += rx*4;
1130                                 Objects[Buddy_objnum].mtype.phys_info.rotvel.z += rz*4;
1131                         }
1132
1133                         //      Shake a guided missile!
1134                         Seismic_tremor_magnitude += rx;
1135                 }
1136         }
1137 }
1138
1139
1140 //      Call this when a smega detonates to start the process of rocking the mine.
1141 void smega_rock_stuff(void)
1142 {
1143         int     i;
1144
1145         for (i=0; i<MAX_SMEGA_DETONATES; i++) {
1146                 if (Smega_detonate_times[i] + SMEGA_SHAKE_TIME < GameTime)
1147                         Smega_detonate_times[i] = 0;
1148         }
1149
1150         for (i=0; i<MAX_SMEGA_DETONATES; i++) {
1151                 if (Smega_detonate_times[i] == 0) {
1152                         Smega_detonate_times[i] = GameTime;
1153                         break;
1154                 }
1155         }
1156 }
1157
1158 int     Super_mines_yes = 1;
1159
1160 //      Call this once/frame to process all super mines in the level.
1161 void process_super_mines_frame(void)
1162 {
1163         int     i, j;
1164         int     start, add;
1165
1166         //      If we don't know of there being any super mines in the level, just
1167         //      check every 8th object each frame.
1168         if (Super_mines_yes == 0) {
1169                 start = FrameCount & 7;
1170                 add = 8;
1171         } else {
1172                 start = 0;
1173                 add = 1;
1174         }
1175
1176         Super_mines_yes = 0;
1177
1178         for (i=start; i<=Highest_object_index; i+=add) {
1179                 if ((Objects[i].type == OBJ_WEAPON) && (Objects[i].id == SUPERPROX_ID)) {
1180                         int     parent_num;
1181
1182                         parent_num = Objects[i].ctype.laser_info.parent_num;
1183
1184                         Super_mines_yes = 1;
1185                         if (Objects[i].lifeleft + F1_0*2 < Weapon_info[SUPERPROX_ID].lifetime) {
1186                                 vms_vector      *bombpos;
1187         
1188                                 bombpos = &Objects[i].pos;
1189         
1190                                 for (j=0; j<=Highest_object_index; j++) {
1191                                         if ((Objects[j].type == OBJ_PLAYER) || (Objects[j].type == OBJ_ROBOT)) {
1192                                                 fix     dist;
1193                 
1194                                                 dist = vm_vec_dist_quick(bombpos, &Objects[j].pos);
1195                 
1196                                                 if (j != parent_num)
1197                                                         if (dist - Objects[j].size < F1_0*20)
1198                                                         {
1199                                                                 if (Objects[i].segnum == Objects[j].segnum)
1200                                                                         Objects[i].lifeleft = 1;
1201                                                                 else {
1202                                                                         //      Object which is close enough to detonate smart mine is not in same segment as smart mine.
1203                                                                         //      Need to do a more expensive check to make sure there isn't an obstruction.
1204                                                                         if (((FrameCount ^ (i+j)) % 4) == 0) {
1205                                                                                 fvi_query       fq;
1206                                                                                 fvi_info                hit_data;
1207                                                                                 int                     fate;
1208
1209                                                                                 mprintf((0, "Expensive proxmine collision check.  Frame %i\n", FrameCount));
1210
1211                                                                                 fq.startseg = Objects[i].segnum;
1212                                                                                 fq.p0                                           = &Objects[i].pos;
1213                                                                                 fq.p1                                           = &Objects[j].pos;
1214                                                                                 fq.rad                                  = 0;
1215                                                                                 fq.thisobjnum                   = i;
1216                                                                                 fq.ignore_obj_list      = NULL;
1217                                                                                 fq.flags                                        = 0;
1218
1219                                                                                 fate = find_vector_intersection(&fq, &hit_data);
1220                                                                                 if (fate != HIT_WALL)
1221                                                                                         Objects[i].lifeleft = 1;
1222                                                                         }
1223                                                                 }
1224                                                         }
1225                                         }
1226                                 }
1227                         }
1228                 }
1229         }
1230
1231 }
1232
1233 #define SPIT_SPEED 20
1234
1235 //this function is for when the player intentionally drops a powerup
1236 //this function is based on drop_powerup()
1237 int spit_powerup(object *spitter, int id,int seed)
1238 {
1239         int             objnum;
1240         object  *obj;
1241         vms_vector      new_velocity, new_pos;
1242
1243         d_srand(seed);
1244
1245         vm_vec_scale_add(&new_velocity,&spitter->mtype.phys_info.velocity,&spitter->orient.fvec,i2f(SPIT_SPEED));
1246
1247         new_velocity.x += (d_rand() - 16384) * SPIT_SPEED * 2;
1248         new_velocity.y += (d_rand() - 16384) * SPIT_SPEED * 2;
1249         new_velocity.z += (d_rand() - 16384) * SPIT_SPEED * 2;
1250
1251         // Give keys zero velocity so they can be tracked better in multi
1252
1253         if ((Game_mode & GM_MULTI) && (id >= POW_KEY_BLUE) && (id <= POW_KEY_GOLD))
1254                 vm_vec_zero(&new_velocity);
1255
1256         //there's a piece of code which lets the player pick up a powerup if
1257         //the distance between him and the powerup is less than 2 time their
1258         //combined radii.  So we need to create powerups pretty far out from
1259         //the player.
1260
1261         vm_vec_scale_add(&new_pos,&spitter->pos,&spitter->orient.fvec,spitter->size);
1262
1263 #ifdef NETWORK
1264         if (Game_mode & GM_MULTI)
1265         {       
1266                 if (Net_create_loc >= MAX_NET_CREATE_OBJECTS)
1267                 {
1268                         mprintf( (0, "WEAPON:Not enough slots to drop all powerups!\n" ));
1269                         return (-1);
1270                 }
1271         }
1272 #endif
1273
1274         objnum = obj_create( OBJ_POWERUP, id, spitter->segnum, &new_pos, &vmd_identity_matrix, Powerup_info[id].size, CT_POWERUP, MT_PHYSICS, RT_POWERUP);
1275
1276         if (objnum < 0 ) {
1277                 mprintf((1, "Can't create object in object_create_egg.  Aborting.\n"));
1278                 Int3();
1279                 return objnum;
1280         }
1281
1282         obj = &Objects[objnum];
1283
1284         obj->mtype.phys_info.velocity = new_velocity;
1285         obj->mtype.phys_info.drag = 512;        //1024;
1286         obj->mtype.phys_info.mass = F1_0;
1287
1288         obj->mtype.phys_info.flags = PF_BOUNCE;
1289
1290         obj->rtype.vclip_info.vclip_num = Powerup_info[obj->id].vclip_num;
1291         obj->rtype.vclip_info.frametime = Vclip[obj->rtype.vclip_info.vclip_num].frame_time;
1292         obj->rtype.vclip_info.framenum = 0;
1293
1294         if (spitter == ConsoleObject)
1295                 obj->ctype.powerup_info.flags |= PF_SPAT_BY_PLAYER;
1296
1297         switch (obj->id) {
1298                 case POW_MISSILE_1:
1299                 case POW_MISSILE_4:
1300                 case POW_SHIELD_BOOST:
1301                 case POW_ENERGY:
1302                         obj->lifeleft = (d_rand() + F1_0*3) * 64;               //      Lives for 3 to 3.5 binary minutes (a binary minute is 64 seconds)
1303                         if (Game_mode & GM_MULTI)
1304                                 obj->lifeleft /= 2;
1305                         break;
1306                 default:
1307                         //if (Game_mode & GM_MULTI)
1308                         //      obj->lifeleft = (d_rand() + F1_0*3) * 64;               //      Lives for 5 to 5.5 binary minutes (a binary minute is 64 seconds)
1309                         break;
1310         }
1311
1312         return objnum;
1313 }
1314
1315 void DropCurrentWeapon ()
1316 {
1317         int objnum,ammo=0,seed;
1318
1319         if (Primary_weapon==0)
1320         {
1321                 HUD_init_message("You cannot drop your base weapon!");
1322                 return;
1323         }
1324
1325         HUD_init_message("%s dropped!",PRIMARY_WEAPON_NAMES(Primary_weapon));
1326         digi_play_sample (SOUND_DROP_WEAPON,F1_0);
1327
1328         seed = d_rand();
1329
1330         objnum = spit_powerup(ConsoleObject,Primary_weapon_to_powerup[Primary_weapon],seed);
1331
1332    if (objnum<0)
1333                 return;
1334
1335         if (Primary_weapon == VULCAN_INDEX || Primary_weapon == GAUSS_INDEX) {
1336
1337                 //if it's one of these, drop some ammo with the weapon
1338
1339                 ammo = Players[Player_num].primary_ammo[VULCAN_INDEX];
1340
1341                 if ((Players[Player_num].primary_weapon_flags & HAS_FLAG(VULCAN_INDEX)) && (Players[Player_num].primary_weapon_flags & HAS_FLAG(GAUSS_INDEX)))
1342                         ammo /= 2;              //if both vulcan & gauss, drop half
1343
1344                 Players[Player_num].primary_ammo[VULCAN_INDEX] -= ammo;
1345
1346                 if (objnum!=-1)
1347                         Objects[objnum].ctype.powerup_info.count = ammo;
1348         }
1349
1350         if (Primary_weapon == OMEGA_INDEX) {
1351
1352                 //dropped weapon has current energy
1353
1354                 if (objnum!=-1)
1355                         Objects[objnum].ctype.powerup_info.count = Omega_charge;
1356         }
1357
1358 #ifdef NETWORK
1359         if ((Game_mode & GM_MULTI) && objnum>-1)
1360                 multi_send_drop_weapon(objnum,seed);
1361 #endif
1362
1363         Players[Player_num].primary_weapon_flags &= (~(1<<Primary_weapon));
1364         auto_select_weapon (0);
1365 }
1366
1367
1368 void DropSecondaryWeapon ()
1369 {
1370         int objnum,seed;
1371
1372         if (Players[Player_num].secondary_ammo[Secondary_weapon] ==0)
1373         {
1374                 HUD_init_message("No secondary weapon to drop!");
1375                 return;
1376         }
1377
1378         if ((Secondary_weapon_to_powerup[Secondary_weapon]==POW_PROXIMITY_WEAPON ||
1379                 Secondary_weapon_to_powerup[Secondary_weapon]==POW_SMART_MINE) &&
1380                 Players[Player_num].secondary_ammo[Secondary_weapon]<4)
1381         {
1382                 HUD_init_message("You need at least 4 to drop!");
1383                 return;
1384         }
1385
1386         HUD_init_message("%s dropped!",SECONDARY_WEAPON_NAMES(Secondary_weapon));
1387         digi_play_sample (SOUND_DROP_WEAPON,F1_0);
1388
1389         seed = d_rand();
1390
1391         objnum = spit_powerup(ConsoleObject,Secondary_weapon_to_powerup[Secondary_weapon],seed);
1392
1393    if (objnum<0)
1394                 return;
1395
1396
1397 #ifdef NETWORK
1398         if ((Game_mode & GM_MULTI) && objnum>-1)
1399                 multi_send_drop_weapon(objnum,seed);
1400 #endif
1401
1402         if (Secondary_weapon_to_powerup[Secondary_weapon]==POW_PROXIMITY_WEAPON ||
1403                 Secondary_weapon_to_powerup[Secondary_weapon]==POW_SMART_MINE)
1404                 Players[Player_num].secondary_ammo[Secondary_weapon]-=4;
1405         else
1406                 Players[Player_num].secondary_ammo[Secondary_weapon]--;
1407
1408         if (Players[Player_num].secondary_ammo[Secondary_weapon]==0)
1409         {
1410                 Players[Player_num].secondary_weapon_flags &= (~(1<<Secondary_weapon));
1411                 auto_select_weapon (1);
1412         }
1413 }
1414
1415 //      ---------------------------------------------------------------------------------------
1416 //      Do seismic disturbance stuff including the looping sounds with changing volume.
1417 void do_seismic_stuff(void)
1418 {
1419         int     stv_save;
1420
1421         stv_save = Seismic_tremor_volume;
1422         Seismic_tremor_magnitude = 0;
1423         Seismic_tremor_volume = 0;
1424
1425         rock_the_mine_frame();
1426         seismic_disturbance_frame();
1427
1428         if (stv_save != 0) {
1429                 if (Seismic_tremor_volume == 0) {
1430                         digi_stop_looping_sound();
1431                         Seismic_sound_playing = 0;
1432                 }
1433
1434                 if ((GameTime > Next_seismic_sound_time) && Seismic_tremor_volume) {
1435                         int     volume;
1436
1437                         volume = Seismic_tremor_volume * 2048;
1438                         if (volume > F1_0)
1439                                 volume = F1_0;
1440                         digi_change_looping_volume(volume);
1441                         Next_seismic_sound_time = GameTime + d_rand()/4 + 8192;
1442                 }
1443         }
1444
1445 }
1446
1447 int tactile_fire_duration[]={120,80,150,250,150,200,100,180,280,100};
1448 int tactile_fire_repeat[]={260,90,160,160,160,210,110,191,291,111};
1449
1450 void tactile_set_button_jolt ()
1451  {
1452   #ifdef TACTILE
1453
1454   FILE *infile;
1455   int t,i;
1456   static int stickmag=-1;
1457   int dur,rep;
1458
1459   dur=tactile_fire_duration[Primary_weapon];
1460   rep=tactile_fire_repeat[Primary_weapon];
1461
1462   if (TactileStick)
1463    {
1464          if (stickmag==-1)
1465           {
1466            if (t=FindArg("-stickmag"))
1467                         stickmag=atoi (Args[t+1]);
1468            else
1469                 stickmag=50;
1470
1471       infile=(FILE *)fopen ("stick.val","rt");
1472                 if (infile!=NULL)
1473                  {
1474                         for (i=0;i<10;i++)
1475                          {
1476                           fscanf (infile,"%d %d\n",&tactile_fire_duration[i],&tactile_fire_repeat[i]);
1477                           mprintf ((0,"scan value[%d]=%d\n",i,tactile_fire_duration[i]));
1478                          }
1479                         fclose (infile);
1480                  }
1481           }
1482     ButtonReflexJolt (0,stickmag,0,dur,rep);
1483    }
1484   #endif
1485  }
1486
1487 /*
1488  * reads n weapon_info structs from a CFILE
1489  */
1490 extern int weapon_info_read_n(weapon_info *wi, int n, CFILE *fp, int file_version)
1491 {
1492         int i, j;
1493
1494         for (i = 0; i < n; i++) {
1495                 wi[i].render_type = cfile_read_byte(fp);
1496                 wi[i].persistent = cfile_read_byte(fp);
1497                 wi[i].model_num = cfile_read_short(fp);
1498                 wi[i].model_num_inner = cfile_read_short(fp);
1499
1500                 wi[i].flash_vclip = cfile_read_byte(fp);
1501                 wi[i].robot_hit_vclip = cfile_read_byte(fp);
1502                 wi[i].flash_sound = cfile_read_short(fp);
1503
1504                 wi[i].wall_hit_vclip = cfile_read_byte(fp);
1505                 wi[i].fire_count = cfile_read_byte(fp);
1506                 wi[i].robot_hit_sound = cfile_read_short(fp);
1507
1508                 wi[i].ammo_usage = cfile_read_byte(fp);
1509                 wi[i].weapon_vclip = cfile_read_byte(fp);
1510                 wi[i].wall_hit_sound = cfile_read_short(fp);
1511
1512                 wi[i].destroyable = cfile_read_byte(fp);
1513                 wi[i].matter = cfile_read_byte(fp);
1514                 wi[i].bounce = cfile_read_byte(fp);
1515                 wi[i].homing_flag = cfile_read_byte(fp);
1516
1517                 wi[i].speedvar = cfile_read_byte(fp);
1518                 wi[i].flags = cfile_read_byte(fp);
1519                 wi[i].flash = cfile_read_byte(fp);
1520                 wi[i].afterburner_size = cfile_read_byte(fp);
1521
1522                 if (file_version >= 3)
1523                         wi[i].children = cfile_read_byte(fp);
1524                 else
1525                         /* Set the type of children correctly when using old
1526                          * datafiles.  In earlier descent versions this was simply
1527                          * hard-coded in create_smart_children().
1528                          */
1529                         switch (i)
1530                         {
1531                         case SMART_ID:
1532                                 wi[i].children = PLAYER_SMART_HOMING_ID;
1533                                 break;
1534                         case SUPERPROX_ID:
1535                                 wi[i].children = SMART_MINE_HOMING_ID;
1536                                 break;
1537 #if 0 /* not present in shareware */
1538                         case ROBOT_SUPERPROX_ID:
1539                                 wi[i].children = ROBOT_SMART_MINE_HOMING_ID;
1540                                 break;
1541                         case EARTHSHAKER_ID:
1542                                 wi[i].children = EARTHSHAKER_MEGA_ID;
1543                                 break;
1544 #endif
1545                         default:
1546                                 wi[i].children = -1;
1547                                 break;
1548                         }
1549
1550                 wi[i].energy_usage = cfile_read_fix(fp);
1551                 wi[i].fire_wait = cfile_read_fix(fp);
1552
1553                 if (file_version >= 3)
1554                         wi[i].multi_damage_scale = cfile_read_fix(fp);
1555                 else /* FIXME: hack this to set the real values */
1556                         wi[i].multi_damage_scale = F1_0;
1557
1558                 bitmap_index_read(&wi[i].bitmap, fp);
1559
1560                 wi[i].blob_size = cfile_read_fix(fp);
1561                 wi[i].flash_size = cfile_read_fix(fp);
1562                 wi[i].impact_size = cfile_read_fix(fp);
1563                 for (j = 0; j < NDL; j++)
1564                         wi[i].strength[j] = cfile_read_fix(fp);
1565                 for (j = 0; j < NDL; j++)
1566                         wi[i].speed[j] = cfile_read_fix(fp);
1567                 wi[i].mass = cfile_read_fix(fp);
1568                 wi[i].drag = cfile_read_fix(fp);
1569                 wi[i].thrust = cfile_read_fix(fp);
1570                 wi[i].po_len_to_width_ratio = cfile_read_fix(fp);
1571                 wi[i].light = cfile_read_fix(fp);
1572                 wi[i].lifetime = cfile_read_fix(fp);
1573                 wi[i].damage_radius = cfile_read_fix(fp);
1574                 bitmap_index_read(&wi[i].picture, fp);
1575                 if (file_version >= 3)
1576                         bitmap_index_read(&wi[i].hires_picture, fp);
1577                 else
1578                         wi[i].hires_picture.index = wi[i].picture.index;
1579         }
1580         return i;
1581 }