From 8fd809f8b17d799d41d60761a218802310b156b6 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 8 Oct 2023 18:25:20 +0100 Subject: [PATCH] M_PrecacheLevelLocks(void): Implement a SECOND level of cacheing for Map and Cup unlocks Determining whether a map or cup is unlocked or not is now linear-time. - Attempted to cache SECRET_SKIN et al as well, but skins are loaded after unlocks... oh well. - Was staring down the barrel of a triple-nested loop for implementing SECRET_ALTMUSIC, so did this first to set good precedent. --- src/dehacked.c | 1 + src/doomstat.h | 3 +++ src/m_cond.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++--- src/p_setup.c | 2 ++ 4 files changed, 73 insertions(+), 4 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 66567fb05..bc3e3ae0a 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -491,6 +491,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) cup = Z_Calloc(sizeof (cupheader_t), PU_STATIC, NULL); cup->id = numkartcupheaders; cup->monitor = 1; + cup->cache_cuplock = MAXUNLOCKABLES; deh_strlcpy(cup->name, word2, sizeof(cup->name), va("Cup header %s: name", word2)); cup->namehash = hash; diff --git a/src/doomstat.h b/src/doomstat.h index 910abb18a..d62cd04a0 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -408,6 +408,8 @@ struct cupheader_t boolean playcredits; ///< Play the credits? + UINT16 cache_cuplock; ///< Cached Unlockable ID + cupwindata_t windata[4]; ///< Data for cup visitation cupheader_t *next; ///< Next cup in linked list }; @@ -534,6 +536,7 @@ struct mapheader_t UINT32 _saveid; ///< Purely assistive in gamedata save processes UINT16 cache_spraycan; ///< Cached Spraycan ID + UINT16 cache_maplock; ///< Cached Unlockable ID // Lua information UINT8 numCustomOptions; ///< Internal. For Lua custom value support. diff --git a/src/m_cond.c b/src/m_cond.c index 941ca1405..802f43aa5 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -684,7 +684,16 @@ void M_ClearSecrets(void) { if (!mapheaderinfo[i]) continue; + mapheaderinfo[i]->cache_spraycan = UINT16_MAX; + + mapheaderinfo[i]->cache_maplock = MAXUNLOCKABLES; + } + + cupheader_t *cup; + for (cup = kartcupheaders; cup; cup = cup->next) + { + cup->cache_cuplock = MAXUNLOCKABLES; } for (i = 0; i < numskincolors; i++) @@ -820,10 +829,54 @@ static void M_AssignSpraycans(void) } } +static void M_PrecacheLevelLocks(void) +{ + UINT16 i, j; + + for (i = 0; i < MAXUNLOCKABLES; ++i) + { + switch (unlockables[i].type) + { + // SECRET_SKIN, SECRET_COLOR, SECRET_FOLLOWER are instantiated too late to use + case SECRET_MAP: + { + UINT16 map = M_UnlockableMapNum(&unlockables[i]); + if (map < nummapheaders + && mapheaderinfo[map]) + { + if (mapheaderinfo[map]->cache_maplock != MAXUNLOCKABLES) + CONS_Alert(CONS_ERROR, "Unlockable %u: Too many SECRET_MAPs associated with Level %s\n", i, mapheaderinfo[map]->lumpname); + mapheaderinfo[map]->cache_maplock = i; + } + break; + } + + case SECRET_CUP: + { + cupheader_t *cup = M_UnlockableCup(&unlockables[i]); + if (cup) + { + if (cup->cache_cuplock != MAXUNLOCKABLES) + CONS_Alert(CONS_ERROR, "Unlockable %u: Too many SECRET_CUPs associated with Cup %s\n", i, cup->name); + cup->cache_cuplock = i; + break; + } + break; + } + + default: + break; + } + } +} + void M_FinaliseGameData(void) { //M_PopulateChallengeGrid(); -- This can be done lazily when we actually need it + // Precache as many unlockables as is meaningfully feasible + M_PrecacheLevelLocks(); + // Place the spraycans, which CAN'T be done lazily. M_AssignSpraycans(); @@ -2341,8 +2394,6 @@ boolean M_SecretUnlocked(INT32 type, boolean local) boolean M_CupLocked(cupheader_t *cup) { - UINT16 i; - // Don't lock maps in dedicated servers. // That just makes hosts' lives hell. if (dedicated) @@ -2355,6 +2406,9 @@ boolean M_CupLocked(cupheader_t *cup) if (!cup) return false; +#if 0 // perfect uncached behaviour + UINT16 i; + for (i = 0; i < MAXUNLOCKABLES; ++i) { if (unlockables[i].type != SECRET_CUP) @@ -2363,14 +2417,16 @@ boolean M_CupLocked(cupheader_t *cup) continue; return !M_CheckNetUnlockByID(i); } +#else + if (cup->cache_cuplock < MAXUNLOCKABLES) + return !M_CheckNetUnlockByID(cup->cache_cuplock); +#endif return false; } boolean M_MapLocked(UINT16 mapnum) { - UINT16 i; - // Don't lock maps in dedicated servers. // That just makes hosts' lives hell. if (dedicated) @@ -2391,6 +2447,9 @@ boolean M_MapLocked(UINT16 mapnum) return M_CupLocked(mapheaderinfo[mapnum-1]->cup); } +#if 0 // perfect uncached behaviour + UINT16 i; + for (i = 0; i < MAXUNLOCKABLES; ++i) { if (unlockables[i].type != SECRET_MAP) @@ -2399,6 +2458,10 @@ boolean M_MapLocked(UINT16 mapnum) continue; return !M_CheckNetUnlockByID(i); } +#else + if (mapheaderinfo[mapnum-1]->cache_maplock < MAXUNLOCKABLES) + return !M_CheckNetUnlockByID(mapheaderinfo[mapnum-1]->cache_maplock); +#endif return false; } diff --git a/src/p_setup.c b/src/p_setup.c index 44ae440f0..b52c9b9a4 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -464,6 +464,8 @@ static void P_ClearSingleMapHeaderInfo(INT16 num) mapheaderinfo[num]->cache_spraycan = UINT16_MAX; + mapheaderinfo[num]->cache_maplock = MAXUNLOCKABLES; + mapheaderinfo[num]->customopts = NULL; mapheaderinfo[num]->numCustomOptions = 0; }