From ad7a1eefdac842246388de69618c837c3f1ab632 Mon Sep 17 00:00:00 2001 From: Steve Date: Sun, 28 Feb 2016 13:02:57 +0000 Subject: [PATCH] Challenge integration updates. --- data/challenges/01.json | 38 +++---- data/challenges/02.json | 40 +++++++ data/challenges/03.json | 44 ++++++++ src/battle/ai.c | 2 +- src/battle/battle.c | 12 ++- src/battle/battle.h | 1 + src/battle/entities.c | 8 +- src/battle/fighters.c | 6 +- src/battle/hud.c | 11 +- src/battle/hud.h | 1 + src/battle/missionInfo.c | 190 ++++++++++++++++++--------------- src/battle/missionInfo.h | 1 + src/battle/objectives.c | 2 - src/battle/player.c | 4 +- src/battle/script.c | 4 +- src/challenges/challengeHome.c | 5 + src/challenges/challenges.c | 60 ++++++++++- src/challenges/challenges.h | 3 + src/defs.h | 2 + src/galaxy/mission.c | 52 ++++++--- src/galaxy/stats.c | 12 +-- src/galaxy/stats.h | 1 + src/structs.h | 16 ++- src/system/load.c | 5 +- src/system/lookup.c | 2 + src/system/util.c | 23 ++++ src/test/testMission.c | 49 +-------- src/test/testMission.h | 2 + 28 files changed, 385 insertions(+), 211 deletions(-) create mode 100644 data/challenges/02.json create mode 100644 data/challenges/03.json diff --git a/data/challenges/01.json b/data/challenges/01.json index 97e816a..b536302 100644 --- a/data/challenges/01.json +++ b/data/challenges/01.json @@ -1,6 +1,6 @@ { - "name" : "Destroy all the Darts", - "description" : "Destroy all the Darts", + "name" : "Destroy all Darts", + "description" : "Destroy all Darts", "background" : "gfx/backgrounds/background03.jpg", "planet" : "gfx/planets/spirit.png", "music" : "", @@ -12,32 +12,20 @@ "x" : 25, "y" : 25 }, - "objectives" : [ - { - "description" : "Destroy Dart", - "targetName" : "Dart", - "targetValue" : 3, - "targetType" : "TT_DESTROY" - } - ], - "challenges" : [ - { - "type" : "CHALLENGE_TIME", - "value" : 20 - }, - { - "type" : "CHALLENGE_TIME", - "value" : 15 - }, - { - "type" : "CHALLENGE_TIME", - "value" : 10 - } - ], + "challenge" : { + "timeLimit" : 30, + "killLimit" : 3, + "challenges" : [ + { + "type" : "CHALLENGE_TIME", + "value" : 30 + } + ] + }, "fighters" : [ { "name" : "Dart", - "types" : "Dart", + "types" : "SimpleDart", "side" : "SIDE_PIRATE", "x" : 25, "y" : 22, diff --git a/data/challenges/02.json b/data/challenges/02.json new file mode 100644 index 0000000..c80aea1 --- /dev/null +++ b/data/challenges/02.json @@ -0,0 +1,40 @@ +{ + "name" : "Destroy all Darts", + "description" : "Destroy all Darts", + "background" : "gfx/backgrounds/background03.jpg", + "planet" : "gfx/planets/spirit.png", + "music" : "", + "player" : { + "type" : "Nymph", + "side" : "SIDE_ALLIES", + "pilot" : "-", + "squadron" : "-", + "x" : 25, + "y" : 25 + }, + "challenge" : { + "timeLimit" : 20, + "killLimit" : 5, + "challenges" : [ + { + "type" : "CHALLENGE_TIME", + "value" : 30 + }, + { + "type" : "CHALLENGE_TIME", + "value" : 15 + } + ] + }, + "fighters" : [ + { + "name" : "Dart", + "types" : "Dart", + "side" : "SIDE_PIRATE", + "x" : 25, + "y" : 22, + "number" : 3, + "scatter" : 1000 + } + ] +} diff --git a/data/challenges/03.json b/data/challenges/03.json new file mode 100644 index 0000000..473a2bd --- /dev/null +++ b/data/challenges/03.json @@ -0,0 +1,44 @@ +{ + "name" : "Destroy all Darts", + "description" : "Destroy all Darts", + "background" : "gfx/backgrounds/background03.jpg", + "planet" : "gfx/planets/spirit.png", + "music" : "", + "player" : { + "type" : "Nymph", + "side" : "SIDE_ALLIES", + "pilot" : "-", + "squadron" : "-", + "x" : 25, + "y" : 25 + }, + "challenge" : { + "timeLimit" : 20, + "killLimit" : 3, + "challenges" : [ + { + "type" : "CHALLENGE_TIME", + "value" : 20 + }, + { + "type" : "CHALLENGE_TIME", + "value" : 15 + }, + { + "type" : "CHALLENGE_TIME", + "value" : 10 + } + ] + }, + "fighters" : [ + { + "name" : "Dart", + "types" : "Dart", + "side" : "SIDE_PIRATE", + "x" : 25, + "y" : 22, + "number" : 3, + "scatter" : 1000 + } + ] +} diff --git a/src/battle/ai.c b/src/battle/ai.c index 8aed1f8..430508a 100644 --- a/src/battle/ai.c +++ b/src/battle/ai.c @@ -290,7 +290,7 @@ static void findTarget(void) Entity *e, **candidates; unsigned int dist, closest; - dist = closest = (battle.epic || (self->aiFlags & AIF_UNLIMITED_RANGE)) ? MAX_TARGET_RANGE : 2000; + dist = closest = (battle.isEpic || (self->aiFlags & AIF_UNLIMITED_RANGE)) ? MAX_TARGET_RANGE : 2000; candidates = getAllEntsWithin(self->x - (self->w / 2) - (dist / 2), self->y - (self->h / 2) - (dist / 2), self->w + dist, self->h + dist, self); diff --git a/src/battle/battle.c b/src/battle/battle.c index 4cc7624..ddd3764 100644 --- a/src/battle/battle.c +++ b/src/battle/battle.c @@ -102,11 +102,11 @@ static void logic(void) if (show == SHOW_BATTLE) { - if (!battle.epic || (battle.epic && !battle.playerSelect)) + if (!battle.isEpic || (battle.isEpic && !battle.playerSelect)) { doBattle(); } - else if (battle.epic && battle.playerSelect) + else if (battle.isEpic && battle.playerSelect) { doPlayerSelect(); } @@ -134,6 +134,8 @@ static void doBattle(void) battle.planet.y -= ssy * 0.05; doObjectives(); + + doChallenges(); doHud(); @@ -303,7 +305,7 @@ static void continueGame(void) destroyBattle(); - if (!battle.isChallenge) + if (!battle.challengeData.isChallenge) { initGalacticMap(); } @@ -344,7 +346,7 @@ static void quitBattle(void) destroyBattle(); - if (!battle.isChallenge) + if (!battle.challengeData.isChallenge) { initGalacticMap(); } @@ -366,7 +368,7 @@ static void postBattle(void) } } - if (!battle.isChallenge) + if (!battle.challengeData.isChallenge) { if (game.currentMission && !game.currentMission->completed) { diff --git a/src/battle/battle.h b/src/battle/battle.h index b2046be..b577b71 100644 --- a/src/battle/battle.h +++ b/src/battle/battle.h @@ -39,6 +39,7 @@ extern void drawHud(void); extern void drawEffects(void); extern void doEffects(void); extern void doObjectives(void); +extern void doChallenges(void); extern void blitScaled(SDL_Texture *texture, int x, int y, int w, int h); extern void initHud(void); extern void initRadar(void); diff --git a/src/battle/entities.c b/src/battle/entities.c index 83be970..6d39a6c 100644 --- a/src/battle/entities.c +++ b/src/battle/entities.c @@ -180,7 +180,7 @@ void doEntities(void) { player = NULL; - battle.playerSelect = battle.epic; + battle.playerSelect = battle.isEpic; } cutRope(e); @@ -221,10 +221,10 @@ void doEntities(void) prev = e; } - battle.numAllies = (battle.epic) ? numAllies : numActiveAllies; - battle.numEnemies = (battle.epic) ? numEnemies : numActiveEnemies; + battle.numAllies = (battle.isEpic) ? numAllies : numActiveAllies; + battle.numEnemies = (battle.isEpic) ? numEnemies : numActiveEnemies; - if (battle.epic && battle.stats[STAT_TIME] % FPS == 0) + if (battle.isEpic && battle.stats[STAT_TIME] % FPS == 0) { if (numAllies > battle.epicFighterLimit) { diff --git a/src/battle/fighters.c b/src/battle/fighters.c index f12daab..0023a8b 100644 --- a/src/battle/fighters.c +++ b/src/battle/fighters.c @@ -283,7 +283,7 @@ void doFighter(void) if (strcmp(self->name, "Civilian") == 0) { battle.stats[STAT_CIVILIANS_KILLED]++; - if (!battle.epic) + if (!battle.isEpic) { addHudMessage(colors.red, "Civilian has been killed"); } @@ -291,7 +291,7 @@ void doFighter(void) else { battle.stats[STAT_ALLIES_KILLED]++; - if (!battle.epic) + if (!battle.isEpic) { addHudMessage(colors.red, "Ally has been killed"); } @@ -483,7 +483,7 @@ static void die(void) break; } - if (self == player && battle.epic) + if (self == player && battle.isEpic) { n = 1; } diff --git a/src/battle/hud.c b/src/battle/hud.c index 2f6216f..1b626f9 100644 --- a/src/battle/hud.c +++ b/src/battle/hud.c @@ -394,13 +394,20 @@ static void drawNumFighters(void) static void drawObjectives(void) { - if (!battle.isChallenge) + if (!battle.challengeData.isChallenge) { drawText(SCREEN_WIDTH / 2, 10, 16, TA_CENTER, colors.white, "%d / %d", battle.numObjectivesComplete, battle.numObjectivesTotal); } else { - drawText(SCREEN_WIDTH / 2, 10, 16, TA_CENTER, colors.white, "%d.%02d", battle.stats[STAT_TIME] / FPS, battle.stats[STAT_TIME] % 100); + if (battle.challengeData.timeLimit) + { + drawText(SCREEN_WIDTH / 2, 10, 16, TA_CENTER, colors.white, timeToString(battle.challengeData.timeLimit - battle.stats[STAT_TIME], 0)); + } + else + { + drawText(SCREEN_WIDTH / 2, 10, 16, TA_CENTER, colors.white, timeToString(battle.stats[STAT_TIME], 0)); + } } } diff --git a/src/battle/hud.h b/src/battle/hud.h index e1f2fe4..1b4cc34 100644 --- a/src/battle/hud.h +++ b/src/battle/hud.h @@ -33,6 +33,7 @@ extern void drawRadarRangeWarning(void); extern int getPercent(float current, float total); extern int playerHasGun(int type); extern char *getTranslatedString(char *string); +extern char *timeToString(long millis, int showHours); extern App app; extern Battle battle; diff --git a/src/battle/missionInfo.c b/src/battle/missionInfo.c index b3b13f2..39753f0 100644 --- a/src/battle/missionInfo.c +++ b/src/battle/missionInfo.c @@ -21,6 +21,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "missionInfo.h" static void drawMissionSummary(SDL_Texture *title); +static void drawObjectives(void); +static void drawChallenges(void); static SDL_Texture *missionStartTexture; static SDL_Texture *missionInProgressTexture; @@ -30,10 +32,10 @@ static const char *objectiveStatus[] = {"Incomplete", "Complete", "Failed", "Con void initMissionInfo(void) { - missionStartTexture = !battle.isChallenge ? getTexture("gfx/battle/missionStart.png") : getTexture("gfx/battle/challengeStart.png"); - missionInProgressTexture = !battle.isChallenge ? getTexture("gfx/battle/missionInProgress.png") : getTexture("gfx/battle/challengeInProgress.png"); - missionCompleteTexture = !battle.isChallenge ? getTexture("gfx/battle/missionComplete.png") : getTexture("gfx/battle/challengeComplete.png"); - missionFailedTexture = !battle.isChallenge ? getTexture("gfx/battle/missionFailed.png") : getTexture("gfx/battle/challengeFailed.png"); + missionStartTexture = !battle.challengeData.isChallenge ? getTexture("gfx/battle/missionStart.png") : getTexture("gfx/battle/challengeStart.png"); + missionInProgressTexture = !battle.challengeData.isChallenge ? getTexture("gfx/battle/missionInProgress.png") : getTexture("gfx/battle/challengeInProgress.png"); + missionCompleteTexture = !battle.challengeData.isChallenge ? getTexture("gfx/battle/missionComplete.png") : getTexture("gfx/battle/challengeComplete.png"); + missionFailedTexture = !battle.challengeData.isChallenge ? getTexture("gfx/battle/missionFailed.png") : getTexture("gfx/battle/challengeFailed.png"); } void drawMissionInfo(void) @@ -70,12 +72,6 @@ void drawMissionInfo(void) static void drawMissionSummary(SDL_Texture *header) { - Objective *o; - Challenge *c; - int y; - SDL_Color color; - char *challengeStatus; - SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND); SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 128); SDL_RenderFillRect(app.renderer, NULL); @@ -83,83 +79,107 @@ static void drawMissionSummary(SDL_Texture *header) blit(header, SCREEN_WIDTH / 2, 150, 1); - y = 215; - - if (!battle.isChallenge) + if (!battle.challengeData.isChallenge) { - drawText(SCREEN_WIDTH / 2, y, 28, TA_CENTER, colors.white, _("OBJECTIVES")); - - y += 10; - - for (o = battle.objectiveHead.next ; o != NULL ; o = o->next) - { - if (o->active) - { - y += 50; - - switch (o->status) - { - case OS_INCOMPLETE: - color = colors.white; - break; - - case OS_COMPLETE: - color = colors.green; - break; - - case OS_FAILED: - color = colors.red; - break; - } - - drawText(SCREEN_WIDTH / 2 - 100, y, 22, TA_RIGHT, colors.white, o->description); - if (o->targetValue > 1 && !o->isCondition) - { - drawText(SCREEN_WIDTH / 2, y, 22, TA_CENTER, colors.white, "%d / %d", o->currentValue, o->targetValue); - } - drawText(SCREEN_WIDTH / 2 + 100, y, 22, TA_LEFT, color, objectiveStatus[o->status]); - } - } - - if (!battle.objectiveHead.next) - { - y += 50; - - drawText(SCREEN_WIDTH / 2, y, 22, TA_CENTER, colors.white, _("(none)")); - } - - y += 75; + drawObjectives(); } - - if (battle.isChallenge) + else { - drawText(SCREEN_WIDTH / 2, y, 24, TA_CENTER, colors.white, game.currentMission->description); - - y += 25; - - for (c = game.currentMission->challengeHead.next ; c != NULL ; c = c->next) - { - y += 50; - - color = colors.white; - - challengeStatus = "Incomplete"; - - if (c->passed) - { - color = colors.green; - - challengeStatus = "Complete"; - } - else if (battle.status == MS_COMPLETE ||battle.status == MS_FAILED) - { - color = colors.red; - - challengeStatus = "Failed"; - } - - drawText(SCREEN_WIDTH / 2 - 50, y, 22, TA_RIGHT, colors.white, "%s", getChallengeDescription(c)); - drawText(SCREEN_WIDTH / 2 + 50, y, 22, TA_LEFT, color, challengeStatus); - } + drawChallenges(); } } + +static void drawObjectives(void) +{ + Objective *o; + SDL_Color color; + int y = 215; + + drawText(SCREEN_WIDTH / 2, y, 28, TA_CENTER, colors.white, _("OBJECTIVES")); + + y += 10; + + for (o = battle.objectiveHead.next ; o != NULL ; o = o->next) + { + if (o->active) + { + y += 50; + + switch (o->status) + { + case OS_INCOMPLETE: + color = colors.white; + break; + + case OS_COMPLETE: + color = colors.green; + break; + + case OS_FAILED: + color = colors.red; + break; + } + + drawText(SCREEN_WIDTH / 2 - 100, y, 22, TA_RIGHT, colors.white, o->description); + if (o->targetValue > 1 && !o->isCondition) + { + drawText(SCREEN_WIDTH / 2, y, 22, TA_CENTER, colors.white, "%d / %d", o->currentValue, o->targetValue); + } + drawText(SCREEN_WIDTH / 2 + 100, y, 22, TA_LEFT, color, objectiveStatus[o->status]); + } + } + + if (!battle.objectiveHead.next) + { + y += 50; + + drawText(SCREEN_WIDTH / 2, y, 22, TA_CENTER, colors.white, _("(none)")); + } + + y += 75; +} + +static void drawChallenges(void) +{ + Challenge *c; + char *challengeStatus; + SDL_Color color; + int y = 215; + + drawText(SCREEN_WIDTH / 2, y, 24, TA_CENTER, colors.white, game.currentMission->description); + + if (battle.status == MS_START && battle.challengeData.timeLimit) + { + y+= 50; + + drawText(SCREEN_WIDTH / 2, y, 20, TA_CENTER, colors.white, "Time Limit: %s", timeToString(battle.challengeData.timeLimit, 0)); + } + + y += 25; + + for (c = game.currentMission->challengeHead.next ; c != NULL ; c = c->next) + { + y += 50; + + color = colors.white; + + challengeStatus = _("Incomplete"); + + if (c->passed) + { + color = colors.green; + + challengeStatus = _("Complete"); + } + else if (battle.status == MS_COMPLETE ||battle.status == MS_FAILED) + { + color = colors.red; + + challengeStatus = _("Failed"); + } + + drawText(SCREEN_WIDTH / 2 - 50, y, 22, TA_RIGHT, colors.white, "%s", getChallengeDescription(c)); + drawText(SCREEN_WIDTH / 2 + 50, y, 22, TA_LEFT, color, challengeStatus); + } +} + diff --git a/src/battle/missionInfo.h b/src/battle/missionInfo.h index 2fb2b51..8574f83 100644 --- a/src/battle/missionInfo.h +++ b/src/battle/missionInfo.h @@ -26,6 +26,7 @@ extern SDL_Texture *getTexture(char *filename); extern char *getChallengeDescription(Challenge *c); extern void drawWidgets(char *groupName); extern char *getTranslatedString(char *string); +extern char *timeToString(long millis, int showHours); extern App app; extern Battle battle; diff --git a/src/battle/objectives.c b/src/battle/objectives.c index a60e641..297141a 100644 --- a/src/battle/objectives.c +++ b/src/battle/objectives.c @@ -64,8 +64,6 @@ void doObjectives(void) if (!battle.manualComplete && numHiddenObjectives == 0 && battle.numObjectivesTotal > 0 && battle.numObjectivesComplete == battle.numObjectivesTotal) { completeMission(); - - updateChallenges(); } if (objectiveFailed) diff --git a/src/battle/player.c b/src/battle/player.c index 5cf0062..959660c 100644 --- a/src/battle/player.c +++ b/src/battle/player.c @@ -114,7 +114,7 @@ void doPlayer(void) if (player->health <= 0 && battle.status == MS_IN_PROGRESS) { - if (!battle.epic) + if (!battle.isEpic) { failMission(); } @@ -326,7 +326,7 @@ void initPlayerSelect(void) } else { - battle.epic = 0; + battle.isEpic = 0; failMission(); } diff --git a/src/battle/script.c b/src/battle/script.c index 2a1dc22..669d682 100644 --- a/src/battle/script.c +++ b/src/battle/script.c @@ -167,12 +167,12 @@ static void executeNextLine(ScriptRunner *runner) } else if (strcmp(command, "RETREAT_ALLIES") == 0) { - battle.epic = 0; + battle.isEpic = 0; retreatAllies(); } else if (strcmp(command, "RETREAT_ENEMIES") == 0) { - battle.epic = 0; + battle.isEpic = 0; retreatEnemies(); } else diff --git a/src/challenges/challengeHome.c b/src/challenges/challengeHome.c index ea327e3..7ded962 100644 --- a/src/challenges/challengeHome.c +++ b/src/challenges/challengeHome.c @@ -177,6 +177,11 @@ static void drawChallenges(void) i++; } + + if (game.currentMission) + { + drawText(SCREEN_WIDTH / 2, SCREEN_HEIGHT - 130, 24, TA_CENTER, colors.white, game.currentMission->description); + } } static void handleKeyboard(void) diff --git a/src/challenges/challenges.c b/src/challenges/challenges.c index 764454d..d400534 100644 --- a/src/challenges/challenges.c +++ b/src/challenges/challenges.c @@ -26,6 +26,9 @@ static void updateArmourChallenge(Challenge *c); static void updateLossesChallenge(Challenge *c); static void updatePlayerKillsChallenge(Challenge *c); static void updateDisabledChallenge(Challenge *c); +static void completeChallenge(void); +static void failChallenge(void); +static void updateChallenges(void); static char *getFormattedChallengeDescription(const char *format, ...); char *getChallengeDescription(Challenge *c); @@ -68,7 +71,28 @@ void initChallenges(void) free(filenames); } -void updateChallenges(void) +void doChallenges(void) +{ + if (battle.challengeData.isChallenge && battle.status == MS_IN_PROGRESS) + { + if (battle.challengeData.timeLimit > 0 && battle.stats[STAT_TIME] / FPS >= battle.challengeData.timeLimit) + { + failChallenge(); + } + + if (battle.challengeData.killLimit > 0 && battle.stats[STAT_ENEMIES_KILLED_PLAYER] >= battle.challengeData.killLimit) + { + completeChallenge(); + } + + if (battle.status != MS_IN_PROGRESS) + { + updateChallenges(); + } + } +} + +static void updateChallenges(void) { Challenge *c; @@ -213,3 +237,37 @@ static char *getFormattedChallengeDescription(const char *format, ...) return descriptionBuffer; } + +static void completeChallenge(void) +{ + if (battle.status == MS_IN_PROGRESS) + { + battle.status = MS_COMPLETE; + battle.missionFinishedTimer = FPS; + selectWidget("continue", "battleWon"); + + game.stats[STAT_CHALLENGES_COMPLETED]++; + + retreatAllies(); + + retreatEnemies(); + + player->flags |= EF_IMMORTAL; + } +} + +static void failChallenge(void) +{ + if (battle.status == MS_IN_PROGRESS) + { + battle.status = MS_FAILED; + battle.missionFinishedTimer = FPS; + selectWidget("retry", "battleLost"); + + retreatAllies(); + + retreatEnemies(); + + player->flags |= EF_IMMORTAL; + } +} diff --git a/src/challenges/challenges.h b/src/challenges/challenges.h index a078c38..4a10d67 100644 --- a/src/challenges/challenges.h +++ b/src/challenges/challenges.h @@ -22,6 +22,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. extern Mission *loadMissionMeta(char *filename); extern char **getFileList(char *dir, int *count); +extern void selectWidget(const char *name, const char *group); +extern void retreatAllies(void); +extern void retreatEnemies(void); extern Battle battle; extern Entity *player; diff --git a/src/defs.h b/src/defs.h index 20977f4..c0c1b91 100644 --- a/src/defs.h +++ b/src/defs.h @@ -267,6 +267,8 @@ enum { STAT_MISSIONS_STARTED, STAT_MISSIONS_COMPLETED, + STAT_CHALLENGES_STARTED, + STAT_CHALLENGES_COMPLETED, STAT_SHOTS_FIRED, STAT_SHOTS_HIT, STAT_ROCKETS_FIRED, diff --git a/src/galaxy/mission.c b/src/galaxy/mission.c index f0c5e39..323eff2 100644 --- a/src/galaxy/mission.c +++ b/src/galaxy/mission.c @@ -30,6 +30,7 @@ static void loadLocations(cJSON *node); static unsigned long hashcode(const char *str); static char **toTypeArray(char *types, int *numTypes); static void loadEpicData(cJSON *node); +static void loadChallengeData(cJSON *node); Mission *loadMissionMeta(char *filename) { @@ -72,24 +73,29 @@ Mission *loadMissionMeta(char *filename) challengeTail = &mission->challengeHead; - node = cJSON_GetObjectItem(root, "challenges"); + node = cJSON_GetObjectItem(root, "challenge"); if (node) { - node = node->child; + node = cJSON_GetObjectItem(node, "challenges"); - while (node) + if (node) { - challenge = malloc(sizeof(Challenge)); - memset(challenge, 0, sizeof(Challenge)); + node = node->child; - challenge->type = lookup(cJSON_GetObjectItem(node, "type")->valuestring); - challenge->value = cJSON_GetObjectItem(node, "value")->valueint; - - challengeTail->next = challenge; - challengeTail = challenge; - - node = node->next; + while (node) + { + challenge = malloc(sizeof(Challenge)); + memset(challenge, 0, sizeof(Challenge)); + + challenge->type = lookup(cJSON_GetObjectItem(node, "type")->valuestring); + challenge->value = cJSON_GetObjectItem(node, "value")->valueint; + + challengeTail->next = challenge; + challengeTail = challenge; + + node = node->next; + } } } @@ -147,6 +153,11 @@ void loadMission(char *filename) loadEpicData(cJSON_GetObjectItem(root, "epic")); } + if (cJSON_GetObjectItem(root, "challenge")) + { + loadChallengeData(cJSON_GetObjectItem(root, "challenge")); + } + if (cJSON_GetObjectItem(root, "manualComplete")) { battle.manualComplete = cJSON_GetObjectItem(root, "manualComplete")->valueint; @@ -165,11 +176,15 @@ void loadMission(char *filename) endSectionTransition(); - /* only increment num missions started if there are objectives (Free Flight excluded, for example) */ + /* only increment num missions / challenges started if there are some (Free Flight excluded, for example) */ if (battle.objectiveHead.next) { game.stats[STAT_MISSIONS_STARTED]++; } + else if (battle.challengeData.isChallenge) + { + game.stats[STAT_CHALLENGES_STARTED]++; + } else { battle.status = MS_IN_PROGRESS; @@ -181,8 +196,6 @@ void loadMission(char *filename) initPlayer(); - battle.isChallenge = game.currentMission->challengeHead.next != NULL; - initMissionInfo(); playMusic(music); @@ -801,7 +814,7 @@ static void loadEpicData(cJSON *node) int numFighters[SIDE_MAX]; memset(numFighters, 0, sizeof(int) * SIDE_MAX); - battle.epic = 1; + battle.isEpic = 1; battle.epicFighterLimit = cJSON_GetObjectItem(node, "fighterLimit")->valueint; @@ -814,6 +827,13 @@ static void loadEpicData(cJSON *node) } } +static void loadChallengeData(cJSON *node) +{ + battle.challengeData.isChallenge = 1; + battle.challengeData.timeLimit = cJSON_GetObjectItem(node, "timeLimit")->valueint * FPS; + battle.challengeData.killLimit = cJSON_GetObjectItem(node, "killLimit")->valueint; +} + Mission *getMission(char *filename) { StarSystem *starSystem; diff --git a/src/galaxy/stats.c b/src/galaxy/stats.c index 8c1a362..b864ad4 100644 --- a/src/galaxy/stats.c +++ b/src/galaxy/stats.c @@ -74,9 +74,8 @@ void initStatsDisplay(void) void drawStats(void) { - int i, y, hours, minutes, seconds, startIndex; + int i, y, startIndex; SDL_Rect r; - char timePlayed[MAX_NAME_LENGTH]; SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND); SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 128); @@ -111,15 +110,8 @@ void drawStats(void) } } - seconds = game.stats[STAT_TIME] / FPS; - minutes = (seconds / 60) % 60; - hours = seconds / (60 * 60); - - seconds %= 60; - - sprintf(timePlayed, "%dh:%02dm:%02ds", hours, minutes, seconds); drawText(r.x + 20, 565, 18, TA_LEFT, colors.white, _("Time Played")); - drawText(r.x + r.w - 20, 565, 18, TA_RIGHT, colors.white, timePlayed); + drawText(r.x + r.w - 20, 565, 18, TA_RIGHT, colors.white, timeToString(game.stats[STAT_TIME], 1)); drawWidgets("stats"); } diff --git a/src/galaxy/stats.h b/src/galaxy/stats.h index 992d781..21013fb 100644 --- a/src/galaxy/stats.h +++ b/src/galaxy/stats.h @@ -26,6 +26,7 @@ extern void drawWidgets(char *groupName); extern void drawText(int x, int y, int size, int align, SDL_Color c, const char *format, ...); extern Widget *getWidget(const char *name, const char *group); extern char *getTranslatedString(char *string); +extern char *timeToString(long millis, int showHours); extern App app; extern Colors colors; diff --git a/src/structs.h b/src/structs.h index b134555..385e459 100644 --- a/src/structs.h +++ b/src/structs.h @@ -250,11 +250,11 @@ struct Mission { char craft[MAX_NAME_LENGTH]; int available; int completed; - int epic; int completedChallenges; int totalChallenges; - SDL_Rect rect; + int epic; Challenge challengeHead; + SDL_Rect rect; Mission *next; }; @@ -282,6 +282,14 @@ struct Quadtree { Quadtree *node[4]; }; +typedef struct { + int isChallenge; + int timeLimit; + int killLimit; + int lossLimit; + int itemLimit; +} ChallengeData; + typedef struct { int entId; SDL_Point camera; @@ -289,8 +297,7 @@ typedef struct { int numEnemies; int numInitialEnemies; int status; - int epic; - int isChallenge; + int isEpic; int epicFighterLimit; int playerSelect; int manualComplete; @@ -314,6 +321,7 @@ typedef struct { Location locationHead, *locationTail; struct cJSON *missionJSON; unsigned int stats[STAT_MAX]; + ChallengeData challengeData; Quadtree quadtree; } Battle; diff --git a/src/system/load.c b/src/system/load.c index 9ca5887..f822be9 100644 --- a/src/system/load.c +++ b/src/system/load.c @@ -94,7 +94,10 @@ static void loadChallenges(cJSON *missionsJSON) challenge = getChallenge(mission, type, value); - challenge->passed = cJSON_GetObjectItem(challengeJSON, "passed")->valueint; + if (challenge) + { + challenge->passed = cJSON_GetObjectItem(challengeJSON, "passed")->valueint; + } } } } diff --git a/src/system/lookup.c b/src/system/lookup.c index 5e9d3e6..3b9b054 100644 --- a/src/system/lookup.c +++ b/src/system/lookup.c @@ -118,6 +118,8 @@ void initLookups(void) addLookup("STAT_MISSIONS_STARTED", STAT_MISSIONS_STARTED); addLookup("STAT_MISSIONS_COMPLETED", STAT_MISSIONS_COMPLETED); + addLookup("STAT_CHALLENGES_STARTED", STAT_CHALLENGES_STARTED); + addLookup("STAT_CHALLENGES_COMPLETED", STAT_CHALLENGES_COMPLETED); addLookup("STAT_SHOTS_FIRED", STAT_SHOTS_FIRED); addLookup("STAT_SHOTS_HIT", STAT_SHOTS_HIT); addLookup("STAT_ROCKETS_FIRED", STAT_ROCKETS_FIRED); diff --git a/src/system/util.c b/src/system/util.c index 3f16008..6444b7d 100644 --- a/src/system/util.c +++ b/src/system/util.c @@ -72,3 +72,26 @@ void getSlope(int x1, int y1, int x2, int y2, float *dx, float *dy) *dy = (y1 - y2); *dy /= steps; } + +char *timeToString(long millis, int showHours) +{ + static char TIME[MAX_NAME_LENGTH]; + + int hours, minutes, seconds; + + seconds = millis / FPS; + minutes = (seconds / 60) % 60; + hours = seconds / (60 * 60); + seconds %= 60; + + if (showHours) + { + sprintf(TIME, "%dh:%02dm:%02ds", hours, minutes, seconds); + } + else + { + sprintf(TIME, "%dm %02ds", minutes, seconds); + } + + return TIME; +} diff --git a/src/test/testMission.c b/src/test/testMission.c index a8abbe8..4a4fb47 100644 --- a/src/test/testMission.c +++ b/src/test/testMission.c @@ -20,58 +20,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "testMission.h" -static Mission mission; - -static void loadChallenges(char *filename); - void loadTestMission(char *filename) { - memset(&mission, 0, sizeof(Mission)); - - STRNCPY(mission.filename, filename, MAX_DESCRIPTION_LENGTH); - - game.currentMission = &mission; + game.currentMission = loadMissionMeta(filename); initBattle(); - loadChallenges(filename); - loadMission(filename); } - -static void loadChallenges(char *filename) -{ - Challenge *challenge, *challengeTail; - cJSON *root, *node; - char *text; - - text = readFile(filename); - - root = cJSON_Parse(text); - - challengeTail = &mission.challengeHead; - - node = cJSON_GetObjectItem(root, "challenges"); - - if (node) - { - node = node->child; - - while (node) - { - challenge = malloc(sizeof(Challenge)); - memset(challenge, 0, sizeof(Challenge)); - - challenge->type = lookup(cJSON_GetObjectItem(node, "type")->valuestring); - challenge->value = cJSON_GetObjectItem(node, "value")->valueint; - - challengeTail->next = challenge; - challengeTail = challenge; - - node = node->next; - } - } - - cJSON_Delete(root); - free(text); -} diff --git a/src/test/testMission.h b/src/test/testMission.h index b7e1f6d..e729a94 100644 --- a/src/test/testMission.h +++ b/src/test/testMission.h @@ -25,6 +25,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. extern void initBattle(void); extern void loadMission(char *filename); extern long lookup(char *name); +extern Mission *loadMissionMeta(char *filename); extern char *readFile(char *filename); +extern Battle battle; extern Game game;