Start save/load

This commit is contained in:
Miloslav Číž 2020-09-19 19:35:26 +02:00
parent c86d30170a
commit b358913150
3 changed files with 155 additions and 56 deletions

View File

@ -64,6 +64,11 @@
*/
#define SFG_DOOR_OPEN_SPEED 2048
/**
Helper special state value.
*/
#define SFG_CANT_SAVE 255
/**
Says the distance in RCL_Units at which level elements (items, monsters etc.)
are active.

168
game.h
View File

@ -132,8 +132,15 @@ void SFG_save(uint8_t data[SFG_SAVE_SIZE]);
storage. Platforms without permanent storage may let this function do nothing.
If implemented, the function should fill the passed array with data from
permanent storage, e.g. a file, a cookie etc.
If this function is called before SFG_save was ever called and no data is
present in permanent memory, this function should do nothing (leave the data
array as is).
This function should return 1 if saving/loading is possible and 0 if not (this
will be used by the game to detect this capability).
*/
void SFG_load(uint8_t data[SFG_SAVE_SIZE]);
uint8_t SFG_load(uint8_t data[SFG_SAVE_SIZE]);
/* ========================================================================= */
@ -323,8 +330,6 @@ struct
uint8_t selectedMenuItem;
uint8_t selectedLevel; ///< Level to play selected in the main menu.
uint8_t antiSpam; ///< Prevents log message spamming.
uint8_t saved; ///< Helper variable to know if game was saved.
uint8_t settings; /**< Dynamic game settings (can be changed at runtime),
bit meaning:
@ -336,6 +341,25 @@ struct
\____ freelook (shearing not sliding back) */
uint8_t blink; ///< Says whether blinkg is currently on or off.
uint8_t saved; /**< Helper variable to know if game was saved. Can be
0 (not saved), 1 (just saved) or 255 (can't save).*/
uint8_t save[SFG_SAVE_SIZE]; /**< Stores the game save state that's kept in
the persistent memory.
The save format is binary and platform independent.
The save contains game settings, game progress and a
saved position. The format is as follows:
4b highest level that has been reached
4b level number of the saved position (15: no save)
8b game settings (SFG_game.settings)
8b health at saved position
8b bullet ammo at saved position
8b rocket ammo at saved position
8b plasma ammo at saved position
32b little endian total play time, in 10ths of sec
16b little endian total enemies killed from start */
} SFG_game;
/**
@ -533,48 +557,12 @@ uint8_t SFG_getDamageValue(uint8_t attackType)
*/
void SFG_gameSave()
{
/*
The save format is binary and platform independent. The save contains game
settings, game progress and a saved position. The format is as follows:
if (SFG_game.saved == SFG_CANT_SAVE)
return;
4b Last level that has been reached and is accessible to play.
4b Level number of the saved position.
8b game settings (SFG_game.settings)
8b Health at saved position.
8b Bullets ammo at saved position.
8b Rockets ammo at saved position.
8b Plasma ammo at saved position.
32b Little endian total play time from start, in 10ths of second.
16b Little endian total enemies killed from start.
*/
SFG_LOG("saving game data");
SFG_LOG("Saving.");
uint8_t data[SFG_SAVE_SIZE];
data[0] = SFG_currentLevel.levelNumber | 0; // TODO
data[1] = SFG_game.settings;
data[2] = SFG_player.health;
for (uint8_t i = 0; i < 3; ++i)
data[3 + i] = SFG_player.ammo[i];
uint32_t time = 0; // TODO
for (uint8_t i = 0; i < 4; ++i)
{
data[6 + i] = time % 256;
time /= 256;
}
uint16_t kills = 0; // TODO
data[10] = kills % 256;
data[11] = kills / 256;
SFG_game.saved = 1;
// SFG_save(data);
SFG_save(SFG_game.save);
}
/**
@ -582,12 +570,17 @@ void SFG_gameSave()
*/
void SFG_gameLoad()
{
SFG_LOG("Loading.");
if (SFG_game.saved == SFG_CANT_SAVE)
return;
uint8_t data[SFG_SAVE_SIZE];
SFG_LOG("loading game data");
uint8_t result = SFG_load(SFG_game.save);
// SFG_load(data);
if (result == 0)
SFG_game.saved = SFG_CANT_SAVE;
SFG_game.settings = SFG_game.save[1];
}
/**
@ -1409,7 +1402,10 @@ void SFG_setAndInitLevel(uint8_t levelNumber)
const SFG_Level *level = &SFG_levels[levelNumber];
SFG_game.currentRandom = 0;
SFG_game.saved = 0;
if (SFG_game.saved != SFG_CANT_SAVE)
SFG_game.saved = 0;
SFG_currentLevel.levelNumber = levelNumber;
SFG_currentLevel.frameEnd = 0;
SFG_currentLevel.monstersDead = 0;
@ -1554,9 +1550,9 @@ void SFG_setAndInitLevel(uint8_t levelNumber)
SFG_currentLevel.frameStart = SFG_game.frame;
SFG_game.spriteAnimationFrame = 0;
SFG_initPlayer();
SFG_initPlayer();
SFG_setGameState(SFG_GAME_STATE_LEVEL_START);
}
@ -1573,7 +1569,6 @@ void SFG_init()
SFG_game.antiSpam = 0;
SFG_game.settings = 0x03;
SFG_enableMusic(1);
SFG_LOG("computing average texture colors")
@ -1628,6 +1623,27 @@ void SFG_init()
SFG_game.lastFrameTimeMs = SFG_getTimeMs();
SFG_game.selectedMenuItem = 0;
SFG_game.selectedLevel = 0;
SFG_game.settings = 0x03;
SFG_game.saved = 0;
// create a default save data:
for (uint16_t i = 0; i < SFG_SAVE_SIZE; ++i)
SFG_game.save[i] = 0;
SFG_game.save[0] = SFG_NUMBER_OF_LEVELS | 0xf0; // all levels revealed
SFG_game.save[1] = SFG_game.settings;
SFG_gameLoad(); // attempt to load settings
if (SFG_game.saved != SFG_CANT_SAVE)
{
SFG_LOG("settings loaded");
}
else
{
SFG_LOG("saving/loading not possible");
}
#if SFG_START_LEVEL == 0
SFG_setGameState(SFG_GAME_STATE_MENU);
@ -3522,13 +3538,20 @@ void SFG_gameStepMenu()
{
case SFG_MENU_ITEM_PLAY:
if (SFG_game.selectedLevel == 0)
{
SFG_setGameState(SFG_GAME_STATE_INTRO);
}
else
{
SFG_setAndInitLevel(SFG_game.selectedLevel);
}
break;
case SFG_MENU_ITEM_LOAD:
SFG_setAndInitLevel(SFG_game.save[0] >> 4);
SFG_player.health = SFG_game.save[2];
SFG_game.save[3] = SFG_player.ammo[3];
SFG_game.save[4] = SFG_player.ammo[4];
SFG_game.save[5] = SFG_player.ammo[5];
break;
case SFG_MENU_ITEM_CONTINUE:
@ -3550,6 +3573,10 @@ void SFG_gameStepMenu()
if ((SFG_game.settings & 0x02) !=
((SFG_game.settings - 1) & 0x02))
SFG_enableMusic(SFG_game.settings & 0x02);
SFG_game.save[1] = SFG_game.settings;
SFG_gameSave();
break;
case SFG_MENU_ITEM_SHEAR:
@ -3563,6 +3590,10 @@ void SFG_gameStepMenu()
SFG_game.settings =
(SFG_game.settings & ~0x0c) | ((current & 0x03) << 2);
SFG_game.save[1] = SFG_game.settings;
SFG_gameSave();
break;
}
@ -3656,10 +3687,37 @@ void SFG_gameStep()
else if (SFG_keyIsDown(SFG_KEY_RIGHT) ||
SFG_keyIsDown(SFG_KEY_LEFT))
{
SFG_setAndInitLevel(SFG_currentLevel.levelNumber);
uint8_t tmp[4];
tmp[0] = SFG_player.health;
tmp[1] = SFG_player.ammo[0];
tmp[2] = SFG_player.ammo[1];
tmp[3] = SFG_player.ammo[2];
SFG_setAndInitLevel(SFG_currentLevel.levelNumber + 1);
SFG_player.health = tmp[0];
SFG_player.ammo[0] = tmp[1];
SFG_player.ammo[1] = tmp[2];
SFG_player.ammo[2] = tmp[3];
if (SFG_keyIsDown(SFG_KEY_RIGHT))
{
// save the current position
SFG_game.save[0] =
(SFG_game.save[0] & 0x0f) | (SFG_currentLevel.levelNumber << 4);
SFG_game.save[2] = SFG_player.health;
SFG_game.save[3] = SFG_player.ammo[0];
SFG_game.save[4] = SFG_player.ammo[1];
SFG_game.save[5] = SFG_player.ammo[2];
SFG_gameSave();
if (SFG_game.saved != SFG_CANT_SAVE)
SFG_game.saved = 1;
}
}
}

View File

@ -29,7 +29,7 @@
// #define SFG_START_LEVEL 6
#define SFG_IMMORTAL 1
// #define SFG_UNLOCK_DOOR 1
#define SFG_UNLOCK_DOOR 1
#define SFG_REVEAL_MAP 1
#define SFG_INFINITE_AMMO 1
@ -62,6 +62,42 @@ uint32_t SFG_getTimeMs()
return SDL_GetTicks();
}
void SFG_save(uint8_t data[SFG_SAVE_SIZE])
{
FILE *f = fopen("anarch.sav","wb");
puts("SDL: opening and writing save file");
if (f == NULL)
{
puts("SDL: could not open the file!");
return;
}
fwrite(data,1,SFG_SAVE_SIZE,f);
fclose(f);
}
uint8_t SFG_load(uint8_t data[SFG_SAVE_SIZE])
{
FILE *f = fopen("anarch.sav","rb");
puts("SDL: opening and reading save file");
if (f == NULL)
{
puts("SDL: no save file to open");
}
else
{
fread(data,1,SFG_SAVE_SIZE,f);
fclose(f);
}
return 1;
}
void SFG_sleepMs(uint16_t timeMs)
{
#ifndef __EMSCRIPTEN__