2018-01-21 10:31:38 +01:00
|
|
|
/*
|
|
|
|
Copyright (C) 2018 Parallel Realities
|
|
|
|
|
|
|
|
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 2
|
|
|
|
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, write to the Free Software
|
|
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "game.h"
|
|
|
|
|
2018-01-28 16:48:20 +01:00
|
|
|
static void loadMetaInfo(void);
|
2018-02-08 22:53:52 +01:00
|
|
|
static void addKeyToStash(Item *item);
|
2018-02-27 09:14:54 +01:00
|
|
|
static int sortItems(const void *a, const void *b);
|
2018-03-19 23:51:37 +01:00
|
|
|
void destroyGame(void);
|
2018-01-24 08:16:52 +01:00
|
|
|
|
|
|
|
void initGame(void)
|
|
|
|
{
|
|
|
|
memset(&game, 0, sizeof(Game));
|
|
|
|
|
2018-01-28 17:14:17 +01:00
|
|
|
game.missionStatusTail = &game.missionStatusHead;
|
2018-02-15 22:37:43 +01:00
|
|
|
game.trophyTail = &game.trophyHead;
|
2018-01-28 17:14:17 +01:00
|
|
|
|
2018-01-24 08:16:52 +01:00
|
|
|
game.cells = 5;
|
|
|
|
game.hearts = 10;
|
|
|
|
|
2018-02-13 22:36:42 +01:00
|
|
|
game.stats[STAT_TIME_PLAYED] = 0;
|
2018-01-28 16:48:20 +01:00
|
|
|
|
|
|
|
loadMetaInfo();
|
2018-03-19 23:51:37 +01:00
|
|
|
|
|
|
|
loadTrophyData();
|
2018-01-24 08:16:52 +01:00
|
|
|
}
|
|
|
|
|
2018-03-20 08:29:05 +01:00
|
|
|
void newGame(void)
|
|
|
|
{
|
|
|
|
destroyGame();
|
|
|
|
|
|
|
|
initGame();
|
|
|
|
}
|
|
|
|
|
2018-02-18 10:28:25 +01:00
|
|
|
int addItem(Item *item, int num)
|
2018-01-24 08:16:52 +01:00
|
|
|
{
|
2018-02-04 09:11:42 +01:00
|
|
|
int i;
|
2018-02-07 08:51:47 +01:00
|
|
|
|
2018-03-17 17:58:14 +01:00
|
|
|
for (i = 0 ; i < MAX_ITEMS ; i++)
|
2018-02-04 09:11:42 +01:00
|
|
|
{
|
2018-03-17 17:58:14 +01:00
|
|
|
if (item->type == ET_KEY && world.bob->items[i] != NULL && world.bob->items[i]->type == ET_KEY && strcmp(item->name, world.bob->items[i]->name) == 0)
|
2018-02-04 09:11:42 +01:00
|
|
|
{
|
2018-03-17 17:58:14 +01:00
|
|
|
world.bob->items[i]->value++;
|
|
|
|
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG, "Inc. %s value (%d)(+1) - Killing", item->name, world.bob->items[i]->value);
|
|
|
|
item->alive = ALIVE_DEAD;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else if (world.bob->items[i] == NULL)
|
|
|
|
{
|
|
|
|
world.bob->items[i] = item;
|
|
|
|
item->canBePickedUp = 0;
|
|
|
|
item->flags |= EF_GONE;
|
|
|
|
if (item->type == ET_KEY)
|
2018-02-06 23:27:02 +01:00
|
|
|
{
|
2018-03-17 17:58:14 +01:00
|
|
|
item->value = num;
|
2018-02-06 23:27:02 +01:00
|
|
|
}
|
2018-03-17 17:58:14 +01:00
|
|
|
|
|
|
|
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG, "Added %s (value=%d)", item->name, world.bob->items[i]->value);
|
|
|
|
|
|
|
|
return 1;
|
2018-02-04 09:11:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2018-01-24 08:16:52 +01:00
|
|
|
}
|
|
|
|
|
2018-01-27 09:53:08 +01:00
|
|
|
int hasItem(char *name)
|
|
|
|
{
|
2018-02-04 09:11:42 +01:00
|
|
|
int i;
|
|
|
|
Item *item;
|
2018-02-06 23:27:02 +01:00
|
|
|
|
2018-02-04 09:11:42 +01:00
|
|
|
for (i = 0 ; i < MAX_ITEMS ; i++)
|
|
|
|
{
|
2018-02-07 08:51:47 +01:00
|
|
|
item = world.bob->items[i];
|
2018-02-04 09:11:42 +01:00
|
|
|
|
|
|
|
if (item != NULL && strcmp(item->name, name) == 0)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-27 09:53:08 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-02-07 08:51:47 +01:00
|
|
|
Item *getItem(char *name)
|
2018-01-27 11:47:53 +01:00
|
|
|
{
|
2018-02-04 09:11:42 +01:00
|
|
|
int i;
|
|
|
|
Item *item;
|
|
|
|
|
|
|
|
for (i = 0 ; i < MAX_ITEMS ; i++)
|
|
|
|
{
|
2018-02-07 08:51:47 +01:00
|
|
|
item = world.bob->items[i];
|
2018-02-04 09:11:42 +01:00
|
|
|
|
|
|
|
if (item != NULL && strcmp(item->name, name) == 0)
|
|
|
|
{
|
|
|
|
return world.bob->items[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-27 11:47:53 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void removeItem(char *name)
|
2018-01-27 09:53:08 +01:00
|
|
|
{
|
2018-02-04 09:11:42 +01:00
|
|
|
int i;
|
|
|
|
Item *item;
|
|
|
|
|
|
|
|
for (i = 0 ; i < MAX_ITEMS ; i++)
|
|
|
|
{
|
2018-02-07 08:51:47 +01:00
|
|
|
item = world.bob->items[i];
|
2018-02-04 09:11:42 +01:00
|
|
|
|
|
|
|
if (item != NULL && strcmp(item->name, name) == 0)
|
|
|
|
{
|
2018-02-07 08:51:47 +01:00
|
|
|
/* only null this, as whether to kill it is handled elsewhere */
|
|
|
|
if (item->type != ET_KEY)
|
|
|
|
{
|
|
|
|
world.bob->items[i] = NULL;
|
2018-02-27 09:14:54 +01:00
|
|
|
qsort(world.bob->items, MAX_ITEMS, sizeof(Entity*), sortItems);
|
2018-02-07 08:51:47 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (--item->value == 0)
|
|
|
|
{
|
|
|
|
item->flags &= ~EF_GONE;
|
|
|
|
item->alive = ALIVE_DEAD;
|
|
|
|
world.bob->items[i] = NULL;
|
2018-02-27 09:14:54 +01:00
|
|
|
qsort(world.bob->items, MAX_ITEMS, sizeof(Entity*), sortItems);
|
|
|
|
return;
|
2018-02-07 08:51:47 +01:00
|
|
|
}
|
|
|
|
}
|
2018-02-04 09:11:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void dropCarriedItems(void)
|
|
|
|
{
|
|
|
|
int i;
|
2018-02-07 08:51:47 +01:00
|
|
|
Item *item;
|
2018-02-08 22:53:52 +01:00
|
|
|
|
|
|
|
memset(game.keys, 0, sizeof(Tuple) * MAX_KEY_TYPES);
|
2018-02-04 09:11:42 +01:00
|
|
|
|
|
|
|
for (i = 0 ; i < MAX_ITEMS ; i++)
|
|
|
|
{
|
2018-02-06 23:27:02 +01:00
|
|
|
item = world.bob->items[i];
|
|
|
|
|
2018-02-08 22:53:52 +01:00
|
|
|
if (item != NULL)
|
|
|
|
{
|
|
|
|
if (item->type == ET_KEY)
|
|
|
|
{
|
|
|
|
addKeyToStash(item);
|
|
|
|
|
|
|
|
item->alive = ALIVE_DEAD;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item->flags &= ~EF_GONE;
|
|
|
|
item->x = world.bob->checkpoints[0].x;
|
|
|
|
item->y = world.bob->checkpoints[0].y;
|
2018-03-04 09:15:30 +01:00
|
|
|
item->collected = 1;
|
2018-03-03 17:05:55 +01:00
|
|
|
item->canBeCarried = 1;
|
|
|
|
item->canBePickedUp = 1;
|
|
|
|
|
2018-02-08 22:53:52 +01:00
|
|
|
/* items can only be collected if they have a thinktime of 0 */
|
|
|
|
item->thinkTime = FPS * 9999;
|
|
|
|
}
|
2018-02-10 20:03:55 +01:00
|
|
|
|
|
|
|
world.bob->items[i] = NULL;
|
2018-02-08 22:53:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void addKeyToStash(Item *item)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
Tuple *t;
|
|
|
|
|
|
|
|
for (i = 0 ; i < MAX_KEY_TYPES ; i++)
|
|
|
|
{
|
|
|
|
t = &game.keys[i];
|
|
|
|
|
|
|
|
if (t->value.i == 0)
|
|
|
|
{
|
2018-02-18 10:28:25 +01:00
|
|
|
STRNCPY(t->key, item->sprite[0]->name, MAX_NAME_LENGTH);
|
2018-02-08 22:53:52 +01:00
|
|
|
t->value.i = item->value;
|
|
|
|
|
2018-02-15 22:37:43 +01:00
|
|
|
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG, "Added %s (x%d) to stash", t->key, t->value.i);
|
2018-02-08 23:26:00 +01:00
|
|
|
|
|
|
|
return;
|
2018-02-08 22:53:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void addKeysFromStash(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
Tuple *t;
|
|
|
|
Item *item;
|
|
|
|
|
|
|
|
for (i = 0 ; i < MAX_KEY_TYPES ; i++)
|
|
|
|
{
|
|
|
|
t = &game.keys[i];
|
|
|
|
|
|
|
|
if (t->value.i > 0)
|
2018-02-06 23:27:02 +01:00
|
|
|
{
|
2018-02-08 22:53:52 +01:00
|
|
|
item = (Item*)createEntity(t->key);
|
2018-02-18 10:28:25 +01:00
|
|
|
self = (Entity*)item;
|
2018-02-08 22:53:52 +01:00
|
|
|
item->init();
|
2018-02-18 10:28:25 +01:00
|
|
|
item->animate();
|
2018-02-08 22:53:52 +01:00
|
|
|
|
2018-03-07 22:36:57 +01:00
|
|
|
item->x = item->startX = world.bob->x;
|
|
|
|
item->y = item->startY = world.bob->y;
|
|
|
|
|
2018-02-18 10:28:25 +01:00
|
|
|
addItem(item, t->value.i);
|
2018-02-08 22:53:52 +01:00
|
|
|
|
2018-02-15 22:37:43 +01:00
|
|
|
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG, "Added %s (x%d) to inventory", item->name, item->value);
|
2018-02-06 23:27:02 +01:00
|
|
|
}
|
2018-02-04 09:11:42 +01:00
|
|
|
}
|
2018-01-27 09:53:08 +01:00
|
|
|
}
|
|
|
|
|
2018-02-22 08:45:57 +01:00
|
|
|
int getMissionStatus(char *id)
|
|
|
|
{
|
|
|
|
Tuple *t;
|
|
|
|
|
|
|
|
for (t = game.missionStatusHead.next ; t != NULL ; t = t->next)
|
|
|
|
{
|
|
|
|
if (strcmp(t->key, id) == 0)
|
|
|
|
{
|
|
|
|
return t->value.i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return MS_LOCKED;
|
|
|
|
}
|
|
|
|
|
2018-01-28 16:48:20 +01:00
|
|
|
static void loadMetaInfo(void)
|
|
|
|
{
|
|
|
|
cJSON *root;
|
|
|
|
char *text;
|
|
|
|
|
|
|
|
text = readFile("data/meta/meta.json");
|
|
|
|
|
|
|
|
root = cJSON_Parse(text);
|
|
|
|
|
|
|
|
game.totalKeys = cJSON_GetObjectItem(root, "totalKeys")->valueint;
|
|
|
|
game.totalTargets = cJSON_GetObjectItem(root, "totalTargets")->valueint;
|
|
|
|
game.totalMIAs = cJSON_GetObjectItem(root, "totalMIAs")->valueint;
|
|
|
|
game.totalHearts = cJSON_GetObjectItem(root, "totalHearts")->valueint;
|
|
|
|
game.totalCells = cJSON_GetObjectItem(root, "totalCells")->valueint;
|
|
|
|
|
|
|
|
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG, "Meta [keys=%d, targets=%d, mias=%d, hearts=%d, cells=%d]", game.totalKeys, game.totalTargets, game.totalMIAs, game.totalHearts, game.totalCells);
|
|
|
|
|
|
|
|
cJSON_Delete(root);
|
|
|
|
|
|
|
|
free(text);
|
|
|
|
}
|
|
|
|
|
2018-02-15 22:37:43 +01:00
|
|
|
void loadGame(void)
|
|
|
|
{
|
|
|
|
cJSON *root, *node, *statsJSON;
|
|
|
|
char *text, filename[MAX_FILENAME_LENGTH], *statName;
|
|
|
|
int i;
|
|
|
|
Tuple *t;
|
|
|
|
Trophy *trophy;
|
2018-03-19 23:51:37 +01:00
|
|
|
|
|
|
|
destroyGame();
|
|
|
|
|
|
|
|
initGame();
|
2018-02-15 22:37:43 +01:00
|
|
|
|
2018-03-18 10:42:34 +01:00
|
|
|
sprintf(filename, "%s/%d/game.json", app.saveDir, game.saveSlot);
|
2018-02-15 22:37:43 +01:00
|
|
|
|
|
|
|
if (fileExists(filename))
|
|
|
|
{
|
|
|
|
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Loading game '%s' ...", filename);
|
|
|
|
|
|
|
|
text = readFile(filename);
|
|
|
|
|
|
|
|
root = cJSON_Parse(text);
|
|
|
|
|
|
|
|
game.cells = cJSON_GetObjectItem(root, "cells")->valueint;
|
|
|
|
game.hearts = cJSON_GetObjectItem(root, "hearts")->valueint;
|
|
|
|
|
|
|
|
statsJSON = cJSON_GetObjectItem(root, "stats");
|
|
|
|
|
|
|
|
for (i = 0 ; i < STAT_MAX ; i++)
|
|
|
|
{
|
|
|
|
statName = getLookupName("STAT_", i);
|
|
|
|
|
|
|
|
if (cJSON_GetObjectItem(statsJSON, statName))
|
|
|
|
{
|
|
|
|
game.stats[i] = cJSON_GetObjectItem(statsJSON, statName)->valueint;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
for (node = cJSON_GetObjectItem(root, "keys")->child ; node != NULL ; node = node->next)
|
|
|
|
{
|
|
|
|
STRNCPY(game.keys[i].key, cJSON_GetObjectItem(node, "type")->valuestring, MAX_NAME_LENGTH);
|
|
|
|
game.keys[i].value.i = cJSON_GetObjectItem(node, "num")->valueint;
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (node = cJSON_GetObjectItem(root, "missions")->child ; node != NULL ; node = node->next)
|
|
|
|
{
|
|
|
|
t = malloc(sizeof(Tuple));
|
|
|
|
memset(t, 0, sizeof(Tuple));
|
|
|
|
game.missionStatusTail->next = t;
|
|
|
|
game.missionStatusTail = t;
|
|
|
|
|
|
|
|
STRNCPY(t->key, cJSON_GetObjectItem(node, "id")->valuestring, MAX_NAME_LENGTH);
|
|
|
|
t->value.i = lookup(cJSON_GetObjectItem(node, "status")->valuestring);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (node = cJSON_GetObjectItem(root, "trophies")->child ; node != NULL ; node = node->next)
|
|
|
|
{
|
|
|
|
trophy = getTrophy(cJSON_GetObjectItem(node, "id")->valuestring);
|
|
|
|
trophy->awardDate = cJSON_GetObjectItem(node, "awardDate")->valueint;
|
|
|
|
}
|
|
|
|
|
|
|
|
cJSON_Delete(root);
|
|
|
|
|
|
|
|
free(text);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void saveGame(void)
|
|
|
|
{
|
|
|
|
cJSON *root, *statsJSON, *keysJSON, *keyJSON, *missionsJSON, *missionJSON, *trophiesJSON, *trophyJSON;
|
|
|
|
char filename[MAX_FILENAME_LENGTH], *out;
|
|
|
|
Tuple *t;
|
|
|
|
Trophy *trophy;
|
|
|
|
int i;
|
|
|
|
|
2018-03-18 10:42:34 +01:00
|
|
|
sprintf(filename, "%s/%d/game.json", app.saveDir, game.saveSlot);
|
2018-02-15 22:37:43 +01:00
|
|
|
|
|
|
|
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Saving game to '%s' ...", filename);
|
|
|
|
|
|
|
|
root = cJSON_CreateObject();
|
|
|
|
cJSON_AddNumberToObject(root, "cells", game.cells);
|
|
|
|
cJSON_AddNumberToObject(root, "hearts", game.hearts);
|
|
|
|
|
|
|
|
statsJSON = cJSON_CreateObject();
|
|
|
|
for (i = 0 ; i < STAT_MAX ; i++)
|
|
|
|
{
|
|
|
|
cJSON_AddNumberToObject(statsJSON, getLookupName("STAT_", i), game.stats[i]);
|
|
|
|
}
|
|
|
|
cJSON_AddItemToObject(root, "stats", statsJSON);
|
|
|
|
|
|
|
|
keysJSON = cJSON_CreateArray();
|
|
|
|
for (i = 0 ; i < MAX_KEY_TYPES ; i++)
|
|
|
|
{
|
|
|
|
keyJSON = cJSON_CreateObject();
|
|
|
|
cJSON_AddStringToObject(keyJSON, "type", game.keys[i].key);
|
|
|
|
cJSON_AddNumberToObject(keyJSON, "num", game.keys[i].value.i);
|
|
|
|
|
|
|
|
cJSON_AddItemToArray(keysJSON, keyJSON);
|
|
|
|
}
|
|
|
|
cJSON_AddItemToObject(root, "keys", keysJSON);
|
|
|
|
|
|
|
|
missionsJSON = cJSON_CreateArray();
|
|
|
|
for (t = game.missionStatusHead.next ; t != NULL ; t = t->next)
|
|
|
|
{
|
|
|
|
missionJSON = cJSON_CreateObject();
|
|
|
|
cJSON_AddStringToObject(missionJSON, "id", t->key);
|
|
|
|
cJSON_AddStringToObject(missionJSON, "status", getLookupName("MS_", t->value.i));
|
|
|
|
|
|
|
|
cJSON_AddItemToArray(missionsJSON, missionJSON);
|
|
|
|
}
|
|
|
|
cJSON_AddItemToObject(root, "missions", missionsJSON);
|
|
|
|
|
|
|
|
trophiesJSON = cJSON_CreateArray();
|
|
|
|
for (trophy = game.trophyHead.next ; trophy != NULL ; trophy = trophy->next)
|
|
|
|
{
|
|
|
|
trophyJSON = cJSON_CreateObject();
|
|
|
|
cJSON_AddStringToObject(trophyJSON, "id", trophy->id);
|
|
|
|
cJSON_AddNumberToObject(trophyJSON, "awardDate", trophy->awardDate);
|
|
|
|
|
|
|
|
cJSON_AddItemToArray(trophiesJSON, trophyJSON);
|
|
|
|
}
|
|
|
|
cJSON_AddItemToObject(root, "trophies", trophiesJSON);
|
|
|
|
|
|
|
|
out = cJSON_Print(root);
|
|
|
|
|
|
|
|
if (!writeFile(filename, out))
|
|
|
|
{
|
|
|
|
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "Failed to save game");
|
2018-03-18 10:42:34 +01:00
|
|
|
exit(1);
|
2018-02-15 22:37:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
cJSON_Delete(root);
|
|
|
|
free(out);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the player quits during a mission, we don't want to save the keys, etc. they have picked up,
|
|
|
|
* so instead reload the old save and cherry pick the data to keep.
|
|
|
|
*/
|
|
|
|
void restoreGameState(void)
|
|
|
|
{
|
|
|
|
cJSON *root, *node, *statsJSON;
|
|
|
|
char *text, filename[MAX_FILENAME_LENGTH];
|
|
|
|
int i;
|
|
|
|
|
2018-03-18 10:42:34 +01:00
|
|
|
sprintf(filename, "%s/%d/game.json", app.saveDir, game.saveSlot);
|
2018-02-15 22:37:43 +01:00
|
|
|
|
|
|
|
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Restoring game from '%s' ...", filename);
|
|
|
|
|
|
|
|
text = readFile(filename);
|
|
|
|
|
|
|
|
root = cJSON_Parse(text);
|
|
|
|
|
|
|
|
game.cells = cJSON_GetObjectItem(root, "cells")->valueint;
|
|
|
|
game.hearts = cJSON_GetObjectItem(root, "hearts")->valueint;
|
|
|
|
|
|
|
|
statsJSON = cJSON_GetObjectItem(root, "stats");
|
|
|
|
|
|
|
|
game.stats[STAT_KEYS_FOUND] = cJSON_GetObjectItem(statsJSON, "STAT_KEYS_FOUND")->valueint;
|
|
|
|
game.stats[STAT_CELLS_FOUND] = cJSON_GetObjectItem(statsJSON, "STAT_CELLS_FOUND")->valueint;
|
|
|
|
game.stats[STAT_HEARTS_FOUND] = cJSON_GetObjectItem(statsJSON, "STAT_HEARTS_FOUND")->valueint;
|
|
|
|
game.stats[STAT_TARGETS_DEFEATED] = cJSON_GetObjectItem(statsJSON, "STAT_TARGETS_DEFEATED")->valueint;
|
|
|
|
game.stats[STAT_MIAS_RESCUED] = cJSON_GetObjectItem(statsJSON, "STAT_MIAS_RESCUED")->valueint;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
memset(game.keys, 0, sizeof(Tuple) * MAX_KEY_TYPES);
|
|
|
|
for (node = cJSON_GetObjectItem(root, "keys")->child ; node != NULL ; node = node->next)
|
|
|
|
{
|
|
|
|
STRNCPY(game.keys[i].key, cJSON_GetObjectItem(node, "type")->valuestring, MAX_NAME_LENGTH);
|
|
|
|
game.keys[i].value.i = cJSON_GetObjectItem(node, "num")->valueint;
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
cJSON_Delete(root);
|
|
|
|
|
|
|
|
free(text);
|
|
|
|
}
|
|
|
|
|
2018-03-19 23:51:37 +01:00
|
|
|
char *getSaveWidgetLabel(char *filename)
|
|
|
|
{
|
|
|
|
static char label[MAX_NAME_LENGTH];
|
|
|
|
cJSON *root, *statsJSON;
|
|
|
|
char *text, *statName;
|
|
|
|
int i, gameDone, gameTotal, stats[STAT_MAX];
|
|
|
|
|
|
|
|
strcpy(label, "");
|
|
|
|
|
|
|
|
sprintf(filename, "%s/%d/game.json", app.saveDir, game.saveSlot);
|
|
|
|
|
|
|
|
text = readFile(filename);
|
|
|
|
|
|
|
|
root = cJSON_Parse(text);
|
|
|
|
|
|
|
|
statsJSON = cJSON_GetObjectItem(root, "stats");
|
|
|
|
|
|
|
|
memset(stats, 0, sizeof(int) * STAT_MAX);
|
|
|
|
|
|
|
|
for (i = 0 ; i < STAT_MAX ; i++)
|
|
|
|
{
|
|
|
|
statName = getLookupName("STAT_", i);
|
|
|
|
|
|
|
|
if (cJSON_GetObjectItem(statsJSON, statName))
|
|
|
|
{
|
|
|
|
stats[i] = cJSON_GetObjectItem(statsJSON, statName)->valueint;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cJSON_Delete(root);
|
|
|
|
|
|
|
|
free(text);
|
|
|
|
|
|
|
|
gameDone = stats[STAT_MISSIONS_COMPLETE] + stats[STAT_MIAS_RESCUED] + stats[STAT_TARGETS_DEFEATED] + stats[STAT_KEYS_FOUND] + stats[STAT_HEARTS_FOUND] + stats[STAT_CELLS_FOUND];
|
|
|
|
gameTotal = game.totalMissions + game.totalMIAs + game.totalTargets + game.totalKeys + game.totalHearts + game.totalCells;
|
|
|
|
|
|
|
|
sprintf(label, "%d%% - %s", getPercent(gameDone, gameTotal), timeToString(stats[STAT_TIME_PLAYED], 1));
|
|
|
|
|
|
|
|
return label;
|
|
|
|
}
|
|
|
|
|
|
|
|
void deleteSaveSlot(int slot)
|
|
|
|
{
|
|
|
|
int i, numFiles;
|
|
|
|
char path[MAX_FILENAME_LENGTH], **filenames;
|
|
|
|
|
|
|
|
sprintf(path, "%s/%d", app.saveDir, slot);
|
|
|
|
|
|
|
|
filenames = getFileList(path, &numFiles);
|
2018-03-21 19:25:29 +01:00
|
|
|
|
2018-03-19 23:51:37 +01:00
|
|
|
for (i = 0 ; i < numFiles ; i++)
|
|
|
|
{
|
2018-03-21 19:25:29 +01:00
|
|
|
sprintf(path, "%s/%d/%s", app.saveDir, slot, filenames[i]);
|
2018-03-19 23:51:37 +01:00
|
|
|
|
|
|
|
if (!deleteFile(path))
|
|
|
|
{
|
|
|
|
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "Failed to delete save file '%s'", path);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(filenames[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(filenames);
|
|
|
|
}
|
|
|
|
|
2018-02-27 09:14:54 +01:00
|
|
|
static int sortItems(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
Entity *e1 = *((Entity**)a);
|
|
|
|
Entity *e2 = *((Entity**)b);
|
|
|
|
|
|
|
|
if (!e1)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else if (!e2)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-21 10:31:38 +01:00
|
|
|
void destroyGame(void)
|
|
|
|
{
|
2018-03-19 23:51:37 +01:00
|
|
|
Tuple *t;
|
|
|
|
Trophy *trophy;
|
|
|
|
|
|
|
|
memset(game.keys, 0, sizeof(Tuple) * MAX_KEY_TYPES);
|
|
|
|
|
|
|
|
while (game.missionStatusHead.next)
|
|
|
|
{
|
|
|
|
t = game.missionStatusHead.next;
|
|
|
|
|
|
|
|
game.missionStatusHead.next = t->next;
|
|
|
|
|
|
|
|
free(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (game.trophyHead.next)
|
|
|
|
{
|
|
|
|
trophy = game.trophyHead.next;
|
|
|
|
|
|
|
|
game.trophyHead.next = trophy->next;
|
|
|
|
|
|
|
|
free(trophy);
|
|
|
|
}
|
2018-01-21 10:31:38 +01:00
|
|
|
}
|