From 758647fab294af5ec08107767e3a32d63b69799b Mon Sep 17 00:00:00 2001 From: mazmazz Date: Sat, 17 Nov 2018 16:32:30 -0500 Subject: [PATCH] More title screen features; menu meta state implementation (active and previous menu types) * Draw background over titlemap * HIDEBACKGROUND to show titlemap * Specify NOMUSIC or IGNOREMUSIC (to not change music) * Linedef execs implemented for tree hierarchy, including bubbling (NOENTERBUBBLE/NOEXITBUBBLE) * Specify ENTERWIPE and EXITWIPE * Menuid hierarchy fixes (Control mapping, joystick setup) * Time attack handling fixes * Specify custom wipes on runtime for D_Display and P_SetupLevel (for titlemap) * Allow for forcing and skipping a wipe * Wipe utility functions F_GetWipeLength and F_WipeExists --- src/d_main.c | 36 +++++- src/dehacked.c | 41 +++++- src/doomstat.h | 2 + src/f_finale.c | 9 +- src/f_finale.h | 5 + src/f_wipe.c | 45 +++++++ src/m_menu.c | 345 ++++++++++++++++++++++++++++++++++++++++++------- src/m_menu.h | 8 +- src/p_setup.c | 8 +- 9 files changed, 438 insertions(+), 61 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index fa89e549e..ef2d481bf 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -233,6 +233,9 @@ void D_ProcessEvents(void) // wipegamestate can be set to -1 to force a wipe on the next draw // added comment : there is a wipe eatch change of the gamestate gamestate_t wipegamestate = GS_LEVEL; +// -1: Default; 0-n: Wipe index; INT16_MAX: do not wipe +INT16 wipetypepre = -1; +INT16 wipetypepost = -1; static void D_Display(void) { @@ -266,7 +269,7 @@ static void D_Display(void) // save the current screen if about to wipe wipe = (gamestate != wipegamestate); - if (wipe) + if (wipe && wipetypepre != INT16_MAX) { // set for all later wipedefindex = gamestate; // wipe_xxx_toblack @@ -278,21 +281,29 @@ static void D_Display(void) wipedefindex = wipe_multinter_toblack; } + if (wipetypepre < 0 || !F_WipeExists(wipetypepre)) + wipetypepre = wipedefs[wipedefindex]; + if (rendermode != render_none) { // Fade to black first - if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction)) // fades to black on its own timing, always - && wipedefs[wipedefindex] != UINT8_MAX) + if ((wipegamestate == FORCEWIPE || + !(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))) // fades to black on its own timing, always + && wipetypepre != UINT8_MAX) { F_WipeStartScreen(); V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); F_WipeEndScreen(); - F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK); + F_RunWipe(wipetypepre, gamestate != GS_TIMEATTACK && gamestate != GS_TITLESCREEN); } F_WipeStartScreen(); } + + wipetypepre = -1; } + else + wipetypepre = -1; // do buffered drawing switch (gamestate) @@ -359,6 +370,10 @@ static void D_Display(void) break; } + // Run menu state updates and linedef execs in titlemap + if (wipe && (gamestate == GS_TITLESCREEN || gamestate == GS_TIMEATTACK)) + M_ApplyMenuMetaState(); + // clean up border stuff // see if the border needs to be initially drawn if (gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction)) @@ -465,18 +480,25 @@ static void D_Display(void) // // wipe update // - if (wipe) + if (wipe && wipetypepost != INT16_MAX) { // note: moved up here because NetUpdate does input changes // and input during wipe tends to mess things up wipedefindex += WIPEFINALSHIFT; + if (wipetypepost < 0 || !F_WipeExists(wipetypepost)) + wipetypepost = wipedefs[wipedefindex]; + if (rendermode != render_none) { F_WipeEndScreen(); - F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK); + F_RunWipe(wipetypepost, gamestate != GS_TIMEATTACK && gamestate != GS_TITLESCREEN); } + + wipetypepost = -1; } + else + wipetypepost = -1; NetUpdate(); // send out any new accumulation @@ -729,6 +751,7 @@ void D_StartTitle(void) gametype = GT_COOP; paused = false; advancedemo = false; + MN_Start(); F_StartTitleScreen(); CON_ToggleOff(); @@ -1378,6 +1401,7 @@ void D_SRB2Main(void) { CON_ToggleOff(); CON_ClearHUD(); + MN_Start(); F_StartTitleScreen(); } else diff --git a/src/dehacked.c b/src/dehacked.c index c3db3f57e..7d37494dc 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -1954,13 +1954,19 @@ static void readmenu(MYFILE *f, INT32 num) value = atoi(word2); // used for numerical settings - CONS_Printf("Menu %d> %s | %s\n", num, word, word2); - if (fastcmp(word, "BACKGROUNDNAME")) { strncpy(menumeta[num].bgname, word2, 8); titlechanged = true; } + else if (fastcmp(word, "HIDEBACKGROUND")) + { + // HACK: Use CHAR_MAX to signal that we want to hide the background + // Only effective during titlemap + menumeta[num].bgname[0] = CHAR_MAX; + menumeta[num].bgname[1] = 0; + titlechanged = true; + } else if (fastcmp(word, "HIDETITLEPICS") || fastcmp(word, "HIDEPICS")) { // true by default, except MM_MAIN @@ -2006,7 +2012,17 @@ static void readmenu(MYFILE *f, INT32 num) else if (fastcmp(word, "MUSICLOOP")) { // true by default except MM_MAIN - menumeta[num].muslooping = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y'); + menumeta[num].muslooping = (value || word2[0] == 'T' || word2[0] == 'Y'); + titlechanged = true; + } + else if (fastcmp(word, "NOMUSIC")) + { + menumeta[num].musstop = (value || word2[0] == 'T' || word2[0] == 'Y'); + titlechanged = true; + } + else if (fastcmp(word, "IGNOREMUSIC")) + { + menumeta[num].musignore = (value || word2[0] == 'T' || word2[0] == 'Y'); titlechanged = true; } else if (fastcmp(word, "FADESTRENGTH")) @@ -2015,9 +2031,14 @@ static void readmenu(MYFILE *f, INT32 num) menumeta[num].fadestrength = value-1; titlechanged = true; } - else if (fastcmp(word, "EXITPARENTS")) + else if (fastcmp(word, "NOENTERBUBBLE")) { - menumeta[num].exitparents = (boolean)(value || word2[0] == 'T' || word2[0] == 'Y'); + menumeta[num].enterbubble = !(value || word2[0] == 'T' || word2[0] == 'Y'); + titlechanged = true; + } + else if (fastcmp(word, "NOEXITBUBBLE")) + { + menumeta[num].exitbubble = !(value || word2[0] == 'T' || word2[0] == 'Y'); titlechanged = true; } else if (fastcmp(word, "ENTERTAG")) @@ -2030,6 +2051,16 @@ static void readmenu(MYFILE *f, INT32 num) menumeta[num].exittag = value; titlechanged = true; } + else if (fastcmp(word, "ENTERWIPE")) + { + menumeta[num].enterwipe = value; + titlechanged = true; + } + else if (fastcmp(word, "EXITWIPE")) + { + menumeta[num].exitwipe = value; + titlechanged = true; + } } } while (!myfeof(f)); // finish when the line is empty diff --git a/src/doomstat.h b/src/doomstat.h index 337eff7a9..1ff261d64 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -546,6 +546,8 @@ extern boolean precache; // wipegamestate can be set to -1 // to force a wipe on the next draw extern gamestate_t wipegamestate; +extern INT16 wipetypepre; +extern INT16 wipetypepost; // debug flag to cancel adaptiveness extern boolean singletics; diff --git a/src/f_finale.c b/src/f_finale.c index c6963cdfa..4a1267265 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1353,15 +1353,16 @@ void F_GameEndTicker(void) // ============== void F_StartTitleScreen(void) { - MN_Start(); - if (menumeta[MN_MAIN].musname[0]) S_ChangeMusic(menumeta[MN_MAIN].musname, menumeta[MN_MAIN].mustrack, menumeta[MN_MAIN].muslooping); else S_ChangeMusicInternal("_title", looptitle); if (gamestate != GS_TITLESCREEN && gamestate != GS_WAITINGPLAYERS) + { finalecount = 0; + wipetypepost = menumeta[MN_MAIN].enterwipe; + } else wipegamestate = GS_TITLESCREEN; @@ -1411,6 +1412,10 @@ void F_StartTitleScreen(void) camera.chase = true; camera.height = 0; + // Run enter linedef exec for MN_MAIN, since this is where we start + if (menumeta[MN_MAIN].entertag) + P_LinedefExecute(menumeta[MN_MAIN].entertag, players[displayplayer].mo, NULL); + wipegamestate = prevwipegamestate; } else diff --git a/src/f_finale.h b/src/f_finale.h index d2cc4da07..3a9ad2091 100644 --- a/src/f_finale.h +++ b/src/f_finale.h @@ -84,12 +84,17 @@ extern UINT8 titlemapinaction; // // WIPE // +// HACK for menu fading while titlemapinaction; skips the level check +#define FORCEWIPE -2 + extern boolean WipeInAction; extern INT32 lastwipetic; void F_WipeStartScreen(void); void F_WipeEndScreen(void); void F_RunWipe(UINT8 wipetype, boolean drawMenu); +tic_t F_GetWipeLength(UINT8 wipetype); +boolean F_WipeExists(UINT8 wipetype); enum { diff --git a/src/f_wipe.c b/src/f_wipe.c index 49ab9cc01..3e0d474c6 100644 --- a/src/f_wipe.c +++ b/src/f_wipe.c @@ -378,3 +378,48 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu) WipeInAction = false; #endif } + +/** Returns tic length of wipe + * One lump equals one tic + */ +tic_t F_GetWipeLength(UINT8 wipetype) +{ +#ifdef NOWIPE + return 0; +#else + static char lumpname[10] = "FADEmmss"; + lumpnum_t lumpnum; + UINT8 wipeframe; + + if (wipetype > 99) + return 0; + + for (wipeframe = 0; wipeframe < 100; wipeframe++) + { + sprintf(&lumpname[4], "%.2hu%.2hu", (UINT16)wipetype, (UINT16)wipeframe); + + lumpnum = W_CheckNumForName(lumpname); + if (lumpnum == LUMPERROR) + return --wipeframe; + } + return --wipeframe; +#endif +} + +boolean F_WipeExists(UINT8 wipetype) +{ +#ifdef NOWIPE + return false; +#else + static char lumpname[10] = "FADEmm00"; + lumpnum_t lumpnum; + + if (wipetype > 99) + return false; + + sprintf(&lumpname[4], "%.2hu00", (UINT16)wipetype); + + lumpnum = W_CheckNumForName(lumpname); + return !(lumpnum == LUMPERROR); +#endif +} diff --git a/src/m_menu.c b/src/m_menu.c index 8899e2a73..fcab1043b 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -1809,9 +1809,9 @@ menu_t MP_RoomDef = menu_t MP_PlayerSetupDef = { #ifdef NONET - MN_MP_MAIN + (MN_MP_SPLITSCREEN << 6) + (MN_MP_PLAYERSETUP << 12), -#else MN_MP_MAIN + (MN_MP_PLAYERSETUP << 6), +#else + MN_MP_MAIN + (MN_MP_SPLITSCREEN << 6) + (MN_MP_PLAYERSETUP << 12), #endif "M_SPLAYR", sizeof (MP_PlayerSetupMenu)/sizeof (menuitem_t), @@ -1850,7 +1850,7 @@ menu_t OP_Joystick2Def = DEFAULTMENUSTYLE( "M_CONTRO", OP_Joystick2Menu, &OP_P2ControlsDef, 50, 30); menu_t OP_JoystickSetDef = { - MN_OP_MAIN + (MN_OP_JOYSTICKSET << 12), // second level (<<6) set on runtime + MN_OP_MAIN + (MN_OP_JOYSTICKSET << MENUBITS*3), // second (<<6) and third level (<<12) set on runtime "M_CONTRO", sizeof (OP_JoystickSetMenu)/sizeof (menuitem_t), &OP_Joystick1Def, @@ -2196,6 +2196,10 @@ static tic_t xscrolltimer; static tic_t yscrolltimer; static INT32 menuanimtimer; +// menu IDs are equal to current/prevMenu in most cases, except MM_SPECIAL when we don't want to operate on Message, Pause, etc. +static UINT32 prevMenuId = 0; +static UINT32 activeMenuId = 0; + typedef struct { char musname[7]; @@ -2203,6 +2207,38 @@ typedef struct boolean muslooping; } menumetamusic_t; +void MN_InitInfoTables(void) +{ + INT32 i; + + // Called in d_main before SOC can get to the tables + // Set menumeta defaults + for (i = 0; i < NUMMENUTYPES; i++) + { + if (i != MN_MAIN) + { + menumeta[i].muslooping = true; + } + if (i == MN_SP_TIMEATTACK || i == MN_SP_NIGHTSATTACK) + strncpy(menumeta[i].musname, "_inter", 7); + if (i == MN_SP_PLAYER) + strncpy(menumeta[i].musname, "_chsel", 7); + + // so-called "undefined" + menumeta[i].fadestrength = -1; + menumeta[i].hidetitlepics = -1; // inherits global hidetitlepics + menumeta[i].enterwipe = -1; + menumeta[i].exitwipe = -1; + // default true + menumeta[i].enterbubble = true; + menumeta[i].exitbubble = true; + } +} + +// ==================================== +// TREE ITERATION +// ==================================== + // UINT32 menutype - current menutype_t // INT32 level - current level up the tree, higher means younger // INT32 *retval - Return value @@ -2210,7 +2246,7 @@ typedef struct // // return true - stop iterating // return false - continue -typedef boolean (*menutree_iterator)(UINT32, INT32, INT32 *, void **); +typedef boolean (*menutree_iterator)(UINT32, INT32, INT32 *, void **, boolean fromoldest); static INT32 M_IterateMenuTree(menutree_iterator itfunc, void *input) { @@ -2220,8 +2256,8 @@ static INT32 M_IterateMenuTree(menutree_iterator itfunc, void *input) for (i = NUMMENULEVELS; i >= 0; i--) { bitmask = ((1 << MENUBITS) - 1) << (MENUBITS*i); - menutype = (currentMenu->menuid & bitmask) >> (MENUBITS*i); - if (itfunc(menutype, i, &retval, &input)) + menutype = (activeMenuId & bitmask) >> (MENUBITS*i); + if (itfunc(menutype, i, &retval, &input, false)) break; } @@ -2236,8 +2272,8 @@ static INT32 M_IterateMenuTreeFromTop(menutree_iterator itfunc, void *input) for (i = 0; i <= NUMMENULEVELS; i++) { bitmask = ((1 << MENUBITS) - 1) << (MENUBITS*i); - menutype = (currentMenu->menuid & bitmask) >> (MENUBITS*i); - if (itfunc(menutype, i, &retval, &input)) + menutype = (activeMenuId & bitmask) >> (MENUBITS*i); + if (itfunc(menutype, i, &retval, &input, true)) break; } @@ -2248,17 +2284,32 @@ static INT32 M_IterateMenuTreeFromTop(menutree_iterator itfunc, void *input) // ITERATORS // ==================================== -static boolean MIT_GetEdgeMenu(UINT32 menutype, INT32 level, INT32 *retval, void **input) +static boolean MIT_GetMenuAtLevel(UINT32 menutype, INT32 level, INT32 *retval, void **input, boolean fromoldest) { + INT32 *inputptr = (INT32*)*input; + INT32 targetlevel = *inputptr; if (menutype) { - *retval = menutype; - return true; + // \todo offset targetlevel by failed initial attempts + if (level == targetlevel || targetlevel < 0) + { + *retval = menutype; + return true; + } + } + else if (targetlevel >= 0) + { + // offset targetlevel by failed attempts; this should only happen in beginning of iteration + if (fromoldest) + (*inputptr)++; + else + (*inputptr)--; // iterating backwards, so count from highest } return false; } -static boolean MIT_GetEdgeLevel(UINT32 menutype, INT32 level, INT32 *retval, void **input) +#if 0 +static boolean MIT_GetEdgeLevel(UINT32 menutype, INT32 level, INT32 *retval, void **input, boolean fromoldest) { if (menutype) { @@ -2267,22 +2318,36 @@ static boolean MIT_GetEdgeLevel(UINT32 menutype, INT32 level, INT32 *retval, voi } return false; } +#endif -static boolean MIT_DrawScrollingBackground(UINT32 menutype, INT32 level, INT32 *retval, void **input) +static boolean MIT_HasMenuType(UINT32 menutype, INT32 level, INT32 *retval, void **input, boolean fromoldest) +{ + menutype_t inputtype = *(menutype_t*)*input; + if (menutype == inputtype) + { + *retval = true; + return true; + } + return false; +} + +static boolean MIT_DrawScrollingBackground(UINT32 menutype, INT32 level, INT32 *retval, void **input, boolean fromoldest) { char *defaultname = (char*)*input; - if (menumeta[menutype].bgname[0]) + if (menumeta[menutype].bgname[0] && menumeta[menutype].bgname[0] != CHAR_MAX) { M_SkyScroll(menumeta[menutype].titlescrollxspeed, menumeta[menutype].titlescrollyspeed, menumeta[menutype].bgname); return true; } - else if (!level && defaultname && defaultname[0]) + else if (menumeta[menutype].bgname[0] == CHAR_MAX && titlemapinaction) // hide the background + return true; + else if (!level && defaultname && defaultname[0] && !titlemapinaction) // hide the background by default in titlemap M_SkyScroll(titlescrollxspeed, titlescrollyspeed, defaultname); return false; } -static boolean MIT_ChangeMusic(UINT32 menutype, INT32 level, INT32 *retval, void **input) +static boolean MIT_ChangeMusic(UINT32 menutype, INT32 level, INT32 *retval, void **input, boolean fromoldest) { menumetamusic_t *defaultmusic = (menumetamusic_t*)*input; @@ -2291,12 +2356,19 @@ static boolean MIT_ChangeMusic(UINT32 menutype, INT32 level, INT32 *retval, void S_ChangeMusic(menumeta[menutype].musname, menumeta[menutype].mustrack, menumeta[menutype].muslooping); return true; } + else if (menumeta[menutype].musstop) + { + S_StopMusic(); + return true; + } + else if (menumeta[menutype].musignore) + return true; else if (!level && defaultmusic && defaultmusic->musname[0]) S_ChangeMusic(defaultmusic->musname, defaultmusic->mustrack, defaultmusic->muslooping); return false; } -static boolean MIT_FadeScreen(UINT32 menutype, INT32 level, INT32 *retval, void **input) +static boolean MIT_FadeScreen(UINT32 menutype, INT32 level, INT32 *retval, void **input, boolean fromoldest) { UINT8 defaultvalue = *(UINT8*)*input; if (menumeta[menutype].fadestrength >= 0) @@ -2310,7 +2382,7 @@ static boolean MIT_FadeScreen(UINT32 menutype, INT32 level, INT32 *retval, void return false; } -static boolean MIT_GetHideTitlePics(UINT32 menutype, INT32 level, INT32 *retval, void **input) +static boolean MIT_GetHideTitlePics(UINT32 menutype, INT32 level, INT32 *retval, void **input, boolean fromoldest) { (void)input; if (menumeta[menutype].hidetitlepics >= 0) @@ -2327,20 +2399,44 @@ static boolean MIT_GetHideTitlePics(UINT32 menutype, INT32 level, INT32 *retval, // TREE RETRIEVAL // ==================================== +#if 0 +// level is nth level relative to top or bottom from tree +static menutype_t M_GetMenuAtLevel(INT32 level, boolean fromoldest) +{ + if (fromoldest) + return M_IterateMenuTreeFromTop(MIT_GetMenuAtLevel, &level); + else + { + if (level >= 0) + level = NUMMENULEVELS - level; // iterating backwards, so count from highest value + return M_IterateMenuTree(MIT_GetMenuAtLevel, &level); + } +} +#endif + static UINT8 M_GetYoungestChildMenu() // aka the active menu { - return M_IterateMenuTree(MIT_GetEdgeMenu, NULL); + INT32 targetlevel = -1; + return M_IterateMenuTree(MIT_GetMenuAtLevel, &targetlevel); } +#if 0 static UINT8 M_GetOldestParentMenu() { - return M_IterateMenuTreeFromTop(MIT_GetEdgeMenu, NULL); + INT32 targetlevel = -1; + return M_IterateMenuTreeFromTop(MIT_GetMenuAtLevel, &targetlevel); } static UINT8 M_GetYoungestChildLevel() // aka the active menu { return M_IterateMenuTree(MIT_GetEdgeLevel, NULL); } +#endif + +static boolean M_HasMenuType(menutype_t needletype) +{ + return M_IterateMenuTreeFromTop(MIT_HasMenuType, &needletype); +} // ==================================== // EFFECTS @@ -2377,6 +2473,162 @@ boolean M_GetHideTitlePics(void) return (retval >= 0 ? retval : hidetitlepics); } +// ==================================== +// MENU STATE +// ==================================== + +static INT32 exitlevel, enterlevel, anceslevel; +static INT16 exittype, entertype; +static INT16 exitwipe, enterwipe; +static boolean exitbubble, enterbubble; +static INT16 exittag, entertag; + +static void M_HandleMenuMetaState(menu_t *newMenu) +{ + INT32 i; + UINT32 bitmask; + SINT8 prevtype, activetype, menutype; + + if (M_HasMenuType(MN_SPECIAL)) + return; + + if (currentMenu && newMenu && currentMenu->menuid == newMenu->menuid) // same menu? + return; + + exittype = entertype = exitlevel = enterlevel = anceslevel = exitwipe = enterwipe = -1; + exitbubble = enterbubble = true; + + prevMenuId = currentMenu ? currentMenu->menuid : 0; + activeMenuId = newMenu ? newMenu->menuid : 0; + + // don't do the below during the in-game menus + if (gamestate != GS_TITLESCREEN && gamestate != GS_TIMEATTACK) + return; + + // Loop through both menu IDs in parallel and look for type changes + // The youngest child in activeMenuId is the entered menu + // The youngest child in prevMenuId is the exited menu + + // 0. Get the type and level of each menu, and level of common ancestor + // 1. Get the wipes for both, then run the exit wipe + // 2. Change music (so that execs can change it again later) + // 3. Run each exit exec on the prevMenuId up to the common ancestor (UNLESS NoBubbleExecs) + // 4. Run each entrance exec on the activeMenuId down from the common ancestor (UNLESS NoBubbleExecs) + // 5. Run the entrance wipe + + // Get the parameters for each menu + for (i = NUMMENULEVELS; i >= 0; i--) + { + bitmask = ((1 << MENUBITS) - 1) << (MENUBITS*i); + prevtype = (prevMenuId & bitmask) >> (MENUBITS*i); + activetype = (activeMenuId & bitmask) >> (MENUBITS*i); + + if (prevtype && (exittype < 0)) + { + exittype = prevtype; + exitlevel = i; + exitwipe = menumeta[exittype].exitwipe; + exitbubble = menumeta[exittype].exitbubble; + exittag = menumeta[exittype].exittag; + } + + if (activetype && (entertype < 0)) + { + entertype = activetype; + enterlevel = i; + enterwipe = menumeta[entertype].enterwipe; + enterbubble = menumeta[entertype].enterbubble; + entertag = menumeta[entertype].entertag; + } + + if (prevtype && activetype && prevtype == activetype && anceslevel < 0) + { + anceslevel = i; + break; + } + } + + // Change the music + M_ChangeMusic("_title", false); + + // Run the linedef execs + if (titlemapinaction) + { + // Run the exit tags + if (enterlevel <= exitlevel) // equals is an edge case + { + if (exitbubble) + { + for (i = exitlevel; i > anceslevel; i--) // don't run the common ancestor's exit tag + { + bitmask = ((1 << MENUBITS) - 1) << (MENUBITS*i); + menutype = (prevMenuId & bitmask) >> (MENUBITS*i); + if (menumeta[menutype].exittag) + P_LinedefExecute(menumeta[menutype].exittag, players[displayplayer].mo, NULL); + } + } + else if (exittag) + P_LinedefExecute(exittag, players[displayplayer].mo, NULL); + } + + // Run the enter tags + if (enterlevel >= exitlevel) // equals is an edge case + { + if (enterbubble) + { + for (i = anceslevel+1; i <= enterlevel; i++) // don't run the common ancestor's enter tag + { + bitmask = ((1 << MENUBITS) - 1) << (MENUBITS*i); + menutype = (activeMenuId & bitmask) >> (MENUBITS*i); + if (menumeta[menutype].entertag) + P_LinedefExecute(menumeta[menutype].entertag, players[displayplayer].mo, NULL); + } + } + else if (entertag) + P_LinedefExecute(entertag, players[displayplayer].mo, NULL); + } + } + + + // Set the wipes for next frame + if ( + (exitwipe >= 0 && enterlevel <= exitlevel) || + (enterwipe >= 0 && enterlevel >= exitlevel) + ) + { + if (gamestate == GS_TIMEATTACK) + wipetypepre = (exitwipe && enterlevel <= exitlevel) ? exitwipe : -1; // force default + else + // HACK: INT16_MAX signals to not wipe + // because 0 is a valid index and -1 means default + wipetypepre = (exitwipe && enterlevel <= exitlevel) ? exitwipe : INT16_MAX; + wipetypepost = (enterwipe && enterlevel >= exitlevel) ? enterwipe : INT16_MAX; + wipegamestate = FORCEWIPE; + // D_Display runs the next step of processing + } + else + M_ApplyMenuMetaState(); // run the next step now +} + +void M_ApplyMenuMetaState(void) +{ +#if 0 + INT32 i; + UINT32 bitmask, menutype; + + if (gamestate != GS_TITLESCREEN && gamestate != GS_TIMEATTACK) + return; + + // 3. Run each exit exec on the prevMenuId up to the common ancestor (UNLESS NoBubbleExecs) + // 4. Run each entrance exec on the activeMenuId down from the common ancestor (UNLESS NoBubbleExecs) + + // \todo placeholder -- do we want any logic to happen between wipes? + // do we want to split linedef execs between pre-wipe and tween-wipe? + + // D_Display runs the enter wipe, if applicable +#endif +} + // ========================================================================= // BASIC MENU HANDLING // ========================================================================= @@ -2402,6 +2654,7 @@ static void M_GoBack(INT32 choice) Z_Free(levelselect.rows); levelselect.rows = NULL; menuactive = false; + wipetypepre = menumeta[M_GetYoungestChildMenu(currentMenu->menuid)].exitwipe; D_StartTitle(); } else @@ -3062,6 +3315,9 @@ void M_SetupNextMenu(menu_t *menudef) if (currentMenu != menudef && !currentMenu->quitroutine()) return; // we can't quit this menu (also used to set parameter from the menu) } + + M_HandleMenuMetaState(menudef); + currentMenu = menudef; itemOn = currentMenu->lastOn; @@ -3170,27 +3426,11 @@ void M_Init(void) // COMMON MENU DRAW ROUTINES // ========================================================================== -void MN_InitInfoTables(void) -{ - INT32 i; - - // Called in d_main before SOC can get to the tables - // Set menumeta defaults - for (i = 0; i < NUMMENUTYPES; i++) - { - if (i != MN_MAIN) - { - menumeta[i].muslooping = true; - } - // so-called "undefined" - menumeta[i].fadestrength = -1; - menumeta[i].hidetitlepics = -1; // inherits global hidetitlepics - } -} - void MN_Start(void) { menuanimtimer = 0; + prevMenuId = 0; + activeMenuId = MainDef.menuid; } void MN_Ticker(boolean run) @@ -5006,7 +5246,10 @@ static void M_DrawMessageMenu(void) // hack: draw RA background in RA menus if (gamestate == GS_TIMEATTACK) + { M_DrawScrollingBackground("SRB2BACK"); + M_DrawFadeScreen(0); + } M_DrawTextBox(currentMenu->x, y - 8, (max+7)>>3, mlines); @@ -7397,6 +7640,7 @@ static void M_DrawSetupChoosePlayerMenu(void) // Black BG V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); //M_DrawScrollingBackground("SRB2BACK"); + //M_DrawFadeScreen(0); // Character select profile images!1 M_DrawTextBox(0, my, 16, 20); @@ -7783,7 +8027,6 @@ void M_DrawTimeAttackMenu(void) INT32 i, x, y, cursory = 0; UINT16 dispstatus; patch_t *PictureOfUrFace; - menutype_t menutype = M_GetYoungestChildMenu(); M_ChangeMusic("_inter", true); // Eww, but needed for when user hits escape during demo playback @@ -7958,15 +8201,13 @@ static void M_TimeAttack(INT32 choice) M_PatchSkinNameTable(); + G_SetGamestate(GS_TIMEATTACK); // do this before M_SetupNextMenu so that menu meta state knows that we're switching M_SetupNextMenu(&SP_TimeAttackDef); if (!M_CanShowLevelInList(cv_nextmap.value-1, -1) && levelselect.rows[0].maplist[0]) CV_SetValue(&cv_nextmap, levelselect.rows[0].maplist[0]); else Nextmap_OnChange(); - G_SetGamestate(GS_TIMEATTACK); - M_ChangeMusic("_inter", true); - itemOn = tastart; // "Start" is selected. } @@ -8137,15 +8378,13 @@ static void M_NightsAttack(INT32 choice) // This is really just to make sure Sonic is the played character, just in case M_PatchSkinNameTable(); + G_SetGamestate(GS_TIMEATTACK); // do this before M_SetupNextMenu so that menu meta state knows that we're switching M_SetupNextMenu(&SP_NightsAttackDef); if (!M_CanShowLevelInList(cv_nextmap.value-1, -1) && levelselect.rows[0].maplist[0]) CV_SetValue(&cv_nextmap, levelselect.rows[0].maplist[0]); else Nextmap_OnChange(); - G_SetGamestate(GS_TIMEATTACK); - M_ChangeMusic("_inter", true); - itemOn = nastart; // "Start" is selected. } @@ -8370,15 +8609,17 @@ static void M_ModeAttackEndGame(INT32 choice) default: case ATTACKING_RECORD: currentMenu = &SP_TimeAttackDef; + wipetypepost = menumeta[MN_SP_TIMEATTACK].enterwipe; break; case ATTACKING_NIGHTS: currentMenu = &SP_NightsAttackDef; + wipetypepost = menumeta[MN_SP_NIGHTSATTACK].enterwipe; break; } itemOn = currentMenu->lastOn; G_SetGamestate(GS_TIMEATTACK); modeattacking = ATTACKING_NONE; - M_ChangeMusic("_inter", true); + M_ChangeMusic("_title", true); Nextmap_OnChange(); } @@ -9556,6 +9797,10 @@ static void M_Setup1PJoystickMenu(INT32 choice) { setupcontrols_secondaryplayer = false; OP_JoystickSetDef.prevMenu = &OP_Joystick1Def; + OP_JoystickSetDef.menuid &= ~(((1 << MENUBITS) - 1) << MENUBITS); + OP_JoystickSetDef.menuid &= ~(((1 << MENUBITS) - 1) << (MENUBITS*2)); + OP_JoystickSetDef.menuid |= MN_OP_P1CONTROLS << MENUBITS; + OP_JoystickSetDef.menuid |= MN_OP_P1JOYSTICK << (MENUBITS*2); M_SetupJoystickMenu(choice); } @@ -9563,6 +9808,10 @@ static void M_Setup2PJoystickMenu(INT32 choice) { setupcontrols_secondaryplayer = true; OP_JoystickSetDef.prevMenu = &OP_Joystick2Def; + OP_JoystickSetDef.menuid &= ~(((1 << MENUBITS) - 1) << MENUBITS); + OP_JoystickSetDef.menuid &= ~(((1 << MENUBITS) - 1) << (MENUBITS*2)); + OP_JoystickSetDef.menuid |= MN_OP_P2CONTROLS << MENUBITS; + OP_JoystickSetDef.menuid |= MN_OP_P2JOYSTICK << (MENUBITS*2); M_SetupJoystickMenu(choice); } @@ -9600,6 +9849,8 @@ static void M_Setup1PControlsMenu(INT32 choice) OP_ChangeControlsMenu[23+3].status = IT_CALL|IT_STRING2; OP_ChangeControlsDef.prevMenu = &OP_P1ControlsDef; + OP_ChangeControlsDef.menuid &= ~(((1 << MENUBITS) - 1) << MENUBITS); // remove first level (<< 6) + OP_ChangeControlsDef.menuid |= MN_OP_P1CONTROLS << MENUBITS; // combine first level (<< 6) M_SetupNextMenu(&OP_ChangeControlsDef); } @@ -9625,6 +9876,8 @@ static void M_Setup2PControlsMenu(INT32 choice) OP_ChangeControlsMenu[23+3].status = IT_GRAYEDOUT2; OP_ChangeControlsDef.prevMenu = &OP_P2ControlsDef; + OP_ChangeControlsDef.menuid &= ~(((1 << MENUBITS) - 1) << MENUBITS); // remove first level (<< 6) + OP_ChangeControlsDef.menuid |= MN_OP_P2CONTROLS << MENUBITS; // combine first level (<< 6) M_SetupNextMenu(&OP_ChangeControlsDef); } diff --git a/src/m_menu.h b/src/m_menu.h index 979165e98..bb58e706e 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -132,11 +132,16 @@ typedef struct char musname[7]; ///< Music track to play. "" for no music. UINT16 mustrack; ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore. boolean muslooping; ///< Loop the music + boolean musstop; ///< Don't play any music + boolean musignore; ///< Let the current music keep playing SINT8 fadestrength; // darken background when displaying this menu, strength 0-31 or -1 for undefined - boolean exitparents; // run exit line exec on parent menus when entering a child menu + boolean enterbubble; // run all entrance line execs after common ancestor and up to child. If false, only run the child's exec + boolean exitbubble; // run all exit line execs from child and up to before common ancestor. If false, only run the child's exec INT32 entertag; // line exec to run on menu enter, if titlemap INT32 exittag; // line exec to run on menu exit, if titlemap + INT16 enterwipe; // wipe type to run on menu enter, -1 means default + INT16 exitwipe; // wipe type to run on menu exit, -1 means default } menumeta_t; extern menumeta_t menumeta[NUMMENUTYPES]; @@ -147,6 +152,7 @@ boolean M_GetHideTitlePics(void); void MN_Ticker(boolean run); void MN_Start(void); void MN_InitInfoTables(void); +void M_ApplyMenuMetaState(void); // Called by main loop, diff --git a/src/p_setup.c b/src/p_setup.c index c1e8c17e3..61fe952ac 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2737,7 +2737,13 @@ boolean P_SetupLevel(boolean skipprecip) V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); F_WipeEndScreen(); - F_RunWipe(wipedefs[wipe_level_toblack], false); + // for titlemap: run a specific wipe if specified + // needed for exiting time attack + if (wipetypepre != INT16_MAX) + F_RunWipe( + (wipetypepre >= 0 && F_WipeExists(wipetypepre)) ? wipetypepre : wipedefs[wipe_level_toblack], + false); + wipetypepre = -1; } // Print "SPEEDING OFF TO [ZONE] [ACT 1]..."