Online emblems

Currently, emblems share with everyone. Will add an option to toggle this.
This commit is contained in:
Sally Coolatta 2022-02-27 16:52:05 -05:00
parent d6d424f102
commit 49fa46d80e
26 changed files with 824 additions and 450 deletions

View file

@ -3645,6 +3645,9 @@ void SV_ResetServer(void)
CV_RevertNetVars();
// Ensure synched when creating a new server
M_CopyGameData(serverGamedata, clientGamedata);
DEBFILE("\n-=-=-=-=-=-=-= Server Reset =-=-=-=-=-=-=-\n\n");
}

View file

@ -1350,6 +1350,9 @@ void D_SRB2Main(void)
CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n");
Z_Init();
clientGamedata = M_NewGameDataStruct();
serverGamedata = M_NewGameDataStruct();
// Do this up here so that WADs loaded through the command line can use ExecCfg
COM_Init();
@ -1479,7 +1482,9 @@ void D_SRB2Main(void)
// confusion issues when loading mods.
strlcpy(gamedatafilename, M_GetNextParm(), sizeof gamedatafilename);
}
G_LoadGameData();
G_LoadGameData(clientGamedata);
M_CopyGameData(serverGamedata, clientGamedata);
#if defined (__unix__) || defined (UNIXCOMMON) || defined (HAVE_SDL)
VID_PrepareModeList(); // Regenerate Modelist according to cv_fullscreen
@ -1710,7 +1715,7 @@ void D_SRB2Main(void)
// ... unless you're in a dedicated server. Yes, technically this means you can view any level by
// running a dedicated server and joining it yourself, but that's better than making dedicated server's
// lives hell.
else if (!dedicated && M_MapLocked(pstartmap))
else if (!dedicated && M_MapLocked(pstartmap, serverGamedata))
I_Error("You need to unlock this level before you can warp to it!\n");
else
{

View file

@ -2036,7 +2036,7 @@ static void Command_Map_f(void)
// ... unless you're in a dedicated server. Yes, technically this means you can view any level by
// running a dedicated server and joining it yourself, but that's better than making dedicated server's
// lives hell.
if (!dedicated && M_MapLocked(newmapnum))
if (!dedicated && M_MapLocked(newmapnum, serverGamedata))
{
CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n"));
Z_Free(realmapname);
@ -3945,18 +3945,12 @@ void ItemFinder_OnChange(void)
if (!cv_itemfinder.value)
return; // it's fine.
if (!M_SecretUnlocked(SECRET_ITEMFINDER))
if (!M_SecretUnlocked(SECRET_ITEMFINDER, clientGamedata))
{
CONS_Printf(M_GetText("You haven't earned this yet.\n"));
CV_StealthSetValue(&cv_itemfinder, 0);
return;
}
else if (netgame || multiplayer)
{
CONS_Printf(M_GetText("This only works in single player.\n"));
CV_StealthSetValue(&cv_itemfinder, 0);
return;
}
}
/** Deals with a pointlimit change by printing the change to the console.
@ -4305,7 +4299,7 @@ void D_GameTypeChanged(INT32 lastgametype)
static void Ringslinger_OnChange(void)
{
if (!M_SecretUnlocked(SECRET_PANDORA) && !netgame && cv_ringslinger.value && !cv_debug)
if (!M_SecretUnlocked(SECRET_PANDORA, serverGamedata) && !netgame && cv_ringslinger.value && !cv_debug)
{
CONS_Printf(M_GetText("You haven't earned this yet.\n"));
CV_StealthSetValue(&cv_ringslinger, 0);
@ -4318,7 +4312,7 @@ static void Ringslinger_OnChange(void)
static void Gravity_OnChange(void)
{
if (!M_SecretUnlocked(SECRET_PANDORA) && !netgame && !cv_debug
if (!M_SecretUnlocked(SECRET_PANDORA, serverGamedata) && !netgame && !cv_debug
&& strcmp(cv_gravity.string, cv_gravity.defaultvalue))
{
CONS_Printf(M_GetText("You haven't earned this yet.\n"));

View file

@ -3849,7 +3849,7 @@ void readmaincfg(MYFILE *f)
if (!GoodDataFileName(word2))
I_Error("Maincfg: bad data file name '%s'\n", word2);
G_SaveGameData();
G_SaveGameData(clientGamedata);
strlcpy(gamedatafilename, word2, sizeof (gamedatafilename));
strlwr(gamedatafilename);
savemoddata = true;

View file

@ -575,7 +575,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
} // end while
if (gamedataadded)
G_LoadGameData();
G_LoadGameData(clientGamedata);
if (gamestate == GS_TITLESCREEN)
{

View file

@ -132,8 +132,6 @@ extern INT32 postimgparam2;
extern INT32 viewwindowx, viewwindowy;
extern INT32 viewwidth, scaledviewwidth;
extern boolean gamedataloaded;
// Player taking events, and displaying.
extern INT32 consoleplayer;
extern INT32 displayplayer;
@ -495,8 +493,6 @@ typedef struct
extern tolinfo_t TYPEOFLEVEL[NUMTOLNAMES];
extern UINT32 lastcustomtol;
extern tic_t totalplaytime;
extern boolean stagefailed;
// Emeralds stored as bits to throw savegame hackers off.
@ -515,52 +511,6 @@ extern INT32 luabanks[NUM_LUABANKS];
extern INT32 nummaprings; //keep track of spawned rings/coins
/** Time attack information, currently a very small structure.
*/
typedef struct
{
tic_t time; ///< Time in which the level was finished.
UINT32 score; ///< Score when the level was finished.
UINT16 rings; ///< Rings when the level was finished.
} recorddata_t;
/** Setup for one NiGHTS map.
* These are dynamically allocated because I am insane
*/
#define GRADE_F 0
#define GRADE_E 1
#define GRADE_D 2
#define GRADE_C 3
#define GRADE_B 4
#define GRADE_A 5
#define GRADE_S 6
typedef struct
{
// 8 mares, 1 overall (0)
UINT8 nummares;
UINT32 score[9];
UINT8 grade[9];
tic_t time[9];
} nightsdata_t;
extern nightsdata_t *nightsrecords[NUMMAPS];
extern recorddata_t *mainrecords[NUMMAPS];
// mapvisited is now a set of flags that says what we've done in the map.
#define MV_VISITED 1
#define MV_BEATEN 2
#define MV_ALLEMERALDS 4
#define MV_ULTIMATE 8
#define MV_PERFECT 16
#define MV_PERFECTRA 32
#define MV_MAX 63 // used in gamedata check, update whenever MV's are added
#define MV_MP 128
extern UINT8 mapvisited[NUMMAPS];
// Temporary holding place for nights data for the current map
extern nightsdata_t ntemprecords;
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
@ -616,10 +566,6 @@ extern INT32 cheats;
extern tic_t hidetime;
extern UINT32 timesBeaten; // # of times the game has been beaten.
extern UINT32 timesBeatenWithEmeralds;
extern UINT32 timesBeatenUltimate;
// ===========================
// Internal parameters, fixed.
// ===========================

View file

@ -1411,7 +1411,7 @@ boolean F_CreditResponder(event_t *event)
break;
}
if (!(timesBeaten) && !(netgame || multiplayer) && !cv_debug)
if (!(serverGamedata->timesBeaten) && !(netgame || multiplayer) && !cv_debug)
return false;
if (event->type != ev_keydown)
@ -1573,23 +1573,17 @@ void F_GameEvaluationDrawer(void)
#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)
{
INT32 startcoord = 32;
V_DrawString(8, 16, V_YELLOWMAP, "Unlocked:");
if (netgame)
V_DrawString(8, 96, V_YELLOWMAP, "Multiplayer games\ncan't unlock\nextras!");
else
for (i = 0; i < MAXUNLOCKABLES; i++)
{
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].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;
}
if (clientGamedata->unlocked[i])
V_DrawString(8, startcoord, 0, unlockables[i].name);
startcoord += 8;
}
}
}
@ -1657,18 +1651,27 @@ void F_GameEvaluationTicker(void)
}
else
{
++timesBeaten;
serverGamedata->timesBeaten++;
clientGamedata->timesBeaten++;
if (ALL7EMERALDS(emeralds))
++timesBeatenWithEmeralds;
{
serverGamedata->timesBeatenWithEmeralds++;
clientGamedata->timesBeatenWithEmeralds++;
}
if (ultimatemode)
++timesBeatenUltimate;
{
serverGamedata->timesBeatenUltimate++;
clientGamedata->timesBeatenUltimate++;
}
if (M_UpdateUnlockablesAndExtraEmblems())
M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
if (M_UpdateUnlockablesAndExtraEmblems(clientGamedata))
S_StartSound(NULL, sfx_s3k68);
G_SaveGameData();
G_SaveGameData(clientGamedata);
}
}
}
@ -2183,7 +2186,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, ((serverGamedata->timesBeaten || finalecount >= STOPPINGPOINT-TICRATE) ? V_PURPLEMAP : V_BLUEMAP)|(trans<<V_ALPHASHIFT), " [S] ===>");
}
if (finalecount > STOPPINGPOINT-(20+(2*TICRATE)))

View file

@ -185,18 +185,6 @@ INT32 tokenbits; // Used for setting token bits
// Old Special Stage
INT32 sstimer; // Time allotted in the special stage
tic_t totalplaytime;
boolean gamedataloaded = false;
// Time attack data for levels
// These are dynamically allocated for space reasons now
recorddata_t *mainrecords[NUMMAPS] = {NULL};
nightsdata_t *nightsrecords[NUMMAPS] = {NULL};
UINT8 mapvisited[NUMMAPS];
// Temporary holding place for nights data for the current map
nightsdata_t ntemprecords;
UINT32 bluescore, redscore; // CTF and Team Match team scores
// ring count... for PERFECT!
@ -252,11 +240,6 @@ INT32 cheats; //for multiplayer cheat commands
tic_t hidetime;
// Grading
UINT32 timesBeaten;
UINT32 timesBeatenWithEmeralds;
UINT32 timesBeatenUltimate;
typedef struct joystickvector2_s
{
INT32 xaxis;
@ -452,86 +435,86 @@ INT16 rw_maximums[NUM_WEAPONS] =
};
// Allocation for time and nights data
void G_AllocMainRecordData(INT16 i)
void G_AllocMainRecordData(INT16 i, gamedata_t *data)
{
if (!mainrecords[i])
mainrecords[i] = Z_Malloc(sizeof(recorddata_t), PU_STATIC, NULL);
memset(mainrecords[i], 0, sizeof(recorddata_t));
if (!data->mainrecords[i])
data->mainrecords[i] = Z_Malloc(sizeof(recorddata_t), PU_STATIC, NULL);
memset(data->mainrecords[i], 0, sizeof(recorddata_t));
}
void G_AllocNightsRecordData(INT16 i)
void G_AllocNightsRecordData(INT16 i, gamedata_t *data)
{
if (!nightsrecords[i])
nightsrecords[i] = Z_Malloc(sizeof(nightsdata_t), PU_STATIC, NULL);
memset(nightsrecords[i], 0, sizeof(nightsdata_t));
if (!data->nightsrecords[i])
data->nightsrecords[i] = Z_Malloc(sizeof(nightsdata_t), PU_STATIC, NULL);
memset(data->nightsrecords[i], 0, sizeof(nightsdata_t));
}
// MAKE SURE YOU SAVE DATA BEFORE CALLING THIS
void G_ClearRecords(void)
void G_ClearRecords(gamedata_t *data)
{
INT16 i;
for (i = 0; i < NUMMAPS; ++i)
{
if (mainrecords[i])
if (data->mainrecords[i])
{
Z_Free(mainrecords[i]);
mainrecords[i] = NULL;
Z_Free(data->mainrecords[i]);
data->mainrecords[i] = NULL;
}
if (nightsrecords[i])
if (data->nightsrecords[i])
{
Z_Free(nightsrecords[i]);
nightsrecords[i] = NULL;
Z_Free(data->nightsrecords[i]);
data->nightsrecords[i] = NULL;
}
}
}
// For easy retrieval of records
UINT32 G_GetBestScore(INT16 map)
UINT32 G_GetBestScore(INT16 map, gamedata_t *data)
{
if (!mainrecords[map-1])
if (!data->mainrecords[map-1])
return 0;
return mainrecords[map-1]->score;
return data->mainrecords[map-1]->score;
}
tic_t G_GetBestTime(INT16 map)
tic_t G_GetBestTime(INT16 map, gamedata_t *data)
{
if (!mainrecords[map-1] || mainrecords[map-1]->time <= 0)
if (!data->mainrecords[map-1] || data->mainrecords[map-1]->time <= 0)
return (tic_t)UINT32_MAX;
return mainrecords[map-1]->time;
return data->mainrecords[map-1]->time;
}
UINT16 G_GetBestRings(INT16 map)
UINT16 G_GetBestRings(INT16 map, gamedata_t *data)
{
if (!mainrecords[map-1])
if (!data->mainrecords[map-1])
return 0;
return mainrecords[map-1]->rings;
return data->mainrecords[map-1]->rings;
}
UINT32 G_GetBestNightsScore(INT16 map, UINT8 mare)
UINT32 G_GetBestNightsScore(INT16 map, UINT8 mare, gamedata_t *data)
{
if (!nightsrecords[map-1])
if (!data->nightsrecords[map-1])
return 0;
return nightsrecords[map-1]->score[mare];
return data->nightsrecords[map-1]->score[mare];
}
tic_t G_GetBestNightsTime(INT16 map, UINT8 mare)
tic_t G_GetBestNightsTime(INT16 map, UINT8 mare, gamedata_t *data)
{
if (!nightsrecords[map-1] || nightsrecords[map-1]->time[mare] <= 0)
if (!data->nightsrecords[map-1] || data->nightsrecords[map-1]->time[mare] <= 0)
return (tic_t)UINT32_MAX;
return nightsrecords[map-1]->time[mare];
return data->nightsrecords[map-1]->time[mare];
}
UINT8 G_GetBestNightsGrade(INT16 map, UINT8 mare)
UINT8 G_GetBestNightsGrade(INT16 map, UINT8 mare, gamedata_t *data)
{
if (!nightsrecords[map-1])
if (!data->nightsrecords[map-1])
return 0;
return nightsrecords[map-1]->grade[mare];
return data->nightsrecords[map-1]->grade[mare];
}
// For easy adding of NiGHTS records
@ -553,7 +536,7 @@ void G_AddTempNightsRecords(UINT32 pscore, tic_t ptime, UINT8 mare)
// Update replay files/data, etc. for Record Attack
// See G_SetNightsRecords for NiGHTS Attack.
//
static void G_UpdateRecordReplays(void)
static void G_UpdateRecordReplays(gamedata_t *data)
{
const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
char *gpath;
@ -561,17 +544,17 @@ static void G_UpdateRecordReplays(void)
UINT8 earnedEmblems;
// Record new best time
if (!mainrecords[gamemap-1])
G_AllocMainRecordData(gamemap-1);
if (!data->mainrecords[gamemap-1])
G_AllocMainRecordData(gamemap-1, data);
if (players[consoleplayer].score > mainrecords[gamemap-1]->score)
mainrecords[gamemap-1]->score = players[consoleplayer].score;
if (players[consoleplayer].score > data->mainrecords[gamemap-1]->score)
data->mainrecords[gamemap-1]->score = players[consoleplayer].score;
if ((mainrecords[gamemap-1]->time == 0) || (players[consoleplayer].realtime < mainrecords[gamemap-1]->time))
mainrecords[gamemap-1]->time = players[consoleplayer].realtime;
if ((data->mainrecords[gamemap-1]->time == 0) || (players[consoleplayer].realtime < data->mainrecords[gamemap-1]->time))
data->mainrecords[gamemap-1]->time = players[consoleplayer].realtime;
if ((UINT16)(players[consoleplayer].rings) > mainrecords[gamemap-1]->rings)
mainrecords[gamemap-1]->rings = (UINT16)(players[consoleplayer].rings);
if ((UINT16)(players[consoleplayer].rings) > data->mainrecords[gamemap-1]->rings)
data->mainrecords[gamemap-1]->rings = (UINT16)(players[consoleplayer].rings);
// Save demo!
bestdemo[255] = '\0';
@ -627,14 +610,14 @@ static void G_UpdateRecordReplays(void)
free(gpath);
// Check emblems when level data is updated
if ((earnedEmblems = M_CheckLevelEmblems()))
if ((earnedEmblems = M_CheckLevelEmblems(data)))
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for Record Attack records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
// Update timeattack menu's replay availability.
Nextmap_OnChange();
}
void G_SetNightsRecords(void)
void G_SetNightsRecords(gamedata_t *data)
{
INT32 i;
UINT32 totalscore = 0;
@ -675,9 +658,9 @@ void G_SetNightsRecords(void)
{
nightsdata_t *maprecords;
if (!nightsrecords[gamemap-1])
G_AllocNightsRecordData(gamemap-1);
maprecords = nightsrecords[gamemap-1];
if (!data->nightsrecords[gamemap-1])
G_AllocNightsRecordData(gamemap-1, data);
maprecords = data->nightsrecords[gamemap-1];
if (maprecords->nummares != ntemprecords.nummares)
maprecords->nummares = ntemprecords.nummares;
@ -739,7 +722,7 @@ void G_SetNightsRecords(void)
}
free(gpath);
if ((earnedEmblems = M_CheckLevelEmblems()))
if ((earnedEmblems = M_CheckLevelEmblems(data)))
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for NiGHTS records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
// If the mare count changed, this will update the score display
@ -3838,7 +3821,7 @@ static INT16 RandMap(UINT32 tolflags, INT16 pprevmap)
for (ix = 0; ix < NUMMAPS; ix++)
if (mapheaderinfo[ix] && (mapheaderinfo[ix]->typeoflevel & tolflags) == tolflags
&& ix != pprevmap // Don't pick the same map.
&& (dedicated || !M_MapLocked(ix+1)) // Don't pick locked maps.
&& (dedicated || !M_MapLocked(ix+1, serverGamedata)) // Don't pick locked maps.
)
okmaps[numokmaps++] = ix;
@ -3855,7 +3838,7 @@ static INT16 RandMap(UINT32 tolflags, INT16 pprevmap)
//
// G_UpdateVisited
//
static void G_UpdateVisited(void)
static void G_UpdateVisited(gamedata_t *data, boolean silent)
{
boolean spec = G_IsSpecialStage(gamemap);
// Update visitation flags?
@ -3865,30 +3848,37 @@ static void G_UpdateVisited(void)
UINT8 earnedEmblems;
// Update visitation flags
mapvisited[gamemap-1] |= MV_BEATEN;
data->mapvisited[gamemap-1] |= MV_BEATEN;
// eh, what the hell
if (ultimatemode)
mapvisited[gamemap-1] |= MV_ULTIMATE;
data->mapvisited[gamemap-1] |= MV_ULTIMATE;
// may seem incorrect but IS possible in what the main game uses as mp special stages, and nummaprings will be -1 in NiGHTS
if (nummaprings > 0 && players[consoleplayer].rings >= nummaprings)
{
mapvisited[gamemap-1] |= MV_PERFECT;
data->mapvisited[gamemap-1] |= MV_PERFECT;
if (modeattacking)
mapvisited[gamemap-1] |= MV_PERFECTRA;
data->mapvisited[gamemap-1] |= MV_PERFECTRA;
}
if (!spec)
{
// not available to special stages because they can only really be done in one order in an unmodified game, so impossible for first six and trivial for seventh
if (ALL7EMERALDS(emeralds))
mapvisited[gamemap-1] |= MV_ALLEMERALDS;
data->mapvisited[gamemap-1] |= MV_ALLEMERALDS;
}
if (modeattacking == ATTACKING_RECORD)
G_UpdateRecordReplays();
else if (modeattacking == ATTACKING_NIGHTS)
G_SetNightsRecords();
if (silent)
{
M_CheckLevelEmblems(data);
}
else
{
if (modeattacking == ATTACKING_RECORD)
G_UpdateRecordReplays(data);
else if (modeattacking == ATTACKING_NIGHTS)
G_SetNightsRecords(data);
}
if ((earnedEmblems = M_CompletionEmblems()))
if ((earnedEmblems = M_CompletionEmblems(data)) && !silent)
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
}
}
@ -4091,7 +4081,8 @@ static void G_DoCompleted(void)
if ((skipstats && !modeattacking) || (modeattacking && stagefailed) || (intertype == int_none))
{
G_UpdateVisited();
G_UpdateVisited(serverGamedata, true);
G_UpdateVisited(clientGamedata, false);
G_HandleSaveLevel();
G_AfterIntermission();
}
@ -4100,7 +4091,8 @@ static void G_DoCompleted(void)
G_SetGamestate(GS_INTERMISSION);
Y_StartIntermission();
Y_LoadIntermissionData();
G_UpdateVisited();
G_UpdateVisited(serverGamedata, true);
G_UpdateVisited(clientGamedata, false);
G_HandleSaveLevel();
}
}
@ -4287,7 +4279,7 @@ void G_LoadGameSettings(void)
// G_LoadGameData
// Loads the main data file, which stores information such as emblems found, etc.
void G_LoadGameData(void)
void G_LoadGameData(gamedata_t *data)
{
size_t length;
INT32 i, j;
@ -4304,13 +4296,13 @@ void G_LoadGameData(void)
INT32 curmare;
// Stop saving, until we successfully load it again.
gamedataloaded = false;
data->loaded = false;
// Clear things so previously read gamedata doesn't transfer
// to new gamedata
G_ClearRecords(); // main and nights records
M_ClearSecrets(); // emblems, unlocks, maps visited, etc
totalplaytime = 0; // total play time (separate from all)
G_ClearRecords(data); // main and nights records
M_ClearSecrets(data); // emblems, unlocks, maps visited, etc
data->totalplaytime = 0; // total play time (separate from all)
if (M_CheckParm("-nodata"))
{
@ -4321,7 +4313,7 @@ void G_LoadGameData(void)
if (M_CheckParm("-resetdata"))
{
// Don't load, but do save. (essentially, reset)
gamedataloaded = true;
data->loaded = true;
return;
}
@ -4329,7 +4321,7 @@ void G_LoadGameData(void)
if (!length)
{
// No gamedata. We can save a new one.
gamedataloaded = true;
data->loaded = true;
return;
}
@ -4352,7 +4344,7 @@ void G_LoadGameData(void)
I_Error("Game data is from another version of SRB2.\nDelete %s(maybe in %s) and try again.", gamedatafilename, gdfolder);
}
totalplaytime = READUINT32(save_p);
data->totalplaytime = READUINT32(save_p);
#ifdef COMPAT_GAMEDATA_ID
if (versionID == COMPAT_GAMEDATA_ID)
@ -4386,7 +4378,7 @@ void G_LoadGameData(void)
// TODO put another cipher on these things? meh, I don't care...
for (i = 0; i < NUMMAPS; i++)
if ((mapvisited[i] = READUINT8(save_p)) > MV_MAX)
if ((data->mapvisited[i] = READUINT8(save_p)) > MV_MAX)
goto datacorrupt;
// To save space, use one bit per collected/achieved/unlocked flag
@ -4394,34 +4386,34 @@ void G_LoadGameData(void)
{
rtemp = READUINT8(save_p);
for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
emblemlocations[j+i].collected = ((rtemp >> j) & 1);
data->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);
data->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);
data->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);
data->achieved[j+i] = ((rtemp >> j) & 1);
i += j;
}
timesBeaten = READUINT32(save_p);
timesBeatenWithEmeralds = READUINT32(save_p);
timesBeatenUltimate = READUINT32(save_p);
data->timesBeaten = READUINT32(save_p);
data->timesBeatenWithEmeralds = READUINT32(save_p);
data->timesBeatenUltimate = READUINT32(save_p);
// Main records
for (i = 0; i < NUMMAPS; ++i)
@ -4436,10 +4428,10 @@ void G_LoadGameData(void)
if (recscore || rectime || recrings)
{
G_AllocMainRecordData((INT16)i);
mainrecords[i]->score = recscore;
mainrecords[i]->time = rectime;
mainrecords[i]->rings = recrings;
G_AllocMainRecordData((INT16)i, data);
data->mainrecords[i]->score = recscore;
data->mainrecords[i]->time = rectime;
data->mainrecords[i]->rings = recrings;
}
}
@ -4449,19 +4441,21 @@ void G_LoadGameData(void)
if ((recmares = READUINT8(save_p)) == 0)
continue;
G_AllocNightsRecordData((INT16)i);
G_AllocNightsRecordData((INT16)i, data);
for (curmare = 0; curmare < (recmares+1); ++curmare)
{
nightsrecords[i]->score[curmare] = READUINT32(save_p);
nightsrecords[i]->grade[curmare] = READUINT8(save_p);
nightsrecords[i]->time[curmare] = (tic_t)READUINT32(save_p);
data->nightsrecords[i]->score[curmare] = READUINT32(save_p);
data->nightsrecords[i]->grade[curmare] = READUINT8(save_p);
data->nightsrecords[i]->time[curmare] = (tic_t)READUINT32(save_p);
if (nightsrecords[i]->grade[curmare] > GRADE_S)
if (data->nightsrecords[i]->grade[curmare] > GRADE_S)
{
goto datacorrupt;
}
}
nightsrecords[i]->nummares = recmares;
data->nightsrecords[i]->nummares = recmares;
}
// done
@ -4472,10 +4466,11 @@ 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;
data->loaded = true;
// Silent update unlockables in case they're out of sync with conditions
M_SilentUpdateUnlockablesAndEmblems();
M_SilentUpdateUnlockablesAndEmblems(data);
M_SilentUpdateSkinAvailabilites();
return;
@ -4495,7 +4490,7 @@ void G_LoadGameData(void)
// G_SaveGameData
// Saves the main data file, which stores information such as emblems found, etc.
void G_SaveGameData(void)
void G_SaveGameData(gamedata_t *data)
{
size_t length;
INT32 i, j;
@ -4503,7 +4498,7 @@ void G_SaveGameData(void)
INT32 curmare;
if (!gamedataloaded)
if (!data->loaded)
return; // If never loaded (-nodata), don't save
save_p = savebuffer = (UINT8 *)malloc(GAMEDATASIZE);
@ -4523,20 +4518,20 @@ void G_SaveGameData(void)
// Version test
WRITEUINT32(save_p, GAMEDATA_ID);
WRITEUINT32(save_p, totalplaytime);
WRITEUINT32(save_p, data->totalplaytime);
WRITEUINT32(save_p, quickncasehash(timeattackfolder, sizeof timeattackfolder));
// TODO put another cipher on these things? meh, I don't care...
for (i = 0; i < NUMMAPS; i++)
WRITEUINT8(save_p, (mapvisited[i] & MV_MAX));
WRITEUINT8(save_p, (data->mapvisited[i] & MV_MAX));
// To save space, use one bit per collected/achieved/unlocked flag
for (i = 0; i < MAXEMBLEMS;)
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
btemp |= (emblemlocations[j+i].collected << j);
btemp |= (data->collected[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
@ -4544,7 +4539,7 @@ void G_SaveGameData(void)
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j)
btemp |= (extraemblems[j+i].collected << j);
btemp |= (data->extraCollected[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
@ -4552,7 +4547,7 @@ void G_SaveGameData(void)
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
btemp |= (unlockables[j+i].unlocked << j);
btemp |= (data->unlocked[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
@ -4560,23 +4555,23 @@ void G_SaveGameData(void)
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
btemp |= (conditionSets[j+i].achieved << j);
btemp |= (data->achieved[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
WRITEUINT32(save_p, timesBeaten);
WRITEUINT32(save_p, timesBeatenWithEmeralds);
WRITEUINT32(save_p, timesBeatenUltimate);
WRITEUINT32(save_p, data->timesBeaten);
WRITEUINT32(save_p, data->timesBeatenWithEmeralds);
WRITEUINT32(save_p, data->timesBeatenUltimate);
// Main records
for (i = 0; i < NUMMAPS; i++)
{
if (mainrecords[i])
if (data->mainrecords[i])
{
WRITEUINT32(save_p, mainrecords[i]->score);
WRITEUINT32(save_p, mainrecords[i]->time);
WRITEUINT16(save_p, mainrecords[i]->rings);
WRITEUINT32(save_p, data->mainrecords[i]->score);
WRITEUINT32(save_p, data->mainrecords[i]->time);
WRITEUINT16(save_p, data->mainrecords[i]->rings);
}
else
{
@ -4590,19 +4585,19 @@ void G_SaveGameData(void)
// NiGHTS records
for (i = 0; i < NUMMAPS; i++)
{
if (!nightsrecords[i] || !nightsrecords[i]->nummares)
if (!data->nightsrecords[i] || !data->nightsrecords[i]->nummares)
{
WRITEUINT8(save_p, 0);
continue;
}
WRITEUINT8(save_p, nightsrecords[i]->nummares);
WRITEUINT8(save_p, data->nightsrecords[i]->nummares);
for (curmare = 0; curmare < (nightsrecords[i]->nummares + 1); ++curmare)
for (curmare = 0; curmare < (data->nightsrecords[i]->nummares + 1); ++curmare)
{
WRITEUINT32(save_p, nightsrecords[i]->score[curmare]);
WRITEUINT8(save_p, nightsrecords[i]->grade[curmare]);
WRITEUINT32(save_p, nightsrecords[i]->time[curmare]);
WRITEUINT32(save_p, data->nightsrecords[i]->score[curmare]);
WRITEUINT8(save_p, data->nightsrecords[i]->grade[curmare]);
WRITEUINT32(save_p, data->nightsrecords[i]->time[curmare]);
}
}

View file

@ -19,6 +19,7 @@
#include "d_event.h"
#include "g_demo.h"
#include "m_cheat.h" // objectplacing
#include "m_cond.h"
extern char gamedatafilename[64];
extern char timeattackfolder[64];
@ -183,7 +184,7 @@ boolean G_IsTitleCardAvailable(void);
// Can be called by the startup code or M_Responder, calls P_SetupLevel.
void G_LoadGame(UINT32 slot, INT16 mapoverride);
void G_SaveGameData(void);
void G_SaveGameData(gamedata_t *data);
void G_SaveGame(UINT32 slot, INT16 mapnum);
@ -239,7 +240,7 @@ void G_SetModeAttackRetryFlag(void);
void G_ClearModeAttackRetryFlag(void);
boolean G_GetModeAttackRetryFlag(void);
void G_LoadGameData(void);
void G_LoadGameData(gamedata_t *data);
void G_LoadGameSettings(void);
void G_SetGameModified(boolean silent);
@ -248,19 +249,19 @@ void G_SetUsedCheats(boolean silent);
void G_SetGamestate(gamestate_t newstate);
// Gamedata record shit
void G_AllocMainRecordData(INT16 i);
void G_AllocNightsRecordData(INT16 i);
void G_ClearRecords(void);
void G_AllocMainRecordData(INT16 i, gamedata_t *data);
void G_AllocNightsRecordData(INT16 i, gamedata_t *data);
void G_ClearRecords(gamedata_t *data);
UINT32 G_GetBestScore(INT16 map);
tic_t G_GetBestTime(INT16 map);
UINT16 G_GetBestRings(INT16 map);
UINT32 G_GetBestNightsScore(INT16 map, UINT8 mare);
tic_t G_GetBestNightsTime(INT16 map, UINT8 mare);
UINT8 G_GetBestNightsGrade(INT16 map, UINT8 mare);
UINT32 G_GetBestScore(INT16 map, gamedata_t *data);
tic_t G_GetBestTime(INT16 map, gamedata_t *data);
UINT16 G_GetBestRings(INT16 map, gamedata_t *data);
UINT32 G_GetBestNightsScore(INT16 map, UINT8 mare, gamedata_t *data);
tic_t G_GetBestNightsTime(INT16 map, UINT8 mare, gamedata_t *data);
UINT8 G_GetBestNightsGrade(INT16 map, UINT8 mare, gamedata_t *data);
void G_AddTempNightsRecords(UINT32 pscore, tic_t ptime, UINT8 mare);
void G_SetNightsRecords(void);
void G_SetNightsRecords(gamedata_t *data);
FUNCMATH INT32 G_TicsToHours(tic_t tics);
FUNCMATH INT32 G_TicsToMinutes(tic_t tics, boolean full);

View file

@ -2996,7 +2996,7 @@ static void HU_DrawCoopOverlay(void)
if (LUA_HudEnabled(hud_tabemblems))
{
V_DrawString(160, 144, 0, va("- %d/%d", M_CountEmblems(), numemblems+numextraemblems));
V_DrawString(160, 144, 0, va("- %d/%d", M_CountEmblems(clientGamedata), numemblems+numextraemblems));
V_DrawScaledPatch(128, 144 - emblemicon->height/4, 0, emblemicon);
}

View file

@ -102,7 +102,7 @@ static UINT8 cheatf_devmode(void)
// Just unlock all the things and turn on -debug and console devmode.
G_SetUsedCheats(false);
for (i = 0; i < MAXUNLOCKABLES; i++)
unlockables[i].unlocked = true;
clientGamedata->unlocked[i] = true;
devparm = true;
cv_debug |= 0x8000;
@ -238,7 +238,7 @@ boolean cht_Responder(event_t *ev)
}
// Console cheat commands rely on these a lot...
#define REQUIRE_PANDORA if (!M_SecretUnlocked(SECRET_PANDORA) && !cv_debug)\
#define REQUIRE_PANDORA if (!M_SecretUnlocked(SECRET_PANDORA, serverGamedata) && !cv_debug)\
{ CONS_Printf(M_GetText("You haven't earned this yet.\n")); return; }
#define REQUIRE_DEVMODE if (!cv_debug)\

View file

@ -21,6 +21,9 @@
#include "r_skins.h" // numskins
#include "r_draw.h" // R_GetColorByName
gamedata_t *clientGamedata; // Our gamedata
gamedata_t *serverGamedata; // Server's gamedata
// Map triggers for linedef executors
// 32 triggers, one bit each
UINT32 unlocktriggers;
@ -41,6 +44,70 @@ unlockable_t unlockables[MAXUNLOCKABLES];
INT32 numemblems = 0;
INT32 numextraemblems = 0;
// Temporary holding place for nights data for the current map
nightsdata_t ntemprecords;
// Create a new gamedata_t, for start-up
gamedata_t *M_NewGameDataStruct(void)
{
gamedata_t *data = Z_Calloc(sizeof (*data), PU_STATIC, NULL);
M_ClearSecrets(data);
G_ClearRecords(data);
return data;
}
void M_CopyGameData(gamedata_t *dest, gamedata_t *src)
{
INT32 i, j;
M_ClearSecrets(dest);
G_ClearRecords(dest);
dest->loaded = src->loaded;
dest->totalplaytime = src->totalplaytime;
dest->timesBeaten = src->timesBeaten;
dest->timesBeatenWithEmeralds = src->timesBeatenWithEmeralds;
dest->timesBeatenUltimate = src->timesBeatenUltimate;
memcpy(dest->achieved, src->achieved, sizeof(dest->achieved));
memcpy(dest->collected, src->collected, sizeof(dest->collected));
memcpy(dest->extraCollected, src->extraCollected, sizeof(dest->extraCollected));
memcpy(dest->unlocked, src->unlocked, sizeof(dest->unlocked));
memcpy(dest->mapvisited, src->mapvisited, sizeof(dest->mapvisited));
// Main records
for (i = 0; i < NUMMAPS; ++i)
{
if (!src->mainrecords[i])
continue;
G_AllocMainRecordData((INT16)i, dest);
dest->mainrecords[i]->score = src->mainrecords[i]->score;
dest->mainrecords[i]->time = src->mainrecords[i]->time;
dest->mainrecords[i]->rings = src->mainrecords[i]->rings;
}
// Nights records
for (i = 0; i < NUMMAPS; ++i)
{
if (!src->nightsrecords[i] || !src->nightsrecords[i]->nummares)
continue;
G_AllocNightsRecordData((INT16)i, dest);
for (j = 0; j < (src->nightsrecords[i]->nummares + 1); j++)
{
dest->nightsrecords[i]->score[j] = src->nightsrecords[i]->score[j];
dest->nightsrecords[i]->grade[j] = src->nightsrecords[i]->grade[j];
dest->nightsrecords[i]->time[j] = src->nightsrecords[i]->time[j];
}
dest->nightsrecords[i]->nummares = src->nightsrecords[i]->nummares;
}
}
void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2)
{
condition_t *cond;
@ -70,89 +137,90 @@ void M_ClearConditionSet(UINT8 set)
conditionSets[set - 1].condition = NULL;
conditionSets[set - 1].numconditions = 0;
}
conditionSets[set - 1].achieved = false;
clientGamedata->achieved[set - 1] = serverGamedata->achieved[set - 1] = false;
}
// Clear ALL secrets.
void M_ClearSecrets(void)
void M_ClearSecrets(gamedata_t *data)
{
INT32 i;
memset(mapvisited, 0, sizeof(mapvisited));
memset(data->mapvisited, 0, sizeof(data->mapvisited));
for (i = 0; i < MAXEMBLEMS; ++i)
emblemlocations[i].collected = false;
data->collected[i] = false;
for (i = 0; i < MAXEXTRAEMBLEMS; ++i)
extraemblems[i].collected = false;
data->extraCollected[i] = false;
for (i = 0; i < MAXUNLOCKABLES; ++i)
unlockables[i].unlocked = false;
data->unlocked[i] = false;
for (i = 0; i < MAXCONDITIONSETS; ++i)
conditionSets[i].achieved = false;
data->achieved[i] = false;
timesBeaten = timesBeatenWithEmeralds = timesBeatenUltimate = 0;
data->timesBeaten = data->timesBeatenWithEmeralds = data->timesBeatenUltimate = 0;
// Re-unlock any always unlocked things
M_SilentUpdateUnlockablesAndEmblems();
M_SilentUpdateUnlockablesAndEmblems(data);
M_SilentUpdateSkinAvailabilites();
}
// ----------------------
// Condition set checking
// ----------------------
static UINT8 M_CheckCondition(condition_t *cn)
static UINT8 M_CheckCondition(condition_t *cn, gamedata_t *data)
{
switch (cn->type)
{
case UC_PLAYTIME: // Requires total playing time >= x
return (totalplaytime >= (unsigned)cn->requirement);
return (data->totalplaytime >= (unsigned)cn->requirement);
case UC_GAMECLEAR: // Requires game beaten >= x times
return (timesBeaten >= (unsigned)cn->requirement);
return (data->timesBeaten >= (unsigned)cn->requirement);
case UC_ALLEMERALDS: // Requires game beaten with all 7 emeralds >= x times
return (timesBeatenWithEmeralds >= (unsigned)cn->requirement);
return (data->timesBeatenWithEmeralds >= (unsigned)cn->requirement);
case UC_ULTIMATECLEAR: // Requires game beaten on ultimate >= x times (in other words, never)
return (timesBeatenUltimate >= (unsigned)cn->requirement);
return (data->timesBeatenUltimate >= (unsigned)cn->requirement);
case UC_OVERALLSCORE: // Requires overall score >= x
return (M_GotHighEnoughScore(cn->requirement));
return (M_GotHighEnoughScore(cn->requirement, data));
case UC_OVERALLTIME: // Requires overall time <= x
return (M_GotLowEnoughTime(cn->requirement));
return (M_GotLowEnoughTime(cn->requirement, data));
case UC_OVERALLRINGS: // Requires overall rings >= x
return (M_GotHighEnoughRings(cn->requirement));
return (M_GotHighEnoughRings(cn->requirement, data));
case UC_MAPVISITED: // Requires map x to be visited
return ((mapvisited[cn->requirement - 1] & MV_VISITED) == MV_VISITED);
return ((data->mapvisited[cn->requirement - 1] & MV_VISITED) == MV_VISITED);
case UC_MAPBEATEN: // Requires map x to be beaten
return ((mapvisited[cn->requirement - 1] & MV_BEATEN) == MV_BEATEN);
return ((data->mapvisited[cn->requirement - 1] & MV_BEATEN) == MV_BEATEN);
case UC_MAPALLEMERALDS: // Requires map x to be beaten with all emeralds in possession
return ((mapvisited[cn->requirement - 1] & MV_ALLEMERALDS) == MV_ALLEMERALDS);
return ((data->mapvisited[cn->requirement - 1] & MV_ALLEMERALDS) == MV_ALLEMERALDS);
case UC_MAPULTIMATE: // Requires map x to be beaten on ultimate
return ((mapvisited[cn->requirement - 1] & MV_ULTIMATE) == MV_ULTIMATE);
return ((data->mapvisited[cn->requirement - 1] & MV_ULTIMATE) == MV_ULTIMATE);
case UC_MAPPERFECT: // Requires map x to be beaten with a perfect bonus
return ((mapvisited[cn->requirement - 1] & MV_PERFECT) == MV_PERFECT);
return ((data->mapvisited[cn->requirement - 1] & MV_PERFECT) == MV_PERFECT);
case UC_MAPSCORE: // Requires score on map >= x
return (G_GetBestScore(cn->extrainfo1) >= (unsigned)cn->requirement);
return (G_GetBestScore(cn->extrainfo1, data) >= (unsigned)cn->requirement);
case UC_MAPTIME: // Requires time on map <= x
return (G_GetBestTime(cn->extrainfo1) <= (unsigned)cn->requirement);
return (G_GetBestTime(cn->extrainfo1, data) <= (unsigned)cn->requirement);
case UC_MAPRINGS: // Requires rings on map >= x
return (G_GetBestRings(cn->extrainfo1) >= cn->requirement);
return (G_GetBestRings(cn->extrainfo1, data) >= cn->requirement);
case UC_NIGHTSSCORE:
return (G_GetBestNightsScore(cn->extrainfo1, (UINT8)cn->extrainfo2) >= (unsigned)cn->requirement);
return (G_GetBestNightsScore(cn->extrainfo1, (UINT8)cn->extrainfo2, data) >= (unsigned)cn->requirement);
case UC_NIGHTSTIME:
return (G_GetBestNightsTime(cn->extrainfo1, (UINT8)cn->extrainfo2) <= (unsigned)cn->requirement);
return (G_GetBestNightsTime(cn->extrainfo1, (UINT8)cn->extrainfo2, data) <= (unsigned)cn->requirement);
case UC_NIGHTSGRADE:
return (G_GetBestNightsGrade(cn->extrainfo1, (UINT8)cn->extrainfo2) >= cn->requirement);
return (G_GetBestNightsGrade(cn->extrainfo1, (UINT8)cn->extrainfo2, data) >= cn->requirement);
case UC_TRIGGER: // requires map trigger set
return !!(unlocktriggers & (1 << cn->requirement));
case UC_TOTALEMBLEMS: // Requires number of emblems >= x
return (M_GotEnoughEmblems(cn->requirement));
return (M_GotEnoughEmblems(cn->requirement, data));
case UC_EMBLEM: // Requires emblem x to be obtained
return emblemlocations[cn->requirement-1].collected;
return data->collected[cn->requirement-1];
case UC_EXTRAEMBLEM: // Requires extra emblem x to be obtained
return extraemblems[cn->requirement-1].collected;
return data->extraCollected[cn->requirement-1];
case UC_CONDITIONSET: // requires condition set x to already be achieved
return M_Achieved(cn->requirement-1);
return M_Achieved(cn->requirement-1, data);
}
return false;
}
static UINT8 M_CheckConditionSet(conditionset_t *c)
static UINT8 M_CheckConditionSet(conditionset_t *c, gamedata_t *data)
{
UINT32 i;
UINT32 lastID = 0;
@ -173,13 +241,13 @@ static UINT8 M_CheckConditionSet(conditionset_t *c)
continue;
lastID = cn->id;
achievedSoFar = M_CheckCondition(cn);
achievedSoFar = M_CheckCondition(cn, data);
}
return achievedSoFar;
}
void M_CheckUnlockConditions(void)
void M_CheckUnlockConditions(gamedata_t *data)
{
INT32 i;
conditionset_t *c;
@ -187,27 +255,27 @@ void M_CheckUnlockConditions(void)
for (i = 0; i < MAXCONDITIONSETS; ++i)
{
c = &conditionSets[i];
if (!c->numconditions || c->achieved)
if (!c->numconditions || data->achieved[i])
continue;
c->achieved = (M_CheckConditionSet(c));
data->achieved[i] = (M_CheckConditionSet(c, data));
}
}
UINT8 M_UpdateUnlockablesAndExtraEmblems(void)
UINT8 M_UpdateUnlockablesAndExtraEmblems(gamedata_t *data)
{
INT32 i;
char cechoText[992] = "";
UINT8 cechoLines = 0;
M_CheckUnlockConditions();
M_CheckUnlockConditions(data);
// Go through extra emblems
for (i = 0; i < numextraemblems; ++i)
{
if (extraemblems[i].collected || !extraemblems[i].conditionset)
if (data->extraCollected[i] || !extraemblems[i].conditionset)
continue;
if ((extraemblems[i].collected = M_Achieved(extraemblems[i].conditionset - 1)) != false)
if ((data->extraCollected[i] = M_Achieved(extraemblems[i].conditionset - 1, data)) != false)
{
strcat(cechoText, va(M_GetText("Got \"%s\" emblem!\\"), extraemblems[i].name));
++cechoLines;
@ -217,14 +285,14 @@ UINT8 M_UpdateUnlockablesAndExtraEmblems(void)
// Fun part: if any of those unlocked we need to go through the
// unlock conditions AGAIN just in case an emblem reward was reached
if (cechoLines)
M_CheckUnlockConditions();
M_CheckUnlockConditions(data);
// Go through unlockables
for (i = 0; i < MAXUNLOCKABLES; ++i)
{
if (unlockables[i].unlocked || !unlockables[i].conditionset)
if (data->unlocked[i] || !unlockables[i].conditionset)
continue;
if ((unlockables[i].unlocked = M_Achieved(unlockables[i].conditionset - 1)) != false)
if ((data->unlocked[i] = M_Achieved(unlockables[i].conditionset - 1, data)) != false)
{
if (unlockables[i].nocecho)
continue;
@ -248,45 +316,49 @@ UINT8 M_UpdateUnlockablesAndExtraEmblems(void)
HU_DoCEcho(slashed);
return true;
}
return false;
}
// Used when loading gamedata to make sure all unlocks are synched with conditions
void M_SilentUpdateUnlockablesAndEmblems(void)
void M_SilentUpdateUnlockablesAndEmblems(gamedata_t *data)
{
INT32 i;
boolean checkAgain = false;
// Just in case they aren't to sync
M_CheckUnlockConditions();
M_CheckLevelEmblems();
M_CheckUnlockConditions(data);
M_CheckLevelEmblems(data);
// Go through extra emblems
for (i = 0; i < numextraemblems; ++i)
{
if (extraemblems[i].collected || !extraemblems[i].conditionset)
if (data->extraCollected[i] || !extraemblems[i].conditionset)
continue;
if ((extraemblems[i].collected = M_Achieved(extraemblems[i].conditionset - 1)) != false)
if ((data->extraCollected[i] = M_Achieved(extraemblems[i].conditionset - 1, data)) != false)
checkAgain = true;
}
// check again if extra emblems unlocked, blah blah, etc
if (checkAgain)
M_CheckUnlockConditions();
M_CheckUnlockConditions(data);
// Go through unlockables
for (i = 0; i < MAXUNLOCKABLES; ++i)
{
if (unlockables[i].unlocked || !unlockables[i].conditionset)
if (data->unlocked[i] || !unlockables[i].conditionset)
continue;
unlockables[i].unlocked = M_Achieved(unlockables[i].conditionset - 1);
data->unlocked[i] = M_Achieved(unlockables[i].conditionset - 1, data);
}
}
void M_SilentUpdateSkinAvailabilites(void)
{
players[consoleplayer].availabilities = players[1].availabilities = R_GetSkinAvailabilities(); // players[1] is supposed to be for 2p
}
// Emblem unlocking shit
UINT8 M_CheckLevelEmblems(void)
UINT8 M_CheckLevelEmblems(gamedata_t *data)
{
INT32 i;
INT32 valToReach;
@ -297,7 +369,7 @@ UINT8 M_CheckLevelEmblems(void)
// Update Score, Time, Rings emblems
for (i = 0; i < numemblems; ++i)
{
if (emblemlocations[i].type <= ET_SKIN || emblemlocations[i].type == ET_MAP || emblemlocations[i].collected)
if (emblemlocations[i].type <= ET_SKIN || emblemlocations[i].type == ET_MAP || data->collected[i])
continue;
levelnum = emblemlocations[i].level;
@ -306,32 +378,32 @@ UINT8 M_CheckLevelEmblems(void)
switch (emblemlocations[i].type)
{
case ET_SCORE: // Requires score on map >= x
res = (G_GetBestScore(levelnum) >= (unsigned)valToReach);
res = (G_GetBestScore(levelnum, data) >= (unsigned)valToReach);
break;
case ET_TIME: // Requires time on map <= x
res = (G_GetBestTime(levelnum) <= (unsigned)valToReach);
res = (G_GetBestTime(levelnum, data) <= (unsigned)valToReach);
break;
case ET_RINGS: // Requires rings on map >= x
res = (G_GetBestRings(levelnum) >= valToReach);
res = (G_GetBestRings(levelnum, data) >= valToReach);
break;
case ET_NGRADE: // Requires NiGHTS grade on map >= x
res = (G_GetBestNightsGrade(levelnum, 0) >= valToReach);
res = (G_GetBestNightsGrade(levelnum, 0, data) >= valToReach);
break;
case ET_NTIME: // Requires NiGHTS time on map <= x
res = (G_GetBestNightsTime(levelnum, 0) <= (unsigned)valToReach);
res = (G_GetBestNightsTime(levelnum, 0, data) <= (unsigned)valToReach);
break;
default: // unreachable but shuts the compiler up.
continue;
}
emblemlocations[i].collected = res;
data->collected[i] = res;
if (res)
++somethingUnlocked;
}
return somethingUnlocked;
}
UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separate print when awarding emblems and it's sorta different enough.
UINT8 M_CompletionEmblems(gamedata_t *data) // Bah! Duplication sucks, but it's for a separate print when awarding emblems and it's sorta different enough.
{
INT32 i;
INT32 embtype;
@ -342,7 +414,7 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa
for (i = 0; i < numemblems; ++i)
{
if (emblemlocations[i].type != ET_MAP || emblemlocations[i].collected)
if (emblemlocations[i].type != ET_MAP || data->collected[i])
continue;
levelnum = emblemlocations[i].level;
@ -358,9 +430,9 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa
if (embtype & ME_PERFECT)
flags |= MV_PERFECT;
res = ((mapvisited[levelnum - 1] & flags) == flags);
res = ((data->mapvisited[levelnum - 1] & flags) == flags);
emblemlocations[i].collected = res;
data->collected[i] = res;
if (res)
++somethingUnlocked;
}
@ -370,48 +442,54 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa
// -------------------
// Quick unlock checks
// -------------------
UINT8 M_AnySecretUnlocked(void)
UINT8 M_AnySecretUnlocked(gamedata_t *data)
{
INT32 i;
for (i = 0; i < MAXUNLOCKABLES; ++i)
{
if (!unlockables[i].nocecho && unlockables[i].unlocked)
if (!unlockables[i].nocecho && data->unlocked[i])
return true;
}
return false;
}
UINT8 M_SecretUnlocked(INT32 type)
UINT8 M_SecretUnlocked(INT32 type, gamedata_t *data)
{
INT32 i;
for (i = 0; i < MAXUNLOCKABLES; ++i)
{
if (unlockables[i].type == type && unlockables[i].unlocked)
if (unlockables[i].type == type && data->unlocked[i])
return true;
}
return false;
}
UINT8 M_MapLocked(INT32 mapnum)
UINT8 M_MapLocked(INT32 mapnum, gamedata_t *data)
{
if (!mapheaderinfo[mapnum-1] || mapheaderinfo[mapnum-1]->unlockrequired < 0)
{
return false;
if (!unlockables[mapheaderinfo[mapnum-1]->unlockrequired].unlocked)
}
if (!data->unlocked[mapheaderinfo[mapnum-1]->unlockrequired])
{
return true;
}
return false;
}
INT32 M_CountEmblems(void)
INT32 M_CountEmblems(gamedata_t *data)
{
INT32 found = 0, i;
for (i = 0; i < numemblems; ++i)
{
if (emblemlocations[i].collected)
if (data->collected[i])
found++;
}
for (i = 0; i < numextraemblems; ++i)
{
if (extraemblems[i].collected)
if (data->extraCollected[i])
found++;
}
return found;
@ -423,23 +501,23 @@ INT32 M_CountEmblems(void)
// Theoretically faster than using M_CountEmblems()
// Stops when it reaches the target number of emblems.
UINT8 M_GotEnoughEmblems(INT32 number)
UINT8 M_GotEnoughEmblems(INT32 number, gamedata_t *data)
{
INT32 i, gottenemblems = 0;
for (i = 0; i < numemblems; ++i)
{
if (emblemlocations[i].collected)
if (data->collected[i])
if (++gottenemblems >= number) return true;
}
for (i = 0; i < numextraemblems; ++i)
{
if (extraemblems[i].collected)
if (data->extraCollected[i])
if (++gottenemblems >= number) return true;
}
return false;
}
UINT8 M_GotHighEnoughScore(INT32 tscore)
UINT8 M_GotHighEnoughScore(INT32 tscore, gamedata_t *data)
{
INT32 mscore = 0;
INT32 i;
@ -448,16 +526,16 @@ UINT8 M_GotHighEnoughScore(INT32 tscore)
{
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
continue;
if (!mainrecords[i])
if (!data->mainrecords[i])
continue;
if ((mscore += mainrecords[i]->score) > tscore)
if ((mscore += data->mainrecords[i]->score) > tscore)
return true;
}
return false;
}
UINT8 M_GotLowEnoughTime(INT32 tictime)
UINT8 M_GotLowEnoughTime(INT32 tictime, gamedata_t *data)
{
INT32 curtics = 0;
INT32 i;
@ -467,15 +545,15 @@ UINT8 M_GotLowEnoughTime(INT32 tictime)
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
continue;
if (!mainrecords[i] || !mainrecords[i]->time)
if (!data->mainrecords[i] || !data->mainrecords[i]->time)
return false;
else if ((curtics += mainrecords[i]->time) > tictime)
else if ((curtics += data->mainrecords[i]->time) > tictime)
return false;
}
return true;
}
UINT8 M_GotHighEnoughRings(INT32 trings)
UINT8 M_GotHighEnoughRings(INT32 trings, gamedata_t *data)
{
INT32 mrings = 0;
INT32 i;
@ -484,10 +562,10 @@ UINT8 M_GotHighEnoughRings(INT32 trings)
{
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
continue;
if (!mainrecords[i])
if (!data->mainrecords[i])
continue;
if ((mrings += mainrecords[i]->rings) > trings)
if ((mrings += data->mainrecords[i]->rings) > trings)
return true;
}
return false;

View file

@ -10,7 +10,11 @@
/// \file m_cond.h
/// \brief Unlockable condition system for SRB2 version 2.1
#ifndef __M_COND__
#define __M_COND__
#include "doomdef.h"
#include "doomdata.h"
// --------
// Typedefs
@ -61,8 +65,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
@ -94,7 +96,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
{
@ -104,7 +105,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
@ -120,7 +120,6 @@ typedef struct
char *stringVar;
UINT8 nocecho;
UINT8 nochecklist;
UINT8 unlocked;
} unlockable_t;
#define SECRET_NONE -6 // Does nil. Use with levels locked by UnlockRequired
@ -143,6 +142,83 @@ typedef struct
#define MAXEXTRAEMBLEMS 16
#define MAXUNLOCKABLES 32
/** Time attack information, currently a very small structure.
*/
typedef struct
{
tic_t time; ///< Time in which the level was finished.
UINT32 score; ///< Score when the level was finished.
UINT16 rings; ///< Rings when the level was finished.
} recorddata_t;
/** Setup for one NiGHTS map.
* These are dynamically allocated because I am insane
*/
#define GRADE_F 0
#define GRADE_E 1
#define GRADE_D 2
#define GRADE_C 3
#define GRADE_B 4
#define GRADE_A 5
#define GRADE_S 6
typedef struct
{
// 8 mares, 1 overall (0)
UINT8 nummares;
UINT32 score[9];
UINT8 grade[9];
tic_t time[9];
} nightsdata_t;
// mapvisited is now a set of flags that says what we've done in the map.
#define MV_VISITED 1
#define MV_BEATEN 2
#define MV_ALLEMERALDS 4
#define MV_ULTIMATE 8
#define MV_PERFECT 16
#define MV_PERFECTRA 32
#define MV_MAX 63 // used in gamedata check, update whenever MV's are added
// Temporary holding place for nights data for the current map
extern nightsdata_t ntemprecords;
// 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];
// TIME ATTACK DATA
recorddata_t *mainrecords[NUMMAPS];
nightsdata_t *nightsrecords[NUMMAPS];
UINT8 mapvisited[NUMMAPS];
// # OF TIMES THE GAME HAS BEEN BEATEN
UINT32 timesBeaten;
UINT32 timesBeatenWithEmeralds;
UINT32 timesBeatenUltimate;
// PLAY TIME
UINT32 totalplaytime;
} gamedata_t;
extern gamedata_t *clientGamedata;
extern gamedata_t *serverGamedata;
extern conditionset_t conditionSets[MAXCONDITIONSETS];
extern emblem_t emblemlocations[MAXEMBLEMS];
extern extraemblem_t extraemblems[MAXEXTRAEMBLEMS];
@ -153,25 +229,30 @@ extern INT32 numextraemblems;
extern UINT32 unlocktriggers;
gamedata_t *M_NewGameDataStruct(void);
void M_CopyGameData(gamedata_t *dest, gamedata_t *src);
// Condition set setup
void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2);
// Clearing secrets
void M_ClearConditionSet(UINT8 set);
void M_ClearSecrets(void);
void M_ClearSecrets(gamedata_t *data);
// Updating conditions and unlockables
void M_CheckUnlockConditions(void);
UINT8 M_UpdateUnlockablesAndExtraEmblems(void);
void M_SilentUpdateUnlockablesAndEmblems(void);
UINT8 M_CheckLevelEmblems(void);
UINT8 M_CompletionEmblems(void);
void M_CheckUnlockConditions(gamedata_t *data);
UINT8 M_UpdateUnlockablesAndExtraEmblems(gamedata_t *data);
void M_SilentUpdateUnlockablesAndEmblems(gamedata_t *data);
UINT8 M_CheckLevelEmblems(gamedata_t *data);
UINT8 M_CompletionEmblems(gamedata_t *data);
void M_SilentUpdateSkinAvailabilites(void);
// Checking unlockable status
UINT8 M_AnySecretUnlocked(void);
UINT8 M_SecretUnlocked(INT32 type);
UINT8 M_MapLocked(INT32 mapnum);
INT32 M_CountEmblems(void);
UINT8 M_AnySecretUnlocked(gamedata_t *data);
UINT8 M_SecretUnlocked(INT32 type, gamedata_t *data);
UINT8 M_MapLocked(INT32 mapnum, gamedata_t *data);
INT32 M_CountEmblems(gamedata_t *data);
// Emblem shit
emblem_t *M_GetLevelEmblems(INT32 mapnum);
@ -183,12 +264,14 @@ const char *M_GetExtraEmblemPatch(extraemblem_t *em, boolean big);
// If you're looking to compare stats for unlocks or what not, use these
// They stop checking upon reaching the target number so they
// should be (theoretically?) slightly faster.
UINT8 M_GotEnoughEmblems(INT32 number);
UINT8 M_GotHighEnoughScore(INT32 tscore);
UINT8 M_GotLowEnoughTime(INT32 tictime);
UINT8 M_GotHighEnoughRings(INT32 trings);
UINT8 M_GotEnoughEmblems(INT32 number, gamedata_t *data);
UINT8 M_GotHighEnoughScore(INT32 tscore, gamedata_t *data);
UINT8 M_GotLowEnoughTime(INT32 tictime, gamedata_t *data);
UINT8 M_GotHighEnoughRings(INT32 trings, gamedata_t *data);
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, data) ((a) >= MAXCONDITIONSETS || data->achieved[a])
#endif

View file

@ -2284,6 +2284,7 @@ static boolean M_CanShowLevelOnPlatter(INT32 mapnum, INT32 gt);
// Nextmap. Used for Level select.
void Nextmap_OnChange(void)
{
gamedata_t *data = clientGamedata;
char *leveltitle;
char tabase[256];
#ifdef OLDNREPLAYNAME
@ -2301,7 +2302,7 @@ void Nextmap_OnChange(void)
{
CV_StealthSetValue(&cv_dummymares, 0);
// Hide the record changing CVAR if only one mare is available.
if (!nightsrecords[cv_nextmap.value-1] || nightsrecords[cv_nextmap.value-1]->nummares < 2)
if (!data->nightsrecords[cv_nextmap.value-1] || data->nightsrecords[cv_nextmap.value-1]->nummares < 2)
SP_NightsAttackMenu[narecords].status = IT_DISABLED;
else
SP_NightsAttackMenu[narecords].status = IT_STRING|IT_CVAR;
@ -2432,14 +2433,15 @@ void Nextmap_OnChange(void)
static void Dummymares_OnChange(void)
{
if (!nightsrecords[cv_nextmap.value-1])
gamedata_t *data = clientGamedata;
if (!data->nightsrecords[cv_nextmap.value-1])
{
CV_StealthSetValue(&cv_dummymares, 0);
return;
}
else
{
UINT8 mares = nightsrecords[cv_nextmap.value-1]->nummares;
UINT8 mares = data->nightsrecords[cv_nextmap.value-1]->nummares;
if (cv_dummymares.value < 0)
CV_StealthSetValue(&cv_dummymares, mares);
@ -3670,9 +3672,9 @@ void M_StartControlPanel(void)
if (!Playing())
{
// Secret menu!
MainMenu[singleplr].alphaKey = (M_AnySecretUnlocked()) ? 76 : 84;
MainMenu[multiplr].alphaKey = (M_AnySecretUnlocked()) ? 84 : 92;
MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
MainMenu[singleplr].alphaKey = (M_AnySecretUnlocked(clientGamedata)) ? 76 : 84;
MainMenu[multiplr].alphaKey = (M_AnySecretUnlocked(clientGamedata)) ? 84 : 92;
MainMenu[secrets].status = (M_AnySecretUnlocked(clientGamedata)) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
currentMenu = &MainDef;
itemOn = singleplr;
@ -3680,14 +3682,14 @@ void M_StartControlPanel(void)
else if (modeattacking)
{
currentMenu = &MAPauseDef;
MAPauseMenu[mapause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS)) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
MAPauseMenu[mapause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS, clientGamedata)) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
itemOn = mapause_continue;
}
else if (!(netgame || multiplayer)) // Single Player
{
if (gamestate != GS_LEVEL || ultimatemode) // intermission, so gray out stuff.
{
SPauseMenu[spause_pandora].status = (M_SecretUnlocked(SECRET_PANDORA)) ? (IT_GRAYEDOUT) : (IT_DISABLED);
SPauseMenu[spause_pandora].status = (M_SecretUnlocked(SECRET_PANDORA, serverGamedata)) ? (IT_GRAYEDOUT) : (IT_DISABLED);
SPauseMenu[spause_retry].status = IT_GRAYEDOUT;
}
else
@ -3696,7 +3698,7 @@ void M_StartControlPanel(void)
if (players[consoleplayer].playerstate != PST_LIVE)
++numlives;
SPauseMenu[spause_pandora].status = (M_SecretUnlocked(SECRET_PANDORA) && !marathonmode) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
SPauseMenu[spause_pandora].status = (M_SecretUnlocked(SECRET_PANDORA, serverGamedata) && !marathonmode) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
// The list of things that can disable retrying is (was?) a little too complex
// for me to want to use the short if statement syntax
@ -3710,7 +3712,7 @@ void M_StartControlPanel(void)
SPauseMenu[spause_levelselect].status = (gamecomplete == 1) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
// And emblem hints.
SPauseMenu[spause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS) && !marathonmode) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
SPauseMenu[spause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS, clientGamedata) && !marathonmode) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
// Shift up Pandora's Box if both pandora and levelselect are active
/*if (SPauseMenu[spause_pandora].status != (IT_DISABLED)
@ -4259,7 +4261,7 @@ static void M_DrawMapEmblems(INT32 mapnum, INT32 x, INT32 y, boolean norecordatt
x -= 4;
lasttype = curtype;
if (emblem->collected)
if (clientGamedata->collected[emblem - emblemlocations])
V_DrawSmallMappedPatch(x, y, 0, W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_PATCH),
R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE));
else
@ -4692,6 +4694,8 @@ static void M_DrawGenericScrollMenu(void)
static void M_DrawPauseMenu(void)
{
gamedata_t *data = clientGamedata;
if (!netgame && !multiplayer && (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION))
{
emblem_t *emblem_detail[3] = {NULL, NULL, NULL};
@ -4720,7 +4724,7 @@ static void M_DrawPauseMenu(void)
{
case ET_SCORE:
snprintf(targettext, 9, "%d", emblem->var);
snprintf(currenttext, 9, "%u", G_GetBestScore(gamemap));
snprintf(currenttext, 9, "%u", G_GetBestScore(gamemap, data));
targettext[8] = 0;
currenttext[8] = 0;
@ -4734,7 +4738,7 @@ static void M_DrawPauseMenu(void)
G_TicsToSeconds((tic_t)emblemslot),
G_TicsToCentiseconds((tic_t)emblemslot));
emblemslot = (INT32)G_GetBestTime(gamemap); // dumb hack pt ii
emblemslot = (INT32)G_GetBestTime(gamemap, data); // dumb hack pt ii
if ((tic_t)emblemslot == UINT32_MAX)
snprintf(currenttext, 9, "-:--.--");
else
@ -4750,7 +4754,7 @@ static void M_DrawPauseMenu(void)
break;
case ET_RINGS:
snprintf(targettext, 9, "%d", emblem->var);
snprintf(currenttext, 9, "%u", G_GetBestRings(gamemap));
snprintf(currenttext, 9, "%u", G_GetBestRings(gamemap, data));
targettext[8] = 0;
currenttext[8] = 0;
@ -4758,8 +4762,8 @@ static void M_DrawPauseMenu(void)
emblemslot = 2;
break;
case ET_NGRADE:
snprintf(targettext, 9, "%u", P_GetScoreForGradeOverall(gamemap, emblem->var));
snprintf(currenttext, 9, "%u", G_GetBestNightsScore(gamemap, 0));
snprintf(targettext, 9, "%u", P_GetScoreForGrade(gamemap, 0, emblem->var));
snprintf(currenttext, 9, "%u", G_GetBestNightsScore(gamemap, 0, data));
targettext[8] = 0;
currenttext[8] = 0;
@ -4773,7 +4777,7 @@ static void M_DrawPauseMenu(void)
G_TicsToSeconds((tic_t)emblemslot),
G_TicsToCentiseconds((tic_t)emblemslot));
emblemslot = (INT32)G_GetBestNightsTime(gamemap, 0); // dumb hack pt iv
emblemslot = (INT32)G_GetBestNightsTime(gamemap, 0, data); // dumb hack pt iv
if ((tic_t)emblemslot == UINT32_MAX)
snprintf(currenttext, 9, "-:--.--");
else
@ -4807,7 +4811,7 @@ static void M_DrawPauseMenu(void)
if (!emblem)
continue;
if (emblem->collected)
if (data->collected[emblem - emblemlocations])
V_DrawSmallMappedPatch(40, 44 + (i*8), 0, W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_PATCH),
R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE));
else
@ -5019,7 +5023,9 @@ static void M_PatchSkinNameTable(void)
//
static boolean M_LevelAvailableOnPlatter(INT32 mapnum)
{
if (M_MapLocked(mapnum+1))
gamedata_t *data = serverGamedata;
if (M_MapLocked(mapnum+1, data))
return false; // not unlocked
switch (levellistmode)
@ -5032,7 +5038,7 @@ static boolean M_LevelAvailableOnPlatter(INT32 mapnum)
return true;
#ifndef DEVELOP
if (mapvisited[mapnum]) // MV_MP
if (data->mapvisited[mapnum])
#endif
return true;
@ -5040,7 +5046,7 @@ static boolean M_LevelAvailableOnPlatter(INT32 mapnum)
case LLM_RECORDATTACK:
case LLM_NIGHTSATTACK:
#ifndef DEVELOP
if (mapvisited[mapnum] & MV_MAX)
if (data->mapvisited[mapnum])
return true;
if (mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED)
@ -5071,7 +5077,7 @@ static boolean M_CanShowLevelOnPlatter(INT32 mapnum, INT32 gt)
if (!mapheaderinfo[mapnum]->lvlttl[0])
return false;
/*if (M_MapLocked(mapnum+1))
/*if (M_MapLocked(mapnum+1, serverGamedata))
return false; // not unlocked*/
switch (levellistmode)
@ -6904,7 +6910,7 @@ static void M_HandleAddons(INT32 choice)
closefilemenu(true);
// secrets disabled by addfile...
MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
MainMenu[secrets].status = (M_AnySecretUnlocked(clientGamedata)) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
if (currentMenu->prevMenu)
M_SetupNextMenu(currentMenu->prevMenu);
@ -7150,7 +7156,9 @@ static boolean checklist_cangodown; // uuuueeerggghhhh HACK
static void M_HandleChecklist(INT32 choice)
{
gamedata_t *data = clientGamedata;
INT32 j;
switch (choice)
{
case KEY_DOWNARROW:
@ -7167,7 +7175,7 @@ static void M_HandleChecklist(INT32 choice)
continue;
if (unlockables[j].conditionset > MAXCONDITIONSETS)
continue;
if (!unlockables[j].unlocked && unlockables[j].showconditionset && !M_Achieved(unlockables[j].showconditionset))
if (!data->unlocked[j] && unlockables[j].showconditionset && !M_Achieved(unlockables[j].showconditionset, data))
continue;
if (unlockables[j].conditionset == unlockables[check_on].conditionset)
continue;
@ -7192,7 +7200,7 @@ static void M_HandleChecklist(INT32 choice)
continue;
if (unlockables[j].conditionset > MAXCONDITIONSETS)
continue;
if (!unlockables[j].unlocked && unlockables[j].showconditionset && !M_Achieved(unlockables[j].showconditionset))
if (!data->unlocked[j] && unlockables[j].showconditionset && !M_Achieved(unlockables[j].showconditionset, data))
continue;
if (j && unlockables[j].conditionset == unlockables[j-1].conditionset)
continue;
@ -7218,6 +7226,9 @@ static void M_HandleChecklist(INT32 choice)
static void M_DrawChecklist(void)
{
gamedata_t *data = clientGamedata;
INT32 emblemCount = M_CountEmblems(data);
INT32 i = check_on, j = 0, y = currentMenu->y, emblems = numemblems+numextraemblems;
UINT32 condnum, previd, maxcond;
condition_t *cond;
@ -7228,7 +7239,7 @@ static void M_DrawChecklist(void)
// draw emblem counter
if (emblems > 0)
{
V_DrawString(42, 20, (emblems == M_CountEmblems()) ? V_GREENMAP : 0, va("%d/%d", M_CountEmblems(), emblems));
V_DrawString(42, 20, (emblems == emblemCount) ? V_GREENMAP : 0, va("%d/%d", emblemCount, emblems));
V_DrawSmallScaledPatch(28, 20, 0, W_CachePatchName("EMBLICON", PU_PATCH));
}
@ -7239,13 +7250,13 @@ static void M_DrawChecklist(void)
{
if (unlockables[i].name[0] == 0 //|| unlockables[i].nochecklist
|| !unlockables[i].conditionset || unlockables[i].conditionset > MAXCONDITIONSETS
|| (!unlockables[i].unlocked && unlockables[i].showconditionset && !M_Achieved(unlockables[i].showconditionset)))
|| (!data->unlocked[i] && unlockables[i].showconditionset && !M_Achieved(unlockables[i].showconditionset, data)))
{
i += 1;
continue;
}
V_DrawString(currentMenu->x, y, ((unlockables[i].unlocked) ? V_GREENMAP : V_TRANSLUCENT)|V_ALLOWLOWERCASE, ((unlockables[i].unlocked || !unlockables[i].nochecklist) ? unlockables[i].name : M_CreateSecretMenuOption(unlockables[i].name)));
V_DrawString(currentMenu->x, y, ((data->unlocked[i]) ? V_GREENMAP : V_TRANSLUCENT)|V_ALLOWLOWERCASE, ((data->unlocked[i] || !unlockables[i].nochecklist) ? unlockables[i].name : M_CreateSecretMenuOption(unlockables[i].name)));
for (j = i+1; j < MAXUNLOCKABLES; j++)
{
@ -7323,7 +7334,7 @@ static void M_DrawChecklist(void)
if (title)
{
const char *level = ((M_MapLocked(cond[condnum].requirement) || !((mapheaderinfo[cond[condnum].requirement-1]->menuflags & LF2_NOVISITNEEDED) || (mapvisited[cond[condnum].requirement-1] & MV_MAX))) ? M_CreateSecretMenuOption(title) : title);
const char *level = ((M_MapLocked(cond[condnum].requirement, data) || !((mapheaderinfo[cond[condnum].requirement-1]->menuflags & LF2_NOVISITNEEDED) || (data->mapvisited[cond[condnum].requirement-1] & MV_MAX))) ? M_CreateSecretMenuOption(title) : title);
switch (cond[condnum].type)
{
@ -7356,7 +7367,7 @@ static void M_DrawChecklist(void)
if (title)
{
const char *level = ((M_MapLocked(cond[condnum].extrainfo1) || !((mapheaderinfo[cond[condnum].extrainfo1-1]->menuflags & LF2_NOVISITNEEDED) || (mapvisited[cond[condnum].extrainfo1-1] & MV_MAX))) ? M_CreateSecretMenuOption(title) : title);
const char *level = ((M_MapLocked(cond[condnum].extrainfo1, data) || !((mapheaderinfo[cond[condnum].extrainfo1-1]->menuflags & LF2_NOVISITNEEDED) || (data->mapvisited[cond[condnum].extrainfo1-1] & MV_MAX))) ? M_CreateSecretMenuOption(title) : title);
switch (cond[condnum].type)
{
@ -7425,7 +7436,7 @@ static void M_DrawChecklist(void)
if (title)
{
const char *level = ((M_MapLocked(cond[condnum].extrainfo1) || !((mapheaderinfo[cond[condnum].extrainfo1-1]->menuflags & LF2_NOVISITNEEDED) || (mapvisited[cond[condnum].extrainfo1-1] & MV_MAX))) ? M_CreateSecretMenuOption(title) : title);
const char *level = ((M_MapLocked(cond[condnum].extrainfo1, data) || !((mapheaderinfo[cond[condnum].extrainfo1-1]->menuflags & LF2_NOVISITNEEDED) || (data->mapvisited[cond[condnum].extrainfo1-1] & MV_MAX))) ? M_CreateSecretMenuOption(title) : title);
switch (cond[condnum].type)
{
@ -7488,7 +7499,7 @@ static void M_DrawChecklist(void)
/*V_DrawString(160, 8+(24*j), V_RETURN8, V_WordWrap(160, 292, 0, unlockables[i].objective));
if (unlockables[i].unlocked)
if (data->unlocked[i])
V_DrawString(308, 8+(24*j), V_YELLOWMAP, "Y");
else
V_DrawString(308, 8+(24*j), V_YELLOWMAP, "N");*/
@ -7517,7 +7528,7 @@ static void M_EmblemHints(INT32 choice)
(void)choice;
SR_EmblemHintMenu[0].status = (local > NUMHINTS*2) ? (IT_STRING | IT_ARROWS) : (IT_DISABLED);
SR_EmblemHintMenu[1].status = (M_SecretUnlocked(SECRET_ITEMFINDER)) ? (IT_CVAR|IT_STRING) : (IT_SECRET);
SR_EmblemHintMenu[1].status = (M_SecretUnlocked(SECRET_ITEMFINDER, clientGamedata)) ? (IT_CVAR|IT_STRING) : (IT_SECRET);
hintpage = 1;
SR_EmblemHintDef.prevMenu = currentMenu;
M_SetupNextMenu(&SR_EmblemHintDef);
@ -7577,7 +7588,7 @@ static void M_DrawEmblemHints(void)
if (totalemblems >= ((hintpage-1)*(NUMHINTS*2) + 1) && totalemblems < (hintpage*NUMHINTS*2)+1){
if (emblem->collected)
if (clientGamedata->collected[i])
{
collected = V_GREENMAP;
V_DrawMappedPatch(x, y+4, 0, W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_PATCH),
@ -8117,7 +8128,7 @@ static void M_SecretsMenu(INT32 choice)
SR_MainMenu[i].status = IT_SECRET;
if (unlockables[ul].unlocked)
if (clientGamedata->unlocked[ul])
{
switch (unlockables[ul].type)
{
@ -8216,7 +8227,7 @@ static void M_SinglePlayerMenu(INT32 choice)
levellistmode = LLM_RECORDATTACK;
if (M_GametypeHasLevels(-1))
SP_MainMenu[sprecordattack].status = (M_SecretUnlocked(SECRET_RECORDATTACK)) ? IT_CALL|IT_STRING : IT_SECRET;
SP_MainMenu[sprecordattack].status = (M_SecretUnlocked(SECRET_RECORDATTACK, clientGamedata)) ? IT_CALL|IT_STRING : IT_SECRET;
else // If Record Attack is nonexistent in the current add-on...
{
SP_MainMenu[sprecordattack].status = IT_NOTHING|IT_DISABLED; // ...hide and disable the Record Attack option...
@ -8226,7 +8237,7 @@ static void M_SinglePlayerMenu(INT32 choice)
levellistmode = LLM_NIGHTSATTACK;
if (M_GametypeHasLevels(-1))
SP_MainMenu[spnightsmode].status = (M_SecretUnlocked(SECRET_NIGHTSMODE)) ? IT_CALL|IT_STRING : IT_SECRET;
SP_MainMenu[spnightsmode].status = (M_SecretUnlocked(SECRET_NIGHTSMODE, clientGamedata)) ? IT_CALL|IT_STRING : IT_SECRET;
else // If NiGHTS Mode is nonexistent in the current add-on...
{
SP_MainMenu[spnightsmode].status = IT_NOTHING|IT_DISABLED; // ...hide and disable the NiGHTS Mode option...
@ -8249,7 +8260,7 @@ static void M_SinglePlayerMenu(INT32 choice)
SP_MainMenu[spnightsmode] .alphaKey += 8;
}
else // Otherwise, if Marathon Run is allowed and Record Attack is unlocked, unlock Marathon Run!
SP_MainMenu[spmarathon].status = (M_SecretUnlocked(SECRET_RECORDATTACK)) ? IT_CALL|IT_STRING|IT_CALL_NOTMODIFIED : IT_SECRET;
SP_MainMenu[spmarathon].status = (M_SecretUnlocked(SECRET_RECORDATTACK, clientGamedata)) ? IT_CALL|IT_STRING|IT_CALL_NOTMODIFIED : IT_SECRET;
if (tutorialmap) // If there's a tutorial available in the current add-on...
@ -9626,7 +9637,7 @@ static void M_Statistics(INT32 choice)
if (!(mapheaderinfo[i]->typeoflevel & TOL_SP) || (mapheaderinfo[i]->menuflags & LF2_HIDEINSTATS))
continue;
if (!(mapvisited[i] & MV_MAX))
if (!(clientGamedata->mapvisited[i] & MV_MAX))
continue;
statsMapList[j++] = i;
@ -9643,6 +9654,7 @@ static void M_Statistics(INT32 choice)
static void M_DrawStatsMaps(int location)
{
gamedata_t *data = clientGamedata;
INT32 y = 80, i = -1;
INT16 mnum;
extraemblem_t *exemblem;
@ -9710,14 +9722,14 @@ static void M_DrawStatsMaps(int location)
{
exemblem = &extraemblems[i];
if (exemblem->collected)
if (data->extraCollected[i])
V_DrawSmallMappedPatch(292, y, 0, W_CachePatchName(M_GetExtraEmblemPatch(exemblem, false), PU_PATCH),
R_GetTranslationColormap(TC_DEFAULT, M_GetExtraEmblemColor(exemblem), GTC_CACHE));
else
V_DrawSmallScaledPatch(292, y, 0, W_CachePatchName("NEEDIT", PU_PATCH));
V_DrawString(20, y, V_YELLOWMAP|V_ALLOWLOWERCASE,
(!exemblem->collected && exemblem->showconditionset && !M_Achieved(exemblem->showconditionset))
(!data->extraCollected[i] && exemblem->showconditionset && !M_Achieved(exemblem->showconditionset, data))
? M_CreateSecretMenuOption(exemblem->description)
: exemblem->description);
}
@ -9734,6 +9746,7 @@ bottomarrow:
static void M_DrawLevelStats(void)
{
gamedata_t *data = clientGamedata;
char beststr[40];
tic_t besttime = 0;
@ -9748,9 +9761,9 @@ static void M_DrawLevelStats(void)
V_DrawString(20, 24, V_YELLOWMAP, "Total Play Time:");
V_DrawCenteredString(BASEVIDWIDTH/2, 32, 0, va("%i hours, %i minutes, %i seconds",
G_TicsToHours(totalplaytime),
G_TicsToMinutes(totalplaytime, false),
G_TicsToSeconds(totalplaytime)));
G_TicsToHours(data->totalplaytime),
G_TicsToMinutes(data->totalplaytime, false),
G_TicsToSeconds(data->totalplaytime)));
for (i = 0; i < NUMMAPS; i++)
{
@ -9759,25 +9772,25 @@ static void M_DrawLevelStats(void)
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
continue;
if (!mainrecords[i])
if (!data->mainrecords[i])
{
mapsunfinished++;
bestunfinished[0] = bestunfinished[1] = bestunfinished[2] = true;
continue;
}
if (mainrecords[i]->score > 0)
bestscore += mainrecords[i]->score;
if (data->mainrecords[i]->score > 0)
bestscore += data->mainrecords[i]->score;
else
mapunfinished = bestunfinished[0] = true;
if (mainrecords[i]->time > 0)
besttime += mainrecords[i]->time;
if (data->mainrecords[i]->time > 0)
besttime += data->mainrecords[i]->time;
else
mapunfinished = bestunfinished[1] = true;
if (mainrecords[i]->rings > 0)
bestrings += mainrecords[i]->rings;
if (data->mainrecords[i]->rings > 0)
bestrings += data->mainrecords[i]->rings;
else
mapunfinished = bestunfinished[2] = true;
@ -9792,7 +9805,7 @@ static void M_DrawLevelStats(void)
else
V_DrawString(20, 56, V_GREENMAP, "(complete)");
V_DrawString(36, 64, 0, va("x %d/%d", M_CountEmblems(), numemblems+numextraemblems));
V_DrawString(36, 64, 0, va("x %d/%d", M_CountEmblems(data), numemblems+numextraemblems));
V_DrawSmallScaledPatch(20, 64, 0, W_CachePatchName("EMBLICON", PU_PATCH));
sprintf(beststr, "%u", bestscore);
@ -9859,6 +9872,7 @@ static void M_HandleLevelStats(INT32 choice)
// Drawing function for Time Attack
void M_DrawTimeAttackMenu(void)
{
gamedata_t *data = clientGamedata;
INT32 i, x, y, empatx, empaty, cursory = 0;
UINT16 dispstatus;
patch_t *PictureOfUrFace; // my WHAT
@ -10017,7 +10031,7 @@ void M_DrawTimeAttackMenu(void)
empatx = empatch->leftoffset / 2;
empaty = empatch->topoffset / 2;
if (em->collected)
if (data->collected[em - emblemlocations])
V_DrawSmallMappedPatch(104+76+empatx, yHeight+lsheadingheight/2+empaty, 0, empatch,
R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_CACHE));
else
@ -10030,34 +10044,34 @@ void M_DrawTimeAttackMenu(void)
// Draw in-level emblems.
M_DrawMapEmblems(cv_nextmap.value, 288, 28, true);
if (!mainrecords[cv_nextmap.value-1] || !mainrecords[cv_nextmap.value-1]->score)
if (!data->mainrecords[cv_nextmap.value-1] || !data->mainrecords[cv_nextmap.value-1]->score)
sprintf(beststr, "(none)");
else
sprintf(beststr, "%u", mainrecords[cv_nextmap.value-1]->score);
sprintf(beststr, "%u", data->mainrecords[cv_nextmap.value-1]->score);
V_DrawString(104-72, 33+lsheadingheight/2, V_YELLOWMAP, "SCORE:");
V_DrawRightAlignedString(104+64, 33+lsheadingheight/2, V_ALLOWLOWERCASE, beststr);
V_DrawRightAlignedString(104+72, 43+lsheadingheight/2, V_ALLOWLOWERCASE, reqscore);
if (!mainrecords[cv_nextmap.value-1] || !mainrecords[cv_nextmap.value-1]->time)
if (!data->mainrecords[cv_nextmap.value-1] || !data->mainrecords[cv_nextmap.value-1]->time)
sprintf(beststr, "(none)");
else
sprintf(beststr, "%i:%02i.%02i", G_TicsToMinutes(mainrecords[cv_nextmap.value-1]->time, true),
G_TicsToSeconds(mainrecords[cv_nextmap.value-1]->time),
G_TicsToCentiseconds(mainrecords[cv_nextmap.value-1]->time));
sprintf(beststr, "%i:%02i.%02i", G_TicsToMinutes(data->mainrecords[cv_nextmap.value-1]->time, true),
G_TicsToSeconds(data->mainrecords[cv_nextmap.value-1]->time),
G_TicsToCentiseconds(data->mainrecords[cv_nextmap.value-1]->time));
V_DrawString(104-72, 53+lsheadingheight/2, V_YELLOWMAP, "TIME:");
V_DrawRightAlignedString(104+64, 53+lsheadingheight/2, V_ALLOWLOWERCASE, beststr);
V_DrawRightAlignedString(104+72, 63+lsheadingheight/2, V_ALLOWLOWERCASE, reqtime);
if (!mainrecords[cv_nextmap.value-1] || !mainrecords[cv_nextmap.value-1]->rings)
if (!data->mainrecords[cv_nextmap.value-1] || !data->mainrecords[cv_nextmap.value-1]->rings)
sprintf(beststr, "(none)");
else
sprintf(beststr, "%hu", mainrecords[cv_nextmap.value-1]->rings);
sprintf(beststr, "%hu", data->mainrecords[cv_nextmap.value-1]->rings);
V_DrawString(104-72, 73+lsheadingheight/2, V_YELLOWMAP, "RINGS:");
V_DrawRightAlignedString(104+64, 73+lsheadingheight/2, V_ALLOWLOWERCASE|((mapvisited[cv_nextmap.value-1] & MV_PERFECTRA) ? V_YELLOWMAP : 0), beststr);
V_DrawRightAlignedString(104+64, 73+lsheadingheight/2, V_ALLOWLOWERCASE|((data->mapvisited[cv_nextmap.value-1] & MV_PERFECTRA) ? V_YELLOWMAP : 0), beststr);
V_DrawRightAlignedString(104+72, 83+lsheadingheight/2, V_ALLOWLOWERCASE, reqrings);
}
@ -10151,6 +10165,7 @@ static void M_TimeAttack(INT32 choice)
// Drawing function for Nights Attack
void M_DrawNightsAttackMenu(void)
{
gamedata_t *data = clientGamedata;
INT32 i, x, y, cursory = 0;
UINT16 dispstatus;
@ -10217,10 +10232,10 @@ void M_DrawNightsAttackMenu(void)
lumpnum_t lumpnum;
char beststr[40];
//UINT8 bestoverall = G_GetBestNightsGrade(cv_nextmap.value, 0);
UINT8 bestgrade = G_GetBestNightsGrade(cv_nextmap.value, cv_dummymares.value);
UINT32 bestscore = G_GetBestNightsScore(cv_nextmap.value, cv_dummymares.value);
tic_t besttime = G_GetBestNightsTime(cv_nextmap.value, cv_dummymares.value);
//UINT8 bestoverall = G_GetBestNightsGrade(cv_nextmap.value, 0, data);
UINT8 bestgrade = G_GetBestNightsGrade(cv_nextmap.value, cv_dummymares.value, data);
UINT32 bestscore = G_GetBestNightsScore(cv_nextmap.value, cv_dummymares.value, data);
tic_t besttime = G_GetBestNightsTime(cv_nextmap.value, cv_dummymares.value, data);
M_DrawLevelPlatterHeader(32-lsheadingheight/2, cv_nextmap.string, true, false);
@ -10301,7 +10316,7 @@ void M_DrawNightsAttackMenu(void)
goto skipThisOne;
}
if (em->collected)
if (data->collected[em - emblemlocations])
V_DrawSmallMappedPatch(xpos, yHeight+lsheadingheight/2, 0, W_CachePatchName(M_GetEmblemPatch(em, false), PU_PATCH),
R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_CACHE));
else
@ -12544,12 +12559,12 @@ static void M_EraseDataResponse(INT32 ch)
// Delete the data
if (erasecontext != 1)
G_ClearRecords();
G_ClearRecords(clientGamedata);
if (erasecontext != 0)
M_ClearSecrets();
M_ClearSecrets(clientGamedata);
if (erasecontext == 2)
{
totalplaytime = 0;
clientGamedata->totalplaytime = 0;
F_StartIntro();
}
BwehHehHe();

View file

@ -14,12 +14,11 @@
#include "doomdef.h"
#include "doomtype.h"
#include "doomstat.h" // totalplaytime
#include "m_random.h"
#include "m_fixed.h"
#include "m_cond.h" // totalplaytime
// ---------------------------
// RNG functions (not synched)
@ -252,5 +251,5 @@ void P_SetRandSeedD(const char *rfile, INT32 rline, UINT32 seed)
*/
UINT32 M_RandomizedSeed(void)
{
return ((totalplaytime & 0xFFFF) << 16)|M_RandomFixed();
return ((serverGamedata->totalplaytime & 0xFFFF) << 16) | M_RandomFixed();
}

View file

@ -740,10 +740,22 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
{
if (demoplayback || (player->bot && player->bot != BOT_MPAI) || special->health <= 0 || special->health > MAXEMBLEMS)
return;
emblemlocations[special->health-1].collected = true;
M_UpdateUnlockablesAndExtraEmblems();
G_SaveGameData();
if (emblemlocations[special->health-1].type == ET_SKIN)
{
INT32 skinnum = M_EmblemSkinNum(&emblemlocations[special->health-1]);
if (player->skin != skinnum)
{
return;
}
}
clientGamedata->collected[special->health-1] = serverGamedata->collected[special->health-1] = true;
M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
M_UpdateUnlockablesAndExtraEmblems(clientGamedata);
G_SaveGameData(clientGamedata);
break;
}

View file

@ -12004,10 +12004,6 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i)
return false; // You already got this token
break;
case MT_EMBLEM:
if (netgame || multiplayer)
return false; // Single player (You're next on my shit list)
break;
default:
break;
}
@ -12175,15 +12171,20 @@ 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;
validEmblem = !emblemlocations[j].collected;
validEmblem = true;
if (emblemlocations[j].type == ET_SKIN)
if (!netgame)
{
INT32 skinnum = M_EmblemSkinNum(&emblemlocations[j]);
validEmblem = !serverGamedata->collected[j];
if (players[0].skin != skinnum)
if (emblemlocations[j].type == ET_SKIN && !multiplayer)
{
validEmblem = false;
INT32 skinnum = M_EmblemSkinNum(&emblemlocations[j]);
if (players[0].skin != skinnum)
{
validEmblem = false;
}
}
}

View file

@ -47,6 +47,7 @@ UINT8 *save_p;
#define ARCHIVEBLOCK_POBJS 0x7F928546
#define ARCHIVEBLOCK_THINKERS 0x7F37037C
#define ARCHIVEBLOCK_SPECIALS 0x7F228378
#define ARCHIVEBLOCK_EMBLEMS 0x7F4A5445
// Note: This cannot be bigger
// than an UINT16
@ -4339,6 +4340,8 @@ static void P_NetArchiveMisc(boolean resending)
WRITEUINT32(save_p, hidetime);
WRITEUINT32(save_p, unlocktriggers);
// Is it paused?
if (paused)
WRITEUINT8(save_p, 0x2f);
@ -4437,6 +4440,8 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading)
hidetime = READUINT32(save_p);
unlocktriggers = READUINT32(save_p);
// Is it paused?
if (READUINT8(save_p) == 0x2f)
paused = true;
@ -4444,6 +4449,224 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading)
return true;
}
static inline void P_NetArchiveEmblems(void)
{
gamedata_t *data = serverGamedata;
INT32 i, j;
UINT8 btemp;
INT32 curmare;
WRITEUINT32(save_p, ARCHIVEBLOCK_EMBLEMS);
// These should be synchronized before savegame loading by the wad files being the same anyway,
// but just in case, for now, we'll leave them here for testing. It would be very bad if they mismatch.
WRITEUINT8(save_p, (UINT8)savemoddata);
WRITEINT32(save_p, numemblems);
WRITEINT32(save_p, numextraemblems);
// The rest of this is lifted straight from G_SaveGameData in g_game.c
// TODO: Optimize this to only send information about emblems, unlocks, etc. which actually exist
// There is no need to go all the way up to MAXEMBLEMS when wads are guaranteed to be the same.
WRITEUINT32(save_p, data->totalplaytime);
// TODO put another cipher on these things? meh, I don't care...
for (i = 0; i < NUMMAPS; i++)
WRITEUINT8(save_p, (data->mapvisited[i] & MV_MAX));
// To save space, use one bit per collected/achieved/unlocked flag
for (i = 0; i < MAXEMBLEMS;)
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
btemp |= (data->collected[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
for (i = 0; i < MAXEXTRAEMBLEMS;)
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j)
btemp |= (data->extraCollected[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
for (i = 0; i < MAXUNLOCKABLES;)
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
btemp |= (data->unlocked[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
for (i = 0; i < MAXCONDITIONSETS;)
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
btemp |= (data->achieved[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
WRITEUINT32(save_p, data->timesBeaten);
WRITEUINT32(save_p, data->timesBeatenWithEmeralds);
WRITEUINT32(save_p, data->timesBeatenUltimate);
// Main records
for (i = 0; i < NUMMAPS; i++)
{
if (data->mainrecords[i])
{
WRITEUINT32(save_p, data->mainrecords[i]->score);
WRITEUINT32(save_p, data->mainrecords[i]->time);
WRITEUINT16(save_p, data->mainrecords[i]->rings);
}
else
{
WRITEUINT32(save_p, 0);
WRITEUINT32(save_p, 0);
WRITEUINT16(save_p, 0);
}
}
// NiGHTS records
for (i = 0; i < NUMMAPS; i++)
{
if (!data->nightsrecords[i] || !data->nightsrecords[i]->nummares)
{
WRITEUINT8(save_p, 0);
continue;
}
WRITEUINT8(save_p, data->nightsrecords[i]->nummares);
for (curmare = 0; curmare < (data->nightsrecords[i]->nummares + 1); ++curmare)
{
WRITEUINT32(save_p, data->nightsrecords[i]->score[curmare]);
WRITEUINT8(save_p, data->nightsrecords[i]->grade[curmare]);
WRITEUINT32(save_p, data->nightsrecords[i]->time[curmare]);
}
}
}
static inline void P_NetUnArchiveEmblems(void)
{
gamedata_t *data = serverGamedata;
INT32 i, j;
UINT8 rtemp;
UINT32 recscore;
tic_t rectime;
UINT16 recrings;
UINT8 recmares;
INT32 curmare;
if (READUINT32(save_p) != ARCHIVEBLOCK_EMBLEMS)
I_Error("Bad $$$.sav at archive block Emblems");
savemoddata = (boolean)READUINT8(save_p); // this one is actually necessary because savemoddata stays false otherwise for some reason.
if (numemblems != READINT32(save_p))
I_Error("numemblems mismatch");
if (numextraemblems != READINT32(save_p))
I_Error("numextraemblems mismatch");
// This shouldn't happen, but if something really fucked up happens and you transfer
// the SERVER player's gamedata over your own CLIENT gamedata,
// then this prevents it from being saved over yours.
data->loaded = false;
M_ClearSecrets(data);
G_ClearRecords(data);
// The rest of this is lifted straight from G_LoadGameData in g_game.c
// TODO: Optimize this to only read information about emblems, unlocks, etc. which actually exist
// There is no need to go all the way up to MAXEMBLEMS when wads are guaranteed to be the same.
data->totalplaytime = READUINT32(save_p);
// TODO put another cipher on these things? meh, I don't care...
for (i = 0; i < NUMMAPS; i++)
if ((data->mapvisited[i] = READUINT8(save_p)) > MV_MAX)
I_Error("Bad $$$.sav dearchiving Emblems");
// To save space, use one bit per collected/achieved/unlocked flag
for (i = 0; i < MAXEMBLEMS;)
{
rtemp = READUINT8(save_p);
for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
data->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)
data->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)
data->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)
data->achieved[j+i] = ((rtemp >> j) & 1);
i += j;
}
data->timesBeaten = READUINT32(save_p);
data->timesBeatenWithEmeralds = READUINT32(save_p);
data->timesBeatenUltimate = READUINT32(save_p);
// Main records
for (i = 0; i < NUMMAPS; ++i)
{
recscore = READUINT32(save_p);
rectime = (tic_t)READUINT32(save_p);
recrings = READUINT16(save_p);
if (recrings > 10000 || recscore > MAXSCORE)
I_Error("Bad $$$.sav dearchiving Emblems");
if (recscore || rectime || recrings)
{
G_AllocMainRecordData((INT16)i, data);
data->mainrecords[i]->score = recscore;
data->mainrecords[i]->time = rectime;
data->mainrecords[i]->rings = recrings;
}
}
// Nights records
for (i = 0; i < NUMMAPS; ++i)
{
if ((recmares = READUINT8(save_p)) == 0)
continue;
G_AllocNightsRecordData((INT16)i, data);
for (curmare = 0; curmare < (recmares+1); ++curmare)
{
data->nightsrecords[i]->score[curmare] = READUINT32(save_p);
data->nightsrecords[i]->grade[curmare] = READUINT8(save_p);
data->nightsrecords[i]->time[curmare] = (tic_t)READUINT32(save_p);
if (data->nightsrecords[i]->grade[curmare] > GRADE_S)
{
I_Error("Bad $$$.sav dearchiving Emblems");
}
}
data->nightsrecords[i]->nummares = recmares;
}
}
static inline void P_ArchiveLuabanksAndConsistency(void)
{
UINT8 i, banksinuse = NUM_LUABANKS;
@ -4507,6 +4730,7 @@ void P_SaveNetGame(boolean resending)
CV_SaveNetVars(&save_p);
P_NetArchiveMisc(resending);
P_NetArchiveEmblems();
// Assign the mobjnumber for pointer tracking
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
@ -4559,6 +4783,7 @@ boolean P_LoadNetGame(boolean reloading)
CV_LoadNetVars(&save_p);
if (!P_NetUnArchiveMisc(reloading))
return false;
P_NetUnArchiveEmblems();
P_NetUnArchivePlayers();
if (gamestate == GS_LEVEL)
{

View file

@ -7460,7 +7460,7 @@ static void P_WriteLetter(void)
{
char *buf, *b;
if (!unlockables[28].unlocked) // pandora's box
if (!serverGamedata->unlocked[28]) // pandora's box
return;
if (modeattacking)
@ -7804,10 +7804,11 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
nextmapoverride = 0;
skipstats = 0;
if (!(netgame || multiplayer || demoplayback))
mapvisited[gamemap-1] |= MV_VISITED;
else if (netgame || multiplayer)
mapvisited[gamemap-1] |= MV_MP; // you want to record that you've been there this session, but not permanently
if (!demoplayback)
{
clientGamedata->mapvisited[gamemap-1] |= MV_VISITED;
serverGamedata->mapvisited[gamemap-1] |= MV_VISITED;
}
levelloading = false;

View file

@ -2937,7 +2937,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
break;
case 441: // Trigger unlockable
if (!(netgame || multiplayer))
{
INT32 trigid = line->args[0];
@ -2948,10 +2947,12 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
unlocktriggers |= 1 << trigid;
// Unlocked something?
if (M_UpdateUnlockablesAndExtraEmblems())
M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
if (M_UpdateUnlockablesAndExtraEmblems(clientGamedata))
{
S_StartSound(NULL, sfx_s3k68);
G_SaveGameData(); // only save if unlocked something
G_SaveGameData(clientGamedata); // only save if unlocked something
}
}
}

View file

@ -675,7 +675,10 @@ void P_Ticker(boolean run)
// Keep track of how long they've been playing!
if (!demoplayback) // Don't increment if a demo is playing.
totalplaytime++;
{
clientGamedata->totalplaytime++;
serverGamedata->totalplaytime++;
}
if (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap))
P_DoSpecialStageStuff();

View file

@ -194,7 +194,7 @@ UINT32 R_GetSkinAvailabilities(void)
return 0;
}
if (unlockables[i].unlocked)
if (clientGamedata->unlocked[i])
{
response |= (1 << unlockShift);
}
@ -242,11 +242,12 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum)
// Force 3.
return true;
}
if (playernum != -1 && players[playernum].bot)
{
//Force 4.
return true;
}
{
// Force 4.
return true;
}
// We will now check if this skin is supposed to be locked or not.
@ -284,7 +285,7 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum)
else
{
// We want to check our global unlockables.
return (unlockables[unlockID].unlocked);
return (clientGamedata->unlocked[unlockID]);
}
}

View file

@ -1692,6 +1692,7 @@ UINT8 soundtestpage = 1;
//
boolean S_PrepareSoundTest(void)
{
gamedata_t *data = clientGamedata;
musicdef_t *def;
INT32 pos = numsoundtestdefs = 0;
@ -1717,9 +1718,9 @@ boolean S_PrepareSoundTest(void)
if (!(def->soundtestpage & soundtestpage))
continue;
soundtestdefs[pos++] = def;
if (def->soundtestcond > 0 && !(mapvisited[def->soundtestcond-1] & MV_BEATEN))
if (def->soundtestcond > 0 && !(data->mapvisited[def->soundtestcond-1] & MV_BEATEN))
continue;
if (def->soundtestcond < 0 && !M_Achieved(-1-def->soundtestcond))
if (def->soundtestcond < 0 && !M_Achieved(-1-def->soundtestcond, data))
continue;
def->allowed = true;
}

View file

@ -2352,7 +2352,7 @@ void I_Quit(void)
#ifndef NONET
D_SaveBan(); // save the ban list
#endif
G_SaveGameData(); // Tails 12-08-2002
G_SaveGameData(clientGamedata); // Tails 12-08-2002
//added:16-02-98: when recording a demo, should exit using 'q' key,
// but sometimes we forget and use 'F10'.. so save here too.
@ -2436,7 +2436,7 @@ void I_Error(const char *error, ...)
if (errorcount == 8)
{
M_SaveConfig(NULL);
G_SaveGameData();
G_SaveGameData(clientGamedata);
}
if (errorcount > 20)
{
@ -2469,7 +2469,7 @@ void I_Error(const char *error, ...)
#ifndef NONET
D_SaveBan(); // save the ban list
#endif
G_SaveGameData(); // Tails 12-08-2002
G_SaveGameData(clientGamedata); // Tails 12-08-2002
// Shutdown. Here might be other errors.
if (demorecording)

View file

@ -1697,7 +1697,7 @@ static void ST_drawNightsRecords(void)
ST_DrawNightsOverlayNum((BASEVIDWIDTH/2 + 56)<<FRACBITS, 160<<FRACBITS, FRACUNIT, aflag, stplyr->lastmarescore, nightsnum, SKINCOLOR_AZURE);
// If new record, say so!
if (!(netgame || multiplayer) && G_GetBestNightsScore(gamemap, stplyr->lastmare + 1) <= stplyr->lastmarescore)
if (!(netgame || multiplayer) && G_GetBestNightsScore(gamemap, stplyr->lastmare + 1, clientGamedata) <= stplyr->lastmarescore)
{
if (stplyr->texttimer & 16)
V_DrawCenteredString(BASEVIDWIDTH/2, 184, V_YELLOWMAP|aflag, "* NEW RECORD *");
@ -2563,8 +2563,11 @@ static void ST_doItemFinderIconsAndSound(void)
emblems[stemblems++] = i;
if (!emblemlocations[i].collected)
if (!(clientGamedata->collected[i] && serverGamedata->collected[i]))
{
// It can be worth collecting again if the server doesn't have it.
++stunfound;
}
if (stemblems >= 16)
break;
@ -2723,7 +2726,7 @@ static void ST_overlayDrawer(void)
ST_drawRaceHUD();
// Emerald Hunt Indicators
if (cv_itemfinder.value && M_SecretUnlocked(SECRET_ITEMFINDER))
if (cv_itemfinder.value && M_SecretUnlocked(SECRET_ITEMFINDER, clientGamedata))
ST_doItemFinderIconsAndSound();
else
ST_doHuntIconsAndSound();

View file

@ -1092,12 +1092,14 @@ void Y_Ticker(void)
S_StartSound(NULL, (gottoken ? sfx_token : sfx_chchng)); // cha-ching!
// Update when done with tally
if (!(netgame || multiplayer) && !demoplayback)
if (!demoplayback)
{
if (M_UpdateUnlockablesAndExtraEmblems())
M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
if (M_UpdateUnlockablesAndExtraEmblems(clientGamedata))
S_StartSound(NULL, sfx_s3k68);
G_SaveGameData();
G_SaveGameData(clientGamedata);
}
}
else if (!(intertic & 1))
@ -1228,12 +1230,14 @@ void Y_Ticker(void)
S_StartSound(NULL, (gottoken ? sfx_token : sfx_chchng)); // cha-ching!
// Update when done with tally
if (!(netgame || multiplayer) && !demoplayback)
if (!demoplayback)
{
if (M_UpdateUnlockablesAndExtraEmblems())
M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
if (M_UpdateUnlockablesAndExtraEmblems(clientGamedata))
S_StartSound(NULL, sfx_s3k68);
G_SaveGameData();
G_SaveGameData(clientGamedata);
}
}
else if (!(intertic & 1))