From 0ac0efda701f5c261d6b953aa0533f214e4a730a Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Thu, 17 Feb 2022 15:05:22 +0100 Subject: [PATCH] Add typing menu overlay for controller parity --- src/k_menu.h | 16 ++++ src/k_menudraw.c | 105 +++++++++++++++++++++- src/k_menufunc.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 338 insertions(+), 4 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index a7acece63..a7b7a86d6 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -357,6 +357,22 @@ extern INT16 skullAnimCounter; // skull animation counter extern INT32 menuKey; // keyboard key pressed for menu +extern boolean menutyping; // Typing sub-menu +extern boolean menutypingclose; // if true, we're closing the menu. +extern boolean keyboardtyping; // If true, all keystrokes are treated as typing (ignores MBT_A etc). This is unset if you try moving the cursor on the virtual keyboard or use your controller +extern SINT8 menutypingfade; // Fade-in and out for typing sub-menu. (in 10 tics) +// While typing, we'll have a fade strongly darken the screen to overlay the typing menu instead + +extern INT16 virtualKeyboard[5][13]; +extern INT16 shift_virtualKeyboard[5][13]; + +// cursor position on the virtual keyboard grid +extern SINT8 keyboardx; +extern SINT8 keyboardy; +extern boolean keyboardcapslock; +extern boolean keyboardshift; + + #define MENUDELAYTIME 7 #define MENUMINDELAY 2 diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 562f8e9e3..602738929 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -223,6 +223,81 @@ void M_DrawMenuForeground(void) } } +// Draws the typing submenu +static void M_DrawMenuTyping(void) +{ + + INT32 i, j; + + INT32 x = 60; + INT32 y = 100 + (9-menutypingfade)*8; + INT32 tflag = (9 - menutypingfade)<menuitems[itemOn].itemaction; + + char buf[8]; // We write there to use drawstring for convenience. + + V_DrawFadeScreen(31, menutypingfade); + + // Draw the string we're editing at the top. + V_DrawString(x, y-48 + 12, V_ALLOWLOWERCASE|tflag, cv->string); + if (skullAnimCounter < 4) + V_DrawCharacter(x + V_StringWidth(cv->string, 0), y - 35, '_' | 0x80, false); + + // Some contextual stuff + if (keyboardtyping) + V_DrawThinString(10, 175, V_ALLOWLOWERCASE|tflag|V_GRAYMAP, "Type using your keyboard. Press Enter to confirm & exit.\nUse your controller or any directional input to use the Virtual Keyboard.\n"); + else + V_DrawThinString(10, 175, V_ALLOWLOWERCASE|tflag|V_GRAYMAP, "Type using the Virtual Keyboard. Use the \'OK\' button to confirm & exit.\nPress any keyboard key not bound to a control to use it."); + + + // Now the keyboard itself + for (i=0; i < 5; i++) + { + for (j=0; j < 13; j++) + { + INT32 mflag = 0; + INT16 c = virtualKeyboard[i][j]; + if (keyboardshift ^ keyboardcapslock) + c = shift_virtualKeyboard[i][j]; + + + if (c == KEY_BACKSPACE) + strcpy(buf, "DEL"); + + else if (c == KEY_RSHIFT) + strcpy(buf, "SHIFT"); + + else if (c == KEY_CAPSLOCK) + strcpy(buf, "CAPS"); + + else if (c == KEY_ENTER) + strcpy(buf, "OK"); + + else if (c == KEY_SPACE) + strcpy(buf, "SPACE"); + + else + { + buf[0] = c; + buf[1] = '\0'; + } + + // highlight: + if (keyboardx == j && keyboardy == i && !keyboardtyping) + mflag |= highlightflags; + else if (keyboardtyping) + mflag |= V_TRANSLUCENT; // grey it out if we can't use it. + + V_DrawString(x, y, V_ALLOWLOWERCASE|tflag|mflag, buf); + + x += V_StringWidth(buf, 0)+8; + } + x = 60; + y += 12; + } +} + // // M_Drawer // Called after the view has been rendered, @@ -274,7 +349,12 @@ void M_Drawer(void) V_DrawThinString(vid.dupx, vid.height - 10*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, va("%s", VERSIONSTRING)); #endif } + + } + // Draw typing overlay when needed, above all other menu elements. + if (menutyping) + M_DrawMenuTyping(); } if (menuwipe) @@ -2093,7 +2173,7 @@ void M_DrawProfileSelect(void) void M_DrawEditProfile(void) { - INT32 y = 40; + INT32 y = 34; INT32 x = 145; INT32 i; @@ -2119,12 +2199,31 @@ void M_DrawEditProfile(void) colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE); // Background - V_DrawFill(0, y, 400 - (menutransition.tics*32), 24, itemOn == i ? 169 : 30); // 169 is the plague colourization + V_DrawFill(0, y, 400 - (menutransition.tics*64), 24, itemOn == i ? 169 : 30); // 169 is the plague colourization // Text V_DrawGamemodeString(x + (menutransition.tics*32), y - 6, V_ALLOWLOWERCASE, colormap, currentMenu->menuitems[i].text); + // Cvar specific handling + switch (currentMenu->menuitems[i].status & IT_TYPE) + { + case IT_CVAR: + { + consvar_t *cv = (consvar_t *)currentMenu->menuitems[i].itemaction; + switch (currentMenu->menuitems[i].status & IT_CVARTYPE) + { + case IT_CV_STRING: + V_DrawFill(0, y+24, 400 - (menutransition.tics*64), 16, itemOn == i ? 169 : 30); // 169 is the plague colourization + V_DrawString(x + 8, y + 29, V_ALLOWLOWERCASE, cv->string); + if (skullAnimCounter < 4 && i == itemOn) + V_DrawCharacter(x + 8 + V_StringWidth(cv->string, 0), y + 29, + '_' | 0x80, false); + y += 16; + break; + } + } + } - y += 42; + y += 34; break; } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 119c4bcf1..ecc5af6a9 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -96,6 +96,35 @@ struct menutransition_s menutransition; // Menu transition properties INT32 menuKey = -1; // keyboard key pressed for menu menucmd_t menucmd[MAXSPLITSCREENPLAYERS]; +// Typing "sub"-menu +boolean menutyping = false; +boolean menutypingclose = false; +SINT8 menutypingfade = 0; +boolean keyboardtyping = false; + +SINT8 keyboardx = 0; +SINT8 keyboardy = 0; +boolean keyboardcapslock = false; +boolean keyboardshift = false; + +INT16 virtualKeyboard[5][13] = { + + {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0}, + {'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 0}, + {'a', 's', 'd', 'f', 'g', 'h', 'i', 'j', 'k', 'l', ';', '\'', '\\'}, + {'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, 0, 0}, + {KEY_SPACE, KEY_RSHIFT, KEY_BACKSPACE, KEY_CAPSLOCK, KEY_ENTER, 0, 0, 0, 0, 0, 0, 0, 0} +}; + +INT16 shift_virtualKeyboard[5][13] = { + + {'!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0}, + {'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', 0}, + {'A', 'S', 'D', 'F', 'G', 'H', 'I', 'J', 'K', 'L', ':', '\"', '|'}, + {'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, 0, 0}, + {KEY_SPACE, KEY_RSHIFT, KEY_BACKSPACE, KEY_CAPSLOCK, KEY_ENTER, 0, 0, 0, 0, 0, 0, 0, 0} +}; + // finish wipes between screens boolean menuwipe = false; @@ -1130,6 +1159,176 @@ boolean M_MenuButtonPressed(UINT8 pid, UINT32 bt) return (menucmd[pid].buttons & bt); } +// Updates the x coordinate of the keybord so prevent it from going in weird places +static void M_UpdateKeyboardX(void) +{ + // 0s are only at the rightmost edges of the keyboard table, so just go backwards until we get something. + while (!virtualKeyboard[keyboardy][keyboardx]) + keyboardx--; +} + +// Hack... +// This is used to prevent keyboard inputs from being processed when they shouldn't. +// We need to find why this is needed so that I can remove this disgusting thing +static INT32 lastkey = 0; +static tic_t lastkeyheldfor = 0; + +static void M_MenuTypingInput(INT32 key) +{ + + const UINT8 pid = 0; + + // Fade-in + + if (menutypingclose) // closing + { + menutypingfade--; + if (!menutypingfade) + menutyping = false; + + return; // prevent inputs while closing the menu. + } + else // opening + { + menutypingfade++; + if (menutypingfade > 9) // Don't fade all the way, but have it VERY strong to be readable + menutypingfade = 9; + else if (menutypingfade < 9) + return; // Don't allow typing until it's fully opened. + } + + // Now handle the inputs. + // HACK: TODO: REMOVE THIS + // This prevents the same key from being processed multiple times. ev_keydown should account for this but it seems like it doesn't. + if (lastkey != key) + { + lastkey = key; + lastkeyheldfor = 0; + } + else + { + lastkeyheldfor++; + if (lastkeyheldfor > 0 && lastkeyheldfor < TICRATE/2) + key = -1; + } + // END OF HACK. + + // Determine when to check for keyboard inputs or controller inputs using menuKey, which is the key passed here as argument. + if (!keyboardtyping) // controller inputs + { + // we pressed a keyboard input that's not any of our buttons + if (key != -1 && menucmd[pid].dpad_lr == 0 && menucmd[pid].dpad_ud == 0 + && !M_MenuButtonPressed(pid, MBT_A) + && !M_MenuButtonPressed(pid, MBT_B) + && !M_MenuButtonPressed(pid, MBT_C) + && !M_MenuButtonPressed(pid, MBT_X) + && !M_MenuButtonPressed(pid, MBT_Y) + && !M_MenuButtonPressed(pid, MBT_Z)) + { + keyboardtyping = true; + } + } + else // Keyboard inputs. + { + // On the flipside, if we're pressing any keyboard input, switch to controller inputs. + if (key == -1 && ( + M_MenuButtonPressed(pid, MBT_A) + || M_MenuButtonPressed(pid, MBT_B) + || M_MenuButtonPressed(pid, MBT_C) + || M_MenuButtonPressed(pid, MBT_X) + || M_MenuButtonPressed(pid, MBT_Y) + || M_MenuButtonPressed(pid, MBT_Z) + || menucmd[pid].dpad_lr != 0 + || menucmd[pid].dpad_ud != 0 + )) + { + keyboardtyping = false; + return; + } + + // OTHERWISE, process keyboard inputs for typing! + if (key == KEY_ENTER) + { + menutypingclose = true; // close menu. + return; + } + else // process everything else as input for typing + { + M_ChangeStringCvar(key); + } + + } + + if (menucmd[pid].delay == 0 && !keyboardtyping) // We must check for this here because we bypass the normal delay check to allow for normal keyboard inputs + { + if (menucmd[pid].dpad_ud > 0) // down + { + keyboardy++; + if (keyboardy > 4) + keyboardy = 0; + + M_UpdateKeyboardX(); + M_SetMenuDelay(pid); + S_StartSound(NULL, sfx_menu1); + } + else if (menucmd[pid].dpad_ud < 0) // up + { + keyboardy--; + if (keyboardy < 0) + keyboardy = 4; + + M_UpdateKeyboardX(); + M_SetMenuDelay(pid); + S_StartSound(NULL, sfx_menu1); + } + else if (menucmd[pid].dpad_lr > 0) // right + { + keyboardx++; + if (!virtualKeyboard[keyboardy][keyboardx]) + keyboardx = 0; + + M_SetMenuDelay(pid); + S_StartSound(NULL, sfx_menu1); + } + else if (menucmd[pid].dpad_lr < 0) // left + { + keyboardx--; + if (keyboardx < 0) + { + keyboardx = 13; + M_UpdateKeyboardX(); + } + M_SetMenuDelay(pid); + S_StartSound(NULL, sfx_menu1); + } + else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + { + // Add the character. First though, check what we're pressing.... + INT16 c = virtualKeyboard[keyboardy][keyboardx]; + if (keyboardshift ^ keyboardcapslock) + c = shift_virtualKeyboard[keyboardy][keyboardx]; + + if (c == KEY_RSHIFT) + keyboardshift = !keyboardshift; + else if (c == KEY_CAPSLOCK) + keyboardcapslock = !keyboardcapslock; + else if (c == KEY_ENTER) + { + menutypingclose = true; // close menu. + return; + } + else + { + M_ChangeStringCvar((INT32)c); // Write! + keyboardshift = false; // undo shift if it had been pressed + } + + M_SetMenuDelay(pid); + S_StartSound(NULL, sfx_menu1); + } + } +} + static void M_HandleMenuInput(void) { void (*routine)(INT32 choice); // for some casting problem @@ -1149,6 +1348,13 @@ static void M_HandleMenuInput(void) return; } + // Typing for CV_IT_STRING + if (menutyping) + { + M_MenuTypingInput(menuKey); + return; + } + if (menucmd[pid].delay > 0) { return; @@ -1210,8 +1416,18 @@ static void M_HandleMenuInput(void) { if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_STRING) { - // As mentioned earlier, we need a typing submenu. + // Routine is null either way routine = NULL; + + // If we're hovering over a IT_CV_STRING option, pressing A/X opens the typing submenu + if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + { + keyboardtyping = menuKey != 0 ? true : false; // If we entered this menu by pressing a menu Key, default to keyboard typing, otherwise use controller. + menutyping = true; + menutypingclose = false; + return; + } + } else { @@ -1467,6 +1683,9 @@ void M_Init(void) CV_RegisterVar(&cv_dummygametype); CV_RegisterVar(&cv_dummyip); + CV_RegisterVar(&cv_dummyprofilename); + CV_RegisterVar(&cv_dummyprofileplayername); + CV_RegisterVar(&cv_dummygpdifficulty); CV_RegisterVar(&cv_dummykartspeed); CV_RegisterVar(&cv_dummygpencore);