Here's an example: Get the rune in E1M7, defeat Chthon, and get to the portal that will lead you back to the start map.
Now that you're in the start map, and you have started it with the rune in hand, save the game.
Now go to the main menu and start a new game, so you start the start map without runes.
Load the saved game. The rune is in your inventary.
When saving that game, you already had the rune when starting the map, so go to the console and enter the "restart" command.
The rune is gone!
Here's the fix; it just saves/loads the serverflags to the file before saving/loading the lightstyles. Bugfixed saves won't load in engines that doesn't support them, but by enabling savegame_compatibility this bugfix will be disabled, thus allowing us to load the game and save it again to convert it to the vanilla Quake format.
host_cmd.c:
Code: Select all
/*
===============================================================================
LOAD / SAVE GAME
===============================================================================
*/
// mankrip - serverflags bugfix - begin
#define VANILLA_SAVESTATE_VERSION 5
#ifdef _arch_dreamcast
#define SAVESTATE_VERSION VANILLA_SAVESTATE_VERSION // VMU savestates always had the serverflags bugfix
#else
#define SAVESTATE_VERSION 6 // only used for full saves
#endif
#define SAVEGAME_VERSION 5 // only used for small saves
cvar_t
savegame_compatibility = {"savegame_compatibility", "0", true}
;
// mankrip - serverflags bugfix - end
[...]
void Host_Savegame_f (void)
{
[...]
fprintf (f, "%i\n", savegame_compatibility.value ? VANILLA_SAVESTATE_VERSION : SAVESTATE_VERSION); // mankrip - serverflags bugfix
Host_SavegameComment (comment);
[...]
if (!savegame_compatibility.value) // mankrip - serverflags bugfix
fprintf (f, "%i\n", svs.serverflags); // mankrip - serverflags bugfix
// write the light styles
for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
{
if (sv.lightstyles[i])
fprintf (f, "%s\n", sv.lightstyles[i]);
else
fprintf (f,"m\n");
}
[...]
}
void Host_Loadgame_f (void)
{
[...]
fscanf (f, "%i\n", &version);
if (version != SAVESTATE_VERSION && version != VANILLA_SAVESTATE_VERSION) // mankrip - serverflags bugfix
{
fclose (f);
Con_Printf ("Savegame is version %i, not %i or %i\n", version, VANILLA_SAVESTATE_VERSION, SAVESTATE_VERSION); // mankrip - serverflags bugfix - edited
return;
}
[...]
fscanf (f, "%f\n",&time);
if (version != VANILLA_SAVESTATE_VERSION) // mankrip - serverflags bugfix
fscanf (f, "%i\n", &svs.serverflags); // mankrip - serverflags bugfix
CL_Disconnect_f ();
#ifdef QUAKE2
SV_SpawnServer (mapname, NULL);
#else
SV_SpawnServer (mapname);
#endif
[...]
}
[...]
void Host_InitCommands (void)
{
Cvar_RegisterVariable (&savegame_compatibility); // mankrip - serverflags bugfix
[...]
}
Code: Select all
void Host_Loadgame_f (void)
{
[...]
fscanf (f, "%i\n", &version);
if (version != SAVESTATE_VERSION && version != VANILLA_SAVESTATE_VERSION) // mankrip - serverflags bugfix
{
fclose (f);
Con_Printf ("Savegame is version %i, not %i or %i\n", version, VANILLA_SAVESTATE_VERSION, SAVESTATE_VERSION); // mankrip - serverflags bugfix - edited
return;
}
[...]
fscanf (f, "%f\n",&time);
// mankrip - serverflags bugfix - begin
// hack to load Makaqu's bugfixed saves that used the old savegame version - begin
#ifndef _arch_dreamcast
if (version == VANILLA_SAVESTATE_VERSION)
{
// load everything up to the line where the first opening brace should be.
// if it's not there, and is on the following line instead, this is a Makaqu bugfixed save.
for (i = 0 ; i < MAX_LIGHTSTYLES ; i++)
fscanf (f, "%s\n", str); // load the lightstyles
fscanf (f, "%s\n", str); // try to load the first opening brace
if (str[0] != '{') // no brace here, so it's not a vanilla version
{
fscanf (f, "%s\n", str); // see if the first opening brace is on the next line
if (str[0] == '{') // the brace is here, so it's not a vanilla version
version = SAVESTATE_VERSION; // not a vanilla version
}
// close and reopen the file
fclose (f);
f = fopen (name, "r");
fscanf (f, "%s\n", str); // skip the version
fscanf (f, "%s\n", str); // skip the comment
for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
fscanf (f, "%s\n", str); // skip spawnparms
fscanf (f, "%s\n", str); // skip the skill
fscanf (f, "%s\n", str); // skip the mapname
fscanf (f, "%s\n", str); // skip the time
// TODO: overwrite the file automatically, updating its version to SAVESTATE_VERSION to fix it?
}
#endif
// hack to load Makaqu's bugfixed saves that used the old savegame version - end
if (version != VANILLA_SAVESTATE_VERSION)
fscanf (f, "%i\n", &svs.serverflags);
// mankrip - serverflags bugfix - end
CL_Disconnect_f ();
#ifdef QUAKE2
SV_SpawnServer (mapname, NULL);
#else
SV_SpawnServer (mapname);
#endif
[...]
}
menu.c:
Code: Select all
extern cvar_t
savegame_compatibility // mankrip - serverflags bugfix
;
[...]
//=============================================================================
/* SINGLE PLAYER MENU */
// mankrip - serverflags bugfix - begin
#ifdef _arch_dreamcast
#define SINGLEPLAYER_ITEMS 5
#else
#define SINGLEPLAYER_ITEMS 6
#endif
// mankrip - serverflags bugfix - end
[...]
void M_SinglePlayer_Draw (void)
{
// mankrip - begin
int y = 20;
M_DrawPlaque ("gfx/ttl_sgl.lmp", true);
M_Print (16, y += 8, " New game");
M_Print (16, y += 8, " Load game ...");
M_Print (16, y += 8, " Save game ...");
M_Print (16, y += 8, " Load state ...");
M_Print (16, y += 8, " Save state ...");
#ifndef _arch_dreamcast
M_Print (16, y += 8, " Savestate version"); M_Print (220, y, (savegame_compatibility.value) ? "Old" : "New"); // serverflags bugfix
#endif
M_DrawCursor (200, 28, m_cursor[m_state]);
// mankrip - end
}
void M_SinglePlayer_Key (int key)
{
if (m_inp_cancel)
M_Main_f ();
else if (m_inp_up)
{
S_LocalSound ("misc/menu1.wav");
if (--m_cursor[m_state] < 0)
m_cursor[m_state] = SINGLEPLAYER_ITEMS - 1;
}
else if (m_inp_down)
{
S_LocalSound ("misc/menu1.wav");
if (++m_cursor[m_state] >= SINGLEPLAYER_ITEMS)
m_cursor[m_state] = 0;
}
// mankrip - serverflags bugfix - begin
#ifndef _arch_dreamcast
else if (m_cursor[m_state] == 5)
{
if (m_inp_left || m_inp_right || m_inp_ok)
{
S_LocalSound ("misc/menu3.wav");
Cvar_SetValue ("savegame_compatibility", !savegame_compatibility.value);
}
}
#endif
// mankrip - serverflags bugfix - end
else if (m_inp_ok)
{
m_entersound = true;
switch (m_cursor[m_state]) // mankrip - replaced cursor variables
{
case 0:
if (sv.active)
M_PopUp_f("Are you sure you want to\nstart a new game?", "disconnect\nmaxplayers 1\nmap start\n"); // mankrip
else
Cbuf_AddText("disconnect\nmaxplayers 1\nmap start\n"); // mankrip
break;
// mankrip - small saves - begin
case 1:
// mankrip - VMU saves - begin
#ifdef _arch_dreamcast
M_VMUloadsmall_f ();
#else
// mankrip - VMU saves - end
M_LoadSmall_f ();
#endif // mankrip - VMU saves
break;
case 2:
// mankrip - VMU saves - begin
#ifdef _arch_dreamcast
M_VMUsavesmall_f ();
#else
// mankrip - VMU saves - end
M_SaveSmall_f ();
#endif // mankrip - VMU saves
break;
// mankrip - small saves - end
case 3:
// mankrip - VMU saves - begin
#ifdef _arch_dreamcast
M_VMUload_f ();
#else
// mankrip - VMU saves - end
M_Load_f ();
#endif // mankrip - VMU saves
break;
case 4:
// mankrip - VMU saves - begin
#ifdef _arch_dreamcast
M_VMUsave_f ();
#else
// mankrip - VMU saves - end
M_Save_f ();
#endif // mankrip - VMU saves
break;
default:
break;
}
}
}
This fix requires a small change in the savegame format. So, since Makaqu's saves already have this difference and older engines won't be able to read its saves properly anyway, I'm thinking of implementing more features in the saves.
One that I really need is to save the actual viewangles of the player. It's really annoying when we want to save the game with the camera pointing at a very specific point, only to have the camera turned to a completely different direction upon loading the save.
Another thing I'm thinking about are the particles. Shoot a rocket, and save the game exactly after it explodes. When you close the menu and go back to the game the particles will be there, but after that, when you load the game the particles are gone. However, saving the state of all those particles could inflate the size of the saves quite a lot, so I'm not sure whether fixing that would be desirable.
What are other bugfixes and improvements that could be added to the saves?