M_CheckNetUnlockByID

System for netsyncing unlocks, inspired by but with nowhere near as many moving parts as (STJr/SRB2!1756).
* `gamedata->unlocked[MAXUNLOCKABLES]` is duplicated to `netUnlocked[MAXUNLOCKABLES]` (or all `true` in `dedicated`
* New `local` boolean for M_SecretUnlocked
* Removed last vestiges of SRB2 special stage token code because it occupied the spot in the netsave we wanted to use.
* Correct typing of multiple `m_cond` functions that returned `boolean` constants as `UINT8`s.
This commit is contained in:
toaster 2022-12-09 17:26:52 +00:00
parent d78668e0a6
commit e7c79ab461
15 changed files with 78 additions and 65 deletions

View file

@ -1966,13 +1966,13 @@ static void CV_SetCVar(consvar_t *var, const char *value, boolean stealth)
return;
}
if (var == &cv_kartencore && !M_SecretUnlocked(SECRET_ENCORE))
if (var == &cv_kartencore && !M_SecretUnlocked(SECRET_ENCORE, false))
{
CONS_Printf(M_GetText("You haven't unlocked Encore Mode yet!\n"));
return;
}
if (var == &cv_kartspeed && !M_SecretUnlocked(SECRET_HARDSPEED))
if (var == &cv_kartspeed && !M_SecretUnlocked(SECRET_HARDSPEED, false))
{
if (!stricmp(value, "Hard") || atoi(value) >= KARTSPEED_HARD)
{
@ -2227,7 +2227,7 @@ void CV_AddValue(consvar_t *var, INT32 increment)
|| var->PossibleValue == dummykartspeed_cons_t
|| var->PossibleValue == gpdifficulty_cons_t)
{
if (!M_SecretUnlocked(SECRET_HARDSPEED))
if (!M_SecretUnlocked(SECRET_HARDSPEED, false))
{
max = KARTSPEED_NORMAL+1;
if (var->PossibleValue == kartspeed_cons_t)

View file

@ -57,6 +57,7 @@
#include "k_boss.h"
#include "doomstat.h"
#include "s_sound.h" // sfx_syfail
#include "m_cond.h" // netUnlocked
// cl loading screen
#include "v_video.h"
@ -3463,6 +3464,11 @@ void SV_ResetServer(void)
CV_RevertNetVars();
// Copy our unlocks to a place where net material can grab at/overwrite them safely.
// (permits all unlocks in dedicated)
for (i = 0; i < MAXUNLOCKABLES; i++)
netUnlocked[i] = (dedicated || gamedata->unlocked[i]);
DEBFILE("\n-=-=-=-=-=-=-= Server Reset =-=-=-=-=-=-=-\n\n");
}

View file

@ -2857,7 +2857,7 @@ static void Command_Map_f(void)
{
newencoremode = !newencoremode;
if (!M_SecretUnlocked(SECRET_ENCORE) && newencoremode == true && !usingcheats)
if (!M_SecretUnlocked(SECRET_ENCORE, false) && newencoremode == true && !usingcheats)
{
CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n"));
return;
@ -4848,7 +4848,7 @@ void ItemFinder_OnChange(void)
if (!cv_itemfinder.value)
return; // it's fine.
if (!M_SecretUnlocked(SECRET_ITEMFINDER))
if (!M_SecretUnlocked(SECRET_ITEMFINDER, true))
{
CONS_Printf(M_GetText("You haven't earned this yet.\n"));
CV_StealthSetValue(&cv_itemfinder, 0);

View file

@ -573,10 +573,6 @@ extern INT32 luabanks[NUM_LUABANKS];
extern INT32 nummaprings; //keep track of spawned rings/coins
extern UINT32 token; ///< Number of tokens collected in a level
extern UINT32 tokenlist; ///< List of tokens collected
extern boolean gottoken; ///< Did you get a token? Used for end of act
extern INT32 tokenbits; ///< Used for setting token bits
extern UINT32 bluescore; ///< Blue Team Scores
extern UINT32 redscore; ///< Red Team Scores

View file

@ -204,10 +204,6 @@ UINT8 stagefailed; // Used for GEMS BONUS? Also to see if you beat the stage.
UINT16 emeralds;
INT32 luabanks[NUM_LUABANKS];
UINT32 token; // Number of tokens collected in a level
UINT32 tokenlist; // List of tokens collected
boolean gottoken; // Did you get a token? Used for end of act
INT32 tokenbits; // Used for setting token bits
// Temporary holding place for nights data for the current map
//nightsdata_t ntemprecords;
@ -3309,7 +3305,7 @@ INT16 G_SometimesGetDifferentGametype(UINT8 prefgametype)
// Most of the gametype references in this condition are intentionally not prefgametype.
// This is so a server CAN continue playing a gametype if they like the taste of it.
// The encore check needs prefgametype so can't use G_RaceGametype...
boolean encorepossible = ((M_SecretUnlocked(SECRET_ENCORE) || encorescramble == 1)
boolean encorepossible = ((M_SecretUnlocked(SECRET_ENCORE, false) || encorescramble == 1)
&& ((gametyperules|gametypedefaultrules[prefgametype]) & GTR_CIRCUIT));
UINT8 encoremodifier = 0;
@ -3870,7 +3866,7 @@ static void G_GetNextMap(void)
while (cup)
{
// Not unlocked? Grab the next result afterwards
if (!marathonmode && cup->unlockrequired < MAXUNLOCKABLES && !gamedata->unlocked[cup->unlockrequired])
if (!marathonmode && M_CheckNetUnlockByID(cup->unlockrequired))
{
cup = cup->next;
gettingresult = 1;
@ -4218,10 +4214,6 @@ static void G_DoContinued(void)
// Reset score
pl->score = 0;
// Allow tokens to come back
tokenlist = 0;
token = 0;
if (!(netgame || multiplayer || demo.playback || demo.recording || metalrecording || modeattacking) && !usedCheats && cursaveslot > 0)
G_SaveGameOver((UINT32)cursaveslot, true);

View file

@ -79,6 +79,7 @@ boolean K_FollowerUsable(INT32 skinnum)
}
// Use the unlockables table directly
// DEFINITELY not M_CheckNetUnlockByID
return (boolean)(gamedata->unlocked[i]);
}

View file

@ -1930,7 +1930,7 @@ static void M_DrawCupPreview(INT16 y, cupheader_t *cup)
V_DrawFill(0, y, BASEVIDWIDTH, 54, 31);
if (cup && (cup->unlockrequired >= MAXUNLOCKABLES || gamedata->unlocked[cup->unlockrequired]))
if (cup && (cup->unlockrequired >= MAXUNLOCKABLES || M_CheckNetUnlockByID(cup->unlockrequired)))
{
i = (cupgrid.previewanim / 82) % cup->numlevels;
while (x < BASEVIDWIDTH + pad)
@ -1968,7 +1968,7 @@ static void M_DrawCupTitle(INT16 y, cupheader_t *cup)
if (cup)
{
boolean unlocked = (cup->unlockrequired >= MAXUNLOCKABLES || gamedata->unlocked[cup->unlockrequired]);
boolean unlocked = (cup->unlockrequired >= MAXUNLOCKABLES || M_CheckNetUnlockByID(cup->unlockrequired));
UINT8 *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GREY, GTC_MENUCACHE);
patch_t *icon = W_CachePatchName(cup->icon, PU_CACHE);
const char *str = (unlocked ? va("%s Cup", cup->name) : "???");
@ -2036,7 +2036,7 @@ void M_DrawCupSelect(void)
V_DrawScaledPatch(x, y, 0, patch);
if (iconcup->unlockrequired < MAXUNLOCKABLES && !gamedata->unlocked[iconcup->unlockrequired])
if (iconcup->unlockrequired < MAXUNLOCKABLES && !M_CheckNetUnlockByID(iconcup->unlockrequired))
{
patch_t *st = W_CachePatchName(va("ICONST0%d", (cupgrid.previewanim % 4) + 1), PU_CACHE);
V_DrawScaledPatch(x + 8, y + icony, 0, st);

View file

@ -3362,7 +3362,7 @@ void M_SetupDifficultySelect(INT32 choice)
PLAY_RaceDifficultyDef.lastOn = drace_cupselect; // Select cup select by default.
}
if (M_SecretUnlocked(SECRET_ENCORE))
if (M_SecretUnlocked(SECRET_ENCORE, false))
{
PLAY_RaceDifficulty[drace_encore].status = IT_STRING2|IT_CVAR; // Encore on/off
}
@ -3477,7 +3477,7 @@ static void M_LevelListFromGametype(INT16 gt)
while (cup)
{
if (cup->unlockrequired >= MAXUNLOCKABLES || gamedata->unlocked[cup->unlockrequired])
if (cup->unlockrequired >= MAXUNLOCKABLES || M_CheckNetUnlockByID(cup->unlockrequired))
{
highestid = cup->id;
if (Playing() && mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->cup == cup)
@ -3611,7 +3611,7 @@ void M_CupSelectHandler(INT32 choice)
M_SetMenuDelay(pid);
if ((!newcup)
|| (newcup && newcup->unlockrequired < MAXUNLOCKABLES && !gamedata->unlocked[newcup->unlockrequired])
|| (newcup->unlockrequired < MAXUNLOCKABLES && !M_CheckNetUnlockByID(newcup->unlockrequired))
|| (newcup->cachedlevels[0] == NEXTMAP_INVALID))
{
S_StartSound(NULL, sfx_s3kb2);
@ -4614,7 +4614,7 @@ void M_InitOptions(INT32 choice)
OPTIONS_MainDef.menuitems[mopt_gameplay].status = IT_STRING | IT_SUBMENU;
OPTIONS_MainDef.menuitems[mopt_server].status = IT_STRING | IT_SUBMENU;
OPTIONS_GameplayDef.menuitems[gopt_encore].status =
(M_SecretUnlocked(SECRET_ENCORE) ? (IT_STRING | IT_CVAR) : IT_DISABLED);
(M_SecretUnlocked(SECRET_ENCORE, false) ? (IT_STRING | IT_CVAR) : IT_DISABLED);
}
OPTIONS_DataDef.menuitems[dopt_erase].status = (gamestate == GS_MENU

View file

@ -403,8 +403,6 @@ int LUA_WriteGlobals(lua_State *L, const char *word)
skincolor_bluering = (UINT16)luaL_checkinteger(L, 2);
else if (fastcmp(word, "emeralds"))
emeralds = (UINT16)luaL_checkinteger(L, 2);
else if (fastcmp(word, "token"))
token = (UINT32)luaL_checkinteger(L, 2);
else if (fastcmp(word, "gravity"))
gravity = (fixed_t)luaL_checkinteger(L, 2);
else if (fastcmp(word, "stoppedclock"))

View file

@ -27,6 +27,7 @@
#include "k_profiles.h"
gamedata_t *gamedata = NULL;
boolean netUnlocked[MAXUNLOCKABLES];
// Map triggers for linedef executors
// 32 triggers, one bit each
@ -442,7 +443,7 @@ void M_ClearSecrets(void)
for (i = 0; i < MAXEMBLEMS; ++i)
gamedata->collected[i] = false;
for (i = 0; i < MAXUNLOCKABLES; ++i)
gamedata->unlocked[i] = false;
gamedata->unlocked[i] = netUnlocked[i] = false;
for (i = 0; i < MAXCONDITIONSETS; ++i)
gamedata->achieved[i] = false;
@ -728,37 +729,47 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa
// Quick unlock checks
// -------------------
UINT8 M_SecretUnlocked(INT32 type)
boolean M_CheckNetUnlockByID(UINT8 unlockid)
{
if (unlockid >= MAXUNLOCKABLES)
{
return true; // default permit
}
if (netgame)
{
return netUnlocked[unlockid];
}
return gamedata->unlocked[unlockid];
}
boolean M_SecretUnlocked(INT32 type, boolean local)
{
INT32 i;
if (dedicated)
return true;
#if 0
(void)type;
(void)i;
return false; // for quick testing
#else
#ifdef DEVELOP
#define CHADYES true
#else
#define CHADYES false
#endif
for (i = 0; i < MAXUNLOCKABLES; ++i)
{
if (unlockables[i].type == type && gamedata->unlocked[i] != CHADYES)
return !CHADYES;
if (unlockables[i].type != type)
continue;
if ((local && gamedata->unlocked[i])
|| M_CheckNetUnlockByID(i))
continue;
return false;
}
return CHADYES;
#undef CHADYES
return true;
#endif //if 0
}
UINT8 M_MapLocked(INT32 mapnum)
boolean M_MapLocked(INT32 mapnum)
{
// Don't lock maps in dedicated servers.
// That just makes hosts' lives hell.
@ -774,16 +785,11 @@ UINT8 M_MapLocked(INT32 mapnum)
if (mapheaderinfo[mapnum-1]->cup)
{
if ((mapheaderinfo[mapnum-1]->cup->unlockrequired < MAXUNLOCKABLES)
&& (!gamedata->unlocked[mapheaderinfo[mapnum-1]->cup->unlockrequired]))
if (!M_CheckNetUnlockByID(mapheaderinfo[mapnum-1]->cup->unlockrequired))
return true;
}
if ((mapheaderinfo[mapnum-1]->unlockrequired < MAXUNLOCKABLES)
&& (!gamedata->unlocked[mapheaderinfo[mapnum-1]->unlockrequired]))
return true;
return false;
return !M_CheckNetUnlockByID(mapheaderinfo[mapnum-1]->unlockrequired);
}
INT32 M_CountEmblems(void)

View file

@ -158,6 +158,9 @@ typedef struct
extern gamedata_t *gamedata;
// Netsynced functional alternative to gamedata->unlocked
extern boolean netUnlocked[MAXUNLOCKABLES];
extern conditionset_t conditionSets[MAXCONDITIONSETS];
extern emblem_t emblemlocations[MAXEMBLEMS];
extern unlockable_t unlockables[MAXUNLOCKABLES];
@ -191,8 +194,9 @@ UINT8 M_CheckLevelEmblems(void);
UINT8 M_CompletionEmblems(void);
// Checking unlockable status
UINT8 M_SecretUnlocked(INT32 type);
UINT8 M_MapLocked(INT32 mapnum);
boolean M_CheckNetUnlockByID(UINT8 unlockid);
boolean M_SecretUnlocked(INT32 type, boolean local);
boolean M_MapLocked(INT32 mapnum);
INT32 M_CountEmblems(void);
// Emblem shit

View file

@ -35,6 +35,7 @@
#include "p_polyobj.h"
#include "lua_script.h"
#include "p_slopes.h"
#include "m_cond.h" // netUnlocked
// SRB2Kart
#include "k_battle.h"
@ -4600,9 +4601,6 @@ static inline void P_UnArchiveSPGame(INT16 mapoverride)
//lastmapsaved = gamemap;
lastmaploaded = gamemap;
tokenlist = 0;
token = 0;
savedata.emeralds = READUINT16(save_p)-357;
READSTRINGN(save_p, testname, sizeof(testname));
@ -4621,7 +4619,7 @@ static inline void P_UnArchiveSPGame(INT16 mapoverride)
static void P_NetArchiveMisc(boolean resending)
{
size_t i;
size_t i, j;
WRITEUINT32(save_p, ARCHIVEBLOCK_MISC);
@ -4642,7 +4640,14 @@ static void P_NetArchiveMisc(boolean resending)
WRITEUINT32(save_p, pig);
}
WRITEUINT32(save_p, tokenlist);
for (i = 0; i < MAXUNLOCKABLES;)
{
UINT8 btemp = 0;
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
btemp |= (netUnlocked[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
WRITEUINT8(save_p, encoremode);
@ -4671,7 +4676,6 @@ static void P_NetArchiveMisc(boolean resending)
WRITEUINT8(save_p, globools);
}
WRITEUINT32(save_p, token);
WRITEUINT32(save_p, bluescore);
WRITEUINT32(save_p, redscore);
@ -4767,7 +4771,7 @@ static void P_NetArchiveMisc(boolean resending)
static inline boolean P_NetUnArchiveMisc(boolean reloading)
{
size_t i;
size_t i, j;
size_t numTasks;
if (READUINT32(save_p) != ARCHIVEBLOCK_MISC)
@ -4801,7 +4805,13 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading)
}
}
tokenlist = READUINT32(save_p);
for (i = 0; i < MAXUNLOCKABLES;)
{
UINT8 rtemp = READUINT8(save_p);
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
netUnlocked[j+i] = ((rtemp >> j) & 1);
i += j;
}
encoremode = (boolean)READUINT8(save_p);
@ -4834,7 +4844,6 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading)
stoppedclock = !!(globools & (1<<1));
}
token = READUINT32(save_p);
bluescore = READUINT32(save_p);
redscore = READUINT32(save_p);

View file

@ -6807,7 +6807,6 @@ static void P_InitLevelSettings(void)
modulothing = 0;
// special stage tokens, emeralds, and ring total
tokenbits = 0;
runemeraldmanager = false;
emeraldspawndelay = 60*TICRATE;

View file

@ -1495,14 +1495,14 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
{ // An unlockable itself must be unlocked!
INT32 unlockid = triggerline->args[1];
if ((modifiedgame && !savemoddata) || (netgame || multiplayer))
if (modifiedgame && !savemoddata)
return false;
else if (unlockid <= 0 || unlockid > MAXUNLOCKABLES) // limited by unlockable count
{
CONS_Debug(DBG_GAMELOGIC, "Unlockable check (sidedef %hu): bad unlockable ID %d\n", triggerline->sidenum[0], unlockid);
return false;
}
else if (!(gamedata->unlocked[unlockid]))
else if (!(M_CheckNetUnlockByID(unlockid)))
return false;
}
break;

View file

@ -171,6 +171,7 @@ UINT8 *R_GetSkinAvailabilities(boolean demolock)
if (unlockables[i].type != SECRET_SKIN)
continue;
// NEVER EVER EVER M_CheckNetUnlockByID
if (gamedata->unlocked[i] != true && !demolock)
continue;
@ -250,6 +251,7 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins)
}
// Use the unlockables table directly
// NOTE: M_CheckNetUnlockByID would be correct in many circumstances... but not all. TODO figure out how to discern.
return (boolean)(gamedata->unlocked[i]);
}