Use enum class for next_level_request_secret_flag

Instead of passing a bare `int` named `secret_flag`, define it as an
`enum class : uint8_t` to name the two special values.

Rework the passing of this value, to deal with some confusing
inconsistencies when reading the code.

Before this change:
In D1:
- Multiplayer will always go to the secret level, regardless of which
  exit door the player used.
In D2:
- Flying through a D1 secret exit in multiplayer shows the on-HUD error
  "Secret Level Teleporter disabled in multiplayer!", and does not exit
  the level.  This is at best confusing, and at worst dangerous, since
  D1 secret exits are only available during the countdown, so the player
  has little time to realize that the normal exit must be used instead.
- Like D1, multiplayer will request to go to the secret level regardless
  of the exit used.  Unlike D1, the caller ignores the flag and always
  advances to the next regular level.

After this change:
- No observable differences for the player in-game.  The questionable D2
  secret exit handling for D1 is retained.
- The code makes clearer that secret exits do not work in D2
  multiplayer, by way of `#if defined(DXX_BUILD_DESCENT_I)` guarding the
  existence of the parameter and all updates to it.
This commit is contained in:
Kp 2022-07-16 15:26:12 +00:00
parent f3d31a6d27
commit b737524415
8 changed files with 69 additions and 34 deletions

View file

@ -153,6 +153,8 @@ void calc_frame_time(void);
#ifdef dsx
namespace dsx {
enum class next_level_request_secret_flag : uint8_t;
struct game_window;
extern game_window *Game_wind;

View file

@ -34,7 +34,7 @@ COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
#include "fwd-window.h"
#include "powerup.h"
#if defined(DXX_BUILD_DESCENT_I) || defined(DXX_BUILD_DESCENT_II)
#ifdef dsx
namespace dcx {
template <std::size_t>
@ -58,33 +58,43 @@ extern int Player_highest_level;
//
// starts a new game on the given level
#ifdef dsx
namespace dsx {
enum class next_level_request_secret_flag : uint8_t
{
only_normal_level,
#if defined(DXX_BUILD_DESCENT_I)
use_secret,
#endif
};
void StartNewGame(int start_level);
// starts the next level
window_event_result StartNewLevel(int level_num);
}
#endif
void InitPlayerObject(); //make sure player's object set up
namespace dsx {
void init_player_stats_game(playernum_t pnum); //clear all stats
}
// called when the player has finished a level
// if secret flag is true, advance to secret level, else next normal level
#ifdef dsx
namespace dsx {
window_event_result PlayerFinishedLevel(int secret_flag);
window_event_result PlayerFinishedLevel(
#if defined(DXX_BUILD_DESCENT_I)
next_level_request_secret_flag secret_flag
#endif
);
// called when the player has died
window_event_result DoPlayerDead(void);
#if defined(DXX_BUILD_DESCENT_I)
#define gameseq_remove_unused_players(Robot_info) gameseq_remove_unused_players()
#undef PlayerFinishedLevel
#elif defined(DXX_BUILD_DESCENT_II)
#undef gameseq_remove_unused_players
#define PlayerFinishedLevel(secret_flag) ((void)secret_flag,PlayerFinishedLevel())
// load just the hxm file
void load_level_robots(int level_num);
void load_level_robots(const d_fname &level_name);
@ -117,7 +127,6 @@ namespace dsx {
void create_player_appearance_effect(const d_vclip_array &Vclip, const object_base &player_obj);
void bash_to_shield(const d_powerup_info_array &Powerup_info, const d_vclip_array &Vclip, object_base &i);
}
#endif
// Show endlevel bonus scores
namespace dcx {

View file

@ -241,7 +241,11 @@ struct dispatch_table
virtual void send_endlevel_packet() const = 0;
virtual void kick_player(const _sockaddr &dump_addr, int why) const = 0;
virtual void disconnect_player(int playernum) const = 0;
virtual int end_current_level(int *secret) const = 0;
virtual int end_current_level(
#if defined(DXX_BUILD_DESCENT_I)
next_level_request_secret_flag *secret
#endif
) const = 0;
virtual void leave_game() const = 0;
};
}

View file

@ -33,7 +33,11 @@ struct dispatch_table final : multi::dispatch_table
virtual void send_endlevel_packet() const override;
virtual void kick_player(const _sockaddr &dump_addr, int why) const override;
virtual void disconnect_player(int playernum) const override;
virtual int end_current_level(int *secret) const override;
virtual int end_current_level(
#if defined(DXX_BUILD_DESCENT_I)
next_level_request_secret_flag *secret
#endif
) const override;
virtual void leave_game() const override;
};

View file

@ -723,8 +723,7 @@ window_event_result start_endlevel_sequence()
if (!(!(Game_mode & GM_MULTI) && (endlevel_movie_played == movie_play_status::skipped) && endlevel_data_loaded))
#endif
{
return PlayerFinishedLevel(0); //done with level
return PlayerFinishedLevel(next_level_request_secret_flag::only_normal_level); //done with level
}
#if defined(DXX_BUILD_DESCENT_II)
int exit_models_loaded = 0;
@ -747,7 +746,7 @@ window_event_result start_endlevel_sequence()
const auto tunnel_length = get_tunnel_length(vcsegptridx, console_seg, exit_console_side);
if (!tunnel_length)
{
return PlayerFinishedLevel(0); //don't do special sequence
return PlayerFinishedLevel(next_level_request_secret_flag::only_normal_level); //don't do special sequence
}
//now pick transition segnum 1/3 of the way in
@ -793,7 +792,7 @@ window_event_result stop_endlevel_sequence()
select_cockpit(PlayerCfg.CockpitMode[0]);
Endlevel_sequence = EL_OFF;
return PlayerFinishedLevel(0);
return PlayerFinishedLevel(next_level_request_secret_flag::only_normal_level);
}
#define VCLIP_BIG_PLAYER_EXPLOSION 58

View file

@ -115,9 +115,10 @@ COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
namespace d1x {
namespace {
static int8_t find_next_level(const int secret_flag, const int current_level_num, const Mission &mission)
static int8_t find_next_level(const next_level_request_secret_flag secret_flag, const int current_level_num, const Mission &mission)
{
if (secret_flag) { //go to secret level instead
if (secret_flag != next_level_request_secret_flag::only_normal_level)
{ //go to secret level instead
for (const auto &&[idx, table_entry] : enumerate(
unchecked_partial_range(
mission.secret_level_table.get(),
@ -221,7 +222,14 @@ static unsigned get_starting_concussion_missile_count()
namespace dsx {
namespace {
static void init_player_stats_ship(object &, fix GameTime64);
static window_event_result AdvanceLevel(int secret_flag);
static window_event_result AdvanceLevel(
#if defined(DXX_BUILD_DESCENT_I)
#undef AdvanceLevel
next_level_request_secret_flag secret_flag
#elif defined(DXX_BUILD_DESCENT_II)
#define AdvanceLevel(secret_flag) ((void)secret_flag,AdvanceLevel())
#endif
);
static void StartLevel(int random_flag);
static void copy_defaults_to_robot_all(const d_robot_info_array &Robot_info);
@ -1658,7 +1666,11 @@ void EnterSecretLevel(void)
#endif
//called when the player has finished a level
window_event_result PlayerFinishedLevel(int secret_flag)
window_event_result (PlayerFinishedLevel)(
#if defined(DXX_BUILD_DESCENT_I)
const next_level_request_secret_flag secret_flag
#endif
)
{
auto &Objects = LevelUniqueObjectState.Objects;
auto &vmobjptr = Objects.vmptr;
@ -1670,7 +1682,7 @@ window_event_result PlayerFinishedLevel(int secret_flag)
auto &player_info = get_local_plrobj().ctype.player_info;
player_info.mission.hostages_rescued_total += player_info.mission.hostages_on_board;
#if defined(DXX_BUILD_DESCENT_I)
if (!(Game_mode & GM_MULTI) && (secret_flag)) {
if (!(Game_mode & GM_MULTI) && secret_flag != next_level_request_secret_flag::only_normal_level) {
using items_type = std::array<newmenu_item, 1>;
struct message_menu : items_type, passive_newmenu
{
@ -1685,7 +1697,7 @@ window_event_result PlayerFinishedLevel(int secret_flag)
run_blocking_newmenu<message_menu>(*grd_curcanv);
}
#elif defined(DXX_BUILD_DESCENT_II)
Assert(!secret_flag);
constexpr auto secret_flag = next_level_request_secret_flag::only_normal_level;
#endif
if (Game_mode & GM_NETWORK)
get_local_player().connected = CONNECT_WAITING; // Finished but did not die
@ -1762,14 +1774,16 @@ static void DoEndGame()
//called to go to the next level (if there is one)
//if secret_flag is true, advance to secret level, else next normal one
// Return true if game over.
static window_event_result AdvanceLevel(int secret_flag)
static window_event_result (AdvanceLevel)(
#if defined(DXX_BUILD_DESCENT_I)
next_level_request_secret_flag secret_flag
#endif
)
{
auto &LevelUniqueControlCenterState = LevelUniqueObjectState.ControlCenterState;
auto rval = window_event_result::handled;
#if defined(DXX_BUILD_DESCENT_II)
Assert(!secret_flag);
// Loading a level can write over homing_flag.
// So for simplicity, reset the homing weapon cheat here.
if (cheats.homingfire)
@ -1796,7 +1810,11 @@ static window_event_result AdvanceLevel(int secret_flag)
if (Game_mode & GM_MULTI)
{
int result;
result = multi::dispatch->end_current_level(&secret_flag); // Wait for other players to reach this point
result = multi::dispatch->end_current_level(
#if defined(DXX_BUILD_DESCENT_I)
&secret_flag
#endif
); // Wait for other players to reach this point
if (result) // failed to sync
{
// check if player has finished the game
@ -1906,7 +1924,7 @@ window_event_result DoPlayerDead()
const auto g = Game_wind;
if (g)
g->set_visible(0);
result = AdvanceLevel(0); //if finished, go on to next level
result = AdvanceLevel(next_level_request_secret_flag::only_normal_level); //if finished, go on to next level
init_player_stats_new_ship(Player_num);
last_drawn_cockpit = cockpit_mode_t{UINT8_MAX};

View file

@ -1660,26 +1660,25 @@ namespace dsx {
namespace multi {
namespace udp {
int dispatch_table::end_current_level(int *secret) const
int dispatch_table::end_current_level(
#if defined(DXX_BUILD_DESCENT_I)
next_level_request_secret_flag *const secret
#endif
) const
{
// Do whatever needs to be done between levels
#if defined(DXX_BUILD_DESCENT_II)
if (EMULATING_D1)
#endif
#if defined(DXX_BUILD_DESCENT_I)
{
// We do not really check if a player has actually found a secret level... yeah, I am too lazy! So just go there and pretend we did!
range_for (const auto i, unchecked_partial_range(Current_mission->secret_level_table.get(), Current_mission->n_secret_levels))
{
if (Current_level_num == i)
{
*secret = 1;
*secret = next_level_request_secret_flag::use_secret;
break;
}
}
}
#if defined(DXX_BUILD_DESCENT_II)
else
*secret = 0;
#endif
Network_status = network_state::endlevel; // We are between levels

View file

@ -347,7 +347,7 @@ window_event_result check_trigger_sub(object &plrobj, const trgnum_t trigger_num
multi_send_endlevel_start(multi_endlevel_type::secret);
if (Game_mode & GM_NETWORK)
multi::dispatch->do_protocol_frame(1, 1);
result = std::max(PlayerFinishedLevel(1), result); //1 means go to secret level
result = std::max(PlayerFinishedLevel(next_level_request_secret_flag::use_secret), result);
LevelUniqueControlCenterState.Control_center_destroyed = 0;
return std::max(result, window_event_result::handled);
}