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