diff --git a/README.md b/README.md index 1e5cb67..ed22f16 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ gfx/trophies/gold.png - dervied from Trophy icon, by Lorc (CC BY 3.0): http://ga gfx/trophies/platinum.png - dervied from Trophy icon, by Lorc (CC BY 3.0): http://game-icons.net/lorc/originals/trophy.html gfx/trophies/silver.png - dervied from Trophy icon, by Lorc (CC BY 3.0): http://game-icons.net/lorc/originals/trophy.html gfx/trophies/unearned.png - dervied from Trophy icon, by Lorc (CC BY 3.0): http://game-icons.net/lorc/originals/trophy.html +gfx/input/mousePointerMove.png, by LoneCoder / para (CC0) All other graphics are CC BY-NC-SA 3.0, with the following attribution: Copyright 2015-2016, Stephen J Sweeney | www.battleforthesolarsystem.com diff --git a/data/battle/bullets.json b/data/battle/bullets.json index ceb516e..fcabc70 100644 --- a/data/battle/bullets.json +++ b/data/battle/bullets.json @@ -17,7 +17,7 @@ { "type" : "BT_MISSILE", - "damage" : 75, + "damage" : 50, "texture" : "gfx/bullets/missile.png", "sound" : "SND_MISSILE", "flags" : "BF_ENGINE+BF_EXPLODES" diff --git a/data/credits/credits.json b/data/credits/credits.json index 0d5d5a7..0364492 100644 --- a/data/credits/credits.json +++ b/data/credits/credits.json @@ -4,6 +4,7 @@ "75 30 ADDITIONAL CODE", "0 24 Richard Sweeney", + "0 24 cxong", "75 30 GRAPHICS", @@ -306,7 +307,7 @@ "0 24 twitter.com/tgfcoder", "75 30 SPECIAL THANKS", - "0 24 akien-mga, bentley, Bertram25, ChliHug, Imerion, nnesse, ptitSeb, Szunti", + "0 24 akien-mga, bentley, Bertram25, ChliHug, Imerion, nnesse, ptitSeb, Szunti, cxong", "150 24 This is a work of fiction. Names, characters, businesses, places, events and incidents are either the products of the author's imagination or used in a fictitious manner. Any resemblance to actual persons, living or dead, or actual events is purely coincidental. The Battle for the Solar System : The Pandoran War is (C) 2015-2016, Stephen J Sweeney, Some Rights Reserved. The Battle for the Solar System and all related materials (including, but not limited to, characters, setting, and story elements) are (C) 2009-2016, Stephen J Sweeney, All Rights Reserved.", diff --git a/data/trophies/trophies.json b/data/trophies/trophies.json index c9c56b7..f970465 100644 --- a/data/trophies/trophies.json +++ b/data/trophies/trophies.json @@ -5,6 +5,13 @@ "description" : "Earn all other trophies", "value" : "TROPHY_PLATINUM" }, + { + "id" : "NORMAL_MODE", + "title" : "Fought back against the odds", + "description" : "Finished the campaign on normal", + "value" : "TROPHY_GOLD", + "hidden" : 1 + }, { "id" : "CAMPAIGN_COMPLETE", "title" : "Plan B", diff --git a/data/widgets/campaign.json b/data/widgets/campaign.json new file mode 100644 index 0000000..b933c2b --- /dev/null +++ b/data/widgets/campaign.json @@ -0,0 +1,64 @@ +[ + { + "name" : "new", + "group" : "campaign", + "type" : "WT_BUTTON", + "text" : "New Campaign", + "x" : -1, + "y" : 300, + "w" : 200, + "h": 34 + }, + { + "name" : "continue", + "group" : "campaign", + "type" : "WT_BUTTON", + "text" : "Continue Campaign", + "x" : -1, + "y" : 400, + "w" : 200, + "h": 34 + }, + { + "name" : "back", + "group" : "campaign", + "type" : "WT_BUTTON", + "text" : "Back", + "x" : -1, + "y" : 680, + "w" : 200, + "h": 34 + }, + + + { + "name" : "easy", + "group" : "campaignDifficulty", + "type" : "WT_BUTTON", + "text" : "Easy Mode", + "x" : -1, + "y" : 200, + "w" : 200, + "h": 34 + }, + { + "name" : "normal", + "group" : "campaignDifficulty", + "type" : "WT_BUTTON", + "text" : "Normal", + "x" : -1, + "y" : 400, + "w" : 200, + "h": 34 + }, + { + "name" : "back", + "group" : "campaignDifficulty", + "type" : "WT_BUTTON", + "text" : "Back", + "x" : -1, + "y" : 680, + "w" : 200, + "h": 34 + } +] diff --git a/src/battle/ai.c b/src/battle/ai.c index 218f20b..8641947 100644 --- a/src/battle/ai.c +++ b/src/battle/ai.c @@ -1111,9 +1111,19 @@ void checkSuspicionLevel(void) { battle.suspicionLevel++; battle.suspicionCoolOff = FPS * 30; + + if (game.difficulty == DIFFICULTY_EASY) + { + battle.suspicionCoolOff = FPS * 60; + } } else if (distance <= SCREEN_HEIGHT / 2) { + if (game.difficulty == DIFFICULTY_EASY) + { + battle.suspicionLevel--; + } + battle.suspicionLevel = MAX(battle.suspicionLevel - 1, 0); } } diff --git a/src/battle/ai.h b/src/battle/ai.h index 465eb74..c73fcda 100644 --- a/src/battle/ai.h +++ b/src/battle/ai.h @@ -46,3 +46,4 @@ extern Colors colors; extern Dev dev; extern Entity *self; extern Entity *player; +extern Game game; diff --git a/src/battle/battle.c b/src/battle/battle.c index 701a580..2ad0ee1 100644 --- a/src/battle/battle.c +++ b/src/battle/battle.c @@ -470,6 +470,11 @@ static void endCampaign(void) { awardTrophy("CAMPAIGN_COMPLETE"); + if (game.difficulty == DIFFICULTY_NORMAL) + { + awardTrophy("NORMAL_MODE"); + } + postBattle(); destroyBattle(); diff --git a/src/battle/bullets.c b/src/battle/bullets.c index 3d7091b..43a479f 100644 --- a/src/battle/bullets.c +++ b/src/battle/bullets.c @@ -187,7 +187,7 @@ static void checkCollisions(Bullet *b) if (battle.hasSuspicionLevel) { - battle.suspicionLevel -= 2; + battle.suspicionLevel -= (game.difficulty == DIFFICULTY_EASY) ? 4 : 2; } } diff --git a/src/battle/bullets.h b/src/battle/bullets.h index a74a80d..782852c 100644 --- a/src/battle/bullets.h +++ b/src/battle/bullets.h @@ -55,3 +55,4 @@ extern App app; extern Battle battle; extern Colors colors; extern Entity *player; +extern Game game; diff --git a/src/battle/entities.c b/src/battle/entities.c index e413505..401dc82 100644 --- a/src/battle/entities.c +++ b/src/battle/entities.c @@ -98,6 +98,10 @@ void doEntities(void) if (--e->shieldRecharge <= 0) { e->shield = MIN(e->shield + 1, e->maxShield); + if (e == player && !game.currentMission->challengeData.isChallenge && game.difficulty == DIFFICULTY_EASY) + { + e->shield = MIN(e->shield + 1, e->maxShield); + } e->shieldRecharge = e->shieldRechargeRate; } @@ -179,6 +183,11 @@ void doEntities(void) if (e->killedBy == player && battle.hasSuspicionLevel) { battle.suspicionLevel -= (MAX_SUSPICION_LEVEL * 0.12); + + if (game.difficulty == DIFFICULTY_EASY) + { + battle.suspicionLevel -= (MAX_SUSPICION_LEVEL * 0.12); + } } if (e == player) diff --git a/src/battle/entities.h b/src/battle/entities.h index f3779d7..c8b3410 100644 --- a/src/battle/entities.h +++ b/src/battle/entities.h @@ -45,3 +45,4 @@ extern Battle battle; extern Dev dev; extern Entity *self; extern Entity *player; +extern Game game; diff --git a/src/battle/fighters.c b/src/battle/fighters.c index e4c2687..492bf91 100644 --- a/src/battle/fighters.c +++ b/src/battle/fighters.c @@ -94,6 +94,11 @@ Entity *spawnFighter(char *name, int x, int y, int side) { e->aiAggression = 4; } + + if (game.difficulty == DIFFICULTY_EASY) + { + e->aiAggression = MIN(e->aiAggression, 2); + } e->action = doAI; e->die = die; diff --git a/src/battle/player.c b/src/battle/player.c index 3a3f29e..f6bad04 100644 --- a/src/battle/player.c +++ b/src/battle/player.c @@ -94,6 +94,12 @@ void initPlayer(void) 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; + + if (!game.currentMission->challengeData.isChallenge && game.difficulty == DIFFICULTY_EASY) + { + player->health = player->maxHealth = (player->maxHealth * 1.25); + player->shield = player->maxShield = (player->maxShield * 1.25); + } } static void setPilotName(void) @@ -214,17 +220,19 @@ static void updateDeathStats(void) static void rechargeBoostECM(void) { - int boostTimer, ecmTimer; + int boostTimer, ecmTimer, i; + + i = (game.currentMission->challengeData.isChallenge || game.difficulty == DIFFICULTY_NORMAL) ? 1 : 2; boostTimer = battle.boostTimer; - battle.boostTimer = MIN(battle.boostTimer + 1, BOOST_RECHARGE_TIME); + battle.boostTimer = MIN(battle.boostTimer + i, BOOST_RECHARGE_TIME); if (boostTimer < BOOST_RECHARGE_TIME && battle.boostTimer == BOOST_RECHARGE_TIME) { playSound(SND_RECHARGED); } ecmTimer = battle.ecmTimer; - battle.ecmTimer = MIN(battle.ecmTimer + 1, ECM_RECHARGE_TIME); + battle.ecmTimer = MIN(battle.ecmTimer + i, ECM_RECHARGE_TIME); if (ecmTimer < ECM_RECHARGE_TIME && battle.ecmTimer == ECM_RECHARGE_TIME) { playSound(SND_RECHARGED); diff --git a/src/defs.h b/src/defs.h index 5f81eb7..3628668 100644 --- a/src/defs.h +++ b/src/defs.h @@ -71,6 +71,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define MAX_TARGET_RANGE 65536 #define MAX_SYSTEM_POWER 100 +#define DIFFICULTY_EASY 0 +#define DIFFICULTY_NORMAL 1 + #define BATTLE_AREA_CELLS 50 #define BATTLE_AREA_WIDTH (640 * BATTLE_AREA_CELLS) #define BATTLE_AREA_HEIGHT (360 * BATTLE_AREA_CELLS) diff --git a/src/galaxy/galacticMap.c b/src/galaxy/galacticMap.c index 7063527..51b9b93 100644 --- a/src/galaxy/galacticMap.c +++ b/src/galaxy/galacticMap.c @@ -73,6 +73,7 @@ static char *SQUADRON_TEXT; static char *COMPLETED_TEXT; static char *EPIC_TEXT; static char *OPTIONAL_TEXT; +static char *EASY_MODE_TEXT; void initGalacticMap(void) { @@ -87,6 +88,7 @@ void initGalacticMap(void) memset(&app.keyboard, 0, sizeof(int) * MAX_KEYBOARD_KEYS); MISSIONS_TEXT = _("Missions: %d / %d"); + EASY_MODE_TEXT = _("Easy Mode"); PILOT_TEXT = _("Pilot: %s"); CRAFT_TEXT = _("Craft: %s"); SQUADRON_TEXT = _("Squadron: %s"); @@ -576,6 +578,11 @@ static void drawInfoBars(void) SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE); drawText((SCREEN_WIDTH / 2), 5, 18, TA_CENTER, colors.white, MISSIONS_TEXT, game.completedMissions, game.availableMissions); + + if (game.difficulty == DIFFICULTY_EASY) + { + drawText(10, 5, 14, TA_LEFT, colors.lightGrey, EASY_MODE_TEXT); + } } static void selectStarSystem(void) diff --git a/src/game/title.c b/src/game/title.c index 396ece5..dcf7a26 100644 --- a/src/game/title.c +++ b/src/game/title.c @@ -35,6 +35,11 @@ static void options(void); static void credits(void); static void quit(void); static void returnFromOptions(void); +static void newCampaign(void); +static void easyCampaign(void); +static void normalCampaign(void); +static void continueCampaign(void); +static void back(void); static SDL_Texture *background; static SDL_Texture *logo; @@ -86,6 +91,14 @@ void initTitle(void) getWidget("credits", "title")->action = credits; getWidget("quit", "title")->action = quit; + getWidget("new", "campaign")->action = newCampaign; + getWidget("continue", "campaign")->action = continueCampaign; + getWidget("back", "campaign")->action = back; + + getWidget("easy", "campaignDifficulty")->action = easyCampaign; + getWidget("normal", "campaignDifficulty")->action = normalCampaign; + getWidget("back", "campaignDifficulty")->action = back; + getWidget("ok", "stats")->action = ok; getWidget("ok", "trophies")->action = ok; @@ -190,6 +203,10 @@ static void draw(void) drawWidgets("title"); break; + case SHOW_CAMPAIGN: + drawWidgets("campaign"); + break; + case SHOW_STATS: drawStats(); break; @@ -227,6 +244,44 @@ static void handleKeyboard(void) static void campaign(void) { + show = SHOW_CAMPAIGN; +} + +static void newCampaign(void) +{ +} + +static void continueCampaign(void) +{ +} + +static void back(void) +{ + switch (show) + { + case SHOW_CAMPAIGN: + show = SHOW_TITLE; + break; + + case SHOW_CAMPAIGN_NEW: + show = SHOW_CAMPAIGN; + break; + } +} + +static void easyCampaign(void) +{ + game.difficulty = DIFFICULTY_EASY; + + destroyBattle(); + + initGalacticMap(); +} + +static void normalCampaign(void) +{ + game.difficulty = DIFFICULTY_NORMAL; + destroyBattle(); initGalacticMap(); diff --git a/src/game/title.h b/src/game/title.h index 8d21d4e..42b17bc 100644 --- a/src/game/title.h +++ b/src/game/title.h @@ -24,6 +24,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define SHOW_STATS 1 #define SHOW_OPTIONS 2 #define SHOW_TROPHIES 3 +#define SHOW_CAMPAIGN 4 +#define SHOW_CAMPAIGN_NEW 5 #define NUM_FIGHTERS 12 diff --git a/src/structs.h b/src/structs.h index 6c2dfc9..b308567 100644 --- a/src/structs.h +++ b/src/structs.h @@ -424,6 +424,7 @@ typedef struct { Mission challengeMissionHead; Mission *currentMission; char selectedStarSystem[MAX_NAME_LENGTH]; + int difficulty; int completedMissions; int availableMissions; int totalMissions; diff --git a/src/system/load.c b/src/system/load.c index db69fbf..e6fac5a 100644 --- a/src/system/load.c +++ b/src/system/load.c @@ -36,6 +36,8 @@ void loadGame(void) root = cJSON_Parse(text); gameJSON = cJSON_GetObjectItem(root, "game"); + + game.difficulty = lookup(getJSONValueStr(gameJSON, "difficulty", "DIFFICULTY_NORMAL")); STRNCPY(game.selectedStarSystem, cJSON_GetObjectItem(gameJSON, "selectedStarSystem")->valuestring, MAX_NAME_LENGTH); diff --git a/src/system/load.h b/src/system/load.h index 262578a..1faea86 100644 --- a/src/system/load.h +++ b/src/system/load.h @@ -30,5 +30,6 @@ extern char *getSaveFilePath(char *filename); extern char *getLookupName(char *prefix, long num); extern StarSystem *getStarSystem(char *name); extern Trophy *getTrophy(char *id); +extern char *getJSONValueStr(cJSON *node, char *name, char *defValue); extern Game game; diff --git a/src/system/lookup.c b/src/system/lookup.c index 9102229..e9fe243 100644 --- a/src/system/lookup.c +++ b/src/system/lookup.c @@ -205,6 +205,9 @@ void initLookups(void) addLookup("TROPHY_SILVER", TROPHY_SILVER); addLookup("TROPHY_GOLD", TROPHY_GOLD); addLookup("TROPHY_PLATINUM", TROPHY_PLATINUM); + + addLookup("DIFFICULTY_NORMAL", DIFFICULTY_NORMAL); + addLookup("DIFFICULTY_EASY", DIFFICULTY_EASY); } static void addLookup(char *name, long value) diff --git a/src/system/save.c b/src/system/save.c index 3e84b89..9f59fdd 100644 --- a/src/system/save.c +++ b/src/system/save.c @@ -38,6 +38,8 @@ void saveGame(void) gameJSON = cJSON_CreateObject(); cJSON_AddItemToObject(root, "game", gameJSON); + cJSON_AddStringToObject(gameJSON, "difficulty", getLookupName("DIFFICULTY_", game.difficulty)); + cJSON_AddStringToObject(gameJSON, "selectedStarSystem", game.selectedStarSystem); saveStarSystems(gameJSON);