mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-03-14 23:21:49 +00:00
Add typing menu overlay for controller parity
This commit is contained in:
parent
8efba24df7
commit
0ac0efda70
3 changed files with 338 additions and 4 deletions
16
src/k_menu.h
16
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
|
||||
|
||||
|
|
|
|||
105
src/k_menudraw.c
105
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)<<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;
|
||||
}
|
||||
|
|
|
|||
221
src/k_menufunc.c
221
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);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue