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