2006-03-20 17:12:09 +00:00
/*
2014-06-01 17:55:23 +00:00
* Portions of this file are copyright Rebirth contributors and licensed as
* described in COPYING . txt .
* Portions of this file are copyright Parallax Software and licensed
* according to the Parallax license below .
* See COPYING . txt for license details .
2006-03-20 17:12:09 +00:00
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ( " PARALLAX " ) . PARALLAX , IN DISTRIBUTING THE CODE TO
END - USERS , AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN , GRANTS A
ROYALTY - FREE , PERPETUAL LICENSE TO SUCH END - USERS FOR USE BY SUCH END - USERS
IN USING , DISPLAYING , AND CREATING DERIVATIVE WORKS THEREOF , SO LONG AS
SUCH USE , DISPLAY OR CREATION IS FOR NON - COMMERCIAL , ROYALTY OR REVENUE
FREE PURPOSES . IN NO EVENT SHALL THE END - USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE - BEARING PURPOSES . THE END - USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE .
COPYRIGHT 1993 - 1999 PARALLAX SOFTWARE CORPORATION . ALL RIGHTS RESERVED .
*/
/*
*
* Functions for weapons . . .
*
*/
2015-03-22 04:16:49 +00:00
# include <stdexcept>
2006-03-20 17:12:09 +00:00
# include <stdlib.h>
# include <stdio.h>
# include <string.h>
2017-06-25 20:46:03 +00:00
# include <type_traits>
2006-03-20 17:12:09 +00:00
2015-04-19 04:18:51 +00:00
# include "hudmsg.h"
2006-03-20 17:12:09 +00:00
# include "game.h"
# include "laser.h"
# include "weapon.h"
# include "player.h"
# include "gauges.h"
2012-07-07 18:35:06 +00:00
# include "dxxerror.h"
2006-03-20 17:12:09 +00:00
# include "sounds.h"
# include "text.h"
# include "powerup.h"
# include "fireball.h"
# include "newdemo.h"
# include "multi.h"
2014-11-23 04:36:58 +00:00
# include "object.h"
2015-01-29 04:27:36 +00:00
# include "segment.h"
2006-03-20 17:12:09 +00:00
# include "newmenu.h"
2012-11-11 00:14:30 +00:00
# include "gamemine.h"
2006-03-20 17:12:09 +00:00
# include "ai.h"
# include "args.h"
2008-04-13 00:28:36 +00:00
# include "playsave.h"
2014-09-27 22:46:25 +00:00
# include "physfs-serial.h"
2006-03-20 17:12:09 +00:00
2014-09-06 04:07:45 +00:00
# include "compiler-range_for.h"
2020-12-20 20:39:07 +00:00
# include "d_enumerate.h"
2020-08-10 03:45:14 +00:00
# include "d_levelstate.h"
2014-09-27 17:55:46 +00:00
# include "partial_range.h"
2014-09-06 04:07:45 +00:00
2006-03-20 17:12:09 +00:00
// Note, only Vulcan cannon requires ammo.
// NOTE: Now Vulcan and Gauss require ammo. -5/3/95 Yuan
//ubyte Default_primary_ammo_level[MAX_PRIMARY_WEAPONS] = {255, 0, 255, 255, 255};
//ubyte Default_secondary_ammo_level[MAX_SECONDARY_WEAPONS] = {3, 0, 0, 0, 0};
// Convert primary weapons to indices in Weapon_info array.
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2016-01-09 16:38:15 +00:00
namespace dsx {
2020-05-02 21:18:42 +00:00
const std : : array < weapon_id_type , MAX_PRIMARY_WEAPONS > Primary_weapon_to_weapon_info { {
2017-02-19 19:33:45 +00:00
weapon_id_type : : LASER_ID , weapon_id_type : : VULCAN_ID , weapon_id_type : : CHEAP_SPREADFIRE_ID , weapon_id_type : : PLASMA_ID , weapon_id_type : : FUSION_ID
2016-08-28 22:41:48 +00:00
} } ;
2020-05-02 21:18:42 +00:00
const std : : array < weapon_id_type , MAX_SECONDARY_WEAPONS > Secondary_weapon_to_weapon_info { { weapon_id_type : : CONCUSSION_ID , weapon_id_type : : HOMING_ID , weapon_id_type : : PROXIMITY_ID , weapon_id_type : : SMART_ID , weapon_id_type : : MEGA_ID } } ;
2013-08-09 15:21:03 +00:00
//for each Secondary weapon, which gun it fires out of
2020-05-02 21:18:42 +00:00
const std : : array < ubyte , MAX_SECONDARY_WEAPONS > Secondary_weapon_to_gun_num { { 4 , 4 , 7 , 7 , 7 } } ;
2016-01-09 16:38:15 +00:00
}
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2015-04-19 04:18:51 +00:00
# include "fvi.h"
2016-01-09 16:38:15 +00:00
namespace dsx {
2020-05-02 21:18:42 +00:00
const std : : array < weapon_id_type , MAX_PRIMARY_WEAPONS > Primary_weapon_to_weapon_info { {
2015-12-03 03:26:49 +00:00
weapon_id_type : : LASER_ID , weapon_id_type : : VULCAN_ID , weapon_id_type : : SPREADFIRE_ID , weapon_id_type : : PLASMA_ID , weapon_id_type : : FUSION_ID ,
weapon_id_type : : SUPER_LASER_ID , weapon_id_type : : GAUSS_ID , weapon_id_type : : HELIX_ID , weapon_id_type : : PHOENIX_ID , weapon_id_type : : OMEGA_ID
2015-04-02 02:36:52 +00:00
} } ;
2020-05-02 21:18:42 +00:00
const std : : array < weapon_id_type , MAX_SECONDARY_WEAPONS > Secondary_weapon_to_weapon_info { {
2015-12-03 03:26:49 +00:00
weapon_id_type : : CONCUSSION_ID , weapon_id_type : : HOMING_ID , weapon_id_type : : PROXIMITY_ID , weapon_id_type : : SMART_ID , weapon_id_type : : MEGA_ID ,
weapon_id_type : : FLASH_ID , weapon_id_type : : GUIDEDMISS_ID , weapon_id_type : : SUPERPROX_ID , weapon_id_type : : MERCURY_ID , weapon_id_type : : EARTHSHAKER_ID
2015-04-02 02:36:52 +00:00
} } ;
2006-03-20 17:12:09 +00:00
//for each Secondary weapon, which gun it fires out of
2020-05-02 21:18:42 +00:00
const std : : array < ubyte , MAX_SECONDARY_WEAPONS > Secondary_weapon_to_gun_num { { 4 , 4 , 7 , 7 , 7 , 4 , 4 , 7 , 4 , 7 } } ;
2016-01-09 16:38:15 +00:00
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2016-01-09 16:38:15 +00:00
namespace dsx {
2020-05-02 21:18:42 +00:00
const std : : array < uint8_t , MAX_SECONDARY_WEAPONS > Secondary_ammo_max { { 20 , 10 , 10 , 5 , 5 ,
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
20 , 20 , 15 , 10 , 10
# endif
2015-04-02 02:36:52 +00:00
} } ;
2006-03-20 17:12:09 +00:00
//for each primary weapon, what kind of powerup gives weapon
2021-01-17 22:23:22 +00:00
const enumerated_array < powerup_type_t , MAX_PRIMARY_WEAPONS , primary_weapon_index_t > Primary_weapon_to_powerup { {
{
powerup_type_t : : POW_LASER ,
powerup_type_t : : POW_VULCAN_WEAPON ,
powerup_type_t : : POW_SPREADFIRE_WEAPON ,
powerup_type_t : : POW_PLASMA_WEAPON ,
powerup_type_t : : POW_FUSION_WEAPON ,
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2021-01-17 22:23:22 +00:00
powerup_type_t : : POW_LASER ,
powerup_type_t : : POW_GAUSS_WEAPON ,
powerup_type_t : : POW_HELIX_WEAPON ,
powerup_type_t : : POW_PHOENIX_WEAPON ,
powerup_type_t : : POW_OMEGA_WEAPON ,
2013-03-03 01:03:33 +00:00
# endif
2021-01-17 22:23:22 +00:00
}
2015-04-02 02:36:52 +00:00
} } ;
2006-03-20 17:12:09 +00:00
//for each Secondary weapon, what kind of powerup gives weapon
2020-05-02 21:18:42 +00:00
const std : : array < powerup_type_t , MAX_SECONDARY_WEAPONS > Secondary_weapon_to_powerup { { POW_MISSILE_1 , POW_HOMING_AMMO_1 , POW_PROXIMITY_WEAPON , POW_SMARTBOMB_WEAPON , POW_MEGA_WEAPON ,
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
POW_SMISSILE1_1 , POW_GUIDED_MISSILE_1 , POW_SMART_MINE , POW_MERCURY_MISSILE_1 , POW_EARTHSHAKER_MISSILE
# endif
2015-04-02 02:36:52 +00:00
} } ;
2006-03-20 17:12:09 +00:00
2014-09-27 17:55:46 +00:00
weapon_info_array Weapon_info ;
2016-01-09 16:38:15 +00:00
}
namespace dcx {
2014-09-20 23:47:27 +00:00
unsigned N_weapon_types ;
2020-12-20 20:39:07 +00:00
namespace {
template < typename cycle_weapon_state >
struct weapon_reorder_menu_items
{
std : : array < newmenu_item , cycle_weapon_state : : max_weapons + 1 > menu_items ;
weapon_reorder_menu_items ( ) ;
} ;
template < typename cycle_weapon_state >
struct weapon_reorder_menu : weapon_reorder_menu_items < cycle_weapon_state > , reorder_newmenu
{
using weapon_reorder_menu_items < cycle_weapon_state > : : menu_items ;
weapon_reorder_menu ( grs_canvas & src ) :
reorder_newmenu ( menu_title { cycle_weapon_state : : reorder_title } , menu_subtitle { " Shift+Up/Down arrow to move item " } , menu_filename { nullptr } , tiny_mode_flag : : normal , tab_processing_flag : : ignore , adjusted_citem : : create ( menu_items , 0 ) , src )
{
}
virtual int subfunction_handler ( const d_event & event ) override ;
} ;
template < typename cycle_weapon_state >
weapon_reorder_menu_items < cycle_weapon_state > : : weapon_reorder_menu_items ( )
{
for ( auto & & [ mi , i ] : enumerate ( menu_items ) )
{
const auto o = cycle_weapon_state : : get_weapon_by_order_slot ( i ) ;
mi . value = o ;
nm_set_item_menu ( mi , cycle_weapon_state : : get_weapon_name ( o ) ) ;
}
}
template < typename cycle_weapon_state >
int weapon_reorder_menu < cycle_weapon_state > : : subfunction_handler ( const d_event & event )
{
switch ( event . type )
{
case EVENT_KEY_COMMAND :
event_key_command ( event ) ;
break ;
case EVENT_WINDOW_CLOSE :
for ( auto & & [ mi , i ] : enumerate ( menu_items ) )
cycle_weapon_state : : get_weapon_by_order_slot ( i ) = mi . value ;
break ;
default :
break ;
}
return 0 ;
}
}
2016-01-09 16:38:15 +00:00
}
2006-03-20 17:12:09 +00:00
// autoselect ordering
2016-01-09 16:38:15 +00:00
namespace dsx {
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2020-05-02 21:18:42 +00:00
constexpr std : : array < uint8_t , MAX_PRIMARY_WEAPONS + 1 > DefaultPrimaryOrder { { 4 , 3 , 2 , 1 , 0 , 255 } } ;
constexpr std : : array < uint8_t , MAX_SECONDARY_WEAPONS + 1 > DefaultSecondaryOrder { { 4 , 3 , 1 , 0 , 255 , 2 } } ;
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2020-05-02 21:18:42 +00:00
constexpr std : : array < uint8_t , MAX_PRIMARY_WEAPONS + 1 > DefaultPrimaryOrder = { { 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 , 0 , 255 } } ;
constexpr std : : array < uint8_t , MAX_SECONDARY_WEAPONS + 1 > DefaultSecondaryOrder = { { 9 , 8 , 4 , 3 , 1 , 5 , 0 , 255 , 7 , 6 , 2 } } ;
2006-03-20 17:12:09 +00:00
2013-03-03 01:03:33 +00:00
//flags whether the last time we use this weapon, it was the 'super' version
# endif
2006-03-20 17:12:09 +00:00
2016-08-28 22:41:48 +00:00
static unsigned get_mapped_weapon_index ( const player_info & player_info , const primary_weapon_index_t weapon_index )
2015-09-19 23:04:36 +00:00
{
2016-08-28 22:41:48 +00:00
# if defined(DXX_BUILD_DESCENT_I)
( void ) player_info ;
# elif defined(DXX_BUILD_DESCENT_II)
if ( weapon_index = = primary_weapon_index_t : : LASER_INDEX & & player_info . laser_level > MAX_LASER_LEVEL )
2015-10-18 21:01:18 +00:00
return primary_weapon_index_t : : SUPER_LASER_INDEX ;
2015-09-19 23:04:36 +00:00
# endif
return weapon_index ;
}
2016-08-25 04:05:32 +00:00
}
2015-09-19 23:04:36 +00:00
2006-03-20 17:12:09 +00:00
// ; (0) Laser Level 1
// ; (1) Laser Level 2
// ; (2) Laser Level 3
// ; (3) Laser Level 4
// ; (4) Unknown Use
// ; (5) Josh Blobs
// ; (6) Unknown Use
// ; (7) Unknown Use
// ; (8) ---------- Concussion Missile ----------
// ; (9) ---------- Flare ----------
// ; (10) ---------- Blue laser that blue guy shoots -----------
// ; (11) ---------- Vulcan Cannon ----------
// ; (12) ---------- Spreadfire Cannon ----------
// ; (13) ---------- Plasma Cannon ----------
// ; (14) ---------- Fusion Cannon ----------
// ; (15) ---------- Homing Missile ----------
// ; (16) ---------- Proximity Bomb ----------
// ; (17) ---------- Smart Missile ----------
// ; (18) ---------- Mega Missile ----------
// ; (19) ---------- Children of the PLAYER'S Smart Missile ----------
// ; (20) ---------- Bad Guy Spreadfire Laser ----------
// ; (21) ---------- SuperMech Homing Missile ----------
// ; (22) ---------- Regular Mech's missile -----------
// ; (23) ---------- Silent Spreadfire Laser ----------
// ; (24) ---------- Red laser that baby spiders shoot -----------
// ; (25) ---------- Green laser that rifleman shoots -----------
// ; (26) ---------- Plasma gun that 'plasguy' fires ------------
// ; (27) ---------- Blobs fired by Red Spiders -----------
// ; (28) ---------- Final Boss's Mega Missile ----------
// ; (29) ---------- Children of the ROBOT'S Smart Missile ----------
// ; (30) Laser Level 5
// ; (31) Laser Level 6
// ; (32) ---------- Super Vulcan Cannon ----------
// ; (33) ---------- Super Spreadfire Cannon ----------
// ; (34) ---------- Super Plasma Cannon ----------
// ; (35) ---------- Super Fusion Cannon ----------
// ------------------------------------------------------------------------------------
// Return:
// Bits set:
// HAS_WEAPON_FLAG
// HAS_ENERGY_FLAG
2010-06-17 09:45:05 +00:00
// HAS_AMMO_FLAG
2006-03-20 17:12:09 +00:00
// See weapon.h for bit values
2016-08-25 04:05:32 +00:00
namespace dsx {
2016-10-02 00:34:43 +00:00
has_weapon_result player_has_primary_weapon ( const player_info & player_info , int weapon_num )
2006-03-20 17:12:09 +00:00
{
int return_value = 0 ;
// Hack! If energy goes negative, you can't fire a weapon that doesn't require energy.
// But energy should not go negative (but it does), so find out why it does!
2016-09-11 18:49:13 +00:00
auto & energy = player_info . energy ;
2006-03-20 17:12:09 +00:00
2015-09-26 21:17:12 +00:00
const auto weapon_index = Primary_weapon_to_weapon_info [ weapon_num ] ;
2006-03-20 17:12:09 +00:00
2015-11-07 21:55:59 +00:00
if ( player_info . primary_weapon_flags & HAS_PRIMARY_FLAG ( weapon_num ) )
2015-04-19 04:18:50 +00:00
return_value | = has_weapon_result : : has_weapon_flag ;
2006-03-20 17:12:09 +00:00
2010-06-17 09:45:05 +00:00
// Special case: Gauss cannon uses vulcan ammo.
2013-09-20 23:12:51 +00:00
if ( weapon_index_uses_vulcan_ammo ( weapon_num ) ) {
2016-10-02 00:34:40 +00:00
if ( Weapon_info [ weapon_index ] . ammo_usage < = player_info . vulcan_ammo )
2015-04-19 04:18:50 +00:00
return_value | = has_weapon_result : : has_ammo_flag ;
2013-09-20 23:12:54 +00:00
}
2015-04-26 20:15:51 +00:00
/* Hack to work around check in do_primary_weapon_select */
2013-09-20 23:12:54 +00:00
else
2015-04-19 04:18:50 +00:00
return_value | = has_weapon_result : : has_ammo_flag ;
2013-09-20 23:12:54 +00:00
2013-09-21 18:01:45 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2013-03-03 01:03:33 +00:00
//added on 1/21/99 by Victor Rachels... yet another hack
//fusion has 0 energy usage, HAS_ENERGY_FLAG was always true
2015-10-18 21:01:18 +00:00
if ( weapon_num = = primary_weapon_index_t : : FUSION_INDEX )
2013-03-03 01:03:33 +00:00
{
2016-07-03 00:54:16 +00:00
if ( energy > = F1_0 * 2 )
2015-04-19 04:18:50 +00:00
return_value | = has_weapon_result : : has_energy_flag ;
2013-03-03 01:03:33 +00:00
}
# elif defined(DXX_BUILD_DESCENT_II)
2015-10-18 21:01:18 +00:00
if ( weapon_num = = primary_weapon_index_t : : OMEGA_INDEX ) { // Hack: Make sure player has energy to omega
2016-10-02 00:34:43 +00:00
if ( energy > 0 | | player_info . Omega_charge )
2015-04-19 04:18:50 +00:00
return_value | = has_weapon_result : : has_energy_flag ;
2013-03-03 01:03:33 +00:00
}
# endif
else
2016-10-02 00:34:43 +00:00
{
const auto energy_usage = Weapon_info [ weapon_index ] . energy_usage ;
/* The test for `energy_usage <= 0` should not be needed.
* However , a Parallax comment suggests that players
* sometimes get negative energy . Use this test in
* preference to coercing negative player energy to zero .
*/
if ( energy_usage < = 0 | | energy_usage < = energy )
2015-04-19 04:18:50 +00:00
return_value | = has_weapon_result : : has_energy_flag ;
2016-10-02 00:34:43 +00:00
}
2015-04-19 04:18:50 +00:00
return return_value ;
}
2016-10-02 00:34:49 +00:00
has_weapon_result player_has_secondary_weapon ( const player_info & player_info , const secondary_weapon_index_t weapon_num )
2015-04-19 04:18:50 +00:00
{
int return_value = 0 ;
2016-10-02 00:34:40 +00:00
const auto secondary_ammo = player_info . secondary_ammo [ weapon_num ] ;
2015-09-26 21:17:12 +00:00
const auto weapon_index = Secondary_weapon_to_weapon_info [ weapon_num ] ;
2015-11-19 03:23:35 +00:00
if ( secondary_ammo & & Weapon_info [ weapon_index ] . ammo_usage < = secondary_ammo )
2015-11-06 03:51:10 +00:00
return_value = has_weapon_result : : has_weapon_flag | has_weapon_result : : has_energy_flag | has_weapon_result : : has_ammo_flag ;
2006-03-20 17:12:09 +00:00
return return_value ;
}
void InitWeaponOrdering ( )
{
// short routine to setup default weapon priorities for new pilots
2014-08-26 02:59:01 +00:00
PlayerCfg . PrimaryOrder = DefaultPrimaryOrder ;
PlayerCfg . SecondaryOrder = DefaultSecondaryOrder ;
2010-06-17 09:45:05 +00:00
}
2006-03-20 17:12:09 +00:00
2015-10-30 02:52:54 +00:00
namespace {
2020-10-12 03:28:26 +00:00
static uint_fast32_t POrderList ( uint_fast32_t num ) ;
static uint_fast32_t SOrderList ( uint_fast32_t num ) ;
2015-10-30 02:52:54 +00:00
class cycle_weapon_state
2006-03-20 17:12:09 +00:00
{
2015-10-30 02:52:54 +00:00
public :
2017-06-25 20:46:03 +00:00
static constexpr std : : integral_constant < uint8_t , 255 > cycle_never_autoselect_below { } ;
2016-01-17 17:36:48 +00:00
static constexpr char DXX_WEAPON_TEXT_NEVER_AUTOSELECT [ ] = " --- Never autoselect below --- " ;
__attribute_cold
__attribute_noreturn
static void report_runtime_error ( const char * ) ;
2015-10-30 02:52:54 +00:00
} ;
2012-05-10 12:39:26 +00:00
2015-10-30 02:52:54 +00:00
class cycle_primary_state : public cycle_weapon_state
{
2016-10-02 00:34:39 +00:00
using weapon_index_type = primary_weapon_index_t ;
2016-10-02 00:34:44 +00:00
player_info & pl_info ;
2015-10-30 02:52:54 +00:00
public :
2016-10-02 00:34:44 +00:00
cycle_primary_state ( player_info & p ) :
pl_info ( p )
{
}
2017-06-25 20:46:03 +00:00
static constexpr std : : integral_constant < uint_fast32_t , MAX_PRIMARY_WEAPONS > max_weapons { } ;
2016-01-17 17:36:48 +00:00
static constexpr char reorder_title [ ] = " Reorder Primary " ;
static constexpr char error_weapon_list_corrupt [ ] = " primary weapon list corrupt " ;
2015-10-30 02:52:54 +00:00
static uint_fast32_t get_cycle_position ( uint_fast32_t i )
{
return POrderList ( i ) ;
}
2016-01-17 17:36:48 +00:00
static player_config : : primary_weapon_order : : reference get_weapon_by_order_slot ( player_config : : primary_weapon_order : : size_type cur_order_slot )
2015-11-07 21:56:00 +00:00
{
return PlayerCfg . PrimaryOrder [ cur_order_slot ] ;
}
bool maybe_select_weapon_by_order_slot ( uint_fast32_t cur_order_slot ) const
{
return maybe_select_weapon_by_type ( get_weapon_by_order_slot ( cur_order_slot ) ) ;
}
2016-10-02 00:34:39 +00:00
bool maybe_select_weapon_by_type ( const uint_fast32_t desired_weapon_idx ) const
2012-05-10 12:39:26 +00:00
{
2016-10-02 00:34:39 +00:00
weapon_index_type desired_weapon = static_cast < weapon_index_type > ( desired_weapon_idx ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2012-05-10 12:39:26 +00:00
// some remapping for SUPER LASER which is not an actual weapon type at all
2016-10-02 00:34:39 +00:00
if ( desired_weapon = = primary_weapon_index_t : : LASER_INDEX )
{
if ( pl_info . laser_level > MAX_LASER_LEVEL )
return false ;
}
else if ( desired_weapon = = primary_weapon_index_t : : SUPER_LASER_INDEX )
2012-05-10 12:39:26 +00:00
{
2015-11-07 21:55:59 +00:00
if ( pl_info . laser_level < = MAX_LASER_LEVEL )
2015-10-30 02:52:54 +00:00
return false ;
2012-05-10 12:39:26 +00:00
else
2015-10-18 21:01:18 +00:00
desired_weapon = primary_weapon_index_t : : LASER_INDEX ;
2012-05-10 12:39:26 +00:00
}
2013-03-03 01:03:33 +00:00
# endif
2016-10-02 00:34:43 +00:00
if ( ! player_has_primary_weapon ( pl_info , desired_weapon ) . has_all ( ) )
2015-10-30 02:52:54 +00:00
return false ;
2016-10-02 00:34:45 +00:00
select_primary_weapon ( pl_info , PRIMARY_WEAPON_NAMES ( desired_weapon ) , desired_weapon , 1 ) ;
2015-10-30 02:52:54 +00:00
return true ;
2012-05-10 12:39:26 +00:00
}
2016-10-02 00:34:45 +00:00
void abandon_auto_select ( )
2015-11-07 21:56:00 +00:00
{
HUD_init_message_literal ( HM_DEFAULT , TXT_NO_PRIMARY ) ;
Prevent sound stacking when all primary weapons are exhausted
ziplantil reported that in Descent 1, if a player has an empty Vulcan
cannon, and no energy, then attempting to fire a weapon would play the
SOUND_ALREADY_SELECTED clip every frame until the player ceased trying
to fire. The problem does not impact Descent 2 since Descent 2 does not
play this sound sample when trying to activate a weapon that is already
active.
If the current weapon is already LASER_INDEX, then
select_primary_weapon(LASER_INDEX) simplifies to:
```
// Pointless, player already using this weapon
newdemo_record_player_weapon(LASER_INDEX)
if (Primary_weapon != LASER_INDEX) {
// skipped, path is false
}
else
{
if (wait_for_rearm) // true for this path
// Bad, plays every frame
digi_play_sample(SOUND_ALREADY_SELECTED);
}
// Pointless, Primary_weapon already is LASER_INDEX
Primary_weapon = LASER_INDEX;
if (weapon_name)
{
// skipped, path is false
}
```
Skipping the call avoids two pointless statements, one bad statement,
and nothing useful. Therefore, the simplest fix for the problem is to
call select_primary_weapon(LASER_INDEX) only if the primary weapon is
not currently LASER_INDEX.
Reported-by: ziplantil <https://github.com/dxx-rebirth/dxx-rebirth/issues/499>
2020-03-28 17:09:28 +00:00
if ( pl_info . Primary_weapon = = primary_weapon_index_t : : LASER_INDEX )
return ;
select_primary_weapon ( pl_info , nullptr , primary_weapon_index_t : : LASER_INDEX , 1 ) ;
2015-11-07 21:56:00 +00:00
}
2016-01-17 17:36:48 +00:00
static const char * get_weapon_name ( uint8_t i )
{
return i = = cycle_never_autoselect_below ? DXX_WEAPON_TEXT_NEVER_AUTOSELECT : PRIMARY_WEAPON_NAMES ( i ) ;
}
2015-10-30 02:52:54 +00:00
} ;
2006-03-20 17:12:09 +00:00
2015-10-30 02:52:54 +00:00
class cycle_secondary_state : public cycle_weapon_state
2006-03-20 17:12:09 +00:00
{
2016-10-02 00:34:39 +00:00
using weapon_index_type = secondary_weapon_index_t ;
2016-10-02 00:34:44 +00:00
player_info & pl_info ;
2015-10-30 02:52:54 +00:00
public :
2016-10-02 00:34:44 +00:00
cycle_secondary_state ( player_info & p ) :
pl_info ( p )
{
}
2017-06-25 20:46:03 +00:00
static constexpr std : : integral_constant < uint_fast32_t , MAX_SECONDARY_WEAPONS > max_weapons { } ;
2016-01-17 17:36:48 +00:00
static constexpr char reorder_title [ ] = " Reorder Secondary " ;
static constexpr char error_weapon_list_corrupt [ ] = " secondary weapon list corrupt " ;
2015-10-30 02:52:54 +00:00
static uint_fast32_t get_cycle_position ( uint_fast32_t i )
{
return SOrderList ( i ) ;
}
2016-01-17 17:36:48 +00:00
static player_config : : secondary_weapon_order : : reference get_weapon_by_order_slot ( player_config : : secondary_weapon_order : : size_type cur_order_slot )
2015-11-07 21:56:00 +00:00
{
return PlayerCfg . SecondaryOrder [ cur_order_slot ] ;
}
2016-10-02 00:34:44 +00:00
bool maybe_select_weapon_by_order_slot ( uint_fast32_t cur_order_slot )
2015-11-07 21:56:00 +00:00
{
return maybe_select_weapon_by_type ( get_weapon_by_order_slot ( cur_order_slot ) ) ;
}
2016-10-02 00:34:44 +00:00
bool maybe_select_weapon_by_type ( const uint_fast32_t desired_weapon_idx )
2015-10-30 02:52:54 +00:00
{
2016-10-02 00:34:39 +00:00
const weapon_index_type desired_weapon = static_cast < weapon_index_type > ( desired_weapon_idx ) ;
2016-10-02 00:34:44 +00:00
if ( ! player_has_secondary_weapon ( pl_info , desired_weapon ) . has_all ( ) )
2015-10-30 02:52:54 +00:00
return false ;
2016-10-02 00:34:46 +00:00
select_secondary_weapon ( pl_info , SECONDARY_WEAPON_NAMES ( desired_weapon ) , desired_weapon , 1 ) ;
2015-10-30 02:52:54 +00:00
return true ;
}
2015-11-07 21:56:00 +00:00
static void abandon_auto_select ( )
{
HUD_init_message_literal ( HM_DEFAULT , " No secondary weapons available! " ) ;
}
2016-01-17 17:36:48 +00:00
static const char * get_weapon_name ( uint8_t i )
{
return i = = cycle_never_autoselect_below ? DXX_WEAPON_TEXT_NEVER_AUTOSELECT : SECONDARY_WEAPON_NAMES ( i ) ;
}
2015-10-30 02:52:54 +00:00
} ;
2016-01-17 17:36:48 +00:00
void cycle_weapon_state : : report_runtime_error ( const char * const p )
{
throw std : : runtime_error ( p ) ;
}
2015-11-06 03:51:10 +00:00
2015-10-30 02:52:54 +00:00
template < typename T >
void CycleWeapon ( T t , const uint_fast32_t effective_weapon )
{
auto cur_order_slot = t . get_cycle_position ( effective_weapon ) ;
const auto autoselect_order_slot = t . get_cycle_position ( t . cycle_never_autoselect_below ) ;
const auto use_restricted_autoselect =
( cur_order_slot < autoselect_order_slot ) & &
( 1 < autoselect_order_slot ) & &
PlayerCfg . CycleAutoselectOnly ;
for ( uint_fast32_t loop = t . max_weapons + 1 ; loop - - ; )
2012-05-10 12:39:26 +00:00
{
cur_order_slot + + ; // next slot
2015-10-30 02:52:54 +00:00
if ( cur_order_slot > = t . max_weapons + 1 ) // loop if necessary
2012-05-10 12:39:26 +00:00
cur_order_slot = 0 ;
2013-04-27 20:48:18 +00:00
if ( cur_order_slot = = autoselect_order_slot ) // what to to with non-autoselect weapons?
2013-04-08 13:04:28 +00:00
{
2013-04-27 20:48:18 +00:00
if ( use_restricted_autoselect )
2013-04-08 13:04:28 +00:00
{
cur_order_slot = 0 ; // loop over or ...
}
else
{
continue ; // continue?
}
}
2015-11-07 21:56:00 +00:00
if ( t . maybe_select_weapon_by_order_slot ( cur_order_slot ) ) // now that is the weapon next to our current one
2012-05-10 12:39:26 +00:00
// select the weapon if we have it
return ;
}
2006-03-20 17:12:09 +00:00
}
2015-10-30 02:52:54 +00:00
}
2016-10-02 00:34:46 +00:00
void CyclePrimary ( player_info & player_info )
2015-10-30 02:52:54 +00:00
{
2016-10-02 00:34:44 +00:00
CycleWeapon ( cycle_primary_state ( player_info ) , get_mapped_weapon_index ( player_info , player_info . Primary_weapon ) ) ;
2015-10-30 02:52:54 +00:00
}
2016-10-02 00:34:46 +00:00
void CycleSecondary ( player_info & player_info )
2015-10-30 02:52:54 +00:00
{
2016-10-02 00:34:44 +00:00
CycleWeapon ( cycle_secondary_state ( player_info ) , player_info . Secondary_weapon ) ;
2015-10-30 02:52:54 +00:00
}
2017-02-19 19:33:44 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2020-10-12 03:28:26 +00:00
namespace {
2017-02-19 19:33:44 +00:00
static inline void set_weapon_last_was_super ( uint8_t & last , const uint8_t mask , const bool is_super )
{
if ( is_super )
last | = mask ;
else
last & = ~ mask ;
}
static inline void set_weapon_last_was_super ( uint8_t & last , const uint_fast32_t weapon_num )
{
const bool is_super = weapon_num > = SUPER_WEAPON ;
set_weapon_last_was_super ( last , 1 < < ( is_super ? weapon_num - SUPER_WEAPON : weapon_num ) , is_super ) ;
}
2020-10-12 03:28:26 +00:00
}
2017-02-19 19:33:44 +00:00
# endif
2016-12-25 00:33:25 +00:00
void set_primary_weapon ( player_info & player_info , const uint_fast32_t weapon_num )
{
if ( Newdemo_state = = ND_STATE_RECORDING )
newdemo_record_player_weapon ( 0 , weapon_num ) ;
player_info . Fusion_charge = 0 ;
player_info . Next_laser_fire_time = 0 ;
player_info . Primary_weapon = static_cast < primary_weapon_index_t > ( weapon_num ) ;
# if defined(DXX_BUILD_DESCENT_II)
//save flag for whether was super version
auto & Primary_last_was_super = player_info . Primary_last_was_super ;
2017-02-19 19:33:44 +00:00
set_weapon_last_was_super ( Primary_last_was_super , weapon_num ) ;
2016-12-25 00:33:25 +00:00
# endif
}
2006-03-20 17:12:09 +00:00
// ------------------------------------------------------------------------------------
//if message flag set, print message saying selected
2016-10-02 00:34:45 +00:00
void select_primary_weapon ( player_info & player_info , const char * const weapon_name , const uint_fast32_t weapon_num , const int wait_for_rearm )
2006-03-20 17:12:09 +00:00
{
if ( Newdemo_state = = ND_STATE_RECORDING )
2015-04-26 20:15:52 +00:00
newdemo_record_player_weapon ( 0 , weapon_num ) ;
2006-03-20 17:12:09 +00:00
2015-04-26 20:15:52 +00:00
{
2016-08-28 22:41:49 +00:00
auto & Primary_weapon = player_info . Primary_weapon ;
2006-03-20 17:12:09 +00:00
if ( Primary_weapon ! = weapon_num ) {
Prevent sound stacking when all primary weapons are exhausted
ziplantil reported that in Descent 1, if a player has an empty Vulcan
cannon, and no energy, then attempting to fire a weapon would play the
SOUND_ALREADY_SELECTED clip every frame until the player ceased trying
to fire. The problem does not impact Descent 2 since Descent 2 does not
play this sound sample when trying to activate a weapon that is already
active.
If the current weapon is already LASER_INDEX, then
select_primary_weapon(LASER_INDEX) simplifies to:
```
// Pointless, player already using this weapon
newdemo_record_player_weapon(LASER_INDEX)
if (Primary_weapon != LASER_INDEX) {
// skipped, path is false
}
else
{
if (wait_for_rearm) // true for this path
// Bad, plays every frame
digi_play_sample(SOUND_ALREADY_SELECTED);
}
// Pointless, Primary_weapon already is LASER_INDEX
Primary_weapon = LASER_INDEX;
if (weapon_name)
{
// skipped, path is false
}
```
Skipping the call avoids two pointless statements, one bad statement,
and nothing useful. Therefore, the simplest fix for the problem is to
call select_primary_weapon(LASER_INDEX) only if the primary weapon is
not currently LASER_INDEX.
Reported-by: ziplantil <https://github.com/dxx-rebirth/dxx-rebirth/issues/499>
2020-03-28 17:09:28 +00:00
Primary_weapon = static_cast < primary_weapon_index_t > ( weapon_num ) ;
2012-03-28 11:49:02 +00:00
# ifndef FUSION_KEEPS_CHARGE
//added 8/6/98 by Victor Rachels to fix fusion charge bug
2016-12-10 17:51:09 +00:00
player_info . Fusion_charge = 0 ;
2012-03-28 11:49:02 +00:00
//end edit - Victor Rachels
# endif
2016-08-28 22:41:47 +00:00
auto & Next_laser_fire_time = player_info . Next_laser_fire_time ;
2015-08-05 02:59:02 +00:00
if ( wait_for_rearm )
2016-07-06 01:54:26 +00:00
{
2015-08-05 02:59:02 +00:00
multi_digi_play_sample_once ( SOUND_GOOD_SELECTION_PRIMARY , F1_0 ) ;
2010-12-22 00:17:59 +00:00
Next_laser_fire_time = GameTime64 + REARM_TIME ;
2016-07-06 01:54:26 +00:00
}
2006-03-20 17:12:09 +00:00
else
Next_laser_fire_time = 0 ;
Prevent sound stacking when all primary weapons are exhausted
ziplantil reported that in Descent 1, if a player has an empty Vulcan
cannon, and no energy, then attempting to fire a weapon would play the
SOUND_ALREADY_SELECTED clip every frame until the player ceased trying
to fire. The problem does not impact Descent 2 since Descent 2 does not
play this sound sample when trying to activate a weapon that is already
active.
If the current weapon is already LASER_INDEX, then
select_primary_weapon(LASER_INDEX) simplifies to:
```
// Pointless, player already using this weapon
newdemo_record_player_weapon(LASER_INDEX)
if (Primary_weapon != LASER_INDEX) {
// skipped, path is false
}
else
{
if (wait_for_rearm) // true for this path
// Bad, plays every frame
digi_play_sample(SOUND_ALREADY_SELECTED);
}
// Pointless, Primary_weapon already is LASER_INDEX
Primary_weapon = LASER_INDEX;
if (weapon_name)
{
// skipped, path is false
}
```
Skipping the call avoids two pointless statements, one bad statement,
and nothing useful. Therefore, the simplest fix for the problem is to
call select_primary_weapon(LASER_INDEX) only if the primary weapon is
not currently LASER_INDEX.
Reported-by: ziplantil <https://github.com/dxx-rebirth/dxx-rebirth/issues/499>
2020-03-28 17:09:28 +00:00
}
else
{
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
Prevent sound stacking when all primary weapons are exhausted
ziplantil reported that in Descent 1, if a player has an empty Vulcan
cannon, and no energy, then attempting to fire a weapon would play the
SOUND_ALREADY_SELECTED clip every frame until the player ceased trying
to fire. The problem does not impact Descent 2 since Descent 2 does not
play this sound sample when trying to activate a weapon that is already
active.
If the current weapon is already LASER_INDEX, then
select_primary_weapon(LASER_INDEX) simplifies to:
```
// Pointless, player already using this weapon
newdemo_record_player_weapon(LASER_INDEX)
if (Primary_weapon != LASER_INDEX) {
// skipped, path is false
}
else
{
if (wait_for_rearm) // true for this path
// Bad, plays every frame
digi_play_sample(SOUND_ALREADY_SELECTED);
}
// Pointless, Primary_weapon already is LASER_INDEX
Primary_weapon = LASER_INDEX;
if (weapon_name)
{
// skipped, path is false
}
```
Skipping the call avoids two pointless statements, one bad statement,
and nothing useful. Therefore, the simplest fix for the problem is to
call select_primary_weapon(LASER_INDEX) only if the primary weapon is
not currently LASER_INDEX.
Reported-by: ziplantil <https://github.com/dxx-rebirth/dxx-rebirth/issues/499>
2020-03-28 17:09:28 +00:00
if ( wait_for_rearm )
/*
* In Descent 1 , requesting a weapon that is already
* armed is pointless , so play a warning sound .
*
* In Descent 2 , the player may be trying to toggle
* between the base and super forms of a weapon , so do
* not generate a warning sound in that case .
*/
digi_play_sample ( SOUND_ALREADY_SELECTED , F1_0 ) ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
//save flag for whether was super version
2017-02-19 19:33:44 +00:00
set_weapon_last_was_super ( player_info . Primary_last_was_super , weapon_num ) ;
2013-03-03 01:03:33 +00:00
# endif
2015-04-26 20:15:52 +00:00
}
if ( weapon_name )
{
# if defined(DXX_BUILD_DESCENT_II)
2015-10-18 21:01:18 +00:00
if ( weapon_num = = primary_weapon_index_t : : LASER_INDEX )
2020-08-24 01:31:28 +00:00
HUD_init_message ( HM_DEFAULT , " %s Level %u %s " , weapon_name , static_cast < unsigned > ( player_info . laser_level ) + 1 , TXT_SELECTED ) ;
2015-04-26 20:15:52 +00:00
else
# endif
HUD_init_message ( HM_DEFAULT , " %s %s " , weapon_name , TXT_SELECTED ) ;
}
}
2006-03-20 17:12:09 +00:00
2016-12-25 00:33:25 +00:00
void set_secondary_weapon_to_concussion ( player_info & player_info )
{
const uint_fast32_t weapon_num = 0 ;
if ( Newdemo_state = = ND_STATE_RECORDING )
newdemo_record_player_weapon ( 1 , weapon_num ) ;
player_info . Next_missile_fire_time = 0 ;
Global_missile_firing_count = 0 ;
player_info . Secondary_weapon = static_cast < secondary_weapon_index_t > ( weapon_num ) ;
# if defined(DXX_BUILD_DESCENT_II)
//save flag for whether was super version
2017-02-19 19:33:45 +00:00
set_weapon_last_was_super ( player_info . Secondary_last_was_super , weapon_num ) ;
2016-12-25 00:33:25 +00:00
# endif
}
2016-10-02 00:34:46 +00:00
void select_secondary_weapon ( player_info & player_info , const char * const weapon_name , const uint_fast32_t weapon_num , const int wait_for_rearm )
2015-04-26 20:15:52 +00:00
{
if ( Newdemo_state = = ND_STATE_RECORDING )
newdemo_record_player_weapon ( 1 , weapon_num ) ;
2006-03-20 17:12:09 +00:00
2015-04-26 20:15:52 +00:00
{
2016-08-28 22:41:48 +00:00
auto & Secondary_weapon = player_info . Secondary_weapon ;
2006-03-20 17:12:09 +00:00
if ( Secondary_weapon ! = weapon_num ) {
2016-10-02 00:34:46 +00:00
auto & Next_missile_fire_time = player_info . Next_missile_fire_time ;
2015-08-05 02:59:02 +00:00
if ( wait_for_rearm )
2016-07-06 01:54:26 +00:00
{
2015-08-05 02:59:02 +00:00
multi_digi_play_sample_once ( SOUND_GOOD_SELECTION_SECONDARY , F1_0 ) ;
2010-12-22 00:17:59 +00:00
Next_missile_fire_time = GameTime64 + REARM_TIME ;
2016-07-06 01:54:26 +00:00
}
2006-03-20 17:12:09 +00:00
else
Next_missile_fire_time = 0 ;
Global_missile_firing_count = 0 ;
} else {
if ( wait_for_rearm )
{
digi_play_sample_once ( SOUND_ALREADY_SELECTED , F1_0 ) ;
}
2010-06-17 09:45:05 +00:00
2006-03-20 17:12:09 +00:00
}
2016-10-02 00:34:43 +00:00
Secondary_weapon = static_cast < secondary_weapon_index_t > ( weapon_num ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
//save flag for whether was super version
2017-02-19 19:33:45 +00:00
set_weapon_last_was_super ( player_info . Secondary_last_was_super , weapon_num ) ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
2015-04-26 20:15:52 +00:00
if ( weapon_name )
2006-03-20 17:12:09 +00:00
{
2015-04-26 20:15:52 +00:00
HUD_init_message ( HM_DEFAULT , " %s %s " , weapon_name , TXT_SELECTED ) ;
2006-03-20 17:12:09 +00:00
}
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2020-10-12 03:28:26 +00:00
namespace {
2015-04-26 20:15:51 +00:00
static bool reject_shareware_weapon_select ( const uint_fast32_t weapon_num , const char * const weapon_name )
{
2013-03-03 01:03:33 +00:00
// do special hud msg. for picking registered weapon in shareware version.
if ( PCSharePig )
if ( weapon_num > = NUM_SHAREWARE_WEAPONS ) {
HUD_init_message ( HM_DEFAULT , " %s %s! " , weapon_name , TXT_NOT_IN_SHAREWARE ) ;
2015-04-26 20:15:51 +00:00
return true ;
2013-03-03 01:03:33 +00:00
}
2015-04-26 20:15:51 +00:00
return false ;
}
2013-03-03 01:03:33 +00:00
2016-10-02 00:34:45 +00:00
static bool reject_unusable_primary_weapon_select ( const player_info & player_info , const uint_fast32_t weapon_num , const char * const weapon_name )
2015-04-26 20:15:51 +00:00
{
2016-10-02 00:34:43 +00:00
const auto weapon_status = player_has_primary_weapon ( player_info , weapon_num ) ;
2015-04-26 20:15:51 +00:00
const char * prefix ;
if ( ! weapon_status . has_weapon ( ) )
prefix = TXT_DONT_HAVE ;
else if ( ! weapon_status . has_ammo ( ) )
prefix = TXT_DONT_HAVE_AMMO ;
else
return false ;
HUD_init_message ( HM_DEFAULT , " %s %s! " , prefix , weapon_name ) ;
return true ;
}
2016-10-02 00:34:49 +00:00
static bool reject_unusable_secondary_weapon_select ( const player_info & player_info , const secondary_weapon_index_t weapon_num , const char * const weapon_name )
2015-04-26 20:15:51 +00:00
{
2016-10-02 00:34:44 +00:00
const auto weapon_status = player_has_secondary_weapon ( player_info , weapon_num ) ;
2015-04-26 20:15:51 +00:00
if ( weapon_status . has_all ( ) )
return false ;
HUD_init_message ( HM_DEFAULT , " %s %s%s " , TXT_HAVE_NO , weapon_name , TXT_SX ) ;
return true ;
}
2020-10-12 03:28:26 +00:00
}
2015-04-26 20:15:51 +00:00
# endif
// ------------------------------------------------------------------------------------
// Select a weapon, primary or secondary.
2016-10-02 00:34:46 +00:00
void do_primary_weapon_select ( player_info & player_info , uint_fast32_t weapon_num )
2015-04-26 20:15:51 +00:00
{
# if defined(DXX_BUILD_DESCENT_I)
//added on 10/9/98 by Victor Rachels to add laser cycle
//end this section addition - Victor Rachels
2015-05-06 02:11:03 +00:00
const auto weapon_name = PRIMARY_WEAPON_NAMES ( weapon_num ) ;
2016-10-02 00:34:45 +00:00
if ( reject_shareware_weapon_select ( weapon_num , weapon_name ) | | reject_unusable_primary_weapon_select ( player_info , weapon_num , weapon_name ) )
2015-04-26 20:15:51 +00:00
{
2015-04-26 20:15:51 +00:00
digi_play_sample ( SOUND_BAD_SELECTION , F1_0 ) ;
return ;
2013-03-03 01:03:33 +00:00
}
2015-04-26 20:15:51 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
has_weapon_result weapon_status ;
2016-08-28 22:41:49 +00:00
auto & Primary_weapon = player_info . Primary_weapon ;
2016-08-06 19:55:23 +00:00
const auto current = Primary_weapon . get_active ( ) ;
2017-02-19 19:33:44 +00:00
const auto last_was_super = player_info . Primary_last_was_super & ( 1 < < weapon_num ) ;
2015-09-26 21:17:12 +00:00
const auto has_flag = weapon_status . has_weapon_flag ;
2015-04-26 20:15:51 +00:00
if ( current = = weapon_num | | current = = weapon_num + SUPER_WEAPON ) {
//already have this selected, so toggle to other of normal/super version
weapon_num + = weapon_num + SUPER_WEAPON - current ;
2016-10-02 00:34:43 +00:00
weapon_status = player_has_primary_weapon ( player_info , weapon_num ) ;
2015-04-26 20:15:51 +00:00
}
2013-03-03 01:03:33 +00:00
else {
2015-04-26 20:15:51 +00:00
const auto weapon_num_save = weapon_num ;
//go to last-select version of requested missile
if ( last_was_super )
weapon_num + = SUPER_WEAPON ;
2016-10-02 00:34:43 +00:00
weapon_status = player_has_primary_weapon ( player_info , weapon_num ) ;
2015-04-26 20:15:51 +00:00
//if don't have last-selected, try other version
if ( ( weapon_status . flags ( ) & has_flag ) ! = has_flag ) {
weapon_num = 2 * weapon_num_save + SUPER_WEAPON - weapon_num ;
2016-10-02 00:34:43 +00:00
weapon_status = player_has_primary_weapon ( player_info , weapon_num ) ;
2015-04-26 20:15:51 +00:00
if ( ( weapon_status . flags ( ) & has_flag ) ! = has_flag )
weapon_num = 2 * weapon_num_save + SUPER_WEAPON - weapon_num ;
}
}
//if we don't have the weapon we're switching to, give error & bail
2015-05-06 02:11:03 +00:00
const auto weapon_name = PRIMARY_WEAPON_NAMES ( weapon_num ) ;
2015-04-26 20:15:51 +00:00
if ( ( weapon_status . flags ( ) & has_flag ) ! = has_flag ) {
{
2015-10-18 21:01:18 +00:00
if ( weapon_num = = primary_weapon_index_t : : SUPER_LASER_INDEX )
2015-04-26 20:15:51 +00:00
return ; //no such thing as super laser, so no error
2015-04-26 20:15:52 +00:00
HUD_init_message ( HM_DEFAULT , " %s %s! " , TXT_DONT_HAVE , weapon_name ) ;
2015-04-26 20:15:51 +00:00
}
digi_play_sample ( SOUND_BAD_SELECTION , F1_0 ) ;
return ;
}
//now actually select the weapon
# endif
2016-10-02 00:34:45 +00:00
select_primary_weapon ( player_info , weapon_name , weapon_num , 1 ) ;
2015-04-26 20:15:51 +00:00
}
2016-10-02 00:34:49 +00:00
void do_secondary_weapon_select ( player_info & player_info , secondary_weapon_index_t weapon_num )
2015-04-26 20:15:51 +00:00
{
# if defined(DXX_BUILD_DESCENT_I)
//added on 10/9/98 by Victor Rachels to add laser cycle
//end this section addition - Victor Rachels
// do special hud msg. for picking registered weapon in shareware version.
2015-05-06 02:11:03 +00:00
const auto weapon_name = SECONDARY_WEAPON_NAMES ( weapon_num ) ;
2016-10-02 00:34:45 +00:00
if ( reject_shareware_weapon_select ( weapon_num , weapon_name ) | | reject_unusable_secondary_weapon_select ( player_info , weapon_num , weapon_name ) )
2015-04-26 20:15:51 +00:00
{
2015-04-26 20:15:51 +00:00
digi_play_sample ( SOUND_BAD_SELECTION , F1_0 ) ;
return ;
2013-03-03 01:03:33 +00:00
}
# elif defined(DXX_BUILD_DESCENT_II)
2015-04-19 04:18:50 +00:00
has_weapon_result weapon_status ;
2006-03-20 17:12:09 +00:00
2016-10-02 00:34:49 +00:00
const auto current = player_info . Secondary_weapon . get_active ( ) ;
2017-02-19 19:33:45 +00:00
const auto last_was_super = player_info . Secondary_last_was_super & ( 1 < < weapon_num ) ;
2016-10-02 00:34:49 +00:00
const auto has_flag = weapon_status . has_weapon_flag | weapon_status . has_ammo_flag ;
2006-03-20 17:12:09 +00:00
if ( current = = weapon_num | | current = = weapon_num + SUPER_WEAPON ) {
//already have this selected, so toggle to other of normal/super version
2016-10-02 00:34:49 +00:00
weapon_num = static_cast < secondary_weapon_index_t > ( ( static_cast < unsigned > ( weapon_num ) * 2 ) + SUPER_WEAPON - current ) ;
2016-10-02 00:34:44 +00:00
weapon_status = player_has_secondary_weapon ( player_info , weapon_num ) ;
2006-03-20 17:12:09 +00:00
}
else {
2015-04-19 04:18:50 +00:00
const auto weapon_num_save = weapon_num ;
2006-03-20 17:12:09 +00:00
//go to last-select version of requested missile
if ( last_was_super )
2016-10-02 00:34:49 +00:00
weapon_num = static_cast < secondary_weapon_index_t > ( static_cast < unsigned > ( weapon_num ) + SUPER_WEAPON ) ;
2006-03-20 17:12:09 +00:00
2016-10-02 00:34:44 +00:00
weapon_status = player_has_secondary_weapon ( player_info , weapon_num ) ;
2006-03-20 17:12:09 +00:00
//if don't have last-selected, try other version
2015-04-19 04:18:50 +00:00
if ( ( weapon_status . flags ( ) & has_flag ) ! = has_flag ) {
2016-10-02 00:34:49 +00:00
weapon_num = static_cast < secondary_weapon_index_t > ( ( static_cast < unsigned > ( weapon_num_save ) * 2 ) + SUPER_WEAPON - static_cast < unsigned > ( weapon_num ) ) ;
2016-10-02 00:34:44 +00:00
weapon_status = player_has_secondary_weapon ( player_info , weapon_num ) ;
2015-04-19 04:18:50 +00:00
if ( ( weapon_status . flags ( ) & has_flag ) ! = has_flag )
2016-10-02 00:34:49 +00:00
weapon_num = static_cast < secondary_weapon_index_t > ( ( static_cast < unsigned > ( weapon_num_save ) * 2 ) + SUPER_WEAPON - static_cast < unsigned > ( weapon_num ) ) ;
2006-03-20 17:12:09 +00:00
}
}
//if we don't have the weapon we're switching to, give error & bail
2015-05-06 02:11:03 +00:00
const auto weapon_name = SECONDARY_WEAPON_NAMES ( weapon_num ) ;
2015-04-19 04:18:50 +00:00
if ( ( weapon_status . flags ( ) & has_flag ) ! = has_flag ) {
2015-04-26 20:15:52 +00:00
HUD_init_message ( HM_DEFAULT , " %s %s%s " , TXT_HAVE_NO , weapon_name , TXT_SX ) ;
2006-03-20 17:12:09 +00:00
digi_play_sample ( SOUND_BAD_SELECTION , F1_0 ) ;
return ;
}
//now actually select the weapon
2013-03-03 01:03:33 +00:00
# endif
2016-10-02 00:34:46 +00:00
select_secondary_weapon ( player_info , weapon_name , weapon_num , 1 ) ;
2006-03-20 17:12:09 +00:00
}
2016-08-25 04:05:32 +00:00
}
2006-03-20 17:12:09 +00:00
2020-10-12 03:28:26 +00:00
namespace {
2015-11-07 21:56:00 +00:00
template < typename T >
void auto_select_weapon ( T t )
{
for ( uint_fast32_t cur_order_slot = 0 ; cur_order_slot ! = t . max_weapons + 1 ; + + cur_order_slot )
{
const auto weapon_type = t . get_weapon_by_order_slot ( cur_order_slot ) ;
if ( weapon_type > = t . max_weapons )
{
t . abandon_auto_select ( ) ;
return ;
}
if ( t . maybe_select_weapon_by_type ( weapon_type ) )
return ;
}
}
2020-10-12 03:28:26 +00:00
}
2016-10-02 00:34:43 +00:00
namespace dsx {
2006-03-20 17:12:09 +00:00
// ----------------------------------------------------------------------------------------
// Automatically select next best weapon if unable to fire current weapon.
// Weapon type: 0==primary, 1==secondary
2016-10-02 00:34:43 +00:00
void auto_select_primary_weapon ( player_info & player_info )
2006-03-20 17:12:09 +00:00
{
2016-10-02 00:34:43 +00:00
if ( ! player_has_primary_weapon ( player_info , player_info . Primary_weapon ) . has_all ( ) )
2016-10-02 00:34:44 +00:00
auto_select_weapon ( cycle_primary_state ( player_info ) ) ;
2015-04-26 20:15:51 +00:00
}
2006-03-20 17:12:09 +00:00
2016-10-02 00:34:43 +00:00
void auto_select_secondary_weapon ( player_info & player_info )
2015-04-26 20:15:51 +00:00
{
2016-10-02 00:34:44 +00:00
if ( ! player_has_secondary_weapon ( player_info , player_info . Secondary_weapon ) . has_all ( ) )
auto_select_weapon ( cycle_secondary_state ( player_info ) ) ;
2006-03-20 17:12:09 +00:00
}
2020-10-12 03:28:26 +00:00
void delayed_autoselect ( player_info & player_info , const control_info & Controls )
2015-10-18 18:11:57 +00:00
{
2015-11-12 02:57:45 +00:00
if ( ! Controls . state . fire_primary )
{
2016-08-28 22:41:49 +00:00
auto & Primary_weapon = player_info . Primary_weapon ;
2016-08-06 19:55:23 +00:00
const auto primary_weapon = Primary_weapon . get_active ( ) ;
const auto delayed_primary = Primary_weapon . get_delayed ( ) ;
2015-11-12 02:57:45 +00:00
if ( delayed_primary ! = primary_weapon )
{
2016-10-02 00:34:43 +00:00
if ( player_has_primary_weapon ( player_info , delayed_primary ) . has_all ( ) )
2016-10-02 00:34:45 +00:00
select_primary_weapon ( player_info , nullptr , delayed_primary , 1 ) ;
2015-11-12 02:57:45 +00:00
else
2016-08-06 19:55:23 +00:00
Primary_weapon . set_delayed ( primary_weapon ) ;
2015-11-12 02:57:45 +00:00
}
}
if ( ! Controls . state . fire_secondary )
{
2016-10-02 00:34:43 +00:00
auto & Secondary_weapon = player_info . Secondary_weapon ;
const auto secondary_weapon = Secondary_weapon . get_active ( ) ;
const auto delayed_secondary = Secondary_weapon . get_delayed ( ) ;
2015-11-12 02:57:45 +00:00
if ( delayed_secondary ! = secondary_weapon )
{
2016-10-02 00:34:44 +00:00
if ( player_has_secondary_weapon ( player_info , delayed_secondary ) . has_all ( ) )
2016-10-02 00:34:46 +00:00
select_secondary_weapon ( player_info , nullptr , delayed_secondary , 1 ) ;
2015-11-12 02:57:45 +00:00
else
2016-10-02 00:34:43 +00:00
Secondary_weapon . set_delayed ( secondary_weapon ) ;
2015-11-12 02:57:45 +00:00
}
}
2015-10-18 18:11:57 +00:00
}
2020-10-12 03:28:26 +00:00
namespace {
static void maybe_autoselect_primary_weapon ( player_info & player_info , int weapon_index , const control_info & Controls )
2015-10-18 18:11:57 +00:00
{
2016-08-28 22:41:48 +00:00
const auto want_switch = [ weapon_index , & player_info ] {
2015-10-18 18:11:57 +00:00
const auto cutpoint = POrderList ( 255 ) ;
const auto weapon_order = POrderList ( weapon_index ) ;
2016-08-28 22:41:49 +00:00
return weapon_order < cutpoint & & weapon_order < POrderList ( get_mapped_weapon_index ( player_info , player_info . Primary_weapon . get_delayed ( ) ) ) ;
2015-10-18 18:11:57 +00:00
} ;
if ( Controls . state . fire_primary & & PlayerCfg . NoFireAutoselect ! = FiringAutoselectMode : : Immediate )
{
if ( PlayerCfg . NoFireAutoselect = = FiringAutoselectMode : : Delayed )
{
if ( want_switch ( ) )
2016-08-28 22:41:49 +00:00
player_info . Primary_weapon . set_delayed ( static_cast < primary_weapon_index_t > ( weapon_index ) ) ;
2015-10-18 18:11:57 +00:00
}
}
else if ( want_switch ( ) )
2016-10-02 00:34:45 +00:00
select_primary_weapon ( player_info , nullptr , weapon_index , 1 ) ;
2015-10-18 18:11:57 +00:00
}
2020-10-12 03:28:26 +00:00
}
2006-03-20 17:12:09 +00:00
// ---------------------------------------------------------------------
//called when one of these weapons is picked up
//when you pick up a secondary, you always get the weapon & ammo for it
// Returns true if powerup picked up, else returns false.
2020-10-12 03:28:26 +00:00
int pick_up_secondary ( player_info & player_info , int weapon_index , int count , const control_info & Controls )
2006-03-20 17:12:09 +00:00
{
int num_picked_up ;
2016-10-02 00:34:42 +00:00
const auto max = PLAYER_MAX_AMMO ( player_info . powerup_flags , Secondary_ammo_max [ weapon_index ] ) ;
2016-08-28 22:41:48 +00:00
auto & secondary_ammo = player_info . secondary_ammo ;
2015-10-30 02:52:56 +00:00
if ( secondary_ammo [ weapon_index ] > = max )
{
HUD_init_message ( HM_DEFAULT | HM_REDUNDANT | HM_MAYDUPL , " %s %i %ss! " , TXT_ALREADY_HAVE , secondary_ammo [ weapon_index ] , SECONDARY_WEAPON_NAMES ( weapon_index ) ) ;
2006-03-20 17:12:09 +00:00
return 0 ;
}
2015-10-30 02:52:56 +00:00
secondary_ammo [ weapon_index ] + = count ;
2006-03-20 17:12:09 +00:00
num_picked_up = count ;
2015-10-30 02:52:56 +00:00
if ( secondary_ammo [ weapon_index ] > max )
{
num_picked_up = count - ( secondary_ammo [ weapon_index ] - max ) ;
secondary_ammo [ weapon_index ] = max ;
2006-03-20 17:12:09 +00:00
}
2015-10-30 02:52:56 +00:00
if ( secondary_ammo [ weapon_index ] = = count ) // only autoselect if player didn't have any
2006-12-24 07:43:11 +00:00
{
2015-09-26 21:17:12 +00:00
const auto weapon_order = SOrderList ( weapon_index ) ;
2016-10-02 00:34:43 +00:00
auto & Secondary_weapon = player_info . Secondary_weapon ;
2018-04-02 03:39:51 +00:00
const auto want_switch = [ weapon_order , & secondary_ammo , & Secondary_weapon ] {
2015-10-18 18:11:57 +00:00
return weapon_order < SOrderList ( 255 ) & & (
2016-10-02 00:34:43 +00:00
secondary_ammo [ Secondary_weapon . get_delayed ( ) ] = = 0 | |
weapon_order < SOrderList ( Secondary_weapon . get_delayed ( ) )
2015-10-18 18:11:57 +00:00
) ;
} ;
if ( Controls . state . fire_secondary & & PlayerCfg . NoFireAutoselect ! = FiringAutoselectMode : : Immediate )
{
if ( PlayerCfg . NoFireAutoselect = = FiringAutoselectMode : : Delayed )
{
if ( want_switch ( ) )
2016-10-02 00:34:43 +00:00
Secondary_weapon . set_delayed ( static_cast < secondary_weapon_index_t > ( weapon_index ) ) ;
2015-10-18 18:11:57 +00:00
}
}
else if ( want_switch ( ) )
2016-10-02 00:34:46 +00:00
select_secondary_weapon ( player_info , nullptr , weapon_index , 1 ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-10-09 02:46:09 +00:00
//if it's a proxbomb or smart mine,
2006-12-24 07:43:11 +00:00
//we want to do a mini-auto-selection that applies to the drop bomb key
2006-03-20 17:12:09 +00:00
2013-09-21 22:01:38 +00:00
if ( weapon_index_is_player_bomb ( weapon_index ) & &
2016-08-28 22:41:48 +00:00
! weapon_index_is_player_bomb ( player_info . Secondary_weapon ) )
{
2017-02-19 19:33:45 +00:00
const auto mask = 1 < < PROXIMITY_INDEX ;
if ( weapon_order < SOrderList ( ( player_info . Secondary_last_was_super & mask ) ? SMART_MINE_INDEX : PROXIMITY_INDEX ) )
set_weapon_last_was_super ( player_info . Secondary_last_was_super , mask , weapon_index = = SMART_MINE_INDEX ) ;
2006-12-24 07:43:11 +00:00
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
//note: flash for all but concussion was 7,14,21
2010-06-17 09:45:05 +00:00
if ( num_picked_up > 1 ) {
2006-03-20 17:12:09 +00:00
PALETTE_FLASH_ADD ( 15 , 15 , 15 ) ;
2010-07-13 06:35:25 +00:00
HUD_init_message ( HM_DEFAULT , " %d %s%s " , num_picked_up , SECONDARY_WEAPON_NAMES ( weapon_index ) , TXT_SX ) ;
2006-03-20 17:12:09 +00:00
}
else {
PALETTE_FLASH_ADD ( 10 , 10 , 10 ) ;
2010-07-13 06:35:25 +00:00
HUD_init_message ( HM_DEFAULT , " %s! " , SECONDARY_WEAPON_NAMES ( weapon_index ) ) ;
2006-03-20 17:12:09 +00:00
}
return 1 ;
}
2016-08-25 04:05:32 +00:00
}
2006-03-20 17:12:09 +00:00
2020-10-12 03:28:26 +00:00
namespace {
2020-12-20 20:39:07 +00:00
template < typename cycle_weapon_state >
2016-01-17 17:36:48 +00:00
static void ReorderWeapon ( )
2006-03-20 17:12:09 +00:00
{
2020-12-20 20:39:07 +00:00
auto menu = window_create < weapon_reorder_menu < cycle_weapon_state > > ( grd_curscreen - > sc_canvas ) ;
( void ) menu ;
2016-01-17 17:36:48 +00:00
}
2010-06-17 09:45:05 +00:00
2020-10-12 03:28:26 +00:00
}
2020-12-19 16:13:26 +00:00
namespace dsx {
void ReorderPrimary ( )
2016-01-17 17:36:48 +00:00
{
ReorderWeapon < cycle_primary_state > ( ) ;
2006-03-20 17:12:09 +00:00
}
2020-12-19 16:13:26 +00:00
void ReorderSecondary ( )
2006-03-20 17:12:09 +00:00
{
2016-01-17 17:36:48 +00:00
ReorderWeapon < cycle_secondary_state > ( ) ;
}
2006-03-20 17:12:09 +00:00
2020-12-19 16:13:26 +00:00
}
2020-10-12 03:28:26 +00:00
namespace {
2016-01-17 17:36:48 +00:00
template < typename T >
static uint_fast32_t search_weapon_order_list ( uint8_t goal )
{
for ( uint_fast32_t i = 0 ; i ! = T : : max_weapons + 1 ; + + i )
if ( T : : get_weapon_by_order_slot ( i ) = = goal )
return i ;
T : : report_runtime_error ( T : : error_weapon_list_corrupt ) ;
2006-03-20 17:12:09 +00:00
}
2020-10-12 03:28:26 +00:00
}
namespace dsx {
namespace {
2015-10-30 02:52:54 +00:00
uint_fast32_t POrderList ( uint_fast32_t num )
2006-03-20 17:12:09 +00:00
{
2016-01-17 17:36:48 +00:00
return search_weapon_order_list < cycle_primary_state > ( num ) ;
2006-03-20 17:12:09 +00:00
}
2015-10-30 02:52:54 +00:00
uint_fast32_t SOrderList ( uint_fast32_t num )
2006-03-20 17:12:09 +00:00
{
2016-01-17 17:36:48 +00:00
return search_weapon_order_list < cycle_secondary_state > ( num ) ;
2006-03-20 17:12:09 +00:00
}
2020-10-12 03:28:26 +00:00
}
2006-03-20 17:12:09 +00:00
//called when a primary weapon is picked up
//returns true if actually picked up
2016-10-02 00:34:45 +00:00
int pick_up_primary ( player_info & player_info , int weapon_index )
2006-03-20 17:12:09 +00:00
{
2013-10-26 19:52:41 +00:00
ushort flag = HAS_PRIMARY_FLAG ( weapon_index ) ;
2006-03-20 17:12:09 +00:00
2015-11-07 21:55:59 +00:00
if ( weapon_index ! = primary_weapon_index_t : : LASER_INDEX & &
( player_info . primary_weapon_flags & flag ) )
2015-10-18 21:01:18 +00:00
{ //already have
2011-04-11 19:27:31 +00:00
HUD_init_message ( HM_DEFAULT | HM_REDUNDANT | HM_MAYDUPL , " %s %s! " , TXT_ALREADY_HAVE_THE , PRIMARY_WEAPON_NAMES ( weapon_index ) ) ;
2006-03-20 17:12:09 +00:00
return 0 ;
}
2015-11-07 21:55:59 +00:00
player_info . primary_weapon_flags | = flag ;
2006-03-20 17:12:09 +00:00
2020-10-12 03:28:26 +00:00
maybe_autoselect_primary_weapon ( player_info , weapon_index , Controls ) ;
2006-03-20 17:12:09 +00:00
PALETTE_FLASH_ADD ( 7 , 14 , 21 ) ;
2010-06-17 09:45:05 +00:00
2015-10-18 21:01:18 +00:00
if ( weapon_index ! = primary_weapon_index_t : : LASER_INDEX )
2010-07-13 06:35:25 +00:00
HUD_init_message ( HM_DEFAULT , " %s! " , PRIMARY_WEAPON_NAMES ( weapon_index ) ) ;
2006-03-20 17:12:09 +00:00
return 1 ;
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2016-10-02 00:34:45 +00:00
void check_to_use_primary_super_laser ( player_info & player_info )
2006-03-20 17:12:09 +00:00
{
2015-11-07 21:55:59 +00:00
if ( ! ( player_info . primary_weapon_flags & HAS_SUPER_LASER_FLAG ) )
2006-03-20 17:12:09 +00:00
{
2015-04-26 20:15:52 +00:00
const auto weapon_index = primary_weapon_index_t : : SUPER_LASER_INDEX ;
const auto pwi = POrderList ( weapon_index ) ;
if ( pwi < POrderList ( 255 ) & &
2016-08-28 22:41:49 +00:00
pwi < POrderList ( player_info . Primary_weapon ) )
2015-04-26 20:15:52 +00:00
{
2016-10-02 00:34:45 +00:00
select_primary_weapon ( player_info , nullptr , primary_weapon_index_t : : LASER_INDEX , 1 ) ;
2015-04-26 20:15:52 +00:00
}
2006-03-20 17:12:09 +00:00
}
PALETTE_FLASH_ADD ( 7 , 14 , 21 ) ;
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2020-10-12 03:28:26 +00:00
namespace {
2015-11-07 21:55:59 +00:00
static void maybe_autoselect_vulcan_weapon ( player_info & player_info )
2015-10-09 02:46:10 +00:00
{
# if defined(DXX_BUILD_DESCENT_I)
const auto weapon_flag_mask = HAS_VULCAN_FLAG ;
# elif defined(DXX_BUILD_DESCENT_II)
const auto weapon_flag_mask = HAS_VULCAN_FLAG | HAS_GAUSS_FLAG ;
# endif
2015-11-07 21:55:59 +00:00
const auto primary_weapon_flags = player_info . primary_weapon_flags ;
2015-10-09 02:46:10 +00:00
if ( ! ( primary_weapon_flags & weapon_flag_mask ) )
return ;
const auto cutpoint = POrderList ( 255 ) ;
auto weapon_index = primary_weapon_index_t : : VULCAN_INDEX ;
# if defined(DXX_BUILD_DESCENT_I)
const auto weapon_order_vulcan = POrderList ( primary_weapon_index_t : : VULCAN_INDEX ) ;
const auto better = weapon_order_vulcan ;
# elif defined(DXX_BUILD_DESCENT_II)
/* If a weapon is missing, pretend its auto-select priority is equal
* to cutpoint . Priority at or worse than cutpoint is never
* auto - selected .
*/
const auto weapon_order_vulcan = ( primary_weapon_flags & HAS_VULCAN_FLAG )
? POrderList ( primary_weapon_index_t : : VULCAN_INDEX )
: cutpoint ;
const auto weapon_order_gauss = ( primary_weapon_flags & HAS_GAUSS_FLAG )
? POrderList ( primary_weapon_index_t : : GAUSS_INDEX )
: cutpoint ;
/* Set better to whichever vulcan-based weapon is higher priority.
* The chosen weapon might still be worse than cutpoint .
*/
const auto better = ( weapon_order_vulcan < weapon_order_gauss )
? weapon_order_vulcan
: ( weapon_index = primary_weapon_index_t : : GAUSS_INDEX , weapon_order_gauss ) ;
# endif
if ( better > = cutpoint )
/* Preferred weapon is not auto-selectable */
return ;
2016-08-28 22:41:49 +00:00
if ( better > = POrderList ( get_mapped_weapon_index ( player_info , player_info . Primary_weapon ) ) )
2015-10-09 02:46:10 +00:00
/* Preferred weapon is not as desirable as the current weapon */
return ;
2020-10-12 03:28:26 +00:00
maybe_autoselect_primary_weapon ( player_info , weapon_index , Controls ) ;
}
2015-10-09 02:46:10 +00:00
}
2006-03-20 17:12:09 +00:00
//called when ammo (for the vulcan cannon) is picked up
// Returns the amount picked up
2016-10-02 00:34:44 +00:00
int pick_up_vulcan_ammo ( player_info & player_info , uint_fast32_t ammo_count , const bool change_weapon )
2006-03-20 17:12:09 +00:00
{
2016-10-02 00:34:42 +00:00
const auto max = PLAYER_MAX_AMMO ( player_info . powerup_flags , VULCAN_AMMO_MAX ) ;
2016-10-02 00:34:40 +00:00
auto & plr_vulcan_ammo = player_info . vulcan_ammo ;
2015-11-07 21:55:59 +00:00
const auto old_ammo = plr_vulcan_ammo ;
2015-10-30 02:52:56 +00:00
if ( old_ammo > = max )
return 0 ;
2006-03-20 17:12:09 +00:00
2015-11-07 21:55:59 +00:00
plr_vulcan_ammo + = ammo_count ;
2006-03-20 17:12:09 +00:00
2015-11-07 21:55:59 +00:00
if ( plr_vulcan_ammo > max ) {
ammo_count + = ( max - plr_vulcan_ammo ) ;
plr_vulcan_ammo = max ;
2006-03-20 17:12:09 +00:00
}
2015-10-09 02:46:10 +00:00
if ( change_weapon & &
2015-10-18 18:11:57 +00:00
! old_ammo )
2016-10-02 00:34:44 +00:00
maybe_autoselect_vulcan_weapon ( player_info ) ;
2006-03-20 17:12:09 +00:00
return ammo_count ; //return amount used
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2017-01-29 21:02:48 +00:00
// Homing weapons cheat
void weapons_homing_all ( )
{
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
auto & vmobjptr = Objects . vmptr ;
2017-06-10 03:31:02 +00:00
range_for ( auto & & objp , vmobjptr )
2017-01-29 21:02:48 +00:00
if ( objp - > type = = OBJ_WEAPON )
objp - > ctype . laser_info . track_goal = object_none ;
range_for ( auto & w , Weapon_info )
w . homing_flag | = 2 ;
}
void weapons_homing_all_reset ( )
{
range_for ( auto & w , Weapon_info )
w . homing_flag & = ~ 2 ;
}
2006-03-20 17:12:09 +00:00
# define SMEGA_SHAKE_TIME (F1_0*2)
// Call this to initialize for a new level.
// Sets all super mega missile detonation times to 0 which means there aren't any.
2015-12-22 04:18:50 +00:00
void init_smega_detonates ( )
2006-03-20 17:12:09 +00:00
{
2019-08-06 02:59:41 +00:00
LevelUniqueSeismicState . Earthshaker_detonate_times = { } ;
2006-03-20 17:12:09 +00:00
}
2017-10-14 17:10:30 +00:00
constexpr std : : integral_constant < int , SOUND_SEISMIC_DISTURBANCE_START > Seismic_sound { } ;
2015-02-14 22:48:29 +00:00
2020-10-12 03:28:26 +00:00
namespace {
2015-02-14 22:48:29 +00:00
static void start_seismic_sound ( )
{
2019-08-06 02:59:41 +00:00
if ( LevelUniqueSeismicState . Next_seismic_sound_time )
2015-02-14 22:48:29 +00:00
return ;
2019-08-06 02:59:41 +00:00
LevelUniqueSeismicState . Next_seismic_sound_time = GameTime64 + d_rand ( ) / 2 ;
2015-02-14 22:48:29 +00:00
digi_play_sample_looping ( Seismic_sound , F1_0 , - 1 , - 1 ) ;
}
2006-03-20 17:12:09 +00:00
2019-08-06 02:59:41 +00:00
static void apply_seismic_effect ( const int entry_fc )
{
const auto fc = std : : min ( std : : max ( entry_fc , 16 ) , 1 ) ;
LevelUniqueSeismicState . Seismic_tremor_volume + = fc ;
if ( ! d_tick_step )
return ;
const auto get_base_disturbance = [ fc ] ( ) {
return fixmul ( d_rand ( ) - 16384 , 3 * F1_0 / 16 + ( F1_0 * ( 16 - fc ) ) / 32 ) ;
} ;
const fix disturb_x = get_base_disturbance ( ) ;
const fix disturb_z = get_base_disturbance ( ) ;
{
auto & rotvel = ConsoleObject - > mtype . phys_info . rotvel ;
rotvel . x + = disturb_x ;
rotvel . z + = disturb_z ;
}
// Shake the buddy!
auto & BuddyState = LevelUniqueObjectState . BuddyState ;
const auto Buddy_objnum = BuddyState . Buddy_objnum ;
if ( Buddy_objnum ! = object_none ) {
auto & objp = * LevelUniqueObjectState . Objects . vmptr ( Buddy_objnum ) ;
auto & rotvel = objp . mtype . phys_info . rotvel ;
rotvel . x + = disturb_x * 4 ;
rotvel . z + = disturb_z * 4 ;
}
// Shake a guided missile!
2019-08-06 02:59:41 +00:00
LevelUniqueSeismicState . Seismic_tremor_magnitude + = disturb_x ;
2019-08-06 02:59:41 +00:00
}
2020-10-12 03:28:26 +00:00
}
2006-03-20 17:12:09 +00:00
// If a smega missile been detonated, rock the mine!
// This should be called every frame.
// Maybe this should affect all robots, being called when they get their physics done.
void rock_the_mine_frame ( void )
{
2019-08-06 02:59:41 +00:00
range_for ( auto & i , LevelUniqueSeismicState . Earthshaker_detonate_times )
2015-02-14 22:48:29 +00:00
{
if ( i ! = 0 ) {
fix delta_time = GameTime64 - i ;
2015-02-14 22:48:29 +00:00
start_seismic_sound ( ) ;
2006-03-20 17:12:09 +00:00
if ( delta_time < SMEGA_SHAKE_TIME ) {
// Control center destroyed, rock the player's ship.
// -- fc = abs(delta_time - SMEGA_SHAKE_TIME/2);
// Changed 10/23/95 to make decreasing for super mega missile.
2019-08-06 02:59:41 +00:00
const int fc = ( SMEGA_SHAKE_TIME - delta_time ) / 2 ;
apply_seismic_effect ( fc / ( SMEGA_SHAKE_TIME / 32 ) ) ;
2006-03-20 17:12:09 +00:00
} else
2015-02-14 22:48:29 +00:00
i = 0 ;
2006-03-20 17:12:09 +00:00
}
}
// Hook in the rumble sound effect here.
}
2015-12-22 04:18:50 +00:00
void init_seismic_disturbances ( )
2006-03-20 17:12:09 +00:00
{
2019-08-06 02:59:40 +00:00
LevelUniqueSeismicState . Seismic_disturbance_end_time = 0 ;
2006-03-20 17:12:09 +00:00
}
2020-10-12 03:28:26 +00:00
namespace {
2006-03-20 17:12:09 +00:00
// Return true if time to start a seismic disturbance.
2015-02-14 22:48:29 +00:00
static bool seismic_disturbance_active ( )
2006-03-20 17:12:09 +00:00
{
2019-08-06 02:59:41 +00:00
const auto level_shake_duration = LevelSharedSeismicState . Level_shake_duration ;
2019-08-06 02:59:41 +00:00
if ( level_shake_duration < 1 )
2015-02-14 22:48:29 +00:00
return false ;
2006-03-20 17:12:09 +00:00
2019-08-06 02:59:40 +00:00
if ( LevelUniqueSeismicState . Seismic_disturbance_end_time & & LevelUniqueSeismicState . Seismic_disturbance_end_time < GameTime64 )
2015-02-14 22:48:29 +00:00
return true ;
bool rval ;
2019-08-06 02:59:41 +00:00
rval = ( 2 * fixmul ( d_rand ( ) , LevelSharedSeismicState . Level_shake_frequency ) ) < FrameTime ;
2006-03-20 17:12:09 +00:00
if ( rval ) {
2019-08-06 02:59:40 +00:00
LevelUniqueSeismicState . Seismic_disturbance_end_time = GameTime64 + level_shake_duration ;
2015-02-14 22:48:29 +00:00
start_seismic_sound ( ) ;
2006-03-20 17:12:09 +00:00
if ( Game_mode & GM_MULTI )
2015-02-14 22:48:29 +00:00
multi_send_seismic ( level_shake_duration ) ;
2006-03-20 17:12:09 +00:00
}
return rval ;
}
2013-10-27 22:00:14 +00:00
static void seismic_disturbance_frame ( void )
2006-03-20 17:12:09 +00:00
{
2019-08-06 02:59:41 +00:00
if ( LevelSharedSeismicState . Level_shake_frequency ) {
2015-02-14 22:48:29 +00:00
if ( seismic_disturbance_active ( ) ) {
2019-08-06 02:59:40 +00:00
fix delta_time = static_cast < fix > ( GameTime64 - LevelUniqueSeismicState . Seismic_disturbance_end_time ) ;
2019-08-06 02:59:41 +00:00
const int fc = abs ( delta_time - LevelSharedSeismicState . Level_shake_duration / 2 ) ;
apply_seismic_effect ( fc / ( F1_0 / 16 ) ) ;
2006-03-20 17:12:09 +00:00
}
}
}
2020-10-12 03:28:26 +00:00
}
2006-03-20 17:12:09 +00:00
// Call this when a smega detonates to start the process of rocking the mine.
void smega_rock_stuff ( void )
{
2019-08-06 02:59:41 +00:00
auto least = & LevelUniqueSeismicState . Earthshaker_detonate_times [ 0 ] ;
range_for ( auto & i , LevelUniqueSeismicState . Earthshaker_detonate_times )
2014-09-06 04:07:45 +00:00
{
if ( i + SMEGA_SHAKE_TIME < GameTime64 )
i = 0 ;
if ( * least > i )
least = & i ;
2006-03-20 17:12:09 +00:00
}
2014-09-06 04:07:45 +00:00
* least = GameTime64 ;
2006-03-20 17:12:09 +00:00
}
2015-01-22 03:34:20 +00:00
static int Super_mines_yes = 1 ;
2020-10-12 03:28:26 +00:00
namespace {
2015-01-22 03:34:20 +00:00
static bool immediate_detonate_smart_mine ( const vcobjptridx_t smart_mine , const vcobjptridx_t target )
{
if ( smart_mine - > segnum = = target - > segnum )
return true ;
// Object which is close enough to detonate smart mine is not in same segment as smart mine.
// Need to do a more expensive check to make sure there isn't an obstruction.
if ( likely ( ( d_tick_count ^ ( static_cast < vcobjptridx_t : : integral_type > ( smart_mine ) + static_cast < vcobjptridx_t : : integral_type > ( target ) ) ) % 4 ) )
// Maybe next frame
return false ;
fvi_query fq { } ;
fvi_info hit_data ;
fq . startseg = smart_mine - > segnum ;
fq . p0 = & smart_mine - > pos ;
fq . p1 = & target - > pos ;
fq . thisobjnum = smart_mine ;
auto fate = find_vector_intersection ( fq , hit_data ) ;
return fate ! = HIT_WALL ;
}
2006-03-20 17:12:09 +00:00
2020-10-12 03:28:26 +00:00
}
2006-03-20 17:12:09 +00:00
// Call this once/frame to process all super mines in the level.
void process_super_mines_frame ( void )
{
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
auto & vmobjptridx = Objects . vmptridx ;
2006-03-20 17:12:09 +00:00
int start , add ;
// If we don't know of there being any super mines in the level, just
// check every 8th object each frame.
if ( Super_mines_yes = = 0 ) {
2012-05-14 17:06:28 +00:00
start = d_tick_count & 7 ;
2006-03-20 17:12:09 +00:00
add = 8 ;
} else {
start = 0 ;
add = 1 ;
}
Super_mines_yes = 0 ;
2015-07-13 01:09:36 +00:00
for ( objnum_t i = start ; i < = Highest_object_index ; i + = add ) {
2017-06-10 03:31:02 +00:00
const auto io = vmobjptridx ( i ) ;
2015-12-03 03:26:49 +00:00
if ( likely ( io - > type ! = OBJ_WEAPON | | get_weapon_id ( io ) ! = weapon_id_type : : SUPERPROX_ID ) )
2015-01-22 03:34:20 +00:00
continue ;
Super_mines_yes = 1 ;
2015-12-03 03:26:49 +00:00
if ( unlikely ( io - > lifeleft + F1_0 * 2 > = Weapon_info [ weapon_id_type : : SUPERPROX_ID ] . lifetime ) )
2015-01-22 03:34:20 +00:00
continue ;
const auto parent_num = io - > ctype . laser_info . parent_num ;
const auto & bombpos = io - > pos ;
2017-06-10 03:31:02 +00:00
range_for ( const auto & & jo , vmobjptridx )
2015-01-22 03:34:20 +00:00
{
2015-12-22 04:18:51 +00:00
if ( unlikely ( jo = = parent_num ) )
2015-01-22 03:34:20 +00:00
continue ;
if ( jo - > type ! = OBJ_PLAYER & & jo - > type ! = OBJ_ROBOT )
continue ;
const auto dist_squared = vm_vec_dist2 ( bombpos , jo - > pos ) ;
2015-03-12 02:21:19 +00:00
const vm_distance distance_threshold { F1_0 * 20 } ;
const auto distance_threshold_squared = distance_threshold * distance_threshold ;
if ( likely ( distance_threshold_squared < dist_squared ) )
2015-01-22 03:34:20 +00:00
/* Cheap check, some false negatives */
continue ;
const fix64 j_size = jo - > size ;
const fix64 j_size_squared = j_size * j_size ;
if ( dist_squared - j_size_squared > = distance_threshold_squared )
/* Accurate check */
continue ;
if ( immediate_detonate_smart_mine ( io , jo ) )
io - > lifeleft = 1 ;
2006-03-20 17:12:09 +00:00
}
}
}
2015-07-02 02:37:55 +00:00
# endif
2006-03-20 17:12:09 +00:00
# define SPIT_SPEED 20
//this function is for when the player intentionally drops a powerup
//this function is based on drop_powerup()
2018-10-21 00:24:07 +00:00
imobjptridx_t spit_powerup ( const d_vclip_array & Vclip , const object_base & spitter , const unsigned id , const unsigned seed )
2006-03-20 17:12:09 +00:00
{
d_srand ( seed ) ;
2018-06-24 05:06:15 +00:00
auto new_velocity = vm_vec_scale_add ( spitter . mtype . phys_info . velocity , spitter . orient . fvec , i2f ( SPIT_SPEED ) ) ;
2006-03-20 17:12:09 +00:00
new_velocity . x + = ( d_rand ( ) - 16384 ) * SPIT_SPEED * 2 ;
new_velocity . y + = ( d_rand ( ) - 16384 ) * SPIT_SPEED * 2 ;
new_velocity . z + = ( d_rand ( ) - 16384 ) * SPIT_SPEED * 2 ;
// Give keys zero velocity so they can be tracked better in multi
if ( ( Game_mode & GM_MULTI ) & & ( id > = POW_KEY_BLUE ) & & ( id < = POW_KEY_GOLD ) )
2014-09-28 21:11:04 +00:00
vm_vec_zero ( new_velocity ) ;
2006-03-20 17:12:09 +00:00
//there's a piece of code which lets the player pick up a powerup if
//the distance between him and the powerup is less than 2 time their
//combined radii. So we need to create powerups pretty far out from
//the player.
2018-06-24 05:06:15 +00:00
const auto new_pos = vm_vec_scale_add ( spitter . pos , spitter . orient . fvec , spitter . size ) ;
2006-03-20 17:12:09 +00:00
if ( Game_mode & GM_MULTI )
2010-06-17 09:45:05 +00:00
{
2006-03-20 17:12:09 +00:00
if ( Net_create_loc > = MAX_NET_CREATE_OBJECTS )
{
2013-12-26 22:21:16 +00:00
return object_none ;
2006-03-20 17:12:09 +00:00
}
}
2020-08-10 03:45:13 +00:00
const auto & & obj = obj_create ( OBJ_POWERUP , id , vmsegptridx ( spitter . segnum ) , new_pos , & vmd_identity_matrix , Powerup_info [ id ] . size , object : : control_type : : powerup , object : : movement_type : : physics , RT_POWERUP ) ;
2006-03-20 17:12:09 +00:00
2016-10-29 23:16:15 +00:00
if ( obj = = object_none )
{
2006-03-20 17:12:09 +00:00
Int3 ( ) ;
2016-10-29 23:16:15 +00:00
return object_none ;
2006-03-20 17:12:09 +00:00
}
obj - > mtype . phys_info . velocity = new_velocity ;
obj - > mtype . phys_info . drag = 512 ; //1024;
obj - > mtype . phys_info . mass = F1_0 ;
obj - > mtype . phys_info . flags = PF_BOUNCE ;
2013-10-07 23:52:33 +00:00
obj - > rtype . vclip_info . vclip_num = Powerup_info [ get_powerup_id ( obj ) ] . vclip_num ;
2006-03-20 17:12:09 +00:00
obj - > rtype . vclip_info . frametime = Vclip [ obj - > rtype . vclip_info . vclip_num ] . frame_time ;
obj - > rtype . vclip_info . framenum = 0 ;
2018-06-24 05:06:15 +00:00
if ( & spitter = = ConsoleObject )
2006-03-20 17:12:09 +00:00
obj - > ctype . powerup_info . flags | = PF_SPAT_BY_PLAYER ;
2013-10-07 23:52:33 +00:00
switch ( get_powerup_id ( obj ) ) {
2006-03-20 17:12:09 +00:00
case POW_MISSILE_1 :
case POW_MISSILE_4 :
case POW_SHIELD_BOOST :
case POW_ENERGY :
obj - > lifeleft = ( d_rand ( ) + F1_0 * 3 ) * 64 ; // Lives for 3 to 3.5 binary minutes (a binary minute is 64 seconds)
if ( Game_mode & GM_MULTI )
obj - > lifeleft / = 2 ;
break ;
default :
//if (Game_mode & GM_MULTI)
// obj->lifeleft = (d_rand() + F1_0*3) * 64; // Lives for 5 to 5.5 binary minutes (a binary minute is 64 seconds)
break ;
}
2014-09-08 03:24:48 +00:00
return obj ;
2006-03-20 17:12:09 +00:00
}
2016-10-02 00:34:44 +00:00
void DropCurrentWeapon ( player_info & player_info )
2006-03-20 17:12:09 +00:00
{
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
auto & vmobjptr = Objects . vmptr ;
2018-12-30 00:43:57 +00:00
if ( LevelUniqueObjectState . num_objects > = Objects . size ( ) )
2009-02-17 11:34:16 +00:00
return ;
2015-04-19 04:18:53 +00:00
powerup_type_t drop_type ;
2016-08-28 22:41:49 +00:00
const auto & Primary_weapon = player_info . Primary_weapon ;
2015-03-28 17:18:02 +00:00
const auto GrantedItems = ( Game_mode & GM_MULTI ) ? Netgame . SpawnGrantedItems : 0 ;
auto weapon_name = PRIMARY_WEAPON_NAMES ( Primary_weapon ) ;
2016-08-06 19:55:23 +00:00
if ( Primary_weapon = = primary_weapon_index_t : : LASER_INDEX )
2006-03-20 17:12:09 +00:00
{
2015-11-07 21:55:59 +00:00
if ( ( player_info . powerup_flags & PLAYER_FLAGS_QUAD_LASERS ) & & ! GrantedItems . has_quad_laser ( ) )
2015-03-28 17:18:02 +00:00
{
/* Sorry, no message. Need to fall through in case player
* wanted to drop a laser powerup .
*/
drop_type = POW_QUAD_FIRE ;
weapon_name = TXT_QUAD_LASERS ;
}
2020-08-24 01:31:28 +00:00
else if ( player_info . laser_level = = laser_level : : _1 )
2015-03-28 17:18:02 +00:00
{
HUD_init_message_literal ( HM_DEFAULT , " You cannot drop your base weapon! " ) ;
return ;
}
# if defined(DXX_BUILD_DESCENT_II)
2015-11-07 21:55:59 +00:00
else if ( player_info . laser_level > MAX_LASER_LEVEL )
2015-03-28 17:18:02 +00:00
{
/* Disallow dropping any super lasers until someone requests
* it .
*/
HUD_init_message_literal ( HM_DEFAULT , " You cannot drop super lasers! " ) ;
return ;
}
# endif
2015-11-07 21:55:59 +00:00
else if ( player_info . laser_level < = map_granted_flags_to_laser_level ( GrantedItems ) )
2015-03-28 17:18:02 +00:00
{
HUD_init_message_literal ( HM_DEFAULT , " You cannot drop granted lasers! " ) ;
return ;
}
else
drop_type = POW_LASER ;
}
else
{
if ( HAS_PRIMARY_FLAG ( Primary_weapon ) & map_granted_flags_to_primary_weapon_flags ( GrantedItems ) )
{
HUD_init_message ( HM_DEFAULT , " You cannot drop granted %s! " , weapon_name ) ;
return ;
}
drop_type = Primary_weapon_to_powerup [ Primary_weapon ] ;
2006-03-20 17:12:09 +00:00
}
2015-03-28 17:18:02 +00:00
const auto seed = d_rand ( ) ;
2018-10-21 00:24:07 +00:00
const auto objnum = spit_powerup ( Vclip , vmobjptr ( ConsoleObject ) , drop_type , seed ) ;
2013-12-29 04:28:07 +00:00
if ( objnum = = object_none )
2015-03-28 17:18:02 +00:00
{
HUD_init_message ( HM_DEFAULT , " Failed to drop %s! " , weapon_name ) ;
2006-03-20 17:12:09 +00:00
return ;
2015-03-28 17:18:02 +00:00
}
HUD_init_message ( HM_DEFAULT , " %s dropped! " , weapon_name ) ;
2015-07-02 02:37:55 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-03-28 17:18:02 +00:00
digi_play_sample ( SOUND_DROP_WEAPON , F1_0 ) ;
2015-07-02 02:37:55 +00:00
# endif
2006-03-20 17:12:09 +00:00
2013-09-20 23:12:51 +00:00
if ( weapon_index_uses_vulcan_ammo ( Primary_weapon ) ) {
2006-03-20 17:12:09 +00:00
//if it's one of these, drop some ammo with the weapon
2016-10-02 00:34:40 +00:00
auto & plr_vulcan_ammo = player_info . vulcan_ammo ;
2015-11-07 21:55:59 +00:00
auto ammo = plr_vulcan_ammo ;
2015-07-02 02:37:55 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-11-07 21:55:59 +00:00
const auto HAS_VULCAN_AND_GAUSS_FLAGS = HAS_VULCAN_FLAG | HAS_GAUSS_FLAG ;
if ( ( player_info . primary_weapon_flags & HAS_VULCAN_AND_GAUSS_FLAGS ) = = HAS_VULCAN_AND_GAUSS_FLAGS )
2006-03-20 17:12:09 +00:00
ammo / = 2 ; //if both vulcan & gauss, drop half
2015-07-02 02:37:55 +00:00
# endif
2006-03-20 17:12:09 +00:00
2015-11-07 21:55:59 +00:00
plr_vulcan_ammo - = ammo ;
2006-03-20 17:12:09 +00:00
2014-11-20 03:00:41 +00:00
objnum - > ctype . powerup_info . count = ammo ;
2006-03-20 17:12:09 +00:00
}
2015-07-02 02:37:55 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-10-18 21:01:18 +00:00
if ( Primary_weapon = = primary_weapon_index_t : : OMEGA_INDEX ) {
2006-03-20 17:12:09 +00:00
//dropped weapon has current energy
2016-10-02 00:34:44 +00:00
objnum - > ctype . powerup_info . count = player_info . Omega_charge ;
2006-03-20 17:12:09 +00:00
}
2015-07-02 02:37:55 +00:00
# endif
2006-03-20 17:12:09 +00:00
2015-03-28 17:18:02 +00:00
if ( Game_mode & GM_MULTI )
2006-03-20 17:12:09 +00:00
multi_send_drop_weapon ( objnum , seed ) ;
2015-10-18 21:01:18 +00:00
if ( Primary_weapon = = primary_weapon_index_t : : LASER_INDEX )
2015-03-28 17:18:02 +00:00
{
if ( drop_type = = POW_QUAD_FIRE )
2015-11-07 21:55:59 +00:00
player_info . powerup_flags & = ~ PLAYER_FLAGS_QUAD_LASERS ;
2015-03-28 17:18:02 +00:00
else
2015-11-07 21:55:59 +00:00
- - player_info . laser_level ;
2015-03-28 17:18:02 +00:00
}
else
2015-11-07 21:55:59 +00:00
player_info . primary_weapon_flags & = ~ HAS_PRIMARY_FLAG ( Primary_weapon ) ;
2016-10-02 00:34:43 +00:00
auto_select_primary_weapon ( player_info ) ;
2006-03-20 17:12:09 +00:00
}
2016-10-02 00:34:44 +00:00
void DropSecondaryWeapon ( player_info & player_info )
2006-03-20 17:12:09 +00:00
{
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
auto & vmobjptr = Objects . vmptr ;
2013-12-29 04:28:07 +00:00
int seed ;
2009-02-17 11:34:16 +00:00
ushort sub_ammo = 0 ;
2010-06-17 09:45:05 +00:00
2018-12-30 00:43:57 +00:00
if ( LevelUniqueObjectState . num_objects > = Objects . size ( ) )
2009-02-17 11:34:16 +00:00
return ;
2006-03-20 17:12:09 +00:00
2016-08-28 22:41:48 +00:00
auto & Secondary_weapon = player_info . Secondary_weapon ;
auto & secondary_ammo = player_info . secondary_ammo [ Secondary_weapon ] ;
2016-07-03 00:54:15 +00:00
if ( secondary_ammo = = 0 )
2006-03-20 17:12:09 +00:00
{
2013-06-23 16:27:34 +00:00
HUD_init_message_literal ( HM_DEFAULT , " No secondary weapon to drop! " ) ;
2006-03-20 17:12:09 +00:00
return ;
}
2010-06-17 09:45:05 +00:00
2015-04-19 04:18:53 +00:00
auto weapon_drop_id = Secondary_weapon_to_powerup [ Secondary_weapon ] ;
2006-03-20 17:12:09 +00:00
2009-02-17 11:34:16 +00:00
// see if we drop single or 4-pack
2015-04-19 04:18:53 +00:00
switch ( weapon_drop_id )
2006-03-20 17:12:09 +00:00
{
2009-02-17 11:34:16 +00:00
case POW_MISSILE_1 :
case POW_HOMING_AMMO_1 :
2015-07-02 02:37:55 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2009-02-17 11:34:16 +00:00
case POW_SMISSILE1_1 :
case POW_GUIDED_MISSILE_1 :
case POW_MERCURY_MISSILE_1 :
2015-07-02 02:37:55 +00:00
# endif
2016-07-03 00:54:15 +00:00
if ( secondary_ammo % 4 )
2009-02-17 11:34:16 +00:00
{
sub_ammo = 1 ;
}
else
{
sub_ammo = 4 ;
2015-04-19 04:18:53 +00:00
//4-pack always is next index
weapon_drop_id = static_cast < powerup_type_t > ( 1 + static_cast < uint_fast32_t > ( weapon_drop_id ) ) ;
2009-02-17 11:34:16 +00:00
}
break ;
case POW_PROXIMITY_WEAPON :
2015-07-02 02:37:55 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2009-02-17 11:34:16 +00:00
case POW_SMART_MINE :
2015-07-02 02:37:55 +00:00
# endif
2016-07-03 00:54:15 +00:00
if ( secondary_ammo < 4 )
2009-02-17 11:34:16 +00:00
{
2013-06-23 16:27:34 +00:00
HUD_init_message_literal ( HM_DEFAULT , " You need at least 4 to drop! " ) ;
2009-02-17 11:34:16 +00:00
return ;
}
else
{
sub_ammo = 4 ;
}
break ;
case POW_SMARTBOMB_WEAPON :
case POW_MEGA_WEAPON :
2015-07-02 02:37:55 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2009-02-17 11:34:16 +00:00
case POW_EARTHSHAKER_MISSILE :
2015-07-02 02:37:55 +00:00
# endif
2009-02-17 11:34:16 +00:00
sub_ammo = 1 ;
break ;
2015-04-19 04:18:53 +00:00
case POW_EXTRA_LIFE :
case POW_ENERGY :
case POW_SHIELD_BOOST :
case POW_LASER :
case POW_KEY_BLUE :
case POW_KEY_RED :
case POW_KEY_GOLD :
case POW_MISSILE_4 :
case POW_QUAD_FIRE :
case POW_VULCAN_WEAPON :
case POW_SPREADFIRE_WEAPON :
case POW_PLASMA_WEAPON :
case POW_FUSION_WEAPON :
case POW_HOMING_AMMO_4 :
case POW_VULCAN_AMMO :
case POW_CLOAK :
case POW_TURBO :
case POW_INVULNERABILITY :
case POW_MEGAWOW :
2015-07-02 02:37:55 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-04-19 04:18:53 +00:00
case POW_GAUSS_WEAPON :
case POW_HELIX_WEAPON :
case POW_PHOENIX_WEAPON :
case POW_OMEGA_WEAPON :
case POW_SUPER_LASER :
case POW_FULL_MAP :
case POW_CONVERTER :
case POW_AMMO_RACK :
case POW_AFTERBURNER :
case POW_HEADLIGHT :
case POW_SMISSILE1_4 :
case POW_GUIDED_MISSILE_4 :
case POW_MERCURY_MISSILE_4 :
case POW_FLAG_BLUE :
case POW_FLAG_RED :
case POW_HOARD_ORB :
2015-07-02 02:37:55 +00:00
# endif
2015-04-19 04:18:53 +00:00
break ;
2006-03-20 17:12:09 +00:00
}
2010-07-13 06:35:25 +00:00
HUD_init_message ( HM_DEFAULT , " %s dropped! " , SECONDARY_WEAPON_NAMES ( Secondary_weapon ) ) ;
2015-07-02 02:37:55 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
digi_play_sample ( SOUND_DROP_WEAPON , F1_0 ) ;
2015-07-02 02:37:55 +00:00
# endif
2006-03-20 17:12:09 +00:00
seed = d_rand ( ) ;
2018-10-21 00:24:07 +00:00
auto objnum = spit_powerup ( Vclip , vmobjptr ( ConsoleObject ) , weapon_drop_id , seed ) ;
2006-03-20 17:12:09 +00:00
2013-12-29 04:28:07 +00:00
if ( objnum = = object_none )
2006-03-20 17:12:09 +00:00
return ;
2016-10-29 23:16:15 +00:00
if ( Game_mode & GM_MULTI )
2006-03-20 17:12:09 +00:00
multi_send_drop_weapon ( objnum , seed ) ;
2016-07-03 00:54:15 +00:00
secondary_ammo - = sub_ammo ;
2006-03-20 17:12:09 +00:00
2016-07-03 00:54:15 +00:00
if ( secondary_ammo = = 0 )
2006-03-20 17:12:09 +00:00
{
2016-10-02 00:34:43 +00:00
auto_select_secondary_weapon ( player_info ) ;
2006-03-20 17:12:09 +00:00
}
}
2015-07-02 02:37:55 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// ---------------------------------------------------------------------------------------
// Do seismic disturbance stuff including the looping sounds with changing volume.
void do_seismic_stuff ( void )
{
2020-05-02 21:18:42 +00:00
const auto stv_save = std : : exchange ( LevelUniqueSeismicState . Seismic_tremor_volume , 0 ) ;
2019-08-06 02:59:41 +00:00
LevelUniqueSeismicState . Seismic_tremor_magnitude = 0 ;
2006-03-20 17:12:09 +00:00
rock_the_mine_frame ( ) ;
seismic_disturbance_frame ( ) ;
if ( stv_save ! = 0 ) {
2019-08-06 02:59:41 +00:00
const auto Seismic_tremor_volume = LevelUniqueSeismicState . Seismic_tremor_volume ;
if ( Seismic_tremor_volume = = 0 )
{
2006-03-20 17:12:09 +00:00
digi_stop_looping_sound ( ) ;
2019-08-06 02:59:41 +00:00
LevelUniqueSeismicState . Next_seismic_sound_time = 0 ;
2006-03-20 17:12:09 +00:00
}
2019-08-06 02:59:41 +00:00
else if ( GameTime64 > LevelUniqueSeismicState . Next_seismic_sound_time )
2019-08-06 02:59:41 +00:00
{
2006-03-20 17:12:09 +00:00
int volume ;
volume = Seismic_tremor_volume * 2048 ;
if ( volume > F1_0 )
volume = F1_0 ;
digi_change_looping_volume ( volume ) ;
2019-08-06 02:59:41 +00:00
LevelUniqueSeismicState . Next_seismic_sound_time = GameTime64 + d_rand ( ) / 4 + 8192 ;
2006-03-20 17:12:09 +00:00
}
}
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2015-12-22 04:18:50 +00:00
}
2014-09-27 22:46:25 +00:00
DEFINE_BITMAP_SERIAL_UDT ( ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2020-12-19 16:13:26 +00:00
DEFINE_SERIAL_UDT_TO_MESSAGE ( dsx : : weapon_info , w , ( w . render , w . model_num , w . model_num_inner , w . persistent , w . flash_vclip , w . flash_sound , w . robot_hit_vclip , w . robot_hit_sound , w . wall_hit_vclip , w . wall_hit_sound , w . fire_count , w . ammo_usage , w . weapon_vclip , w . destroyable , w . matter , w . bounce , w . homing_flag , w . dum1 , w . dum2 , w . dum3 , w . energy_usage , w . fire_wait , w . bitmap , w . blob_size , w . flash_size , w . impact_size , w . strength , w . speed , w . mass , w . drag , w . thrust , w . po_len_to_width_ratio , w . light , w . lifetime , w . damage_radius , w . picture ) ) ;
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2014-09-27 22:46:25 +00:00
namespace {
2020-12-19 16:13:26 +00:00
struct v2_weapon_info : weapon_info
{
} ;
2014-09-27 22:46:25 +00:00
}
2006-03-20 17:12:09 +00:00
2014-09-27 22:46:25 +00:00
template < typename Accessor >
void postprocess_udt ( Accessor & , v2_weapon_info & w )
{
2015-12-03 03:26:49 +00:00
w . children = weapon_id_type : : unspecified ;
2014-09-27 22:46:25 +00:00
w . multi_damage_scale = F1_0 ;
w . hires_picture = w . picture ;
}
2006-03-20 17:12:09 +00:00
2020-08-24 01:31:28 +00:00
DEFINE_SERIAL_UDT_TO_MESSAGE ( v2_weapon_info , w , ( w . render , w . persistent , w . model_num , w . model_num_inner , w . flash_vclip , w . robot_hit_vclip , w . flash_sound , w . wall_hit_vclip , w . fire_count , w . robot_hit_sound , w . ammo_usage , w . weapon_vclip , w . wall_hit_sound , w . destroyable , w . matter , w . bounce , w . homing_flag , w . speedvar , w . flags , w . flash , w . afterburner_size , w . energy_usage , w . fire_wait , w . bitmap , w . blob_size , w . flash_size , w . impact_size , w . strength , w . speed , w . mass , w . drag , w . thrust , w . po_len_to_width_ratio , w . light , w . lifetime , w . damage_radius , w . picture ) ) ;
DEFINE_SERIAL_UDT_TO_MESSAGE ( weapon_info , w , ( w . render , w . persistent , w . model_num , w . model_num_inner , w . flash_vclip , w . robot_hit_vclip , w . flash_sound , w . wall_hit_vclip , w . fire_count , w . robot_hit_sound , w . ammo_usage , w . weapon_vclip , w . wall_hit_sound , w . destroyable , w . matter , w . bounce , w . homing_flag , w . speedvar , w . flags , w . flash , w . afterburner_size , w . children , w . energy_usage , w . fire_wait , w . multi_damage_scale , w . bitmap , w . blob_size , w . flash_size , w . impact_size , w . strength , w . speed , w . mass , w . drag , w . thrust , w . po_len_to_width_ratio , w . light , w . lifetime , w . damage_radius , w . picture , w . hires_picture ) ) ;
2014-09-27 22:46:25 +00:00
# endif
2006-03-20 17:12:09 +00:00
2015-12-04 03:36:31 +00:00
#if 0
2014-09-27 22:46:25 +00:00
void weapon_info_write ( PHYSFS_File * fp , const weapon_info & w )
{
PHYSFSX_serialize_write ( fp , w ) ;
}
2015-12-04 03:36:31 +00:00
# endif
2006-03-20 17:12:09 +00:00
2014-09-27 22:46:25 +00:00
/*
2016-01-09 16:38:14 +00:00
* reads n weapon_info structs from a PHYSFS_File
2014-09-27 22:46:25 +00:00
*/
2016-01-09 16:38:15 +00:00
namespace dsx {
2014-09-27 22:46:25 +00:00
void weapon_info_read_n ( weapon_info_array & wi , std : : size_t count , PHYSFS_File * fp , int file_version , std : : size_t offset )
{
auto r = partial_range ( wi , offset , count ) ;
2015-01-18 01:58:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
( void ) file_version ;
# elif defined(DXX_BUILD_DESCENT_II)
2014-09-27 17:55:46 +00:00
if ( file_version < 3 )
{
2014-09-27 22:46:25 +00:00
range_for ( auto & w , r )
PHYSFSX_serialize_read ( fp , static_cast < v2_weapon_info & > ( w ) ) ;
2014-09-27 17:55:46 +00:00
/* Set the type of children correctly when using old
* datafiles . In earlier descent versions this was simply
* hard - coded in create_smart_children ( ) .
*/
2015-12-03 03:26:49 +00:00
wi [ weapon_id_type : : SMART_ID ] . children = weapon_id_type : : PLAYER_SMART_HOMING_ID ;
wi [ weapon_id_type : : SUPERPROX_ID ] . children = weapon_id_type : : SMART_MINE_HOMING_ID ;
2014-09-27 22:46:25 +00:00
return ;
2014-09-27 17:55:46 +00:00
}
# endif
2014-09-27 22:46:25 +00:00
range_for ( auto & w , r )
{
PHYSFSX_serialize_read ( fp , w ) ;
}
2006-03-20 17:12:09 +00:00
}
2016-01-09 16:38:15 +00:00
}