Remove a TON of special-case menu code

You can now combine submenu/cvar/call to allow for more complex behavior,
without the need for keyhandlers. All those dummy menuitems relying on
keyhandlers now use cvars, like everything else does.

Yes, several things are still hardcoded, but player setup no longer
needs its own miniature menu system :^)
This commit is contained in:
GenericHeroGuy 2025-06-08 16:59:56 +02:00
parent acb2cb752e
commit 56edab2480
6 changed files with 255 additions and 681 deletions

View file

@ -2108,19 +2108,28 @@ void CV_AddValue(consvar_t *var, INT32 increment)
if (!increment) if (!increment)
return; return;
if (var == &cv_forceskin) // Special handling. if (var == &cv_forceskin || var == &cv_chooseskin) // Special handling.
{ {
INT32 oldvalue = var->value; INT32 oldvalue = var->value;
INT32 min = var == &cv_forceskin ? -1 : 0;
newvalue = oldvalue; newvalue = oldvalue;
do do
{ {
newvalue += increment; newvalue += increment;
if (newvalue < -1) if (newvalue < min)
newvalue = (numskins - 1); newvalue = numskins-1;
else if (newvalue >= numskins) else if (newvalue >= numskins)
newvalue = -1; newvalue = min;
} while ((oldvalue != newvalue) } while (oldvalue != newvalue && !R_SkinUsable(-1, newvalue));
&& !(R_SkinUsable(-1, newvalue))); }
else if (var == &cv_playercolor[0] || var == &cv_playercolor[1] || var == &cv_playercolor[2] || var == &cv_playercolor[3] || var == &cv_dummycolor)
{
// Special case for the playercolor variable, used only directly from the menu
newvalue = var->value;
if (increment > 0) // Going up!
newvalue = M_GetColorAfter(newvalue);
else if (increment < 0) // Going down!
newvalue = M_GetColorBefore(newvalue);
} }
else else
newvalue = var->value + increment; newvalue = var->value + increment;
@ -2184,56 +2193,6 @@ void CV_AddValue(consvar_t *var, INT32 increment)
if (var->PossibleValue[max].value == var->value) if (var->PossibleValue[max].value == var->value)
currentindice = max; currentindice = max;
// The following options will NOT handle netsyncing.
if (var == &cv_chooseskin)
{
// Special case for the chooseskin variable, used only directly from the menu
newvalue = var->value - 1;
do
{
if (increment > 0) // Going up!
{
newvalue++;
if (newvalue == MAXSKINS)
newvalue = 0;
}
else if (increment < 0) // Going down!
{
newvalue--;
if (newvalue == -1)
newvalue = MAXSKINS-1;
}
} while (var->PossibleValue[newvalue].strvalue == NULL);
var->value = newvalue + 1;
var->string = var->PossibleValue[newvalue].strvalue;
var->func();
return;
}
else if (var == &cv_playercolor[0] || var == &cv_playercolor[1] || var == &cv_playercolor[2] || var == &cv_playercolor[3])
{
// Special case for the playercolor variable, used only directly from the menu
if (increment > 0) // Going up!
{
newvalue = var->value + 1;
if (newvalue > numskincolors-1)
newvalue = 1;
var->value = newvalue;
var->string = var->PossibleValue[var->value].strvalue;
var->func();
return;
}
else if (increment < 0) // Going down!
{
newvalue = var->value - 1;
if (newvalue < 1)
newvalue = numskincolors-1;
var->value = newvalue;
var->string = var->PossibleValue[var->value].strvalue;
var->func();
return;
}
}
if ((var == &cv_kartspeed || var == &cv_kartbattlespeed) && !M_SecretUnlocked(SECRET_HARDSPEED)) if ((var == &cv_kartspeed || var == &cv_kartbattlespeed) && !M_SecretUnlocked(SECRET_HARDSPEED))
{ {
max = (M_SecretUnlocked(SECRET_HARDSPEED) ? 4 : 3); max = (M_SecretUnlocked(SECRET_HARDSPEED) ? 4 : 3);

View file

@ -1853,6 +1853,12 @@ static struct { const char *name; consvar_t *var; } HIDDENVARS[] = {
{ "DUMMYATTACKINGCHAINING", &cv_dummyattackingchaining }, { "DUMMYATTACKINGCHAINING", &cv_dummyattackingchaining },
{ "DUMMYATTACKINGSLIPDASH", &cv_dummyattackingslipdash }, { "DUMMYATTACKINGSLIPDASH", &cv_dummyattackingslipdash },
{ "DUMMYATTACKINGPURPLEDRIFT", &cv_dummyattackingpurpledrift }, { "DUMMYATTACKINGPURPLEDRIFT", &cv_dummyattackingpurpledrift },
{ "DUMMYSTAFF", &cv_dummystaff },
{ "DUMMYMULTIPLAYER", &cv_dummymultiplayer },
{ "DUMMYIP", &cv_dummyip },
{ "DUMMYNAME", &cv_dummyname },
{ "DUMMYFOLLOWER", &cv_dummyfollower },
{ "DUMMYCOLOR", &cv_dummycolor },
{ NULL, NULL } { NULL, NULL }
}; };
@ -1866,8 +1872,6 @@ static void readmenuitem(MYFILE *f, menuitem_t *menuitem)
char *tmp; char *tmp;
UINT16 status = 0; UINT16 status = 0;
boolean actionset = false;
boolean textset = false;
// taking quite possibly the only opportunity i'll ever get // taking quite possibly the only opportunity i'll ever get
// to avoid three tabs of indentation... // to avoid three tabs of indentation...
@ -1932,12 +1936,11 @@ static void readmenuitem(MYFILE *f, menuitem_t *menuitem)
continue; continue;
} }
if (textset) if (status & IT_DISPLAY)
{ {
WARN0("text already set!"); WARN0("text already set!");
continue; continue;
} }
textset = true;
status |= flags; status |= flags;
menuitem->text = Z_StrDup(word2); menuitem->text = Z_StrDup(word2);
} }
@ -1956,11 +1959,6 @@ static void readmenuitem(MYFILE *f, menuitem_t *menuitem)
continue; continue;
} }
if (actionset)
{
WARN0("action already set!");
continue;
}
consvar_t *cvar = CV_FindVar(word2); consvar_t *cvar = CV_FindVar(word2);
if (!cvar) if (!cvar)
for (size_t i = 0; HIDDENVARS[i].name; i++) for (size_t i = 0; HIDDENVARS[i].name; i++)
@ -1974,78 +1972,50 @@ static void readmenuitem(MYFILE *f, menuitem_t *menuitem)
WARN("unable to find cvar '%s'", word2); WARN("unable to find cvar '%s'", word2);
continue; continue;
} }
actionset = true;
status |= flags; status |= flags;
menuitem->itemaction.cvar = cvar; menuitem->cvar = cvar;
} }
else if (fastcmp(word, "SUBMENU")) else if (fastcmp(word, "SUBMENU"))
{ {
if (actionset)
{
WARN0("action already set!");
continue;
}
menutype_t mn = get_menutype(word2); menutype_t mn = get_menutype(word2);
if (mn == MAXMENUTYPES) if (mn == MAXMENUTYPES)
{ {
WARN("unknown menu '%s'", word2); WARN("unknown menu '%s'", word2);
continue; continue;
} }
actionset = true;
status |= IT_SUBMENU; status |= IT_SUBMENU;
menuitem->itemaction.submenu = mn; menuitem->submenu = mn;
} }
else if (fastncmp(word, "CALL", 4) || fastcmp(word, "ARROWS")) else if (fastcmp(word, "CALL") || fastcmp(word, "ARROWS"))
{ {
UINT16 flags; UINT16 flags;
if (word[0] == 'C') if (word[0] == 'C')
{
flags = IT_CALL; flags = IT_CALL;
if (fastcmp(word+4, "NOTMODIFIED"))
flags |= IT_CALL_NOTMODIFIED;
else if (word[4])
{
WARN("unknown word '%s'", word);
continue;
}
}
else if (word[0] == 'A') else if (word[0] == 'A')
flags = IT_ARROWS; flags = IT_ARROWS;
else else
I_Error("bruh"); // i should probably just make "CALL" the stem for all of these I_Error("bruh"); // i should probably just make "CALL" the stem for all of these
if (actionset)
{
WARN0("action already set!");
continue;
}
menufunc_f *routine = get_menuroutine(word2); menufunc_f *routine = get_menuroutine(word2);
if (!routine) if (!routine)
{ {
WARN("unknown call routine '%s'", word2); WARN("unknown call routine '%s'", word2);
continue; continue;
} }
actionset = true;
status |= flags; status |= flags;
menuitem->itemaction.routine = routine; menuitem->routine = routine;
} }
else if (fastcmp(word, "DUMMY")) else if (fastcmp(word, "DUMMY"))
{ {
if (actionset)
{
WARN0("action already set!");
continue;
}
actionset = true;
status |= IT_DUMMY; status |= IT_DUMMY;
menuitem->itemaction.routine = NULL; menuitem->routine = NULL;
} }
else else
WARN("unknown word '%s'", word); WARN("unknown word '%s'", word);
} }
while (!myfeof(f)); // finish when the line is empty while (!myfeof(f)); // finish when the line is empty
if (textset || actionset) if (status)
menuitem->status = status; menuitem->status = status;
Z_Free(s); Z_Free(s);
} }

View file

@ -692,9 +692,8 @@ struct menu_routine_s const MENU_ROUTINES[] = {
{ "SETGUESTREPLAY", &MR_SetGuestReplay }, { "SETGUESTREPLAY", &MR_SetGuestReplay },
{ "REPLAYTIMEATTACK", &MR_ReplayTimeAttack }, { "REPLAYTIMEATTACK", &MR_ReplayTimeAttack },
{ "TIMEATTACKPRESET", &MR_TimeAttackPreset }, { "TIMEATTACKPRESET", &MR_TimeAttackPreset },
{ "HANDLESTAFFREPLAY", &MR_HandleStaffReplay }, { "REPLAYSTAFF", &MR_ReplayStaff },
{ "HANDLEMPMAINMENU", &MR_HandleMPMainMenu }, { "CONNECTIP", &MR_ConnectIP },
{ "HANDLESETUPMULTIPLAYER", &MR_HandleSetupMultiPlayer },
{ "QUITMULTIPLAYERMENU", &MR_QuitMultiPlayerMenu }, { "QUITMULTIPLAYERMENU", &MR_QuitMultiPlayerMenu },
{ "STARTSERVERMENU", &MR_StartServerMenu }, { "STARTSERVERMENU", &MR_StartServerMenu },
{ "STARTSERVER", &MR_StartServer }, { "STARTSERVER", &MR_StartServer },
@ -710,7 +709,7 @@ struct menu_routine_s const MENU_ROUTINES[] = {
{ "OPENGLOPTIONSMENU", &MR_OpenGLOptionsMenu }, { "OPENGLOPTIONSMENU", &MR_OpenGLOptionsMenu },
#endif #endif
{ "HANDLEVIDEOMODE", &MR_HandleVideoMode }, { "HANDLEVIDEOMODE", &MR_HandleVideoMode },
{ "HANDLESOUNDTEST", &MR_HandleSoundTest }, { "PLAYSOUND", &MR_PlaySound },
{ "MUSICTEST", &MR_MusicTest }, { "MUSICTEST", &MR_MusicTest },
{ "QUITMUSICTEST", &MR_QuitMusicTest }, { "QUITMUSICTEST", &MR_QuitMusicTest },
{ "SCREENSHOTOPTIONS", &MR_ScreenshotOptions }, { "SCREENSHOTOPTIONS", &MR_ScreenshotOptions },
@ -765,7 +764,6 @@ struct menu_drawer_s const MENU_DRAWERS[] = {
{ "DRAWTIMEATTACKMENU", &M_DrawTimeAttackMenu }, { "DRAWTIMEATTACKMENU", &M_DrawTimeAttackMenu },
{ "DRAWMPMAINMENU", &M_DrawMPMainMenu }, { "DRAWMPMAINMENU", &M_DrawMPMainMenu },
{ "DRAWSETUPMULTIPLAYERMENU", &M_DrawSetupMultiPlayerMenu }, { "DRAWSETUPMULTIPLAYERMENU", &M_DrawSetupMultiPlayerMenu },
{ "DRAWSERVERMENU", &M_DrawServerMenu },
{ "DRAWVIDEOMENU", &M_DrawVideoMenu }, { "DRAWVIDEOMENU", &M_DrawVideoMenu },
{ "DRAWVIDEOMODE", &M_DrawVideoMode }, { "DRAWVIDEOMODE", &M_DrawVideoMode },
{ "DRAWSKYROOM", &M_DrawSkyRoom }, { "DRAWSKYROOM", &M_DrawSkyRoom },

View file

@ -592,14 +592,14 @@ static void G_UpdateRecordReplays(void)
parts = M_PathParts(gpath); parts = M_PathParts(gpath);
M_MkdirEachUntil(gpath, parts - 4, parts - 1, 0755); M_MkdirEachUntil(gpath, parts - 4, parts - 1, 0755);
snprintf(lastdemo, 255, "%s-%s-%s-last.lmp", gpath, cv_chooseskin.string, gamemode); snprintf(lastdemo, 255, "%s-%s-%s-last.lmp", gpath, skins[cv_chooseskin.value].name, gamemode);
if (FIL_FileExists(lastdemo)) if (FIL_FileExists(lastdemo))
{ {
UINT8 *buf; UINT8 *buf;
size_t len = FIL_ReadFile(lastdemo, &buf); size_t len = FIL_ReadFile(lastdemo, &buf);
snprintf(bestdemo, 255, "%s-%s-%s-time-best.lmp", gpath, cv_chooseskin.string, gamemode); snprintf(bestdemo, 255, "%s-%s-%s-time-best.lmp", gpath, skins[cv_chooseskin.value].name, gamemode);
if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1) if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1)
{ // Better time, save this demo. { // Better time, save this demo.
if (FIL_FileExists(bestdemo)) if (FIL_FileExists(bestdemo))
@ -610,7 +610,7 @@ static void G_UpdateRecordReplays(void)
if (modeattacking == ATTACKING_TIME) if (modeattacking == ATTACKING_TIME)
{ {
snprintf(bestdemo, 255, "%s-%s-%s-lap-best.lmp", gpath, cv_chooseskin.string, gamemode); snprintf(bestdemo, 255, "%s-%s-%s-lap-best.lmp", gpath, skins[cv_chooseskin.value].name, gamemode);
if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & (1<<1)) if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & (1<<1))
{ // Better lap time, save this demo. { // Better lap time, save this demo.
if (FIL_FileExists(bestdemo)) if (FIL_FileExists(bestdemo))

File diff suppressed because it is too large Load diff

View file

@ -157,13 +157,12 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt);
// flags for items in the menu // flags for items in the menu
// menu handle (what we do when key is pressed // menu handle (what we do when key is pressed
#define IT_TYPE (1+2+4) #define IT_TYPE (1+2+4+8192+16384)
#define IT_CALL 1 // call the function #define IT_CALL 1 // call the function
#define IT_ARROWS 2 // call function with 0 for left arrow and 1 for right arrow in param #define IT_ARROWS 2 // call function with 0 for left arrow and 1 for right arrow in param
#define IT_DUMMY 4 // selectable, but does nothing #define IT_DUMMY 4 // selectable, but does nothing
#define IT_SUBMENU (1+2) // go to sub menu #define IT_SUBMENU 8192 // go to sub menu
#define IT_CVAR (1+4) // handle as a cvar #define IT_CVAR 16384 // handle as a cvar
#define IT_PAIR (2+4) // no handling, define both sides of text
// display flags // display flags
#define IT_DISPLAY (8+16+32) #define IT_DISPLAY (8+16+32)
@ -180,11 +179,6 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt);
#define IT_CV_STRING 128 #define IT_CV_STRING 128
#define IT_CV_INTEGERSTEP 256 // if cvar is CV_FLOAT, modify it by 1 instead of 0.0625 #define IT_CV_INTEGERSTEP 256 // if cvar is CV_FLOAT, modify it by 1 instead of 0.0625
//call/submenu specific
// There used to be a lot more here but ...
// A lot of them became redundant with the advent of the Pause menu, so they were removed
#define IT_CALL_NOTMODIFIED 64
// extra flags // extra flags
#define IT_CENTER 512 // if IT_PATCH, center it on screen #define IT_CENTER 512 // if IT_PATCH, center it on screen
#define IT_HIDDEN 1024 // invisible, unselectable #define IT_HIDDEN 1024 // invisible, unselectable
@ -201,13 +195,6 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt);
typedef INT32 (menufunc_f)(INT32); typedef INT32 (menufunc_f)(INT32);
typedef void (menudrawer_f)(void); typedef void (menudrawer_f)(void);
typedef union
{
menutype_t submenu; // IT_SUBMENU
consvar_t *cvar; // IT_CVAR
menufunc_f *routine; // IT_CALL, IT_ARROWS
} itemaction_t;
// //
// MENU TYPEDEFS // MENU TYPEDEFS
// //
@ -218,12 +205,14 @@ struct menuitem_t
// show IT_xxx // show IT_xxx
UINT16 status; UINT16 status;
menutype_t submenu; // IT_SUBMENU
consvar_t *cvar; // IT_CVAR
menufunc_f *routine; // IT_CALL, IT_ARROWS
const char *patch; const char *patch;
const char *text; // used when FONTBxx lump is found const char *text; // used when FONTBxx lump is found
const char *tooltip; const char *tooltip;
itemaction_t itemaction;
INT32 argument; INT32 argument;
INT16 x, y; INT16 x, y;
}; };
@ -269,9 +258,8 @@ INT32 MR_ChooseTimeAttack(INT32 choice);
INT32 MR_SetGuestReplay(INT32 arg); INT32 MR_SetGuestReplay(INT32 arg);
INT32 MR_TimeAttackPreset(INT32 arg); INT32 MR_TimeAttackPreset(INT32 arg);
INT32 MR_ReplayTimeAttack(INT32 arg); INT32 MR_ReplayTimeAttack(INT32 arg);
INT32 MR_HandleStaffReplay(INT32 choice); INT32 MR_ReplayStaff(INT32 choice);
INT32 MR_HandleMPMainMenu(INT32 choice); INT32 MR_ConnectIP(INT32 choice);
INT32 MR_HandleSetupMultiPlayer(INT32 choice);
INT32 MR_QuitMultiPlayerMenu(INT32 choice); INT32 MR_QuitMultiPlayerMenu(INT32 choice);
INT32 MR_StartServerMenu(INT32 choice); INT32 MR_StartServerMenu(INT32 choice);
INT32 MR_StartServer(INT32 choice); INT32 MR_StartServer(INT32 choice);
@ -287,7 +275,7 @@ INT32 MR_VideoModeMenu(INT32 choice);
INT32 MR_OpenGLOptionsMenu(INT32 choice); INT32 MR_OpenGLOptionsMenu(INT32 choice);
#endif #endif
INT32 MR_HandleVideoMode(INT32 ch); INT32 MR_HandleVideoMode(INT32 ch);
INT32 MR_HandleSoundTest(INT32 choice); INT32 MR_PlaySound(INT32 choice);
INT32 MR_MusicTest(INT32 choice); INT32 MR_MusicTest(INT32 choice);
INT32 MR_QuitMusicTest(INT32 choice); INT32 MR_QuitMusicTest(INT32 choice);
INT32 MR_ScreenshotOptions(INT32 choice); INT32 MR_ScreenshotOptions(INT32 choice);
@ -337,7 +325,6 @@ void M_DrawReplayHut(void);
void M_DrawTimeAttackMenu(void); void M_DrawTimeAttackMenu(void);
void M_DrawMPMainMenu(void); void M_DrawMPMainMenu(void);
void M_DrawSetupMultiPlayerMenu(void); void M_DrawSetupMultiPlayerMenu(void);
void M_DrawServerMenu(void);
void M_DrawConnectMenu(void); void M_DrawConnectMenu(void);
void M_DrawVideoMenu(void); void M_DrawVideoMenu(void);
void M_DrawVideoMode(void); void M_DrawVideoMode(void);
@ -415,6 +402,8 @@ extern consvar_t cv_dummygpdifficulty, cv_dummygpencore, cv_dummygpcup;
extern consvar_t cv_dummymenuplayer, cv_dummyteam, cv_dummyspectate, cv_dummyscramble; extern consvar_t cv_dummymenuplayer, cv_dummyteam, cv_dummyspectate, cv_dummyscramble;
extern consvar_t cv_dummyattackingrings, cv_dummyattackingstacking, cv_dummyattackingchaining; extern consvar_t cv_dummyattackingrings, cv_dummyattackingstacking, cv_dummyattackingchaining;
extern consvar_t cv_dummyattackingslipdash, cv_dummyattackingpurpledrift; extern consvar_t cv_dummyattackingslipdash, cv_dummyattackingpurpledrift;
extern consvar_t cv_dummystaff;
extern consvar_t cv_dummymultiplayer, cv_dummyip, cv_dummyname, cv_dummyfollower, cv_dummycolor;
extern consvar_t cv_menucaps; extern consvar_t cv_menucaps;
// allow menu text to be displayed in lowercase // allow menu text to be displayed in lowercase