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