Completes game saving and resuming

This commit is contained in:
Linus Probert 2020-03-03 23:50:11 +01:00
parent b385f6e73a
commit 5a9335576a
6 changed files with 303 additions and 41 deletions

View File

@ -192,6 +192,7 @@ add_executable(breakhack
src/util src/util
src/event src/event
src/player src/player
src/save
src/map src/map
src/map_lua src/map_lua
src/camera src/camera

View File

@ -58,6 +58,7 @@
#include "sprite_util.h" #include "sprite_util.h"
#include "event.h" #include "event.h"
#include "config.h" #include "config.h"
#include "save.h"
#ifdef STEAM_BUILD #ifdef STEAM_BUILD
#include "checksum.h" #include "checksum.h"
@ -420,6 +421,56 @@ goToCharacterMenu(void *unused)
gGameState = CHARACTER_MENU; gGameState = CHARACTER_MENU;
} }
static void
choose_music(void)
{
if (cLevel > (unsigned int) (quickGame ? 11 : 19)) {
mixer_play_music(BOSS_MUSIC0);
} else if (cLevel % (quickGame ? 3 : 5) == 0) {
gui_log("You sense something powerful in the vicinity");
mixer_play_music(BOSS_MUSIC0);
} else {
mixer_play_music(GAME_MUSIC0 + get_random(2));
}
}
static void
continueGame(void *unused)
{
(void) unused;
const Save *save = save_get();
quickGame = save->quickGame;
arcadeGame = save->arcadeGame;
playerClass = save->player_class;
cLevel = save->map_level;
set_random_seed(save->seed);
debug("Loading seed: %d", save->seed);
debug("Loading map level: %d", save->map_level);
gGameState = PLAYING;
if (gPlayer)
player_destroy(gPlayer);
gPlayer = player_create(playerClass, gCamera);
// Load player from save
gPlayer->daggers = save->player_daggers;
gPlayer->xp = save->player_xp;
gPlayer->stateData = save->player_state;
gPlayer->stats = save->player_stats;
gPlayer->stat_data = save->player_player_stats;
gPlayer->potion_sips = save->player_potion_sips;
gPlayer->equipment = save->player_equipment;
gPlayer->gold = save->player_gold;
choose_music();
resetGame();
skillbar_reset(gSkillBar);
gui_clear_message_log();
gui_log("The Dungeon Crawl continues!");
gui_event_message("Welcome back!");
}
static void static void
startRegularGame(void *unused) startRegularGame(void *unused)
{ {
@ -459,6 +510,7 @@ static void
goToMainMenu(void *unused) goToMainMenu(void *unused)
{ {
UNUSED(unused); UNUSED(unused);
save_load();
gui_clear_message_log(); gui_clear_message_log();
gGameState = MENU; gGameState = MENU;
menu_destroy(inGameMenu); menu_destroy(inGameMenu);
@ -474,37 +526,47 @@ static void
goToGameSelectMenu(void *unused) goToGameSelectMenu(void *unused)
{ {
UNUSED(unused); UNUSED(unused);
static TEXT_MENU_ITEM menuItems[] = { int item_count = 3;
{
"STANDARD GAME",
"Standard 20 level game, recommended for new players",
startRegularGame
},
#ifdef STEAM_BUILD #ifdef STEAM_BUILD
{ item_count += 1;
"WEEKLY CHALLENGE",
"Quick game with weekly leaderboards at breakhack.net",
startWeeklyGame
},
#endif #endif
{ if (save_exists()) {
"QUICK GAME", item_count += 1;
"Shorter 12 level game, with more action earlier in the game", }
startQuickGame TEXT_MENU_ITEM *menuItems = ec_malloc(item_count * sizeof(TEXT_MENU_ITEM));
}, int i = 0;
{ if (save_exists()) {
"ARCADE GAME", menuItems[i++] = (TEXT_MENU_ITEM) {
"One big level with lots of action", "CONTINUE",
startArcadeGame "Continue your last session",
} continueGame,
};
}
menuItems[i++] = (TEXT_MENU_ITEM) {
"STANDARD GAME",
"Standard 20 level game, recommended for new players",
startRegularGame
};
#ifdef STEAM_BUILD
menuItems[i++] = (TEXT_MENU_ITEM) {
"WEEKLY CHALLENGE",
"Quick game with weekly leaderboards at breakhack.net",
startWeeklyGame
};
#endif
menuItems[i++] = (TEXT_MENU_ITEM) {
"QUICK GAME",
"Shorter 12 level game, with more action earlier in the game",
startQuickGame
};
menuItems[i++] = (TEXT_MENU_ITEM) {
"ARCADE GAME",
"One big level with lots of action",
startArcadeGame
}; };
#ifdef STEAM_BUILD menu_create_text_menu(&gameSelectMenu, menuItems, item_count, gRenderer);
int count = 4; free(menuItems);
#else
int count = 3;
#endif
menu_create_text_menu(&gameSelectMenu, &menuItems[0], count, gRenderer);
gGameState = GAME_SELECT; gGameState = GAME_SELECT;
} }
@ -691,6 +753,7 @@ init(void)
event_register_listener(on_event_callback); event_register_listener(on_event_callback);
settings_init(); settings_init();
save_init();
hiscore_init(); hiscore_init();
initMainMenu(); initMainMenu();
@ -874,17 +937,17 @@ check_next_level(void)
return; return;
} }
if (tile->levelExit) { if (tile->levelExit) {
mixer_play_effect(NEXT_LEVEL);
++cLevel; ++cLevel;
if (cLevel > (unsigned int) (quickGame ? 11 : 19)) { if (!weeklyGame) {
mixer_play_music(BOSS_MUSIC0); save_save(get_random_seed(),
} else if (cLevel % (quickGame ? 3 : 5) == 0) { cLevel,
gui_log("You sense something powerful in the vicinity"); quickGame,
mixer_play_music(BOSS_MUSIC0); arcadeGame,
} else { gPlayer);
mixer_play_music(GAME_MUSIC0 + get_random(2));
} }
mixer_play_effect(NEXT_LEVEL);
choose_music();
if (!gameCompleted()) { if (!gameCompleted()) {
resetGame(); resetGame();
} }
@ -1116,6 +1179,7 @@ run_game(void)
camera_shake(VECTOR2D_RIGHT, 800); camera_shake(VECTOR2D_RIGHT, 800);
gui_log("The dungeon consumed you"); gui_log("The dungeon consumed you");
gui_event_message("You died!"); gui_event_message("You died!");
save_clear();
end_game_details(); end_game_details();
mixer_play_effect(SPLAT); mixer_play_effect(SPLAT);
gGameState = GAME_OVER; gGameState = GAME_OVER;
@ -1349,6 +1413,7 @@ void close(void)
texturecache_close(); texturecache_close();
settings_close(); settings_close();
hiscore_close(); hiscore_close();
save_close();
#ifdef STEAM_BUILD #ifdef STEAM_BUILD
steam_shutdown(); steam_shutdown();

View File

@ -26,8 +26,8 @@
#include "sprite.h" #include "sprite.h"
typedef struct TEXT_MENU_ITEM { typedef struct TEXT_MENU_ITEM {
char label[20]; char *label;
char description[100]; char *description;
void (*callback)(void*); void (*callback)(void*);
} TEXT_MENU_ITEM; } TEXT_MENU_ITEM;

147
src/save.c Normal file
View File

@ -0,0 +1,147 @@
/*
* BreakHack - A dungeone crawler RPG
* Copyright (C) 2018 Linus Probert <linus.probert@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "save.h"
#include "sqlite3.h"
#include "db.h"
#include "defines.h"
#include "util.h"
static sqlite3 *db = NULL;
static bool loaded = false;
static Save save;
static
DbQuery MIGRATE_COMMAND = {
"CREATE TABLE IF NOT EXISTS saves("
"major_version INTEGER, "
"minor_version INTEGER, "
"patch_version INTEGER, "
"save BLOB)",
NULL, NULL
};
static
DbQuery CLEAR_SAVE = { "DELETE FROM saves", NULL, NULL };
static void
create_table(void)
{
db_execute(db, &MIGRATE_COMMAND);
}
void
save_load(void)
{
debug("Loading save");
const char *query =
"SELECT save FROM saves "
"WHERE major_version = ?"
"AND minor_version = ?"
"AND patch_version = ?"
"LIMIT 1";
sqlite3_stmt *stmt = db_prepare(db, query);
sqlite3_bind_int(stmt, 1, MAJOR_VERSION);
sqlite3_bind_int(stmt, 2, MINOR_VERSION);
sqlite3_bind_int(stmt, 3, PATCH_VERSION);
if (SQLITE_ROW == sqlite3_step(stmt)) {
int size = sqlite3_column_bytes(stmt, 0);
debug("Reading save bytes: %d", size);
memcpy(&save, sqlite3_column_blob(stmt, 0), size);
loaded = true;
} else {
loaded = false;
}
sqlite3_finalize(stmt);
}
void
save_init(void)
{
if (!db_open(DB_FILE, &db)) {
db_close(&db);
fatal("Exiting");
}
create_table();
save_load();
}
const Save *
save_get(void)
{
return &save;
}
bool
save_exists(void)
{
return loaded;
}
void
save_save(unsigned int seed,
unsigned int map_level,
bool quickGame,
bool arcadeGame,
Player *player)
{
debug("Saving game, Seed: %d, Map level: %d", seed, map_level);
save_clear();
save.seed = seed;
save.map_level = map_level;
save.quickGame = quickGame;
save.arcadeGame = arcadeGame;
save.player_stats = player->stats;
save.player_daggers = player->daggers;
save.player_gold = player->gold;
save.player_xp = player->xp;
save.player_potion_sips = player->potion_sips;
save.player_player_stats = player->stat_data;
save.player_state = player->stateData;
save.player_class = player->class;
save.player_equipment = player->equipment;
const char *query =
"INSERT INTO saves"
"(major_version, minor_version, patch_version, save) "
"VALUES(?, ?, ?, ?)";
sqlite3_stmt *stmt = db_prepare(db, query);
sqlite3_bind_int(stmt, 1, MAJOR_VERSION);
sqlite3_bind_int(stmt, 2, MINOR_VERSION);
sqlite3_bind_int(stmt, 3, PATCH_VERSION);
sqlite3_bind_blob(stmt, 4, &save, sizeof(Save), SQLITE_STATIC);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
}
void
save_clear(void)
{
db_execute(db, &CLEAR_SAVE);
}
void
save_close(void)
{
db_close(&db);
}

View File

@ -1,16 +1,67 @@
/*
* BreakHack - A dungeone crawler RPG
* Copyright (C) 2018 Linus Probert <linus.probert@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SAVE_H_ #ifndef SAVE_H_
#define SAVE_H_ #define SAVE_H_
#include <stdbool.h>
#include "player.h" #include "player.h"
#include "artifact.h"
typedef struct Save { typedef struct Save {
int seed; int seed;
bool quickGame;
bool arcadeGame;
unsigned int map_level; unsigned int map_level;
unsigned int player_level;
unsigned int player_daggers; unsigned int player_daggers;
unsigned int player_xp; unsigned int player_xp;
unsigned int player_potion_sips;
unsigned int potion_sips;
Stats player_stats;
PlayerStatData player_player_stats;
double player_gold; double player_gold;
class_t class; PlayerStateData player_state;
class_t player_class;
PlayerEquipment player_equipment;
} Save; } Save;
void
save_init(void);
void
save_load(void);
const Save *
save_get(void);
bool
save_exists(void);
void
save_save(unsigned int seed,
unsigned int map_level,
bool quickGame,
bool arcadeGame,
Player *player);
void
save_clear(void);
void
save_close(void);
#endif // SAVE_H_ #endif // SAVE_H_

View File

@ -51,8 +51,6 @@ texturecache_add(const char *path)
texture_load_from_file(tc->texture, path, renderer); texture_load_from_file(tc->texture, path, renderer);
ht_set(textures, path, tc); ht_set(textures, path, tc);
debug("Cached texture: %s", path); debug("Cached texture: %s", path);
} else {
debug("Retrieved cached texture: %s", path);
} }
return tc->texture; return tc->texture;