diff --git a/CMakeLists.txt b/CMakeLists.txt index 2cfef6281..c74eaba98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,6 +76,8 @@ option(SRB2_CONFIG_HWRENDER "Enable hardware render (OpenGL) support" ON) option(SRB2_CONFIG_STATIC_OPENGL "Enable static linking GL (do not do this)" OFF) option(SRB2_CONFIG_ERRORMODE "Compile C code with warnings treated as errors." OFF) option(SRB2_CONFIG_DEBUGMODE "Compile with PARANOIA, ZDEBUG, RANGECHECK and PACKETDROP defined." OFF) +option(SRB2_CONFIG_DEV_BUILD "Compile a development build." OFF) +option(SRB2_CONFIG_TESTERS "Compile a build for testers." OFF) option(SRB2_CONFIG_MOBJCONSISTANCY "Compile with MOBJCONSISTANCY defined." OFF) option(SRB2_CONFIG_PACKETDROP "Compile with PACKETDROP defined." OFF) option(SRB2_CONFIG_ZDEBUG "Compile with ZDEBUG defined." OFF) @@ -202,6 +204,10 @@ if("${SRB2_SDL2_EXE_NAME}" STREQUAL "") if(NOT "${SRB2_GIT_REVISION}" STREQUAL "master") list(APPEND EXE_NAME_PARTS ${SRB2_GIT_REVISION}) endif() + + if (SRB2_CONFIG_TESTERS) + list(APPEND EXE_NAME_PARTS "TESTERS") + endif() else() list(APPEND EXE_NAME_PARTS ${SRB2_SDL2_EXE_NAME}) endif() diff --git a/CMakePresets.json b/CMakePresets.json index 4ff081636..5cdf3d1bd 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -33,14 +33,6 @@ "SRB2_CONFIG_TESTERS": "ON" } }, - { - "name": "host-testers", - "description": "Build to use when hosting, to let testers join", - "inherits": "default", - "cacheVariables": { - "SRB2_CONFIG_HOSTTESTERS": "ON" - } - }, { "name": "release", "description": "Build for game's release", diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 917020a75..b7cce51f6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -205,12 +205,6 @@ set(SRB2_CONFIG_USEASM OFF CACHE BOOL "Enable NASM tmap implementation for software mode speedup.") set(SRB2_CONFIG_YASM OFF CACHE BOOL "Use YASM in place of NASM.") -set(SRB2_CONFIG_DEV_BUILD OFF CACHE BOOL - "Compile a development build of Dr Robotnik's Ring Racers.") -set(SRB2_CONFIG_TESTERS OFF CACHE BOOL - "Compile a build for testers.") -set(SRB2_CONFIG_HOSTTESTERS OFF CACHE BOOL - "Compile a build to host netgames for testers builds.") add_subdirectory(blua) @@ -556,9 +550,6 @@ endif() if(SRB2_CONFIG_TESTERS) target_compile_definitions(SRB2SDL2 PRIVATE -DTESTERS) endif() -if(SRB2_CONFIG_HOSTTESTERS) - target_compile_definitions(SRB2SDL2 PRIVATE -DHOSTTESTERS) -endif() if(SRB2_CONFIG_MOBJCONSISTANCY) target_compile_definitions(SRB2SDL2 PRIVATE -DMOBJCONSISTANCY) endif() diff --git a/src/Makefile.d/features.mk b/src/Makefile.d/features.mk index c8e43b6ea..7290de68b 100644 --- a/src/Makefile.d/features.mk +++ b/src/Makefile.d/features.mk @@ -6,7 +6,7 @@ passthru_opts+=\ NO_IPV6 NOHW NOMD5 NOPOSTPROCESSING\ MOBJCONSISTANCY PACKETDROP ZDEBUG\ HAVE_MINIUPNPC\ - HAVE_DISCORDRPC TESTERS HOSTTESTERS DEVELOP + HAVE_DISCORDRPC TESTERS DEVELOP # build with debugging information ifdef DEBUGMODE diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index 12fdd56c5..d87f2756e 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -2236,6 +2236,21 @@ bool CallFunc_MusicRemap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM:: return false; } +/*-------------------------------------------------- + bool CallFunc_Freeze(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) + + Updates level freeze. +--------------------------------------------------*/ +bool CallFunc_Freeze(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + (void)argV; + (void)argC; + + P_SetFreezeLevel(argV[0] != 0); + + return false; +} + /*-------------------------------------------------- bool CallFunc_Get/SetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) diff --git a/src/acs/call-funcs.hpp b/src/acs/call-funcs.hpp index 3bfd32548..89ab3d351 100644 --- a/src/acs/call-funcs.hpp +++ b/src/acs/call-funcs.hpp @@ -103,6 +103,8 @@ bool CallFunc_DialogueSetSpeaker(ACSVM::Thread *thread, const ACSVM::Word *argV, bool CallFunc_DialogueSetCustomSpeaker(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_DialogueNewText(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_Freeze(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); + bool CallFunc_GetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_SetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_GetSideProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); diff --git a/src/acs/environment.cpp b/src/acs/environment.cpp index d178c4873..48a16fdc8 100644 --- a/src/acs/environment.cpp +++ b/src/acs/environment.cpp @@ -181,6 +181,7 @@ Environment::Environment() addFuncDataACS0( 508, addCallFunc(CallFunc_MusicPlay)); addFuncDataACS0( 509, addCallFunc(CallFunc_MusicStopAll)); addFuncDataACS0( 510, addCallFunc(CallFunc_MusicRemap)); + addFuncDataACS0( 511, addCallFunc(CallFunc_Freeze)); addFuncDataACS0( 600, addCallFunc(CallFunc_DialogueSetSpeaker)); addFuncDataACS0( 601, addCallFunc(CallFunc_DialogueSetCustomSpeaker)); diff --git a/src/cvars.cpp b/src/cvars.cpp index 2dad5faef..5e56394ef 100644 --- a/src/cvars.cpp +++ b/src/cvars.cpp @@ -421,6 +421,10 @@ consvar_t cv_scr_depth = Player("scr_depth", "16 bits").values({{8, "8 bits"}, { consvar_t cv_scr_width = Player("scr_width", "640").values(CV_Unsigned); consvar_t cv_scr_height = Player("scr_height", "400").values(CV_Unsigned); +consvar_t cv_scr_scale = Player("scr_scale", "1.0").floating_point(); +consvar_t cv_scr_x = Player("scr_x", "0.0").floating_point(); +consvar_t cv_scr_y = Player("scr_y", "0.0").floating_point(); + consvar_t cv_seenames = Player("seenames", "On").on_off(); consvar_t cv_shadow = Player("shadow", "On").on_off(); consvar_t cv_shittyscreen = Player("televisionsignal", "Okay").flags(CV_NOSHOWHELP).values({{0, "Okay"}, {1, "Shitty"}, {2, "Extra Shitty"}}).dont_save(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 45076986f..e5f461985 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -419,6 +419,9 @@ void D_RegisterServerCommands(void) COM_AddDebugCommand("downloads", Command_Downloads_f); COM_AddDebugCommand("give", Command_KartGiveItem_f); + COM_AddDebugCommand("give2", Command_KartGiveItem_f); + COM_AddDebugCommand("give3", Command_KartGiveItem_f); + COM_AddDebugCommand("give4", Command_KartGiveItem_f); COM_AddCommand("schedule_add", Command_Schedule_Add); COM_AddCommand("schedule_clear", Command_Schedule_Clear); @@ -534,6 +537,7 @@ void D_RegisterClientCommands(void) // add cheats COM_AddDebugCommand("noclip", Command_CheatNoClip_f); COM_AddDebugCommand("god", Command_CheatGod_f); + COM_AddDebugCommand("freeze", Command_CheatFreeze_f); COM_AddDebugCommand("setrings", Command_Setrings_f); COM_AddDebugCommand("setspheres", Command_Setspheres_f); COM_AddDebugCommand("setlives", Command_Setlives_f); @@ -1605,6 +1609,22 @@ static void GetViewablePlayerPlaceRange(INT32 *first, INT32 *last) } } +static int GetCommandViewNumber(void) +{ + char c = COM_Argv(0)[strlen(COM_Argv(0))-1];/* may be digit */ + + switch (c) + { + default: + return 0; + + case '2': + case '3': + case '4': + return c - '1'; + } +} + #define PRINTVIEWPOINT( pre,suf ) \ CONS_Printf(pre"viewing \x84(%d) \x83%s\x80"suf".\n",\ (*displayplayerp), player_names[(*displayplayerp)]); @@ -1612,21 +1632,11 @@ static void Command_View_f(void) { INT32 *displayplayerp; INT32 olddisplayplayer; - int viewnum; + int viewnum = 1 + GetCommandViewNumber(); const char *playerparam; INT32 placenum; INT32 playernum; INT32 firstplace, lastplace; - char c; - /* easy peasy */ - c = COM_Argv(0)[strlen(COM_Argv(0))-1];/* may be digit */ - switch (c) - { - case '2': viewnum = 2; break; - case '3': viewnum = 3; break; - case '4': viewnum = 4; break; - default: viewnum = 1; - } if (viewnum > 1 && !( multiplayer && demo.playback )) { @@ -1709,13 +1719,6 @@ static void Command_SetViews_f(void) UINT8 splits; UINT8 newsplits; - if (!( demo.playback && multiplayer )) - { - CONS_Alert(CONS_NOTICE, - "You must be viewing a multiplayer replay to use this.\n"); - return; - } - if (COM_Argc() != 2) { CONS_Printf("setviews : set the number of split screens\n"); @@ -1726,12 +1729,26 @@ static void Command_SetViews_f(void) newsplits = atoi(COM_Argv(1)); newsplits = min(max(newsplits, 1), 4); - if (newsplits > splits) + + if (newsplits > splits && demo.playback && multiplayer) + { G_AdjustView(newsplits, 0, true); + } else { + // Even if the splits go beyond the real number of + // splitscreen players, displayplayers was filled + // with duplicates of P1 (see Got_AddPlayer). r_splitscreen = newsplits-1; R_ExecuteSetViewSize(); + + // If promoting (outside of replays), make sure the + // camera is in the correct position. + UINT8 i; + for (i = splits + 1; i <= newsplits; ++i) + { + G_FixCamera(i); + } } } @@ -3687,7 +3704,7 @@ static void Command_Login_f(void) boolean IsPlayerAdmin(INT32 playernum) { -#if defined(DEVELOP) && !(defined(HOSTTESTERS) || defined(TESTERS)) +#if 0 // defined(DEVELOP) return playernum != serverplayer; #else INT32 i; @@ -4642,8 +4659,6 @@ static void Command_Version_f(void) // DEVELOP build #if defined(TESTERS) CONS_Printf("\x88" "TESTERS " "\x80"); -#elif defined(HOSTTESTERS) - CONS_Printf("\x82" "HOSTTESTERS " "\x80"); #elif defined(DEVELOP) CONS_Printf("\x87" "DEVELOP " "\x80"); #endif @@ -5737,6 +5752,13 @@ static void Got_Cheat(UINT8 **cp, INT32 playernum) break; } + case CHEAT_FREEZE: { + const char *status = P_FreezeCheat() ? "off" : "on"; + P_SetFreezeCheat( !P_FreezeCheat() ); + CV_CheaterWarning(targetPlayer, va("freeze %s", status)); + break; + } + case NUMBER_OF_CHEATS: break; } @@ -5911,6 +5933,8 @@ static void Command_Archivetest_f(void) */ static void Command_KartGiveItem_f(void) { + UINT8 localplayer = g_localplayers[GetCommandViewNumber()]; + int ac; const char *name; INT32 item; @@ -5966,7 +5990,7 @@ static void Command_KartGiveItem_f(void) else amt = BATTLE_POWERUP_TIME; - D_Cheat(consoleplayer, CHEAT_GIVEPOWERUP, item, amt); + D_Cheat(localplayer, CHEAT_GIVEPOWERUP, item, amt); } else if (item < NUMKARTITEMS) { @@ -5977,7 +6001,7 @@ static void Command_KartGiveItem_f(void) else amt = (item != KITEM_NONE);/* default to one quantity, or zero, if KITEM_NONE */ - D_Cheat(consoleplayer, CHEAT_GIVEITEM, item, amt); + D_Cheat(localplayer, CHEAT_GIVEITEM, item, amt); } else { diff --git a/src/deh_tables.c b/src/deh_tables.c index 1d8d4eaec..3b0ec401d 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -289,6 +289,7 @@ actionpointer_t actionpointers[] = {{A_SSMineFlash}, "A_SSMINEFLASH"}, {{A_LandMineExplode}, "A_LANDMINEEXPLODE"}, {{A_BallhogExplode}, "A_BALLHOGEXPLODE"}, + {{A_SpecialStageBombExplode},"A_SPECIALSTAGEBOMBEXPLODE"}, {{A_LightningFollowPlayer}, "A_LIGHTNINGFOLLOWPLAYER"}, {{A_FZBoomFlash}, "A_FZBOOMFLASH"}, {{A_FZBoomSmoke}, "A_FZBOOMSMOKE"}, @@ -4372,6 +4373,17 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_EERIEFOG4", "S_EERIEFOG5", + // Chaos Chute + "S_SPECIALSTAGEARCH", + "S_SPECIALSTAGEBOMB", + "S_SPECIALSTAGEBOMB_DISARM", + "S_SPECIALSTAGEBOMB_EXPLODE", + "S_SPECIALSTAGEBOMB_DISAPPEAR", + "S_SPECIALSTAGEBOMB_FLICKER1", + "S_SPECIALSTAGEBOMB_FLICKER2", + "S_SPECIALSTAGEBOMB_FLICKERLOOP", + "S_SPECIALSTAGEBOMB_RESET", + // SMK ports "S_SMK_PIPE1", // Generic pipes "S_SMK_PIPE2", @@ -4674,6 +4686,11 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_CHECKPOINT_SPARK9", "S_CHECKPOINT_SPARK10", "S_CHECKPOINT_SPARK11", + + "S_BALLSWITCH_BALL", + "S_BALLSWITCH_BALL_ACTIVE", + "S_BALLSWITCH_PAD", + "S_BALLSWITCH_PAD_ACTIVE", }; // RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1", @@ -5731,6 +5748,10 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_EERIEFOG", "MT_EERIEFOGGEN", + // Chaos Chute + "MT_SPECIALSTAGEARCH", + "MT_SPECIALSTAGEBOMB", + // SMK ports "MT_SMK_PIPE", "MT_SMK_MOLESPAWNER", @@ -5827,6 +5848,9 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_CHECKPOINT_END", "MT_SCRIPT_THING", + + "MT_BALLSWITCH_BALL", + "MT_BALLSWITCH_PAD", }; const char *const MOBJFLAG_LIST[] = { diff --git a/src/f_finale.c b/src/f_finale.c index 4b4c87941..5ded47495 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1565,9 +1565,6 @@ void F_VersionDrawer(void) #if defined(TESTERS) addtext(V_SKYMAP, "Tester client"); addtext(V_TRANSLUCENT, va("%s", compdate)); -#elif defined(HOSTTESTERS) - addtext(V_REDMAP, "Netgame host for testers"); - addtext(V_TRANSLUCENT, va("%s", compdate)); #elif defined(DEVELOP) addtext(V_TRANSLUCENT, va("%s %s", comprevision, compnote)); addtext(V_TRANSLUCENT, D_GetFancyBranchName()); @@ -1711,8 +1708,6 @@ void F_TitleScreenDrawer(void) #ifdef DEVELOP #if defined(TESTERS) V_DrawCenteredString(BASEVIDWIDTH/2, 96, V_SKYMAP, "Tester EXE"); -#elif defined(HOSTTESTERS) - V_DrawCenteredThinString(BASEVIDWIDTH/2, 96, V_REDMAP, "Tester netgame host EXE"); #else V_DrawCenteredString(BASEVIDWIDTH/2, 96, 0, "Development EXE"); #endif diff --git a/src/f_wipe.cpp b/src/f_wipe.cpp index 39a7b2aa4..6a8587586 100644 --- a/src/f_wipe.cpp +++ b/src/f_wipe.cpp @@ -432,7 +432,7 @@ void F_WipeEndScreen(void) dst_region.h = std::min(dst_region.h, backbuf_deets.height); rhi->copy_framebuffer_to_texture(ctx, hw_state->wipe_frames.end, dst_region, dst_region); - hw_state->blit_rect->set_output(dst_region.w, dst_region.h, false, true); + hw_state->blit_rect->set_output(0, 0, dst_region.w, dst_region.h, false, true); rhi::TextureDetails start_deets = rhi->get_texture_details(hw_state->wipe_frames.start); hw_state->blit_rect->set_texture(hw_state->wipe_frames.start, start_deets.width, start_deets.height); hw_state->blit_rect->draw(*rhi, ctx); diff --git a/src/g_build_ticcmd.cpp b/src/g_build_ticcmd.cpp index 54f5069af..b81c205e4 100644 --- a/src/g_build_ticcmd.cpp +++ b/src/g_build_ticcmd.cpp @@ -206,7 +206,15 @@ class TiccmdBuilder else #endif { - localangle[viewnum] += angleChange; + int p = g_localplayers[forplayer()]; + + for (int i = 0; i <= r_splitscreen; ++i) + { + if (displayplayers[i] == p) + { + localangle[i] += angleChange; + } + } } } @@ -237,7 +245,7 @@ class TiccmdBuilder bool director_input() { - if (demo.freecam || G_IsPartyLocal(displayplayers[forplayer()]) == true) + if (demo.freecam || !K_DirectorIsAvailable(viewnum)) { return false; } diff --git a/src/g_game.c b/src/g_game.c index de0711af5..ce2f31a00 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -89,7 +89,7 @@ JoyType_t Joystick[MAXSPLITSCREENPLAYERS]; // SRB2kart char gamedatafilename[64] = -#if defined (TESTERS) || defined (HOSTTESTERS) +#if defined (TESTERS) "test" #elif defined(DEVELOP) "develop" @@ -1494,13 +1494,15 @@ boolean G_CouldView(INT32 playernum) // boolean G_CanView(INT32 playernum, UINT8 viewnum, boolean onlyactive) { + if (!playeringame[playernum] || players[playernum].spectator) + { + return false; + } + UINT8 splits; UINT8 viewd; INT32 *displayplayerp; - if (!(onlyactive ? G_CouldView(playernum) : (playeringame[playernum] && !players[playernum].spectator))) - return false; - splits = r_splitscreen+1; if (viewnum > splits) viewnum = splits; @@ -1509,15 +1511,18 @@ boolean G_CanView(INT32 playernum, UINT8 viewnum, boolean onlyactive) { displayplayerp = (&displayplayers[viewd-1]); if ((*displayplayerp) == playernum) - return false; + return true; } for (viewd = viewnum + 1; viewd <= splits; ++viewd) { displayplayerp = (&displayplayers[viewd-1]); if ((*displayplayerp) == playernum) - return false; + return true; } + if (onlyactive && !G_CouldView(playernum)) + return false; + return true; } @@ -1567,7 +1572,6 @@ void G_ResetView(UINT8 viewnum, INT32 playernum, boolean onlyactive) UINT8 viewd; INT32 *displayplayerp; - camera_t *camerap; INT32 olddisplayplayer; INT32 playersviewable; @@ -1618,22 +1622,14 @@ void G_ResetView(UINT8 viewnum, INT32 playernum, boolean onlyactive) (*displayplayerp) = playernum; if ((*displayplayerp) != olddisplayplayer) { - camerap = &camera[viewnum-1]; - P_ResetCamera(&players[(*displayplayerp)], camerap); - - R_ResetViewInterpolation(viewnum); + G_FixCamera(viewnum); } if (viewnum > splits) { for (viewd = splits+1; viewd < viewnum; ++viewd) { - displayplayerp = (&displayplayers[viewd-1]); - camerap = &camera[viewd]; - - (*displayplayerp) = G_FindView(0, viewd, onlyactive, false); - - P_ResetCamera(&players[(*displayplayerp)], camerap); + G_FixCamera(viewd); } } @@ -1695,6 +1691,26 @@ void G_ResetViews(void) } } +// +// G_FixCamera +// Reset camera position, angle and interpolation on a view +// after changing state. +// +void G_FixCamera(UINT8 view) +{ + player_t *player = &players[displayplayers[view - 1]]; + + // The order of displayplayers can change, which would + // invalidate localangle. + localangle[view - 1] = player->angleturn; + + P_ResetCamera(player, &camera[view - 1]); + + // Make sure the viewport doesn't interpolate at all into + // its new position -- just snap instantly into place. + R_ResetViewInterpolation(view); +} + // // G_Ticker // Make ticcmd_ts for the players. diff --git a/src/g_game.h b/src/g_game.h index 885c7a44b..eeda11a79 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -240,6 +240,7 @@ INT32 G_CountPlayersPotentiallyViewable(boolean active); void G_ResetViews(void); void G_ResetView(UINT8 viewnum, INT32 playernum, boolean onlyactive); void G_AdjustView(UINT8 viewnum, INT32 offset, boolean onlyactive); +void G_FixCamera(UINT8 viewnum); void G_AddPlayer(INT32 playernum); void G_SpectatePlayerOnJoin(INT32 playernum); diff --git a/src/g_party.cpp b/src/g_party.cpp index 2b72d49f0..0caad76b7 100644 --- a/src/g_party.cpp +++ b/src/g_party.cpp @@ -19,7 +19,7 @@ #include "d_clisrv.h" // playerconsole #include "doomdef.h" // MAXPLAYERS #include "doomstat.h" // consoleplayer -#include "g_game.h" // localangle +#include "g_game.h" // G_FixCamera #include "g_party.h" #include "g_state.h" #include "p_local.h" @@ -136,20 +136,8 @@ public: for (std::size_t i = 0; i < size(); ++i) { - const playernum_t player = at(i); - - displayplayers[i] = player; - - // The order of displayplayers can change, which - // would make localangle invalid now. - localangle[i] = players[player].angleturn; - - P_ResetCamera(&players[player], &camera[i]); - - // Make sure the viewport doesn't interpolate at - // all into its new position -- just snap - // instantly into place. - R_ResetViewInterpolation(1 + i); + displayplayers[i] = at(i); + G_FixCamera(1 + i); } r_splitscreen = size() - 1; diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index a16c8b447..b8cfc5784 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -5398,17 +5398,7 @@ static void HWR_DrawSkyBackground(player_t *player) { FTransform dometransform; const float fpov = FIXED_TO_FLOAT(cv_fov[viewssnum].value+player->fovadd); - postimg_t *type = &postimgtype[0]; - SINT8 i; - - for (i = r_splitscreen; i >= 0; i--) - { - if (player == &players[displayplayers[i]]) - { - type = &postimgtype[i]; - break; - } - } + postimg_t *type = &postimgtype[R_GetViewNumber()]; memset(&dometransform, 0x00, sizeof(FTransform)); @@ -6104,20 +6094,10 @@ INT32 HWR_GetTextureUsed(void) void HWR_DoPostProcessor(player_t *player) { - postimg_t *type = &postimgtype[0]; - SINT8 i; + postimg_t *type = &postimgtype[R_GetViewNumber()]; HWD.pfnUnSetShader(); - for (i = r_splitscreen; i >= 0; i--) - { - if (player == &players[displayplayers[i]]) - { - type = &postimgtype[i]; - break; - } - } - // Armageddon Blast Flash! // Could this even be considered postprocessor? if (player->flashcount) diff --git a/src/hud/powerup.cpp b/src/hud/powerup.cpp index dfdabca15..d4cf264bc 100644 --- a/src/hud/powerup.cpp +++ b/src/hud/powerup.cpp @@ -90,10 +90,10 @@ void K_drawKartPowerUps(void) switch (r_splitscreen) { case 0: - return { make_drawer(307, 55, Draw::Font::kZVote), "PWRU", -17, 7, -35, -1 }; + return { make_drawer(307, 58, Draw::Font::kZVote), "PWRU", -17, 7, -35, -1 }; case 1: - return { make_drawer(318, viewnum == 0 ? 55 : 155, Draw::Font::kPing), "PWRS", -9, 6, -19, -1 }; + return { make_drawer(318, viewnum == 0 ? 58 : 147, Draw::Font::kPing), "PWRS", -9, 6, -19, -1 }; } // 3/4P diff --git a/src/hud/timer.cpp b/src/hud/timer.cpp index 12c09d592..98456e8dd 100644 --- a/src/hud/timer.cpp +++ b/src/hud/timer.cpp @@ -4,6 +4,7 @@ #include "../g_game.h" #include "../k_hud.h" #include "../p_local.h" +#include "../r_fps.h" #include "../v_draw.hpp" using srb2::Draw; @@ -24,13 +25,13 @@ void K_drawKart2PTimestamp(void) { auto get_row = [] { - if (stplyr == &players[displayplayers[0]]) + if (R_GetViewNumber() == 0) { - return Draw(286, 31).flags(V_SNAPTOTOP); + return Draw(287, 33).flags(V_SNAPTOTOP); } else { - return Draw(286, 163).flags(V_SNAPTOBOTTOM); + return Draw(287, 156).flags(V_SNAPTOBOTTOM); } }; diff --git a/src/hwr2/blit_rect.cpp b/src/hwr2/blit_rect.cpp index a5101e0f0..df3e791bb 100644 --- a/src/hwr2/blit_rect.cpp +++ b/src/hwr2/blit_rect.cpp @@ -100,7 +100,7 @@ void BlitRectPass::transfer(Rhi& rhi, Handle ctx) if (output_correct_aspect_) { aspect = static_cast(texture_width_) / static_cast(texture_height_); - output_aspect = static_cast(output_width_) / static_cast(output_height_); + output_aspect = static_cast(output_position_.w) / static_cast(output_position_.h); } bool taller = aspect > output_aspect; @@ -137,7 +137,7 @@ void BlitRectPass::transfer(Rhi& rhi, Handle ctx) void BlitRectPass::graphics(Rhi& rhi, Handle ctx) { rhi.bind_pipeline(ctx, pipeline_); - rhi.set_viewport(ctx, {0, 0, output_width_, output_height_}); + rhi.set_viewport(ctx, output_position_); rhi.bind_uniform_set(ctx, 0, uniform_sets_[0]); rhi.bind_uniform_set(ctx, 1, uniform_sets_[1]); rhi.bind_binding_set(ctx, binding_set_); diff --git a/src/hwr2/blit_rect.hpp b/src/hwr2/blit_rect.hpp index 014a876bb..f2acd9887 100644 --- a/src/hwr2/blit_rect.hpp +++ b/src/hwr2/blit_rect.hpp @@ -26,8 +26,7 @@ class BlitRectPass uint32_t texture_width_ = 0; uint32_t texture_height_ = 0; rhi::Handle output_; - uint32_t output_width_ = 0; - uint32_t output_height_ = 0; + rhi::Rect output_position_; bool output_correct_aspect_ = false; bool output_flip_ = false; rhi::Handle quad_vbo_; @@ -63,14 +62,15 @@ public: /// @param width texture width /// @param height texture height void set_output( + int32_t x, + int32_t y, uint32_t width, uint32_t height, bool correct_aspect, bool flip ) noexcept { - output_width_ = width; - output_height_ = height; + output_position_ = {x, y, width, height}; output_correct_aspect_ = correct_aspect; output_flip_ = flip; } diff --git a/src/hwr2/upscale_backbuffer.cpp b/src/hwr2/upscale_backbuffer.cpp index 0d3666e5c..f7e187388 100644 --- a/src/hwr2/upscale_backbuffer.cpp +++ b/src/hwr2/upscale_backbuffer.cpp @@ -37,6 +37,19 @@ void UpscaleBackbuffer::begin_pass(Rhi& rhi, Handle ctx) remake = true; } + auto new_renderpass = [&rhi = rhi](AttachmentLoadOp load_op, AttachmentStoreOp store_op) + { + RenderPassDesc desc {}; + desc.use_depth_stencil = true; + desc.color_load_op = load_op; + desc.color_store_op = store_op; + desc.depth_load_op = load_op; + desc.depth_store_op = store_op; + desc.stencil_load_op = load_op; + desc.stencil_store_op = store_op; + return rhi.create_render_pass(desc); + }; + if (remake) { if (color_) @@ -63,23 +76,22 @@ void UpscaleBackbuffer::begin_pass(Rhi& rhi, Handle ctx) depth_tex.height = vid_height; depth_ = rhi.create_renderbuffer(depth_tex); - } - if (!renderpass_) + if (!renderpass_clear_) + { + renderpass_clear_ = new_renderpass(AttachmentLoadOp::kClear, AttachmentStoreOp::kStore); + } + } + else { - RenderPassDesc desc {}; - desc.use_depth_stencil = true; - desc.color_load_op = AttachmentLoadOp::kLoad; - desc.color_store_op = AttachmentStoreOp::kStore; - desc.depth_load_op = AttachmentLoadOp::kLoad; - desc.depth_store_op = AttachmentStoreOp::kStore; - desc.stencil_load_op = AttachmentLoadOp::kLoad; - desc.stencil_store_op = AttachmentStoreOp::kStore; - renderpass_ = rhi.create_render_pass(desc); + if (!renderpass_) + { + renderpass_ = new_renderpass(AttachmentLoadOp::kLoad, AttachmentStoreOp::kStore); + } } RenderPassBeginInfo begin_info {}; - begin_info.render_pass = renderpass_; + begin_info.render_pass = remake ? renderpass_clear_ : renderpass_; begin_info.clear_color = {0, 0, 0, 1}; begin_info.color_attachment = color_; begin_info.depth_stencil_attachment = depth_; diff --git a/src/hwr2/upscale_backbuffer.hpp b/src/hwr2/upscale_backbuffer.hpp index d7b34a276..0f6903dd1 100644 --- a/src/hwr2/upscale_backbuffer.hpp +++ b/src/hwr2/upscale_backbuffer.hpp @@ -20,6 +20,7 @@ class UpscaleBackbuffer rhi::Handle color_; rhi::Handle depth_; rhi::Handle renderpass_; + rhi::Handle renderpass_clear_; public: UpscaleBackbuffer(); diff --git a/src/i_video_common.cpp b/src/i_video_common.cpp index 78eecf3fe..84241491a 100644 --- a/src/i_video_common.cpp +++ b/src/i_video_common.cpp @@ -16,8 +16,10 @@ #include #include +#include "command.h" #include "cxxutil.hpp" #include "f_finale.h" +#include "m_fixed.h" #include "m_misc.h" #include "hwr2/hardware_state.hpp" #include "hwr2/patch_atlas.hpp" @@ -46,6 +48,8 @@ #include "st_stuff.h" #include "v_video.h" +extern "C" consvar_t cv_scr_scale, cv_scr_x, cv_scr_y; + using namespace srb2; using namespace srb2::hwr2; using namespace srb2::rhi; @@ -103,62 +107,6 @@ static void postframe_update(Rhi& rhi) g_hw_state.palette_manager->destroy_per_frame_resources(rhi); } -#ifdef HWRENDER -static void finish_legacy_ogl_update() -{ - int player; - - SCR_CalculateFPS(); - - if (st_overlay) - { - if (cv_songcredits.value) - HU_DrawSongCredits(); - - if (cv_ticrate.value) - SCR_DisplayTicRate(); - - if (cv_showping.value && netgame && (consoleplayer != serverplayer || !server_lagless)) - { - if (server_lagless) - { - if (consoleplayer != serverplayer) - SCR_DisplayLocalPing(); - } - else - { - for (player = 1; player < MAXPLAYERS; player++) - { - if (D_IsPlayerHumanAndGaming(player)) - { - SCR_DisplayLocalPing(); - break; - } - } - } - } - if (cv_mindelay.value && consoleplayer == serverplayer && Playing()) - SCR_DisplayLocalPing(); - } - - if (marathonmode) - SCR_DisplayMarathonInfo(); - - // draw captions if enabled - if (cv_closedcaptioning.value) - SCR_ClosedCaptions(); - -#ifdef HAVE_DISCORDRPC - if (discordRequestList != NULL) - ST_AskToJoinEnvelope(); -#endif - - ST_drawDebugInfo(); - - OglSdlFinishUpdate(cv_vidwait.value); -} -#endif - static void temp_legacy_finishupdate_draws() { SCR_CalculateFPS(); @@ -211,6 +159,14 @@ static void temp_legacy_finishupdate_draws() ST_drawDebugInfo(); } +#ifdef HWRENDER +static void finish_legacy_ogl_update() +{ + temp_legacy_finishupdate_draws(); + OglSdlFinishUpdate(cv_vidwait.value); +} +#endif + static void new_twodee_frame() { g_2d = Twodee(); @@ -329,7 +285,20 @@ void I_FinishUpdate(void) rhi->begin_default_render_pass(ctx, true); // Upscale draw the backbuffer (with postprocessing maybe?) - g_hw_state.blit_rect->set_output(vid.realwidth, vid.realheight, true, true); + if (cv_scr_scale.value != FRACUNIT) + { + float f = std::max(FixedToFloat(cv_scr_scale.value), 0.f); + float w = vid.realwidth * f; + float h = vid.realheight * f; + float x = (vid.realwidth - w) * (0.5f + (FixedToFloat(cv_scr_x.value) * 0.5f)); + float y = (vid.realheight - h) * (0.5f + (FixedToFloat(cv_scr_y.value) * 0.5f)); + + g_hw_state.blit_rect->set_output(x, y, w, h, true, true); + } + else + { + g_hw_state.blit_rect->set_output(0, 0, vid.realwidth, vid.realheight, true, true); + } g_hw_state.blit_rect->set_texture(g_hw_state.backbuffer->color(), static_cast(vid.width), static_cast(vid.height)); g_hw_state.blit_rect->draw(*rhi, ctx); rhi->end_render_pass(ctx); diff --git a/src/info.c b/src/info.c index ef194ca3f..9967c61a1 100644 --- a/src/info.c +++ b/src/info.c @@ -798,6 +798,10 @@ char sprnames[NUMSPRITES + 1][5] = // Eerie Grove "EGFG", + // Chaos Chute + "SARC", + "SSBM", + // SMK ports "SMKP", "MTYM", @@ -893,6 +897,8 @@ char sprnames[NUMSPRITES + 1][5] = "CPT2", // Checkpoint Stick "CPT3", // Checkpoint Base + "SA2S", // SA2-style Ball Switch + // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later "VIEW", }; @@ -5092,6 +5098,17 @@ state_t states[NUMSTATES] = {SPR_EGFG, FF_TRANS90|FF_FULLBRIGHT|3, 7, {A_SetRandomTics}, 5, 9, S_EERIEFOG5}, // S_EERIEFOG4 {SPR_EGFG, FF_TRANS90|FF_FULLBRIGHT|4, 7, {A_SetRandomTics}, 5, 9, S_EERIEFOG1}, // S_EERIEFOG5 + // Chaos Chute + {SPR_SARC, FF_PAPERSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIALSTAGEARCH + {SPR_SSBM, FF_GLOBALANIM|FF_ANIMATE|0, -1, {NULL}, 3, 3, S_NULL}, // S_SPECIALSTAGEBOMB + {SPR_SSBM, 0, 1, {A_SetObjectFlags}, MF_NOCLIPTHING, 2, S_SPECIALSTAGEBOMB_EXPLODE}, // S_SPECIALSTAGEBOMB_DISARM + {SPR_NULL, 0, 0, {A_SpecialStageBombExplode}, 0, 0, S_SPECIALSTAGEBOMB_DISAPPEAR}, // S_SPECIALSTAGEBOMB_EXPLODE + {SPR_NULL, 0, 28*TICRATE, {A_Pain}, 0, 0, S_SPECIALSTAGEBOMB_FLICKER1}, // S_SPECIALSTAGEBOMB_DISAPPEAR + {SPR_SSBM, FF_GLOBALANIM|FF_ANIMATE|0, 1, {NULL}, 3, 3, S_SPECIALSTAGEBOMB_FLICKER2}, // S_SPECIALSTAGEBOMB_FLICKER1 + {SPR_NULL, 0, 1, {NULL}, 0, 0, S_SPECIALSTAGEBOMB_FLICKERLOOP}, // S_SPECIALSTAGEBOMB_FLICKER2 + {SPR_NULL, 0, 0, {A_Repeat}, TICRATE, S_SPECIALSTAGEBOMB_FLICKER1, S_SPECIALSTAGEBOMB_RESET}, // S_SPECIALSTAGEBOMB_FLICKERLOOP + {SPR_NULL, 0, 0, {A_SetObjectFlags}, MF_NOCLIPTHING, 1, S_SPECIALSTAGEBOMB}, // S_SPECIALSTAGEBOMB_RESET + // SMK ports {SPR_SMKP, 0, -1, {NULL}, 0, 0, S_SMK_PIPE1}, // S_SMK_PIPE1 {SPR_SMKP, 1, -1, {NULL}, 0, 0, S_SMK_PIPE2}, // S_SMK_PIPE2 @@ -5421,6 +5438,11 @@ state_t states[NUMSTATES] = {SPR_SGNS, FF_ADD|FF_FULLBRIGHT|8, 1, {NULL}, 0, 0, S_CHECKPOINT_SPARK10}, // S_CHECKPOINT_SPARK9 {SPR_SGNS, FF_ADD|FF_FULLBRIGHT|3, 1, {NULL}, 0, 0, S_CHECKPOINT_SPARK11}, // S_CHECKPOINT_SPARK10 {SPR_SGNS, FF_ADD|FF_FULLBRIGHT|2, 1, {NULL}, 0, 0, S_CHECKPOINT_SPARK1}, // S_CHECKPOINT_SPARK11 + + {SPR_SA2S, FF_SEMIBRIGHT|3, -1, {NULL}, 0, 0, S_NULL}, // S_BALLSWITCH_BALL + {SPR_SA2S, FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM|4, -1, {NULL}, 1, 1, S_NULL}, // S_BALLSWITCH_BALL_ACTIVE + {SPR_SA2S, FF_FLOORSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_BALLSWITCH_PAD + {SPR_SA2S, FF_FLOORSPRITE|FF_ANIMATE|FF_GLOBALANIM|1, -1, {NULL}, 1, 1, S_NULL}, // S_BALLSWITCH_PAD_ACTIVE }; mobjinfo_t mobjinfo[NUMMOBJTYPES] = @@ -28331,6 +28353,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_SPECIALSTAGEARCH + 3889, // doomednum + S_SPECIALSTAGEARCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 0, // dispoffset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_SCENERY|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_SPECIALSTAGEBOMB + 3890, // doomednum + S_SPECIALSTAGEBOMB, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_SPECIALSTAGEBOMB_DISARM, // painstate + 0, // painchance + sfx_s24b, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 60*FRACUNIT, // radius + 100*FRACUNIT, // height + 0, // dispoffset + 7, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_SPECIALSTAGEBOMB_EXPLODE // raisestate + }, + { // MT_SMK_PIPE 3970, // doomednum S_SMK_PIPE1, // spawnstate @@ -29407,7 +29483,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 0, // damage sfx_None, // activesound - MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPTHING, // flags + MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPTHING|MF_DRAWFROMFARAWAY, // flags S_NULL // raisestate }, @@ -29434,7 +29510,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 0, // damage sfx_None, // activesound - MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT, // flags + MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_DRAWFROMFARAWAY, // flags S_NULL // raisestate }, @@ -30412,6 +30488,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_SCENERY, // flags S_NULL // raisestate }, + + { // MT_BALLSWITCH_BALL + 5000, // doomednum + S_BALLSWITCH_BALL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 36*FRACUNIT, // radius + 64*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_SHOOTABLE|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_BALLSWITCH_PAD + -1, // doomednum + S_BALLSWITCH_PAD, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 64*FRACUNIT, // radius + 16*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_SCENERY, // flags + S_NULL // raisestate + }, }; skincolor_t skincolors[MAXSKINCOLORS] = { diff --git a/src/info.h b/src/info.h index 09e79d655..578c2c043 100644 --- a/src/info.h +++ b/src/info.h @@ -282,6 +282,7 @@ enum actionnum A_SSMINEEXPLODE, A_LANDMINEEXPLODE, A_BALLHOGEXPLODE, + A_SPECIALSTAGEBOMBEXPLODE, A_LIGHTNINGFOLLOWPLAYER, A_FZBOOMFLASH, A_FZBOOMSMOKE, @@ -557,6 +558,7 @@ void A_SSMineFlash(); void A_LandMineExplode(); void A_LandMineExplode(); void A_BallhogExplode(); +void A_SpecialStageBombExplode(); void A_LightningFollowPlayer(); void A_FZBoomFlash(); void A_FZBoomSmoke(); @@ -1352,6 +1354,10 @@ typedef enum sprite // Eerie Grove SPR_EGFG, + // Chaos Chute + SPR_SARC, + SPR_SSBM, + // SMK ports SPR_SMKP, SPR_MTYM, @@ -1447,6 +1453,8 @@ typedef enum sprite SPR_CPT2, // Checkpoint Stick SPR_CPT3, // Checkpoint Base + SPR_SA2S, // SA2-style Ball Switch + // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later SPR_VIEW, @@ -5530,6 +5538,17 @@ typedef enum state S_EERIEFOG4, S_EERIEFOG5, + // Chaos Chute + S_SPECIALSTAGEARCH, + S_SPECIALSTAGEBOMB, + S_SPECIALSTAGEBOMB_DISARM, + S_SPECIALSTAGEBOMB_EXPLODE, + S_SPECIALSTAGEBOMB_DISAPPEAR, + S_SPECIALSTAGEBOMB_FLICKER1, + S_SPECIALSTAGEBOMB_FLICKER2, + S_SPECIALSTAGEBOMB_FLICKERLOOP, + S_SPECIALSTAGEBOMB_RESET, + // SMK ports S_SMK_PIPE1, // Generic pipes S_SMK_PIPE2, @@ -5849,6 +5868,11 @@ typedef enum state S_CHECKPOINT_SPARK10, S_CHECKPOINT_SPARK11, + S_BALLSWITCH_BALL, + S_BALLSWITCH_BALL_ACTIVE, + S_BALLSWITCH_PAD, + S_BALLSWITCH_PAD_ACTIVE, + S_FIRSTFREESLOT, S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1, NUMSTATES @@ -6924,6 +6948,10 @@ typedef enum mobj_type MT_EERIEFOG, MT_EERIEFOGGEN, + // Chaos Chute + MT_SPECIALSTAGEARCH, + MT_SPECIALSTAGEBOMB, + // SMK ports MT_SMK_PIPE, MT_SMK_MOLESPAWNER, @@ -7021,6 +7049,9 @@ typedef enum mobj_type MT_CHECKPOINT_END, MT_SCRIPT_THING, + MT_BALLSWITCH_BALL, + MT_BALLSWITCH_PAD, + MT_FIRSTFREESLOT, MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1, NUMMOBJTYPES diff --git a/src/k_battle.c b/src/k_battle.c index 64673953d..8b8918d3d 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -801,24 +801,6 @@ void K_BattleInit(boolean singleplayercontext) battleprisons = true; } - if (gametyperules & GTR_BUMPERS) - { - const INT32 startingHealth = K_BumpersToHealth(K_StartingBumperCount()); - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - - if (players[i].mo) - { - players[i].mo->health = startingHealth; - } - - K_SpawnPlayerBattleBumpers(players+i); - } - } - g_battleufo.due = starttime; g_battleufo.previousId = Obj_GetFirstBattleUFOSpawnerID(); } diff --git a/src/k_botsearch.c b/src/k_botsearch.c index e4227f777..bdc7ecf62 100644 --- a/src/k_botsearch.c +++ b/src/k_botsearch.c @@ -458,6 +458,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing) case MT_BUBBLESHIELDTRAP: case MT_DUELBOMB: case MT_GACHABOM: + case MT_SPECIALSTAGEBOMB: K_AddDodgeObject(thing, side, 20); break; case MT_SHRINK_GUN: diff --git a/src/k_collide.cpp b/src/k_collide.cpp index 0d1d46348..41740137e 100644 --- a/src/k_collide.cpp +++ b/src/k_collide.cpp @@ -929,16 +929,12 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim) } else { - if (victim->type == MT_ORBINAUT || victim->type == MT_JAWZ || victim->type == MT_GACHABOM - || victim->type == MT_BANANA || victim->type == MT_EGGMANITEM || victim->type == MT_BALLHOG - || victim->type == MT_SSMINE || victim->type == MT_LANDMINE || victim->type == MT_SINK - || victim->type == MT_GARDENTOP || victim->type == MT_DROPTARGET || victim->type == MT_BATTLECAPSULE - || victim->type == MT_MONITOR || victim->type == MT_SPECIAL_UFO || victim->type == MT_BATTLEUFO) + if (victim->flags & MF_SHOOTABLE) { // Monitor hack. We can hit monitors once per instawhip, no multihit shredding! // Damage values in Obj_MonitorGetDamage. // Apply to UFO also -- steelt 29062023 - if (victim->type == MT_MONITOR || victim->type == MT_BATTLEUFO) + if (victim->type == MT_MONITOR || victim->type == MT_BATTLEUFO || victim->type == MT_BALLSWITCH_BALL) { if (shield->extravalue1 == 1) return false; diff --git a/src/k_dialogue.h b/src/k_dialogue.h index 7b4f7437e..4d6c05c79 100644 --- a/src/k_dialogue.h +++ b/src/k_dialogue.h @@ -25,6 +25,8 @@ void K_UnsetDialogue(void); void K_DrawDialogue(void); void K_TickDialogue(void); +boolean K_DialogueFreeze(void); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/k_dialogue.hpp b/src/k_dialogue.hpp index c3fb8dd5a..8639524fc 100644 --- a/src/k_dialogue.hpp +++ b/src/k_dialogue.hpp @@ -48,6 +48,7 @@ private: bool syllable; bool dismissable; + bool freeze; void Init(void); //void Unset(void); diff --git a/src/k_director.c b/src/k_director.c index dd2f55ad6..d2d82212c 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -11,6 +11,7 @@ #include "k_director.h" #include "d_netcmd.h" #include "p_local.h" +#include "g_party.h" #define SWITCHTIME TICRATE * 5 // cooldown between unforced switches #define BOREDOMTIME 3 * TICRATE / 2 // how long until players considered far apart? @@ -322,3 +323,9 @@ void K_ToggleDirector(boolean active) directorinfo.active = active; } + +boolean K_DirectorIsAvailable(UINT8 viewnum) +{ + return viewnum <= r_splitscreen && viewnum < G_PartySize(consoleplayer) && + displayplayers[viewnum] != G_PartyMember(consoleplayer, viewnum); +} diff --git a/src/k_director.h b/src/k_director.h index 47b4b2265..297bff72e 100644 --- a/src/k_director.h +++ b/src/k_director.h @@ -28,6 +28,7 @@ void K_UpdateDirector(void); void K_DrawDirectorDebugger(void); void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source); void K_ToggleDirector(boolean active); +boolean K_DirectorIsAvailable(UINT8 viewnum); #ifdef __cplusplus } // extern "C" diff --git a/src/k_follower.c b/src/k_follower.c index 499c1781f..f288d63f1 100644 --- a/src/k_follower.c +++ b/src/k_follower.c @@ -435,7 +435,7 @@ void K_HandleFollower(player_t *player) } else // follower exists, woo! { - if (player->follower->hitlag != 0) + if (P_MobjIsFrozen(player->follower)) { // Don't update frames in hitlag return; diff --git a/src/k_hud.c b/src/k_hud.c index 38b80b035..e3e6edcbf 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -100,6 +100,7 @@ static patch_t *kp_speedometersticker; static patch_t *kp_speedometerlabel[4]; static patch_t *kp_rankbumper; +static patch_t *kp_bigbumper; static patch_t *kp_tinybumper[2]; static patch_t *kp_ranknobumpers; static patch_t *kp_rankcapsule; @@ -439,6 +440,7 @@ void K_LoadKartHUDGraphics(void) // Extra ranking icons HU_UpdatePatch(&kp_rankbumper, "K_BLNICO"); + HU_UpdatePatch(&kp_bigbumper, "K_BLNBIG"); HU_UpdatePatch(&kp_tinybumper[0], "K_BLNA"); HU_UpdatePatch(&kp_tinybumper[1], "K_BLNB"); HU_UpdatePatch(&kp_ranknobumpers, "K_NOBLNS"); @@ -1481,7 +1483,7 @@ static void K_drawKartItem(void) // pain and suffering defined below if (offset) { - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + if (!(R_GetViewNumber() & 1)) // If we are P1 or P3... { fx = ITEM_X; fy = ITEM_Y; @@ -1507,6 +1509,11 @@ static void K_drawKartItem(void) fflags = V_SNAPTOTOP|V_SNAPTOLEFT|V_SPLITSCREEN; } + if (r_splitscreen == 1) + { + fy -= 5; + } + V_DrawScaledPatch(fx, fy, V_HUDTRANS|V_SLIDEIN|fflags, localbg); // Need to draw these in a particular order, for sorting. @@ -1622,7 +1629,7 @@ static void K_drawKartItem(void) { xo++; - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // Flip for P1 and P3 (yes, that's correct) + if (!(R_GetViewNumber() & 1)) // Flip for P1 and P3 (yes, that's correct) { xo -= 62; flip = V_FLIP; @@ -1730,7 +1737,7 @@ static void K_drawKartSlotMachine(void) if (offset) { boxoffx -= 4; - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + if (!(R_GetViewNumber() & 1)) // If we are P1 or P3... { fx = ITEM_X + 10; fy = ITEM_Y + 10; @@ -1759,6 +1766,11 @@ static void K_drawKartSlotMachine(void) fflags = V_SNAPTOTOP|V_SNAPTOLEFT|V_SPLITSCREEN; } + if (r_splitscreen == 1) + { + fy -= 5; + } + V_DrawScaledPatch(fx, fy, V_HUDTRANS|V_SLIDEIN|fflags, localbg); V_SetClipRect( @@ -2052,7 +2064,7 @@ static void K_DrawKartPositionNum(UINT8 num) { fx = BASEVIDWIDTH << FRACBITS; - if (stplyr == &players[displayplayers[0]]) + if (R_GetViewNumber() == 0) { // for player 1: display this at the top right, above the minimap. fy = 0; @@ -2071,8 +2083,7 @@ static void K_DrawKartPositionNum(UINT8 num) { fy = BASEVIDHEIGHT << FRACBITS; - if (stplyr == &players[displayplayers[0]] - || stplyr == &players[displayplayers[2]]) + if (!(R_GetViewNumber() & 1)) // If we are P1 or P3... { // If we are P1 or P3... fx = 0; @@ -2128,7 +2139,7 @@ static void K_DrawKartPositionNum(UINT8 num) color = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_POSNUM, GTC_CACHE); } - if ((fflags & V_SNAPTORIGHT) == 0 && num > 9) + if ((fflags & V_SNAPTORIGHT) == 0) { const UINT8 splitIndex = (r_splitscreen > 0) ? 1 : 0; UINT8 adjustNum = num; @@ -2512,7 +2523,7 @@ static void K_drawKartEmeralds(void) if (r_splitscreen < 2) { startx -= 8; - if (r_splitscreen == 1 && stplyr == &players[displayplayers[0]]) + if (r_splitscreen == 1 && R_GetViewNumber() == 0) { starty = 1; } @@ -2522,7 +2533,7 @@ static void K_drawKartEmeralds(void) { xindex = 2; starty -= 15; - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + if (!(R_GetViewNumber() & 1)) // If we are P1 or P3... { startx = LAPS_X; splitflags = V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_SPLITSCREEN; @@ -2586,7 +2597,7 @@ static void K_drawKartLaps(void) } else { - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + if (!(R_GetViewNumber() & 1)) // If we are P1 or P3... { fx = LAPS_X; fy = LAPS_Y; @@ -2693,7 +2704,7 @@ static void K_drawRingCounter(boolean gametypeinfoshown) } else { - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + if (!(R_GetViewNumber() & 1)) // If we are P1 or P3... { fx = LAPS_X; fy = LAPS_Y; @@ -2728,10 +2739,16 @@ static void K_drawRingCounter(boolean gametypeinfoshown) V_DrawMappedPatch(fr+ringx, fy-3, V_HUDTRANS|V_SLIDEIN|splitflags|ringflip, kp_smallring[ringanim_realframe], (colorring ? ringmap : NULL)); if (stplyr->hudrings < 0) // Draw the minus for ring debt - V_DrawMappedPatch(fr+7, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringdebtminussmall, ringmap); - - V_DrawMappedPatch(fr+11, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[rn[0]], ringmap); - V_DrawMappedPatch(fr+15, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[rn[1]], ringmap); + { + V_DrawMappedPatch(fr+11, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringdebtminussmall, ringmap); + V_DrawMappedPatch(fr+15, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[rn[0]], ringmap); + V_DrawMappedPatch(fr+19, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[rn[1]], ringmap); + } + else + { + V_DrawMappedPatch(fr+11, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[rn[0]], ringmap); + V_DrawMappedPatch(fr+15, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[rn[1]], ringmap); + } // SPB ring lock if (stplyr->pflags & PF_RINGLOCK) @@ -2833,7 +2850,7 @@ static void K_drawKartAccessibilityIcons(boolean gametypeinfoshown, INT32 fx) { fx = LAPS_X+44; fy = LAPS_Y; - if (!(stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]])) // If we are not P1 or P3... + if (R_GetViewNumber() & 1) // If we are not P1 or P3... { splitflags ^= (V_SNAPTOLEFT|V_SNAPTORIGHT); fx = (BASEVIDWIDTH/2) - fx; @@ -2904,6 +2921,11 @@ static void K_drawKartSpeedometer(boolean gametypeinfoshown) INT32 splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN; INT32 fy = LAPS_Y-14; + if (battleprisons) + { + fy -= 2; + } + if (!stplyr->exiting) // Keep the same speed value as when you crossed the finish line! { switch (cv_kartspeedometer.value) @@ -2976,7 +2998,23 @@ static void K_drawBlueSphereMeter(boolean gametypeinfoshown) if (r_splitscreen < 2) // don't change shit for THIS splitscreen. { fx = LAPS_X; - fy = LAPS_Y-7; + fy = LAPS_Y-4; + + if (battleprisons) + { + if (r_splitscreen == 1) + { + fy -= 8; + } + else + { + fy -= 5; + } + } + else if (r_splitscreen == 1) + { + fy -= 5; + } if (gametypeinfoshown) { @@ -2992,7 +3030,7 @@ static void K_drawBlueSphereMeter(boolean gametypeinfoshown) else { xstep = 8; - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + if (!(R_GetViewNumber() & 1)) // If we are P1 or P3... { fx = LAPS_X-2; fy = LAPS_Y; @@ -3006,6 +3044,11 @@ static void K_drawBlueSphereMeter(boolean gametypeinfoshown) xstep = -xstep; } + if (battleprisons) + { + fy -= 5; + } + if (gametypeinfoshown) { fy -= 16; @@ -3077,7 +3120,7 @@ static void K_drawKartBumpersOrKarma(void) } else { - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + if (!(R_GetViewNumber() & 1)) // If we are P1 or P3... { fx = LAPS_X; fy = LAPS_Y; @@ -3093,11 +3136,13 @@ static void K_drawKartBumpersOrKarma(void) } V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy, V_HUDTRANS|V_SLIDEIN|splitflags|flipflag, kp_ringstickersplit[0]); - V_DrawScaledPatch(fx+22, fy, V_HUDTRANS|V_SLIDEIN|splitflags, frameslash); + + fx += 2; if (battleprisons) { - V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|V_SLIDEIN|splitflags, kp_rankcapsule, NULL); + V_DrawScaledPatch(fx+22, fy, V_HUDTRANS|V_SLIDEIN|splitflags, frameslash); + V_DrawMappedPatch(fx-1, fy-2, V_HUDTRANS|V_SLIDEIN|splitflags, kp_rankcapsule, NULL); if (numtargets > 9 || maptargets > 9) { @@ -3122,54 +3167,42 @@ static void K_drawKartBumpersOrKarma(void) } else { - const INT32 maxbumper = K_StartingBumperCount(); const UINT8 bumpers = K_Bumpers(stplyr); - V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|V_SLIDEIN|splitflags, kp_rankbumper, colormap); + V_DrawMappedPatch(fx-1, fy-2, V_HUDTRANS|V_SLIDEIN|splitflags, kp_rankbumper, colormap); - if (bumpers > 9 || maxbumper > 9) - { - UINT8 ln[2]; - ln[0] = (bumpers / 10 % 10); - ln[1] = (bumpers % 10); + UINT8 ln[2]; + ln[0] = (bumpers / 10 % 10); + ln[1] = (bumpers % 10); - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); - - ln[0] = ((abs(maxbumper) / 10) % 10); - ln[1] = (abs(maxbumper) % 10); - - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); - } - else - { - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(bumpers) % 10]); - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(maxbumper) % 10]); - } + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[ln[0]]); + V_DrawScaledPatch(fx+19, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[ln[1]]); } } else { + INT32 fy = r_splitscreen == 1 ? LAPS_Y-3 : LAPS_Y; + if (battleprisons) { if (numtargets > 9 && maptargets > 9) - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_capsulestickerwide, NULL); + V_DrawMappedPatch(LAPS_X, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_capsulestickerwide, NULL); else - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_capsulesticker, NULL); - V_DrawTimerString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", numtargets, maptargets)); + V_DrawMappedPatch(LAPS_X, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_capsulesticker, NULL); + V_DrawTimerString(LAPS_X+47, fy+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", numtargets, maptargets)); } else { - const INT32 maxbumper = K_StartingBumperCount(); const UINT8 bumpers = K_Bumpers(stplyr); - if (bumpers > 9 && maxbumper > 9) - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_bumperstickerwide, colormap); - else - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_bumpersticker, colormap); + if (r_splitscreen == 0) + { + fy += 2; + } - V_DrawTimerString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", bumpers, maxbumper)); + K_DrawSticker(LAPS_X+12, fy+5, bumpers > 9 ? 64 : 52, V_HUDTRANS|V_SLIDEIN|splitflags, false); + V_DrawMappedPatch(LAPS_X+15, fy-5, V_HUDTRANS|V_SLIDEIN|splitflags, kp_bigbumper, colormap); + V_DrawTimerString(LAPS_X+47, fy+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d", bumpers)); } } } @@ -3418,7 +3451,7 @@ static void K_DrawNameTagForPlayer(fixed_t x, fixed_t y, player_t *p) // Since there's no "V_DrawFixedFill", and I don't feel like making it, // fuck it, we're gonna just V_NOSCALESTART hack it - if (cnum & 1) + if (r_splitscreen > 1 && cnum & 1) { x += (BASEVIDWIDTH/2) * FRACUNIT; } @@ -3517,6 +3550,31 @@ static void K_drawKartNameTags(void) return; } + // Crop within splitscreen bounds + switch (r_splitscreen) + { + case 1: + V_SetClipRect( + 0, + cnum == 1 ? (BASEVIDHEIGHT / 2) * FRACUNIT : 0, + BASEVIDWIDTH * FRACUNIT, + (BASEVIDHEIGHT / 2) * FRACUNIT, + 0 + ); + break; + + case 2: + case 3: + V_SetClipRect( + cnum & 1 ? (BASEVIDWIDTH / 2) * FRACUNIT : 0, + cnum > 1 ? (BASEVIDHEIGHT / 2) * FRACUNIT : 0, + (BASEVIDWIDTH / 2) * FRACUNIT, + (BASEVIDHEIGHT / 2) * FRACUNIT, + 0 + ); + break; + } + c.x = viewx; c.y = viewy; c.z = viewz; @@ -3704,20 +3762,10 @@ static void K_drawKartNameTags(void) if (result.onScreen == true) { - if (!(demo.playback == true && demo.freecam == true)) + if (!(demo.playback == true && demo.freecam == true) && P_IsDisplayPlayer(ntplayer) && + ntplayer != &players[displayplayers[cnum]]) { - for (j = 0; j <= (unsigned)r_splitscreen; j++) - { - if (ntplayer == &players[displayplayers[j]]) - { - break; - } - } - - if (j <= (unsigned)r_splitscreen && j != cnum) - { - localindicator = j; - } + localindicator = G_PartyPosition(ntplayer - players); } if (localindicator >= 0) @@ -3742,6 +3790,8 @@ static void K_drawKartNameTags(void) } } } + + V_ClearClipRect(); } #define PROGRESSION_BAR_WIDTH 120 @@ -3896,7 +3946,7 @@ static void K_drawKartMinimap(void) // Only draw for the first player // Maybe move this somewhere else where this won't be a concern? - if (stplyr != &players[displayplayers[0]]) + if (R_GetViewNumber() != 0) return; if (specialstageinfo.valid == true) @@ -4431,7 +4481,7 @@ static void K_drawKartFinish(boolean finish) interpx = R_InterpolateFixed(ox, x); - if (r_splitscreen && stplyr == &players[displayplayers[1]]) + if (r_splitscreen && R_GetViewNumber() == 1) interpx = -interpx; V_DrawFixedPatch(interpx + (STCD_X<spectator || !stplyr->mo || (stplyr->mo->renderflags & RF_DONTDRAW)) return; - if (stplyr == &players[displayplayers[1]] && r_splitscreen) - { pn = pnum[1]; tn = turn[1]; dr = drift[1]; } - else if (stplyr == &players[displayplayers[2]] && r_splitscreen > 1) - { pn = pnum[2]; tn = turn[2]; dr = drift[2]; } - else if (stplyr == &players[displayplayers[3]] && r_splitscreen > 2) - { pn = pnum[3]; tn = turn[3]; dr = drift[3]; } - else - { pn = pnum[0]; tn = turn[0]; dr = drift[0]; } + { + UINT8 view = R_GetViewNumber(); + pn = pnum[view]; + tn = turn[view]; + dr = drift[view]; + } if (r_splitscreen) { @@ -4805,14 +4853,12 @@ static void K_drawKartFirstPerson(void) V_DrawFixedPatch(x, y, scale, splitflags, kp_fpview[target], colmap); - if (stplyr == &players[displayplayers[1]] && r_splitscreen) - { pnum[1] = pn; turn[1] = tn; drift[1] = dr; } - else if (stplyr == &players[displayplayers[2]] && r_splitscreen > 1) - { pnum[2] = pn; turn[2] = tn; drift[2] = dr; } - else if (stplyr == &players[displayplayers[3]] && r_splitscreen > 2) - { pnum[3] = pn; turn[3] = tn; drift[3] = dr; } - else - { pnum[0] = pn; turn[0] = tn; drift[0] = dr; } + { + UINT8 view = R_GetViewNumber(); + pnum[view] = pn; + turn[view] = tn; + drift[view] = dr; + } } // doesn't need to ever support 4p @@ -5060,23 +5106,26 @@ void K_drawKartFreePlay(void) if (((leveltime-lt_endtime) % TICRATE) < TICRATE/2) return; - const fixed_t x = ((BASEVIDWIDTH - (LAPS_X+6)) * FRACUNIT) - \ - V_StringScaledWidth( + INT32 h_snap = (r_splitscreen < 2 || R_GetViewNumber() & 1) ? V_SNAPTORIGHT : V_SNAPTOLEFT; + fixed_t x = ((r_splitscreen > 1 ? BASEVIDWIDTH/4 : BASEVIDWIDTH - (LAPS_X+6)) * FRACUNIT); + fixed_t y = ((r_splitscreen ? BASEVIDHEIGHT/2 : BASEVIDHEIGHT) - 20) * FRACUNIT; + + x -= V_StringScaledWidth( FRACUNIT, FRACUNIT, FRACUNIT, - V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, + V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|h_snap|V_SPLITSCREEN, KART_FONT, "FREE PLAY" - ); + ) / (r_splitscreen > 1 ? 2 : 1); V_DrawStringScaled( x, - (LAPS_Y+3) * FRACUNIT, + y, FRACUNIT, FRACUNIT, FRACUNIT, - V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, + V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|h_snap|V_SPLITSCREEN, NULL, KART_FONT, "FREE PLAY" @@ -5086,33 +5135,26 @@ void K_drawKartFreePlay(void) static void Draw_party_ping (int ss, INT32 snap) { - HU_drawMiniPing(0, 0, playerpingtable[displayplayers[ss]], V_HUDTRANS|V_SPLITSCREEN|V_SNAPTOTOP|snap); + HU_drawMiniPing(0, 0, playerpingtable[displayplayers[ss]], V_SPLITSCREEN|V_SNAPTOTOP|snap); } static void K_drawMiniPing (void) { UINT32 f = V_SNAPTORIGHT; - UINT8 i; + UINT8 i = R_GetViewNumber(); if (!cv_showping.value) { return; } - for (i = 0; i <= r_splitscreen; i++) + if (r_splitscreen > 1 && !(i & 1)) { - if (stplyr == &players[displayplayers[i]]) - { - if (r_splitscreen > 1 && !(i & 1)) - { - f = V_SNAPTOLEFT; - } - - Draw_party_ping(i, f); - break; - } + f = V_SNAPTOLEFT; } + + Draw_party_ping(i, f); } void K_drawButton(fixed_t x, fixed_t y, INT32 flags, patch_t *button[2], boolean pressed) @@ -5210,7 +5252,7 @@ static void K_drawDistributionDebugger(void) fixed_t y = -pad; size_t i; - if (stplyr != &players[displayplayers[0]]) // only for p1 + if (R_GetViewNumber() != 0) // only for p1 { return; } @@ -5268,7 +5310,7 @@ static void K_DrawWaypointDebugger(void) if (cv_kartdebugwaypoints.value == 0) return; - if (stplyr != &players[displayplayers[0]]) // only for p1 + if (R_GetViewNumber() != 0) // only for p1 return; if (netgame) @@ -5298,7 +5340,7 @@ static void K_DrawBotDebugger(void) return; } - if (stplyr != &players[displayplayers[0]]) // only for p1 + if (R_GetViewNumber() != 0) // only for p1 { return; } @@ -5368,7 +5410,7 @@ static void K_DrawGPRankDebugger(void) return; } - if (stplyr != &players[displayplayers[0]]) // only for p1 + if (R_GetViewNumber() != 0) // only for p1 { return; } @@ -5425,7 +5467,7 @@ void K_drawKartHUD(void) K_drawKartFirstPerson(); // Draw full screen stuff that turns off the rest of the HUD - if (mapreset && stplyr == &players[displayplayers[0]]) + if (mapreset && R_GetViewNumber() == 0) { K_drawChallengerScreen(); return; @@ -5455,19 +5497,6 @@ void K_drawKartHUD(void) K_drawKartMinimap(); } - // Draw the item window - if (LUA_HudEnabled(hud_item) && !freecam) - { - if (stplyr->itemRoulette.ringbox && stplyr->itemamount == 0 && stplyr->itemtype == 0) - { - K_drawKartSlotMachine(); - } - else - { - K_drawKartItem(); - } - } - if (demo.title) ; else if (!r_splitscreen) @@ -5482,18 +5511,23 @@ void K_drawKartHUD(void) islonesome = K_drawKartPositionFaces(); } - else if (r_splitscreen == 1) + else { - if (LUA_HudEnabled(hud_time)) + islonesome = M_NotFreePlay() == false; + + if (r_splitscreen == 1) { - K_drawKart2PTimestamp(); + if (LUA_HudEnabled(hud_time)) + { + K_drawKart2PTimestamp(); + } } - } - else if (viewnum == r_splitscreen) - { - if (LUA_HudEnabled(hud_time)) + else if (viewnum == r_splitscreen) { - K_drawKart4PTimestamp(); + if (LUA_HudEnabled(hud_time)) + { + K_drawKart4PTimestamp(); + } } } @@ -5581,6 +5615,19 @@ void K_drawKartHUD(void) if (LUA_HudEnabled(hud_position)) K_drawInput(); } + + // Draw the item window + if (LUA_HudEnabled(hud_item) && !freecam) + { + if (stplyr->itemRoulette.ringbox && stplyr->itemamount == 0 && stplyr->itemtype == 0) + { + K_drawKartSlotMachine(); + } + else + { + K_drawKartItem(); + } + } } } @@ -5640,7 +5687,7 @@ void K_drawKartHUD(void) K_drawKartPowerUps(); - if (G_IsPartyLocal(displayplayers[viewnum]) == false) + if (K_DirectorIsAvailable(viewnum) == true) { K_drawDirectorHUD(); } diff --git a/src/k_kart.c b/src/k_kart.c index ec4435df2..5cd99108e 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -264,13 +264,13 @@ UINT32 K_GetPlayerDontDrawFlag(player_t *player) return flag; if (player == &players[displayplayers[0]]) - flag = RF_DONTDRAWP1; - else if (r_splitscreen >= 1 && player == &players[displayplayers[1]]) - flag = RF_DONTDRAWP2; - else if (r_splitscreen >= 2 && player == &players[displayplayers[2]]) - flag = RF_DONTDRAWP3; - else if (r_splitscreen >= 3 && player == &players[displayplayers[3]]) - flag = RF_DONTDRAWP4; + flag |= RF_DONTDRAWP1; + if (r_splitscreen >= 1 && player == &players[displayplayers[1]]) + flag |= RF_DONTDRAWP2; + if (r_splitscreen >= 2 && player == &players[displayplayers[2]]) + flag |= RF_DONTDRAWP3; + if (r_splitscreen >= 3 && player == &players[displayplayers[3]]) + flag |= RF_DONTDRAWP4; return flag; } @@ -3809,13 +3809,33 @@ void K_RemoveGrowShrink(player_t *player) boolean K_IsBigger(mobj_t *compare, mobj_t *other) { + fixed_t compareScale, otherScale; + if ((compare == NULL || P_MobjWasRemoved(compare) == true) || (other == NULL || P_MobjWasRemoved(other) == true)) { return false; } - return (compare->scale > other->scale + (mapobjectscale / 4)); + if ((compareScale = P_GetMobjDefaultScale(compare)) != FRACUNIT) + { + compareScale = FixedDiv(compare->scale, compareScale); + } + else + { + compareScale = compare->scale; + } + + if ((otherScale = P_GetMobjDefaultScale(other)) != FRACUNIT) + { + otherScale = FixedDiv(other->scale, otherScale); + } + else + { + otherScale = other->scale; + } + + return (compareScale > otherScale + (mapobjectscale / 4)); } static fixed_t K_TumbleZ(mobj_t *mo, fixed_t input) @@ -4536,7 +4556,7 @@ void K_MineFlashScreen(mobj_t *source) } // Spawns the purely visual explosion -void K_SpawnMineExplosion(mobj_t *source, UINT8 color, tic_t delay) +void K_SpawnMineExplosion(mobj_t *source, skincolornum_t color, tic_t delay) { INT32 i, radius, height; mobj_t *smoldering = P_SpawnMobj(source->x, source->y, source->z, MT_SMOLDERING); @@ -4626,6 +4646,38 @@ void K_SpawnMineExplosion(mobj_t *source, UINT8 color, tic_t delay) #undef MINEQUAKEDIST +void K_SpawnLandMineExplosion(mobj_t *source, skincolornum_t color, tic_t delay) +{ + mobj_t *smoldering; + mobj_t *expl; + UINT8 i; + + // Spawn smoke remains: + smoldering = P_SpawnMobj(source->x, source->y, source->z, MT_SMOLDERING); + P_SetScale(smoldering, source->scale); + smoldering->tics = TICRATE*3; + smoldering->hitlag = delay; + + // spawn a few physics explosions + for (i = 0; i < 15; i++) + { + expl = P_SpawnMobj(source->x, source->y, source->z + source->scale, MT_BOOMEXPLODE); + expl->color = color; + expl->tics = (i+1); + expl->hitlag = delay; + expl->renderflags |= RF_DONTDRAW; + + //K_MatchGenericExtraFlags(expl, actor); + P_SetScale(expl, source->scale*4); + + expl->momx = P_RandomRange(PR_EXPLOSION, -3, 3)*source->scale/2; + expl->momy = P_RandomRange(PR_EXPLOSION, -3, 3)*source->scale/2; + + // 100/45 = 2.22 fu/t + expl->momz = ((i+1)*source->scale*5/2)*P_MobjFlip(expl); + } +} + fixed_t K_ItemScaleForPlayer(player_t *player) { switch (player->itemscale) @@ -7882,8 +7934,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) debtflag->color = player->skincolor; debtflag->fuse = 2; - - debtflag->renderflags = K_GetPlayerDontDrawFlag(player); } if (player->springstars && (leveltime & 1)) diff --git a/src/k_kart.h b/src/k_kart.h index e33580f81..0f310d403 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -120,7 +120,8 @@ void K_DebtStingPlayer(player_t *player, mobj_t *source); void K_GiveBumpersToPlayer(player_t *player, player_t *victim, UINT8 amount); void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount); void K_MineFlashScreen(mobj_t *source); -void K_SpawnMineExplosion(mobj_t *source, UINT8 color, tic_t delay); +void K_SpawnMineExplosion(mobj_t *source, skincolornum_t color, tic_t delay); +void K_SpawnLandMineExplosion(mobj_t *source, skincolornum_t color, tic_t delay); void K_RunFinishLineBeam(void); UINT16 K_DriftSparkColor(player_t *player, INT32 charge); void K_SpawnBoostTrail(player_t *player); diff --git a/src/k_objects.h b/src/k_objects.h index d5e06e8e8..78df2bb62 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -233,6 +233,12 @@ boolean Obj_GetCheckpointRespawnPosition(const mobj_t *checkpoint, vector3_t *re angle_t Obj_GetCheckpointRespawnAngle(const mobj_t *checkpoint); void Obj_ActivateCheckpointInstantly(mobj_t* mobj); +/* Ball Switch */ +void Obj_BallSwitchInit(mobj_t *mobj); +void Obj_BallSwitchThink(mobj_t *mobj); +void Obj_BallSwitchTouched(mobj_t *mobj, mobj_t *toucher); +void Obj_BallSwitchDamaged(mobj_t *mobj, mobj_t *inflictor, mobj_t *source); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/k_tally.cpp b/src/k_tally.cpp index 3e5c16f27..36f5ed0d1 100644 --- a/src/k_tally.cpp +++ b/src/k_tally.cpp @@ -33,6 +33,7 @@ #include "m_easing.h" #include "s_sound.h" #include "st_stuff.h" +#include "r_fps.h" boolean level_tally_t::UseBonuses(void) { @@ -883,14 +884,14 @@ void level_tally_t::Draw(void) SINT8 h_transition_sign = 1; if (r_splitscreen > 1) { - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) + if (!(R_GetViewNumber() & 1)) { h_transition_sign = -h_transition_sign; } } else if (r_splitscreen > 0) { - if (stplyr == &players[displayplayers[1]]) + if (R_GetViewNumber() == 1) { h_transition_sign = -h_transition_sign; } diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index c5e6c0672..c9c463d4a 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -662,7 +662,7 @@ static int libd_drawOnMinimap(lua_State *L) if (gamestate != GS_LEVEL) return 0; - if (stplyr != &players[displayplayers[0]]) + if (R_GetViewNumber() != 0) return 0; AutomapPic = mapheaderinfo[gamemap-1]->minimapPic; diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index c30d0d54d..5816da17b 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -599,7 +599,6 @@ static int player_set(lua_State *L) if (plr == &players[displayplayers[i]]) { localaiming[i] = plr->aiming; - break; } } } diff --git a/src/m_cheat.c b/src/m_cheat.c index b4cb60d83..e44735eb9 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -303,6 +303,14 @@ void Command_CheatGod_f(void) D_Cheat(consoleplayer, CHEAT_GOD); } +void Command_CheatFreeze_f(void) +{ + REQUIRE_CHEATS; + REQUIRE_INLEVEL; + + D_Cheat(consoleplayer, CHEAT_FREEZE); +} + void Command_Scale_f(void) { const double scaled = atof(COM_Argv(1)); diff --git a/src/m_cheat.h b/src/m_cheat.h index 1c1270e9b..a8d0505cc 100644 --- a/src/m_cheat.h +++ b/src/m_cheat.h @@ -41,6 +41,7 @@ typedef enum { CHEAT_RESPAWNAT, CHEAT_GIVEPOWERUP, CHEAT_SPHERES, + CHEAT_FREEZE, NUMBER_OF_CHEATS } cheat_t; @@ -74,6 +75,7 @@ void OP_ObjectplaceMovement(player_t *player); // void Command_CheatNoClip_f(void); void Command_CheatGod_f(void); +void Command_CheatFreeze_f(void); void Command_Savecheckpoint_f(void); void Command_Setrings_f(void); void Command_Setspheres_f(void); diff --git a/src/menus/play-online-1.c b/src/menus/play-online-1.c index e58490187..7a7b54386 100644 --- a/src/menus/play-online-1.c +++ b/src/menus/play-online-1.c @@ -78,7 +78,7 @@ menuitem_t PLAY_MP_OptSelect[] = {IT_STRING_CALL_NOTESTERS, "Host Game", "Start your own online game!", NULL, {.routine = M_PreMPHostInit}, 0, 0}, - {IT_STRING_CALL_NOTESTERS, "Server Browser", "Search for game servers to play in.", + {IT_STRING | IT_CALL, "Server Browser", "Search for game servers to play in.", NULL, {.routine = M_PreMPRoomSelectInit}, 0, 0}, {IT_STRING | IT_CALL, "Join by IP", "Join an online game by its IP address.", diff --git a/src/menus/play-online-room-select.c b/src/menus/play-online-room-select.c index d0d6b9a58..05c458c4b 100644 --- a/src/menus/play-online-room-select.c +++ b/src/menus/play-online-room-select.c @@ -75,11 +75,13 @@ void M_MPRoomSelectInit(INT32 choice) mpmenu.scrolln = 0; mpmenu.slide = 0; +#ifndef TESTERS if ((modifiedgame == true) || (M_SecretUnlocked(SECRET_ADDONS, true) == false)) { M_ServersMenu(0); return; } +#endif // TESTERS M_SetupNextMenu(&PLAY_MP_RoomSelectDef, false); } diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index 89d5a17c1..694588783 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -30,4 +30,5 @@ target_sources(SRB2SDL2 PRIVATE emerald.c checkpoint.cpp shadow.cpp + ball-switch.cpp ) diff --git a/src/objects/ball-switch.cpp b/src/objects/ball-switch.cpp new file mode 100644 index 000000000..c2bd5cc76 --- /dev/null +++ b/src/objects/ball-switch.cpp @@ -0,0 +1,241 @@ +#include "../k_objects.h" + +#include "../doomdef.h" +#include "../info.h" +#include "../p_local.h" +#include "../r_main.h" +#include "../k_hitlag.h" + +#define ball_pad(o) ((o)->target) +#define ball_instawhipped(o) ((o)->extravalue1) // see instawhip collide + +#define ball_cooldown(o) ((o)->cvmem) + +#define ball_activedefer(o) ((o)->extravalue2) +#define ball_activator(o) ((o)->tracer) + +namespace +{ + +struct BallSwitch_Pad : mobj_t +{ + statenum_t Anim() const { return static_cast(this->state - states); } + void Anim(statenum_t n) + { + if (Anim() != n) + { + P_SetMobjState(this, n); + } + } + + void Spawned() + { + renderflags |= RF_FLOORSPRITE|RF_NOSPLATBILLBOARD|RF_SLOPESPLAT|RF_NOSPLATROLLANGLE; + } + + void Tick(boolean active) + { + if (active == true) + { + Anim(S_BALLSWITCH_PAD_ACTIVE); + } + else + { + Anim(S_BALLSWITCH_PAD); + } + } +}; + +struct BallSwitch_Ball : mobj_t +{ + BallSwitch_Pad *Pad() const { return static_cast( ball_pad(this) ); } + void Pad(BallSwitch_Pad *n) { P_SetTarget(&ball_pad(this), n); } + + statenum_t Anim() const { return static_cast(this->state - states); } + void Anim(statenum_t n) + { + if (Anim() != n) + { + P_SetMobjState(this, n); + } + } + + INT32 Cooldown() const { return ball_cooldown(this); } + void Cooldown(INT32 n) { ball_cooldown(this) = n; } + boolean Active() const { return (ball_cooldown(this) != 0); } + + boolean DeferActivation() const { return ball_activedefer(this); } + mobj_t *Activator() const { return ball_activator(this); } + + void DeferActivation(boolean n, mobj_t *src) + { + ball_activedefer(this) = n; + P_SetTarget(&ball_activator(this), src); + } + + SINT8 IntSign(int value) const + { + if (value > 0) + { + return 1; + } + + if (value < 0) + { + return -1; + } + + return 0; + } + + void Spawned() + { + Pad( static_cast( P_SpawnMobjFromMobj(this, 0, 0, 0, MT_BALLSWITCH_PAD) ) ); + Pad()->Spawned(); + + this->z += Pad()->height * P_MobjFlip(this); + } + + void Tick() + { + if (P_MobjWasRemoved(Pad()) == true) + { + P_RemoveMobj(this); + return; + } + + ball_instawhipped(this) = 0; + + if (DeferActivation() == true) + { + P_ActivateThingSpecial(this, Activator()); + Cooldown(-1); // maybe later? + DeferActivation(false, nullptr); + } + + fixed_t ourZ = P_GetMobjFeet(this); + fixed_t theirZ = P_GetMobjHead(Pad()); + + fixed_t dist = P_AproxDistance(P_AproxDistance(Pad()->x - this->x, Pad()->y - this->y), theirZ - ourZ); + fixed_t move = P_AproxDistance(P_AproxDistance(this->momx, this->momy), this->momz); + + constexpr INT32 accelScale = 4; + + if (dist < accelScale * this->scale && move < accelScale * this->scale) + { + P_SetOrigin(this, Pad()->x, Pad()->y, theirZ); + this->momx = this->momy = this->momz = 0; + } + else + { + static constexpr const INT32 accel[2] = { FRACUNIT*3/4, FRACUNIT*3/16 }; + constexpr fixed_t frict = FRACUNIT*99/100; + + this->momx = FixedMul(this->momx, frict); + this->momy = FixedMul(this->momy, frict); + this->momz = FixedMul(this->momz, frict); + + SINT8 xSign = IntSign(Pad()->x - this->x); + SINT8 ySign = IntSign(Pad()->y - this->y); + SINT8 zSign = IntSign(theirZ - ourZ); + + boolean xAway = (IntSign(this->momx) == xSign); + boolean yAway = (IntSign(this->momy) == ySign); + boolean zAway = (IntSign(this->momz) == zSign); + + this->momx += FixedMul(accel[xAway], accelScale * this->scale) * xSign; + this->momy += FixedMul(accel[yAway], accelScale * this->scale) * ySign; + this->momz += FixedMul(accel[zAway], accelScale * this->scale) * zSign; + + this->angle += FixedAngle(move * 2); + if (dist > this->radius * 2) + { + P_Thrust(this, this->angle, (move / accelScale) * 2 / 3); + } + } + + if (Active() == true) + { + INT32 cool = Cooldown(); + if (cool > 0) + { + Cooldown(cool - 1); + } + + Anim(S_BALLSWITCH_BALL_ACTIVE); + } + else + { + Anim(S_BALLSWITCH_BALL); + } + + Pad()->Tick(Active()); + } + + void Push(mobj_t *toucher, const fixed_t pushValue, const fixed_t repelValue) + { + fixed_t push = FixedMul(pushValue, toucher->scale); + fixed_t repel = FixedMul(repelValue, this->scale); + + angle_t thrustAngle = R_PointToAngle2(toucher->x, toucher->y, this->x, this->y); + fixed_t thrustAngleCos = FINECOSINE(thrustAngle >> ANGLETOFINESHIFT); + fixed_t thrustAngleSin = FINESINE(thrustAngle >> ANGLETOFINESHIFT); + + fixed_t thisZ = this->z + (this->height / 2); + fixed_t toucherZ = toucher->z + (toucher->height / 2); + + angle_t thrustPitch = R_PointToAngle2(0, toucherZ, R_PointToDist2(toucher->x, toucher->y, this->x, this->y), thisZ); + fixed_t thrustPitchCos = FINECOSINE(thrustPitch >> ANGLETOFINESHIFT); + fixed_t thrustPitchSin = FINESINE(thrustPitch >> ANGLETOFINESHIFT); + + this->momx += FixedMul(FixedMul(push, thrustAngleCos), thrustPitchCos); + this->momy += FixedMul(FixedMul(push, thrustAngleSin), thrustPitchCos); + this->momz += FixedMul(push, thrustPitchSin); + + toucher->momx -= FixedMul(FixedMul(repel, thrustAngleCos), thrustPitchCos); + toucher->momy -= FixedMul(FixedMul(repel, thrustAngleSin), thrustPitchCos); + toucher->momz -= FixedMul(repel, thrustPitchSin); + } + + void Touch(mobj_t *toucher) + { + Push(toucher, 4 << FRACBITS, 6 << FRACBITS); + } + + void Hit(mobj_t *inflictor, mobj_t *source) + { + Push(inflictor, 64 << FRACBITS, 1 << FRACBITS); + K_SetHitLagForObjects(this, inflictor, source, 4, true); + + if (Active() == false) + { + DeferActivation(true, source); + } + } +}; + +}; // namespace + +void Obj_BallSwitchInit(mobj_t *mobj) +{ + BallSwitch_Ball *ball = static_cast(mobj); + ball->Spawned(); +} + +void Obj_BallSwitchThink(mobj_t *mobj) +{ + BallSwitch_Ball *ball = static_cast(mobj); + ball->Tick(); +} + +void Obj_BallSwitchTouched(mobj_t *mobj, mobj_t *toucher) +{ + BallSwitch_Ball *ball = static_cast(mobj); + ball->Touch(toucher); +} + +void Obj_BallSwitchDamaged(mobj_t *mobj, mobj_t *inflictor, mobj_t *source) +{ + BallSwitch_Ball *ball = static_cast(mobj); + ball->Hit(inflictor, source); +} diff --git a/src/p_enemy.c b/src/p_enemy.c index 86f0761ea..084ccb169 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -314,6 +314,7 @@ void A_SSMineExplode(mobj_t *actor); void A_SSMineFlash(mobj_t *actor); void A_LandMineExplode(mobj_t *actor); void A_BallhogExplode(mobj_t *actor); +void A_SpecialStageBombExplode(mobj_t *actor); void A_LightningFollowPlayer(mobj_t *actor); void A_FZBoomFlash(mobj_t *actor); void A_FZBoomSmoke(mobj_t *actor); @@ -13064,11 +13065,7 @@ void A_SSMineFlash(mobj_t *actor) void A_LandMineExplode(mobj_t *actor) { - mobj_t *expl; - INT32 colour = SKINCOLOR_KETCHUP; // we spell words properly here - INT32 i; - mobj_t *smoldering; - + skincolornum_t colour = SKINCOLOR_KETCHUP; // we spell words properly here tic_t delay = actor->reactiontime; if (LUA_CallAction(A_LANDMINEEXPLODE, actor)) @@ -13085,33 +13082,10 @@ void A_LandMineExplode(mobj_t *actor) if (actor->target && !P_MobjWasRemoved(actor->target)) colour = actor->target->color; - // Spawn smoke remains: - smoldering = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMOLDERING); - P_SetScale(smoldering, actor->scale); - smoldering->tics = TICRATE*3; - smoldering->hitlag = delay; + K_SpawnLandMineExplosion(actor, colour, delay); actor->fuse = actor->tics; // disappear when this state ends. - // spawn a few physics explosions - for (i = 0; i < 15; i++) - { - expl = P_SpawnMobj(actor->x, actor->y, actor->z + actor->scale, MT_BOOMEXPLODE); - expl->color = colour; - expl->tics = (i+1); - expl->hitlag = delay; - expl->renderflags |= RF_DONTDRAW; - - //K_MatchGenericExtraFlags(expl, actor); - P_SetScale(expl, actor->scale*4); - - expl->momx = P_RandomRange(PR_EXPLOSION, -3, 3)*actor->scale/2; - expl->momy = P_RandomRange(PR_EXPLOSION, -3, 3)*actor->scale/2; - - // 100/45 = 2.22 fu/t - expl->momz = ((i+1)*actor->scale*5/2)*P_MobjFlip(expl); - } - Obj_SpawnBrolyKi(actor, delay); } @@ -13129,6 +13103,15 @@ void A_BallhogExplode(mobj_t *actor) return; } +void A_SpecialStageBombExplode(mobj_t *actor) +{ + if (LUA_CallAction(A_SPECIALSTAGEBOMBEXPLODE, actor)) + return; + + K_SpawnLandMineExplosion(actor, SKINCOLOR_KETCHUP, actor->hitlag); + P_StartQuakeFromMobj(TICRATE/6, 24 * actor->scale, 512 * mapobjectscale, actor); +} + // A_LightningFollowPlayer: // Dumb simple function that gives a mobj its target's momentums without updating its angle. void A_LightningFollowPlayer(mobj_t *actor) diff --git a/src/p_inter.c b/src/p_inter.c index b76d4e4af..a6f468c31 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -565,6 +565,26 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) P_SetTarget(&special->target, toucher); S_StartSound(special, sfx_s1a2); return; + + case MT_SPECIALSTAGEBOMB: + // only attempt to damage the player if they're not invincible + if (!(player->invincibilitytimer > 0 + || K_IsBigger(toucher, special) == true + || K_PlayerGuard(player) == true + || player->hyudorotimer > 0)) + { + if (P_DamageMobj(toucher, special, special, 1, DMG_STUMBLE)) + { + P_SetMobjState(special, special->info->painstate); + special->eflags |= MFE_DAMAGEHITLAG; + return; + } + } + // if we didn't damage the player, just explode + P_SetMobjState(special, special->info->painstate); + P_SetMobjState(special, special->info->raisestate); // immediately explode visually + return; + case MT_CDUFO: // SRB2kart if (special->fuse || !P_CanPickupItem(player, 1)) return; @@ -832,6 +852,12 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) Obj_DashRingTouch(special, player); return; + case MT_BALLSWITCH_BALL: + { + Obj_BallSwitchTouched(special, toucher); + return; + } + default: // SOC or script pickup P_SetTarget(&special->target, toucher); break; @@ -1428,7 +1454,6 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget if (target->player == &players[displayplayers[i]]) { localaiming[i] = 0; - break; } } @@ -2413,6 +2438,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da return false; } + if (target->type == MT_BALLSWITCH_BALL) + { + Obj_BallSwitchDamaged(target, inflictor, source); + return false; + } + if (!force) { if (!spbpop) diff --git a/src/p_local.h b/src/p_local.h index 4c6b44449..15b338f9d 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -154,7 +154,7 @@ void P_AddPlayerScore(player_t *player, UINT32 amount); void P_ResetCamera(player_t *player, camera_t *thiscam); boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam); void P_SlideCameraMove(camera_t *thiscam); -void P_DemoCameraMovement(camera_t *cam); +void P_DemoCameraMovement(camera_t *cam, UINT8 num); boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcalled); void P_ToggleDemoCamera(void); @@ -167,7 +167,6 @@ boolean P_IsMachineLocalPlayer(player_t *player); boolean P_IsDisplayPlayer(player_t *player); void P_SetPlayerAngle(player_t *player, angle_t angle); -angle_t P_GetLocalAngle(player_t *player); void P_ForceLocalAngle(player_t *player, angle_t angle); boolean P_PlayerFullbright(player_t *player); @@ -256,6 +255,7 @@ mobjtype_t P_GetMobjtype(UINT16 mthingtype); void P_RespawnSpecials(void); +fixed_t P_GetMobjDefaultScale(mobj_t *mobj); mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type); void P_CalculatePrecipFloor(precipmobj_t *mobj); @@ -333,6 +333,8 @@ void P_FlashPal(player_t *pl, UINT16 type, UINT16 duration); #define PAL_RECYCLE 3 #define PAL_NUKE 4 +boolean P_MobjIsFrozen(mobj_t *mobj); + // // P_ENEMY // diff --git a/src/p_map.c b/src/p_map.c index 92a2a65d4..ff22d543a 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -573,7 +573,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) return BMIT_CONTINUE; // Ignore the collision if BOTH things are in hitlag. - if (thing->hitlag > 0 && tm.thing->hitlag > 0) + if (P_MobjIsFrozen(thing) && P_MobjIsFrozen(tm.thing)) return BMIT_CONTINUE; if ((thing->flags & MF_NOCLIPTHING) || !(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE|MF_SPRING))) diff --git a/src/p_mobj.c b/src/p_mobj.c index 2fb2d4e55..dc846a3f8 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9727,6 +9727,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj) case MT_RAINBOWDASHRING: Obj_RainbowDashRingThink(mobj); break; + case MT_BALLSWITCH_BALL: + { + Obj_BallSwitchThink(mobj); + break; + } default: // check mobj against possible water content, before movement code P_MobjCheckWater(mobj); @@ -10031,6 +10036,7 @@ void P_MobjThinker(mobj_t *mobj) mobj->flags2 &= ~(MF2_ALREADYHIT); // Don't run any thinker code while in hitlag + mobj->eflags &= ~(MFE_PAUSED); if ((mobj->player ? mobj->hitlag - mobj->player->nullHitlag : mobj->hitlag) > 0) { mobj->eflags |= MFE_PAUSED; @@ -10073,11 +10079,14 @@ void P_MobjThinker(mobj_t *mobj) if (mobj->type == MT_HITLAG && mobj->hitlag == 0) mobj->renderflags &= ~RF_DONTDRAW; */ + } + if (P_MobjIsFrozen(mobj)) + { return; } - mobj->eflags &= ~(MFE_PUSHED|MFE_SPRUNG|MFE_JUSTBOUNCEDWALL|MFE_DAMAGEHITLAG|MFE_SLOPELAUNCHED|MFE_PAUSED); + mobj->eflags &= ~(MFE_PUSHED|MFE_SPRUNG|MFE_JUSTBOUNCEDWALL|MFE_DAMAGEHITLAG|MFE_SLOPELAUNCHED); // sal: what the hell? is there any reason this isn't done, like, literally ANYWHERE else? P_SetTarget(&tm.floorthing, NULL); @@ -10528,6 +10537,20 @@ void P_SceneryThinker(mobj_t *mobj) // GAME SPAWN FUNCTIONS // +fixed_t P_GetMobjDefaultScale(mobj_t *mobj) +{ + switch(mobj->type) + { + case MT_SPECIALSTAGEARCH: + return 5*FRACUNIT; + case MT_SPECIALSTAGEBOMB: + return 3*FRACUNIT/4; + default: + break; + } + return FRACUNIT; +} + static void P_DefaultMobjShadowScale(mobj_t *thing) { thing->shadowscale = 0; @@ -10628,6 +10651,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) SINT8 sc = -1; state_t *st; mobj_t *mobj; + fixed_t scale; if (type == MT_NULL) { @@ -10680,13 +10704,12 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) // All mobjs are created at 100% scale. mobj->scale = FRACUNIT; - mobj->destscale = mobj->scale; - mobj->scalespeed = FRACUNIT/12; + mobj->destscale = mapobjectscale; + mobj->scalespeed = mapobjectscale/12; - if (mapobjectscale != FRACUNIT) //&& !(mobj->type == MT_BLACKEGGMAN) + if ((scale = P_GetMobjDefaultScale(mobj)) != FRACUNIT) { - mobj->destscale = mapobjectscale; - mobj->scalespeed = mapobjectscale/12; + mobj->destscale = FixedMul(mobj->destscale, scale); } // Sprite rendering @@ -11174,6 +11197,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) case MT_SNEAKERPANELSPAWNER: Obj_SneakerPanelSpawnerSpawn(mobj); break; + case MT_BALLSWITCH_BALL: + Obj_BallSwitchInit(mobj); + break; default: break; } @@ -11664,6 +11690,9 @@ static void P_SpawnPrecipitationAt(fixed_t basex, fixed_t basey) for (j = 0; j < numparticles; j++) { + INT32 floorz; + INT32 ceilingz; + rainmo = P_SpawnPrecipMobj(x, y, z, type); if (randomstates > 0) @@ -11682,8 +11711,19 @@ static void P_SpawnPrecipitationAt(fixed_t basex, fixed_t basey) } } - // Randomly assign a height, now that floorz is set. - rainmo->z = M_RandomRange(rainmo->floorz >> FRACBITS, rainmo->ceilingz >> FRACBITS) << FRACBITS; + floorz = rainmo->floorz >> FRACBITS; + ceilingz = rainmo->ceilingz >> FRACBITS; + + if (floorz < ceilingz) + { + // Randomly assign a height, now that floorz is set. + rainmo->z = M_RandomRange(floorz, ceilingz) << FRACBITS; + } + else + { + // ...except if the floor is above the ceiling. + rainmo->z = ceilingz << FRACBITS; + } } } } @@ -12074,13 +12114,8 @@ void P_SpawnPlayer(INT32 playernum) if ((gametyperules & GTR_BUMPERS) && !p->spectator) { - // At leveltime == 2, K_TimerInit will get called and reset - // the bumpers to the initial value for the level. - if (leveltime > 2) // Reset those bumpers! - { - mobj->health = K_BumpersToHealth(K_StartingBumperCount()); - K_SpawnPlayerBattleBumpers(p); - } + mobj->health = K_BumpersToHealth(K_StartingBumperCount()); + K_SpawnPlayerBattleBumpers(p); } // Block visuals diff --git a/src/p_mobj.h b/src/p_mobj.h index bf07a567c..585dff7c7 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -434,6 +434,8 @@ struct mobj_t INT32 script_args[NUM_SCRIPT_ARGS]; char *script_stringargs[NUM_SCRIPT_STRINGARGS]; + boolean frozen; + // WARNING: New fields must be added separately to savegame and Lua. }; diff --git a/src/p_saveg.c b/src/p_saveg.c index f48fdaa3f..86b88b0e7 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -2542,7 +2542,7 @@ typedef enum MD2_WAYPOINTCAP = 1<<25, MD2_KITEMCAP = 1<<26, MD2_ITNEXT = 1<<27, - MD2_LASTMOMZ = 1<<28, + MD2_FROZEN = 1<<28, MD2_TERRAIN = 1<<29, MD2_WATERSKIP = 1<<30, MD2_LIGHTLEVEL = (INT32)(1U<<31), @@ -2733,7 +2733,7 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8 } // not the default but the most probable - if (mobj->momx != 0 || mobj->momy != 0 || mobj->momz != 0 || mobj->pmomz != 0) + if (mobj->momx != 0 || mobj->momy != 0 || mobj->momz != 0 || mobj->pmomz != 0 || mobj->lastmomz != 0) diff |= MD_MOM; if (mobj->radius != mobj->info->radius) diff |= MD_RADIUS; @@ -2855,8 +2855,8 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8 diff2 |= MD2_KITEMCAP; if (mobj->itnext) diff2 |= MD2_ITNEXT; - if (mobj->lastmomz) - diff2 |= MD2_LASTMOMZ; + if (mobj->frozen) + diff2 |= MD2_FROZEN; if (mobj->terrain != NULL || mobj->terrainOverlay != NULL) diff2 |= MD2_TERRAIN; @@ -2919,6 +2919,7 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8 WRITEFIXED(save->p, mobj->momy); WRITEFIXED(save->p, mobj->momz); WRITEFIXED(save->p, mobj->pmomz); + WRITEFIXED(save->p, mobj->lastmomz); } if (diff & MD_RADIUS) WRITEFIXED(save->p, mobj->radius); @@ -3123,9 +3124,9 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8 { WRITEINT32(save->p, mobj->dispoffset); } - if (diff2 & MD2_LASTMOMZ) + if (diff2 & MD2_FROZEN) { - WRITEINT32(save->p, mobj->lastmomz); + WRITEUINT8(save->p, mobj->frozen); } if (diff2 & MD2_TERRAIN) { @@ -4102,6 +4103,7 @@ static thinker_t* LoadMobjThinker(savebuffer_t *save, actionf_p1 thinker) mobj->momy = READFIXED(save->p); mobj->momz = READFIXED(save->p); mobj->pmomz = READFIXED(save->p); + mobj->lastmomz = READFIXED(save->p); } // otherwise they're zero, and the memset took care of it if (diff & MD_RADIUS) @@ -4357,9 +4359,9 @@ static thinker_t* LoadMobjThinker(savebuffer_t *save, actionf_p1 thinker) { mobj->dispoffset = READINT32(save->p); } - if (diff2 & MD2_LASTMOMZ) + if (diff2 & MD2_FROZEN) { - mobj->lastmomz = READINT32(save->p); + mobj->frozen = (boolean)READUINT8(save->p); } if (diff2 & MD2_TERRAIN) { diff --git a/src/p_setup.c b/src/p_setup.c index 94d5e84d7..44ae440f0 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7539,6 +7539,9 @@ static void P_InitLevelSettings(void) leveltime = 0; modulothing = 0; + P_SetFreezeLevel(false); + P_SetFreezeCheat(false); + K_TimerReset(); nummaprings = 0; diff --git a/src/p_spec.c b/src/p_spec.c index 8a56c4709..599ee327f 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2867,7 +2867,6 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha camera[i].z += z; camera[i].subsector = R_PointInSubsector(camera[i].x, camera[i].y); R_RelativeTeleportViewInterpolation(i, x, y, z, 0); - break; } } } diff --git a/src/p_telept.c b/src/p_telept.c index 779c152c4..641e070c7 100644 --- a/src/p_telept.c +++ b/src/p_telept.c @@ -79,7 +79,6 @@ void P_MixUp(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, if (camera[i].chase) P_ResetCamera(thing->player, &camera[i]); R_ResetViewInterpolation(i + 1); - break; } // don't run in place after a teleport @@ -170,8 +169,6 @@ boolean P_Teleport(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle } R_ResetViewInterpolation(1 + i); - - break; } } diff --git a/src/p_tick.c b/src/p_tick.c index f411aa382..957dc93f0 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -52,6 +52,63 @@ tic_t leveltime; boolean thinkersCompleted; +static boolean g_freezeCheat; +static boolean g_freezeLevel; + +boolean P_LevelIsFrozen(void) +{ + return (g_freezeLevel || g_freezeCheat); +} + +boolean P_FreezeCheat(void) +{ + return (g_freezeLevel || g_freezeCheat); +} + +void P_SetFreezeCheat(boolean value) +{ + g_freezeCheat = value; +} + +void P_SetFreezeLevel(boolean value) +{ + g_freezeLevel = value; +} + +boolean P_MobjIsFrozen(mobj_t *mobj) +{ + if (g_freezeCheat == true) + { + // freeze cheat + switch (mobj->type) + { + case MT_PLAYER: + { + break; + } + default: + { + return true; + } + } + } + + if (g_freezeLevel == true) + { + // level totally frozen + return true; + } + + if ((mobj->eflags & MFE_PAUSED) == MFE_PAUSED) + { + // hitlag + return true; + } + + // manual + return mobj->frozen; +} + INT32 P_AltFlip(INT32 n, tic_t tics) { return leveltime % (2 * tics) < tics ? n : -(n); diff --git a/src/p_tick.h b/src/p_tick.h index d05813c1c..58c154a1a 100644 --- a/src/p_tick.h +++ b/src/p_tick.h @@ -27,6 +27,12 @@ extern "C" { extern tic_t leveltime; extern boolean thinkersCompleted; +boolean P_LevelIsFrozen(void); +boolean P_FreezeCheat(void); +void P_SetFreezeCheat(boolean value); +void P_SetFreezeLevel(boolean value); +boolean P_MobjIsFrozen(mobj_t *mobj); + // Called by G_Ticker. Carries out all thinking of enemies and players. void Command_Numthinkers_f(void); void Command_CountMobjs_f(void); diff --git a/src/p_user.c b/src/p_user.c index 15b96298a..122a8964e 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2887,7 +2887,7 @@ fixed_t t_cam_rotate[MAXSPLITSCREENPLAYERS] = {-42,-42,-42,-42}; struct demofreecam_s democam; -void P_DemoCameraMovement(camera_t *cam) +void P_DemoCameraMovement(camera_t *cam, UINT8 num) { ticcmd_t *cmd; angle_t thrustangle; @@ -2897,7 +2897,7 @@ void P_DemoCameraMovement(camera_t *cam) boolean moving = false; // first off we need to get button input - cmd = D_LocalTiccmd(0); + cmd = D_LocalTiccmd(num); if (cmd->aiming != 0) { @@ -3083,18 +3083,6 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (thiscam->subsector == NULL || thiscam->subsector->sector == NULL) return true; - if (demo.freecam || player->spectator) - { - P_DemoCameraMovement(thiscam); - return true; - } - - if (paused || P_AutoPause()) - return true; - - playerScale = FixedDiv(player->mo->scale, mapobjectscale); - scaleDiff = playerScale - FRACUNIT; - if (thiscam == &camera[1]) // Camera 2 { num = 1; @@ -3112,9 +3100,21 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall num = 0; } + if (demo.freecam || player->spectator) + { + P_DemoCameraMovement(thiscam, num); + return true; + } + + if (paused || P_AutoPause()) + return true; + + playerScale = FixedDiv(player->mo->scale, mapobjectscale); + scaleDiff = playerScale - FRACUNIT; + mo = player->mo; - if (mo->hitlag > 0 || player->playerstate == PST_DEAD) + if (P_MobjIsFrozen(mo) || player->playerstate == PST_DEAD) { // Do not move the camera while in hitlag! // The camera zooming out after you got hit makes it hard to focus on the vibration. @@ -3544,19 +3544,10 @@ boolean P_SpectatorJoinGame(player_t *player) player->enteredGame = true; // Reset away view (some code referenced from Got_Teamchange) + if (G_IsPartyLocal(player - players)) { - UINT8 i = 0; - const UINT8 *localplayertable = G_PartyArray(consoleplayer); - - for (i = 0; i <= r_splitscreen; i++) - { - if (localplayertable[i] == (player-players)) - { - LUA_HookViewpointSwitch(player, player, true); - displayplayers[i] = (player-players); - break; - } - } + LUA_HookViewpointSwitch(player, player, true); + displayplayers[G_PartyPosition(player - players)] = (player-players); } // a surprise tool that will help us later... @@ -3572,11 +3563,11 @@ boolean P_SpectatorJoinGame(player_t *player) } // the below is first person only, if you're curious. check out P_CalcChasePostImg in p_mobj.c for chasecam -static void P_CalcPostImg(player_t *player) +static void P_CalcPostImg(player_t *player, size_t viewnum) { sector_t *sector = player->mo->subsector->sector; - postimg_t *type = NULL; - INT32 *param; + postimg_t *type = &postimgtype[viewnum]; + INT32 *param = &postimgparam[viewnum]; fixed_t pviewheight; size_t i; @@ -3591,16 +3582,6 @@ static void P_CalcPostImg(player_t *player) pviewheight = player->awayview.mobj->z; } - for (i = 0; i <= (unsigned)r_splitscreen; i++) - { - if (player == &players[displayplayers[i]]) - { - type = &postimgtype[i]; - param = &postimgparam[i]; - break; - } - } - // see if we are in heat (no, not THAT kind of heat...) for (i = 0; i < sector->tags.count; i++) { @@ -4208,7 +4189,7 @@ void P_PlayerThink(player_t *player) ALL ABOVE THIS BLOCK OCCURS EVEN WITH HITLAG / ------------------------------------------ */ - if (player->mo->hitlag > 0) + if (P_MobjIsFrozen(player->mo)) { return; } @@ -4400,7 +4381,6 @@ void P_PlayerThink(player_t *player) // void P_PlayerAfterThink(player_t *player) { - camera_t *thiscam = NULL; // if not one of the displayed players, just don't bother UINT8 i; #ifdef PARANOIA @@ -4425,15 +4405,6 @@ void P_PlayerAfterThink(player_t *player) P_PlayerInSpecialSector(player); #endif - for (i = 0; i <= r_splitscreen; i++) - { - if (player == &players[displayplayers[i]]) - { - thiscam = &camera[i]; - break; - } - } - if (player->playerstate == PST_DEAD) { // Followers need handled while dead. @@ -4448,12 +4419,20 @@ void P_PlayerAfterThink(player_t *player) return; } - if (thiscam) { - if (!thiscam->chase) // bob view only if looking through the player's eyes + boolean chase = true; + + for (i = 0; i <= r_splitscreen; i++) + { + if (player == &players[displayplayers[i]] && !camera[i].chase) + { + chase = false; + } + } + + if (!chase) // bob view only if looking through the player's eyes { P_CalcHeight(player); - P_CalcPostImg(player); } else { @@ -4466,6 +4445,14 @@ void P_PlayerAfterThink(player_t *player) else player->viewz = player->mo->z + player->viewheight; } + + for (i = 0; i <= r_splitscreen; i++) + { + if (player == &players[displayplayers[i]] && !camera[i].chase) + { + P_CalcPostImg(player, i); + } + } } // spectator invisibility and nogravity. @@ -4530,10 +4517,15 @@ void P_PlayerAfterThink(player_t *player) K_UpdateBotGameplayVars(player); } - if (thiscam) + for (i = 0; i <= r_splitscreen; i++) { + if (player != &players[displayplayers[i]]) + { + continue; + } + // Store before it gets 0'd out - thiscam->pmomz = player->mo->pmomz; + camera[i].pmomz = player->mo->pmomz; } if (P_IsObjectOnGround(player->mo)) @@ -4546,7 +4538,7 @@ void P_IncrementGriefValue(player_t *player, UINT32 *grief, const UINT32 griefMa INT32 progress = player->distancetofinishprev - player->distancetofinish; boolean exceptions = ( player->flashing != 0 - || player->mo->hitlag != 0 + || P_MobjIsFrozen(player->mo) || player->airtime > 3*TICRATE/2 || (player->justbumped > 0 && player->justbumped < bumptime-1) ); @@ -4632,21 +4624,6 @@ void P_SetPlayerAngle(player_t *player, angle_t angle) player->angleturn = angle; } -angle_t P_GetLocalAngle(player_t *player) -{ - // this function is from vanilla srb2. can you tell? - // (hint: they have separate variables for all of this shit instead of arrays) - UINT8 i; - - for (i = 0; i <= r_splitscreen; i++) - { - if (player == &players[displayplayers[i]]) - return localangle[i]; - } - - return 0; -} - void P_ForceLocalAngle(player_t *player, angle_t angle) { UINT8 i; @@ -4658,8 +4635,6 @@ void P_ForceLocalAngle(player_t *player, angle_t angle) if (player == &players[displayplayers[i]]) { localangle[i] = angle; - - break; } } diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp index d89333bb7..f4e11bad4 100644 --- a/src/r_bsp.cpp +++ b/src/r_bsp.cpp @@ -20,6 +20,7 @@ #include "r_local.h" #include "r_state.h" #include "r_portal.h" // Add seg portals +#include "r_fps.h" #include "r_splats.h" #include "p_local.h" // camera @@ -276,18 +277,11 @@ sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel, mobj_t *viewmobj = viewplayer->mo; INT32 heightsec; boolean underwater; - UINT8 i; + UINT8 i = R_GetViewNumber(); - for (i = 0; i <= r_splitscreen; i++) - { - if (viewplayer == &players[displayplayers[i]] && camera[i].chase) - { - heightsec = R_PointInSubsector(camera[i].x, camera[i].y)->sector->heightsec; - break; - } - } - - if (i > r_splitscreen && viewmobj) + if (camera[i].chase) + heightsec = R_PointInSubsector(camera[i].x, camera[i].y)->sector->heightsec; + else if (i > r_splitscreen && viewmobj) heightsec = R_PointInSubsector(viewmobj->x, viewmobj->y)->sector->heightsec; else return sec; diff --git a/src/r_fps.c b/src/r_fps.c index c15a4b632..09c94e2a5 100644 --- a/src/r_fps.c +++ b/src/r_fps.c @@ -22,6 +22,7 @@ #include "r_state.h" #include "z_zone.h" #include "console.h" // con_startup_loadprogress +#include "i_time.h" UINT32 R_GetFramerateCap(void) { @@ -58,6 +59,7 @@ static viewvars_t skyview_old[MAXSPLITSCREENPLAYERS]; static viewvars_t skyview_new[MAXSPLITSCREENPLAYERS]; static viewvars_t *oldview = &pview_old[0]; +static tic_t last_view_update; static int oldview_invalid[MAXSPLITSCREENPLAYERS] = {0, 0, 0, 0}; viewvars_t *newview = &pview_new[0]; @@ -165,21 +167,27 @@ void R_UpdateViewInterpolation(void) skyview_old[i] = skyview_new[i]; if (oldview_invalid[i] > 0) oldview_invalid[i]--; } + + last_view_update = I_GetTime(); } void R_ResetViewInterpolation(UINT8 p) { + // Wait an extra tic if the interpolation state hasn't + // updated yet. + int t = last_view_update == I_GetTime() ? 1 : 2; + if (p == 0) { UINT8 i; for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { - oldview_invalid[i]++; + oldview_invalid[i] = t; } } else { - oldview_invalid[p - 1]++; + oldview_invalid[p - 1] = t; } } diff --git a/src/r_main.c b/src/r_main.c index 929175e1c..3dc986b4a 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -218,7 +218,7 @@ void ChaseCam4_OnChange(void) // // killough 5/2/98: reformatted // -INT32 R_PointOnSide(fixed_t x, fixed_t y, node_t *node) +INT32 R_PointOnSide(fixed_t x, fixed_t y, node_t *restrict node) { if (!node->dx) return x <= node->x ? node->dy > 0 : node->dy < 0; @@ -230,9 +230,10 @@ INT32 R_PointOnSide(fixed_t x, fixed_t y, node_t *node) y -= node->y; // Try to quickly decide by looking at sign bits. - if ((node->dy ^ node->dx ^ x ^ y) < 0) - return (node->dy ^ x) < 0; // (left is negative) - return FixedMul(y, node->dx>>FRACBITS) >= FixedMul(node->dy>>FRACBITS, x); + // also use a mask to avoid branch prediction + INT32 mask = (node->dy ^ node->dx ^ x ^ y) >> 31; + return (mask & ((node->dy ^ x) < 0)) | // (left is negative) + (~mask & (FixedMul(y, node->dx>>FRACBITS) >= FixedMul(node->dy>>FRACBITS, x))); } // killough 5/2/98: reformatted diff --git a/src/r_plane.cpp b/src/r_plane.cpp index e5e29ea0a..1df08da37 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -20,6 +20,7 @@ #include "g_game.h" #include "p_setup.h" // levelflats #include "p_slopes.h" +#include "r_fps.h" #include "r_data.h" #include "r_textures.h" #include "r_local.h" @@ -963,8 +964,6 @@ void R_DrawSinglePlane(visplane_t *pl) planeripple.active = true; if (spanfunctype == SPANDRAWFUNC_TRANS) { - UINT8 i; - spanfunctype = SPANDRAWFUNC_WATER; // Copy the current scene, ugh @@ -977,43 +976,38 @@ void R_DrawSinglePlane(visplane_t *pl) bottom = viewheight; // Only copy the part of the screen we need - for (i = 0; i <= r_splitscreen; i++) + UINT8 i = R_GetViewNumber(); + INT32 scrx = 0; + INT32 scry = top; + INT32 offset; + + if (r_splitscreen == 1) { - if (viewplayer == &players[displayplayers[i]]) + if (i & 1) { - INT32 scrx = 0; - INT32 scry = top; - INT32 offset; - - if (r_splitscreen == 1) - { - if (i & 1) - { - scry += viewheight; - } - } - else - { - if (i & 1) - { - scrx += viewwidth; - } - - if (i / 2) - { - scry += viewheight; - } - } - - offset = (scry*vid.width) + scrx; - - // No idea if this works - VID_BlitLinearScreen(screens[0] + offset, - screens[1] + (top*vid.width), // intentionally not +offset - viewwidth, bottom-top, - vid.width, vid.width); + scry += viewheight; } } + else + { + if (i & 1) + { + scrx += viewwidth; + } + + if (i / 2) + { + scry += viewheight; + } + } + + offset = (scry*vid.width) + scrx; + + // No idea if this works + VID_BlitLinearScreen(screens[0] + offset, + screens[1] + (top*vid.width), // intentionally not +offset + viewwidth, bottom-top, + vid.width, vid.width); } } #endif diff --git a/src/screen.c b/src/screen.c index 3cfb86f3f..0e4bbb109 100644 --- a/src/screen.c +++ b/src/screen.c @@ -532,12 +532,9 @@ void SCR_DisplayTicRate(void) const UINT8 *ticcntcolor = NULL; UINT32 cap = R_GetFramerateCap(); UINT32 benchmark = (cap == 0) ? I_GetRefreshRate() : cap; - INT32 x = 318; + INT32 x = 317; double fps = round(averageFPS); - // draw "FPS" - V_DrawFixedPatch(306< (benchmark * 0.9)) ticcntcolor = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_MINT, GTC_CACHE); else if (fps < (benchmark * 0.5)) @@ -571,15 +568,28 @@ void SCR_DisplayTicRate(void) void SCR_DisplayLocalPing(void) { - boolean offline; - - UINT32 ping = playerpingtable[consoleplayer]; // consoleplayer's ping is everyone's ping in a splitnetgame :P - if (! r_splitscreen && ( cv_showping.value == 1 || (cv_showping.value == 2 && ping > servermaxping) )) // only show 2 (warning) if our ping is at a bad level + // Splitscreen party has its own ping counter, but show the + // 1P version anyway in some cases: + // - On intermission, vote, etc. gamestates where the player + // HUD is not drawn. + // - If the menu is opened, since it draws over the player + // HUD. + if (r_splitscreen && gamestate == GS_LEVEL && !menuactive) { - INT32 dispy = cv_ticrate.value ? 160 : 181; - offline = (consoleplayer == serverplayer); - HU_drawPing(307 * FRACUNIT, dispy * FRACUNIT, ping, V_SNAPTORIGHT | V_SNAPTOBOTTOM | V_HUDTRANS, offline, 0); + return; } + + UINT32 ping = playerpingtable[consoleplayer]; + + if (cv_showping.value == 2 && ping <= servermaxping) // only show 2 (warning) if our ping is at a bad level + { + return; + } + + INT32 dispy = cv_ticrate.value ? 170 : 181; + boolean offline = (consoleplayer == serverplayer); + + HU_drawPing(307 * FRACUNIT, dispy * FRACUNIT, ping, V_SNAPTORIGHT | V_SNAPTOBOTTOM, offline, 0); } diff --git a/src/sdl/i_video.cpp b/src/sdl/i_video.cpp index 84af0eed8..329b52476 100644 --- a/src/sdl/i_video.cpp +++ b/src/sdl/i_video.cpp @@ -235,6 +235,11 @@ static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen, SDL_bool SDL_GetWindowSize(window, &width, &height); vid.realwidth = static_cast(width); vid.realheight = static_cast(height); + + if (graphics_started) + { + I_UpdateNoVsync(); + } } static INT32 Impl_SDL_Scancode_To_Keycode(SDL_Scancode code) @@ -501,6 +506,11 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt) case SDL_WINDOWEVENT_MOVED: window_x = evt.data1; window_y = evt.data2; + break; + case SDL_WINDOWEVENT_SIZE_CHANGED: + vid.realwidth = evt.data1; + vid.realheight = evt.data2; + break; } if (FOCUSUNION == oldfocus) // No state change @@ -1535,7 +1545,7 @@ INT32 VID_SetMode(INT32 modeNum) static SDL_bool Impl_CreateWindow(SDL_bool fullscreen) { - int flags = 0; + uint32_t flags = SDL_WINDOW_RESIZABLE; if (rendermode == render_none) // dedicated return SDL_TRUE; // Monster Iestyn -- not sure if it really matters what we return here tbh diff --git a/src/st_stuff.c b/src/st_stuff.c index 85eb89706..153e2a48b 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1203,7 +1203,7 @@ static void ST_overlayDrawer(void) { char name[MAXPLAYERNAME+12]; - INT32 y = (stplyr == &players[displayplayers[0]]) ? 4 : BASEVIDHEIGHT/2-12; + INT32 y = (viewnum == 0) ? 4 : BASEVIDHEIGHT/2-12; sprintf(name, "VIEWPOINT: %s", player_names[stplyr-players]); V_DrawRightAlignedThinString(BASEVIDWIDTH-40, y, V_HUDTRANSHALF|V_SNAPTOTOP|V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, name); } @@ -1451,8 +1451,15 @@ static fixed_t ST_CalculateFadeIn(player_t *player) if (player->tally.hudSlide != 0) { tic_t timer = length - player->tally.hudSlide; + fixed_t f = timer * FRACUNIT; - return ((timer * FRACUNIT) + (FRACUNIT - rendertimefrac)) / length; + // Not interpolated at very beginning or end + if (timer > 0 && timer < length) + { + f += FRACUNIT - rendertimefrac; + } + + return f / length; } tic_t timer = lt_exitticker; @@ -1467,9 +1474,18 @@ static fixed_t ST_CalculateFadeIn(player_t *player) if (timer < length) { - return ((timer * FRACUNIT) + rendertimefrac) / length; + fixed_t f = timer * FRACUNIT; + + // Not interpolated at very beginning + if (timer > 0) + { + f += rendertimefrac; + } + + return f / length; } + // Not interpolated at very end return FRACUNIT; } diff --git a/src/v_video.cpp b/src/v_video.cpp index e0f2bf791..2aa4067a6 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -27,6 +27,7 @@ #include "f_finale.h" #include "r_draw.h" #include "console.h" +#include "r_fps.h" #include "i_video.h" // rendermode #include "z_zone.h" @@ -513,8 +514,7 @@ void V_AdjustXYWithSnap(INT32 *x, INT32 *y, UINT32 options, INT32 dupx, INT32 du INT32 screenheight = vid.height; INT32 basewidth = BASEVIDWIDTH * dupx; INT32 baseheight = BASEVIDHEIGHT * dupy; - SINT8 player = -1; - UINT8 i; + SINT8 player = R_GetViewNumber(); if (options & V_SPLITSCREEN) { @@ -531,15 +531,6 @@ void V_AdjustXYWithSnap(INT32 *x, INT32 *y, UINT32 options, INT32 dupx, INT32 du } } - for (i = 0; i <= r_splitscreen; i++) - { - if (stplyr == &players[displayplayers[i]]) - { - player = i; - break; - } - } - if (vid.width != (BASEVIDWIDTH * dupx)) { if (options & V_SNAPTORIGHT) @@ -586,7 +577,7 @@ void V_AdjustXYWithSnap(INT32 *x, INT32 *y, UINT32 options, INT32 dupx, INT32 du if (r_splitscreen > 1) { - if (stplyr == &players[displayplayers[1]] || stplyr == &players[displayplayers[3]]) + if (player & 1) slidefromright = true; }