Completes game saving and resuming
This commit is contained in:
parent
b385f6e73a
commit
5a9335576a
|
@ -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
|
||||||
|
|
111
src/main.c
111
src/main.c
|
@ -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;
|
||||||
{
|
#ifdef STEAM_BUILD
|
||||||
|
item_count += 1;
|
||||||
|
#endif
|
||||||
|
if (save_exists()) {
|
||||||
|
item_count += 1;
|
||||||
|
}
|
||||||
|
TEXT_MENU_ITEM *menuItems = ec_malloc(item_count * sizeof(TEXT_MENU_ITEM));
|
||||||
|
int i = 0;
|
||||||
|
if (save_exists()) {
|
||||||
|
menuItems[i++] = (TEXT_MENU_ITEM) {
|
||||||
|
"CONTINUE",
|
||||||
|
"Continue your last session",
|
||||||
|
continueGame,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
menuItems[i++] = (TEXT_MENU_ITEM) {
|
||||||
"STANDARD GAME",
|
"STANDARD GAME",
|
||||||
"Standard 20 level game, recommended for new players",
|
"Standard 20 level game, recommended for new players",
|
||||||
startRegularGame
|
startRegularGame
|
||||||
},
|
};
|
||||||
#ifdef STEAM_BUILD
|
#ifdef STEAM_BUILD
|
||||||
{
|
menuItems[i++] = (TEXT_MENU_ITEM) {
|
||||||
"WEEKLY CHALLENGE",
|
"WEEKLY CHALLENGE",
|
||||||
"Quick game with weekly leaderboards at breakhack.net",
|
"Quick game with weekly leaderboards at breakhack.net",
|
||||||
startWeeklyGame
|
startWeeklyGame
|
||||||
},
|
};
|
||||||
#endif
|
#endif
|
||||||
{
|
menuItems[i++] = (TEXT_MENU_ITEM) {
|
||||||
"QUICK GAME",
|
"QUICK GAME",
|
||||||
"Shorter 12 level game, with more action earlier in the game",
|
"Shorter 12 level game, with more action earlier in the game",
|
||||||
startQuickGame
|
startQuickGame
|
||||||
},
|
};
|
||||||
{
|
menuItems[i++] = (TEXT_MENU_ITEM) {
|
||||||
"ARCADE GAME",
|
"ARCADE GAME",
|
||||||
"One big level with lots of action",
|
"One big level with lots of action",
|
||||||
startArcadeGame
|
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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
55
src/save.h
55
src/save.h
|
@ -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_
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue