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/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 79e5bd91e..3ca13d642 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4549,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/k_menu.h b/src/k_menu.h index 049559ce4..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 @@ -682,7 +686,8 @@ void M_PlayMenuJam(void); boolean M_ConsiderSealedSwapAlert(void); -void M_OpenVirtualKeyboard(boolean gamepad); +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); diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 0929e714e..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)) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 7833da1e3..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 @@ -831,7 +839,7 @@ void M_ClearMenus(boolean callexitmenufunc) D_StartTitle(); } - menutyping.active = false; + M_AbortVirtualKeyboard(); menumessage.active = false; menuactive = false; @@ -918,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; @@ -940,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); } // @@ -1124,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/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/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/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/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())