Allow obtaining ring, time & grade emblems in regular SP gameplay.

This commit is contained in:
spherallic 2022-03-02 23:47:15 +01:00 committed by Sally Coolatta
parent 13c30ace2e
commit 86b4f93610
10 changed files with 279 additions and 170 deletions

View file

@ -2339,7 +2339,7 @@ static void Got_Clearscores(UINT8 **cp, INT32 playernum)
}
for (i = 0; i < MAXPLAYERS; i++)
players[i].score = 0;
players[i].score = players[i].recordscore = 0;
CONS_Printf(M_GetText("Scores have been reset by the server.\n"));
}

View file

@ -453,7 +453,8 @@ typedef struct player_s
INT32 skin;
UINT32 availabilities;
UINT32 score; // player score
UINT32 score; // player score (total)
UINT32 recordscore; // player score (per life / map)
fixed_t dashspeed; // dashing speed
fixed_t normalspeed; // Normal ground

View file

@ -519,140 +519,154 @@ UINT8 G_GetBestNightsGrade(INT16 map, UINT8 mare, gamedata_t *data)
}
// For easy adding of NiGHTS records
void G_AddTempNightsRecords(UINT32 pscore, tic_t ptime, UINT8 mare)
void G_AddTempNightsRecords(player_t *player, UINT32 pscore, tic_t ptime, UINT8 mare)
{
ntemprecords.score[mare] = pscore;
ntemprecords.grade[mare] = P_GetGrade(pscore, gamemap, mare - 1);
ntemprecords.time[mare] = ptime;
const UINT8 playerID = player - players;
I_Assert(player != NULL);
ntemprecords[playerID].score[mare] = pscore;
ntemprecords[playerID].grade[mare] = P_GetGrade(pscore, gamemap, mare - 1);
ntemprecords[playerID].time[mare] = ptime;
// Update nummares
// Note that mare "0" is overall, mare "1" is the first real mare
if (ntemprecords.nummares < mare)
ntemprecords.nummares = mare;
if (ntemprecords[playerID].nummares < mare)
ntemprecords[playerID].nummares = mare;
}
//
// G_UpdateRecordReplays
// G_SetMainRecords
//
// Update replay files/data, etc. for Record Attack
// See G_SetNightsRecords for NiGHTS Attack.
//
static void G_UpdateRecordReplays(gamedata_t *data)
static void G_SetMainRecords(gamedata_t *data, player_t *player)
{
const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
char *gpath;
char lastdemo[256], bestdemo[256];
UINT8 earnedEmblems;
I_Assert(player != NULL);
// Record new best time
if (!data->mainrecords[gamemap-1])
G_AllocMainRecordData(gamemap-1, data);
if (players[consoleplayer].score > data->mainrecords[gamemap-1]->score)
data->mainrecords[gamemap-1]->score = players[consoleplayer].score;
if (player->recordscore > data->mainrecords[gamemap-1]->score)
data->mainrecords[gamemap-1]->score = player->recordscore;
if ((data->mainrecords[gamemap-1]->time == 0) || (players[consoleplayer].realtime < data->mainrecords[gamemap-1]->time))
data->mainrecords[gamemap-1]->time = players[consoleplayer].realtime;
if ((data->mainrecords[gamemap-1]->time == 0) || (player->realtime < data->mainrecords[gamemap-1]->time))
data->mainrecords[gamemap-1]->time = player->realtime;
if ((UINT16)(players[consoleplayer].rings) > data->mainrecords[gamemap-1]->rings)
data->mainrecords[gamemap-1]->rings = (UINT16)(players[consoleplayer].rings);
if ((UINT16)(player->rings) > data->mainrecords[gamemap-1]->rings)
data->mainrecords[gamemap-1]->rings = (UINT16)(player->rings);
// Save demo!
bestdemo[255] = '\0';
lastdemo[255] = '\0';
G_SetDemoTime(players[consoleplayer].realtime, players[consoleplayer].score, (UINT16)(players[consoleplayer].rings));
G_CheckDemoStatus();
I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755);
I_mkdir(va("%s"PATHSEP"replay"PATHSEP"%s", srb2home, timeattackfolder), 0755);
if ((gpath = malloc(glen)) == NULL)
I_Error("Out of memory for replay filepath\n");
sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (FIL_FileExists(lastdemo))
if (modeattacking)
{
UINT8 *buf;
size_t len = FIL_ReadFile(lastdemo, &buf);
const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
char *gpath;
char lastdemo[256], bestdemo[256];
snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1)
{ // Better time, save this demo.
if (FIL_FileExists(bestdemo))
remove(bestdemo);
FIL_WriteFile(bestdemo, buf, len);
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo);
// Save demo!
bestdemo[255] = '\0';
lastdemo[255] = '\0';
G_SetDemoTime(player->realtime, player->recordscore, (UINT16)(player->rings));
G_CheckDemoStatus();
I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755);
I_mkdir(va("%s"PATHSEP"replay"PATHSEP"%s", srb2home, timeattackfolder), 0755);
if ((gpath = malloc(glen)) == NULL)
I_Error("Out of memory for replay filepath\n");
sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (FIL_FileExists(lastdemo))
{
UINT8 *buf;
size_t len = FIL_ReadFile(lastdemo, &buf);
snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1)
{ // Better time, save this demo.
if (FIL_FileExists(bestdemo))
remove(bestdemo);
FIL_WriteFile(bestdemo, buf, len);
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo);
}
snprintf(bestdemo, 255, "%s-%s-score-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<1)))
{ // Better score, save this demo.
if (FIL_FileExists(bestdemo))
remove(bestdemo);
FIL_WriteFile(bestdemo, buf, len);
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW HIGH SCORE!"), M_GetText("Saved replay as"), bestdemo);
}
snprintf(bestdemo, 255, "%s-%s-rings-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<2)))
{ // Better rings, save this demo.
if (FIL_FileExists(bestdemo))
remove(bestdemo);
FIL_WriteFile(bestdemo, buf, len);
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW MOST RINGS!"), M_GetText("Saved replay as"), bestdemo);
}
//CONS_Printf("%s '%s'\n", M_GetText("Saved replay as"), lastdemo);
Z_Free(buf);
}
snprintf(bestdemo, 255, "%s-%s-score-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<1)))
{ // Better score, save this demo.
if (FIL_FileExists(bestdemo))
remove(bestdemo);
FIL_WriteFile(bestdemo, buf, len);
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW HIGH SCORE!"), M_GetText("Saved replay as"), bestdemo);
}
snprintf(bestdemo, 255, "%s-%s-rings-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<2)))
{ // Better rings, save this demo.
if (FIL_FileExists(bestdemo))
remove(bestdemo);
FIL_WriteFile(bestdemo, buf, len);
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW MOST RINGS!"), M_GetText("Saved replay as"), bestdemo);
}
//CONS_Printf("%s '%s'\n", M_GetText("Saved replay as"), lastdemo);
Z_Free(buf);
free(gpath);
}
free(gpath);
// Check emblems when level data is updated
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(gamedata_t *data)
static void G_SetNightsRecords(gamedata_t *data, player_t *player)
{
INT32 i;
nightsdata_t *const ntemprecord = &ntemprecords[player - players];
UINT32 totalscore = 0;
tic_t totaltime = 0;
INT32 i;
UINT8 earnedEmblems;
const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
char *gpath;
char lastdemo[256], bestdemo[256];
if (!ntemprecords.nummares)
if (!ntemprecord->nummares)
{
return;
}
// Set overall
{
UINT8 totalrank = 0, realrank = 0;
for (i = 1; i <= ntemprecords.nummares; ++i)
for (i = 1; i <= ntemprecord->nummares; ++i)
{
totalscore += ntemprecords.score[i];
totalrank += ntemprecords.grade[i];
totaltime += ntemprecords.time[i];
totalscore += ntemprecord->score[i];
totalrank += ntemprecord->grade[i];
totaltime += ntemprecord->time[i];
}
// Determine overall grade
realrank = (UINT8)((FixedDiv((fixed_t)totalrank << FRACBITS, ntemprecords.nummares << FRACBITS) + (FRACUNIT/2)) >> FRACBITS);
realrank = (UINT8)((FixedDiv((fixed_t)totalrank << FRACBITS, ntemprecord->nummares << FRACBITS) + (FRACUNIT/2)) >> FRACBITS);
// You need ALL rainbow As to get a rainbow A overall
if (realrank == GRADE_S && (totalrank / ntemprecords.nummares) != GRADE_S)
if (realrank == GRADE_S && (totalrank / ntemprecord->nummares) != GRADE_S)
{
realrank = GRADE_A;
}
ntemprecords.score[0] = totalscore;
ntemprecords.grade[0] = realrank;
ntemprecords.time[0] = totaltime;
ntemprecord->score[0] = totalscore;
ntemprecord->grade[0] = realrank;
ntemprecord->time[0] = totaltime;
}
// Now take all temp records and put them in the actual records
@ -660,71 +674,85 @@ void G_SetNightsRecords(gamedata_t *data)
nightsdata_t *maprecords;
if (!data->nightsrecords[gamemap-1])
{
G_AllocNightsRecordData(gamemap-1, data);
}
maprecords = data->nightsrecords[gamemap-1];
if (maprecords->nummares != ntemprecords.nummares)
maprecords->nummares = ntemprecords.nummares;
for (i = 0; i < ntemprecords.nummares + 1; ++i)
if (maprecords->nummares != ntemprecord->nummares)
{
if (maprecords->score[i] < ntemprecords.score[i])
maprecords->score[i] = ntemprecords.score[i];
if (maprecords->grade[i] < ntemprecords.grade[i])
maprecords->grade[i] = ntemprecords.grade[i];
if (!maprecords->time[i] || maprecords->time[i] > ntemprecords.time[i])
maprecords->time[i] = ntemprecords.time[i];
maprecords->nummares = ntemprecord->nummares;
}
for (i = 0; i < ntemprecord->nummares + 1; ++i)
{
if (maprecords->score[i] < ntemprecord->score[i])
maprecords->score[i] = ntemprecord->score[i];
if (maprecords->grade[i] < ntemprecord->grade[i])
maprecords->grade[i] = ntemprecord->grade[i];
if (!maprecords->time[i] || maprecords->time[i] > ntemprecord->time[i])
maprecords->time[i] = ntemprecord->time[i];
}
}
memset(&ntemprecords, 0, sizeof(nightsdata_t));
memset(&ntemprecords[player - players], 0, sizeof(nightsdata_t));
// Save demo!
bestdemo[255] = '\0';
lastdemo[255] = '\0';
G_SetDemoTime(totaltime, totalscore, 0);
G_CheckDemoStatus();
I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755);
I_mkdir(va("%s"PATHSEP"replay"PATHSEP"%s", srb2home, timeattackfolder), 0755);
if ((gpath = malloc(glen)) == NULL)
I_Error("Out of memory for replay filepath\n");
sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (FIL_FileExists(lastdemo))
if (modeattacking)
{
UINT8 *buf;
size_t len = FIL_ReadFile(lastdemo, &buf);
const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
char *gpath;
char lastdemo[256], bestdemo[256];
snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, skins[cv_chooseskin.value-1].name);;
if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1)
{ // Better time, save this demo.
if (FIL_FileExists(bestdemo))
remove(bestdemo);
FIL_WriteFile(bestdemo, buf, len);
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo);
// Save demo!
bestdemo[255] = '\0';
lastdemo[255] = '\0';
G_SetDemoTime(totaltime, totalscore, 0);
G_CheckDemoStatus();
I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755);
I_mkdir(va("%s"PATHSEP"replay"PATHSEP"%s", srb2home, timeattackfolder), 0755);
if ((gpath = malloc(glen)) == NULL)
I_Error("Out of memory for replay filepath\n");
sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (FIL_FileExists(lastdemo))
{
UINT8 *buf;
size_t len = FIL_ReadFile(lastdemo, &buf);
snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, skins[cv_chooseskin.value-1].name);;
if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1)
{ // Better time, save this demo.
if (FIL_FileExists(bestdemo))
remove(bestdemo);
FIL_WriteFile(bestdemo, buf, len);
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo);
}
snprintf(bestdemo, 255, "%s-%s-score-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<1)))
{ // Better score, save this demo.
if (FIL_FileExists(bestdemo))
remove(bestdemo);
FIL_WriteFile(bestdemo, buf, len);
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW HIGH SCORE!"), M_GetText("Saved replay as"), bestdemo);
}
//CONS_Printf("%s '%s'\n", M_GetText("Saved replay as"), lastdemo);
Z_Free(buf);
}
snprintf(bestdemo, 255, "%s-%s-score-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<1)))
{ // Better score, save this demo.
if (FIL_FileExists(bestdemo))
remove(bestdemo);
FIL_WriteFile(bestdemo, buf, len);
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW HIGH SCORE!"), M_GetText("Saved replay as"), bestdemo);
}
//CONS_Printf("%s '%s'\n", M_GetText("Saved replay as"), lastdemo);
Z_Free(buf);
free(gpath);
}
free(gpath);
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
Nextmap_OnChange();
@ -2287,7 +2315,7 @@ void G_Ticker(boolean run)
p->lives = startinglivesbalance[0];
p->continues = 1;
p->score = 0;
p->score = p->recordscore = 0;
// The latter two should clear by themselves, but just in case
p->pflags &= ~(PF_TAGIT|PF_GAMETYPEOVER|PF_FULLSTASIS);
@ -2490,6 +2518,7 @@ static inline void G_PlayerFinishLevel(INT32 player)
memset(p->powers, 0, sizeof (p->powers));
p->ringweapons = 0;
p->recordscore = 0;
p->mo->flags2 &= ~MF2_SHADOW; // cancel invisibility
P_FlashPal(p, 0, 0); // Resets
@ -2513,6 +2542,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
{
player_t *p;
INT32 score;
INT32 recordscore;
INT32 lives;
INT32 continues;
fixed_t camerascale;
@ -3165,6 +3195,7 @@ void G_DoReborn(INT32 playernum)
{
if (!playeringame[i])
continue;
players[i].recordscore = 0;
players[i].starpostscale = 0;
players[i].starpostangle = 0;
players[i].starposttime = 0;
@ -3844,15 +3875,16 @@ static INT16 RandMap(UINT32 tolflags, INT16 pprevmap)
//
// G_UpdateVisited
//
static void G_UpdateVisited(gamedata_t *data, boolean silent)
static void G_UpdateVisited(gamedata_t *data, player_t *player, boolean silent)
{
boolean spec = G_IsSpecialStage(gamemap);
// Update visitation flags?
if (!demoplayback
&& G_CoopGametype() // Campaign mode
&& !stagefailed) // Did not fail the stage
{
UINT8 earnedEmblems;
UINT16 totalrings = 0;
INT32 i;
// Update visitation flags
data->mapvisited[gamemap-1] |= MV_BEATEN;
@ -3861,36 +3893,62 @@ static void G_UpdateVisited(gamedata_t *data, boolean silent)
if (ultimatemode)
data->mapvisited[gamemap-1] |= MV_ULTIMATE;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
{
continue;
}
totalrings += players[i].rings;
}
// 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)
if (nummaprings > 0 && totalrings >= nummaprings)
{
data->mapvisited[gamemap-1] |= MV_PERFECT;
if (modeattacking)
data->mapvisited[gamemap-1] |= MV_PERFECTRA;
}
if (!spec)
if (!G_IsSpecialStage(gamemap))
{
// 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))
data->mapvisited[gamemap-1] |= MV_ALLEMERALDS;
}
if ((earnedEmblems = M_CompletionEmblems(data)) && !silent)
{
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
}
if (silent)
{
if (modeattacking)
M_CheckLevelEmblems(data);
M_CheckLevelEmblems(data);
}
else
{
if (modeattacking == ATTACKING_RECORD)
G_UpdateRecordReplays(data);
else if (modeattacking == ATTACKING_NIGHTS)
G_SetNightsRecords(data);
if (mapheaderinfo[gamemap-1]->menuflags & LF2_RECORDATTACK)
G_SetMainRecords(data, player);
else if (mapheaderinfo[gamemap-1]->menuflags & LF2_NIGHTSATTACK)
G_SetNightsRecords(data, player);
}
}
}
if ((earnedEmblems = M_CompletionEmblems(data)) && !silent)
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
static void G_UpdateAllVisited(void)
{
// Update server
G_UpdateVisited(serverGamedata, &players[serverplayer], true);
// Update client
G_UpdateVisited(clientGamedata, &players[consoleplayer], false);
if (splitscreen)
{
// Allow P2 to get emblems too, why not :)
G_UpdateVisited(clientGamedata, &players[secondarydisplayplayer], false);
}
}
@ -3914,6 +3972,9 @@ static boolean CanSaveLevel(INT32 mapnum)
static void G_HandleSaveLevel(void)
{
// Update records & emblems
G_UpdateAllVisited();
// do this before running the intermission or custom cutscene, mostly for the sake of marathon mode but it also massively reduces redundant file save events in f_finale.c
if (nextmap >= 1100-1)
{
@ -4092,8 +4153,6 @@ static void G_DoCompleted(void)
if ((skipstats && !modeattacking) || (modeattacking && stagefailed) || (intertype == int_none))
{
G_UpdateVisited(serverGamedata, true);
G_UpdateVisited(clientGamedata, false);
G_HandleSaveLevel();
G_AfterIntermission();
}
@ -4102,8 +4161,6 @@ static void G_DoCompleted(void)
G_SetGamestate(GS_INTERMISSION);
Y_StartIntermission();
Y_LoadIntermissionData();
G_UpdateVisited(serverGamedata, true);
G_UpdateVisited(clientGamedata, false);
G_HandleSaveLevel();
}
}
@ -4992,6 +5049,7 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
players[i].playerstate = PST_REBORN;
players[i].starpostscale = players[i].starpostangle = players[i].starpostnum = players[i].starposttime = 0;
players[i].starpostx = players[i].starposty = players[i].starpostz = 0;
players[i].recordscore = 0;
if (netgame || multiplayer)
{

View file

@ -260,8 +260,7 @@ 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(gamedata_t *data);
void G_AddTempNightsRecords(player_t *player, UINT32 pscore, tic_t ptime, UINT8 mare);
FUNCMATH INT32 G_TicsToHours(tic_t tics);
FUNCMATH INT32 G_TicsToMinutes(tic_t tics, boolean full);

View file

@ -114,6 +114,7 @@ enum player_e
player_skin,
player_availabilities,
player_score,
player_recordscore,
player_dashspeed,
player_normalspeed,
player_runspeed,
@ -260,6 +261,7 @@ static const char *const player_opt[] = {
"skin",
"availabilities",
"score",
"recordscore",
"dashspeed",
"normalspeed",
"runspeed",
@ -495,6 +497,9 @@ static int player_get(lua_State *L)
case player_score:
lua_pushinteger(L, plr->score);
break;
case player_recordscore:
lua_pushinteger(L, plr->recordscore);
break;
case player_dashspeed:
lua_pushfixed(L, plr->dashspeed);
break;
@ -957,6 +962,9 @@ static int player_set(lua_State *L)
case player_score:
plr->score = (UINT32)luaL_checkinteger(L, 3);
break;
case player_recordscore:
plr->recordscore = (UINT32)luaL_checkinteger(L, 3);
break;
case player_dashspeed:
plr->dashspeed = luaL_checkfixed(L, 3);
break;

View file

@ -45,7 +45,7 @@ INT32 numemblems = 0;
INT32 numextraemblems = 0;
// Temporary holding place for nights data for the current map
nightsdata_t ntemprecords;
nightsdata_t ntemprecords[MAXPLAYERS];
// Create a new gamedata_t, for start-up
gamedata_t *M_NewGameDataStruct(void)

View file

@ -181,7 +181,7 @@ typedef struct
#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;
extern nightsdata_t ntemprecords[MAXPLAYERS];
// GAMEDATA STRUCTURE
// Everything that would get saved in gamedata.dat

View file

@ -179,6 +179,7 @@ static void P_NetArchivePlayers(void)
WRITEINT32(save_p, players[i].skin);
WRITEUINT32(save_p, players[i].availabilities);
WRITEUINT32(save_p, players[i].score);
WRITEUINT32(save_p, players[i].recordscore);
WRITEFIXED(save_p, players[i].dashspeed);
WRITESINT8(save_p, players[i].lives);
WRITESINT8(save_p, players[i].continues);
@ -407,6 +408,7 @@ static void P_NetUnArchivePlayers(void)
players[i].skin = READINT32(save_p);
players[i].availabilities = READUINT32(save_p);
players[i].score = READUINT32(save_p);
players[i].recordscore = READUINT32(save_p);
players[i].dashspeed = READFIXED(save_p); // dashing speed
players[i].lives = READSINT8(save_p);
players[i].continues = READSINT8(save_p); // continues that player has acquired
@ -4342,8 +4344,6 @@ static void P_NetArchiveMisc(boolean resending)
WRITEUINT32(save_p, hidetime);
WRITEUINT32(save_p, unlocktriggers);
// Is it paused?
if (paused)
WRITEUINT8(save_p, 0x2f);
@ -4442,8 +4442,6 @@ 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;
@ -4451,7 +4449,6 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading)
return true;
}
static inline void P_NetArchiveEmblems(void)
{
gamedata_t *data = serverGamedata;
@ -4550,6 +4547,27 @@ static inline void P_NetArchiveEmblems(void)
WRITEUINT32(save_p, data->nightsrecords[i]->time[curmare]);
}
}
// Mid-map stuff
WRITEUINT32(save_p, unlocktriggers);
for (i = 0; i < MAXPLAYERS; i++)
{
if (!ntemprecords[i].nummares)
{
WRITEUINT8(save_p, 0);
continue;
}
WRITEUINT8(save_p, ntemprecords[i].nummares);
for (curmare = 0; curmare < (ntemprecords[i].nummares + 1); ++curmare)
{
WRITEUINT32(save_p, ntemprecords[i].score[curmare]);
WRITEUINT8(save_p, ntemprecords[i].grade[curmare]);
WRITEUINT32(save_p, ntemprecords[i].time[curmare]);
}
}
}
static inline void P_NetUnArchiveEmblems(void)
@ -4569,9 +4587,9 @@ static inline void P_NetUnArchiveEmblems(void)
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");
I_Error("Bad $$$.sav dearchiving Emblems (numemblems mismatch)");
if (numextraemblems != READINT32(save_p))
I_Error("numextraemblems mismatch");
I_Error("Bad $$$.sav dearchiving Emblems (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,
@ -4590,7 +4608,7 @@ static inline void P_NetUnArchiveEmblems(void)
// 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");
I_Error("Bad $$$.sav dearchiving Emblems (invalid visit flags)");
// To save space, use one bit per collected/achieved/unlocked flag
for (i = 0; i < MAXEMBLEMS;)
@ -4634,7 +4652,7 @@ static inline void P_NetUnArchiveEmblems(void)
recrings = READUINT16(save_p);
if (recrings > 10000 || recscore > MAXSCORE)
I_Error("Bad $$$.sav dearchiving Emblems");
I_Error("Bad $$$.sav dearchiving Emblems (invalid score)");
if (recscore || rectime || recrings)
{
@ -4661,12 +4679,35 @@ static inline void P_NetUnArchiveEmblems(void)
if (data->nightsrecords[i]->grade[curmare] > GRADE_S)
{
I_Error("Bad $$$.sav dearchiving Emblems");
I_Error("Bad $$$.sav dearchiving Emblems (invalid grade)");
}
}
data->nightsrecords[i]->nummares = recmares;
}
// Mid-map stuff
unlocktriggers = READUINT32(save_p);
for (i = 0; i < MAXPLAYERS; ++i)
{
if ((recmares = READUINT8(save_p)) == 0)
continue;
for (curmare = 0; curmare < (recmares+1); ++curmare)
{
ntemprecords[i].score[curmare] = READUINT32(save_p);
ntemprecords[i].grade[curmare] = READUINT8(save_p);
ntemprecords[i].time[curmare] = (tic_t)READUINT32(save_p);
if (ntemprecords[i].grade[curmare] > GRADE_S)
{
I_Error("Bad $$$.sav dearchiving Emblems (invalid temp grade)");
}
}
ntemprecords[i].nummares = recmares;
}
}
static inline void P_ArchiveLuabanksAndConsistency(void)

View file

@ -6985,7 +6985,7 @@ static void P_InitLevelSettings(void)
stagefailed = G_IsSpecialStage(gamemap);
// Reset temporary record data
memset(&ntemprecords, 0, sizeof(nightsdata_t));
memset(&ntemprecords, 0, sizeof(ntemprecords));
// earthquake camera
memset(&quake,0,sizeof(struct quake));

View file

@ -882,8 +882,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
}
// Add score to leaderboards now
if (!(netgame||multiplayer) && P_IsLocalPlayer(&players[i]))
G_AddTempNightsRecords(players[i].marescore, leveltime - player->marebegunat, players[i].mare + 1);
G_AddTempNightsRecords(player, players[i].marescore, leveltime - player->marebegunat, players[i].mare + 1);
// transfer scores anyway
players[i].totalmarescore += players[i].marescore;
@ -909,8 +908,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
player->finishedrings = (INT16)(player->rings);
// Add score to temp leaderboards
if (!(netgame||multiplayer) && P_IsLocalPlayer(player))
G_AddTempNightsRecords(player->marescore, leveltime - player->marebegunat, (UINT8)(oldmare + 1));
G_AddTempNightsRecords(player, player->marescore, leveltime - player->marebegunat, (UINT8)(oldmare + 1));
// Starting a new mare, transfer scores
player->totalmarescore += player->marescore;
@ -1440,6 +1438,10 @@ void P_AddPlayerScore(player_t *player, UINT32 amount)
if (player->score > MAXSCORE)
player->score = MAXSCORE;
player->recordscore += amount;
if (player->recordscore > MAXSCORE)
player->recordscore = MAXSCORE;
// check for extra lives every 50000 pts
if (!ultimatemode && !modeattacking && player->score > oldscore && player->score % 50000 < amount && (gametyperules & GTR_LIVES))
{
@ -11732,7 +11734,7 @@ void P_PlayerThink(player_t *player)
if (player->spectator)
{
if (!(gametyperules & GTR_CAMPAIGN))
player->score = 0;
player->score = player->recordscore = 0;
}
else if ((netgame || multiplayer) && player->lives <= 0 && !G_CoopGametype())
{