Add typing menu overlay for controller parity

This commit is contained in:
SinnamonLat 2022-02-17 15:05:22 +01:00
parent 8efba24df7
commit 0ac0efda70
3 changed files with 338 additions and 4 deletions

View file

@ -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

View file

@ -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)<<V_ALPHASHIFT;
consvar_t *cv = (consvar_t *)currentMenu->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;
}

View file

@ -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);