diff --git a/src/deh_soc.c b/src/deh_soc.c index 9ccee0746..9ceb61f95 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -1372,13 +1372,6 @@ void readlevelheader(MYFILE *f, char * name) else mapheaderinfo[num]->menuflags &= ~LF2_HIDEINMENU; } - else if (fastcmp(word, "HIDEINSTATS")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num]->menuflags |= LF2_HIDEINSTATS; - else - mapheaderinfo[num]->menuflags &= ~LF2_HIDEINSTATS; - } else if (fastcmp(word, "NOTIMEATTACK") || fastcmp(word, "NORECORDATTACK")) { // RECORDATTACK is an accepted alias if (i || word2[0] == 'T' || word2[0] == 'Y') @@ -1393,6 +1386,13 @@ void readlevelheader(MYFILE *f, char * name) else mapheaderinfo[num]->menuflags &= ~LF2_FINISHNEEDED; } + else if (fastcmp(word, "NOVISITNEEDED")) + { + if (i || word2[0] == 'T' || word2[0] == 'Y') + mapheaderinfo[num]->menuflags |= LF2_NOVISITNEEDED; + else + mapheaderinfo[num]->menuflags &= ~LF2_NOVISITNEEDED; + } else if (fastcmp(word, "GRAVITY")) mapheaderinfo[num]->gravity = FLOAT_TO_FIXED(atof(word2)); else if (fastcmp(word, "DESTROYOBJECTSFORCHALLENGES")) diff --git a/src/deh_tables.c b/src/deh_tables.c index 68b3c3f87..f1273c78b 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -6890,8 +6890,8 @@ struct int_const_s const INT_CONST[] = { {"LF_SUBTRACTNUM",LF_SUBTRACTNUM}, // And map flags {"LF2_HIDEINMENU",LF2_HIDEINMENU}, - {"LF2_HIDEINSTATS",LF2_HIDEINSTATS}, {"LF2_NOTIMEATTACK",LF2_NOTIMEATTACK}, + {"LF2_NOVISITNEEDED",LF2_NOVISITNEEDED}, {"LF2_FINISHNEEDED",LF2_FINISHNEEDED}, // Emeralds diff --git a/src/doomstat.h b/src/doomstat.h index 20ab4069b..5247ea082 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -151,8 +151,6 @@ struct skinreference_t #define MV_SPBATTACK (1<<3) #define MV_MYSTICMELODY (1<<4) #define MV_MAX (MV_VISITED|MV_BEATEN|MV_ENCORE|MV_SPBATTACK|MV_MYSTICMELODY) -#define MV_FINISHNEEDED (1<<7) -#define MV_PERSISTUNLOADED (MV_SPBATTACK|MV_FINISHNEEDED) struct recorddata_t { @@ -573,8 +571,8 @@ struct mapheader_t #define LF_SUBTRACTNUM (1<<3) ///< Use subtractive position number (for bright levels) #define LF2_HIDEINMENU (1<<0) ///< Hide in the multiplayer menu -#define LF2_HIDEINSTATS (1<<1) ///< Hide in the statistics screen -#define LF2_NOTIMEATTACK (1<<2) ///< Hide this map in Time Attack modes +#define LF2_NOTIMEATTACK (1<<1) ///< Hide this map in Time Attack modes +#define LF2_NOVISITNEEDED (1<<2) ///< Map does not require visitation to be selectable #define LF2_FINISHNEEDED (1<<3) ///< Not available in Time Attack modes until you beat the level extern mapheader_t** mapheaderinfo; diff --git a/src/g_game.c b/src/g_game.c index c4e30f9f9..ef9b54f3c 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3451,10 +3451,32 @@ static INT32 TOLMaps(UINT8 pgametype) continue; } - if (M_MapLocked(i + 1)) + // Only care about restrictions if the host is a listen server. + if (!dedicated) { - // Don't include locked - continue; + if (!(mapheaderinfo[i]->menuflags & LF2_NOVISITNEEDED) + && !(mapheaderinfo[i]->records.mapvisited & MV_VISITED) + && !( + mapheaderinfo[i]->cup + && mapheaderinfo[i]->cup->cachedlevels[0] == i + )) + { + // Not visited OR head of cup + continue; + } + + if ((mapheaderinfo[i]->menuflags & LF2_FINISHNEEDED) + && !(mapheaderinfo[i]->records.mapvisited & MV_BEATEN)) + { + // Not completed + continue; + } + + if (M_MapLocked(i + 1) == true) + { + // We haven't earned this one. + continue; + } } num++; @@ -3536,10 +3558,32 @@ tryAgain: continue; } - if (M_MapLocked(i + 1) == true) + // Only care about restrictions if the host is a listen server. + if (!dedicated) { - // We haven't earned this one. - continue; + if (!(mapheaderinfo[i]->menuflags & LF2_NOVISITNEEDED) + && !(mapheaderinfo[i]->records.mapvisited & MV_VISITED) + && !( + mapheaderinfo[i]->cup + && mapheaderinfo[i]->cup->cachedlevels[0] == i + )) + { + // Not visited OR head of cup + continue; + } + + if ((mapheaderinfo[i]->menuflags & LF2_FINISHNEEDED) + && !(mapheaderinfo[i]->records.mapvisited & MV_BEATEN)) + { + // Not completed + continue; + } + + if (M_MapLocked(i + 1) == true) + { + // We haven't earned this one. + continue; + } } if (ignoreBuffers == false) @@ -3974,7 +4018,7 @@ void G_GetNextMap(void) continue; } - for (i = 0; i < cup->numlevels; i++) + for (i = 0; i < CUPCACHE_PODIUM; i++) { cm = cup->cachedlevels[i]; @@ -3982,9 +4026,35 @@ void G_GetNextMap(void) if (cm >= nummapheaders || !mapheaderinfo[cm] || mapheaderinfo[cm]->lumpnum == LUMPERROR - || !(mapheaderinfo[cm]->typeoflevel & tolflag) - || (!marathonmode && M_MapLocked(cm+1))) + || !(mapheaderinfo[cm]->typeoflevel & tolflag)) + { continue; + } + + // Only care about restrictions if the host is a listen server. + if (!dedicated && !marathonmode) + { + if (!(mapheaderinfo[cm]->menuflags & LF2_NOVISITNEEDED) + && !(mapheaderinfo[cm]->records.mapvisited & MV_VISITED) + && i != 0) + { + // Not visited OR head of cup + continue; + } + + if ((mapheaderinfo[cm]->menuflags & LF2_FINISHNEEDED) + && !(mapheaderinfo[cm]->records.mapvisited & MV_BEATEN)) + { + // Not completed + continue; + } + + if (M_MapLocked(cm + 1) == true) + { + // We haven't earned this one. + continue; + } + } // If the map is in multiple cups, only consider the first one valid. if (mapheaderinfo[cm]->cup != cup) @@ -4029,24 +4099,50 @@ void G_GetNextMap(void) else { cm = prevmap; - if (++cm >= nummapheaders) - cm = 0; - while (cm != prevmap) + do { + if (++cm >= nummapheaders) + cm = 0; + if (!mapheaderinfo[cm] || mapheaderinfo[cm]->lumpnum == LUMPERROR || !(mapheaderinfo[cm]->typeoflevel & tolflag) - || (mapheaderinfo[cm]->menuflags & LF2_HIDEINMENU) - || M_MapLocked(cm+1)) + || (mapheaderinfo[cm]->menuflags & LF2_HIDEINMENU)) { - if (++cm >= nummapheaders) - cm = 0; continue; } + // Only care about restrictions if the host is a listen server. + if (!dedicated && !marathonmode) + { + if (!(mapheaderinfo[cm]->menuflags & LF2_NOVISITNEEDED) + && !(mapheaderinfo[cm]->records.mapvisited & MV_VISITED) + && !( + mapheaderinfo[cm]->cup + && mapheaderinfo[cm]->cup->cachedlevels[0] == cm + )) + { + // Not visited OR head of cup + continue; + } + + if ((mapheaderinfo[cm]->menuflags & LF2_FINISHNEEDED) + && !(mapheaderinfo[cm]->records.mapvisited & MV_BEATEN)) + { + // Not completed + continue; + } + + if (M_MapLocked(cm + 1) == true) + { + // We haven't earned this one. + continue; + } + } + break; - } + } while (cm != prevmap); nextmap = cm; } @@ -4295,26 +4391,27 @@ static void G_DoCompleted(void) // If the current gametype has no intermission screen set, then don't start it. Y_DetermineIntermissionType(); - if (intertype == int_none) + if (intertype != int_none) { - G_UpdateVisited(); - if (grandprixinfo.gp == true) - { - K_UpdateGPRank(&grandprixinfo.rank); - } - G_AfterIntermission(); - } - else - { - G_SetGamestate(GS_INTERMISSION); Y_StartIntermission(); - G_UpdateVisited(); } + else if (grandprixinfo.gp == true) + { + K_UpdateGPRank(&grandprixinfo.rank); + } + + G_UpdateVisited(); // This isn't in the above blocks because many // mechanisms can queue up a gamedata save. if (gamedata->deferredsave) G_SaveGameData(); + + // Seperate from the above, as Y_StartIntermission can no-sell. + if (intertype == int_none) + { + G_AfterIntermission(); + } } // See also F_EndCutscene, the only other place which handles intra-map/ending transitions @@ -4972,8 +5069,7 @@ void G_LoadGameData(void) M_Memcpy(&mapheaderinfo[mapnum]->records, &dummyrecord, sizeof(recorddata_t)); } else if ( - ((dummyrecord.mapvisited & MV_PERSISTUNLOADED) != 0 - && (dummyrecord.mapvisited & MV_BEATEN) != 0) + (dummyrecord.mapvisited & MV_BEATEN) || dummyrecord.time != 0 || dummyrecord.lap != 0 ) @@ -5575,11 +5671,6 @@ void G_SaveGameData(void) UINT8 mapvisitedtemp = (mapheaderinfo[i]->records.mapvisited & MV_MAX); - if ((mapheaderinfo[i]->menuflags & (LF2_FINISHNEEDED|LF2_HIDEINMENU))) - { - mapvisitedtemp |= MV_FINISHNEEDED; - } - WRITEUINT8(save.p, mapvisitedtemp); WRITEUINT32(save.p, mapheaderinfo[i]->records.time); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 5abc59fd1..2c428df43 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -97,6 +97,7 @@ static char hu_tick; patch_t *missingpat; patch_t *blanklvl, *nolvl; +patch_t *unvisitedlvl[4]; // song credits static patch_t *songcreditbg; @@ -196,6 +197,11 @@ void HU_LoadGraphics(void) HU_UpdatePatch(&blanklvl, "BLANKLVL"); HU_UpdatePatch(&nolvl, "M_NOLVL"); + for (i = 0; i < 4; i++) + { + HU_UpdatePatch(&unvisitedlvl[i], "PREVST0%d", i+1); + } + HU_UpdatePatch(&songcreditbg, "K_SONGCR"); // cache ping gfx: diff --git a/src/k_menudraw.c b/src/k_menudraw.c index edba3b6c2..0c8e9ed42 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -205,6 +205,17 @@ void M_PickMenuBGMap(void) continue; } + if (!(mapheaderinfo[i]->menuflags & LF2_NOVISITNEEDED) + && !(mapheaderinfo[i]->records.mapvisited & MV_VISITED) + && !( + mapheaderinfo[i]->cup + && mapheaderinfo[i]->cup->cachedlevels[0] == i + )) + { + // Not visited OR head of cup + continue; + } + if (M_MapLocked(i + 1) == true) { // We haven't earned this one. @@ -2676,24 +2687,28 @@ void M_DrawRaceDifficulty(void) // LEVEL SELECT -static void M_DrawCupPreview(INT16 y, levelsearch_t *levelsearch) +static void M_DrawCupPreview(INT16 y, levelsearch_t *baselevelsearch) { + levelsearch_t locklesslevelsearch = *baselevelsearch; // full copy + locklesslevelsearch.checklocked = false; + UINT8 i = 0; - INT16 maxlevels = M_CountLevelsToShowInList(levelsearch); + INT16 maxlevels = M_CountLevelsToShowInList(&locklesslevelsearch); const fixed_t step = (82 * FRACUNIT); fixed_t previewanimwork = (cupgrid.previewanim * FRACUNIT) + rendertimefrac_unpaused; fixed_t x = -(previewanimwork % step); - INT16 add; - INT16 map, start = M_GetFirstLevelInList(&i, levelsearch); + INT16 map, start = M_GetFirstLevelInList(&i, &locklesslevelsearch); UINT8 starti = i; - if (levelsearch->cup && maxlevels > 0) + patch_t *staticpat = unvisitedlvl[cupgrid.previewanim % 4]; + + if (baselevelsearch->cup && maxlevels > 0) { - add = (previewanimwork / step) % maxlevels; + INT16 add = (previewanimwork / step) % maxlevels; map = start; while (add > 0) { - map = M_GetNextLevelInList(map, &i, levelsearch); + map = M_GetNextLevelInList(map, &i, &locklesslevelsearch); if (map >= nummapheaders) { @@ -2710,24 +2725,35 @@ static void M_DrawCupPreview(INT16 y, levelsearch_t *levelsearch) i = starti; } - K_DrawMapThumbnail( - x + FRACUNIT, (y+2)<menuflags & (LF2_NOTIMEATTACK|LF2_HIDEINSTATS|LF2_HIDEINMENU))) + if (!mapheaderinfo[i] || (mapheaderinfo[i]->menuflags & (LF2_NOTIMEATTACK|LF2_HIDEINMENU))) continue; // Has to be accessible via time attack diff --git a/src/m_cond.c b/src/m_cond.c index a026625cf..9414e73fa 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -1094,7 +1094,7 @@ static void M_PrecacheLevelLocks(void) { if (!mapheaderinfo[mapcheck] || mapheaderinfo[mapcheck]->cup != NULL) continue; - if (mapheaderinfo[mapcheck]->menuflags & (LF2_HIDEINSTATS|LF2_HIDEINMENU)) + if (mapheaderinfo[mapcheck]->menuflags & LF2_HIDEINMENU) continue; if (((mapheaderinfo[mapcheck]->typeoflevel & TOL_TUTORIAL) == TOL_TUTORIAL) != ((mapheaderinfo[map]->typeoflevel & TOL_TUTORIAL) == TOL_TUTORIAL)) @@ -1468,6 +1468,11 @@ boolean M_CheckCondition(condition_t *cn, player_t *player) case UC_UNLOCKPERCENT: { + // Don't let netgame sessions intefere + // (or have this give a performance hit) + if (Playing()) + return false; + UINT16 i, unlocked = cn->extrainfo2, total = 0; // Special case for maps @@ -1475,7 +1480,7 @@ boolean M_CheckCondition(condition_t *cn, player_t *player) { for (i = 0; i < basenummapheaders; i++) { - if (!mapheaderinfo[i] || mapheaderinfo[i]->menuflags & (LF2_HIDEINSTATS|LF2_HIDEINMENU)) + if (!mapheaderinfo[i] || mapheaderinfo[i]->menuflags & (LF2_HIDEINMENU)) continue; total++; @@ -1957,7 +1962,7 @@ static char *M_BuildConditionTitle(UINT16 map) { char *title, *ref; - if (((mapheaderinfo[map]->menuflags & LF2_FINISHNEEDED) + if ((!(mapheaderinfo[map]->menuflags & LF2_NOVISITNEEDED) // the following is intentionally not MV_BEATEN, just in case the title is for "Finish a round on X" && !(mapheaderinfo[map]->records.mapvisited & MV_VISITED)) || M_MapLocked(map+1)) diff --git a/src/menus/extras-statistics.c b/src/menus/extras-statistics.c index 1c2866c5a..293c90227 100644 --- a/src/menus/extras-statistics.c +++ b/src/menus/extras-statistics.c @@ -21,13 +21,22 @@ static boolean M_StatisticsAddMap(UINT16 map, cupheader_t *cup, boolean *headere return false; // Check for no visibility - if (mapheaderinfo[map]->menuflags & (LF2_HIDEINSTATS|LF2_HIDEINMENU)) + if (mapheaderinfo[map]->menuflags & LF2_HIDEINMENU) return false; + // Check for visitation + // (M_CanShowLevelInList also checks being the first map in a cup, + // but we don't need to do this here because it'd just muddy stats!) + if (!(mapheaderinfo[map]->menuflags & LF2_NOVISITNEEDED) + && !(mapheaderinfo[map]->records.mapvisited & MV_VISITED)) + return false; + +#if 0 // Check for completion if ((mapheaderinfo[map]->menuflags & LF2_FINISHNEEDED) && !(mapheaderinfo[map]->records.mapvisited & MV_BEATEN)) return false; +#endif // Check for unlock if (M_MapLocked(map+1)) @@ -80,11 +89,7 @@ static void M_StatisticsMaps(void) headerexists = false; for (i = 0; i < nummapheaders; i++) { - if (M_StatisticsAddMap(i, NULL, &headerexists, true)) - { - if (!(mapheaderinfo[i]->records.mapvisited & MV_BEATEN)) - break; - } + M_StatisticsAddMap(i, NULL, &headerexists, true); } if ((i = statisticsmenu.numextramedals) != 0) diff --git a/src/menus/transient/level-select.c b/src/menus/transient/level-select.c index 73189a3bb..28eed536a 100644 --- a/src/menus/transient/level-select.c +++ b/src/menus/transient/level-select.c @@ -77,11 +77,11 @@ boolean M_CanShowLevelInList(INT16 mapnum, levelsearch_t *levelsearch) if (levelsearch->timeattack && (mapheaderinfo[mapnum]->menuflags & LF2_NOTIMEATTACK)) return false; + cupheader_t *cup = (levelsearch->cup == &dummy_lostandfound) ? NULL : levelsearch->cup; + // Don't permit cup when no cup requested (also no dupes in time attack) if (levelsearch->cupmode) { - cupheader_t *cup = (levelsearch->cup == &dummy_lostandfound) ? NULL : levelsearch->cup; - if ((!cup || levelsearch->timeattack) && mapheaderinfo[mapnum]->cup != cup) return false; @@ -90,6 +90,12 @@ boolean M_CanShowLevelInList(INT16 mapnum, levelsearch_t *levelsearch) // Finally, the most complex check: does the map have lock conditions? if (levelsearch->checklocked) { + // Check for visitation + if (!(mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED) + && !(mapheaderinfo[mapnum]->records.mapvisited & MV_VISITED) + && !(cup && cup->cachedlevels[0] == mapnum)) + return false; + // Check for completion if ((mapheaderinfo[mapnum]->menuflags & LF2_FINISHNEEDED) && !(mapheaderinfo[mapnum]->records.mapvisited & MV_BEATEN)) @@ -128,22 +134,9 @@ UINT16 M_CountLevelsToShowInList(levelsearch_t *levelsearch) for (i = 0; i < nummapheaders; i++) { - if (M_CanShowLevelInList(i, levelsearch)) - { - count++; - - // Tutorial will only show what you've made your way to - if (!levelsearch->checklocked) - continue; - if (!levelsearch->tutorial) - continue; - if (i >= basenummapheaders) - continue; - if (mapheaderinfo[i]->records.mapvisited & MV_BEATEN) - continue; - - break; - } + if (!M_CanShowLevelInList(i, levelsearch)) + continue; + count++; } return count; @@ -206,13 +199,6 @@ UINT16 M_GetNextLevelInList(UINT16 mapnum, UINT8 *i, levelsearch_t *levelsearch) } else { - // Tutorial will only show what you've made your way to - if (levelsearch->checklocked - && levelsearch->tutorial - && mapnum < basenummapheaders - && !(mapheaderinfo[mapnum]->records.mapvisited & MV_BEATEN)) - return NEXTMAP_INVALID; - mapnum++; while (!M_CanShowLevelInList(mapnum, levelsearch) && mapnum < nummapheaders) mapnum++; diff --git a/src/menus/transient/pause-game.c b/src/menus/transient/pause-game.c index bbf9c3eec..762b778cc 100644 --- a/src/menus/transient/pause-game.c +++ b/src/menus/transient/pause-game.c @@ -336,7 +336,14 @@ void M_HandlePauseMenuGametype(INT32 choice) M_ClearMenus(true); if (server || IsPlayerAdmin(consoleplayer)) { - D_SetupVote(menugametype); + if (cv_advancemap.value == 3) // Voting screen. + { + D_SetupVote(menugametype); + } + else // ideally for "random" only, but no sane fallback for "same" and "next" + { + COM_ImmedExecute(va("randommap -gt %s", gametypes[menugametype]->name)); + } } return; } diff --git a/src/r_defs.h b/src/r_defs.h index 33933bed4..4a05ad7bb 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -900,6 +900,7 @@ struct patch_t extern patch_t *missingpat; extern patch_t *blanklvl, *nolvl; +extern patch_t *unvisitedlvl[4]; #if defined(_MSC_VER) #pragma pack(1) diff --git a/src/s_sound.c b/src/s_sound.c index 7190b014f..43dbe2e49 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -1393,11 +1393,13 @@ static boolean S_SoundTestDefLocked(musicdef_t *def) mapheader_t *header = mapheaderinfo[def->sequence.map]; - // Is the level tied to SP progression? - if (( - (header->menuflags & (LF2_FINISHNEEDED|LF2_HIDEINMENU)) - || (def->sequence.altref == ALTREF_REQUIRESBEATEN) // Associated music only when completed - ) + // Visitation required? + if (!(header->menuflags & LF2_NOVISITNEEDED) + && !(header->records.mapvisited & MV_VISITED)) + return true; + + // Associated music only when completed + if ((def->sequence.altref == ALTREF_REQUIRESBEATEN) && !(header->records.mapvisited & MV_BEATEN)) return true; diff --git a/src/y_inter.c b/src/y_inter.c index 5a384c16f..84ffa9398 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1982,10 +1982,7 @@ void Y_StartIntermission(void) // determine the tic everybody's scores/PWR starts getting sorted sorttic = -1; if (!timer) - { - // Prevent a weird bug - timer = 1; - } + ; else if ( ( // Match Race or Time Attack netgame == false @@ -2033,11 +2030,6 @@ void Y_StartIntermission(void) if (prevmap >= nummapheaders || !mapheaderinfo[prevmap]) I_Error("Y_StartIntermission: Internal map ID %d not found (nummapheaders = %d)", prevmap, nummapheaders); - if (timer > 1 && musiccountdown == 0) - Music_Play("intermission"); - - S_ShowMusicCredit(); // Always call - switch (intertype) { case int_score: @@ -2058,9 +2050,6 @@ void Y_StartIntermission(void) break; } - LUA_HUD_DestroyDrawList(luahuddrawlist_intermission); - luahuddrawlist_intermission = LUA_HUD_CreateDrawList(); - if (powertype != PWRLV_DISABLED) { for (i = 0; i < MAXPLAYERS; i++) @@ -2078,6 +2067,22 @@ void Y_StartIntermission(void) SV_BumpMatchStats(); } + if (!timer) + { + Y_EndIntermission(); + return; + } + + G_SetGamestate(GS_INTERMISSION); + + if (musiccountdown == 0) + Music_Play("intermission"); + + S_ShowMusicCredit(); // Always call + + LUA_HUD_DestroyDrawList(luahuddrawlist_intermission); + luahuddrawlist_intermission = LUA_HUD_CreateDrawList(); + if (roundqueue.size > 0 && roundqueue.position == roundqueue.size) { Automate_Run(AEV_QUEUEEND);