* New, pretty save select! Requires patch.dta to really appreciate.

* Changed the save format a little more, since I had free reign over it.
* Modified the string drawing functions to not awkwardly clip at the very edges of the screen, considering the relevant patch drawing functions avoid out-of-range memory writes.
This commit is contained in:
toasterbabe 2017-08-20 23:18:47 +01:00
parent 42211f02b4
commit 706eb5efeb
9 changed files with 357 additions and 329 deletions

View File

@ -1114,7 +1114,7 @@ void F_StartCredits(void)
M_ClearMenus(true); M_ClearMenus(true);
// Save the second we enter the credits // Save the second we enter the credits
if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && cursaveslot >= 0) if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && cursaveslot > 0)
G_SaveGame((UINT32)cursaveslot); G_SaveGame((UINT32)cursaveslot);
if (creditscutscene) if (creditscutscene)
@ -1252,7 +1252,7 @@ static boolean drawemblem = false, drawchaosemblem = false;
void F_StartGameEvaluation(void) void F_StartGameEvaluation(void)
{ {
// Credits option in secrets menu // Credits option in secrets menu
if (cursaveslot == -2) if (cursaveslot == -1)
{ {
F_StartGameEnd(); F_StartGameEnd();
return; return;
@ -1266,7 +1266,7 @@ void F_StartGameEvaluation(void)
// Save the second we enter the evaluation // Save the second we enter the evaluation
// We need to do this again! Remember, it's possible a mod designed skipped // We need to do this again! Remember, it's possible a mod designed skipped
// the credits sequence! // the credits sequence!
if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && cursaveslot >= 0) if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && cursaveslot > 0)
G_SaveGame((UINT32)cursaveslot); G_SaveGame((UINT32)cursaveslot);
gameaction = ga_nothing; gameaction = ga_nothing;

View File

@ -76,7 +76,7 @@ INT16 gamemap = 1;
INT16 maptol; INT16 maptol;
UINT8 globalweather = 0; UINT8 globalweather = 0;
INT32 curWeather = PRECIP_NONE; INT32 curWeather = PRECIP_NONE;
INT32 cursaveslot = -1; // Auto-save 1p savegame slot INT32 cursaveslot = 0; // Auto-save 1p savegame slot
//INT16 lastmapsaved = 0; // Last map we auto-saved at //INT16 lastmapsaved = 0; // Last map we auto-saved at
INT16 lastmaploaded = 0; // Last map the game loaded INT16 lastmaploaded = 0; // Last map the game loaded
boolean gamecomplete = false; boolean gamecomplete = false;
@ -3132,7 +3132,7 @@ static void G_DoContinued(void)
tokenlist = 0; tokenlist = 0;
token = 0; token = 0;
if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) && (!modifiedgame || savemoddata) && cursaveslot >= 0) if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) && (!modifiedgame || savemoddata) && cursaveslot > 0)
G_SaveGameOver((UINT32)cursaveslot, true); G_SaveGameOver((UINT32)cursaveslot, true);
// Reset # of lives // Reset # of lives
@ -3475,59 +3475,6 @@ void G_SaveGameData(void)
#define VERSIONSIZE 16 #define VERSIONSIZE 16
#ifdef SAVEGAMES_OTHERVERSIONS
static INT16 startonmapnum = 0;
//
// User wants to load a savegame from a different version?
//
static void M_ForceLoadGameResponse(INT32 ch)
{
if (ch != 'y' && ch != KEY_ENTER)
{
//refused
Z_Free(savebuffer);
save_p = savebuffer = NULL;
startonmapnum = 0;
M_SetupNextMenu(&SP_LoadDef);
return;
}
// pick up where we left off.
save_p += VERSIONSIZE;
if (!P_LoadGame(startonmapnum))
{
M_ClearMenus(true); // so ESC backs out to title
M_StartMessage(M_GetText("Savegame file corrupted\n\nPress ESC\n"), NULL, MM_NOTHING);
Command_ExitGame_f();
Z_Free(savebuffer);
save_p = savebuffer = NULL;
startonmapnum = 0;
// no cheating!
memset(&savedata, 0, sizeof(savedata));
return;
}
// done
Z_Free(savebuffer);
save_p = savebuffer = NULL;
startonmapnum = 0;
//set cursaveslot to -1 so nothing gets saved.
cursaveslot = -1;
displayplayer = consoleplayer;
multiplayer = splitscreen = false;
if (setsizeneeded)
R_ExecuteSetViewSize();
M_ClearMenus(true);
CON_ToggleOff();
}
#endif
// //
// G_InitFromSavegame // G_InitFromSavegame
// Can be called by the startup code or the menu task. // Can be called by the startup code or the menu task.
@ -3687,7 +3634,6 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives)
{ {
char temp[sizeof(timeattackfolder)]; char temp[sizeof(timeattackfolder)];
INT32 fake; // Dummy variable
UINT8 *end_p = savebuffer + length; UINT8 *end_p = savebuffer + length;
UINT8 *lives_p; UINT8 *lives_p;
SINT8 pllives; SINT8 pllives;
@ -3696,25 +3642,20 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives)
// Version check // Version check
memset(vcheck, 0, sizeof (vcheck)); memset(vcheck, 0, sizeof (vcheck));
sprintf(vcheck, "version %d", VERSION); sprintf(vcheck, "version %d", VERSION);
#ifndef SAVEGAMES_OTHERVERSIONS if (strcmp((const char *)save_p, (const char *)vcheck)) BADSAVE
if (strcmp((const char *)save_p, (const char *)vcheck))
BADSAVE;
#endif
save_p += VERSIONSIZE; save_p += VERSIONSIZE;
// P_UnArchiveMisc() // P_UnArchiveMisc()
(void)READINT16(save_p); (void)READINT16(save_p);
CHECKPOS CHECKPOS
fake = READUINT16(save_p)-357; // emeralds (void)READUINT16(save_p); // emeralds
CHECKPOS CHECKPOS
READSTRINGN(save_p, temp, sizeof(temp)); // mod it belongs to READSTRINGN(save_p, temp, sizeof(temp)); // mod it belongs to
if (strcmp(temp, timeattackfolder)) BADSAVE if (strcmp(temp, timeattackfolder)) BADSAVE
// P_UnArchivePlayer() // P_UnArchivePlayer()
CHECKPOS CHECKPOS
(void)READUINT8(save_p); (void)READUINT16(save_p);
CHECKPOS
(void)READUINT8(save_p);
CHECKPOS CHECKPOS
WRITEUINT8(save_p, numgameovers); WRITEUINT8(save_p, numgameovers);
@ -3733,14 +3674,6 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives)
CHECKPOS CHECKPOS
(void)READINT32(save_p); // continues (void)READINT32(save_p); // continues
if (fake & (1<<10))
{
CHECKPOS
(void)READUINT8(save_p);
CHECKPOS
(void)READUINT8(save_p); // because why not.
}
// File end marker check // File end marker check
CHECKPOS CHECKPOS
if (READUINT8(save_p) != 0x1d) BADSAVE; if (READUINT8(save_p) != 0x1d) BADSAVE;
@ -3768,7 +3701,7 @@ cleanup:
// //
void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, boolean SSSG, boolean FLS) void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, boolean SSSG, boolean FLS)
{ {
UINT8 color = 0; UINT8 color = skins[pickedchar].prefcolor;
paused = false; paused = false;
if (demoplayback) if (demoplayback)
@ -3780,10 +3713,8 @@ void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, b
if (savedata.lives > 0) if (savedata.lives > 0)
{ {
color = savedata.skincolor; if ((botingame = ((botskin = savedata.botskin) != 0)))
botskin = savedata.botskin; botcolor = skins[botskin-1].prefcolor;
botcolor = savedata.botcolor;
botingame = (botskin != 0);
} }
else if (splitscreen != SSSG) else if (splitscreen != SSSG)
{ {
@ -3791,8 +3722,7 @@ void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, b
SplitScreen_OnChange(); SplitScreen_OnChange();
} }
if (!color) color = skins[pickedchar].prefcolor;
color = skins[pickedchar].prefcolor;
SetPlayerSkinByNum(consoleplayer, pickedchar); SetPlayerSkinByNum(consoleplayer, pickedchar);
CV_StealthSet(&cv_skin, skins[pickedchar].name); CV_StealthSet(&cv_skin, skins[pickedchar].name);
CV_StealthSetValue(&cv_playercolor, color); CV_StealthSetValue(&cv_playercolor, color);

View File

@ -172,7 +172,8 @@ static char joystickInfo[8][25];
static UINT32 serverlistpage; static UINT32 serverlistpage;
#endif #endif
static saveinfo_t savegameinfo[MAXSAVEGAMES]; // Extra info about the save games. static UINT8 numsaves = 0;
static saveinfo_t* savegameinfo = NULL; // Extra info about the save games.
INT16 startmap; // Mario, NiGHTS, or just a plain old normal game? INT16 startmap; // Mario, NiGHTS, or just a plain old normal game?
@ -4406,12 +4407,12 @@ static void M_DrawLevelPlatterMenu(void)
V_DrawScaledPatch(lsbasex + cursorx-17, lsbasey+50+lsoffs[0], 0, W_CachePatchName("M_CURSOR", PU_CACHE)); V_DrawScaledPatch(lsbasex + cursorx-17, lsbasey+50+lsoffs[0], 0, W_CachePatchName("M_CURSOR", PU_CACHE));
// handle movement of cursor box // handle movement of cursor box
if (abs(lsoffs[0]) > 1) if (lsoffs[0] > 1 || lsoffs[0] < -1)
lsoffs[0] = 2*lsoffs[0]/3; lsoffs[0] = 2*lsoffs[0]/3;
else else
lsoffs[0] = 0; lsoffs[0] = 0;
if (abs(lsoffs[1]) > 1) if (lsoffs[1] > 1 || lsoffs[1] < -1)
lsoffs[1] = 2*lsoffs[1]/3; lsoffs[1] = 2*lsoffs[1]/3;
else else
lsoffs[1] = 0; lsoffs[1] = 0;
@ -5395,7 +5396,7 @@ static void M_LevelSelectWarp(INT32 choice)
G_LoadGame((UINT32)cursaveslot, startmap); G_LoadGame((UINT32)cursaveslot, startmap);
else else
{ {
cursaveslot = -1; cursaveslot = 0;
M_SetupChoosePlayer(0); M_SetupChoosePlayer(0);
} }
} }
@ -5970,7 +5971,7 @@ static void M_CustomWarp(INT32 choice)
static void M_Credits(INT32 choice) static void M_Credits(INT32 choice)
{ {
(void)choice; (void)choice;
cursaveslot = -2; cursaveslot = -1;
M_ClearMenus(true); M_ClearMenus(true);
F_StartCredits(); F_StartCredits();
} }
@ -6028,152 +6029,264 @@ static void M_LoadGameLevelSelect(INT32 choice)
// LOAD GAME MENU // LOAD GAME MENU
// ============== // ==============
static INT32 saveSlotSelected = 0; static INT32 saveSlotSelected = 1;
static short menumovedir = 0; static INT32 loadgamescroll = 0;
static UINT8 loadgameoffset = 0;
static void M_DrawLoadGameData(void) static void M_DrawLoadGameData(void)
{ {
INT32 ecks; INT32 i, savetodraw, x, y;
INT32 i; skin_t *charskin = NULL;
ecks = SP_LoadDef.x + 24; for (i = -2; i <= 2; i++)
M_DrawTextBox(SP_LoadDef.x-12,144, 24, 4);
if (saveSlotSelected == NOSAVESLOT) // last slot is play without saving
{ {
if (ultimate_selectable) savetodraw = (saveSlotSelected + i + numsaves)%numsaves;
x = (BASEVIDWIDTH/2 - 42 + loadgamescroll) + (i*90);
y = 33 + 9;
{ {
V_DrawCenteredString(ecks + 68, 144, V_ORANGEMAP, "ULTIMATE MODE"); INT32 diff = x - (BASEVIDWIDTH/2 - 42);
V_DrawCenteredString(ecks + 68, 156, 0, "NO RINGS, NO ONE-UPS,"); if (diff < 0)
V_DrawCenteredString(ecks + 68, 164, 0, "NO CONTINUES, ONE LIFE,"); diff = -diff;
V_DrawCenteredString(ecks + 68, 172, 0, "FINAL DESTINATION."); diff = (42 - diff)/3 - loadgameoffset;
if (diff < 0)
diff = 0;
y -= diff;
} }
else
{ if (savetodraw == 0)
V_DrawCenteredString(ecks + 68, 144, V_ORANGEMAP, "PLAY WITHOUT SAVING");
V_DrawCenteredString(ecks + 68, 156, 0, "THIS GAME WILL NOT BE");
V_DrawCenteredString(ecks + 68, 164, 0, "SAVED, BUT YOU CAN STILL");
V_DrawCenteredString(ecks + 68, 172, 0, "GET EMBLEMS AND SECRETS.");
}
return;
}
if (savegameinfo[saveSlotSelected].lives == -42) // Empty
{
V_DrawCenteredString(ecks + 68, 160, 0, "NO DATA");
return;
}
if (savegameinfo[saveSlotSelected].lives == -666) // savegame is bad
{
V_DrawCenteredString(ecks + 68, 144, V_REDMAP, "CORRUPT SAVE FILE");
V_DrawCenteredString(ecks + 68, 156, 0, "THIS SAVE FILE");
V_DrawCenteredString(ecks + 68, 164, 0, "CAN NOT BE LOADED.");
V_DrawCenteredString(ecks + 68, 172, 0, "DELETE USING BACKSPACE.");
return;
}
// Draw the back sprite, it looks ugly if we don't
V_DrawScaledPatch(SP_LoadDef.x, 144+8, 0, livesback);
if (savegameinfo[saveSlotSelected].skincolor == 0)
V_DrawScaledPatch(SP_LoadDef.x,144+8,0,W_CachePatchName(skins[savegameinfo[saveSlotSelected].skinnum].face, PU_CACHE));
else
{
UINT8 *colormap = R_GetTranslationColormap(savegameinfo[saveSlotSelected].skinnum, savegameinfo[saveSlotSelected].skincolor, 0);
V_DrawMappedPatch(SP_LoadDef.x,144+8,0,W_CachePatchName(skins[savegameinfo[saveSlotSelected].skinnum].face, PU_CACHE), colormap);
}
V_DrawString(ecks + 12, 152, 0, savegameinfo[saveSlotSelected].playername);
#ifdef SAVEGAMES_OTHERVERSIONS
if (savegameinfo[saveSlotSelected].gamemap & 16384)
V_DrawCenteredString(ecks + 68, 144, V_REDMAP, "OUTDATED SAVE FILE!");
#endif
if (savegameinfo[saveSlotSelected].gamemap & 8192)
V_DrawString(ecks + 12, 160, V_GREENMAP, "CLEAR!");
else
V_DrawString(ecks + 12, 160, 0, va("%s", savegameinfo[saveSlotSelected].levelname));
// Use the big face pic for lives, duh. :3
V_DrawScaledPatch(ecks + 12, 175, 0, W_CachePatchName("STLIVEX", PU_HUDGFX));
if (savegameinfo[saveSlotSelected].lives == 0x7F)
V_DrawScaledPatch(ecks + 40 - 17, 172, 0, tallinfin);
else
V_DrawTallNum(ecks + 40, 172, 0, savegameinfo[saveSlotSelected].lives);
// Absolute ridiculousness, condensed into another function.
V_DrawContinueIcon(ecks + 58, 182, 0, savegameinfo[saveSlotSelected].skinnum, savegameinfo[saveSlotSelected].skincolor);
V_DrawScaledPatch(ecks + 68, 175, 0, W_CachePatchName("STLIVEX", PU_HUDGFX));
V_DrawTallNum(ecks + 96, 172, 0, savegameinfo[saveSlotSelected].continues);
for (i = 0; i < 7; ++i)
{
if (savegameinfo[saveSlotSelected].numemeralds & (1 << i))
V_DrawScaledPatch(ecks + 104 + (i * 8), 172, 0, tinyemeraldpics[i]);
}
}
#define LOADBARHEIGHT SP_LoadDef.y + (LINEHEIGHT * (j+1)) + ymod
#define CURSORHEIGHT SP_LoadDef.y + (LINEHEIGHT*3) - 1
static void M_DrawLoad(void)
{
INT32 i, j;
INT32 ymod = 0, offset = 0;
M_DrawMenuTitle();
if (menumovedir != 0) //movement illusion
{
ymod = (-(LINEHEIGHT/4))*menumovedir;
offset = ((menumovedir > 0) ? -1 : 1);
}
V_DrawCenteredString(BASEVIDWIDTH/2, 40, 0, "Press backspace to delete a save.");
for (i = MAXSAVEGAMES + saveSlotSelected - 2 + offset, j = 0;i <= MAXSAVEGAMES + saveSlotSelected + 2 + offset; i++, j++)
{
if ((menumovedir < 0 && j == 4) || (menumovedir > 0 && j == 0))
continue; //this helps give the illusion of movement
M_DrawSaveLoadBorder(SP_LoadDef.x, LOADBARHEIGHT);
if ((i%MAXSAVEGAMES) == NOSAVESLOT) // play without saving
{ {
V_DrawSmallScaledPatch(x, y, 0,
W_CachePatchName("SAVENONE", PU_CACHE));
x += 2;
y += 1;
V_DrawString(x, y,
((savetodraw == saveSlotSelected) ? V_YELLOWMAP : 0),
"NO FILE");
if (savetodraw == saveSlotSelected)
V_DrawFill(x, y+9, 80, 1, yellowmap[3]);
y += 11;
V_DrawSmallScaledPatch(x, y, V_STATIC,
W_CachePatchName("BLACKLVL", PU_CACHE));
y += 41;
if (ultimate_selectable) if (ultimate_selectable)
V_DrawCenteredString(SP_LoadDef.x+92, LOADBARHEIGHT - 1, V_ORANGEMAP, "ULTIMATE MODE"); V_DrawRightAlignedThinString(x + 79, y, V_REDMAP, "ULTIMATE.");
else else
V_DrawCenteredString(SP_LoadDef.x+92, LOADBARHEIGHT - 1, V_ORANGEMAP, "PLAY WITHOUT SAVING"); V_DrawRightAlignedThinString(x + 79, y, V_GRAYMAP, "DON'T SAVE!");
continue; continue;
} }
if (savegameinfo[i%MAXSAVEGAMES].lives == -42) savetodraw--;
V_DrawString(SP_LoadDef.x-6, LOADBARHEIGHT - 1, V_TRANSLUCENT, "NO DATA");
else if (savegameinfo[i%MAXSAVEGAMES].lives == -666)
V_DrawString(SP_LoadDef.x-6, LOADBARHEIGHT - 1, V_REDMAP, "CORRUPT SAVE FILE");
else if (savegameinfo[i%MAXSAVEGAMES].gamemap & 8192)
V_DrawString(SP_LoadDef.x-6, LOADBARHEIGHT - 1, V_GREENMAP, "CLEAR!");
else
V_DrawString(SP_LoadDef.x-6, LOADBARHEIGHT - 1, 0, va("%s", savegameinfo[i%MAXSAVEGAMES].levelname));
//Draw the save slot number on the right side // signpost background
V_DrawRightAlignedString(SP_LoadDef.x+192, LOADBARHEIGHT - 1, 0, va("%d",(i%MAXSAVEGAMES) + 1)); {
UINT8 col;
if (savegameinfo[savetodraw].botskin == 3) // & knuckles
col = 105;
else if (savegameinfo[savetodraw].botskin) // tailsbot or custom
col = 134;
else if (savegameinfo[savetodraw].lives == -42)
col = 26;
else if (savegameinfo[savetodraw].lives == -666)
col = 47;
else
{
charskin = &skins[savegameinfo[savetodraw].skinnum];
col = (charskin->prefcolor - 1)*2;
col = Color_Index[Color_Opposite[col]-1][Color_Opposite[col+1]];
}
V_DrawFill(x+6, y+64, 72, 50, col);
}
V_DrawSmallScaledPatch(x, y, 0,
W_CachePatchName("SAVEBACK", PU_CACHE));
x += 2;
y += 1;
V_DrawString(x, y,
((savetodraw == saveSlotSelected-1) ? V_YELLOWMAP : 0),
va("FILE %d", savetodraw+1));
if (savetodraw == saveSlotSelected-1)
V_DrawFill(x, y+9, 80, 1, yellowmap[3]);
y += 11;
// level image area
{
patch_t *patch;
INT32 flags = 0;
if ((savegameinfo[savetodraw].lives == -42)
|| (savegameinfo[savetodraw].lives == -666))
{
patch = W_CachePatchName("BLACKLVL", PU_CACHE);
flags = V_STATIC;
}
else if (savegameinfo[savetodraw].gamemap & 8192)
patch = W_CachePatchName("GAMEDONE", PU_CACHE);
else
{
lumpnum_t lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName((savegameinfo[savetodraw].gamemap) & 8191)));
if (lumpnum != LUMPERROR)
patch = W_CachePatchNum(lumpnum, PU_CACHE);
else
patch = W_CachePatchName("BLANKLVL", PU_CACHE);
}
V_DrawSmallScaledPatch(x, y, flags, patch);
y += 41;
if (savegameinfo[savetodraw].lives == -42)
V_DrawRightAlignedThinString(x + 79, y, V_GRAYMAP, "NEW GAME");
else if (savegameinfo[savetodraw].lives == -666)
V_DrawRightAlignedThinString(x + 79, y, V_REDMAP, "CAN'T LOAD!");
else if (savegameinfo[savetodraw].gamemap & 8192)
V_DrawRightAlignedThinString(x + 79, y, V_GREENMAP, "CLEAR!");
else
V_DrawRightAlignedThinString(x + 79, y, V_YELLOWMAP, savegameinfo[savetodraw].levelname);
}
if ((savegameinfo[savetodraw].lives == -42)
|| (savegameinfo[savetodraw].lives == -666))
continue;
y += 51;
// character heads, lives, and continues
{
spritedef_t *sprdef;
spriteframe_t *sprframe;
patch_t *patch;
UINT8 *colormap = NULL;
INT32 tempx = (x+40)<<FRACBITS, tempy = y<<FRACBITS, flip = 0;
// botskin first
if (savegameinfo[savetodraw].botskin)
{
skin_t *charbotskin = &skins[savegameinfo[savetodraw].botskin-1];
sprdef = &charbotskin->sprites[SPR2_SIGN];
if (!sprdef->numframes)
goto skipbot;
colormap = R_GetTranslationColormap(savegameinfo[savetodraw].botskin, charbotskin->prefcolor, 0);
sprframe = &sprdef->spriteframes[0];
patch = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE);
V_DrawFixedPatch(
tempx + (18<<FRACBITS),
tempy - (4<<FRACBITS),
charbotskin->highresscale,
0, patch, colormap);
Z_Free(colormap);
tempx -= (15<<FRACBITS);
flip = V_FLIP;
}
skipbot:
// signpost image
if (!charskin) // shut up compiler
goto skipsign;
sprdef = &charskin->sprites[SPR2_SIGN];
colormap = R_GetTranslationColormap(savegameinfo[savetodraw].skinnum, charskin->prefcolor, 0);
if (!sprdef->numframes)
goto skipsign;
sprframe = &sprdef->spriteframes[0];
patch = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE);
V_DrawFixedPatch(
tempx,
tempy,
charskin->highresscale,
flip, patch, colormap);
skipsign:
y += 25;
tempx = x + 10;
if (savegameinfo[savetodraw].lives != 0x7f
&& savegameinfo[savetodraw].lives > 9)
tempx -= 4;
if (!charskin) // shut up compiler
goto skiplife;
// lives
sprdef = &charskin->sprites[SPR2_LIFE];
if (!sprdef->numframes)
goto skiplife;
sprframe = &sprdef->spriteframes[0];
patch = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE);
V_DrawFixedPatch(
(tempx + 4)<<FRACBITS,
(y + 6)<<FRACBITS,
charskin->highresscale/2,
0, patch, colormap);
skiplife:
if (colormap)
Z_Free(colormap);
patch = W_CachePatchName("STLIVEX", PU_CACHE);
V_DrawScaledPatch(tempx + 9, y + 2, 0, patch);
tempx += 16;
if (savegameinfo[savetodraw].lives == 0x7f)
V_DrawCharacter(tempx, y + 1, '\x16', false);
else
V_DrawString(tempx, y, 0, va("%d", savegameinfo[savetodraw].lives));
tempx = x + 47;
if (savegameinfo[savetodraw].continues > 9)
tempx -= 4;
// continues
if (savegameinfo[savetodraw].continues > 0)
{
V_DrawSmallScaledPatch(tempx, y, 0, W_CachePatchName("CONTSAVE", PU_CACHE));
V_DrawScaledPatch(tempx + 9, y + 2, 0, patch);
V_DrawString(tempx + 16, y, 0, va("%d", savegameinfo[savetodraw].continues));
}
else
{
V_DrawSmallScaledPatch(tempx, y, 0, W_CachePatchName("CONTNONE", PU_CACHE));
V_DrawScaledPatch(tempx + 9, y + 2, 0, W_CachePatchName("STNONEX", PU_CACHE));
V_DrawString(tempx + 16, y, V_GRAYMAP, "0");
}
}
x += 6;
y -= 12;
// tiny emeralds
{
INT32 j;
for (j = 0; j < 7; ++j)
{
if (savegameinfo[savetodraw].numemeralds & (1 << j))
V_DrawScaledPatch(x, y, 0, tinyemeraldpics[j]);
x += 10;
}
}
} }
}
//Draw cursors on both sides. static void M_DrawLoad(void)
V_DrawScaledPatch( 32, CURSORHEIGHT, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); {
V_DrawScaledPatch(274, CURSORHEIGHT, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); M_DrawMenuTitle();
if (loadgamescroll > 1 || loadgamescroll < -1)
loadgamescroll = 2*loadgamescroll/3;
else
loadgamescroll = 0;
if (loadgameoffset > 1)
loadgameoffset = 2*loadgameoffset/3;
else
loadgameoffset = 0;
M_DrawLoadGameData(); M_DrawLoadGameData();
//finishing the movement illusion
if (menumovedir)
menumovedir += ((menumovedir > 0) ? 1 : -1);
if (abs(menumovedir) > 3)
menumovedir = 0;
} }
#undef LOADBARHEIGHT
#undef CURSORHEIGHT
// //
// User wants to load this game // User wants to load this game
@ -6185,7 +6298,7 @@ static void M_LoadSelect(INT32 choice)
if (saveSlotSelected == NOSAVESLOT) //last slot is play without saving if (saveSlotSelected == NOSAVESLOT) //last slot is play without saving
{ {
M_NewGame(); M_NewGame();
cursaveslot = -1; cursaveslot = 0;
return; return;
} }
@ -6194,8 +6307,8 @@ static void M_LoadSelect(INT32 choice)
// This slot is empty, so start a new game here. // This slot is empty, so start a new game here.
M_NewGame(); M_NewGame();
} }
else if (savegameinfo[saveSlotSelected].gamemap & 8192) // Completed else if (savegameinfo[saveSlotSelected-1].gamemap & 8192) // Completed
M_LoadGameLevelSelect(saveSlotSelected + 1); M_LoadGameLevelSelect(0);
else else
G_LoadGame((UINT32)saveSlotSelected, 0); G_LoadGame((UINT32)saveSlotSelected, 0);
@ -6217,12 +6330,11 @@ static void M_ReadSavegameInfo(UINT32 slot)
INT32 fake; // Dummy variable INT32 fake; // Dummy variable
char temp[sizeof(timeattackfolder)]; char temp[sizeof(timeattackfolder)];
char vcheck[VERSIONSIZE]; char vcheck[VERSIONSIZE];
#ifdef SAVEGAMES_OTHERVERSIONS
boolean oldversion = false;
#endif
sprintf(savename, savegamename, slot); sprintf(savename, savegamename, slot);
slot--;
length = FIL_ReadFile(savename, &savebuffer); length = FIL_ReadFile(savename, &savebuffer);
if (length == 0) if (length == 0)
{ {
@ -6238,14 +6350,7 @@ static void M_ReadSavegameInfo(UINT32 slot)
// Version check // Version check
memset(vcheck, 0, sizeof (vcheck)); memset(vcheck, 0, sizeof (vcheck));
sprintf(vcheck, "version %d", VERSION); sprintf(vcheck, "version %d", VERSION);
if (strcmp((const char *)save_p, (const char *)vcheck)) if (strcmp((const char *)save_p, (const char *)vcheck)) BADSAVE
{
#ifdef SAVEGAMES_OTHERVERSIONS
oldversion = true;
#else
BADSAVE // Incompatible versions?
#endif
}
save_p += VERSIONSIZE; save_p += VERSIONSIZE;
// dearchive all the modifications // dearchive all the modifications
@ -6267,20 +6372,10 @@ static void M_ReadSavegameInfo(UINT32 slot)
savegameinfo[slot].actnum = mapheaderinfo[(fake-1) & 8191]->actnum; savegameinfo[slot].actnum = mapheaderinfo[(fake-1) & 8191]->actnum;
} }
#ifdef SAVEGAMES_OTHERVERSIONS
if (oldversion)
{
if (fake == 24) //meh, let's count old Clear! saves too
fake |= 8192;
fake |= 16384; // marker for outdated version
}
#endif
savegameinfo[slot].gamemap = fake; savegameinfo[slot].gamemap = fake;
CHECKPOS CHECKPOS
fake = READUINT16(save_p)-357; // emeralds savegameinfo[slot].numemeralds = READUINT16(save_p)-357; // emeralds
savegameinfo[slot].numemeralds = (UINT8)fake;
CHECKPOS CHECKPOS
READSTRINGN(save_p, temp, sizeof(temp)); // mod it belongs to READSTRINGN(save_p, temp, sizeof(temp)); // mod it belongs to
@ -6289,11 +6384,15 @@ static void M_ReadSavegameInfo(UINT32 slot)
// P_UnArchivePlayer() // P_UnArchivePlayer()
CHECKPOS CHECKPOS
savegameinfo[slot].skincolor = READUINT8(save_p); fake = READUINT16(save_p);
savegameinfo[slot].skinnum = fake & ((1<<5) - 1);
CHECKPOS if (savegameinfo[slot].skinnum >= numskins
savegameinfo[slot].skinnum = READUINT8(save_p); || !R_SkinUsable(-1, savegameinfo[slot].skinnum))
if (savegameinfo[slot].skinnum >= numskins) BADSAVE BADSAVE
savegameinfo[slot].botskin = fake >> 5;
if (savegameinfo[slot].botskin-1 >= numskins
|| !R_SkinUsable(-1, savegameinfo[slot].botskin-1))
savegameinfo[slot].botskin = 0;
CHECKPOS CHECKPOS
(void)READUINT8(save_p); // numgameovers (void)READUINT8(save_p); // numgameovers
@ -6304,27 +6403,6 @@ static void M_ReadSavegameInfo(UINT32 slot)
CHECKPOS CHECKPOS
savegameinfo[slot].continues = READINT32(save_p); // continues savegameinfo[slot].continues = READINT32(save_p); // continues
if (fake & (1<<10))
{
CHECKPOS
savegameinfo[slot].botskin = READUINT8(save_p);
if (savegameinfo[slot].botskin-1 >= numskins)
savegameinfo[slot].botskin = 0;
CHECKPOS
savegameinfo[slot].botcolor = READUINT8(save_p); // because why not.
}
else
savegameinfo[slot].botskin = 0;
if (savegameinfo[slot].botskin)
snprintf(savegameinfo[slot].playername, 32, "%s & %s",
skins[savegameinfo[slot].skinnum].realname,
skins[savegameinfo[slot].botskin-1].realname);
else
strcpy(savegameinfo[slot].playername, skins[savegameinfo[slot].skinnum].realname);
savegameinfo[slot].playername[31] = 0;
// File end marker check // File end marker check
CHECKPOS CHECKPOS
if (READUINT8(save_p) != 0x1d) BADSAVE; if (READUINT8(save_p) != 0x1d) BADSAVE;
@ -6343,21 +6421,45 @@ static void M_ReadSavegameInfo(UINT32 slot)
static void M_ReadSaveStrings(void) static void M_ReadSaveStrings(void)
{ {
FILE *handle; FILE *handle;
UINT32 i; SINT8 i;
char name[256]; char name[256];
boolean nofile[MAXSAVEGAMES-1];
UINT8 tolerance = 0;
for (i = 0; i < MAXSAVEGAMES; i++) loadgamescroll = 0;
loadgameoffset = 14;
for (i = 1; ((i < MAXSAVEGAMES) && ((i <= saveSlotSelected) || (tolerance < 3))); i++) // slot 0 is no save
{ {
snprintf(name, sizeof name, savegamename, i); snprintf(name, sizeof name, savegamename, i);
name[sizeof name - 1] = '\0'; name[sizeof name - 1] = '\0';
handle = fopen(name, "rb"); handle = fopen(name, "rb");
if (handle == NULL) if ((nofile[i-1] = (handle == NULL)))
{ {
savegameinfo[i].lives = -42; tolerance++;
continue; continue;
} }
fclose(handle); fclose(handle);
tolerance = 0;
}
if (savegameinfo)
Z_Free(savegameinfo);
savegameinfo = NULL;
numsaves = i;
savegameinfo = Z_Realloc(savegameinfo, numsaves*sizeof(saveinfo_t), PU_STATIC, NULL);
if (!savegameinfo)
I_Error("Insufficient memory to prepare save platter");
for (; i > 0; i--)
{
if (nofile[i-1] == true)
{
savegameinfo[i-1].lives = -42;
continue;
}
M_ReadSavegameInfo(i); M_ReadSavegameInfo(i);
} }
} }
@ -6377,8 +6479,8 @@ static void M_SaveGameDeleteResponse(INT32 ch)
name[sizeof name - 1] = '\0'; name[sizeof name - 1] = '\0';
remove(name); remove(name);
// Refresh savegame menu info S_StartSound(NULL, sfx_bewar1+M_RandomKey(4)); // Bweh heh he
M_ReadSaveStrings(); M_ReadSaveStrings(); // reload the menu
} }
static void M_HandleLoadSave(INT32 choice) static void M_HandleLoadSave(INT32 choice)
@ -6387,26 +6489,33 @@ static void M_HandleLoadSave(INT32 choice)
switch (choice) switch (choice)
{ {
case KEY_DOWNARROW: case KEY_RIGHTARROW:
S_StartSound(NULL, sfx_menu1); S_StartSound(NULL, sfx_s3kb7);
++saveSlotSelected; ++saveSlotSelected;
if (saveSlotSelected >= MAXSAVEGAMES) if (saveSlotSelected >= numsaves)
saveSlotSelected -= MAXSAVEGAMES; saveSlotSelected -= numsaves;
menumovedir = 1; loadgamescroll = 90;
break; break;
case KEY_UPARROW: case KEY_LEFTARROW:
S_StartSound(NULL, sfx_menu1); S_StartSound(NULL, sfx_s3kb7);
--saveSlotSelected; --saveSlotSelected;
if (saveSlotSelected < 0) if (saveSlotSelected < 0)
saveSlotSelected += MAXSAVEGAMES; saveSlotSelected += numsaves;
menumovedir = -1; loadgamescroll = -90;
break; break;
case KEY_ENTER: case KEY_ENTER:
S_StartSound(NULL, sfx_menu1); if (savegameinfo[saveSlotSelected-1].lives != -666) // don't allow loading of "bad saves"
if (savegameinfo[saveSlotSelected].lives != -666) // don't allow loading of "bad saves" {
S_StartSound(NULL, sfx_menu1);
M_LoadSelect(saveSlotSelected); M_LoadSelect(saveSlotSelected);
}
else if (!loadgameoffset)
{
S_StartSound(NULL, sfx_s3kb2);
loadgameoffset = 14;
}
break; break;
case KEY_ESCAPE: case KEY_ESCAPE:
@ -6414,11 +6523,19 @@ static void M_HandleLoadSave(INT32 choice)
break; break;
case KEY_BACKSPACE: case KEY_BACKSPACE:
S_StartSound(NULL, sfx_menu1);
// Don't allow people to 'delete' "Play without Saving." // Don't allow people to 'delete' "Play without Saving."
// Nor allow people to 'delete' slots with no saves in them. // Nor allow people to 'delete' slots with no saves in them.
if (saveSlotSelected != NOSAVESLOT && savegameinfo[saveSlotSelected].lives != -42) if (saveSlotSelected != NOSAVESLOT && savegameinfo[saveSlotSelected-1].lives != -42)
{
loadgamescroll = 0;
S_StartSound(NULL, sfx_skid);
M_StartMessage(M_GetText("Are you sure you want to delete\nthis save game?\n\n(Press 'Y' to confirm)\n"),M_SaveGameDeleteResponse,MM_YESNO); M_StartMessage(M_GetText("Are you sure you want to delete\nthis save game?\n\n(Press 'Y' to confirm)\n"),M_SaveGameDeleteResponse,MM_YESNO);
}
else if (!loadgameoffset)
{
S_StartSound(NULL, sfx_s3kb2);
loadgameoffset = 14;
}
break; break;
} }
if (exitmenu) if (exitmenu)
@ -6427,6 +6544,8 @@ static void M_HandleLoadSave(INT32 choice)
M_SetupNextMenu(currentMenu->prevMenu); M_SetupNextMenu(currentMenu->prevMenu);
else else
M_ClearMenus(true); M_ClearMenus(true);
Z_Free(savegameinfo);
savegameinfo = NULL;
} }
} }
@ -6451,9 +6570,9 @@ void M_ForceSaveSlotSelected(INT32 sslot)
return; return;
// Figure out whether to display up movement or down movement // Figure out whether to display up movement or down movement
menumovedir = (saveSlotSelected - sslot) > 0 ? -1 : 1; /*menumovedir = (saveSlotSelected - sslot) > 0 ? -1 : 1;
if (abs(saveSlotSelected - sslot) > (MAXSAVEGAMES>>1)) if (abs(saveSlotSelected - sslot) > (MAXSAVEGAMES>>1))
menumovedir *= -1; menumovedir *= -1;*/
saveSlotSelected = sslot; saveSlotSelected = sslot;
} }
@ -6724,7 +6843,7 @@ static void M_ChoosePlayer(INT32 choice)
} }
if (startmap != spstage_start) if (startmap != spstage_start)
cursaveslot = -1; cursaveslot = 0;
//lastmapsaved = 0; //lastmapsaved = 0;
gamecomplete = false; gamecomplete = false;
@ -6735,6 +6854,10 @@ static void M_ChoosePlayer(INT32 choice)
if (levelselect.rows) if (levelselect.rows)
Z_Free(levelselect.rows); Z_Free(levelselect.rows);
levelselect.rows = NULL; levelselect.rows = NULL;
if (savegameinfo)
Z_Free(savegameinfo);
savegameinfo = NULL;
} }
// =============== // ===============

View File

@ -215,13 +215,10 @@ typedef struct
// savegame struct for save game menu // savegame struct for save game menu
typedef struct typedef struct
{ {
char playername[32];
char levelname[32]; char levelname[32];
UINT8 actnum; UINT8 actnum;
UINT8 skincolor;
UINT8 skinnum; UINT8 skinnum;
UINT8 botskin; UINT8 botskin;
UINT8 botcolor;
UINT8 numemeralds; UINT8 numemeralds;
INT32 lives; INT32 lives;
INT32 continues; INT32 continues;
@ -237,7 +234,7 @@ extern INT16 startmap;
extern INT32 ultimate_selectable; extern INT32 ultimate_selectable;
#define MAXSAVEGAMES 31 //note: last save game is "no save" #define MAXSAVEGAMES 31 //note: last save game is "no save"
#define NOSAVESLOT MAXSAVEGAMES-1 //slot where Play Without Saving appears #define NOSAVESLOT 0 //slot where Play Without Saving appears
void M_ForceSaveSlotSelected(INT32 sslot); void M_ForceSaveSlotSelected(INT32 sslot);

View File

@ -2293,7 +2293,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) && numgameovers < maxgameovers) if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) && numgameovers < maxgameovers)
{ {
numgameovers++; numgameovers++;
if ((!modifiedgame || savemoddata) && cursaveslot >= 0) if ((!modifiedgame || savemoddata) && cursaveslot > 0)
G_SaveGameOver((UINT32)cursaveslot, (target->player->continues <= 0)); G_SaveGameOver((UINT32)cursaveslot, (target->player->continues <= 0));
} }
} }

View File

@ -64,23 +64,16 @@ typedef enum
static inline void P_ArchivePlayer(void) static inline void P_ArchivePlayer(void)
{ {
const player_t *player = &players[consoleplayer]; const player_t *player = &players[consoleplayer];
INT16 skininfo = player->skin + (botskin<<5);
SINT8 pllives = player->lives; SINT8 pllives = player->lives;
if (pllives < startinglivesbalance[numgameovers]) // Bump up to 3 lives if the player if (pllives < startinglivesbalance[numgameovers]) // Bump up to 3 lives if the player
pllives = startinglivesbalance[numgameovers]; // has less than that. pllives = startinglivesbalance[numgameovers]; // has less than that.
WRITEUINT8(save_p, player->skincolor); WRITEUINT16(save_p, skininfo);
WRITEUINT8(save_p, player->skin);
WRITEUINT8(save_p, numgameovers); WRITEUINT8(save_p, numgameovers);
WRITESINT8(save_p, pllives); WRITESINT8(save_p, pllives);
WRITEUINT32(save_p, player->score); WRITEUINT32(save_p, player->score);
WRITEINT32(save_p, player->continues); WRITEINT32(save_p, player->continues);
if (botskin)
{
WRITEUINT8(save_p, botskin);
WRITEUINT8(save_p, botcolor);
}
} }
// //
@ -88,23 +81,14 @@ static inline void P_ArchivePlayer(void)
// //
static inline void P_UnArchivePlayer(void) static inline void P_UnArchivePlayer(void)
{ {
savedata.skincolor = READUINT8(save_p); INT16 skininfo = READUINT16(save_p);
savedata.skin = READUINT8(save_p); savedata.skin = skininfo & ((1<<5) - 1);
savedata.botskin = skininfo >> 5;
savedata.numgameovers = READUINT8(save_p); savedata.numgameovers = READUINT8(save_p);
savedata.lives = READSINT8(save_p); savedata.lives = READSINT8(save_p);
savedata.score = READUINT32(save_p); savedata.score = READUINT32(save_p);
savedata.continues = READINT32(save_p); savedata.continues = READINT32(save_p);
if (savedata.botcolor)
{
savedata.botskin = READUINT8(save_p);
if (savedata.botskin-1 >= numskins)
savedata.botskin = 0;
savedata.botcolor = READUINT8(save_p);
}
else
savedata.botskin = 0;
} }
// //
@ -3164,7 +3148,7 @@ static inline void P_ArchiveMisc(void)
//lastmapsaved = gamemap; //lastmapsaved = gamemap;
lastmaploaded = gamemap; lastmaploaded = gamemap;
WRITEUINT16(save_p, (botskin ? (emeralds|(1<<10)) : emeralds)+357); WRITEUINT16(save_p, emeralds+357);
WRITESTRINGN(save_p, timeattackfolder, sizeof(timeattackfolder)); WRITESTRINGN(save_p, timeattackfolder, sizeof(timeattackfolder));
} }
@ -3194,9 +3178,6 @@ static inline void P_UnArchiveSPGame(INT16 mapoverride)
token = 0; token = 0;
savedata.emeralds = READUINT16(save_p)-357; savedata.emeralds = READUINT16(save_p)-357;
if (savedata.emeralds & (1<<10))
savedata.botcolor = 0xFF;
savedata.emeralds &= 0xff;
READSTRINGN(save_p, testname, sizeof(testname)); READSTRINGN(save_p, testname, sizeof(testname));

View File

@ -30,10 +30,8 @@ mobj_t *P_FindNewPosition(UINT32 oldposition);
typedef struct typedef struct
{ {
UINT8 skincolor;
UINT8 skin; UINT8 skin;
UINT8 botskin; UINT8 botskin;
UINT8 botcolor;
INT32 score; INT32 score;
INT32 lives; INT32 lives;
INT32 continues; INT32 continues;

View File

@ -3019,7 +3019,7 @@ boolean P_SetupLevel(boolean skipprecip)
P_RunCachedActions(); P_RunCachedActions();
if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking || players[consoleplayer].lives <= 0) if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking || players[consoleplayer].lives <= 0)
&& (!modifiedgame || savemoddata) && cursaveslot >= 0 && CanSaveLevel(gamemap)) && (!modifiedgame || savemoddata) && cursaveslot > 0 && CanSaveLevel(gamemap))
G_SaveGame((UINT32)cursaveslot); G_SaveGame((UINT32)cursaveslot);
lastmaploaded = gamemap; // HAS to be set after saving!! lastmaploaded = gamemap; // HAS to be set after saving!!
@ -3030,9 +3030,8 @@ boolean P_SetupLevel(boolean skipprecip)
players[consoleplayer].continues = savedata.continues; players[consoleplayer].continues = savedata.continues;
players[consoleplayer].lives = savedata.lives; players[consoleplayer].lives = savedata.lives;
players[consoleplayer].score = savedata.score; players[consoleplayer].score = savedata.score;
botskin = savedata.botskin; if ((botingame = ((botskin = savedata.botskin) != 0)))
botcolor = savedata.botcolor; botcolor = skins[botskin-1].prefcolor;
botingame = (savedata.botskin != 0);
emeralds = savedata.emeralds; emeralds = savedata.emeralds;
savedata.lives = 0; savedata.lives = 0;
} }
@ -3201,8 +3200,8 @@ boolean P_AddWadFile(const char *wadfilename, char **firstmapname)
ST_Start(); ST_Start();
// Prevent savefile cheating // Prevent savefile cheating
if (cursaveslot >= 0) if (cursaveslot > 0)
cursaveslot = -1; cursaveslot = 0;
if (replacedcurrentmap && gamestate == GS_LEVEL && (netgame || multiplayer)) if (replacedcurrentmap && gamestate == GS_LEVEL && (netgame || multiplayer))
{ {

View File

@ -1422,9 +1422,9 @@ void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string)
else else
w = SHORT(hu_font[c]->width) * dupx; w = SHORT(hu_font[c]->width) * dupx;
if (cx + w > scrwidth) if (cx > scrwidth)
break; break;
if (cx < 0) //left boundary check if (cx + w < 0) //left boundary check
{ {
cx += w; cx += w;
continue; continue;
@ -1628,9 +1628,9 @@ void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string)
else else
w = (SHORT(tny_font[c]->width) * dupx); w = (SHORT(tny_font[c]->width) * dupx);
if (cx + w > scrwidth) if (cx > scrwidth)
break; break;
if (cx < 0) //left boundary check if (cx + w < 0) //left boundary check
{ {
cx += w; cx += w;
continue; continue;