Conversion to using gamedata_t

A small piece of (STJr/SRB2!1756).
Due to how RR currently handles time attack records and how it WILL handle unlocks, it's not currently feasible to split everything such that you can have two independent gamedata_t... but what's done so far is certainly more sane and less dependent on global variables
Other minor refactors:
- M_UpdateUnlockablesAndExtraEmblems and M_SilentUpdateUnlockablesAndEmblems are now one function with a boolean for loudness
- Unlock prints are currently living in the console, since the cecho stuff was a little broken
This commit is contained in:
toaster 2022-11-30 13:19:21 +00:00
parent 6a2aa87e95
commit 845fe12b52
18 changed files with 174 additions and 186 deletions

View file

@ -1377,6 +1377,8 @@ void D_SRB2Main(void)
Z_Init();
CON_SetLoadingProgress(LOADED_ZINIT);
M_NewGameDataStruct();
// Do this up here so that WADs loaded through the command line can use ExecCfg
COM_Init();

View file

@ -186,8 +186,6 @@ extern INT32 postimgparam[MAXSPLITSCREENPLAYERS];
extern INT32 viewwindowx, viewwindowy;
extern INT32 viewwidth, scaledviewwidth;
extern boolean gamedataloaded;
// Player taking events, and displaying.
extern INT32 consoleplayer;
extern INT32 displayplayers[MAXSPLITSCREENPLAYERS];
@ -538,9 +536,6 @@ typedef struct
extern tolinfo_t TYPEOFLEVEL[NUMTOLNAMES];
extern UINT32 lastcustomtol;
extern tic_t totalplaytime;
extern UINT32 matchesplayed;
extern UINT8 stagefailed;
// Emeralds stored as bits to throw savegame hackers off.
@ -682,8 +677,6 @@ extern INT16 votelevels[4][2];
extern SINT8 votes[MAXPLAYERS];
extern SINT8 pickedvote;
extern UINT32 timesBeaten; // # of times the game has been beaten.
// ===========================
// Internal parameters, fixed.
// ===========================

View file

@ -866,7 +866,7 @@ boolean F_CreditResponder(event_t *event)
return false;
}
/*if (!(timesBeaten) && !(netgame || multiplayer) && !cht_debug)
/*if (!(gamedata->timesBeaten) && !(netgame || multiplayer) && !cht_debug)
return false;*/
if (key != KEY_ESCAPE && key != KEY_ENTER && key != KEY_BACKSPACE)
@ -1024,31 +1024,6 @@ void F_GameEvaluationDrawer(void)
V_DrawCreditString((BASEVIDWIDTH - V_CreditStringWidth(endingtext))<<(FRACBITS-1), (BASEVIDHEIGHT-100)<<(FRACBITS-1), 0, endingtext);
#if 0 // the following looks like hot garbage the more unlockables we add, and we now have a lot of unlockables
if (finalecount >= 5*TICRATE)
{
V_DrawString(8, 16, V_YELLOWMAP, "Unlocked:");
if (!usedCheats)
{
INT32 startcoord = 32;
for (i = 0; i < MAXUNLOCKABLES; i++)
{
if (unlockables[i].conditionset && unlockables[i].conditionset < MAXCONDITIONSETS
&& unlockables[i].type && !unlockables[i].nocecho)
{
if (unlockables[i].unlocked)
V_DrawString(8, startcoord, 0, unlockables[i].name);
startcoord += 8;
}
}
}
else
V_DrawString(8, 96, V_YELLOWMAP, "Cheated games\ncan't unlock\nextras!");
}
#endif
if (marathonmode)
{
const char *rtatext, *cuttext;
@ -1101,9 +1076,9 @@ void F_GameEvaluationTicker(void)
{
if (!usedCheats)
{
++timesBeaten;
++gamedata->timesBeaten;
if (M_UpdateUnlockablesAndExtraEmblems())
if (M_UpdateUnlockablesAndExtraEmblems(true))
S_StartSound(NULL, sfx_s3k68);
G_SaveGameData();
@ -1611,7 +1586,7 @@ void F_EndingDrawer(void)
//colset(linkmap, 164, 165, 169); -- the ideal purple colour to represent a clicked in-game link, but not worth it just for a soundtest-controlled secret
V_DrawCenteredString(BASEVIDWIDTH/2, 8, V_ALLOWLOWERCASE|(trans<<V_ALPHASHIFT), str);
V_DrawCharacter(32, BASEVIDHEIGHT-16, '>'|(trans<<V_ALPHASHIFT), false);
V_DrawString(40, ((finalecount == STOPPINGPOINT-(20+TICRATE)) ? 1 : 0)+BASEVIDHEIGHT-16, ((timesBeaten || finalecount >= STOPPINGPOINT-TICRATE) ? V_PURPLEMAP : V_BLUEMAP)|(trans<<V_ALPHASHIFT), " [S] ===>");
V_DrawString(40, ((finalecount == STOPPINGPOINT-(20+TICRATE)) ? 1 : 0)+BASEVIDHEIGHT-16, ((gamedata->timesBeaten || finalecount >= STOPPINGPOINT-TICRATE) ? V_PURPLEMAP : V_BLUEMAP)|(trans<<V_ALPHASHIFT), " [S] ===>");
}
if (finalecount > STOPPINGPOINT-(20+(2*TICRATE)))

View file

@ -189,7 +189,8 @@ struct quake quake;
// Map Header Information
mapheader_t** mapheaderinfo = {NULL};
INT32 nummapheaders, mapallocsize = 0;
INT32 nummapheaders = 0;
INT32 mapallocsize = 0;
// Kart cup definitions
cupheader_t *kartcupheaders = NULL;
@ -208,10 +209,6 @@ 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
tic_t totalplaytime;
UINT32 matchesplayed; // SRB2Kart
boolean gamedataloaded = false;
// Temporary holding place for nights data for the current map
//nightsdata_t ntemprecords;
@ -340,9 +337,6 @@ static void G_ResetRandMapBuffer(void)
//intentionally not resetting randmaps.counttogametype here
}
// Grading
UINT32 timesBeaten;
typedef struct joystickvector2_s
{
INT32 xaxis;
@ -600,7 +594,7 @@ static void G_UpdateRecordReplays(void)
if ((earnedEmblems = M_CheckLevelEmblems()))
CONS_Printf(M_GetText("\x82" "Earned %hu medal%s for Record Attack records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
if (M_UpdateUnlockablesAndExtraEmblems())
if (M_UpdateUnlockablesAndExtraEmblems(true))
S_StartSound(NULL, sfx_ncitem);
// SRB2Kart - save here so you NEVER lose your earned times/medals.
@ -2194,8 +2188,8 @@ static inline void G_PlayerFinishLevel(INT32 player)
{
if (legitimateexit && !demo.playback && !mapreset) // (yes you're allowed to unlock stuff this way when the game is modified)
{
matchesplayed++;
if (M_UpdateUnlockablesAndExtraEmblems())
gamedata->matchesplayed++;
if (M_UpdateUnlockablesAndExtraEmblems(true))
S_StartSound(NULL, sfx_ncitem);
G_SaveGameData();
}
@ -3691,7 +3685,7 @@ static void G_UpdateVisited(void)
if ((earnedEmblems = M_CompletionEmblems()))
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
if (M_UpdateUnlockablesAndExtraEmblems())
if (M_UpdateUnlockablesAndExtraEmblems(true))
S_StartSound(NULL, sfx_ncitem);
G_SaveGameData();
}
@ -3876,7 +3870,7 @@ static void G_GetNextMap(void)
while (cup)
{
// Not unlocked? Grab the next result afterwards
if (!marathonmode && cup->unlockrequired != -1 && !unlockables[cup->unlockrequired].unlocked)
if (!marathonmode && cup->unlockrequired != -1 && !gamedata->unlocked[cup->unlockrequired])
{
cup = cup->next;
gettingresult = 1;
@ -4326,15 +4320,15 @@ void G_LoadGameData(void)
UINT32 numgamedatamapheaders;
// Stop saving, until we successfully load it again.
gamedataloaded = false;
gamedata->loaded = false;
// Clear things so previously read gamedata doesn't transfer
// to new gamedata
G_ClearRecords(); // main and nights records
G_ClearRecords(); // records
M_ClearSecrets(); // emblems, unlocks, maps visited, etc
totalplaytime = 0; // total play time (separate from all)
matchesplayed = 0; // SRB2Kart: matches played & finished
gamedata->totalplaytime = 0; // total play time (separate from all)
gamedata->matchesplayed = 0; // SRB2Kart: matches played & finished
if (M_CheckParm("-nodata"))
{
@ -4345,7 +4339,7 @@ void G_LoadGameData(void)
if (M_CheckParm("-resetdata"))
{
// Don't load, but do save. (essentially, reset)
gamedataloaded = true;
gamedata->loaded = true;
return;
}
@ -4353,7 +4347,7 @@ void G_LoadGameData(void)
if (!length)
{
// No gamedata. We can save a new one.
gamedataloaded = true;
gamedata->loaded = true;
return;
}
@ -4372,8 +4366,8 @@ void G_LoadGameData(void)
I_Error("Game data is not for Ring Racers v2.0.\nDelete %s(maybe in %s) and try again.", gamedatafilename, gdfolder);
}
totalplaytime = READUINT32(save_p);
matchesplayed = READUINT32(save_p);
gamedata->totalplaytime = READUINT32(save_p);
gamedata->matchesplayed = READUINT32(save_p);
{
// Quick & dirty hash for what mod this save file is for.
@ -4392,32 +4386,32 @@ void G_LoadGameData(void)
{
rtemp = READUINT8(save_p);
for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
emblemlocations[j+i].collected = ((rtemp >> j) & 1);
gamedata->collected[j+i] = ((rtemp >> j) & 1);
i += j;
}
for (i = 0; i < MAXEXTRAEMBLEMS;)
{
rtemp = READUINT8(save_p);
for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j)
extraemblems[j+i].collected = ((rtemp >> j) & 1);
gamedata->extraCollected[j+i] = ((rtemp >> j) & 1);
i += j;
}
for (i = 0; i < MAXUNLOCKABLES;)
{
rtemp = READUINT8(save_p);
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
unlockables[j+i].unlocked = ((rtemp >> j) & 1);
gamedata->unlocked[j+i] = ((rtemp >> j) & 1);
i += j;
}
for (i = 0; i < MAXCONDITIONSETS;)
{
rtemp = READUINT8(save_p);
for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
conditionSets[j+i].achieved = ((rtemp >> j) & 1);
gamedata->achieved[j+i] = ((rtemp >> j) & 1);
i += j;
}
timesBeaten = READUINT32(save_p);
gamedata->timesBeaten = READUINT32(save_p);
// Main records
numgamedatamapheaders = READUINT32(save_p);
@ -4469,10 +4463,10 @@ void G_LoadGameData(void)
// It used to do this much earlier, but this would cause the gamedata to
// save over itself when it I_Errors from the corruption landing point below,
// which can accidentally delete players' legitimate data if the code ever has any tiny mistakes!
gamedataloaded = true;
gamedata->loaded = true;
// Silent update unlockables in case they're out of sync with conditions
M_SilentUpdateUnlockablesAndEmblems();
M_UpdateUnlockablesAndExtraEmblems(false);
return;
@ -4498,7 +4492,7 @@ void G_SaveGameData(void)
INT32 i, j;
UINT8 btemp;
if (!gamedataloaded)
if (!gamedata->loaded)
return; // If never loaded (-nodata), don't save
if (usedCheats)
@ -4522,8 +4516,8 @@ void G_SaveGameData(void)
// Version test
WRITEUINT32(save_p, GD_VERSIONCHECK); // 4
WRITEUINT32(save_p, totalplaytime); // 4
WRITEUINT32(save_p, matchesplayed); // 4
WRITEUINT32(save_p, gamedata->totalplaytime); // 4
WRITEUINT32(save_p, gamedata->matchesplayed); // 4
WRITEUINT32(save_p, quickncasehash(timeattackfolder, 64));
// To save space, use one bit per collected/achieved/unlocked flag
@ -4531,7 +4525,7 @@ void G_SaveGameData(void)
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
btemp |= (emblemlocations[j+i].collected << j);
btemp |= (gamedata->collected[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
@ -4539,7 +4533,7 @@ void G_SaveGameData(void)
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j)
btemp |= (extraemblems[j+i].collected << j);
btemp |= (gamedata->extraCollected[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
@ -4547,7 +4541,7 @@ void G_SaveGameData(void)
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
btemp |= (unlockables[j+i].unlocked << j);
btemp |= (gamedata->unlocked[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
@ -4555,12 +4549,12 @@ void G_SaveGameData(void)
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
btemp |= (conditionSets[j+i].achieved << j);
btemp |= (gamedata->achieved[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
WRITEUINT32(save_p, timesBeaten); // 4
WRITEUINT32(save_p, gamedata->timesBeaten); // 4
// Main records
WRITEUINT32(save_p, nummapheaders); // 4

View file

@ -1494,7 +1494,7 @@ void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UI
static boolean canplaysound = true;
tic_t timetoreach = emblem->var;
if (emblem->collected)
if (gamedata->collected[(emblem-emblemlocations)])
{
emblempic[curemb] = W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_CACHE);
emblemcol[curemb] = R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE);

View file

@ -1908,7 +1908,7 @@ static void M_DrawCupPreview(INT16 y, cupheader_t *cup)
V_DrawFill(0, y, BASEVIDWIDTH, 54, 31);
if (cup && (cup->unlockrequired == -1 || unlockables[cup->unlockrequired].unlocked))
if (cup && (cup->unlockrequired == -1 || gamedata->unlocked[cup->unlockrequired]))
{
i = (cupgrid.previewanim / 82) % cup->numlevels;
while (x < BASEVIDWIDTH + pad)
@ -1946,7 +1946,7 @@ static void M_DrawCupTitle(INT16 y, cupheader_t *cup)
if (cup)
{
boolean unlocked = (cup->unlockrequired == -1 || unlockables[cup->unlockrequired].unlocked);
boolean unlocked = (cup->unlockrequired == -1 || gamedata->unlocked[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) : "???");
@ -2014,7 +2014,7 @@ void M_DrawCupSelect(void)
V_DrawScaledPatch(x, y, 0, patch);
if (iconcup->unlockrequired != -1 && !unlockables[iconcup->unlockrequired].unlocked)
if (iconcup->unlockrequired != -1 && !gamedata->unlocked[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

@ -343,8 +343,8 @@ static void M_EraseDataResponse(INT32 ch)
if (optionsmenu.erasecontext == 2)
{
// SRB2Kart: This actually needs to be done FIRST, so that you don't immediately regain playtime/matches secrets
totalplaytime = 0;
matchesplayed = 0;
gamedata->totalplaytime = 0;
gamedata->matchesplayed = 0;
}
if (optionsmenu.erasecontext != 1)
G_ClearRecords();
@ -3441,7 +3441,7 @@ static void M_LevelListFromGametype(INT16 gt)
while (cup)
{
if (cup->unlockrequired == -1 || unlockables[cup->unlockrequired].unlocked)
if (cup->unlockrequired == -1 || gamedata->unlocked[cup->unlockrequired])
{
highestid = cup->id;
if (Playing() && mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->cup == cup)
@ -3575,7 +3575,7 @@ void M_CupSelectHandler(INT32 choice)
M_SetMenuDelay(pid);
if ((!newcup)
|| (newcup && newcup->unlockrequired != -1 && !unlockables[newcup->unlockrequired].unlocked)
|| (newcup && newcup->unlockrequired != -1 && !gamedata->unlocked[newcup->unlockrequired])
|| (newcup->cachedlevels[0] == NEXTMAP_INVALID))
{
S_StartSound(NULL, sfx_s3kb2);

View file

@ -418,7 +418,7 @@ void K_CashInPowerLevels(void)
{
pr->powerlevels[powerType] = clientpowerlevels[i][powerType];
if (M_UpdateUnlockablesAndExtraEmblems())
if (M_UpdateUnlockablesAndExtraEmblems(true))
{
S_StartSound(NULL, sfx_ncitem);
}
@ -642,7 +642,7 @@ void K_PlayerForfeit(UINT8 playerNum, boolean pointLoss)
{
pr->powerlevels[powerType] = yourPower + inc;
if (M_UpdateUnlockablesAndExtraEmblems())
if (M_UpdateUnlockablesAndExtraEmblems(true))
{
S_StartSound(NULL, sfx_ncitem);
}

View file

@ -76,16 +76,16 @@ static UINT8 cheatf_warp(void)
{
if (!unlockables[i].conditionset)
continue;
if (!unlockables[i].unlocked)
if (!gamedata->unlocked[i])
{
unlockables[i].unlocked = true;
gamedata->unlocked[i] = true;
success = true;
}
}
if (success)
{
G_SaveGameData(); //G_SetUsedCheats();
G_SetUsedCheats();
S_StartSound(0, sfx_kc42);
}
@ -111,7 +111,7 @@ static UINT8 cheatf_devmode(void)
// Just unlock all the things and turn on -debug and console devmode.
G_SetUsedCheats();
for (i = 0; i < MAXUNLOCKABLES; i++)
unlockables[i].unlocked = true;
gamedata->unlocked[i] = true;
devparm = true;
cht_debug |= 0x8000;

View file

@ -24,6 +24,8 @@
#include "k_pwrlv.h"
#include "k_profiles.h"
gamedata_t *gamedata = NULL;
// Map triggers for linedef executors
// 32 triggers, one bit each
UINT32 unlocktriggers;
@ -44,6 +46,14 @@ unlockable_t unlockables[MAXUNLOCKABLES];
INT32 numemblems = 0;
INT32 numextraemblems = 0;
// Create a new gamedata_t, for start-up
void M_NewGameDataStruct(void)
{
gamedata = Z_Calloc(sizeof (gamedata_t), PU_STATIC, NULL);
M_ClearSecrets();
G_ClearRecords();
}
void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2)
{
condition_t *cond;
@ -73,7 +83,7 @@ void M_ClearConditionSet(UINT8 set)
conditionSets[set - 1].condition = NULL;
conditionSets[set - 1].numconditions = 0;
}
conditionSets[set - 1].achieved = false;
gamedata->achieved[set - 1] = false;
}
// Clear ALL secrets.
@ -87,18 +97,18 @@ void M_ClearSecrets(void)
}
for (i = 0; i < MAXEMBLEMS; ++i)
emblemlocations[i].collected = false;
gamedata->collected[i] = false;
for (i = 0; i < MAXEXTRAEMBLEMS; ++i)
extraemblems[i].collected = false;
gamedata->extraCollected[i] = false;
for (i = 0; i < MAXUNLOCKABLES; ++i)
unlockables[i].unlocked = false;
gamedata->unlocked[i] = false;
for (i = 0; i < MAXCONDITIONSETS; ++i)
conditionSets[i].achieved = false;
gamedata->achieved[i] = false;
timesBeaten = 0;
gamedata->timesBeaten = 0;
// Re-unlock any always unlocked things
M_SilentUpdateUnlockablesAndEmblems();
M_UpdateUnlockablesAndExtraEmblems(false);
}
// ----------------------
@ -109,9 +119,9 @@ UINT8 M_CheckCondition(condition_t *cn)
switch (cn->type)
{
case UC_PLAYTIME: // Requires total playing time >= x
return (totalplaytime >= (unsigned)cn->requirement);
return (gamedata->totalplaytime >= (unsigned)cn->requirement);
case UC_MATCHESPLAYED: // Requires any level completed >= x times
return (matchesplayed >= (unsigned)cn->requirement);
return (gamedata->matchesplayed >= (unsigned)cn->requirement);
case UC_POWERLEVEL: // Requires power level >= x on a certain gametype
{
UINT8 i;
@ -128,7 +138,7 @@ UINT8 M_CheckCondition(condition_t *cn)
return false;
}
case UC_GAMECLEAR: // Requires game beaten >= x times
return (timesBeaten >= (unsigned)cn->requirement);
return (gamedata->timesBeaten >= (unsigned)cn->requirement);
case UC_OVERALLTIME: // Requires overall time <= x
return (M_GotLowEnoughTime(cn->requirement));
case UC_MAPVISITED: // Requires map x to be visited
@ -152,9 +162,9 @@ UINT8 M_CheckCondition(condition_t *cn)
case UC_TOTALEMBLEMS: // Requires number of emblems >= x
return (M_GotEnoughEmblems(cn->requirement));
case UC_EMBLEM: // Requires emblem x to be obtained
return emblemlocations[cn->requirement-1].collected;
return gamedata->collected[cn->requirement-1];
case UC_EXTRAEMBLEM: // Requires extra emblem x to be obtained
return extraemblems[cn->requirement-1].collected;
return gamedata->extraCollected[cn->requirement-1];
case UC_CONDITIONSET: // requires condition set x to already be achieved
return M_Achieved(cn->requirement-1);
}
@ -196,14 +206,14 @@ void M_CheckUnlockConditions(void)
for (i = 0; i < MAXCONDITIONSETS; ++i)
{
c = &conditionSets[i];
if (!c->numconditions || c->achieved)
if (!c->numconditions || gamedata->achieved[i])
continue;
c->achieved = (M_CheckConditionSet(c));
gamedata->achieved[i] = (M_CheckConditionSet(c));
}
}
UINT8 M_UpdateUnlockablesAndExtraEmblems(void)
boolean M_UpdateUnlockablesAndExtraEmblems(boolean loud)
{
INT32 i;
char cechoText[992] = "";
@ -211,16 +221,31 @@ UINT8 M_UpdateUnlockablesAndExtraEmblems(void)
M_CheckUnlockConditions();
if (!loud)
{
// Just in case they aren't to sync
M_CheckLevelEmblems();
M_CompletionEmblems();
}
// Go through extra emblems
for (i = 0; i < numextraemblems; ++i)
{
if (extraemblems[i].collected || !extraemblems[i].conditionset)
continue;
if ((extraemblems[i].collected = M_Achieved(extraemblems[i].conditionset - 1)) != false)
if (gamedata->extraCollected[i] || !extraemblems[i].conditionset)
{
strcat(cechoText, va(M_GetText("Got \"%s\" medal!\\"), extraemblems[i].name));
++cechoLines;
continue;
}
if ((gamedata->extraCollected[i] = M_Achieved(extraemblems[i].conditionset - 1)) == false)
{
continue;
}
if (loud)
{
strcat(cechoText, va("Got \"%s\" medal!\n", extraemblems[i].name));
}
++cechoLines;
}
// Fun part: if any of those unlocked we need to go through the
@ -231,67 +256,40 @@ UINT8 M_UpdateUnlockablesAndExtraEmblems(void)
// Go through unlockables
for (i = 0; i < MAXUNLOCKABLES; ++i)
{
if (unlockables[i].unlocked || !unlockables[i].conditionset)
continue;
if ((unlockables[i].unlocked = M_Achieved(unlockables[i].conditionset - 1)) != false)
if (gamedata->unlocked[i] || !unlockables[i].conditionset)
{
if (unlockables[i].nocecho)
continue;
strcat(cechoText, va(M_GetText("\"%s\" unlocked!\\"), unlockables[i].name));
++cechoLines;
continue;
}
if ((gamedata->unlocked[i] = M_Achieved(unlockables[i].conditionset - 1)) == false)
{
continue;
}
if (unlockables[i].nocecho)
{
continue;
}
if (loud)
{
strcat(cechoText, va("\"%s\" unlocked!\n", unlockables[i].name));
}
++cechoLines;
}
// Announce
if (cechoLines)
if (cechoLines && loud)
{
char slashed[1024] = "";
for (i = 0; (i < 19) && (i < 24 - cechoLines); ++i)
slashed[i] = '\\';
slashed[i] = 0;
strcat(slashed, cechoText);
HU_SetCEchoFlags(V_YELLOWMAP);
HU_SetCEchoDuration(6);
HU_DoCEcho(slashed);
#ifdef DEVELOP
// todo make debugmode
CONS_Printf("%s\n", cechoText);
#endif
return true;
}
return false;
}
// Used when loading gamedata to make sure all unlocks are synched with conditions
void M_SilentUpdateUnlockablesAndEmblems(void)
{
INT32 i;
boolean checkAgain = false;
// Just in case they aren't to sync
M_CheckUnlockConditions();
M_CheckLevelEmblems();
// Go through extra emblems
for (i = 0; i < numextraemblems; ++i)
{
if (extraemblems[i].collected || !extraemblems[i].conditionset)
continue;
if ((extraemblems[i].collected = M_Achieved(extraemblems[i].conditionset - 1)) != false)
checkAgain = true;
}
// check again if extra emblems unlocked, blah blah, etc
if (checkAgain)
M_CheckUnlockConditions();
// Go through unlockables
for (i = 0; i < MAXUNLOCKABLES; ++i)
{
if (unlockables[i].unlocked || !unlockables[i].conditionset)
continue;
unlockables[i].unlocked = M_Achieved(unlockables[i].conditionset - 1);
}
}
// Emblem unlocking shit
UINT8 M_CheckLevelEmblems(void)
{
@ -306,7 +304,7 @@ UINT8 M_CheckLevelEmblems(void)
{
INT32 checkLevel;
if (emblemlocations[i].type < ET_TIME || emblemlocations[i].collected)
if (emblemlocations[i].type < ET_TIME || gamedata->collected[i])
continue;
checkLevel = G_MapNumber(emblemlocations[i].level);
@ -326,7 +324,7 @@ UINT8 M_CheckLevelEmblems(void)
continue;
}
emblemlocations[i].collected = res;
gamedata->collected[i] = res;
if (res)
++somethingUnlocked;
}
@ -346,7 +344,7 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa
{
INT32 checkLevel;
if (emblemlocations[i].type < ET_TIME || emblemlocations[i].collected)
if (emblemlocations[i].type < ET_TIME || gamedata->collected[i])
continue;
checkLevel = G_MapNumber(emblemlocations[i].level);
@ -363,7 +361,7 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa
res = ((mapheaderinfo[levelnum]->mapvisited & flags) == flags);
emblemlocations[i].collected = res;
gamedata->collected[i] = res;
if (res)
++somethingUnlocked;
}
@ -385,7 +383,7 @@ UINT8 M_AnySecretUnlocked(void)
for (i = 0; i < MAXUNLOCKABLES; ++i)
{
if (!unlockables[i].nocecho && unlockables[i].unlocked)
if (!unlockables[i].nocecho && gamedata->unlocked[i])
return true;
}
return false;
@ -412,7 +410,7 @@ UINT8 M_SecretUnlocked(INT32 type)
for (i = 0; i < MAXUNLOCKABLES; ++i)
{
if (unlockables[i].type == type && unlockables[i].unlocked != CHADYES)
if (unlockables[i].type == type && gamedata->unlocked[i] != CHADYES)
return !CHADYES;
}
return CHADYES;
@ -435,7 +433,7 @@ UINT8 M_MapLocked(INT32 mapnum)
if (!mapheaderinfo[mapnum-1] || mapheaderinfo[mapnum-1]->unlockrequired < 0)
return false;
if (!unlockables[mapheaderinfo[mapnum-1]->unlockrequired].unlocked)
if (!gamedata->unlocked[mapheaderinfo[mapnum-1]->unlockrequired])
return true;
return false;
@ -447,12 +445,12 @@ INT32 M_CountEmblems(void)
INT32 found = 0, i;
for (i = 0; i < numemblems; ++i)
{
if (emblemlocations[i].collected)
if (gamedata->collected[i])
found++;
}
for (i = 0; i < numextraemblems; ++i)
{
if (extraemblems[i].collected)
if (gamedata->extraCollected[i])
found++;
}
return found;
@ -469,12 +467,12 @@ UINT8 M_GotEnoughEmblems(INT32 number)
INT32 i, gottenemblems = 0;
for (i = 0; i < numemblems; ++i)
{
if (emblemlocations[i].collected)
if (gamedata->collected[i])
if (++gottenemblems >= number) return true;
}
for (i = 0; i < numextraemblems; ++i)
{
if (extraemblems[i].collected)
if (gamedata->extraCollected[i])
if (++gottenemblems >= number) return true;
}
return false;

View file

@ -52,8 +52,6 @@ typedef struct
{
UINT32 numconditions; /// <- number of conditions.
condition_t *condition; /// <- All conditionals to be checked.
UINT8 achieved; /// <- Whether this conditional has been achieved already or not.
/// (Conditional checking is skipped if true -- it's assumed you can't relock an unlockable)
} conditionset_t;
// Emblem information
@ -79,7 +77,6 @@ typedef struct
INT32 var; ///< If needed, specifies information on the target amount to achieve (or target skin)
char *stringVar; ///< String version
char hint[110]; ///< Hint for emblem hints menu
UINT8 collected; ///< Do you have this emblem?
} emblem_t;
typedef struct
{
@ -89,7 +86,6 @@ typedef struct
UINT8 showconditionset; ///< Condition set that shows this emblem.
UINT8 sprite; ///< emblem sprite to use, 0 - 25
UINT16 color; ///< skincolor to use
UINT8 collected; ///< Do you have this emblem?
} extraemblem_t;
// Unlockable information
@ -104,7 +100,6 @@ typedef struct
char *stringVar;
UINT8 nocecho;
UINT8 nochecklist;
UINT8 unlocked;
} unlockable_t;
#define SECRET_NONE 0 // Does nil. Use with levels locked by UnlockRequired
@ -135,6 +130,35 @@ typedef struct
#define MAXEXTRAEMBLEMS 16
#define MAXUNLOCKABLES 32
// GAMEDATA STRUCTURE
// Everything that would get saved in gamedata.dat
typedef struct
{
// WHENEVER OR NOT WE'RE READY TO SAVE
boolean loaded;
// CONDITION SETS ACHIEVED
boolean achieved[MAXCONDITIONSETS];
// EMBLEMS COLLECTED
boolean collected[MAXEMBLEMS];
// EXTRA EMBLEMS COLLECTED
boolean extraCollected[MAXEXTRAEMBLEMS];
// UNLOCKABLES UNLOCKED
boolean unlocked[MAXUNLOCKABLES];
// # OF TIMES THE GAME HAS BEEN BEATEN
UINT32 timesBeaten;
// PLAY TIME
UINT32 totalplaytime;
UINT32 matchesplayed;
} gamedata_t;
extern gamedata_t *gamedata;
extern conditionset_t conditionSets[MAXCONDITIONSETS];
extern emblem_t emblemlocations[MAXEMBLEMS];
extern extraemblem_t extraemblems[MAXEXTRAEMBLEMS];
@ -145,6 +169,8 @@ extern INT32 numextraemblems;
extern UINT32 unlocktriggers;
void M_NewGameDataStruct(void);
// Condition set setup
void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2);
@ -155,8 +181,7 @@ void M_ClearSecrets(void);
// Updating conditions and unlockables
void M_CheckUnlockConditions(void);
UINT8 M_CheckCondition(condition_t *cn);
UINT8 M_UpdateUnlockablesAndExtraEmblems(void);
void M_SilentUpdateUnlockablesAndEmblems(void);
boolean M_UpdateUnlockablesAndExtraEmblems(boolean silent);
UINT8 M_CheckLevelEmblems(void);
UINT8 M_CompletionEmblems(void);
@ -182,4 +207,4 @@ UINT8 M_GotLowEnoughTime(INT32 tictime);
INT32 M_UnlockableSkinNum(unlockable_t *unlock);
INT32 M_EmblemSkinNum(emblem_t *emblem);
#define M_Achieved(a) ((a) >= MAXCONDITIONSETS || conditionSets[a].achieved)
#define M_Achieved(a) ((a) >= MAXCONDITIONSETS || gamedata->achieved[a])

View file

@ -14,7 +14,7 @@
#include "doomdef.h"
#include "doomtype.h"
#include "doomstat.h" // totalplaytime
#include "m_cond.h" // gamedata->totalplaytime
#include "m_random.h"
#include "m_fixed.h"
@ -372,5 +372,5 @@ void P_ClearRandom(UINT32 seed)
*/
UINT32 M_RandomizedSeed(void)
{
return ((totalplaytime & 0xFFFF) << 16) | M_RandomFixed();
return ((gamedata->totalplaytime & 0xFFFF) << 16) | M_RandomFixed();
}

View file

@ -504,8 +504,8 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (demo.playback || special->health > MAXEMBLEMS)
return;
emblemlocations[special->health-1].collected = true;
M_UpdateUnlockablesAndExtraEmblems();
gamedata->collected[special->health-1] = true;
M_UpdateUnlockablesAndExtraEmblems(true);
G_SaveGameData();
break;
}

View file

@ -11969,7 +11969,7 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj)
emcolor = M_GetEmblemColor(&emblemlocations[j]); // workaround for compiler complaint about bad function casting
mobj->color = (UINT16)emcolor;
if (emblemlocations[j].collected)
if (gamedata->collected[j])
{
P_UnsetThingPosition(mobj);
mobj->flags |= MF_NOCLIP;

View file

@ -7567,7 +7567,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
{
mapheaderinfo[gamemap-1]->mapvisited |= MV_VISITED;
if (M_UpdateUnlockablesAndExtraEmblems())
if (M_UpdateUnlockablesAndExtraEmblems(true))
S_StartSound(NULL, sfx_ncitem);
G_SaveGameData();
}

View file

@ -1502,7 +1502,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
CONS_Debug(DBG_GAMELOGIC, "Unlockable check (sidedef %hu): bad unlockable ID %d\n", triggerline->sidenum[0], unlockid);
return false;
}
else if (!(unlockables[unlockid].unlocked))
else if (!(gamedata->unlocked[unlockid]))
return false;
}
break;
@ -2763,7 +2763,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
unlocktriggers |= 1 << trigid;
// Unlocked something?
if (M_UpdateUnlockablesAndExtraEmblems())
if (M_UpdateUnlockablesAndExtraEmblems(true))
{
S_StartSound(NULL, sfx_s3k68);
G_SaveGameData(); // only save if unlocked something

View file

@ -20,6 +20,7 @@
#include "st_stuff.h"
#include "p_polyobj.h"
#include "m_random.h"
#include "m_cond.h" // gamedata->playtime
#include "lua_script.h"
#include "lua_hook.h"
#include "m_perfstats.h"
@ -628,7 +629,7 @@ void P_Ticker(boolean run)
// Keep track of how long they've been playing!
if (!demo.playback) // Don't increment if a demo is playing.
totalplaytime++;
gamedata->totalplaytime++;
// formality so kitemcap gets updated properly each frame.
P_RunKartItems();

View file

@ -171,7 +171,7 @@ UINT8 *R_GetSkinAvailabilities(boolean demolock)
if (unlockables[i].type != SECRET_SKIN)
continue;
if (unlockables[i].unlocked != true && !demolock)
if (gamedata->unlocked[i] != true && !demolock)
continue;
skinid = M_UnlockableSkinNum(&unlockables[i]);
@ -250,7 +250,7 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins)
}
// Use the unlockables table directly
return (boolean)(unlockables[i].unlocked);
return (boolean)(gamedata->unlocked[i]);
}
// returns true if the skin name is found (loaded from pwad)