Make message boxes separate overlays

This commit is contained in:
SinnamonLat 2022-02-27 00:15:31 +01:00
parent f5bd0dc3e4
commit 2696effe7a
3 changed files with 241 additions and 111 deletions

View file

@ -379,6 +379,30 @@ extern struct menutyping_s
} menutyping;
// While typing, we'll have a fade strongly darken the screen to overlay the typing menu instead
typedef enum
{
MA_NONE = 0,
MA_YES,
MA_NO
} manswer_e;
#define MAXMENUMESSAGE 256
extern struct menumessage_s
{
boolean active;
INT32 flags; // MM_
char message[MAXMENUMESSAGE]; // message to display
SINT8 fadetimer; // opening
INT32 x;
INT32 y;
INT32 m;
void (*routine)(INT32 choice); // Normal routine
void (*eroutine)(event_t *ev); // Event routine (MM_EVENTHANDLER)
} menumessage;
void M_HandleMenuMessage(void);
#define MENUDELAYTIME 7
#define MENUMINDELAY 2

View file

@ -298,6 +298,68 @@ static void M_DrawMenuTyping(void)
}
}
// Draw the message popup submenu
static void M_DrawMenuMessage(void)
{
INT32 y = menumessage.y + (9-menumessage.fadetimer)*20;
size_t i, start = 0;
INT16 max;
char string[MAXMENUMESSAGE];
INT32 mlines;
const char *msg = menumessage.message;
mlines = menumessage.m>>8;
max = (INT16)((UINT8)(menumessage.m & 0xFF)*8);
V_DrawFadeScreen(31, menumessage.fadetimer);
M_DrawTextBox(menumessage.x, y - 8, (max+7)>>3, mlines);
while (*(msg+start))
{
size_t len = strlen(msg+start);
for (i = 0; i < len; i++)
{
if (*(msg+start+i) == '\n')
{
memset(string, 0, MAXMENUMESSAGE);
if (i >= MAXMENUMESSAGE)
{
CONS_Printf("M_DrawMenuMessage: too long segment in %s\n", msg);
return;
}
else
{
strncpy(string,msg+start, i);
string[i] = '\0';
start += i;
i = (size_t)-1; //added : 07-02-98 : damned!
start++;
}
break;
}
}
if (i == strlen(msg+start))
{
if (i >= MAXMENUMESSAGE)
{
CONS_Printf("M_DrawMenuMessage: too long segment in %s\n", msg);
return;
}
else
{
strcpy(string, msg + start);
start += i;
}
}
V_DrawString((BASEVIDWIDTH - V_StringWidth(string, 0))/2, y, V_ALLOWLOWERCASE, string);
y += 8;
}
}
//
// M_Drawer
// Called after the view has been rendered,
@ -352,6 +414,11 @@ void M_Drawer(void)
}
// Draw message overlay when needed
if (menumessage.active)
M_DrawMenuMessage();
// Draw typing overlay when needed, above all other menu elements.
if (menutyping.active)
M_DrawMenuTyping();
@ -648,7 +715,7 @@ void M_DrawMessageMenu(void)
INT32 y = currentMenu->y;
size_t i, start = 0;
INT16 max;
char string[MAXMSGLINELEN];
char string[MAXMENUMESSAGE];
INT32 mlines;
const char *msg = currentMenu->menuitems[0].text;
@ -665,8 +732,8 @@ void M_DrawMessageMenu(void)
{
if (*(msg+start+i) == '\n')
{
memset(string, 0, MAXMSGLINELEN);
if (i >= MAXMSGLINELEN)
memset(string, 0, MAXMENUMESSAGE);
if (i >= MAXMENUMESSAGE)
{
CONS_Printf("M_DrawMessageMenu: too long segment in %s\n", msg);
return;
@ -685,7 +752,7 @@ void M_DrawMessageMenu(void)
if (i == strlen(msg+start))
{
if (i >= MAXMSGLINELEN)
if (i >= MAXMENUMESSAGE)
{
CONS_Printf("M_DrawMessageMenu: too long segment in %s\n", msg);
return;
@ -2448,13 +2515,21 @@ void M_DrawProfileControls(void)
// Overlay for control binding
if (optionsmenu.bindcontrol)
{
V_DrawFadeScreen(31, 8);
INT16 reversetimer = TICRATE*5 - optionsmenu.bindtimer;
INT32 fade = reversetimer;
INT32 ypos;
M_DrawTextBox((BASEVIDWIDTH/2) - (120), (BASEVIDHEIGHT/2) - (16), 30, 4);
if (fade > 9)
fade = 9;
V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2) - (4), 0, va("Press key #%d for control", optionsmenu.bindcontrol));
V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2) - (4) +10, 0, va("\"%s\"", currentMenu->menuitems[itemOn].text));
V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2) - (4) +20, highlightflags, va("(WAIT %d SECONDS TO SKIP)", optionsmenu.bindtimer/TICRATE));
ypos = (BASEVIDHEIGHT/2) - 4 +16*(9 - fade);
V_DrawFadeScreen(31, fade);
M_DrawTextBox((BASEVIDWIDTH/2) - (120), ypos - 12, 30, 4);
V_DrawCenteredString(BASEVIDWIDTH/2, ypos, 0, va("Press key #%d for control", optionsmenu.bindcontrol));
V_DrawCenteredString(BASEVIDWIDTH/2, ypos +10, 0, va("\"%s\"", currentMenu->menuitems[itemOn].text));
V_DrawCenteredString(BASEVIDWIDTH/2, ypos +20, highlightflags, va("(WAIT %d SECONDS TO SKIP)", optionsmenu.bindtimer/TICRATE));
}
}

View file

@ -96,8 +96,10 @@ struct menutransition_s menutransition; // Menu transition properties
INT32 menuKey = -1; // keyboard key pressed for menu
menucmd_t menucmd[MAXSPLITSCREENPLAYERS];
// Typing "sub"-menu
// message prompt struct
struct menumessage_s menumessage;
// Typing "sub"-menu
struct menutyping_s menutyping;
// keyboard layouts
@ -455,7 +457,7 @@ static void M_EraseDataResponse(INT32 ch)
{
UINT8 i;
if (ch != 'y' && ch != KEY_ENTER)
if (ch == MA_NO)
return;
S_StartSound(NULL, sfx_itrole); // bweh heh heh
@ -480,7 +482,7 @@ static void M_EraseDataResponse(INT32 ch)
void M_EraseData(INT32 choice)
{
const char *eschoice, *esstr = M_GetText("Are you sure you want to erase\n%s?\n\n(Press 'Y' to confirm)\n");
const char *eschoice, *esstr = M_GetText("Are you sure you want to erase\n%s?\n\n(Press A to confirm)\n");
optionsmenu.erasecontext = (UINT8)choice;
@ -833,6 +835,14 @@ boolean M_Responder(event_t *ev)
return true; // eat events.
}
// event handler for MM_EVENTHANDLER
if (menumessage.active && menumessage.flags == MM_EVENTHANDLER && menumessage.routine)
{
CONS_Printf("MM_EVENTHANDLER...\n");
menumessage.eroutine(ev); // What a terrible hack...
return true;
}
// Handle menu handling in-game.
if (menuactive == false)
{
@ -999,6 +1009,9 @@ void M_ClearMenus(boolean callexitmenufunc)
if (gamestate == GS_MENU) // Back to title screen
D_StartTitle();
menutyping.active = false;
menumessage.active = false;
menuactive = false;
}
@ -1346,6 +1359,12 @@ static void M_HandleMenuInput(void)
return;
}
if (menumessage.active)
{
M_HandleMenuMessage();
return;
}
// Typing for CV_IT_STRING
if (menutyping.active)
{
@ -1375,41 +1394,6 @@ static void M_HandleMenuInput(void)
return;
}
// TODO: Move this to message menu code
if (currentMenu->menuitems[itemOn].status == IT_MSGHANDLER)
{
if (currentMenu->menuitems[itemOn].mvar1 != MM_EVENTHANDLER)
{
if (menucmd[pid].buttons != 0 && menucmd[pid].buttonsHeld == 0)
{
if (routine)
{
routine(menuKey);
}
M_StopMessage(0);
noFurtherInput = true;
M_SetMenuDelay(pid);
return;
}
return;
}
else
{
//#if 0 // this shit is crazy
if (routine)
{
//void (*otherroutine)(event_t *sev) = currentMenu->menuitems[itemOn].itemaction;
//otherroutine(menuKey); //Alam: what a hack
routine(menuKey);
}
//#endif
return;
}
}
// BP: one of the more big hack i have never made
if (routine && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_CVAR)
{
@ -1727,7 +1711,7 @@ menu_t MessageDef =
0, 0, // x, y (TO HACK)
0, 0, // extra1, extra2
0, 0, // transition tics
M_DrawMessageMenu, // drawing routine ->
NULL, // drawing routine ->
NULL, // ticker routine
NULL, // init routine
NULL, // quit routine
@ -1753,12 +1737,16 @@ static inline size_t M_StringHeight(const char *string)
// default message handler
void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtype)
{
const UINT8 pid = 0;
size_t max = 0, start = 0, i, strlines;
static char *message = NULL;
Z_Free(message);
message = Z_StrDup(string);
DEBFILE(message);
CONS_Printf("M_StartMessage()...\n");
// Rudementary word wrapping.
// Simple and effective. Does not handle nonuniform letter sizes, colors, etc. but who cares.
strlines = 0;
@ -1789,34 +1777,28 @@ void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtyp
}
}
strncpy(menumessage.message, string, MAXMENUMESSAGE);
menumessage.flags = itemtype;
menumessage.routine = routine;
menumessage.fadetimer = 1;
menumessage.active = true;
start = 0;
max = 0;
M_StartControlPanel(); // can't put menuactive to true
if (currentMenu == &MessageDef) // Prevent recursion
MessageDef.prevMenu = ((demo.playback) ? &PAUSE_PlaybackMenuDef : &MainDef);
else
MessageDef.prevMenu = currentMenu;
MessageDef.menuitems[0].text = message;
MessageDef.menuitems[0].mvar1 = (UINT8)itemtype;
if (!routine && itemtype != MM_NOTHING) itemtype = MM_NOTHING;
switch (itemtype)
if (!routine || menumessage.flags == MM_NOTHING)
{
case MM_NOTHING:
MessageDef.menuitems[0].status = IT_MSGHANDLER;
MessageDef.menuitems[0].itemaction = M_StopMessage;
break;
case MM_YESNO:
MessageDef.menuitems[0].status = IT_MSGHANDLER;
MessageDef.menuitems[0].itemaction = routine;
break;
case MM_EVENTHANDLER:
MessageDef.menuitems[0].status = IT_MSGHANDLER;
MessageDef.menuitems[0].itemaction = routine;
break;
menumessage.flags = MM_NOTHING;
menumessage.routine = M_StopMessage;
}
// event routine
if (menumessage.flags == MM_EVENTHANDLER)
{
menumessage.eroutine = routine;
menumessage.routine = NULL;
}
//added : 06-02-98: now draw a textbox around the message
// compute lenght max and the numbers of lines
for (strlines = 0; *(message+start); strlines++)
@ -1838,21 +1820,73 @@ void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtyp
start += i;
}
MessageDef.x = (INT16)((BASEVIDWIDTH - 8*max-16)/2);
MessageDef.y = (INT16)((BASEVIDHEIGHT - M_StringHeight(message))/2);
menumessage.x = (INT16)((BASEVIDWIDTH - 8*max-16)/2);
menumessage.y = (INT16)((BASEVIDHEIGHT - M_StringHeight(message))/2);
MessageDef.lastOn = (INT16)((strlines<<8)+max);
menumessage.m = (INT16)((strlines<<8)+max);
//M_SetupNextMenu();
currentMenu = &MessageDef;
itemOn = 0;
M_SetMenuDelay(pid); // Set menu delay to avoid setting off any of the handlers.
}
void M_StopMessage(INT32 choice)
{
(void)choice;
if (menuactive)
M_SetupNextMenu(MessageDef.prevMenu, true);
const char pid = 0;
(void) choice;
menumessage.active = false;
M_SetMenuDelay(pid);
}
// regular handler for MM_NOTHING and MM_YESNO
void M_HandleMenuMessage(void)
{
const UINT8 pid = 0;
boolean btok = M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X);
boolean btnok = M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y);
menumessage.fadetimer++;
if (menumessage.fadetimer > 9)
menumessage.fadetimer = 9;
switch (menumessage.flags)
{
// Send 1 to the routine if we're pressing A/B/X/Y
case MM_NOTHING:
{
// send 1 if any button is pressed, 0 otherwise.
if (btok || btnok)
menumessage.routine(0);
break;
}
// Send 1 to the routine if we're pressing A/X, 2 if B/Y, 0 otherwise.
case MM_YESNO:
{
INT32 answer = MA_NONE;
if (btok)
answer = MA_YES;
else if (btnok)
answer = MA_NO;
// send 1 if btok is pressed, 2 if nok is pressed, 0 otherwise.
if (answer)
{
menumessage.routine(answer);
M_StopMessage(0);
}
break;
}
// MM_EVENTHANDLER: In M_Responder to allow full event compat.
default:
break;
}
// if we detect any keypress, don't forget to set the menu delay regardless.
if (btok || btnok)
M_SetMenuDelay(pid);
}
// =========
@ -1941,27 +1975,26 @@ void M_QuitResponse(INT32 ch)
tic_t ptime;
INT32 mrand;
if (ch != 'y' && ch != KEY_ENTER)
return;
if (!(netgame || cv_debug))
if (ch == MA_YES)
{
mrand = M_RandomKey(sizeof(quitsounds) / sizeof(INT32));
if (quitsounds[mrand])
S_StartSound(NULL, quitsounds[mrand]);
//added : 12-02-98: do that instead of I_WaitVbl which does not work
ptime = I_GetTime() + NEWTICRATE*2; // Shortened the quit time, used to be 2 seconds Tails 03-26-2001
while (ptime > I_GetTime())
if (!(netgame || cv_debug))
{
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
V_DrawSmallScaledPatch(0, 0, 0, W_CachePatchName("GAMEQUIT", PU_CACHE)); // Demo 3 Quit Screen Tails 06-16-2001
I_FinishUpdate(); // Update the screen with the image Tails 06-19-2001
I_Sleep();
}
}
mrand = M_RandomKey(sizeof(quitsounds) / sizeof(INT32));
if (quitsounds[mrand])
S_StartSound(NULL, quitsounds[mrand]);
I_Quit();
//added : 12-02-98: do that instead of I_WaitVbl which does not work
ptime = I_GetTime() + NEWTICRATE*2; // Shortened the quit time, used to be 2 seconds Tails 03-26-2001
while (ptime > I_GetTime())
{
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
V_DrawSmallScaledPatch(0, 0, 0, W_CachePatchName("GAMEQUIT", PU_CACHE)); // Demo 3 Quit Screen Tails 06-16-2001
I_FinishUpdate(); // Update the screen with the image Tails 06-19-2001
I_Sleep();
}
}
I_Quit();
}
}
void M_QuitSRB2(INT32 choice)
@ -1969,7 +2002,7 @@ void M_QuitSRB2(INT32 choice)
// We pick index 0 which is language sensitive, or one at random,
// between 1 and maximum number.
(void)choice;
M_StartMessage("Are you sure you want to quit playing?\n\n(Press 'Y' to exit)", M_QuitResponse, MM_YESNO);
M_StartMessage("Are you sure you want to quit playing?\n\n(Press A to exit)", M_QuitResponse, MM_YESNO);
}
// =========
@ -2577,7 +2610,7 @@ static void M_MPConfirmCharacterSelection(void)
static void M_MPConfirmCharacterResponse(INT32 ch)
{
if (ch == 'y' || ch == KEY_ENTER)
if (ch == MA_YES)
M_MPConfirmCharacterSelection();
M_ClearMenus(true);
@ -2665,7 +2698,7 @@ void M_CharacterSelectTick(void)
if (playeringame[j] && !players[consoleplayer].spectator)
{
// Warn the player!
M_StartMessage(M_GetText("Any player who has changed skin will\nautomatically spectate. Proceed?\n(Press 'Y' to confirm)\n"), M_MPConfirmCharacterResponse, MM_YESNO);
M_StartMessage(M_GetText("Any player who has changed skin will\nautomatically spectate. Proceed?\n(Press A to confirm)\n"), M_MPConfirmCharacterResponse, MM_YESNO);
return;
}
}
@ -3936,6 +3969,7 @@ static void SetDeviceOnPress(void)
}
}
// Prompt a device selection window (just tap any button on the device you want)
void M_ProfileDeviceSelect(INT32 choice)
{
@ -3949,7 +3983,6 @@ void M_ProfileDeviceSelect(INT32 choice)
optionsmenu.contx = optionsmenu.tcontx = controlleroffsets[gc_a][0];
optionsmenu.conty = optionsmenu.tconty = controlleroffsets[gc_a][1];
//M_StartMessage(M_GetText("Press any key on the device\nyou would like to use"), M_ProfileDeviceSelectResponse, MM_EVENTHANDLER);
M_SetupNextMenu(&OPTIONS_ProfileControlsDef, false); // Don't set device here anymore.
}
@ -4445,11 +4478,9 @@ void M_ConfirmEnterGame(INT32 choice)
static void M_ExitGameResponse(INT32 ch)
{
if (ch != 'y' && ch != KEY_ENTER)
return;
if (ch == MA_YES)
G_SetExitGameFlag();
//Command_ExitGame_f();
G_SetExitGameFlag();
M_ClearMenus(true);
}
@ -4462,7 +4493,7 @@ void M_EndGame(INT32 choice)
if (!Playing())
return;
M_StartMessage(M_GetText("Are you sure you want to return\nto the title screen?\n(Press 'Y' to confirm)\n"), M_ExitGameResponse, MM_YESNO);
M_StartMessage(M_GetText("Are you sure you want to return\nto the title screen?\n(Press A to confirm)\n"), M_ExitGameResponse, MM_YESNO);
}
@ -4951,7 +4982,7 @@ boolean M_AddonsRefresh(void)
if (message)
{
M_StartMessage(message,M_AddonsClearName,MM_EVENTHANDLER);
M_StartMessage(message,M_AddonsClearName,MM_YESNO);
return true;
}
@ -4964,7 +4995,7 @@ boolean M_AddonsRefresh(void)
static void M_AddonExec(INT32 ch)
{
if (ch != 'y' && ch != KEY_ENTER)
if (ch == MA_NO)
return;
S_StartSound(NULL, sfx_zoom);
@ -5117,11 +5148,11 @@ void M_HandleAddons(INT32 choice)
break;
case EXT_TXT:
M_StartMessage(va("%c%s\x80\nThis file may not be a console script.\nAttempt to run anyways? \n\n(Press 'Y' to confirm)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), dirmenu[dir_on[menudepthleft]]+DIR_STRING),M_AddonExec,MM_YESNO);
M_StartMessage(va("%c%s\x80\nThis file may not be a console script.\nAttempt to run anyways? \n\n(Press A to confirm)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), dirmenu[dir_on[menudepthleft]]+DIR_STRING),M_AddonExec,MM_YESNO);
break;
case EXT_CFG:
M_AddonExec(KEY_ENTER);
M_AddonExec(MA_YES);
break;
case EXT_LUA: