diff --git a/src/console.c b/src/console.c index bf2dd069f..282f833a4 100644 --- a/src/console.c +++ b/src/console.c @@ -1512,6 +1512,8 @@ void CONS_Printf(const char *fmt, ...) vsprintf(txt, fmt, argptr); va_end(argptr); + Lock_state(); + // echo console prints to log file DEBFILE(txt); @@ -1521,8 +1523,6 @@ void CONS_Printf(const char *fmt, ...) CON_LogMessage(txt); - Lock_state(); - // make sure new text is visible con_scrollup = 0; diff --git a/src/cvars.cpp b/src/cvars.cpp index a9e9d1243..1aa0d9a0b 100644 --- a/src/cvars.cpp +++ b/src/cvars.cpp @@ -359,6 +359,7 @@ consvar_t cv_drawdist_precip = Player("drawdist_precip", "1024").values({ {0, "None"}, }); +consvar_t cv_drawinput = Player("drawinput", "No").yes_no(); consvar_t cv_ffloorclip = Player("ffloorclip", "On").on_off(); consvar_t cv_fpscap = Player("fpscap", "Match refresh rate").values({ @@ -445,7 +446,6 @@ consvar_t cv_showfocuslost = Player("showfocuslost", "Yes").yes_no(); void R_SetViewSize(void); consvar_t cv_showhud = Player("showhud", "Yes").yes_no().onchange(R_SetViewSize).dont_save(); -consvar_t cv_showinputjoy = Player("showinputjoy", "Off").on_off().dont_save(); consvar_t cv_skybox = Player("skybox", "On").on_off(); // Display song credits @@ -804,6 +804,7 @@ consvar_t cv_capsuletest = OnlineCheat("capsuletest", "Off").values(capsuletest_ consvar_t cv_debugcheese = OnlineCheat("debugcheese", "Off").on_off().description("Disable checks that prevent farming item boxes"); consvar_t cv_debugencorevote = OnlineCheat("debugencorevote", "Off").on_off().description("Force encore choice to appear on vote screen"); +consvar_t cv_debuglapcheat = OnlineCheat("debuglapcheat", "Off").on_off().description("Permit far waypoint jumps and disable lap cheat prevention"); consvar_t cv_forcebots = OnlineCheat("forcebots", "No").yes_no().description("Force bots to appear, even in wrong game modes"); void ForceSkin_OnChange(void); @@ -816,6 +817,8 @@ consvar_t cv_kartdebugbots = OnlineCheat("debugbots", "Off").on_off().descriptio consvar_t cv_kartdebugdistribution = OnlineCheat("debugitemodds", "Off").on_off().description("Show items that the roulette can roll"); consvar_t cv_kartdebughuddrop = OnlineCheat("debugitemdrop", "Off").on_off().description("Players drop paper items when damaged in any way"); +consvar_t cv_kartdebugbotwhip = OnlineCheat("debugbotwhip", "Off").on_off().description("Disable bot ring and item pickups"); + extern CV_PossibleValue_t kartdebugitem_cons_t[]; consvar_t cv_kartdebugitem = OnlineCheat("debugitem", "None").values(kartdebugitem_cons_t).description("Force item boxes to only roll one kind of item"); @@ -892,7 +895,6 @@ consvar_t cv_debugrender_spriteclip = PlayerCheat("debugrender_spriteclip", "Off consvar_t cv_debugrender_visplanes = PlayerCheat("debugrender_visplanes", "Off").on_off().description("Highlight the number of visplanes"); consvar_t cv_devmode_screen = PlayerCheat("devmode_screen", "1").min_max(1, 4).description("Choose which splitscreen player devmode applies to"); consvar_t cv_drawpickups = PlayerCheat("drawpickups", "Yes").yes_no().description("Hide rings, spheres, item capsules, prison capsules (visual only)"); -consvar_t cv_drawinput = PlayerCheat("drawinput", "No").yes_no().description("Draw turn inputs outside of Record Attack (turn solver debugging)"); void lua_profile_OnChange(void); consvar_t cv_lua_profile = PlayerCheat("lua_profile", "0").values(CV_Unsigned).onchange(lua_profile_OnChange).description("Show hook timings over an average of N tics"); diff --git a/src/d_main.cpp b/src/d_main.cpp index 793c14013..19fd9c1db 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -278,12 +278,6 @@ void D_ProcessEvents(void) HandleGamepadDeviceEvents(ev); - if (demo.savemode == demovars_s::DSM_TITLEENTRY) - { - if (G_DemoTitleResponder(ev)) - continue; - } - // console input #ifdef HAVE_THREADS I_lock_mutex(&con_mutex); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index cc16907fa..fdda510dd 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2878,7 +2878,7 @@ static void Got_Mapcmd(const UINT8 **cp, INT32 playernum) if (demo.playback && !demo.timing) precache = false; - demo.savemode = (cv_recordmultiplayerdemos.value == 2) ? DSM_WILLAUTOSAVE : DSM_NOTSAVING; + demo.willsave = (cv_recordmultiplayerdemos.value == 2); demo.savebutton = 0; G_InitNew(pencoremode, mapnumber, presetplayer, skipprecutscene); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index d08ad5f7b..6316cb8b3 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -94,6 +94,7 @@ extern consvar_t cv_kartdebugitem, cv_kartdebugamount, cv_kartdebugdistribution, extern consvar_t cv_kartdebugnodes, cv_kartdebugcolorize, cv_kartdebugdirector; extern consvar_t cv_spbtest, cv_reducevfx, cv_screenshake; extern consvar_t cv_kartdebugwaypoints, cv_kartdebugbots; +extern consvar_t cv_kartdebugbotwhip; extern consvar_t cv_kartdebugstart; extern consvar_t cv_debugrank; extern consvar_t cv_battletest; diff --git a/src/deh_soc.c b/src/deh_soc.c index 88755ead8..8817ad40b 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -1299,6 +1299,8 @@ void readlevelheader(MYFILE *f, char * name) mapheaderinfo[num]->encorepal = (UINT16)i; else if (fastcmp(word, "NUMLAPS")) mapheaderinfo[num]->numlaps = (UINT8)i; + else if (fastcmp(word, "LAPSPERSECTION")) + mapheaderinfo[num]->lapspersection = max((UINT8)i, 1u); else if (fastcmp(word, "SKYBOXSCALE")) mapheaderinfo[num]->skybox_scalex = mapheaderinfo[num]->skybox_scaley = mapheaderinfo[num]->skybox_scalez = (INT16)i; else if (fastcmp(word, "SKYBOXSCALEX")) diff --git a/src/doomstat.h b/src/doomstat.h index 2515d9e1d..c74dda7ac 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -509,6 +509,7 @@ struct mapheader_t UINT16 levelflags; ///< LF_flags: merged booleans into one UINT16 for space, see below UINT32 typeoflevel; ///< Combination of typeoflevel flags. UINT8 numlaps; ///< Number of laps in circuit mode, unless overridden. + UINT8 lapspersection; ///< Number of laps per section in hybrid section-circuit maps. fixed_t gravity; ///< Map-wide gravity. char relevantskin[SKINNAMESIZE+1]; ///< Skin to use for tutorial (if not provided, uses Eggman.) @@ -927,7 +928,6 @@ extern tic_t g_fast_forward; #include "d_clisrv.h" -extern consvar_t cv_showinputjoy; // display joystick in time attack extern consvar_t cv_forceskin; // force clients to use the server's skin extern consvar_t cv_downloading; // allow clients to downloading WADs. extern consvar_t cv_nettimeout; // SRB2Kart: Advanced server options menu diff --git a/src/g_demo.cpp b/src/g_demo.cpp index eac0b1989..e727eacba 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -65,6 +65,30 @@ #include "k_credits.h" #include "k_grandprix.h" +static menuitem_t TitleEntry[] = +{ + {IT_NOTHING | IT_SPACE, "Save Replay", NULL, + NULL, {NULL}, 0, 0}, +}; + +static menu_t TitleEntryDef = { + sizeof (TitleEntry) / sizeof (menuitem_t), + NULL, + 0, + TitleEntry, + 0, 0, + 0, 0, + MBF_SOUNDLESS, + NULL, + 0, 0, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + boolean nodrawers; // for comparative timing purposes boolean noblit; // for comparative timing purposes tic_t demostarttime; // for comparative timing purposes @@ -1752,6 +1776,9 @@ void G_ConfirmRewind(tic_t rewindtime) // void G_RecordDemo(const char *name) { + if (demo.recording) + G_CheckDemoStatus(); + extern consvar_t cv_netdemosize; INT32 maxsize; @@ -3968,7 +3995,7 @@ boolean G_CheckDemoStatus(void) if (!demo.recording) return false; - if (modeattacking || demo.savemode != demovars_s::DSM_NOTSAVING) + if (modeattacking || demo.willsave) { if (demobuf.p) { @@ -3992,6 +4019,9 @@ void G_SaveDemo(void) UINT8 i; #endif + if (currentMenu == &TitleEntryDef) + M_ClearMenus(true); + // Ensure extrainfo pointer is always available, even if no info is present. if (demoinfo_p && *(UINT32 *)demoinfo_p == 0) { @@ -4054,14 +4084,13 @@ void G_SaveDemo(void) md5_buffer((char *)p+16, (demobuf.buffer + length) - (p+16), p); #endif - if (FIL_WriteFile(demoname, demobuf.buffer, demobuf.p - demobuf.buffer)) // finally output the file. - demo.savemode = demovars_s::DSM_SAVED; + bool saved = FIL_WriteFile(demoname, demobuf.buffer, demobuf.p - demobuf.buffer); // finally output the file. Z_Free(demobuf.buffer); demo.recording = false; if (!modeattacking) { - if (demo.savemode == demovars_s::DSM_SAVED) + if (saved) { CONS_Printf(M_GetText("Demo %s recorded\n"), demoname); if (gamedata->eversavedreplay == false) @@ -4076,55 +4105,6 @@ void G_SaveDemo(void) } } -boolean G_DemoTitleResponder(event_t *ev) -{ - size_t len; - INT32 ch; - - if (ev->type != ev_keydown) - return false; - - ch = (INT32)ev->data1; - - // Only ESC and non-keyboard keys abort connection - if (ch == KEY_ESCAPE) - { - demo.savemode = (cv_recordmultiplayerdemos.value == 2) ? demovars_s::DSM_WILLAUTOSAVE : demovars_s::DSM_NOTSAVING; - return true; - } - - if (ch == KEY_ENTER || ch >= NUMKEYS) - { - demo.savemode = demovars_s::DSM_WILLSAVE; - return true; - } - - if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && fontv[HU_FONT].font[ch-HU_FONTSTART]) - || ch == ' ') // Allow spaces, of course - { - len = strlen(demo.titlename); - if (len < 64) - { - demo.titlename[len+1] = 0; - demo.titlename[len] = CON_ShiftChar(ch); - } - } - else if (ch == KEY_BACKSPACE) - { - if (shiftdown) - memset(demo.titlename, 0, sizeof(demo.titlename)); - else - { - len = strlen(demo.titlename); - - if (len > 0) - demo.titlename[len-1] = 0; - } - } - - return true; -} - boolean G_CheckDemoTitleEntry(void) { if (menuactive || chat_on) @@ -4133,7 +4113,18 @@ boolean G_CheckDemoTitleEntry(void) if (!G_PlayerInputDown(0, gc_b, 0) && !G_PlayerInputDown(0, gc_x, 0)) return false; - demo.savemode = demovars_s::DSM_TITLEENTRY; + demo.willsave = true; + M_OpenVirtualKeyboard( + false, + sizeof demo.titlename, + [](const char* replace) -> const char* + { + if (replace) + strlcpy(demo.titlename, replace, sizeof demo.titlename); + return demo.titlename; + }, + &TitleEntryDef + ); return true; } diff --git a/src/g_demo.h b/src/g_demo.h index 4e89b24ef..91b2c7ee7 100644 --- a/src/g_demo.h +++ b/src/g_demo.h @@ -90,13 +90,7 @@ struct demovars_s { boolean netgame; // multiplayer netgame tic_t savebutton; // Used to determine when the local player can choose to save the replay while the race is still going - enum { - DSM_NOTSAVING, - DSM_WILLAUTOSAVE, - DSM_TITLEENTRY, - DSM_WILLSAVE, - DSM_SAVED - } savemode; + boolean willsave; boolean freecam; @@ -232,8 +226,6 @@ void G_DeferedPlayDemo(const char *demo); void G_SaveDemo(void); -boolean G_DemoTitleResponder(event_t *ev); - boolean G_CheckDemoTitleEntry(void); typedef enum diff --git a/src/g_game.c b/src/g_game.c index 976b18392..3ca13d642 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3933,15 +3933,68 @@ void G_GPCupIntoRoundQueue(cupheader_t *cup, UINT8 setgametype, boolean setencor // At the end of the Cup is a Rank-restricted treat. // So we append it to the end of the roundqueue. // (as long as it exists, of course!) - cupLevelNum = cup->cachedlevels[CUPCACHE_SPECIAL]; - if (cupLevelNum < nummapheaders) { - G_MapIntoRoundQueue( - cupLevelNum, - G_GuessGametypeByTOL(mapheaderinfo[cupLevelNum]->typeoflevel), - setencore, // if this isn't correct, Got_Mapcmd will fix it - true // Rank-restricted! - ); + // Of course, this last minute game design tweak + // has to make things a little complicated. We + // basically just make sure they're dispensed + // at the intended difficulty sequence until + // you've got them all, at which point they + // become their intended order permanently. + // ~toast 010324 + cupheader_t *emeraldcup = NULL; + + if (gamedata->sealedswaps[GDMAX_SEALEDSWAPS-1] != NULL // all found + || cup->id >= basenumkartcupheaders // custom content + || M_SecretUnlocked(SECRET_SPECIALATTACK, false)) // true order + { + // Standard order. + emeraldcup = cup; + } + else + { + // Determine order from sealedswaps. + for (i = 0; (i < GDMAX_SEALEDSWAPS && gamedata->sealedswaps[i]); i++) + { + if (gamedata->sealedswaps[i] != grandprixinfo.cup) + continue; + + // Repeat visit, grab the same ID. + break; + } + + // If there's pending stars, get them from the associated cup order. + if (i < GDMAX_SEALEDSWAPS) + { + emeraldcup = kartcupheaders; + while (emeraldcup) + { + if (emeraldcup->id >= basenumkartcupheaders) + { + emeraldcup = NULL; + break; + } + + if (emeraldcup->emeraldnum == i+1) + break; + + emeraldcup = emeraldcup->next; + } + } + } + + if (emeraldcup) + { + cupLevelNum = emeraldcup->cachedlevels[CUPCACHE_SPECIAL]; + if (cupLevelNum < nummapheaders) + { + G_MapIntoRoundQueue( + cupLevelNum, + G_GuessGametypeByTOL(mapheaderinfo[cupLevelNum]->typeoflevel), + setencore, // if this isn't correct, Got_Mapcmd will fix it + true // Rank-restricted! + ); + } + } } if (roundqueue.size == 0) @@ -4496,7 +4549,7 @@ void G_AfterIntermission(void) M_PlaybackQuit(0); return; } - else if (demo.recording && (modeattacking || demo.savemode != DSM_NOTSAVING)) + else if (demo.recording && (modeattacking || demo.willsave)) G_SaveDemo(); if (modeattacking) // End the run. diff --git a/src/g_gamedata.cpp b/src/g_gamedata.cpp index 105f997ad..d522ce7ab 100644 --- a/src/g_gamedata.cpp +++ b/src/g_gamedata.cpp @@ -64,6 +64,7 @@ void srb2::save_ng_gamedata() ng.milestones.majorkeyskipattempted = gamedata->majorkeyskipattempted; ng.milestones.finishedtutorialchallenge = gamedata->finishedtutorialchallenge; ng.milestones.enteredtutorialchallenge = gamedata->enteredtutorialchallenge; + ng.milestones.sealedswapalerted = gamedata->sealedswapalerted; ng.milestones.gonerlevel = gamedata->gonerlevel; ng.prisons.thisprisoneggpickup = gamedata->thisprisoneggpickup; ng.prisons.prisoneggstothispickup = gamedata->prisoneggstothispickup; @@ -176,7 +177,7 @@ void srb2::save_ng_gamedata() } for (auto cup = kartcupheaders; cup; cup = cup->next) { - if (cup->windata[0].best_placement == 0) + if (cup->windata[0].best_placement == 0 && cup->windata[1].got_emerald == false) { continue; } @@ -229,6 +230,17 @@ void srb2::save_ng_gamedata() ng.cups[cupdata.name] = std::move(cupdata); } + for (int i = 0; (i < GDMAX_SEALEDSWAPS && gamedata->sealedswaps[i]); i++) + { + srb2::GamedataSealedSwapJson sealedswap {}; + + cupheader_t* cup = gamedata->sealedswaps[i]; + + sealedswap.name = std::string(cup->name); + + ng.sealedswaps.emplace_back(std::move(sealedswap)); + } + std::string gamedataname_s {gamedatafilename}; fs::path savepath {fmt::format("{}/{}", srb2home, gamedataname_s)}; fs::path tmpsavepath {fmt::format("{}/{}.tmp", srb2home, gamedataname_s)}; @@ -418,6 +430,7 @@ void srb2::load_ng_gamedata() gamedata->majorkeyskipattempted = js.milestones.majorkeyskipattempted; gamedata->finishedtutorialchallenge = js.milestones.finishedtutorialchallenge; gamedata->enteredtutorialchallenge = js.milestones.enteredtutorialchallenge; + gamedata->sealedswapalerted = js.milestones.sealedswapalerted; gamedata->gonerlevel = js.milestones.gonerlevel; gamedata->thisprisoneggpickup = js.prisons.thisprisoneggpickup; gamedata->prisoneggstothispickup = js.prisons.prisoneggstothispickup; @@ -720,6 +733,33 @@ void srb2::load_ng_gamedata() } } + size_t sealedswaps_size = js.sealedswaps.size(); + for (size_t i = 0; i < std::min((size_t)GDMAX_SEALEDSWAPS, sealedswaps_size); i++) + { + cupheader_t* cup = nullptr; + + // Find BASE cups only + for (cup = kartcupheaders; cup; cup = cup->next) + { + if (cup->id >= basenumkartcupheaders) + { + cup = NULL; + break; + } + + std::string cupname = std::string(cup->name); + if (cupname == js.sealedswaps[i].name) + { + break; + } + } + + if (cup) + { + gamedata->sealedswaps[i] = cup; + } + } + M_FinaliseGameData(); } diff --git a/src/g_gamedata.h b/src/g_gamedata.h index d476f2e46..f479da427 100644 --- a/src/g_gamedata.h +++ b/src/g_gamedata.h @@ -70,6 +70,7 @@ struct GamedataMilestonesJson final bool majorkeyskipattempted; bool finishedtutorialchallenge; bool enteredtutorialchallenge; + bool sealedswapalerted; NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT( GamedataMilestonesJson, @@ -81,7 +82,8 @@ struct GamedataMilestonesJson final chaokeytutorial, majorkeyskipattempted, finishedtutorialchallenge, - enteredtutorialchallenge + enteredtutorialchallenge, + sealedswapalerted ) }; @@ -184,6 +186,13 @@ struct GamedataCupJson final NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataCupJson, name, records) }; +struct GamedataSealedSwapJson final +{ + std::string name; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataSealedSwapJson, name) +}; + struct GamedataJson final { GamedataPlaytimeJson playtime; @@ -203,6 +212,7 @@ struct GamedataJson final std::unordered_map maps; std::vector spraycans; std::unordered_map cups; + std::vector sealedswaps; NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT( GamedataJson, @@ -222,7 +232,8 @@ struct GamedataJson final skins, maps, spraycans, - cups + cups, + sealedswaps ) }; diff --git a/src/hud/input-display.cpp b/src/hud/input-display.cpp index 1acc0c068..8d82799ac 100644 --- a/src/hud/input-display.cpp +++ b/src/hud/input-display.cpp @@ -7,6 +7,7 @@ // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- +#include #include #include @@ -18,6 +19,8 @@ #include "../i_joy.h" #include "../k_hud.h" #include "../k_kart.h" +#include "../m_easing.h" +#include "../p_tick.h" #include "../v_draw.hpp" using srb2::Draw; @@ -59,8 +62,21 @@ const char* dpad_suffix(const Vec2& v) }; // namespace -void K_DrawInputDisplay(INT32 x, INT32 y, INT32 flags, char mode, UINT8 pid, boolean local, boolean transparent) +void K_DrawInputDisplay(float x, float y, INT32 flags, char mode, UINT8 pid, boolean local, boolean transparent) { + auto fade_in = [] + { + constexpr tic_t kStart = TICRATE; + constexpr tic_t kDuration = TICRATE/2; + fixed_t f = std::min(std::max(leveltime, kStart) - kStart, kDuration) * FRACUNIT / kDuration; + return Easing_Linear(f, 0, 9); + }; + auto alpha_to_flag = [](int alpha) { return (9 - alpha) << V_ALPHASHIFT; }; + + int alpha = fade_in(); + if (alpha == 0) + return; + const ticcmd_t& cmd = players[displayplayers[pid]].cmd; const boolean analog = (mode == '4' || mode == '5') ? players[displayplayers[pid]].analoginput : false; const std::string prefix = fmt::format("PR{}", mode); @@ -73,7 +89,8 @@ void K_DrawInputDisplay(INT32 x, INT32 y, INT32 flags, char mode, UINT8 pid, boo Draw box = Draw(x, y).flags(flags); - box.flags(transparent ? V_TRANSLUCENT : 0).patch(gfx("CONT")); + box.flags(alpha_to_flag(alpha / (transparent ? 2 : 1))).patch(gfx("CONT")); + box = box.flags(alpha_to_flag(alpha)); Vec2 dpad = local ? Vec2 { diff --git a/src/k_botitem.cpp b/src/k_botitem.cpp index 84baaab6b..db9cd35f7 100644 --- a/src/k_botitem.cpp +++ b/src/k_botitem.cpp @@ -1479,13 +1479,19 @@ static void K_BotItemInstashield(const player_t *player, ticcmd_t *cmd) const fixed_t radius = FixedMul(mobjinfo[MT_INSTAWHIP].radius, player->mo->scale); size_t i = SIZE_MAX; - if (K_ItemButtonWasDown(player) == true) - { - // Release the button, dude. - return; - } + boolean nearbyThreat = false; // Someone's near enough to worry about, start charging. + boolean attackOpportunity = false; // Someone's close enough to hit! + boolean coastIsClear = true; // Nobody is nearby, let any pending charge go. - if (player->instaWhipCharge || leveltime < starttime || player->spindash) + UINT8 stupidRating = MAXBOTDIFFICULTY - player->botvars.difficulty; + // Weak bots take a second to react on offense. + UINT8 reactiontime = stupidRating; + // Weak bots misjudge their attack range. Purely accurate at Lv.MAX, 250% overestimate at Lv.1 + fixed_t radiusWithError = radius + 3*(radius * stupidRating / MAXBOTDIFFICULTY)/2; + + // Future work: Expand threat range versus fast pursuers. + + if (leveltime < starttime || player->spindash || player->defenseLockout) { // Instashield is on cooldown. return; @@ -1517,18 +1523,41 @@ static void K_BotItemInstashield(const player_t *player, ticcmd_t *cmd) (player->mo->z - target->mo->z) / 4 ); - if (dist <= radius) + if (dist <= 8 * radius) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * 2); + coastIsClear = false; + } + + if (dist <= 5 * radius) + { + nearbyThreat = true; + } + + if (dist <= (radiusWithError + target->mo->radius)) + { + attackOpportunity = true; + K_ItemConfirmForTarget(player, cmd, target, 1); } } - if (player->botvars.itemconfirm > 10*TICRATE) + if (player->instaWhipCharge) // Already charging, do we stay committed? { - // Use it!! - cmd->buttons |= BT_ATTACK; - //player->botvars.itemconfirm = 0; + cmd->buttons |= BT_ATTACK; // Keep holding, unless... + + // ...there are no attackers that are even distantly threatening... + if (coastIsClear) + cmd->buttons &= ~BT_ATTACK; + + // ...or we're ready to rock. + if (attackOpportunity && player->instaWhipCharge >= (INSTAWHIP_CHARGETIME + reactiontime) && player->botvars.itemconfirm >= reactiontime) + cmd->buttons &= ~BT_ATTACK; } + else // When should we get spooked and start a charge? + { + if (nearbyThreat) + cmd->buttons |= BT_ATTACK; + } + } /*-------------------------------------------------- @@ -1800,7 +1829,7 @@ static void K_UpdateBotGameplayVarsItemUsageMash(player_t *player) --------------------------------------------------*/ void K_UpdateBotGameplayVarsItemUsage(player_t *player) { - if (player->itemflags & IF_USERINGS) + if (player->itemflags & IF_USERINGS && !player->instaWhipCharge) { return; } diff --git a/src/k_hitlag.c b/src/k_hitlag.c index 5a81cfd5a..5b40ac9cd 100644 --- a/src/k_hitlag.c +++ b/src/k_hitlag.c @@ -20,6 +20,7 @@ #include "p_local.h" #include "r_main.h" #include "s_sound.h" +#include "m_easing.h" /*-------------------------------------------------- void K_AddHitLag(mobj_t *mo, INT32 tics, boolean fromDamage) @@ -152,7 +153,11 @@ static void K_PlayHitLagSFX(mobj_t *victim, UINT8 tics) soundID = sfx_dmgb1; } - soundID += ((tics * (NUM_HITLAG_SOUNDS - 1)) + (MAXHITLAGTICS >> 1)) / MAXHITLAGTICS; + soundID += Easing_Linear( + min(FRACUNIT, FRACUNIT*tics/MAXHITLAGTICS), + 0, + NUM_HITLAG_SOUNDS-1 + ); S_StartSound(victim, soundID); } diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 2cb721138..e7b2f2c6b 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -51,6 +51,7 @@ #include "g_party.h" #include "k_hitlag.h" #include "g_input.h" +#include "k_dialogue.h" //{ Patch Definitions static patch_t *kp_nodraw; @@ -5163,19 +5164,28 @@ static void K_drawKartFirstPerson(void) static void K_drawInput(void) { + UINT8 viewnum = R_GetViewNumber(); + boolean freecam = camera[viewnum].freecam; //disable some hud elements w/ freecam + + if (!cv_drawinput.value && !modeattacking && gametype != GT_TUTORIAL) + return; + + if (stplyr->spectator || freecam || demo.attract) + return; + INT32 def[4][3] = { {247, 156, V_SNAPTOBOTTOM | V_SNAPTORIGHT}, // 1p {247, 56, V_SNAPTOBOTTOM | V_SNAPTORIGHT}, // 2p {6, 52, V_SNAPTOBOTTOM | V_SNAPTOLEFT}, // 4p left {282 - BASEVIDWIDTH/2, 52, V_SNAPTOBOTTOM | V_SNAPTORIGHT}, // 4p right }; - INT32 k = r_splitscreen <= 1 ? r_splitscreen : 2 + (R_GetViewNumber() & 1); - INT32 flags = def[k][2] | V_SPLITSCREEN | V_SLIDEIN; + INT32 k = r_splitscreen <= 1 ? r_splitscreen : 2 + (viewnum & 1); + INT32 flags = def[k][2] | V_SPLITSCREEN; char mode = ((stplyr->pflags & PF_ANALOGSTICK) ? '4' : '2') + (r_splitscreen > 1); bool local = !demo.playback && P_IsMachineLocalPlayer(stplyr); K_DrawInputDisplay( - def[k][0], - def[k][1], + def[k][0] - FixedToFloat(K_GetDialogueSlide(34 * FRACUNIT)), + def[k][1] - FixedToFloat(K_GetDialogueSlide(51 * FRACUNIT)), flags, mode, (local ? G_LocalSplitscreenPartyPosition : G_PartyPosition)(stplyr - players), @@ -6062,13 +6072,6 @@ void K_drawKartHUD(void) K_drawRingCounter(gametypeinfoshown); } - if ((modeattacking && !bossinfo.valid) || cv_drawinput.value) - { - // Draw the input UI - if (LUA_HudEnabled(hud_position)) - K_drawInput(); - } - // Draw the item window if (LUA_HudEnabled(hud_item) && !freecam) { @@ -6128,7 +6131,10 @@ void K_drawKartHUD(void) K_drawEmeraldWin(true); if (modeattacking || freecam) // everything after here is MP and debug only + { + K_drawInput(); return; + } if ((gametyperules & GTR_KARMA) && !r_splitscreen && (stplyr->karthud[khud_yougotem] % 2)) // * YOU GOT EM * V_DrawScaledPatch(BASEVIDWIDTH/2 - (SHORT(kp_yougotem->width)/2), 32, V_HUDTRANS, kp_yougotem); @@ -6147,6 +6153,10 @@ void K_drawKartHUD(void) { K_drawSpectatorHUD(true); } + else + { + K_drawInput(); + } if (cv_kartdebugdistribution.value) K_drawDistributionDebugger(); diff --git a/src/k_hud.h b/src/k_hud.h index 422f3559f..94ee18343 100644 --- a/src/k_hud.h +++ b/src/k_hud.h @@ -63,7 +63,7 @@ void K_DrawKartPositionNumXY( boolean exit, boolean lastLap, boolean losing ); -void K_DrawInputDisplay(INT32 x, INT32 y, INT32 flags, char mode, UINT8 pid, boolean local, boolean transparent); +void K_DrawInputDisplay(float x, float y, INT32 flags, char mode, UINT8 pid, boolean local, boolean transparent); extern patch_t *kp_capsuletarget_arrow[2][2]; extern patch_t *kp_capsuletarget_icon[2]; diff --git a/src/k_kart.c b/src/k_kart.c index b4c0a5011..8f947d80d 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8534,6 +8534,17 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) else if (player->rings < -20) player->rings = -20; + if (cv_kartdebugbotwhip.value) + { + if (player->bot) + { + player->rings = 0; + player->itemtype = 0; + player->itemamount = 0; + player->itemRoulette.active = false; + } + } + if (player->spheres > 40) player->spheres = 40; // where's the < 0 check? see below the following block! @@ -9909,9 +9920,10 @@ static void K_UpdateDistanceFromFinishLine(player_t *const player) // correct we need to add to it the length of the entire circuit multiplied by the number of laps // left after this one. This will give us the total distance to the finish line, and allow item // distance calculation to work easily - if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) == 0U) + const mapheader_t *mapheader = mapheaderinfo[gamemap - 1]; + if ((mapheader->levelflags & LF_SECTIONRACE) == 0U) { - const UINT8 numfulllapsleft = ((UINT8)numlaps - player->laps); + const UINT8 numfulllapsleft = ((UINT8)numlaps - player->laps) / mapheader->lapspersection; player->distancetofinish += numfulllapsleft * K_GetCircuitLength(); } } @@ -9953,18 +9965,27 @@ static void K_UpdatePlayerWaypoints(player_t *const player) UINT32 delta = u32_delta(player->distancetofinish, player->distancetofinishprev); if (player->respawn.state == RESPAWNST_NONE && delta > distance_threshold && old_currentwaypoint != NULL) { - CONS_Debug(DBG_GAMELOGIC, "Player %s: waypoint ID %d too far away (%u > %u)\n", - sizeu1(player - players), K_GetWaypointID(player->nextwaypoint), delta, distance_threshold); + extern consvar_t cv_debuglapcheat; +#define debug_args "Player %s: waypoint ID %d too far away (%u > %u)\n", \ + sizeu1(player - players), K_GetWaypointID(player->nextwaypoint), delta, distance_threshold + if (cv_debuglapcheat.value) + CONS_Printf(debug_args); + else + CONS_Debug(DBG_GAMELOGIC, debug_args); +#undef debug_args - // Distance jump is too great, keep the old waypoints and old distance. - player->currentwaypoint = old_currentwaypoint; - player->nextwaypoint = old_nextwaypoint; - player->distancetofinish = player->distancetofinishprev; - - // Start the auto respawn timer when the distance jumps. - if (!player->bigwaypointgap) + if (!cv_debuglapcheat.value) { - player->bigwaypointgap = 35; + // Distance jump is too great, keep the old waypoints and old distance. + player->currentwaypoint = old_currentwaypoint; + player->nextwaypoint = old_nextwaypoint; + player->distancetofinish = player->distancetofinishprev; + + // Start the auto respawn timer when the distance jumps. + if (!player->bigwaypointgap) + { + player->bigwaypointgap = 35; + } } } else @@ -11382,7 +11403,7 @@ static void K_KartSpindash(player_t *player) if (player->spindash > 0 && (buttons & (BT_DRIFT|BT_BRAKE|BT_ACCELERATE)) != (BT_DRIFT|BT_BRAKE|BT_ACCELERATE)) { - player->spindashspeed = (player->spindash * FRACUNIT) / MAXCHARGETIME; + player->spindashspeed = (min(player->spindash, MAXCHARGETIME) * FRACUNIT) / MAXCHARGETIME; player->spindashboost = TICRATE; // if spindash was charged enough, give a small thrust. @@ -12003,6 +12024,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) { S_StartSound(player->mo, sfx_kc50); player->instaWhipCharge = 0; + player->botvars.itemconfirm = 0; } else { @@ -12436,6 +12458,8 @@ void K_MoveKartPlayer(player_t *player, boolean onground) { INT32 numhogs = min((player->ballhogcharge / BALLHOGINCREMENT), player->itemamount); + K_SetItemOut(player); // need this to set itemscale + if (numhogs <= 0) { // no tapfire scams @@ -12464,6 +12488,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) K_PlayAttackTaunt(player->mo); } + K_UnsetItemOut(player); player->ballhogcharge = 0; player->itemflags &= ~IF_HOLDREADY; player->botvars.itemconfirm = 0; @@ -12475,7 +12500,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) { player->itemamount--; + K_SetItemOut(player); K_ThrowKartItem(player, true, MT_SPB, 1, 0, 0); + K_UnsetItemOut(player); K_PlayAttackTaunt(player->mo); player->botvars.itemconfirm = 0; } @@ -12802,7 +12829,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground) case KITEM_GACHABOM: if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) { + K_SetItemOut(player); // need this to set itemscale K_ThrowKartItem(player, true, MT_GACHABOM, 0, 0, 0); + K_UnsetItemOut(player); K_PlayAttackTaunt(player->mo); player->itemamount--; player->roundconditions.gachabom_miser = ( diff --git a/src/k_menu.h b/src/k_menu.h index d84d97414..ae093bf1c 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -542,6 +542,7 @@ extern INT32 menuKey; // keyboard key pressed for menu extern INT16 virtualKeyboard[5][NUMVIRTUALKEYSINROW]; extern INT16 shift_virtualKeyboard[5][NUMVIRTUALKEYSINROW]; +typedef const char *(*vkb_query_fn_t)(const char *replace); extern struct menutyping_s { boolean active; // Active @@ -554,7 +555,10 @@ extern struct menutyping_s boolean keyboardcapslock; boolean keyboardshift; - char cache[MAXSTRINGLENGTH]; // cached string + vkb_query_fn_t queryfn; // callback on open and close + menu_t *dummymenu; + size_t cachelen; + char *cache; // cached string } menutyping; // While typing, we'll have a fade strongly darken the screen to overlay the typing menu instead @@ -680,7 +684,10 @@ void M_Init(void); void M_PlayMenuJam(void); -void M_OpenVirtualKeyboard(boolean gamepad); +boolean M_ConsiderSealedSwapAlert(void); + +void M_OpenVirtualKeyboard(boolean gamepad, size_t cachelen, vkb_query_fn_t queryfn, menu_t *dummymenu); +void M_AbortVirtualKeyboard(void); void M_MenuTypingInput(INT32 key); void M_QuitResponse(INT32 ch); @@ -1347,6 +1354,8 @@ extern struct challengesmenu_s { boolean chaokeyadd, keywasadded; UINT8 chaokeyhold; + boolean considersealedswapalert; + boolean requestflip; UINT16 unlockcount[CMC_MAX]; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index bb9205fe2..b0a125478 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -495,6 +495,21 @@ static void M_DrawMenuTooltips(void) } } +static const char *M_MenuTypingCroppedString(void) +{ + static char buf[36]; + const char *p = menutyping.cache; + size_t n = strlen(p); + if (n > sizeof buf) + { + p += n - sizeof buf; + n = sizeof buf; + } + memcpy(buf, p, n); + buf[n] = '\0'; + return buf; +} + // Draws the typing submenu static void M_DrawMenuTyping(void) { @@ -534,7 +549,7 @@ static void M_DrawMenuTyping(void) V_DrawFill(x + 4, y + 4 + 5, 1, 8+6, 121); V_DrawFill(x + 5 + boxwidth - 8, y + 4 + 5, 1, 8+6, 121); - INT32 textwidth = M_DrawCaretString(x + 8, y + 12, menutyping.cache, true); + INT32 textwidth = M_DrawCaretString(x + 8, y + 12, M_MenuTypingCroppedString(), true); if (skullAnimCounter < 4 && menutyping.menutypingclose == false && menutyping.menutypingfade == (menutyping.keyboardtyping ? 9 : 18)) @@ -2823,6 +2838,57 @@ fixed_t M_DrawCupWinData(INT32 rankx, INT32 ranky, cupheader_t *cup, UINT8 diffi rankx += 19 - (rankw / 2); cupwindata_t *windata = &(cup->windata[difficulty]); + UINT8 emeraldnum = UINT8_MAX; + + if (!noemerald) + { + if (gamedata->sealedswaps[GDMAX_SEALEDSWAPS-1] != NULL // all found + || cup->id >= basenumkartcupheaders // custom content + || M_SecretUnlocked(SECRET_SPECIALATTACK, true)) // true order + { + // Standard order. + if (windata->got_emerald == true) + { + emeraldnum = cup->emeraldnum; + } + } + else + { + // Determine order from sealedswaps. + UINT8 i; + for (i = 0; (i < GDMAX_SEALEDSWAPS && gamedata->sealedswaps[i]); i++) + { + if (gamedata->sealedswaps[i] != cup) + continue; + + break; + } + + // If there's pending stars, get them from the associated cup order. + if (i < GDMAX_SEALEDSWAPS) + { + cupheader_t *emeraldcup = kartcupheaders; + while (emeraldcup) + { + if (emeraldcup->id >= basenumkartcupheaders) + { + emeraldcup = NULL; + break; + } + + if (emeraldcup->emeraldnum == i+1) + { + if (emeraldcup->windata[difficulty].got_emerald == true) + emeraldnum = i+1; + break; + } + + emeraldcup = emeraldcup->next; + } + } + } + } + if (windata->best_placement == 0) { if (statsmode) @@ -2832,14 +2898,13 @@ fixed_t M_DrawCupWinData(INT32 rankx, INT32 ranky, cupheader_t *cup, UINT8 diffi V_DrawCharacter((14-4)/2 + rankx, ranky, '.' | V_GRAYMAP, false); rankx += 14 + 1; V_DrawCharacter((12-4)/2 + rankx, ranky, '.' | V_GRAYMAP, false); - - if (!noemerald) - { - rankx += 12 + 1; - V_DrawCharacter((12-4)/2 + rankx, ranky, '.' | V_GRAYMAP, false); - } } - return rankw; + else + { + rankx += 14 + 1; + } + + goto windataemeraldmaybe; } UINT8 *colormap = NULL; @@ -2934,13 +2999,15 @@ fixed_t M_DrawCupWinData(INT32 rankx, INT32 ranky, cupheader_t *cup, UINT8 diffi if (charPat) V_DrawFixedPatch((rankx)*FRACUNIT, (ranky)*FRACUNIT, FRACUNIT, 0, charPat, colormap); +windataemeraldmaybe: + rankx += 12 + 1; if (noemerald) ; - else if (windata->got_emerald == true) + else if (emeraldnum != UINT8_MAX) { - if (cup->emeraldnum == 0) + if (emeraldnum == 0) V_DrawCharacter(rankx+2, ranky+2, '+', false); else { @@ -2948,14 +3015,14 @@ fixed_t M_DrawCupWinData(INT32 rankx, INT32 ranky, cupheader_t *cup, UINT8 diffi if (!flash) { - UINT16 col = SKINCOLOR_CHAOSEMERALD1 + (cup->emeraldnum-1) % 7; + UINT16 col = SKINCOLOR_CHAOSEMERALD1 + (emeraldnum-1) % 7; colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); } const char *emname = va( "%sMAP%c", - (cup->emeraldnum > 7) ? "SUP" : "EME", + (emeraldnum > 7) ? "SUP" : "EME", colormap ? '\0' : 'B' ); @@ -3116,7 +3183,7 @@ void M_DrawCupSelect(void) y += 44; //(8 + 100) - (20 + 44) } - if (windata && windata->best_placement != 0) + if (windata) { M_DrawCupWinData( x, diff --git a/src/k_menufunc.c b/src/k_menufunc.c index efb78969d..dc86b141c 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -188,6 +188,14 @@ static void M_ChangeCvar(INT32 choice) M_ChangeCvarDirect(choice, currentMenu->menuitems[itemOn].itemaction.cvar); } +static const char *M_QueryCvarAction(const char *replace) +{ + consvar_t *cvar = currentMenu->menuitems[itemOn].itemaction.cvar; + if (replace) + CV_Set(cvar, replace); + return cvar->string; +} + boolean M_NextOpt(void) { INT16 oldItemOn = itemOn; // prevent infinite loop @@ -560,6 +568,35 @@ void M_PlayMenuJam(void) #undef IsCurrentlyPlaying +boolean M_ConsiderSealedSwapAlert(void) +{ + if (gamedata->sealedswapalerted == true) + return false; + + if (gamedata->sealedswaps[GDMAX_SEALEDSWAPS-1] != NULL // all found + || M_SecretUnlocked(SECRET_SPECIALATTACK, true)) // true order + { + gamedata->sealedswapalerted = true; + + // Don't make a message if no Sealed Stars have yet been found. + if (gamedata->everseenspecial == false) + return false; + + M_StartMessage( + "Message from the Stars", + "As if called by fate, the Emeralds you've\n" + "collected return to their rightful places...\n" + "\n" + "The Sealed Stars are now ordered via Cups!\n", + NULL, MM_NOTHING, NULL, NULL + ); + + return true; + } + + return false; +} + void M_ValidateRestoreMenu(void) { if (restoreMenu == NULL || restoreMenu == &MAIN_GonerDef) @@ -629,6 +666,11 @@ menu_t *M_SpecificMenuRestore(menu_t *torestore) M_SetupPlayMenu(-1); PLAY_CharSelectDef.prevMenu = &MainDef; + if (torestore != &MISC_ChallengesDef) + { + M_ConsiderSealedSwapAlert(); + } + return torestore; } @@ -797,7 +839,7 @@ void M_ClearMenus(boolean callexitmenufunc) D_StartTitle(); } - menutyping.active = false; + M_AbortVirtualKeyboard(); menumessage.active = false; menuactive = false; @@ -884,6 +926,8 @@ void M_SetupNextMenu(menu_t *menudef, boolean notransition) void M_GoBack(INT32 choice) { + const INT16 behaviourflags = currentMenu->behaviourflags; + (void)choice; noFurtherInput = true; @@ -906,7 +950,8 @@ void M_GoBack(INT32 choice) else // No returning to the title screen. M_QuitSRB2(-1); - S_StartSound(NULL, sfx_s3k5b); + if (!(behaviourflags & MBF_SOUNDLESS)) + S_StartSound(NULL, sfx_s3k5b); } // @@ -1090,7 +1135,8 @@ static void M_HandleMenuInput(void) // If we're hovering over a IT_CV_STRING option, pressing A/X opens the typing submenu if (M_MenuConfirmPressed(pid)) { - M_OpenVirtualKeyboard(thisMenuKey == -1); // If we entered this menu by pressing a menu Key, default to keyboard typing, otherwise use controller. + // If we entered this menu by pressing a menu Key, default to keyboard typing, otherwise use controller. + M_OpenVirtualKeyboard(thisMenuKey == -1, MAXSTRINGLENGTH, M_QueryCvarAction, NULL); return; } else if (M_MenuExtraPressed(pid)) diff --git a/src/k_podium.cpp b/src/k_podium.cpp index 103640d02..30e863298 100644 --- a/src/k_podium.cpp +++ b/src/k_podium.cpp @@ -83,6 +83,7 @@ static struct podiumData_s sfxenum_t gradeVoice; cupheader_t *cup; + UINT8 emeraldnum; boolean fastForward; @@ -102,6 +103,7 @@ void podiumData_s::Init(void) { rank = grandprixinfo.rank; cup = grandprixinfo.cup; + emeraldnum = cup->emeraldnum; } else { @@ -119,6 +121,7 @@ void podiumData_s::Init(void) cup = cup->next; } + emeraldnum = 0; memset(&rank, 0, sizeof(gpRank_t)); rank.skin = players[consoleplayer].skin; @@ -628,12 +631,7 @@ void podiumData_s::Draw(void) case GPEVENT_SPECIAL: { srb2::Draw drawer_emerald = drawer_gametype; - UINT8 emeraldNum = 0; - - if (cup != nullptr) - { - emeraldNum = cup->emeraldnum; - } + UINT8 emeraldNum = g_podiumData.emeraldnum; boolean useWhiteFrame = ((leveltime & 1) || !dta->gotSpecialPrize); patch_t *emeraldPatch = nullptr; @@ -844,12 +842,7 @@ void podiumData_s::Draw(void) if (rank.specialWon == true) { - UINT8 emeraldNum = 0; - - if (cup != nullptr) - { - emeraldNum = cup->emeraldnum; - } + UINT8 emeraldNum = g_podiumData.emeraldnum; const boolean emeraldBlink = (leveltime & 1); patch_t *emeraldOverlay = nullptr; @@ -1263,6 +1256,60 @@ void K_ResetCeremony(void) return; } + cupheader_t *emeraldcup = NULL; + + if (gamedata->sealedswaps[GDMAX_SEALEDSWAPS-1] != NULL // all found + || grandprixinfo.cup->id >= basenumkartcupheaders // custom content + || M_SecretUnlocked(SECRET_SPECIALATTACK, false)) // true order + { + // Standard order. + emeraldcup = grandprixinfo.cup; + } + else + { + // Determine order from sealedswaps. + for (i = 0; i < GDMAX_SEALEDSWAPS; i++) + { + if (gamedata->sealedswaps[i] == NULL) + { + if (g_podiumData.rank.specialWon == true) + { + // First visit! Mark it off. + gamedata->sealedswaps[i] = grandprixinfo.cup; + } + + break; + } + + if (gamedata->sealedswaps[i] != grandprixinfo.cup) + continue; + + // Repeat visit, grab the same ID. + break; + } + + // If there's pending stars, apply them to the new cup order. + if (i < GDMAX_SEALEDSWAPS) + { + emeraldcup = kartcupheaders; + while (emeraldcup) + { + if (emeraldcup->id >= basenumkartcupheaders) + { + emeraldcup = NULL; + break; + } + + if (emeraldcup->emeraldnum == i+1) + break; + + emeraldcup = emeraldcup->next; + } + + g_podiumData.emeraldnum = i+1; + } + } + // Write grade, position, and emerald-having-ness for later sessions! i = (grandprixinfo.masterbots) ? KARTGP_MASTER : grandprixinfo.gamespeed; @@ -1292,9 +1339,9 @@ void K_ResetCeremony(void) anymerit = true; } - if (g_podiumData.rank.specialWon == true) + if (g_podiumData.rank.specialWon == true && emeraldcup) { - grandprixinfo.cup->windata[i].got_emerald = true; + emeraldcup->windata[i].got_emerald = true; anymerit = true; } diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 7ec807c87..9117858d1 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -2535,6 +2535,8 @@ static int mapheaderinfo_get(lua_State *L) lua_pushinteger(L, header->palette); else if (fastcmp(field,"numlaps")) lua_pushinteger(L, header->numlaps); + else if (fastcmp(field,"lapspersection")) + lua_pushinteger(L, header->lapspersection); else if (fastcmp(field,"levelselect")) lua_pushinteger(L, header->levelselect); else if (fastcmp(field,"levelflags")) diff --git a/src/m_cheat.c b/src/m_cheat.c index 779d6b526..7a0f1e063 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -96,6 +96,7 @@ static UINT8 cheatf_warp(void) if (success) { gamedata->gonerlevel = GDGONER_DONE; + gamedata->sealedswapalerted = true; G_SetUsedCheats(); } @@ -231,6 +232,7 @@ static UINT8 cheatf_devmode(void) } gamedata->gonerlevel = GDGONER_DONE; + gamedata->sealedswapalerted = true; M_ClearMenus(true); diff --git a/src/m_cond.c b/src/m_cond.c index 345641454..4e35471ed 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -666,6 +666,7 @@ void M_ClearStats(void) gamedata->majorkeyskipattempted = false; gamedata->enteredtutorialchallenge = false; gamedata->finishedtutorialchallenge = false; + gamedata->sealedswapalerted = false; gamedata->musicstate = GDMUSIC_NONE; gamedata->importprofilewins = false; @@ -720,6 +721,8 @@ void M_ClearSecrets(void) skincolors[i].cache_spraycan = UINT16_MAX; } + memset(gamedata->sealedswaps, 0, sizeof(gamedata->sealedswaps)); + Z_Free(gamedata->challengegrid); gamedata->challengegrid = NULL; gamedata->challengegridwidth = 0; diff --git a/src/m_cond.h b/src/m_cond.h index 393d046c5..210a09767 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -291,6 +291,7 @@ typedef enum { // This is the largest number of 9s that will fit in UINT32 and UINT16 respectively. #define GDMAX_RINGS 999999999 #define GDMAX_CHAOKEYS 9999 +#define GDMAX_SEALEDSWAPS 7 #define GDCONVERT_ROUNDSTOKEY 14 @@ -371,12 +372,15 @@ struct gamedata_t UINT32 totalrings; UINT32 totaltumbletime; - // Chao Key condition bypass + // CHAO KEYS AND THEIR GENERATION UINT32 pendingkeyrounds; UINT8 pendingkeyroundoffset; UINT16 keyspending; UINT16 chaokeys; + // EMERALD REMAPPING + cupheader_t *sealedswaps[GDMAX_SEALEDSWAPS]; + // SPECIFIC SPECIAL EVENTS boolean everloadedaddon; boolean everfinishedcredits; @@ -387,6 +391,7 @@ struct gamedata_t boolean majorkeyskipattempted; boolean enteredtutorialchallenge; boolean finishedtutorialchallenge; + boolean sealedswapalerted; gdmusic_t musicstate; UINT8 gonerlevel; diff --git a/src/menus/extras-challenges.c b/src/menus/extras-challenges.c index 6fe8ac330..e1df3066b 100644 --- a/src/menus/extras-challenges.c +++ b/src/menus/extras-challenges.c @@ -335,6 +335,7 @@ menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu) challengesmenu.requestnew = false; challengesmenu.chaokeyadd = false; challengesmenu.keywasadded = false; + challengesmenu.considersealedswapalert = false; challengesmenu.chaokeyhold = 0; challengesmenu.currentunlock = MAXUNLOCKABLES; challengesmenu.unlockcondition = NULL; @@ -668,10 +669,15 @@ void M_ChallengesTick(void) if (challengesmenu.currentunlock < MAXUNLOCKABLES && challengesmenu.unlockanim == UNLOCKTIME) { + unlockable_t *ref = &unlockables[challengesmenu.currentunlock]; + // Unlock animation... also tied directly to the actual unlock! gamedata->unlocked[challengesmenu.currentunlock] = true; M_UpdateUnlockablesAndExtraEmblems(true, true); + if (ref->type == SECRET_SPECIALATTACK) + challengesmenu.considersealedswapalert = true; + // Update shown description just in case..? if (challengesmenu.unlockcondition) Z_Free(challengesmenu.unlockcondition); @@ -682,12 +688,10 @@ void M_ChallengesTick(void) if (challengesmenu.extradata) { - unlockable_t *ref; UINT16 bombcolor; M_UpdateChallengeGridExtraData(challengesmenu.extradata); - ref = &unlockables[challengesmenu.currentunlock]; bombcolor = SKINCOLOR_NONE; if (ref->color != SKINCOLOR_NONE && ref->color < numskincolors) @@ -747,7 +751,14 @@ void M_ChallengesTick(void) // Play music the moment control returns. M_PlayMenuJam(); - if (gamedata->chaokeytutorial == false + if (challengesmenu.considersealedswapalert == true + && M_ConsiderSealedSwapAlert() == true) + { + // No keygen tutorial in this case... + // not ideal but at least unlikely to + // get at same time?? :V + } + else if (gamedata->chaokeytutorial == false && challengesmenu.keywasadded == true) { M_ChallengesTutorial(CCTUTORIAL_KEYGEN); diff --git a/src/menus/options-profiles-edit-accessibility.cpp b/src/menus/options-profiles-edit-accessibility.cpp index 8ace78f24..ca031279c 100644 --- a/src/menus/options-profiles-edit-accessibility.cpp +++ b/src/menus/options-profiles-edit-accessibility.cpp @@ -8,7 +8,7 @@ #include "../m_easing.h" #include "../p_local.h" // cv_tilting -extern "C" consvar_t cv_mindelay; +extern "C" consvar_t cv_mindelay, cv_drawinput; using srb2::Draw; @@ -119,6 +119,9 @@ menuitem_t OPTIONS_ProfileAccessibility[] = { {IT_STRING | IT_CVAR, "Screenshake", "Adjust shake intensity from hazards and offroad.", NULL, {.cvar = &cv_screenshake}, 0, 0}, + + {IT_STRING | IT_CVAR, "Input Display", "Show virtual controller on the HUD.", + NULL, {.cvar = &cv_drawinput}, 0, 0}, }; menu_t OPTIONS_ProfileAccessibilityDef = { diff --git a/src/menus/transient/message-box.c b/src/menus/transient/message-box.c index 72bc50731..ad901ef11 100644 --- a/src/menus/transient/message-box.c +++ b/src/menus/transient/message-box.c @@ -116,6 +116,9 @@ void M_StopMessage(INT32 choice) boolean M_MenuMessageTick(void) { + if (menuwipe) + return false; + if (menumessage.closing) { if (menumessage.closing > MENUMESSAGECLOSE) diff --git a/src/menus/transient/virtual-keyboard.c b/src/menus/transient/virtual-keyboard.c index 81afaed8e..4204dce09 100644 --- a/src/menus/transient/virtual-keyboard.c +++ b/src/menus/transient/virtual-keyboard.c @@ -5,6 +5,7 @@ #include "../../s_sound.h" #include "../../console.h" // CON_ShiftChar #include "../../i_system.h" // I_Clipboard funcs +#include "../../z_zone.h" // Typing "sub"-menu struct menutyping_s menutyping; @@ -91,9 +92,9 @@ boolean M_ChangeStringCvar(INT32 choice) const char *paste = I_ClipboardPaste(); if (paste == NULL || paste[0] == '\0') ; - else if (len < MAXSTRINGLENGTH - 1) + else if (len < menutyping.cachelen) { - strlcat(menutyping.cache, paste, MAXSTRINGLENGTH); + strlcat(menutyping.cache, paste, menutyping.cachelen + 1); S_StartSound(NULL, sfx_tmxbdn); // Tails } @@ -146,7 +147,7 @@ boolean M_ChangeStringCvar(INT32 choice) if (choice >= 32 && choice <= 127) { len = strlen(menutyping.cache); - if (len < MAXSTRINGLENGTH - 1) + if (len < menutyping.cachelen) { menutyping.cache[len++] = (char)choice; menutyping.cache[len] = 0; @@ -180,7 +181,19 @@ static void M_ToggleVirtualShift(void) static void M_CloseVirtualKeyboard(void) { menutyping.menutypingclose = true; // close menu. - CV_Set(currentMenu->menuitems[itemOn].itemaction.cvar, menutyping.cache); + menutyping.queryfn(menutyping.cache); +} + +void M_AbortVirtualKeyboard(void) +{ + if (!menutyping.active) + return; + + menutyping.active = false; + Z_Free(menutyping.cache); + + if (currentMenu == menutyping.dummymenu) + M_GoBack(0); } static boolean M_IsTypingKey(INT32 key) @@ -202,7 +215,7 @@ void M_MenuTypingInput(INT32 key) // Closing menutyping.menutypingfade--; if (!menutyping.menutypingfade) - menutyping.active = false; + M_AbortVirtualKeyboard(); return; // prevent inputs while closing the menu. } @@ -405,11 +418,28 @@ void M_MenuTypingInput(INT32 key) } } -void M_OpenVirtualKeyboard(boolean gamepad) +void M_OpenVirtualKeyboard(boolean gamepad, size_t cachelen, vkb_query_fn_t queryfn, menu_t *dummymenu) { menutyping.keyboardtyping = !gamepad; menutyping.active = true; menutyping.menutypingclose = false; - strlcpy(menutyping.cache, currentMenu->menuitems[itemOn].itemaction.cvar->string, MAXSTRINGLENGTH); + menutyping.queryfn = queryfn; + menutyping.dummymenu = dummymenu; + menutyping.cachelen = cachelen; + Z_Malloc(cachelen + 1, PU_STATIC, &menutyping.cache); + strlcpy(menutyping.cache, queryfn(NULL), cachelen + 1); + + if (dummymenu) + { + if (!menuactive) + { + M_StartControlPanel(); + dummymenu->prevMenu = NULL; + } + else + dummymenu->prevMenu = currentMenu; + + M_SetupNextMenu(dummymenu, true); + } } diff --git a/src/objects/gachabom-rebound.cpp b/src/objects/gachabom-rebound.cpp index 91e1f23c5..35c2bf7e7 100644 --- a/src/objects/gachabom-rebound.cpp +++ b/src/objects/gachabom-rebound.cpp @@ -228,7 +228,7 @@ void Obj_SpawnGachaBomRebound(mobj_t* source, mobj_t* target) { mobj_t *x = P_SpawnMobjFromMobjUnscaled(source, 0, 0, target->height / 2, MT_GACHABOM_REBOUND); - x->color = target->color; + x->color = target->player ? target->player->skincolor : target->color; x->angle = angle; if (!(gametyperules & GTR_BUMPERS) || battleprisons) diff --git a/src/p_inter.c b/src/p_inter.c index 11e32dbff..1889946e8 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2935,7 +2935,9 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da { sfx = sfx_invind; } - else if (K_IsBigger(target, inflictor) == true) + else if (K_IsBigger(target, inflictor) == true && + // SPB bypasses grow (K_IsBigger handles NULL check) + (type != DMG_EXPLODE || inflictor->type != MT_SPBEXPLOSION || !inflictor->movefactor)) { sfx = sfx_grownd; } diff --git a/src/p_mobj.c b/src/p_mobj.c index 12bf519bf..a686fa609 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11290,7 +11290,7 @@ tic_t itemrespawntime[ITEMQUESIZE]; size_t iquehead, iquetail; #ifdef PARANOIA -#define SCRAMBLE_REMOVED // Force debug build to crash when Removed mobj is accessed +//#define SCRAMBLE_REMOVED // Force debug build to crash when Removed mobj is accessed #endif void P_RemoveMobj(mobj_t *mobj) { diff --git a/src/p_saveg.c b/src/p_saveg.c index c3bb62629..dbbc95339 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -6286,6 +6286,14 @@ static boolean P_UnArchiveSPGame(savebuffer_t *save) { UINT32 val = READUINT32(save->p); + if (roundqueue.entries[i].rankrestricted && roundqueue.position != i+1) + { + // If this is a Sealed Star that hasn't yet been + // reached, don't be picky about divergance. Just + // use the base game without question. ~toast 010324 + continue; + } + mapnum = roundqueue.entries[i].mapnum; if (mapnum < nummapheaders && mapheaderinfo[mapnum] != NULL) { diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 27dd80043..f73355636 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -458,6 +458,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 num) mapheaderinfo[num]->palette = UINT16_MAX; mapheaderinfo[num]->encorepal = UINT16_MAX; mapheaderinfo[num]->numlaps = NUMLAPS_DEFAULT; + mapheaderinfo[num]->lapspersection = 1; mapheaderinfo[num]->levelselect = 0; mapheaderinfo[num]->levelflags = 0; mapheaderinfo[num]->menuflags = 0; diff --git a/src/p_tick.c b/src/p_tick.c index 1d273e328..3e1e2bb1c 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -1178,9 +1178,8 @@ void P_Ticker(boolean run) { G_WriteAllGhostTics(); - if (cv_recordmultiplayerdemos.value && (demo.savemode == DSM_NOTSAVING || demo.savemode == DSM_WILLAUTOSAVE)) - if (demo.savebutton && demo.savebutton + 3*TICRATE < leveltime) - G_CheckDemoTitleEntry(); + if (cv_recordmultiplayerdemos.value && demo.savebutton && demo.savebutton + 3*TICRATE < leveltime) + G_CheckDemoTitleEntry(); } else if (demo.playback) // Use Ghost data for consistency checks. { diff --git a/src/p_user.c b/src/p_user.c index 815551626..7adfa49ea 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -296,9 +296,29 @@ UINT8 P_GetNextEmerald(void) { cupheader_t *cup = NULL; - if (grandprixinfo.gp == true) + if (grandprixinfo.gp == true && grandprixinfo.cup) { - cup = grandprixinfo.cup; + if (gamedata->sealedswaps[GDMAX_SEALEDSWAPS-1] != NULL // all found + || grandprixinfo.cup->id >= basenumkartcupheaders // custom content + || M_SecretUnlocked(SECRET_SPECIALATTACK, false)) // true order + { + cup = grandprixinfo.cup; + } + else + { + // Determine order from sealedswaps. + UINT8 i; + for (i = 0; (i < GDMAX_SEALEDSWAPS && gamedata->sealedswaps[i]); i++) + { + if (gamedata->sealedswaps[i] != grandprixinfo.cup) + continue; + + // Repeat visit, grab the same ID. + break; + } + + return i+1; + } } if (cup == NULL) diff --git a/src/r_main.cpp b/src/r_main.cpp index 365bd6674..e82da5cc6 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -1617,7 +1617,11 @@ void R_RenderPlayerView(void) { if (top > bot) std::swap(top, bot); - UINT8* p = &screens[0][x + top * vid.width]; + if (top < 0) + top = 0; + if (bot > viewheight-1) + bot = viewheight-1; + UINT8* p = &topleft[x + top * vid.width]; while (top <= bot) { *p = 35; @@ -1634,7 +1638,7 @@ void R_RenderPlayerView(void) INT32 bottom = pl->bottom[pl->minx]; span(pl->minx, top, bottom); span(pl->maxx, pl->top[pl->maxx], pl->bottom[pl->maxx]); - for (INT32 x = pl->minx + 1; x < pl->maxx; ++x) + for (INT32 x = pl->minx + 1; x < std::min(pl->maxx, viewwidth); ++x) { INT32 new_top = pl->top[x]; INT32 new_bottom = pl->bottom[x]; @@ -1668,14 +1672,14 @@ void R_RenderPlayerView(void) INT32 width = (portal->end - portal->start); INT32 i; - for (i = 0; i < width; ++i) + for (i = 0; i < std::min(width, viewwidth); ++i) { INT32 yl = std::max(portal->ceilingclip[i] + 1, 0); INT32 yh = std::min(static_cast(portal->floorclip[i]), viewheight); for (; yl < yh; ++yl) { - screens[0][portal->start + i + (yl * vid.width)] = pal; + topleft[portal->start + i + (yl * vid.width)] = pal; } } diff --git a/src/r_plane.cpp b/src/r_plane.cpp index fba7de8a3..0d0dd68e8 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -126,6 +126,18 @@ static void R_UpdatePlaneRipple(drawspandata_t* ds) static void R_SetSlopePlaneVectors(drawspandata_t* ds, visplane_t *pl, INT32 y, fixed_t xoff, fixed_t yoff); +static bool R_CheckMapPlane(const char* funcname, INT32 y, INT32 x1, INT32 x2) +{ + if (x1 == x2) + return true; + + if (x1 < x2 && x1 >= 0 && x2 < viewwidth && y >= 0 && y < viewheight) + return true; + + CONS_Debug(DBG_RENDER, "%s: x1=%d, x2=%d at y=%d\n", funcname, x1, x2, y); + return false; +} + static void R_MapPlane(drawspandata_t *ds, spandrawfunc_t *spanfunc, INT32 y, INT32 x1, INT32 x2, boolean allow_parallel) { ZoneScoped; @@ -133,13 +145,8 @@ static void R_MapPlane(drawspandata_t *ds, spandrawfunc_t *spanfunc, INT32 y, IN fixed_t distance = 0, span; size_t pindex; -#ifdef RANGECHECK - if (x2 < x1 || x1 < 0 || x2 >= viewwidth || y > viewheight) - I_Error("R_MapPlane: %d, %d at %d", x1, x2, y); -#endif - - if (x1 >= vid.width) - x1 = vid.width - 1; + if (!R_CheckMapPlane(__func__, y, x1, x2)) + return; angle = (ds->currentplane->viewangle + ds->currentplane->plangle)>>ANGLETOFINESHIFT; planecos = FINECOSINE(angle); @@ -216,13 +223,9 @@ static void R_MapPlane(drawspandata_t *ds, spandrawfunc_t *spanfunc, INT32 y, IN static void R_MapTiltedPlane(drawspandata_t *ds, void(*spanfunc)(drawspandata_t*), INT32 y, INT32 x1, INT32 x2, boolean allow_parallel) { ZoneScoped; -#ifdef RANGECHECK - if (x2 < x1 || x1 < 0 || x2 >= viewwidth || y >= viewheight || y < 0) - I_Error("R_MapTiltedPlane: %d, %d at %d", x1, x2, y); -#endif - if (x1 >= vid.width) - x1 = vid.width - 1; + if (!R_CheckMapPlane(__func__, y, x1, x2)) + return; // Water ripple effect if (ds->planeripple.active) diff --git a/src/st_stuff.c b/src/st_stuff.c index d65d19ea1..65f048b7c 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1303,36 +1303,6 @@ static void ST_overlayDrawer(void) K_DrawMidVote(); } -void ST_DrawDemoTitleEntry(void) -{ - static UINT8 anim = 0; - char *nametodraw; - - anim++; - anim %= 8; - - nametodraw = demo.titlename; - while (V_StringWidth(nametodraw, 0) > MAXSTRINGLENGTH*8 - 8) - nametodraw++; - -#define x (BASEVIDWIDTH/2 - 139) -#define y (BASEVIDHEIGHT/2) - M_DrawTextBox(x, y + 4, MAXSTRINGLENGTH, 1); - V_DrawString(x + 8, y + 12, 0, nametodraw); - if (anim < 4) - V_DrawCharacter(x + 8 + V_StringWidth(nametodraw, 0), y + 12, - '_' | 0x80, false); - - M_DrawTextBox(x + 30, y - 24, 26, 1); - V_DrawString(x + 38, y - 16, 0, "Enter the name of the replay."); - - M_DrawTextBox(x + 50, y + 20, 20, 1); - V_DrawThinString(x + 58, y + 28, 0, "Escape - Cancel"); - V_DrawRightAlignedThinString(x + 220, y + 28, 0, "Enter - Confirm"); -#undef x -#undef y -} - // MayonakaStatic: draw Midnight Channel's TV-like borders static void ST_MayonakaStatic(void) { @@ -1487,6 +1457,15 @@ void ST_DrawServerSplash(boolean timelimited) } } +void ST_DrawSaveReplayHint(INT32 flags) +{ + V_DrawRightAlignedThinString( + BASEVIDWIDTH - 2, 2, + flags|V_YELLOWMAP, + demo.willsave ? "Replay will be saved. \xAB" "Change title" : "\xAB" "or " "\xAE" "Save replay" + ); +} + static fixed_t ST_CalculateFadeIn(player_t *player) { const tic_t length = TICRATE/4; @@ -1626,26 +1605,6 @@ void ST_Drawer(void) INT32 flags = V_SNAPTOTOP | V_SNAPTORIGHT | (Easing_Linear(min(t, fadeLength) * FRACUNIT / fadeLength, 9, 0) << V_ALPHASHIFT); - switch (demo.savemode) - { - case DSM_NOTSAVING: - V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, flags|V_YELLOWMAP, "\xAB" "or " "\xAE" "Save replay"); - break; - - case DSM_WILLAUTOSAVE: - V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, flags|V_YELLOWMAP, "Replay will be saved. \xAB" "Change title"); - break; - - case DSM_WILLSAVE: - V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, flags|V_YELLOWMAP, "Replay will be saved."); - break; - - case DSM_TITLEENTRY: - ST_DrawDemoTitleEntry(); - break; - - default: // Don't render anything - break; - } + ST_DrawSaveReplayHint(flags); } } diff --git a/src/st_stuff.h b/src/st_stuff.h index a48eaf213..23eecb034 100644 --- a/src/st_stuff.h +++ b/src/st_stuff.h @@ -33,9 +33,6 @@ extern "C" { // Called by main loop. void ST_Ticker(boolean run); -// Called when naming a replay. -void ST_DrawDemoTitleEntry(void); - #ifdef HAVE_DISCORDRPC // Called when you have Discord asks void ST_AskToJoinEnvelope(void); @@ -79,6 +76,7 @@ extern tic_t lt_exitticker, lt_endtime; extern tic_t lt_fade; void ST_DrawServerSplash(boolean timelimited); +void ST_DrawSaveReplayHint(INT32 flags); // return if player a is in the same team as player b boolean ST_SameTeam(player_t *a, player_t *b); diff --git a/src/v_video.cpp b/src/v_video.cpp index 1db7bd01c..06991d0e5 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -2702,9 +2702,11 @@ void V_DrawStringScaled( c -= font->start; if (V_CharacterValid(font, c) == true) { + // Remove offsets from patch + fixed_t patchxofs = SHORT (font->font[c]->leftoffset) * dupx * FRACUNIT; cw = SHORT (font->font[c]->width) * dupx; cxoff = (*fontspec.dim_fn)(scale, fontspec.chw, hchw, dupx, &cw); - V_DrawFixedPatch(cx + cxoff, cy + cyoff, scale, + V_DrawFixedPatch(cx + cxoff + patchxofs, cy + cyoff, scale, flags, font->font[c], colormap); cx += cw; } diff --git a/src/y_inter.cpp b/src/y_inter.cpp index 1822debde..688afbd8c 100644 --- a/src/y_inter.cpp +++ b/src/y_inter.cpp @@ -80,7 +80,6 @@ static INT32 powertype = PWRLV_DISABLED; static INT32 intertic; static INT32 endtic = -1; static INT32 sorttic = -1; -static INT32 replayprompttic; static fixed_t mqscroll = 0; static fixed_t chkscroll = 0; @@ -1690,35 +1689,8 @@ skiptallydrawer: } finalcounter: - { - if ((modeattacking == ATTACKING_NONE) && (demo.recording || demo.savemode == demovars_s::DSM_SAVED) && !demo.playback) - { - switch (demo.savemode) - { - case demovars_s::DSM_NOTSAVING: - { - INT32 buttonx = BASEVIDWIDTH; - INT32 buttony = 2; - - K_drawButtonAnim(buttonx - 76, buttony, 0, kp_button_b[1], replayprompttic); - V_DrawRightAlignedThinString(buttonx - 55, buttony, highlightflags, "or"); - K_drawButtonAnim(buttonx - 55, buttony, 0, kp_button_x[1], replayprompttic); - V_DrawRightAlignedThinString(buttonx - 2, buttony, highlightflags, "Save replay"); - break; - } - case demovars_s::DSM_SAVED: - V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, highlightflags, "Replay saved!"); - break; - - case demovars_s::DSM_TITLEENTRY: - ST_DrawDemoTitleEntry(); - break; - - default: // Don't render any text here - break; - } - } - } + if ((modeattacking == ATTACKING_NONE) && demo.recording) + ST_DrawSaveReplayHint(0); if (Y_CanSkipIntermission()) { @@ -1754,16 +1726,7 @@ void Y_Ticker(void) return; if (demo.recording) - { - if (demo.savemode == demovars_s::DSM_NOTSAVING) - { - replayprompttic++; - G_CheckDemoTitleEntry(); - } - - if (demo.savemode == demovars_s::DSM_WILLSAVE || demo.savemode == demovars_s::DSM_WILLAUTOSAVE) - G_SaveDemo(); - } + G_CheckDemoTitleEntry(); // Check for pause or menu up in single player if (paused || P_AutoPause())