Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Portions of this file are copyright Rebirth contributors and licensed as
  3.  * described in COPYING.txt.
  4.  * Portions of this file are copyright Parallax Software and licensed
  5.  * according to the Parallax license below.
  6.  * See COPYING.txt for license details.
  7.  
  8. THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
  9. SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
  10. END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
  11. ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
  12. IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
  13. SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
  14. FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
  15. CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
  16. AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
  17. COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  18. */
  19.  
  20. /*
  21.  *
  22.  * Code for powerup objects.
  23.  *
  24.  */
  25.  
  26.  
  27. #include <stdarg.h>
  28. #include <stdio.h>
  29. #include <string.h>
  30.  
  31. #include "maths.h"
  32. #include "vecmat.h"
  33. #include "gr.h"
  34. #include "3d.h"
  35. #include "dxxerror.h"
  36. #include "inferno.h"
  37. #include "object.h"
  38. #include "game.h"
  39. #include "key.h"
  40. #include "fireball.h"
  41. #include "powerup.h"
  42. #include "gauges.h"
  43. #include "sounds.h"
  44. #include "player.h"
  45. #include "physfs-serial.h"
  46. #include "text.h"
  47. #include "weapon.h"
  48. #include "laser.h"
  49. #include "scores.h"
  50. #include "multi.h"
  51. #include "segment.h"
  52. #include "lighting.h"
  53. #include "controls.h"
  54. #include "kconfig.h"
  55. #include "newdemo.h"
  56. #include "escort.h"
  57. #if DXX_USE_EDITOR
  58. #include "gr.h" //      for powerup outline drawing
  59. #include "editor/editor.h"
  60. #endif
  61. #include "hudmsg.h"
  62. #include "playsave.h"
  63. #include "partial_range.h"
  64.  
  65. namespace dcx {
  66. unsigned N_powerup_types;
  67. }
  68. namespace dsx {
  69. d_powerup_info_array Powerup_info;
  70.  
  71. //process this powerup for this frame
  72. void do_powerup_frame(const d_vclip_array &Vclip, const vmobjptridx_t obj)
  73. {
  74.         vclip_info *vci = &obj->rtype.vclip_info;
  75.  
  76. #if defined(DXX_BUILD_DESCENT_I)
  77.         const fix fudge = 0;
  78. #elif defined(DXX_BUILD_DESCENT_II)
  79.         long objnum = obj;
  80.         const fix fudge = (FrameTime * (objnum&3)) >> 4;
  81. #endif
  82.        
  83.         auto &vc = Vclip[vci->vclip_num];
  84.         const auto vc_frame_time = vc.frame_time;
  85.         if (vc_frame_time > 0)
  86.         {
  87.         const auto vc_num_frames1 = vc.num_frames - 1;
  88.         vci->frametime -= FrameTime+fudge;
  89.        
  90.         while (vci->frametime < 0 ) {
  91.  
  92.                 vci->frametime += vc_frame_time;
  93.                 if (vci->framenum > vc_num_frames1)
  94.                         vci->framenum=0;
  95.  
  96. #if defined(DXX_BUILD_DESCENT_II)
  97.                 if (objnum&1)
  98.                 {
  99.                         if (-- vci->framenum > vc_num_frames1)
  100.                                 vci->framenum = vc_num_frames1;
  101.                 }
  102.                 else
  103. #endif
  104.                 {
  105.                         if (vci->framenum >= vc_num_frames1)
  106.                                 vci->framenum=0;
  107.                         else
  108.                                 vci->framenum++;
  109.                 }
  110.         }
  111.         }
  112.  
  113.         if (obj->lifeleft <= 0) {
  114.                 object_create_explosion(vmsegptridx(obj->segnum), obj->pos, F1_0*7/2, VCLIP_POWERUP_DISAPPEARANCE);
  115.  
  116.                 if ( Vclip[VCLIP_POWERUP_DISAPPEARANCE].sound_num > -1 )
  117.                         digi_link_sound_to_object(Vclip[VCLIP_POWERUP_DISAPPEARANCE].sound_num, obj, 0, F1_0, sound_stack::allow_stacking);
  118.         }
  119. }
  120.  
  121. }
  122.  
  123. namespace dcx {
  124.  
  125. void draw_powerup(const d_vclip_array &Vclip, grs_canvas &canvas, const object_base &obj)
  126. {
  127.         auto &vci = obj.rtype.vclip_info;
  128.         draw_object_blob(canvas, obj, Vclip[vci.vclip_num].frames[vci.framenum]);
  129. }
  130.  
  131. }
  132.  
  133. static void _powerup_basic_nonhud(int redadd, int greenadd, int blueadd, int score)
  134. {
  135.         PALETTE_FLASH_ADD(redadd,greenadd,blueadd);
  136.         add_points_to_score(ConsoleObject->ctype.player_info, score);
  137. }
  138.  
  139. void (powerup_basic)(int redadd, int greenadd, int blueadd, int score, const char *format, ...)
  140. {
  141.         va_list args;
  142.  
  143.         va_start(args, format );
  144.         HUD_init_message_va(HM_DEFAULT, format, args);
  145.         va_end(args);
  146.         _powerup_basic_nonhud(redadd, greenadd, blueadd, score);
  147. }
  148.  
  149. void powerup_basic_str(int redadd, int greenadd, int blueadd, int score, const char *str)
  150. {
  151.         HUD_init_message_literal(HM_DEFAULT, str);
  152.         _powerup_basic_nonhud(redadd, greenadd, blueadd, score);
  153. }
  154.  
  155. //#ifndef RELEASE
  156. //      Give the megawow powerup!
  157. namespace dsx {
  158. void do_megawow_powerup(int quantity)
  159. {
  160.         auto &Objects = LevelUniqueObjectState.Objects;
  161.         auto &vmobjptr = Objects.vmptr;
  162.         powerup_basic(30, 0, 30, 1, "MEGA-WOWIE-ZOWIE!");
  163.         auto &player_info = get_local_plrobj().ctype.player_info;
  164. #if defined(DXX_BUILD_DESCENT_I)
  165.         player_info.primary_weapon_flags = (HAS_LASER_FLAG | HAS_VULCAN_FLAG | HAS_SPREADFIRE_FLAG | HAS_PLASMA_FLAG | HAS_FUSION_FLAG);
  166. #elif defined(DXX_BUILD_DESCENT_II)
  167.         player_info.primary_weapon_flags = (HAS_LASER_FLAG | HAS_VULCAN_FLAG | HAS_SPREADFIRE_FLAG | HAS_PLASMA_FLAG | HAS_FUSION_FLAG) | (HAS_GAUSS_FLAG | HAS_HELIX_FLAG | HAS_PHOENIX_FLAG | HAS_OMEGA_FLAG);
  168. #endif
  169.         player_info.vulcan_ammo = VULCAN_AMMO_MAX;
  170.  
  171.         auto &secondary_ammo = player_info.secondary_ammo;
  172.         range_for (auto &i, partial_range(secondary_ammo, 3u))
  173.                 i = quantity;
  174.  
  175.         range_for (auto &i, partial_range(secondary_ammo, 3u, secondary_ammo.size()))
  176.                 i = quantity/5;
  177.  
  178.         player_info.energy = F1_0*200;
  179.         get_local_plrobj().shields = F1_0*200;
  180.         player_info.powerup_flags |= PLAYER_FLAGS_QUAD_LASERS;
  181. #if defined(DXX_BUILD_DESCENT_I)
  182.         const auto laser_level = MAX_LASER_LEVEL;
  183. #elif defined(DXX_BUILD_DESCENT_II)
  184.         player_info.Omega_charge = MAX_OMEGA_CHARGE;
  185.         if (game_mode_hoard())
  186.                 player_info.hoard.orbs = player_info.max_hoard_orbs;
  187.         const auto laser_level = MAX_SUPER_LASER_LEVEL;
  188. #endif
  189.         if (Newdemo_state == ND_STATE_RECORDING)
  190.                 newdemo_record_laser_level(player_info.laser_level, laser_level);
  191.         player_info.laser_level = laser_level;
  192. }
  193. }
  194. //#endif
  195.  
  196. namespace dsx {
  197.  
  198. static int pick_up_energy(player_info &player_info)
  199. {
  200.         int     used=0;
  201.  
  202.         auto &energy = player_info.energy;
  203.         if (energy < MAX_ENERGY) {
  204.                 fix boost;
  205.                 const auto Difficulty_level = GameUniqueState.Difficulty_level;
  206.                 boost = 3*F1_0 + 3*F1_0*(NDL - Difficulty_level);
  207. #if defined(DXX_BUILD_DESCENT_II)
  208.                 if (Difficulty_level == 0)
  209.                         boost += boost/2;
  210. #endif
  211.                 energy += boost;
  212.                 if (energy > MAX_ENERGY)
  213.                         energy = MAX_ENERGY;
  214.                 powerup_basic(15, 15, 7, ENERGY_SCORE, "%s %s %d", TXT_ENERGY, TXT_BOOSTED_TO, f2ir(energy));
  215.                 used=1;
  216.         } else
  217.                 HUD_init_message(HM_DEFAULT|HM_REDUNDANT|HM_MAYDUPL, TXT_MAXED_OUT,TXT_ENERGY);
  218.  
  219.         return used;
  220. }
  221.  
  222. static int pick_up_primary_or_energy(player_info &player_info, int weapon_index)
  223. {
  224.         const auto used = pick_up_primary(player_info, weapon_index);
  225.         if (used || (Game_mode & GM_MULTI))
  226.                 return used;
  227.         return pick_up_energy(player_info);
  228. }
  229.  
  230. static int pick_up_vulcan_ammo(player_info &player_info)
  231. {
  232.         int     used=0;
  233.         if (pick_up_vulcan_ammo(player_info, VULCAN_AMMO_AMOUNT, false)) {
  234.                 powerup_basic(7, 14, 21, VULCAN_AMMO_SCORE, "%s!", TXT_VULCAN_AMMO);
  235.                 used = 1;
  236.         } else {
  237.                 const auto max = PLAYER_MAX_AMMO(player_info.powerup_flags, VULCAN_AMMO_MAX);
  238.                 HUD_init_message(HM_DEFAULT | HM_REDUNDANT | HM_MAYDUPL, "%s %d %s!", TXT_ALREADY_HAVE, vulcan_ammo_scale(max), TXT_VULCAN_ROUNDS);
  239.                 used = 0;
  240.         }
  241.         return used;
  242. }
  243.  
  244. static int pick_up_key(const int r, const int g, const int b, player_flags &player_flags, const PLAYER_FLAG key_flag, const char *const key_name, const powerup_type_t id)
  245. {
  246.         if (player_flags & key_flag)
  247.                 return 0;
  248.         player_flags |= key_flag;
  249.         powerup_basic(r, g, b, KEY_SCORE, "%s %s", key_name, TXT_ACCESS_GRANTED);
  250.         multi_digi_play_sample(Powerup_info[id].hit_sound, F1_0);
  251. #if defined(DXX_BUILD_DESCENT_II)
  252.         auto &BuddyState = LevelUniqueObjectState.BuddyState;
  253.         invalidate_escort_goal(BuddyState);
  254. #endif
  255.         return (Game_mode & GM_MULTI) ? 0 : 1;
  256. }
  257.  
  258. //      returns true if powerup consumed
  259. namespace {
  260.  
  261. #if defined(DXX_BUILD_DESCENT_II)
  262. template <int r, int g, int b>
  263. struct player_hit_basic_silent_powerup
  264. {
  265.         const char *const desc_pickup;
  266.         player_hit_basic_silent_powerup(const char *const p) :
  267.                 desc_pickup(p)
  268.         {
  269.         }
  270.         void report() const
  271.         {
  272.                 powerup_basic_str(r, g, b, 0, desc_pickup);
  273.         }
  274.         template <PLAYER_FLAG player_flag>
  275.                 void pickup(player_flags &powerup_flags) const
  276.                 {
  277.                         powerup_flags |= player_flag;
  278.                         report();
  279.                 }
  280. };
  281.  
  282. template <int r, int g, int b, powerup_type_t id>
  283. struct player_hit_basic_sound_powerup : player_hit_basic_silent_powerup<r, g, b>
  284. {
  285.         using base_type = player_hit_basic_silent_powerup<r, g, b>;
  286.         DXX_INHERIT_CONSTRUCTORS(player_hit_basic_sound_powerup, base_type);
  287.         template <PLAYER_FLAG player_flag>
  288.                 void pickup(player_flags &powerup_flags) const
  289.                 {
  290.                         multi_digi_play_sample(Powerup_info[id].hit_sound, F1_0);
  291.                         base_type::template pickup<player_flag>(powerup_flags);
  292.                 }
  293. };
  294.  
  295. using player_hit_silent_rb_powerup = player_hit_basic_silent_powerup<15, 0, 15>;
  296.  
  297. struct player_hit_afterburner_powerup : player_hit_basic_sound_powerup<15, 15, 15, POW_AFTERBURNER>
  298. {
  299.         using base_type = player_hit_basic_sound_powerup<15, 15, 15, POW_AFTERBURNER>;
  300.         DXX_INHERIT_CONSTRUCTORS(player_hit_afterburner_powerup, base_type);
  301.         template <PLAYER_FLAG player_flag>
  302.                 void pickup(player_flags &powerup_flags) const
  303.                 {
  304.                         Afterburner_charge = f1_0;
  305.                         base_type::template pickup<player_flag>(powerup_flags);
  306.                 }
  307. };
  308.  
  309. struct player_hit_headlight_powerup
  310. {
  311.         /* Template parameter unused, but required for signature
  312.          * compatibility with the other player_hit_* structures.
  313.          */
  314.         template <PLAYER_FLAG>
  315.                 void pickup(player_flags &powerup_flags) const
  316.                 {
  317.                         process(powerup_flags);
  318.                 }
  319.         void process(player_flags &powerup_flags) const
  320.         {
  321.                 const auto active = PlayerCfg.HeadlightActiveDefault;
  322.                 powerup_flags |= active
  323.                         ? PLAYER_FLAG::HEADLIGHT_PRESENT_AND_ON
  324.                         : PLAYER_FLAG::HEADLIGHT;
  325.                 powerup_basic(15, 0, 15, 0, "HEADLIGHT BOOST! (Headlight is O%s)", active ? "N" : "FF");
  326.                 multi_digi_play_sample(Powerup_info[POW_HEADLIGHT].hit_sound, F1_0);
  327.                 if (active && (Game_mode & GM_MULTI))
  328.                         multi_send_flags (Player_num);
  329.         }
  330. };
  331.  
  332. template <unsigned TEAM>
  333. static int player_hit_flag_powerup(player_info &player_info, const char *const desc)
  334. {
  335.         if (!game_mode_capture_flag())
  336.                 return 0;
  337.         const auto pnum = Player_num;
  338.         if (get_team(pnum) == TEAM)
  339.         {
  340.                 player_info.powerup_flags |= PLAYER_FLAGS_FLAG;
  341.                 powerup_basic_str(15, 0, 15, 0, desc);
  342.                 multi_send_got_flag(pnum);
  343.                 return 1;
  344.         }
  345.         return 0;
  346. }
  347. #endif
  348.  
  349. struct player_hit_quadlaser_powerup
  350. {
  351.         /* Template parameter unused, but required for signature
  352.          * compatibility with the other player_hit_* structures.
  353.          */
  354.         template <PLAYER_FLAG>
  355.                 void pickup(player_flags &powerup_flags) const
  356.                 {
  357.                         process(powerup_flags);
  358.                 }
  359.         void process(player_flags &powerup_flags) const
  360.         {
  361.                 powerup_flags |= PLAYER_FLAGS_QUAD_LASERS;
  362.                 powerup_basic(15, 15, 7, QUAD_FIRE_SCORE, "%s!", TXT_QUAD_LASERS);
  363.                 update_laser_weapon_info();
  364.         }
  365. };
  366.  
  367. }
  368.  
  369. static int player_has_powerup(player_info &player_info, const char *const desc_have)
  370. {
  371.         HUD_init_message(HM_DEFAULT | HM_REDUNDANT | HM_MAYDUPL, "%s %s!", TXT_ALREADY_HAVE, desc_have);
  372.         return (Game_mode & GM_MULTI) ? 0 : pick_up_energy(player_info);
  373. }
  374.  
  375. template <PLAYER_FLAG player_flag, typename F>
  376. static int player_hit_powerup(player_info &player_info, const char *const desc_have, const F &&pickup)
  377. {
  378.         auto &powerup_flags = player_info.powerup_flags;
  379.         return (powerup_flags & player_flag)
  380.                 ? player_has_powerup(player_info, desc_have)
  381.                 : (pickup.template pickup<player_flag>(powerup_flags), 1);
  382. }
  383.  
  384. int do_powerup(const vmobjptridx_t obj)
  385. {
  386.         auto &Objects = LevelUniqueObjectState.Objects;
  387.         auto &vcobjptr = Objects.vcptr;
  388.         auto &vmobjptr = Objects.vmptr;
  389.         int used=0;
  390. #if defined(DXX_BUILD_DESCENT_I)
  391.         int vulcan_ammo_to_add_with_cannon;
  392. #endif
  393.         int special_used=0;             //for when hitting vulcan cannon gets vulcan ammo
  394.  
  395.         if (Player_dead_state != player_dead_state::no ||
  396.                 ConsoleObject->type == OBJ_GHOST ||
  397.                 get_local_plrobj().shields < 0)
  398.                 return 0;
  399.  
  400.         if ((obj->ctype.powerup_info.flags & PF_SPAT_BY_PLAYER) && obj->ctype.powerup_info.creation_time>0 && GameTime64<obj->ctype.powerup_info.creation_time+i2f(2))
  401.                 return 0;               //not enough time elapsed
  402.  
  403.         if (Game_mode & GM_MULTI)
  404.         {
  405.                 /*
  406.                  * The fact: Collecting a powerup is decided Client-side and due to PING it takes time for other players to know if one collected a powerup actually. This may lead to the case two players collect the same powerup!
  407.                  * The solution: Let us check if someone else is closer to a powerup and if so, do not collect it.
  408.                  * NOTE: Player positions computed by 'shortpos' and PING can still cause a small margin of error.
  409.                  */
  410.                 vms_vector tvec;
  411.                 fix mydist = vm_vec_normalized_dir(tvec, obj->pos, ConsoleObject->pos);
  412.  
  413.                 for (unsigned i = 0; i < MAX_PLAYERS; ++i)
  414.                 {
  415.                         if (i == Player_num)
  416.                                 continue;
  417.                         auto &plr = *vcplayerptr(i);
  418.                         if (plr.connected != CONNECT_PLAYING)
  419.                                 continue;
  420.                         auto &o = *vcobjptr(plr.objnum);
  421.                         if (o.type == OBJ_GHOST)
  422.                                 continue;
  423.                         if (mydist > vm_vec_normalized_dir(tvec, obj->pos, o.pos))
  424.                                 return 0;
  425.                 }
  426.         }
  427.  
  428.         auto &plrobj = get_local_plrobj();
  429.         auto &player_info = plrobj.ctype.player_info;
  430.         auto id = get_powerup_id(obj);
  431.         switch (id)
  432.         {
  433.                 case POW_EXTRA_LIFE:
  434.                         get_local_player().lives++;
  435.                         powerup_basic_str(15, 15, 15, 0, TXT_EXTRA_LIFE);
  436.                         used=1;
  437.                         break;
  438.                 case POW_ENERGY:
  439.                         used = pick_up_energy(player_info);
  440.                         break;
  441.                 case POW_SHIELD_BOOST:
  442.                         {
  443.                                 auto &shields = plrobj.shields;
  444.                         if (shields < MAX_SHIELDS) {
  445.                                 const auto Difficulty_level = GameUniqueState.Difficulty_level;
  446.                                 fix boost = 3*F1_0 + 3*F1_0*(NDL - Difficulty_level);
  447. #if defined(DXX_BUILD_DESCENT_II)
  448.                                 if (Difficulty_level == 0)
  449.                                         boost += boost/2;
  450. #endif
  451.                                 shields += boost;
  452.                                 if (shields > MAX_SHIELDS)
  453.                                         shields = MAX_SHIELDS;
  454.                                 powerup_basic(0, 0, 15, SHIELD_SCORE, "%s %s %d", TXT_SHIELD, TXT_BOOSTED_TO, f2ir(shields));
  455.                                 used=1;
  456.                         } else
  457.                                 HUD_init_message(HM_DEFAULT|HM_REDUNDANT|HM_MAYDUPL, TXT_MAXED_OUT,TXT_SHIELD);
  458.                         break;
  459.                         }
  460.                 case POW_LASER:
  461.                         if (player_info.laser_level >= MAX_LASER_LEVEL) {
  462. #if defined(DXX_BUILD_DESCENT_I)
  463.                                 player_info.laser_level = MAX_LASER_LEVEL;
  464. #endif
  465.                                 HUD_init_message(HM_DEFAULT|HM_REDUNDANT|HM_MAYDUPL, TXT_MAXED_OUT,TXT_LASER);
  466.                         } else {
  467.                                 if (Newdemo_state == ND_STATE_RECORDING)
  468.                                         newdemo_record_laser_level(player_info.laser_level, player_info.laser_level + 1);
  469.                                 ++ player_info.laser_level;
  470.                                 powerup_basic(10, 0, 10, LASER_SCORE, "%s %s %d",TXT_LASER,TXT_BOOSTED_TO, player_info.laser_level+1);
  471.                                 pick_up_primary(player_info, primary_weapon_index_t::LASER_INDEX);
  472.                                 used=1;
  473.                         }
  474.                         if (!used && !(Game_mode & GM_MULTI) )
  475.                                 used = pick_up_energy(player_info);
  476.                         break;
  477.                 case POW_MISSILE_1:
  478.                         used=pick_up_secondary(player_info, CONCUSSION_INDEX, 1);
  479.                         break;
  480.                 case POW_MISSILE_4:
  481.                         used=pick_up_secondary(player_info, CONCUSSION_INDEX, 4);
  482.                         break;
  483.  
  484.                 case POW_KEY_BLUE:
  485.                         used = pick_up_key(0, 0, 15, player_info.powerup_flags, PLAYER_FLAGS_BLUE_KEY, TXT_BLUE, id);
  486.                         break;
  487.                 case POW_KEY_RED:
  488.                         used = pick_up_key(15, 0, 0, player_info.powerup_flags, PLAYER_FLAGS_RED_KEY, TXT_RED, id);
  489.                         break;
  490.                 case POW_KEY_GOLD:
  491.                         used = pick_up_key(15, 15, 7, player_info.powerup_flags, PLAYER_FLAGS_GOLD_KEY, TXT_YELLOW, id);
  492.                         break;
  493.                 case POW_QUAD_FIRE:
  494.                         used = player_hit_powerup<PLAYER_FLAGS_QUAD_LASERS>(player_info, TXT_QUAD_LASERS, player_hit_quadlaser_powerup());
  495.                         break;
  496.  
  497.                 case    POW_VULCAN_WEAPON:
  498. #if defined(DXX_BUILD_DESCENT_I)
  499.                         if ((used = pick_up_primary(player_info, primary_weapon_index_t::VULCAN_INDEX)) != 0) {
  500.                                 vulcan_ammo_to_add_with_cannon = obj->ctype.powerup_info.count;
  501.                                 if (vulcan_ammo_to_add_with_cannon < VULCAN_WEAPON_AMMO_AMOUNT) vulcan_ammo_to_add_with_cannon = VULCAN_WEAPON_AMMO_AMOUNT;
  502.                                 pick_up_vulcan_ammo(player_info, vulcan_ammo_to_add_with_cannon);
  503.                         }
  504.  
  505. //added/edited 8/3/98 by Victor Rachels to fix vulcan multi bug
  506. //check if multi, if so, pick up ammo w/o using, set ammo left. else, normal
  507.  
  508. //killed 8/27/98 by Victor Rachels to fix vulcan ammo multiplying.  new way
  509. // is by spewing the current held ammo when dead.
  510. //-killed                        if (!used && (Game_mode & GM_MULTI))
  511. //-killed                        {
  512. //-killed                         int tempcount;                          
  513. //-killed                           tempcount=Players[Player_num].primary_ammo[VULCAN_INDEX];
  514. //-killed                            if (pick_up_ammo(CLASS_PRIMARY, VULCAN_INDEX, obj->ctype.powerup_info.count))
  515. //-killed                             obj->ctype.powerup_info.count -= Players[Player_num].primary_ammo[VULCAN_INDEX]-tempcount;
  516. //-killed                        }
  517. //end kill - Victor Rachels
  518.  
  519.                         if (!used && !(Game_mode & GM_MULTI) )
  520. //end addition/edit - Victor Rachels
  521.                                 used = pick_up_vulcan_ammo(player_info);
  522.                         break;
  523. #elif defined(DXX_BUILD_DESCENT_II)
  524.                 case    POW_GAUSS_WEAPON: {
  525.                         int ammo = obj->ctype.powerup_info.count;
  526.  
  527.                         used = pick_up_primary(player_info, (get_powerup_id(obj) == POW_VULCAN_WEAPON)
  528.                                 ? primary_weapon_index_t::VULCAN_INDEX
  529.                                 : primary_weapon_index_t::GAUSS_INDEX
  530.                         );
  531.  
  532.                         //didn't get the weapon (because we already have it), but
  533.                         //maybe snag some of the ammo.  if single-player, grab all the ammo
  534.                         //and remove the powerup.  If multi-player take ammo in excess of
  535.                         //the amount in a powerup, and leave the rest.
  536.                         if (! used)
  537.                                 if ((Game_mode & GM_MULTI) )
  538.                                         ammo -= VULCAN_AMMO_AMOUNT;     //don't let take all ammo
  539.  
  540.                         if (ammo > 0) {
  541.                                 int ammo_used;
  542.                                 ammo_used = pick_up_vulcan_ammo(player_info, ammo);
  543.                                 obj->ctype.powerup_info.count -= ammo_used;
  544.                                 if (!used && ammo_used) {
  545.                                         powerup_basic(7, 14, 21, VULCAN_AMMO_SCORE, "%s!", TXT_VULCAN_AMMO);
  546.                                         special_used = 1;
  547.                                         id = POW_VULCAN_AMMO;           //set new id for making sound at end of this function
  548.                                         if (obj->ctype.powerup_info.count == 0)
  549.                                                 used = 1;               //say used if all ammo taken
  550.                                         if (Game_mode & GM_MULTI)
  551.                                                 multi_send_vulcan_weapon_ammo_adjust(obj); // let other players know how much ammo we took.
  552.                                 }
  553.                         }
  554.  
  555.                         break;
  556.                 }
  557. #endif
  558.  
  559.                 case    POW_SPREADFIRE_WEAPON:
  560.                         used = pick_up_primary_or_energy(player_info, primary_weapon_index_t::SPREADFIRE_INDEX);
  561.                         break;
  562.                 case    POW_PLASMA_WEAPON:
  563.                         used = pick_up_primary_or_energy(player_info, primary_weapon_index_t::PLASMA_INDEX);
  564.                         break;
  565.                 case    POW_FUSION_WEAPON:
  566.                         used = pick_up_primary_or_energy(player_info, primary_weapon_index_t::FUSION_INDEX);
  567.                         break;
  568.  
  569. #if defined(DXX_BUILD_DESCENT_II)
  570.                 case    POW_HELIX_WEAPON:
  571.                         used = pick_up_primary_or_energy(player_info, primary_weapon_index_t::HELIX_INDEX);
  572.                         break;
  573.  
  574.                 case    POW_PHOENIX_WEAPON:
  575.                         used = pick_up_primary_or_energy(player_info, primary_weapon_index_t::PHOENIX_INDEX);
  576.                         break;
  577.  
  578.                 case    POW_OMEGA_WEAPON:
  579.                         used = pick_up_primary(player_info, primary_weapon_index_t::OMEGA_INDEX);
  580.                         if (used)
  581.                                 player_info.Omega_charge = obj->ctype.powerup_info.count;
  582.                         if (!used && !(Game_mode & GM_MULTI) )
  583.                                 used = pick_up_energy(player_info);
  584.                         break;
  585. #endif
  586.  
  587.                 case    POW_PROXIMITY_WEAPON:
  588.                         used=pick_up_secondary(player_info, PROXIMITY_INDEX, 4);
  589.                         break;
  590.                 case    POW_SMARTBOMB_WEAPON:
  591.                         used=pick_up_secondary(player_info, SMART_INDEX, 1);
  592.                         break;
  593.                 case    POW_MEGA_WEAPON:
  594.                         used=pick_up_secondary(player_info, MEGA_INDEX, 1);
  595.                         break;
  596. #if defined(DXX_BUILD_DESCENT_II)
  597.                 case    POW_SMISSILE1_1:
  598.                         used=pick_up_secondary(player_info, SMISSILE1_INDEX, 1);
  599.                         break;
  600.                 case    POW_SMISSILE1_4:
  601.                         used=pick_up_secondary(player_info, SMISSILE1_INDEX, 4);
  602.                         break;
  603.                 case    POW_GUIDED_MISSILE_1:
  604.                         used=pick_up_secondary(player_info, GUIDED_INDEX, 1);
  605.                         break;
  606.                 case    POW_GUIDED_MISSILE_4:
  607.                         used=pick_up_secondary(player_info, GUIDED_INDEX, 4);
  608.                         break;
  609.                 case    POW_SMART_MINE:
  610.                         used=pick_up_secondary(player_info, SMART_MINE_INDEX, 4);
  611.                         break;
  612.                 case    POW_MERCURY_MISSILE_1:
  613.                         used=pick_up_secondary(player_info, SMISSILE4_INDEX, 1);
  614.                         break;
  615.                 case    POW_MERCURY_MISSILE_4:
  616.                         used=pick_up_secondary(player_info, SMISSILE4_INDEX, 4);
  617.                         break;
  618.                 case    POW_EARTHSHAKER_MISSILE:
  619.                         used=pick_up_secondary(player_info, SMISSILE5_INDEX, 1);
  620.                         break;
  621. #endif
  622.                 case    POW_VULCAN_AMMO:
  623.                         used = pick_up_vulcan_ammo(player_info);
  624. #if defined(DXX_BUILD_DESCENT_I)
  625.                         if (!used && !(Game_mode & GM_MULTI) )
  626.                                 used = pick_up_vulcan_ammo(player_info);
  627. #endif
  628.                         break;
  629.                 case    POW_HOMING_AMMO_1:
  630.                         used=pick_up_secondary(player_info, HOMING_INDEX, 1);
  631.                         break;
  632.                 case    POW_HOMING_AMMO_4:
  633.                         used=pick_up_secondary(player_info, HOMING_INDEX, 4);
  634.                         break;
  635.                 case    POW_CLOAK:
  636.                         if (player_info.powerup_flags & PLAYER_FLAGS_CLOAKED) {
  637.                                 HUD_init_message(HM_DEFAULT|HM_REDUNDANT|HM_MAYDUPL, "%s %s!",TXT_ALREADY_ARE,TXT_CLOAKED);
  638.                                 break;
  639.                         } else {
  640.                                 player_info.cloak_time = GameTime64;    //      Not! changed by awareness events (like player fires laser).
  641.                                 player_info.powerup_flags |= PLAYER_FLAGS_CLOAKED;
  642.                                 ai_do_cloak_stuff();
  643.                                 if (Game_mode & GM_MULTI)
  644.                                         multi_send_cloak();
  645.                                 powerup_basic(-10,-10,-10, CLOAK_SCORE, "%s!",TXT_CLOAKING_DEVICE);
  646.                                 used = 1;
  647.                                 break;
  648.                         }
  649.                 case    POW_INVULNERABILITY:
  650.                         {
  651.                                 auto &pl_flags = player_info.powerup_flags;
  652.                                 if (pl_flags & PLAYER_FLAGS_INVULNERABLE) {
  653.                                         if (!player_info.FakingInvul)
  654.                                         {
  655.                                 HUD_init_message(HM_DEFAULT|HM_REDUNDANT|HM_MAYDUPL, "%s %s!",TXT_ALREADY_ARE,TXT_INVULNERABLE);
  656.                                 break;
  657.                                         }
  658.                         }
  659.                                 player_info.FakingInvul = 0;
  660.                                 pl_flags |= PLAYER_FLAGS_INVULNERABLE;
  661.                                 player_info.invulnerable_time = GameTime64;
  662.                                 powerup_basic(7, 14, 21, INVULNERABILITY_SCORE, "%s!",TXT_INVULNERABILITY);
  663.                                 used = 1;
  664.                                 break;
  665.                         }
  666.         #ifndef RELEASE
  667.                 case    POW_MEGAWOW:
  668.                         do_megawow_powerup(50);
  669.                         used = 1;
  670.                         break;
  671.         #endif
  672.  
  673. #if defined(DXX_BUILD_DESCENT_II)
  674.                 case POW_FULL_MAP:
  675.                         used = player_hit_powerup<PLAYER_FLAGS_MAP_ALL>(player_info, "the FULL MAP", player_hit_silent_rb_powerup("FULL MAP!"));
  676.                         break;
  677.  
  678.                 case POW_CONVERTER:
  679.                         used = player_hit_powerup<PLAYER_FLAGS_CONVERTER>(player_info, "the Converter", player_hit_silent_rb_powerup("Energy -> shield converter!"));
  680.                         break;
  681.  
  682.                 case POW_SUPER_LASER:
  683.                         if (player_info.laser_level >= MAX_SUPER_LASER_LEVEL)
  684.                         {
  685.                                 player_info.laser_level = MAX_SUPER_LASER_LEVEL;
  686.                                 HUD_init_message_literal(HM_DEFAULT|HM_REDUNDANT|HM_MAYDUPL, "SUPER LASER MAXED OUT!");
  687.                         } else {
  688.                                 const auto old_level = player_info.laser_level;
  689.  
  690.                                 if (player_info.laser_level <= MAX_LASER_LEVEL)
  691.                                         player_info.laser_level = MAX_LASER_LEVEL;
  692.                                 ++ player_info.laser_level;
  693.                                 if (Newdemo_state == ND_STATE_RECORDING)
  694.                                         newdemo_record_laser_level(old_level, player_info.laser_level);
  695.                                 powerup_basic(10, 0, 10, LASER_SCORE, "Super Boost to Laser level %d", player_info.laser_level + 1);
  696.                                 if (player_info.Primary_weapon != primary_weapon_index_t::LASER_INDEX)
  697.                                         check_to_use_primary_super_laser(player_info);
  698.                                 used=1;
  699.                         }
  700.                         if (!used && !(Game_mode & GM_MULTI) )
  701.                                 used = pick_up_energy(player_info);
  702.                         break;
  703.  
  704.                 case POW_AMMO_RACK:
  705.                         used = player_hit_powerup<PLAYER_FLAGS_AMMO_RACK>(player_info, "the Ammo rack", player_hit_basic_sound_powerup<15, 0, 15, POW_AMMO_RACK>("AMMO RACK!"));
  706.                         break;
  707.  
  708.                 case POW_AFTERBURNER:
  709.                         used = player_hit_powerup<PLAYER_FLAGS_AFTERBURNER>(player_info, "the Afterburner", player_hit_afterburner_powerup("AFTERBURNER!"));
  710.                         break;
  711.  
  712.                 case POW_HEADLIGHT:
  713.                         used = player_hit_powerup<PLAYER_FLAGS_HEADLIGHT>(player_info, "the Headlight boost", player_hit_headlight_powerup());
  714.                         break;
  715.  
  716.                 case POW_FLAG_BLUE:
  717.                         used = player_hit_flag_powerup<TEAM_RED>(player_info, "BLUE FLAG!");
  718.                    break;
  719.  
  720.                 case POW_HOARD_ORB:
  721.                         if (game_mode_hoard())                 
  722.                         {
  723.                                 auto &proximity = player_info.hoard.orbs;
  724.                                 if (proximity < player_info.max_hoard_orbs)
  725.                                 {
  726.                                         ++ proximity;
  727.                                         powerup_basic(15, 0, 15, 0, "Orb!!!");
  728.                                         player_info.powerup_flags |= PLAYER_FLAGS_FLAG;
  729.                                         used=1;
  730.                                         multi_send_got_orb (Player_num);
  731.                                 }
  732.                         }
  733.                   break;       
  734.  
  735.                 case POW_FLAG_RED:
  736.                         used = player_hit_flag_powerup<TEAM_BLUE>(player_info, "RED FLAG!");
  737.                    break;
  738.  
  739. //              case POW_HOARD_ORB:
  740.  
  741. #endif
  742.                 default:
  743.                         break;
  744.                 }
  745.  
  746. //always say used, until physics problem (getting stuck on unused powerup)
  747. //is solved.  Note also the break statements above that are commented out
  748. //!!    used=1;
  749.  
  750.         if ((used || special_used) && Powerup_info[id].hit_sound  > -1 ) {
  751.                 multi_digi_play_sample(Powerup_info[id].hit_sound, F1_0);
  752.                 detect_escort_goal_accomplished(obj);
  753.         }
  754.  
  755.         return used;
  756.  
  757. }
  758. }
  759.  
  760. DEFINE_SERIAL_UDT_TO_MESSAGE(powerup_type_info, pti, (pti.vclip_num, pti.hit_sound, pti.size, pti.light));
  761. ASSERT_SERIAL_UDT_MESSAGE_SIZE(powerup_type_info, 16);
  762.  
  763. namespace dcx {
  764.  
  765. void powerup_type_info_read(PHYSFS_File *fp, powerup_type_info &pti)
  766. {
  767.         PHYSFSX_serialize_read(fp, pti);
  768. }
  769.  
  770. void powerup_type_info_write(PHYSFS_File *fp, const powerup_type_info &pti)
  771. {
  772.         PHYSFSX_serialize_write(fp, pti);
  773. }
  774.  
  775. }
  776.