diff --git a/common.mk b/common.mk index 900896b..34c4b31 100644 --- a/common.mk +++ b/common.mk @@ -10,7 +10,7 @@ DEPS += defs.h structs.h OBJS += ai.o OBJS += battle.o bullets.o -OBJS += capitalShips.o challengeHome.o challenges.o cJSON.o +OBJS += capitalShips.o challengeHome.o challenges.o cJSON.o controls.o OBJS += debris.o dev.o draw.o OBJS += effects.o entities.o extractionPoint.o OBJS += fighters.o @@ -24,7 +24,7 @@ OBJS += player.o OBJS += quadtree.o OBJS += radar.o resources.o rope.o OBJS += save.o script.o sound.o starfield.o starSystems.o stats.o -OBJS += testMission.o textures.o text.o title.o transition.o +OBJS += testMission.o textures.o text.o title.o transition.o trophies.o OBJS += util.o OBJS += waypoints.o widgets.o diff --git a/data/app/config.json b/data/app/config.json index ba00589..3080aea 100644 --- a/data/app/config.json +++ b/data/app/config.json @@ -1,8 +1,29 @@ { "winWidth" : 1280, "winHeight" : 720, - "vSync" : 1, "fullscreen" : 0, "musicVolume" : 8, - "soundVolume" : 10 + "soundVolume" : 10, + "controls" : { + "keys": { + "CONTROL_BOOST" : 26, + "CONTROL_ECM" : 7, + "CONTROL_BRAKE" : 22, + "CONTROL_TARGET" : 4, + "CONTROL_MISSILE" : 44, + "CONTROL_GUNS" : 29, + "CONTROL_RADAR" : 27, + "CONTROL_NEXT_FIGHTER": 7, + "CONTROL_PREV_FIGHTER": 4 + }, + "mouse" : { + "CONTROL_BOOST" : -1, + "CONTROL_ECM" : -1, + "CONTROL_BRAKE" : -1, + "CONTROL_TARGET" : -1, + "CONTROL_MISSILE" : -1, + "CONTROL_GUNS" : -1, + "CONTROL_RADAR" : -1 + } + } } diff --git a/data/trophies/trophies.json b/data/trophies/trophies.json new file mode 100644 index 0000000..12d3e7e --- /dev/null +++ b/data/trophies/trophies.json @@ -0,0 +1,68 @@ +[ + { + "id" : "READY_DUTY", + "title" : "Ready for duty", + "description" : "Complete all the training missions at Sol", + "value" : "TROPHY_BRONZE" + }, + { + "id" : "EPIC", + "title" : "Truly epic", + "description" : "Survive an epic battle", + "value" : "TROPHY_BRONZE" + }, + { + "id" : "BIGGER_FALL", + "title" : "The bigger they are", + "description" : "Destroy an enemy capital ship", + "value" : "TROPHY_SILVER" + }, + { + "id" : "CAMPAIGN_COYOTE", + "title" : "Coyote Ugly", + "description" : "Complete all missions at Coyote", + "value" : "TROPHY_BRONZE" + }, + { + "id" : "PANDORAN_FIRST", + "title" : "Plenty more where that came from", + "description" : "Engage the Pandorans in battle", + "value" : "TROPHY_BRONZE" + }, + { + "id" : "ATAF_FIRST", + "title" : "Man, I gotta get me one of these!", + "description" : "Pilot an ATAF during the campaign", + "value" : "TROPHY_BRONZE" + }, + { + "id" : "CHALLENGE_25", + "title" : "", + "description" : "Complete 25% of all challenges", + "value" : "TROPHY_BRONZE" + }, + { + "id" : "CHALLENGE_50", + "title" : "", + "description" : "Complete 50% of all challenges", + "value" : "TROPHY_SILVER" + }, + { + "id" : "CHALLENGE_100", + "title" : "", + "description" : "Complete 100% of all challenges", + "value" : "TROPHY_GOLD" + }, + { + "id" : "FIRE_10000", + "title" : "Your name's on one of these!", + "description" : "Fire 10,000 shots", + "value" : "TROPHY_SILVER" + }, + { + "id" : "MISSILE_1000", + "title" : "Dodge this!", + "description" : "Launch 1,000 missiles", + "value" : "TROPHY_SILVER" + } +] diff --git a/data/widgets/options.json b/data/widgets/options.json index 1031e3f..f94ef55 100644 --- a/data/widgets/options.json +++ b/data/widgets/options.json @@ -43,6 +43,16 @@ "w" : 400, "h": 34 }, + { + "name" : "controls", + "group" : "options", + "type" : "WT_BUTTON", + "text" : "Controls ...", + "x" : -1, + "y" : 475, + "w" : 400, + "h": 34 + }, { "name" : "ok", "group" : "options", diff --git a/makefile.pandora b/makefile.pandora index 245eec0..ab02437 100644 --- a/makefile.pandora +++ b/makefile.pandora @@ -9,7 +9,7 @@ OBJS += unixInit.o include common.mk -CXXFLAGS += `sdl2-config --cflags` -DVERSION=$(VERSION) -DREVISION=$(REVISION) -DDATA_DIR=\"$(DATA_DIR)\" -DLOCALE_DIR=\"$(LOCALE_DIR)\" +CXXFLAGS += `sdl2-config --cflags` -DVERSION=$(VERSION) -DREVISION=$(REVISION) -DDATA_DIR=\"$(DATA_DIR)\" -DLOCALE_DIR=\"$(LOCALE_DIR)\" -DFIXED_RESOLUTION=1 CXXFLAGS += -ansi -Wstrict-prototypes CXXFLAGS += -g -lefence -flto -flto-odr-type-merging diff --git a/src/battle/player.c b/src/battle/player.c index fd7ad49..4c9cf38 100644 --- a/src/battle/player.c +++ b/src/battle/player.c @@ -43,28 +43,28 @@ static Entity *availablePlayerUnits[MAX_SELECTABLE_PLAYERS]; void initPlayer(void) { int i, n; - + memset(&availableGuns, 0, sizeof(int) * BT_MAX); - + battle.numPlayerGuns = 0; - + player->selectedGunType = -1; - + if (!player->combinedGuns) { for (i = 0 ; i < MAX_FIGHTER_GUNS ; i++) { n = player->guns[i].type; - + if (n) { if (!availableGuns[n]) { battle.numPlayerGuns++; } - + availableGuns[n] = 1; - + if (player->selectedGunType == -1) { player->selectedGunType = n; @@ -76,16 +76,16 @@ void initPlayer(void) { player->selectedGunType = 0; } - + STRNCPY(player->name, "Player", MAX_NAME_LENGTH); - + player->action = NULL; - + battle.boostTimer = BOOST_RECHARGE_TIME; battle.ecmTimer = ECM_RECHARGE_TIME; - + game.stats[STAT_EPIC_KILL_STREAK] = MAX(game.stats[STAT_EPIC_KILL_STREAK], battle.stats[STAT_EPIC_KILL_STREAK]); - + battle.stats[STAT_EPIC_KILL_STREAK] = 0; } @@ -93,7 +93,7 @@ void doPlayer(void) { battle.boostTimer = MIN(battle.boostTimer + 1, BOOST_RECHARGE_TIME); battle.ecmTimer = MIN(battle.ecmTimer + 1, ECM_RECHARGE_TIME); - + if (player != NULL) { self = player; @@ -106,17 +106,17 @@ void doPlayer(void) if (player->alive == ALIVE_ALIVE) { handleKeyboard(); - + handleMouse(); - + if (!player->target || player->target->health <= 0 || player->target->systemPower <= 0) { selectTarget(); } } - + player->angle = ((int)player->angle) % 360; - + if (player->health <= 0 && battle.status == MS_IN_PROGRESS) { if (!battle.isEpic) @@ -128,18 +128,18 @@ void doPlayer(void) initPlayerSelect(); } } - + if (battle.status == MS_IN_PROGRESS) { selectMissionTarget(); } - + if (dev.playerUnlimitedMissiles) { player->missiles = 999; } } - + if (battle.boostTimer == (int)BOOST_FINISHED_TIME) { deactivateBoost(); @@ -173,69 +173,69 @@ static void handleKeyboard(void) { if (battle.status == MS_IN_PROGRESS) { - if (app.keyboard[SDL_SCANCODE_W]) + if (isKeyControl(CONTROL_BOOST)) { if (battle.boostTimer == BOOST_RECHARGE_TIME) { playSound(SND_BOOST); - + activateBoost(); } else { playSound(SND_GUI_DENIED); } - - app.keyboard[SDL_SCANCODE_W] = 0; + + clearControl(CONTROL_BOOST); } - - if (app.keyboard[SDL_SCANCODE_A]) + + if (isKeyControl(CONTROL_TARGET)) { selectTarget(); - - app.keyboard[SDL_SCANCODE_A] = 0; + + clearControl(CONTROL_TARGET); } - - if (app.keyboard[SDL_SCANCODE_D]) + + if (isKeyControl(CONTROL_ECM)) { if (battle.ecmTimer == ECM_RECHARGE_TIME) { playSound(SND_ECM); - + activateECM(); } else { playSound(SND_GUI_DENIED); } - - app.keyboard[SDL_SCANCODE_D] = 0; + + clearControl(CONTROL_ECM); } - - if (app.keyboard[SDL_SCANCODE_S]) + + if (isKeyControl(CONTROL_BRAKE)) { applyFighterBrakes(); } - - if (app.keyboard[SDL_SCANCODE_Z]) + + if (isKeyControl(CONTROL_GUNS)) { switchGuns(); - - app.keyboard[SDL_SCANCODE_Z] = 0; + + clearControl(CONTROL_GUNS); } - - if (app.keyboard[SDL_SCANCODE_X]) + + if (isKeyControl(CONTROL_RADAR)) { cycleRadarZoom(); - - app.keyboard[SDL_SCANCODE_X] = 0; + + clearControl(CONTROL_RADAR); } - - if (app.keyboard[SDL_SCANCODE_SPACE]) + + if (isKeyControl(CONTROL_MISSILE)) { preFireMissile(); - - app.keyboard[SDL_SCANCODE_SPACE] = 0; + + clearControl(CONTROL_MISSILE); } } else @@ -298,19 +298,19 @@ static void faceMouse(void) { int dir; int x, y, wantedAngle; - + x = player->x - battle.camera.x; y = player->y - battle.camera.y; wantedAngle = getAngle(x, y, app.mouse.x, app.mouse.y); - + wantedAngle %= 360; - + if (fabs(wantedAngle - player->angle) > 2) { dir = ((int)(wantedAngle - player->angle + 360)) % 360 > 180 ? -1 : 1; - + player->angle += dir * 4; - + player->angle = mod(player->angle, 360); } } @@ -333,11 +333,11 @@ static void preFireMissile(void) void initPlayerSelect(void) { Entity *e; - + memset(&availablePlayerUnits, 0, sizeof(Entity*) * MAX_SELECTABLE_PLAYERS); - + selectedPlayerIndex = 0; - + for (e = battle.entityHead.next ; e != NULL ; e = e->next) { if (e->active && e->type == ET_FIGHTER && e->health > 0 && e->side == SIDE_ALLIES && selectedPlayerIndex < MAX_SELECTABLE_PLAYERS) @@ -345,17 +345,17 @@ void initPlayerSelect(void) availablePlayerUnits[selectedPlayerIndex++] = e; } } - + if (selectedPlayerIndex > 0) { battle.playerSelect = 1; - selectedPlayerIndex = 0; + selectedPlayerIndex = 0; memset(&app.keyboard, 0, sizeof(int) * MAX_KEYBOARD_KEYS); } else { battle.isEpic = 0; - + failMission(); } } @@ -391,13 +391,13 @@ static void selectNewPlayer(int dir) do { selectedPlayerIndex += dir; - + selectedPlayerIndex = mod(selectedPlayerIndex, MAX_SELECTABLE_PLAYERS); - + player = availablePlayerUnits[selectedPlayerIndex]; } while (player == NULL); - + battle.camera.x = player->x - (SCREEN_WIDTH / 2); battle.camera.y = player->y - (SCREEN_HEIGHT / 2); } @@ -407,22 +407,22 @@ static void activateBoost(void) self->dx += sin(TO_RAIDANS(self->angle)) * 10; self->dy += -cos(TO_RAIDANS(self->angle)) * 10; self->thrust = sqrt((self->dx * self->dx) + (self->dy * self->dy)); - + battle.boostTimer = 0; - + battle.stats[STAT_BOOST]++; } static void deactivateBoost(void) { float v, thrust; - + thrust = -1; - + while (thrust != self->thrust) { thrust = self->thrust; - + v = (self->speed / sqrt(self->thrust)); self->dx = v * self->dx; self->dy = v * self->dy; @@ -433,18 +433,18 @@ static void deactivateBoost(void) static void activateECM(void) { battle.ecmTimer = 0; - + addECMEffect(player); - + battle.stats[STAT_ECM]++; } static void switchGuns(void) { int i; - + i = player->selectedGunType; - + if (!player->combinedGuns) { do @@ -453,7 +453,7 @@ static void switchGuns(void) } while (!availableGuns[i]); } - + if (player->selectedGunType != i) { playSound(SND_SELECT_WEAPON); @@ -468,7 +468,7 @@ static void selectTarget(void) int i, total; Entity *e, *near; Entity *targets[MAX_SELECTABLE_TARGETS]; - + i = 0; near = NULL; memset(targets, 0, sizeof(Entity*) * MAX_SELECTABLE_TARGETS); @@ -488,7 +488,7 @@ static void selectTarget(void) near = e; closest = dist; } - + if (collision(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, e->x - battle.camera.x - (e->w / 2), e->y - battle.camera.y - (e->h / 2), e->w, e->h)) { targets[i++] = e; @@ -499,9 +499,9 @@ static void selectTarget(void) } } } - + total = i; - + for (i = 0 ; i < total ; i++) { if (targets[i] == player->target) @@ -517,7 +517,7 @@ static void selectTarget(void) } } } - + if (!player->target || !targets[0]) { player->target = near; @@ -529,15 +529,15 @@ static void selectMissionTarget(void) unsigned int closest = MAX_TARGET_RANGE; unsigned int dist = MAX_TARGET_RANGE; Entity *e; - + battle.missionTarget = NULL; - + for (e = battle.entityHead.next ; e != NULL ; e = e->next) { if (e->active && e->flags & EF_MISSION_TARGET && e->alive == ALIVE_ALIVE) { dist = getDistance(self->x, self->y, e->x, e->y); - + if (battle.missionTarget == NULL) { battle.missionTarget = e; diff --git a/src/battle/player.h b/src/battle/player.h index 5036cda..251a142 100644 --- a/src/battle/player.h +++ b/src/battle/player.h @@ -37,6 +37,9 @@ extern float getAngle(int x1, int y1, int x2, int y2); extern int collision(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2); extern char *getTranslatedString(char *string); extern void addECMEffect(Entity *ent); +extern int isKeyControl(int type); +extern int isMouseControl(int type); +extern void clearControl(int type); extern App app; extern Battle battle; diff --git a/src/defs.h b/src/defs.h index d6de44d..8c5b117 100644 --- a/src/defs.h +++ b/src/defs.h @@ -26,6 +26,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define DATA_DIR "" #endif +#ifndef FIXED_RESOLUTION +#define FIXED_RESOLUTION 0 +#endif + #define _(string) getTranslatedString(string) #define PI 3.14159265358979323846 @@ -39,7 +43,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define SCREEN_HEIGHT 720 #define MAX_KEYBOARD_KEYS 350 -#define MAX_MOUSE_BUTTONS 8 +#define MAX_MOUSE_BUTTONS 5 #define FPS 60 #define LOGIC_RATE (1000 / FPS) @@ -113,6 +117,22 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define BOOST_FINISHED_TIME (FPS * 0.75) #define ECM_RECHARGE_TIME (FPS * 7) +enum +{ + CONTROL_FIRE, + CONTROL_ACCELERATE, + CONTROL_BOOST, + CONTROL_ECM, + CONTROL_BRAKE, + CONTROL_TARGET, + CONTROL_MISSILE, + CONTROL_GUNS, + CONTROL_RADAR, + CONTROL_NEXT_FIGHTER, + CONTROL_PREV_FIGHTER, + CONTROL_MAX +}; + enum { MD_NONE, diff --git a/src/galaxy/mission.h b/src/galaxy/mission.h index ce381a5..ca482bd 100644 --- a/src/galaxy/mission.h +++ b/src/galaxy/mission.h @@ -19,11 +19,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "../common.h" +#include "../json/cJSON.h" #include "time.h" -#include "../json/cJSON.h" - extern long lookup(char *name); extern char *readFile(char *filename); extern SDL_Texture *getTexture(char *filename); diff --git a/src/game/controls.c b/src/game/controls.c new file mode 100644 index 0000000..79cab7b --- /dev/null +++ b/src/game/controls.c @@ -0,0 +1,31 @@ +/* +Copyright (C) 2015-2016 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 "controls.h" + +int isKeyControl(int type) +{ + return app.keyboard[app.keyControls[type]]; +} + +void clearControl(int type) +{ + app.keyboard[app.keyControls[type]] = 0; +} diff --git a/src/game/controls.h b/src/game/controls.h new file mode 100644 index 0000000..5db7e31 --- /dev/null +++ b/src/game/controls.h @@ -0,0 +1,24 @@ +/* +Copyright (C) 2015-2016 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 "../common.h" + +extern App app; +extern Game game; diff --git a/src/game/trophies.c b/src/game/trophies.c new file mode 100644 index 0000000..8a89b7a --- /dev/null +++ b/src/game/trophies.c @@ -0,0 +1,115 @@ +/* +Copyright (C) 2015-2016 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 "trophies.h" + +static int recentlyAwarded; +static void loadTrophyData(char *filename); + +void initTrophies(void) +{ + recentlyAwarded = 0; + + loadTrophyData("data/trophies/trophies.json"); +} + +void awardTrophy(char *id) +{ + Trophy *t; + + for (t = game.trophyHead.next ; t != NULL ; t = t->next) + { + if (!t->awarded && strcmp(t->id, id) == 0) + { + t->awarded = 1; + t->awardDate = time(NULL); + t->notify = 1; + + recentlyAwarded++; + } + } +} + +void drawTrophyAlerts(void) +{ + Trophy *t; + + if (recentlyAwarded) + { + for (t = game.trophyHead.next ; t != NULL ; t = t->next) + { + if (t->notify) + { + /* handle notification */ + return; + } + } + } +} + +void drawTrophies(void) +{ +} + +Trophy *getTrophy(char *id) +{ + Trophy *t; + + for (t = game.trophyHead.next ; t != NULL ; t = t->next) + { + if (strcmp(t->id, id) == 0) + { + return t; + } + } + + return NULL; +} + +static void loadTrophyData(char *filename) +{ + cJSON *root, *node; + char *text; + Trophy *t, *tail; + + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Loading %s", filename); + + text = readFile(getFileLocation(filename)); + root = cJSON_Parse(text); + + tail = &game.trophyHead; + + for (node = root->child ; node != NULL ; node = node->next) + { + t = malloc(sizeof(Trophy)); + memset(t, 0, sizeof(Trophy)); + + STRNCPY(t->id, cJSON_GetObjectItem(node, "id")->valuestring, MAX_NAME_LENGTH); + STRNCPY(t->title, cJSON_GetObjectItem(node, "title")->valuestring, MAX_DESCRIPTION_LENGTH); + STRNCPY(t->description, cJSON_GetObjectItem(node, "description")->valuestring, MAX_DESCRIPTION_LENGTH); + t->value = lookup(cJSON_GetObjectItem(node, "value")->valuestring); + + tail->next = t; + tail = t; + } + + cJSON_Delete(root); + free(text); +} diff --git a/src/game/trophies.h b/src/game/trophies.h new file mode 100644 index 0000000..6492006 --- /dev/null +++ b/src/game/trophies.h @@ -0,0 +1,30 @@ +/* +Copyright (C) 2015-2016 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 "../common.h" +#include "../json/cJSON.h" + +#include "time.h" + +extern char *getFileLocation(char *filename); +extern long lookup(char *name); +extern char *readFile(char *filename); + +extern Game game; diff --git a/src/main.c b/src/main.c index 5a8e051..b72336b 100644 --- a/src/main.c +++ b/src/main.c @@ -38,6 +38,8 @@ int main(int argc, char *argv[]) srand(time(NULL)); init18N(argc, argv); + + initLookups(); initSDL(); diff --git a/src/main.h b/src/main.h index 7ad48b7..a63d1f3 100644 --- a/src/main.h +++ b/src/main.h @@ -44,6 +44,8 @@ extern void loadGame(void); extern int fileExists(char *filename); extern char *getSaveFilePath(char *filename); extern void init18N(int argc, char *argv[]); +extern void initLookups(void); +extern void initGame(void); App app; Colors colors; diff --git a/src/structs.h b/src/structs.h index cdc59f7..5f1987c 100644 --- a/src/structs.h +++ b/src/structs.h @@ -38,6 +38,7 @@ typedef struct GridCell GridCell; typedef struct ScriptRunner ScriptRunner; typedef struct Location Location; typedef struct Bucket Bucket; +typedef struct Trophy Trophy; typedef struct { int debug; @@ -261,6 +262,7 @@ struct Mission { char pilot[MAX_NAME_LENGTH]; char squadron[MAX_NAME_LENGTH]; char craft[MAX_NAME_LENGTH]; + char trophyId[MAX_NAME_LENGTH]; int available; int completed; int completedChallenges; @@ -336,6 +338,17 @@ struct ScriptRunner { ScriptRunner *next; }; +struct Trophy { + char id[MAX_NAME_LENGTH]; + char title[MAX_DESCRIPTION_LENGTH]; + char description[MAX_DESCRIPTION_LENGTH]; + int value; + int awarded; + unsigned long awardDate; + int notify; + Trophy *next; +}; + typedef struct { StarSystem starSystemHead; Mission challengeMissionHead; @@ -345,6 +358,7 @@ typedef struct { int availableMissions; int totalMissions; unsigned int stats[STAT_MAX]; + Trophy trophyHead; } Game; struct Widget { @@ -408,6 +422,8 @@ typedef struct { SDL_Window *window; Delegate delegate; ModalDialog modalDialog; + int keyControls[CONTROL_MAX]; + int mouseControls[CONTROL_MAX]; } App; typedef struct { diff --git a/src/system/init.c b/src/system/init.c index a9e4359..364a77a 100644 --- a/src/system/init.c +++ b/src/system/init.c @@ -29,24 +29,24 @@ void init18N(int argc, char *argv[]) { int i; int languageId = -1; - + setlocale(LC_NUMERIC, ""); - + for (i = 1 ; i < argc ; i++) { if (strcmp(argv[i], "-language") == 0) { languageId = i + 1; - + if (languageId >= argc) { SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "You must specify a language to use with -language. Using default.\n"); } } } - + setLanguage("tbftss", languageId == -1 ? NULL : argv[languageId]); - + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Numeric is %s\n", setlocale(LC_NUMERIC, "C")); SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "atof(2.75) is %f\n", atof("2.75")); } @@ -54,39 +54,39 @@ void init18N(int argc, char *argv[]) void initSDL(void) { int rendererFlags, windowFlags; - + /* do this here, so we don't destroy the save dir stored in app */ memset(&app, 0, sizeof(App)); - + /* done in src/plat/ */ createSaveFolder(); - + rendererFlags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC; windowFlags = 0; - + loadConfig(); - + if (app.fullscreen) { windowFlags |= SDL_WINDOW_FULLSCREEN; } - + if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) < 0) { printf("Couldn't initialize SDL: %s\n", SDL_GetError()); exit(1); } - + SDL_ShowCursor(0); - + if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024) == -1) { printf("Couldn't initialize SDL Mixer\n"); exit(1); } - + Mix_AllocateChannels(64); - + Mix_Volume(-1, app.soundVolume * MIX_MAX_VOLUME / 10); Mix_VolumeMusic(app.musicVolume * MIX_MAX_VOLUME / 10); @@ -103,14 +103,14 @@ void initSDL(void) printf("Couldn't initialize SDL TTF: %s\n", SDL_GetError()); exit(1); } - + app.backBuffer = SDL_CreateTexture(app.renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, SCREEN_WIDTH, SCREEN_HEIGHT); - + app.scaleX = SCREEN_WIDTH; app.scaleX /= app.winWidth; app.scaleY = SCREEN_HEIGHT; app.scaleY /= app.winHeight; - + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Game scale factor: %.2f,%.2f\n", app.scaleX, app.scaleY); } @@ -120,7 +120,6 @@ void initGameSystem(void) void (*initFuncs[]) (void) = { initFonts, initInput, - initLookups, initResources, initSounds, initWidgets, @@ -136,9 +135,9 @@ void initGameSystem(void) initModalDialog, initBackground }; - + numInitFuns = sizeof(initFuncs) / sizeof(void*); - + initColor(&colors.red, 255, 0, 0); initColor(&colors.orange, 255, 128, 0); initColor(&colors.yellow, 255, 255, 0); @@ -150,11 +149,11 @@ void initGameSystem(void) initColor(&colors.black, 0, 0, 0); initColor(&colors.lightGrey, 192, 192, 192); initColor(&colors.darkGrey, 128, 128, 128); - + for (i = 0 ; i < numInitFuns ; i++) { showLoadingStep(i, numInitFuns); - + initFuncs[i](); } } @@ -165,28 +164,28 @@ void initGameSystem(void) static void showLoadingStep(float step, float maxSteps) { SDL_Rect r; - + prepareScene(); - + r.w = SCREEN_WIDTH - 400; r.h = 14; r.x = (SCREEN_WIDTH / 2) - r.w / 2; r.y = (SCREEN_HEIGHT / 2) - r.h / 2; - + SDL_SetRenderDrawColor(app.renderer, 128, 128, 128, 255); SDL_RenderDrawRect(app.renderer, &r); - + r.w *= (step / maxSteps); r.x += 2; r.y += 2; r.w -= 4; r.h -= 4; - + SDL_SetRenderDrawColor(app.renderer, 128, 196, 255, 255); SDL_RenderFillRect(app.renderer, &r); - + presentScene(); - + SDL_Delay(1); } @@ -202,11 +201,12 @@ static void initColor(SDL_Color *c, int r, int g, int b) static void loadConfig(void) { - cJSON *root; + int i; + cJSON *root, *controlsJSON, *node; char *text, *configFilename; - + configFilename = getSaveFilePath("config.json"); - + if (fileExists(configFilename)) { text = readFile(configFilename); @@ -215,29 +215,54 @@ static void loadConfig(void) { text = readFile(getFileLocation("data/app/config.json")); } - + root = cJSON_Parse(text); - + app.winWidth = cJSON_GetObjectItem(root, "winWidth")->valueint; app.winHeight = cJSON_GetObjectItem(root, "winHeight")->valueint; app.fullscreen = cJSON_GetObjectItem(root, "fullscreen")->valueint; app.musicVolume = cJSON_GetObjectItem(root, "musicVolume")->valueint; app.soundVolume = cJSON_GetObjectItem(root, "soundVolume")->valueint; - + + controlsJSON = cJSON_GetObjectItem(root, "controls"); + if (controlsJSON) + { + node = cJSON_GetObjectItem(controlsJSON, "keys")->child; + while (node) + { + i = lookup(node->string); + + app.keyControls[i] = node->valueint; + + node = node->next; + } + + node = cJSON_GetObjectItem(controlsJSON, "mouse")->child; + while (node) + { + i = lookup(node->string); + + app.mouseControls[i] = node->valueint; + + node = node->next; + } + } + cJSON_Delete(root); free(text); - + /* so that the player doesn't get confused if this is a new game */ saveConfig(); } void saveConfig(void) { + int i; char *out, *configFilename; - cJSON *root; - + cJSON *root, *controlsJSON, *keysJSON, *mouseJSON; + configFilename = getSaveFilePath("config.json"); - + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Saving config ..."); root = cJSON_CreateObject(); @@ -246,12 +271,30 @@ void saveConfig(void) cJSON_AddNumberToObject(root, "fullscreen", app.fullscreen); cJSON_AddNumberToObject(root, "musicVolume", app.musicVolume); cJSON_AddNumberToObject(root, "soundVolume", app.soundVolume); + + keysJSON = cJSON_CreateObject(); + for (i = 0 ; i < CONTROL_MAX ; i++) + { + cJSON_AddNumberToObject(keysJSON, getLookupName("CONTROL_", i), app.keyControls[i]); + } + + mouseJSON = cJSON_CreateObject(); + for (i = 0 ; i < CONTROL_MAX ; i++) + { + cJSON_AddNumberToObject(mouseJSON, getLookupName("CONTROL_", i), app.mouseControls[i]); + } + + controlsJSON = cJSON_CreateObject(); + cJSON_AddItemToObject(controlsJSON, "keys", keysJSON); + cJSON_AddItemToObject(controlsJSON, "mouse", mouseJSON); + + cJSON_AddItemToObject(root, "controls", controlsJSON); out = cJSON_Print(root); if (!writeFile(configFilename, out)) { - printf("Failed to save config\n"); + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "Failed to save config"); } cJSON_Delete(root); @@ -261,36 +304,36 @@ void saveConfig(void) void cleanup(void) { SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Cleaning up ..."); - + SDL_DestroyRenderer(app.renderer); SDL_DestroyWindow(app.window); - + destroyLookups(); - + destroyTextures(); - + expireTexts(1); - + destroyFonts(); - + destroySounds(); - + destroyGame(); - + destroyFighterDefs(); - + destroyCapitalShipDefs(); - + destroyBulletDefs(); - + destroyItemDefs(); - + destroyStarSystems(); - + destroyBattle(); - + destroyGalacticMap(); - + destroyWidgets(); destroyResources(); @@ -298,6 +341,6 @@ void cleanup(void) TTF_Quit(); SDL_Quit(); - + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Done"); } diff --git a/src/system/init.h b/src/system/init.h index 4ed57d6..56fb8d3 100644 --- a/src/system/init.h +++ b/src/system/init.h @@ -40,7 +40,6 @@ extern void loadCapitalShipDefs(void); extern void loadItemDefs(void); extern void initFonts(void); extern void initBulletDefs(void); -extern void initLookups(void); extern void initBattle(void); extern void initGame(void); extern void initStats(void); @@ -69,6 +68,9 @@ extern void initModalDialog(void); extern void createSaveFolder(void); extern char *getFileLocation(char *filename); extern void setLanguage(char *applicationName, char *languageCode); +extern char *getLookupName(char *prefix, long num); +extern long lookup(char *name); extern App app; extern Colors colors; +extern Game game; diff --git a/src/system/input.c b/src/system/input.c index 3df05b1..59fa8a4 100644 --- a/src/system/input.c +++ b/src/system/input.c @@ -25,29 +25,38 @@ static SDL_Texture *mousePointer; void initInput(void) { memset(&app.mouse, 0, sizeof(Mouse)); - + mousePointer = getTexture("gfx/input/mousePointer.png"); - + SDL_QueryTexture(mousePointer, NULL, NULL, &app.mouse.w, &app.mouse.h); } void doMouseDown(SDL_MouseButtonEvent *event) { - app.mouse.button[event->button] = 1; + if (event->button >= 0 && event->button < MAX_MOUSE_BUTTONS) + { + app.mouse.button[event->button] = 1; + } } void doMouseUp(SDL_MouseButtonEvent *event) { - app.mouse.button[event->button] = 0; + if (event->button >= 0 && event->button < MAX_MOUSE_BUTTONS) + { + app.mouse.button[event->button] = 0; + } } +/* + * Note: the following assumes that SDL_BUTTON_X1 and SDL_BUTTON_X2 are 4 and 5, respectively. They usually are. + */ void doMouseWheel(SDL_MouseWheelEvent *event) { if (event->y == -1) { app.mouse.button[SDL_BUTTON_X1] = 1; } - + if (event->y == 1) { app.mouse.button[SDL_BUTTON_X2] = 1; @@ -63,12 +72,12 @@ void doMouseMotion(SDL_MouseMotionEvent *event) void drawMouse(void) { int x, y; - + SDL_GetMouseState(&x, &y); - + app.mouse.x = x * app.scaleX; app.mouse.y = y * app.scaleY; - + blit(mousePointer, app.mouse.x, app.mouse.y, 1); } @@ -76,17 +85,17 @@ void clearInput(void) { SDL_Event event; int i; - + for (i = 0 ; i < MAX_KEYBOARD_KEYS ; i++) { app.keyboard[i] = 0; } - + for (i = 0 ; i < MAX_MOUSE_BUTTONS ; i++) { app.mouse.button[i] = 0; } - + while (SDL_PollEvent(&event)) { } diff --git a/src/system/load.c b/src/system/load.c index f822be9..0ca8612 100644 --- a/src/system/load.c +++ b/src/system/load.c @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "load.h" -static void loadStats(cJSON *stats); +static void loadStats(cJSON *statsJSON); static void loadStarSystems(cJSON *starSystemsJSON); static void loadMissions(cJSON *missionsCJSON); static void loadChallenges(cJSON *challengesCJSON); @@ -29,20 +29,20 @@ void loadGame(void) { cJSON *root, *gameJSON; char *text; - + text = readFile(getSaveFilePath("game.save")); root = cJSON_Parse(text); - + gameJSON = cJSON_GetObjectItem(root, "game"); - + STRNCPY(game.selectedStarSystem, cJSON_GetObjectItem(gameJSON, "selectedStarSystem")->valuestring, MAX_NAME_LENGTH); - + loadStarSystems(cJSON_GetObjectItem(gameJSON, "starSystems")); - + loadChallenges(cJSON_GetObjectItem(gameJSON, "challenges")); - + loadStats(cJSON_GetObjectItem(gameJSON, "stats")); - + cJSON_Delete(root); free(text); } @@ -51,13 +51,13 @@ static void loadStarSystems(cJSON *starSystemsJSON) { StarSystem *starSystem; cJSON *starSystemJSON; - + for (starSystemJSON = starSystemsJSON->child ; starSystemJSON != NULL ; starSystemJSON = starSystemJSON->next) { starSystem = getStarSystem(cJSON_GetObjectItem(starSystemJSON, "name")->valuestring); - + starSystem->side = lookup(cJSON_GetObjectItem(starSystemJSON, "side")->valuestring); - + loadMissions(cJSON_GetObjectItem(starSystemJSON, "missions")); } } @@ -66,7 +66,7 @@ static void loadMissions(cJSON *missionsJSON) { Mission *mission; cJSON *missionJSON; - + for (missionJSON = missionsJSON->child ; missionJSON != NULL ; missionJSON = missionJSON->next) { mission = getMission(cJSON_GetObjectItem(missionJSON, "filename")->valuestring); @@ -80,20 +80,20 @@ static void loadChallenges(cJSON *missionsJSON) Challenge *challenge; cJSON *missionJSON, *challengeJSON; int type, value; - + if (missionsJSON) { for (missionJSON = missionsJSON->child ; missionJSON != NULL ; missionJSON = missionJSON->next) { mission = getMission(cJSON_GetObjectItem(missionJSON, "filename")->valuestring); - + for (challengeJSON = cJSON_GetObjectItem(missionJSON, "challenges")->child ; challengeJSON != NULL ; challengeJSON = challengeJSON->next) { type = lookup(cJSON_GetObjectItem(challengeJSON, "type")->valuestring); value = cJSON_GetObjectItem(challengeJSON, "value")->valueint; - + challenge = getChallenge(mission, type, value); - + if (challenge) { challenge->passed = cJSON_GetObjectItem(challengeJSON, "passed")->valueint; @@ -103,18 +103,18 @@ static void loadChallenges(cJSON *missionsJSON) } } -static void loadStats(cJSON *stats) +static void loadStats(cJSON *statsJSON) { int i; char *statName; - + for (i = 0 ; i < STAT_MAX ; i++) { statName = getLookupName("STAT_", i); - - if (statName && cJSON_GetObjectItem(stats, statName)) + + if (statName && cJSON_GetObjectItem(statsJSON, statName)) { - game.stats[i] = cJSON_GetObjectItem(stats, statName)->valueint; + game.stats[i] = cJSON_GetObjectItem(statsJSON, statName)->valueint; } } } diff --git a/src/system/lookup.c b/src/system/lookup.c index ffc09a8..00d7c62 100644 --- a/src/system/lookup.c +++ b/src/system/lookup.c @@ -29,11 +29,23 @@ void initLookups(void) { memset(&head, 0, sizeof(Lookup)); tail = &head; - + + addLookup("CONTROL_FIRE", CONTROL_FIRE); + addLookup("CONTROL_ACCELERATE", CONTROL_ACCELERATE); + addLookup("CONTROL_BOOST", CONTROL_BOOST); + addLookup("CONTROL_ECM", CONTROL_ECM); + addLookup("CONTROL_BRAKE", CONTROL_BRAKE); + addLookup("CONTROL_TARGET", CONTROL_TARGET); + addLookup("CONTROL_MISSILE", CONTROL_MISSILE); + addLookup("CONTROL_GUNS", CONTROL_GUNS); + addLookup("CONTROL_RADAR", CONTROL_RADAR); + addLookup("CONTROL_NEXT_FIGHTER", CONTROL_NEXT_FIGHTER); + addLookup("CONTROL_PREV_FIGHTER", CONTROL_PREV_FIGHTER); + addLookup("ET_WAYPOINT", ET_WAYPOINT); addLookup("ET_EXTRACTION_POINT", ET_EXTRACTION_POINT); addLookup("ET_CAPITAL_SHIP", ET_CAPITAL_SHIP); - + addLookup("EF_NO_KILL", EF_NO_KILL); addLookup("EF_DISABLED", EF_DISABLED); addLookup("EF_MUST_DISABLE", EF_MUST_DISABLE); @@ -48,7 +60,7 @@ void initLookups(void) addLookup("EF_SECONDARY_TARGET", EF_SECONDARY_TARGET); addLookup("EF_AI_TARGET", EF_AI_TARGET); addLookup("EF_AI_LEADER", EF_AI_LEADER); - + addLookup("AIF_NONE", AIF_NONE); addLookup("AIF_MOVES_TO_PLAYER", AIF_MOVES_TO_PLAYER); addLookup("AIF_FOLLOWS_PLAYER", AIF_FOLLOWS_PLAYER); @@ -64,48 +76,48 @@ void initLookups(void) addLookup("AIF_LONG_RANGE_FIRE", AIF_LONG_RANGE_FIRE); addLookup("AIF_MOVES_TO_LEADER", AIF_MOVES_TO_LEADER); addLookup("AIF_WANDERS", AIF_WANDERS); - + addLookup("DT_ANY", DT_ANY); addLookup("DT_NO_SPIN", DT_NO_SPIN); addLookup("DT_INSTANT", DT_INSTANT); - + addLookup("TT_DESTROY", TT_DESTROY); addLookup("TT_DISABLE", TT_DISABLE); addLookup("TT_WAYPOINT", TT_WAYPOINT); addLookup("TT_ESCAPED", TT_ESCAPED); addLookup("TT_PLAYER_ESCAPED", TT_PLAYER_ESCAPED); addLookup("TT_ITEM", TT_ITEM); - + addLookup("WT_BUTTON", WT_BUTTON); addLookup("WT_SELECT", WT_SELECT); addLookup("WT_IMG_BUTTON", WT_IMG_BUTTON); - + addLookup("SIDE_ALLIES", SIDE_ALLIES); addLookup("SIDE_PIRATE", SIDE_PIRATE); addLookup("SIDE_REBEL", SIDE_REBEL); addLookup("SIDE_PANDORAN", SIDE_PANDORAN); addLookup("SIDE_CSN", SIDE_CSN); addLookup("SIDE_UNF", SIDE_UNF); - + addLookup("SND_PARTICLE", SND_PARTICLE); addLookup("SND_PLASMA", SND_PLASMA); addLookup("SND_LASER", SND_LASER); addLookup("SND_MAG", SND_MAG); addLookup("SND_MISSILE", SND_MISSILE); - + addLookup("BT_PARTICLE", BT_PARTICLE); addLookup("BT_PLASMA", BT_PLASMA); addLookup("BT_LASER", BT_LASER); addLookup("BT_MAG", BT_MAG); addLookup("BT_ROCKET", BT_ROCKET); addLookup("BT_MISSILE", BT_MISSILE); - + addLookup("BF_NONE", BF_NONE); addLookup("BF_ENGINE", BF_ENGINE); addLookup("BF_SYSTEM_DAMAGE", BF_SYSTEM_DAMAGE); addLookup("BF_SHIELD_DAMAGE", BF_SHIELD_DAMAGE); addLookup("BF_EXPLODES", BF_EXPLODES); - + addLookup("CHALLENGE_ARMOUR", CHALLENGE_ARMOUR); addLookup("CHALLENGE_TIME", CHALLENGE_TIME); addLookup("CHALLENGE_SHOT_ACCURACY", CHALLENGE_SHOT_ACCURACY); @@ -117,7 +129,7 @@ void initLookups(void) addLookup("CHALLENGE_PLAYER_KILLS", CHALLENGE_PLAYER_KILLS); addLookup("CHALLENGE_DISABLE", CHALLENGE_DISABLE); addLookup("CHALLENGE_TIME_MINS", CHALLENGE_TIME_MINS); - + addLookup("STAT_PERCENT_COMPLETE", STAT_PERCENT_COMPLETE); addLookup("STAT_MISSIONS_STARTED", STAT_MISSIONS_STARTED); addLookup("STAT_MISSIONS_COMPLETED", STAT_MISSIONS_COMPLETED); @@ -158,10 +170,10 @@ static void addLookup(char *name, long value) { Lookup *lookup = malloc(sizeof(Lookup)); memset(lookup, 0, sizeof(Lookup)); - + STRNCPY(lookup->name, name, MAX_NAME_LENGTH); lookup->value = value; - + tail->next = lookup; tail = lookup; } @@ -169,7 +181,7 @@ static void addLookup(char *name, long value) long lookup(char *name) { Lookup *l; - + for (l = head.next ; l != NULL ; l = l->next) { if (strcmp(l->name, name) == 0) @@ -177,18 +189,18 @@ long lookup(char *name) return l->value; } } - + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "No such lookup value '%s'", name); - + exit(1); - + return 0; } char *getLookupName(char *prefix, long num) { Lookup *l; - + for (l = head.next ; l != NULL ; l = l->next) { if (l->value == num && strncmp(prefix, l->name, strlen(prefix)) == 0) @@ -196,11 +208,11 @@ char *getLookupName(char *prefix, long num) return l->name; } } - + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "No such lookup value %ld, prefix=%s", num, prefix); - + exit(1); - + return ""; } @@ -209,11 +221,11 @@ char *getFlagValues(char *prefix, long flags) static char flagStr[MAX_DESCRIPTION_LENGTH]; int requirePlus; Lookup *l; - + memset(flagStr, '\0', MAX_DESCRIPTION_LENGTH); - + requirePlus = 0; - + for (l = head.next ; l != NULL ; l = l->next) { if (flags & l->value && strncmp(prefix, l->name, strlen(prefix)) == 0) @@ -222,13 +234,13 @@ char *getFlagValues(char *prefix, long flags) { strcat(flagStr, "+"); } - + strcat(flagStr, l->name); - + requirePlus = 1; } } - + return flagStr; } @@ -236,28 +248,28 @@ long flagsToLong(char *flags, int *add) { char *flag; long total; - + total = 0; - + if (add) { *add = (flags[0] == '+'); } - + flag = strtok(flags, "+"); while (flag) { total += lookup(flag); flag = strtok(NULL, "+"); } - + return total; } void destroyLookups(void) { Lookup *l; - + while (head.next) { l = head.next; diff --git a/src/system/options.c b/src/system/options.c index edbbc40..481e70c 100644 --- a/src/system/options.c +++ b/src/system/options.c @@ -31,57 +31,67 @@ static void (*returnFromOptions)(void); void initOptions(void (*rtn)(void)) { char optionStr[MAX_NAME_LENGTH]; - + selectWidget("windowSize", "options"); - + getWidget("windowSize", "options")->onChange = changeWindowSize; getWidget("soundVolume", "options")->onChange = changeSoundVolume; getWidget("musicVolume", "options")->onChange = changeMusicVolume; getWidget("fullscreen", "options")->onChange = changeFullscreen; getWidget("ok", "options")->action = ok; - + sprintf(optionStr, "%d x %d", app.winWidth, app.winHeight); setWidgetOption("windowSize", "options", optionStr); - + sprintf(optionStr, "%d", app.soundVolume); setWidgetOption("soundVolume", "options", optionStr); - + sprintf(optionStr, "%d", app.musicVolume); setWidgetOption("musicVolume", "options", optionStr); - + setWidgetOption("fullscreen", "options", app.fullscreen ? "On" : "Off"); - + + #if FIXED_RESOLUTION + getWidget("windowSize", "options")->enabled = 0; + getWidget("fullscreen", "options")->enabled = 0; + #endif + returnFromOptions = rtn; } void drawOptions(void) { SDL_Rect r; - + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND); SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 128); SDL_RenderFillRect(app.renderer, NULL); SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE); - + r.w = 500; r.h = 600; r.x = (SCREEN_WIDTH / 2) - r.w / 2; r.y = (SCREEN_HEIGHT / 2) - r.h / 2; - + SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 0); SDL_RenderFillRect(app.renderer, &r); SDL_SetRenderDrawColor(app.renderer, 200, 200, 200, 255); SDL_RenderDrawRect(app.renderer, &r); - + drawText(SCREEN_WIDTH / 2, 70, 28, TA_CENTER, colors.white, _("Options")); - + SDL_SetRenderDrawColor(app.renderer, 128, 128, 128, 255); SDL_RenderDrawLine(app.renderer, r.x, 120, r.x + r.w, 120); - - drawWidgets("options"); - + + drawWidgets("options"); + limitTextWidth(r.w - 100); + #if !FIXED_RESOLUTION drawText(SCREEN_WIDTH / 2, r.y + r.h - 135, 16, TA_CENTER, colors.yellow, _("Note: you must restart the game for window size and fullscreen options to take effect.")); + #else + drawText(SCREEN_WIDTH / 2, r.y + r.h - 135, 16, TA_CENTER, colors.yellow, _("Note: this device does not support changing the screen resolution.")); + #endif + limitTextWidth(0); } @@ -93,14 +103,14 @@ static void changeWindowSize(char *value) static void changeSoundVolume(char *value) { app.soundVolume = atoi(value); - + Mix_Volume(-1, app.soundVolume * MIX_MAX_VOLUME / 10); } static void changeMusicVolume(char *value) { app.musicVolume = atoi(value); - + Mix_VolumeMusic(app.musicVolume * MIX_MAX_VOLUME / 10); } @@ -112,6 +122,6 @@ static void changeFullscreen(char *value) static void ok(void) { saveConfig(); - + returnFromOptions(); } diff --git a/src/system/save.c b/src/system/save.c index 626a6ca..e0bdb2b 100644 --- a/src/system/save.c +++ b/src/system/save.c @@ -29,17 +29,17 @@ void saveGame(void) { char *out; cJSON *root, *gameJSON; - + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Saving Game ..."); root = cJSON_CreateObject(); gameJSON = cJSON_CreateObject(); cJSON_AddItemToObject(root, "game", gameJSON); - + cJSON_AddStringToObject(gameJSON, "selectedStarSystem", game.selectedStarSystem); - + saveStarSystems(gameJSON); - + saveChallenges(gameJSON); saveStats(gameJSON); @@ -56,20 +56,20 @@ static void saveStarSystems(cJSON *gameJSON) { cJSON *starSystemJSON, *starSystemsJSON; StarSystem *starSystem; - + starSystemsJSON = cJSON_CreateArray(); for (starSystem = game.starSystemHead.next ; starSystem != NULL ; starSystem = starSystem->next) { starSystemJSON = cJSON_CreateObject(); - + cJSON_AddStringToObject(starSystemJSON, "name", starSystem->name); cJSON_AddStringToObject(starSystemJSON, "side", getLookupName("SIDE_", starSystem->side)); cJSON_AddItemToObject(starSystemJSON, "missions", getMissionsJSON(starSystem)); - + cJSON_AddItemToArray(starSystemsJSON, starSystemJSON); } - + cJSON_AddItemToObject(gameJSON, "starSystems", starSystemsJSON); } @@ -77,19 +77,19 @@ static cJSON *getMissionsJSON(StarSystem *starSystem) { cJSON *missionJSON, *missionsJSON; Mission *mission; - + missionsJSON = cJSON_CreateArray(); - + for (mission = starSystem->missionHead.next ; mission != NULL ; mission = mission->next) { missionJSON = cJSON_CreateObject(); - + cJSON_AddStringToObject(missionJSON, "filename", mission->filename); cJSON_AddNumberToObject(missionJSON, "completed", mission->completed); - + cJSON_AddItemToArray(missionsJSON, missionJSON); } - + return missionsJSON; } @@ -99,15 +99,15 @@ static void saveChallenges(cJSON *gameJSON) Mission *mission; Challenge *c; cJSON *missionsJSON, *missionJSON, *challengesJSON, *challengeJSON; - + missionsJSON = cJSON_CreateArray(); - + for (mission = game.challengeMissionHead.next ; mission != NULL ; mission = mission->next) { missionJSON = cJSON_CreateObject(); - + cJSON_AddStringToObject(missionJSON, "filename", mission->filename); - + challengesJSON = cJSON_CreateArray(); for (i = 0 ; i < MAX_CHALLENGES ; i++) @@ -124,21 +124,21 @@ static void saveChallenges(cJSON *gameJSON) cJSON_AddItemToArray(challengesJSON, challengeJSON); } } - + cJSON_AddItemToObject(missionJSON, "challenges", challengesJSON); - + cJSON_AddItemToArray(missionsJSON, missionJSON); } - + cJSON_AddItemToObject(gameJSON, "challenges", missionsJSON); } static void saveStats(cJSON *gameJSON) { int i; - + cJSON *stats = cJSON_CreateObject(); - + for (i = 0 ; i < STAT_MAX ; i++) { cJSON_AddNumberToObject(stats, getLookupName("STAT_", i), game.stats[i]);