2015-10-20 13:51:49 +02:00
|
|
|
/*
|
2019-01-01 17:14:11 +01:00
|
|
|
Copyright (C) 2015-2019 Parallel Realities
|
2015-10-20 13:51:49 +02:00
|
|
|
|
|
|
|
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 "challenges.h"
|
|
|
|
|
|
|
|
static void updateTimeChallenge(Challenge *c);
|
2016-04-13 12:02:08 +02:00
|
|
|
static void updateSurvivalChallenge(Challenge *c);
|
2015-10-20 13:51:49 +02:00
|
|
|
static void updateAccuracyChallenge(Challenge *c);
|
|
|
|
static void updateArmourChallenge(Challenge *c);
|
|
|
|
static void updateLossesChallenge(Challenge *c);
|
|
|
|
static void updatePlayerKillsChallenge(Challenge *c);
|
2015-10-24 17:00:06 +02:00
|
|
|
static void updateDisabledChallenge(Challenge *c);
|
2016-03-07 13:29:23 +01:00
|
|
|
static void updateItemsChallenge(Challenge *c);
|
2016-05-25 13:25:13 +02:00
|
|
|
static void updateSurrenderChallenge(Challenge *c);
|
2016-06-01 10:32:10 +02:00
|
|
|
static void updateWaypointChallenge(Challenge *c);
|
2016-07-29 10:56:27 +02:00
|
|
|
static void updateRescueChallenge(Challenge *c);
|
2016-02-28 14:02:57 +01:00
|
|
|
static void completeChallenge(void);
|
|
|
|
static void failChallenge(void);
|
2016-04-03 15:04:31 +02:00
|
|
|
static int updateChallenges(void);
|
2015-10-20 13:51:49 +02:00
|
|
|
static char *getFormattedChallengeDescription(const char *format, ...);
|
|
|
|
char *getChallengeDescription(Challenge *c);
|
2016-03-04 23:59:16 +01:00
|
|
|
static int challengeFinished(void);
|
2016-03-05 16:34:37 +01:00
|
|
|
static int alreadyPassed(void);
|
2016-03-01 08:21:31 +01:00
|
|
|
static void printStats(void);
|
2015-10-20 13:51:49 +02:00
|
|
|
|
|
|
|
static char descriptionBuffer[MAX_DESCRIPTION_LENGTH];
|
2016-02-29 11:47:41 +01:00
|
|
|
static const char *challengeDescription[CHALLENGE_MAX];
|
2015-10-20 13:51:49 +02:00
|
|
|
|
2016-02-27 13:14:29 +01:00
|
|
|
void initChallenges(void)
|
|
|
|
{
|
|
|
|
Mission *mission, *tail;
|
|
|
|
char **filenames;
|
|
|
|
char path[MAX_FILENAME_LENGTH];
|
|
|
|
int count, i;
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-02-29 11:47:41 +01:00
|
|
|
challengeDescription[CHALLENGE_ARMOUR] = _("Retain at least %d%% armour");
|
|
|
|
challengeDescription[CHALLENGE_TIME] = _("Complete challenge in %d seconds or less");
|
2016-04-13 12:02:08 +02:00
|
|
|
challengeDescription[CHALLENGE_SURVIVE] = _("Survive for %d seconds");
|
2016-03-03 10:12:08 +01:00
|
|
|
challengeDescription[CHALLENGE_SHOT_ACCURACY] = _("Attain a %d%% shot hit accuracy");
|
|
|
|
challengeDescription[CHALLENGE_ROCKET_ACCURACY] = _("Attain a %d%% rocket hit accuracy");
|
|
|
|
challengeDescription[CHALLENGE_MISSILE_ACCURACY] = _("Attain a %d%% missile hit accuracy");
|
2016-02-29 11:47:41 +01:00
|
|
|
challengeDescription[CHALLENGE_NO_LOSSES] = _("Do not lose any team mates");
|
|
|
|
challengeDescription[CHALLENGE_1_LOSS] = _("Do not lose more than 1 team mate");
|
|
|
|
challengeDescription[CHALLENGE_LOSSES] = _("Do not lose more than %d team mates");
|
|
|
|
challengeDescription[CHALLENGE_PLAYER_KILLS] = _("Take down %d enemy targets");
|
|
|
|
challengeDescription[CHALLENGE_DISABLE] = _("Disable %d or more enemy fighters");
|
2016-03-07 13:29:23 +01:00
|
|
|
challengeDescription[CHALLENGE_ITEMS] = _("Collect %d packages");
|
2016-05-15 09:19:26 +02:00
|
|
|
challengeDescription[CHALLENGE_PLAYER_ITEMS] = _("Collect %d packages");
|
2016-03-12 00:45:47 +01:00
|
|
|
challengeDescription[CHALLENGE_RESCUE] = _("Rescue %d civilians");
|
2016-05-25 13:25:13 +02:00
|
|
|
challengeDescription[CHALLENGE_SURRENDER] = _("Cause %d enemies to surrender");
|
2016-06-01 10:32:10 +02:00
|
|
|
challengeDescription[CHALLENGE_WAYPOINTS] = _("Reach %d waypoints");
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-02-27 13:14:29 +01:00
|
|
|
tail = &game.challengeMissionHead;
|
2016-03-04 15:29:50 +01:00
|
|
|
|
|
|
|
filenames = getFileList("data/challenges", &count);
|
|
|
|
|
2016-02-27 13:14:29 +01:00
|
|
|
for (i = 0 ; i < count ; i++)
|
|
|
|
{
|
|
|
|
sprintf(path, "data/challenges/%s", filenames[i]);
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-02-27 13:14:29 +01:00
|
|
|
mission = loadMissionMeta(path);
|
2016-03-07 20:03:55 +01:00
|
|
|
|
|
|
|
if (mission)
|
|
|
|
{
|
|
|
|
tail->next = mission;
|
|
|
|
tail = mission;
|
|
|
|
}
|
|
|
|
|
2016-02-27 13:14:29 +01:00
|
|
|
free(filenames[i]);
|
|
|
|
}
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-02-27 13:14:29 +01:00
|
|
|
free(filenames);
|
|
|
|
}
|
|
|
|
|
2016-04-13 12:02:08 +02:00
|
|
|
void loadChallenge(Mission *mission, cJSON *node)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
Challenge *challenge;
|
|
|
|
|
|
|
|
mission->challengeData.isChallenge = 1;
|
|
|
|
|
|
|
|
/* limits */
|
|
|
|
mission->challengeData.timeLimit = getJSONValue(node, "timeLimit", 0) * FPS;
|
|
|
|
mission->challengeData.killLimit = getJSONValue(node, "killLimit", 0);
|
|
|
|
mission->challengeData.escapeLimit = getJSONValue(node, "escapeLimit", 0);
|
|
|
|
mission->challengeData.waypointLimit = getJSONValue(node, "waypointLimit", 0);
|
|
|
|
mission->challengeData.itemLimit = getJSONValue(node, "itemLimit", 0);
|
2016-05-15 09:19:26 +02:00
|
|
|
mission->challengeData.playerItemLimit = getJSONValue(node, "playerItemLimit", 0);
|
2016-04-13 12:02:08 +02:00
|
|
|
mission->challengeData.rescueLimit = getJSONValue(node, "rescueLimit", 0);
|
2016-05-15 09:19:26 +02:00
|
|
|
mission->challengeData.disableLimit = getJSONValue(node, "disableLimit", 0);
|
2016-05-25 13:25:13 +02:00
|
|
|
mission->challengeData.surrenderLimit = getJSONValue(node, "surrenderLimit", 0);
|
2016-04-13 12:02:08 +02:00
|
|
|
|
|
|
|
/* restrictions */
|
|
|
|
mission->challengeData.noMissiles = getJSONValue(node, "noMissiles", 0);
|
|
|
|
mission->challengeData.noECM = getJSONValue(node, "noECM", 0);
|
|
|
|
mission->challengeData.noBoost = getJSONValue(node, "noBoost", 0);
|
|
|
|
mission->challengeData.noGuns = getJSONValue(node, "noGuns", 0);
|
|
|
|
|
|
|
|
if (getJSONValue(node, "noWeapons", 0))
|
|
|
|
{
|
|
|
|
mission->challengeData.noMissiles = mission->challengeData.noGuns = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* misc */
|
|
|
|
mission->challengeData.allowPlayerDeath = getJSONValue(node, "allowPlayerDeath", 0);
|
2016-05-15 09:19:26 +02:00
|
|
|
mission->challengeData.clearWaypointEnemies = getJSONValue(node, "clearWaypointEnemies", 0);
|
|
|
|
mission->challengeData.eliminateThreats = getJSONValue(node, "eliminateThreats", 0);
|
|
|
|
mission->challengeData.isDeathMatch = getJSONValue(node, "isDeathMatch", 0);
|
2016-04-13 12:02:08 +02:00
|
|
|
|
|
|
|
node = cJSON_GetObjectItem(node, "challenges");
|
|
|
|
|
|
|
|
if (node)
|
|
|
|
{
|
|
|
|
node = node->child;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
|
|
|
|
while (node && i < MAX_CHALLENGES)
|
|
|
|
{
|
|
|
|
challenge = malloc(sizeof(Challenge));
|
|
|
|
memset(challenge, 0, sizeof(Challenge));
|
|
|
|
|
|
|
|
challenge->type = lookup(cJSON_GetObjectItem(node, "type")->valuestring);
|
|
|
|
challenge->value = cJSON_GetObjectItem(node, "value")->valueint;
|
|
|
|
|
|
|
|
mission->challengeData.challenges[i] = challenge;
|
|
|
|
|
|
|
|
node = node->next;
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-28 14:02:57 +01:00
|
|
|
void doChallenges(void)
|
|
|
|
{
|
2016-04-03 15:04:31 +02:00
|
|
|
int passed;
|
|
|
|
|
2016-03-02 07:46:13 +01:00
|
|
|
if (game.currentMission->challengeData.isChallenge && battle.status == MS_IN_PROGRESS)
|
2016-02-28 14:02:57 +01:00
|
|
|
{
|
2016-03-04 23:59:16 +01:00
|
|
|
if (challengeFinished())
|
2016-02-28 14:02:57 +01:00
|
|
|
{
|
2016-05-15 09:19:26 +02:00
|
|
|
passed = 0;
|
|
|
|
|
|
|
|
if (player->health > 0 || (player->health <= 0 && game.currentMission->challengeData.allowPlayerDeath))
|
|
|
|
{
|
|
|
|
passed = updateChallenges();
|
|
|
|
}
|
2016-04-03 15:04:31 +02:00
|
|
|
|
|
|
|
if (passed)
|
2016-03-03 15:19:29 +01:00
|
|
|
{
|
2016-04-03 15:04:31 +02:00
|
|
|
completeChallenge();
|
2016-03-03 15:19:29 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-04-03 15:04:31 +02:00
|
|
|
failChallenge();
|
2016-03-03 15:19:29 +01:00
|
|
|
}
|
2016-02-28 14:02:57 +01:00
|
|
|
}
|
2016-03-04 23:59:16 +01:00
|
|
|
}
|
|
|
|
}
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-03-04 23:59:16 +01:00
|
|
|
static int challengeFinished(void)
|
|
|
|
{
|
|
|
|
if (game.currentMission->challengeData.timeLimit > 0 && battle.stats[STAT_TIME] >= game.currentMission->challengeData.timeLimit)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* disabled enemies count as killed during challenges - not player exclusive, but no need to worry about AI contributions here */
|
2016-03-13 11:51:48 +01:00
|
|
|
if (game.currentMission->challengeData.killLimit > 0 && (battle.stats[STAT_ENEMIES_KILLED_PLAYER] + battle.stats[STAT_CAPITAL_SHIPS_DESTROYED] + battle.stats[STAT_ENEMIES_DISABLED]) >= game.currentMission->challengeData.killLimit)
|
2016-03-04 23:59:16 +01:00
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-03-06 10:47:44 +01:00
|
|
|
if (game.currentMission->challengeData.escapeLimit > 0 && (battle.stats[STAT_ENEMIES_KILLED_PLAYER] + battle.stats[STAT_ENEMIES_ESCAPED]) >= game.currentMission->challengeData.escapeLimit)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-03-04 23:59:16 +01:00
|
|
|
if (game.currentMission->challengeData.waypointLimit > 0 && battle.stats[STAT_WAYPOINTS_VISITED] >= game.currentMission->challengeData.waypointLimit)
|
|
|
|
{
|
|
|
|
return 1;
|
2016-03-03 15:19:29 +01:00
|
|
|
}
|
2016-03-04 23:59:16 +01:00
|
|
|
|
2016-03-07 13:29:23 +01:00
|
|
|
if (game.currentMission->challengeData.itemLimit > 0 && battle.stats[STAT_ITEMS_COLLECTED] + battle.stats[STAT_ITEMS_COLLECTED_PLAYER] >= game.currentMission->challengeData.itemLimit)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-05-15 09:19:26 +02:00
|
|
|
if (game.currentMission->challengeData.playerItemLimit > 0 && battle.stats[STAT_ITEMS_COLLECTED_PLAYER] >= game.currentMission->challengeData.playerItemLimit)
|
2016-03-12 00:45:47 +01:00
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-05-15 09:19:26 +02:00
|
|
|
if (game.currentMission->challengeData.rescueLimit > 0 && (battle.stats[STAT_CIVILIANS_RESCUED] + battle.stats[STAT_CIVILIANS_KILLED]) >= game.currentMission->challengeData.rescueLimit)
|
2016-03-04 23:59:16 +01:00
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-06-02 10:50:23 +02:00
|
|
|
if (game.currentMission->challengeData.surrenderLimit > 0 && battle.stats[STAT_ENEMIES_SURRENDERED] >= game.currentMission->challengeData.surrenderLimit)
|
2016-05-25 13:25:13 +02:00
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-06-01 10:32:10 +02:00
|
|
|
if (game.currentMission->challengeData.waypointLimit > 0 && (battle.stats[STAT_WAYPOINTS_VISITED]) >= game.currentMission->challengeData.waypointLimit)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-05-15 09:19:26 +02:00
|
|
|
if (game.currentMission->challengeData.eliminateThreats && !battle.hasThreats)
|
2016-04-02 10:37:39 +02:00
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-05-15 09:19:26 +02:00
|
|
|
return (player->health <= 0 || player->alive == ALIVE_ESCAPED || battle.scriptedEnd);
|
2016-03-03 15:19:29 +01:00
|
|
|
}
|
|
|
|
|
2016-04-03 15:04:31 +02:00
|
|
|
static int updateChallenges(void)
|
2015-10-20 13:51:49 +02:00
|
|
|
{
|
2016-04-03 15:04:31 +02:00
|
|
|
int i, numPassed;
|
2015-10-20 13:51:49 +02:00
|
|
|
Challenge *c;
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-03-03 10:12:08 +01:00
|
|
|
updateAccuracyStats(battle.stats);
|
2016-04-03 15:04:31 +02:00
|
|
|
|
|
|
|
numPassed = 0;
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-03-03 00:20:37 +01:00
|
|
|
for (i = 0 ; i < MAX_CHALLENGES ; i++)
|
2015-10-20 13:51:49 +02:00
|
|
|
{
|
2016-03-03 08:44:04 +01:00
|
|
|
c = game.currentMission->challengeData.challenges[i];
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-04-03 15:04:31 +02:00
|
|
|
if (c)
|
2015-10-20 13:51:49 +02:00
|
|
|
{
|
2016-04-03 15:04:31 +02:00
|
|
|
if (!c->passed)
|
2015-10-20 13:51:49 +02:00
|
|
|
{
|
2016-04-03 15:04:31 +02:00
|
|
|
switch (c->type)
|
|
|
|
{
|
|
|
|
case CHALLENGE_TIME:
|
|
|
|
updateTimeChallenge(c);
|
|
|
|
break;
|
2016-04-13 12:02:08 +02:00
|
|
|
|
|
|
|
case CHALLENGE_SURVIVE:
|
|
|
|
updateSurvivalChallenge(c);
|
|
|
|
break;
|
2016-04-03 15:04:31 +02:00
|
|
|
|
|
|
|
case CHALLENGE_SHOT_ACCURACY:
|
|
|
|
case CHALLENGE_ROCKET_ACCURACY:
|
|
|
|
case CHALLENGE_MISSILE_ACCURACY:
|
|
|
|
updateAccuracyChallenge(c);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CHALLENGE_ARMOUR:
|
|
|
|
updateArmourChallenge(c);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CHALLENGE_NO_LOSSES:
|
|
|
|
case CHALLENGE_1_LOSS:
|
|
|
|
case CHALLENGE_LOSSES:
|
|
|
|
updateLossesChallenge(c);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CHALLENGE_PLAYER_KILLS:
|
|
|
|
updatePlayerKillsChallenge(c);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CHALLENGE_DISABLE:
|
|
|
|
updateDisabledChallenge(c);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CHALLENGE_ITEMS:
|
2016-05-15 09:19:26 +02:00
|
|
|
case CHALLENGE_PLAYER_ITEMS:
|
2016-04-03 15:04:31 +02:00
|
|
|
updateItemsChallenge(c);
|
|
|
|
break;
|
2016-05-25 13:25:13 +02:00
|
|
|
|
|
|
|
case CHALLENGE_SURRENDER:
|
|
|
|
updateSurrenderChallenge(c);
|
|
|
|
break;
|
2016-06-01 10:32:10 +02:00
|
|
|
|
|
|
|
case CHALLENGE_WAYPOINTS:
|
|
|
|
updateWaypointChallenge(c);
|
|
|
|
break;
|
2016-07-29 10:56:27 +02:00
|
|
|
|
|
|
|
case CHALLENGE_RESCUE:
|
|
|
|
updateRescueChallenge(c);
|
|
|
|
break;
|
2016-04-03 15:04:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c->passed)
|
|
|
|
{
|
|
|
|
numPassed++;
|
2015-10-20 13:51:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-03-01 08:21:31 +01:00
|
|
|
if (dev.debug)
|
|
|
|
{
|
|
|
|
printStats();
|
|
|
|
}
|
2016-04-03 15:04:31 +02:00
|
|
|
|
|
|
|
return numPassed;
|
2016-03-01 08:21:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void printStats(void)
|
|
|
|
{
|
|
|
|
int i;
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-03-01 08:21:31 +01:00
|
|
|
for (i = 0 ; i < STAT_MAX ; i++)
|
|
|
|
{
|
|
|
|
if (battle.stats[i])
|
|
|
|
{
|
|
|
|
if (i != STAT_TIME)
|
|
|
|
{
|
|
|
|
printf("DEBUG: %s=%d\n", getLookupName("STAT_", i), battle.stats[i]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
printf("DEBUG: %s=%s\n", getLookupName("STAT_", i), timeToString(battle.stats[i], 0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-10-20 13:51:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void updateTimeChallenge(Challenge *c)
|
|
|
|
{
|
2016-03-04 23:59:16 +01:00
|
|
|
if (battle.stats[STAT_TIME] / FPS < c->value)
|
2015-10-20 13:51:49 +02:00
|
|
|
{
|
2016-03-04 23:59:16 +01:00
|
|
|
c->passed = 1;
|
2015-10-20 13:51:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-13 12:02:08 +02:00
|
|
|
static void updateSurvivalChallenge(Challenge *c)
|
|
|
|
{
|
|
|
|
if (battle.stats[STAT_TIME] / FPS >= c->value)
|
|
|
|
{
|
|
|
|
c->passed = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-20 13:51:49 +02:00
|
|
|
static void updateAccuracyChallenge(Challenge *c)
|
|
|
|
{
|
|
|
|
float percent;
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-03-03 10:12:08 +01:00
|
|
|
switch (c->type)
|
|
|
|
{
|
|
|
|
case CHALLENGE_SHOT_ACCURACY:
|
|
|
|
percent = battle.stats[STAT_SHOT_ACCURACY];
|
|
|
|
break;
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-03-03 10:12:08 +01:00
|
|
|
case CHALLENGE_ROCKET_ACCURACY:
|
|
|
|
percent = battle.stats[STAT_ROCKET_ACCURACY];
|
|
|
|
break;
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-03-03 10:12:08 +01:00
|
|
|
case CHALLENGE_MISSILE_ACCURACY:
|
|
|
|
percent = battle.stats[STAT_MISSILE_ACCURACY];
|
|
|
|
break;
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-03-03 10:12:08 +01:00
|
|
|
default:
|
|
|
|
percent = 0;
|
|
|
|
break;
|
|
|
|
}
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-02-27 17:16:21 +01:00
|
|
|
if (percent >= c->value)
|
2015-10-20 13:51:49 +02:00
|
|
|
{
|
|
|
|
c->passed = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void updateArmourChallenge(Challenge *c)
|
|
|
|
{
|
|
|
|
float percent;
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2015-10-20 13:51:49 +02:00
|
|
|
percent = player->health;
|
|
|
|
percent /= player->maxHealth;
|
|
|
|
percent *= 100;
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-02-27 17:16:21 +01:00
|
|
|
if (percent >= c->value)
|
2015-10-20 13:51:49 +02:00
|
|
|
{
|
|
|
|
c->passed = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void updateLossesChallenge(Challenge *c)
|
|
|
|
{
|
|
|
|
if (!c->passed)
|
|
|
|
{
|
2016-02-27 17:16:21 +01:00
|
|
|
c->passed = battle.stats[STAT_ALLIES_KILLED] <= c->value;
|
2015-10-20 13:51:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void updatePlayerKillsChallenge(Challenge *c)
|
|
|
|
{
|
|
|
|
if (!c->passed)
|
|
|
|
{
|
2016-02-27 17:16:21 +01:00
|
|
|
c->passed = battle.stats[STAT_ENEMIES_KILLED_PLAYER] >= c->value;
|
2015-10-20 13:51:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-24 17:00:06 +02:00
|
|
|
static void updateDisabledChallenge(Challenge *c)
|
|
|
|
{
|
|
|
|
if (!c->passed)
|
|
|
|
{
|
2016-02-27 17:16:21 +01:00
|
|
|
c->passed = battle.stats[STAT_ENEMIES_DISABLED] >= c->value;
|
2015-10-24 17:00:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-07 13:29:23 +01:00
|
|
|
static void updateItemsChallenge(Challenge *c)
|
|
|
|
{
|
|
|
|
if (!c->passed)
|
|
|
|
{
|
2016-05-15 09:19:26 +02:00
|
|
|
if (c->type == CHALLENGE_ITEMS)
|
|
|
|
{
|
|
|
|
c->passed = battle.stats[STAT_ITEMS_COLLECTED] + battle.stats[STAT_ITEMS_COLLECTED_PLAYER] >= c->value;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
c->passed = battle.stats[STAT_ITEMS_COLLECTED_PLAYER] >= c->value;
|
|
|
|
}
|
2016-03-07 13:29:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-25 13:25:13 +02:00
|
|
|
static void updateSurrenderChallenge(Challenge *c)
|
|
|
|
{
|
|
|
|
if (!c->passed)
|
|
|
|
{
|
|
|
|
c->passed = battle.stats[STAT_ENEMIES_SURRENDERED] >= c->value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-01 10:32:10 +02:00
|
|
|
static void updateWaypointChallenge(Challenge *c)
|
|
|
|
{
|
|
|
|
if (!c->passed)
|
|
|
|
{
|
|
|
|
c->passed = battle.stats[STAT_WAYPOINTS_VISITED] >= c->value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-29 10:56:27 +02:00
|
|
|
static void updateRescueChallenge(Challenge *c)
|
|
|
|
{
|
|
|
|
if (!c->passed)
|
|
|
|
{
|
|
|
|
c->passed = battle.stats[STAT_CIVILIANS_RESCUED] >= c->value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-20 13:51:49 +02:00
|
|
|
char *getChallengeDescription(Challenge *c)
|
|
|
|
{
|
2016-05-15 09:19:26 +02:00
|
|
|
if (c->type == CHALLENGE_TIME)
|
|
|
|
{
|
|
|
|
if (c->value <= 90)
|
|
|
|
{
|
|
|
|
return getFormattedChallengeDescription(challengeDescription[CHALLENGE_TIME], c->value);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return getFormattedChallengeDescription(_("Complete challenge in %s or less"), timeToString(c->value * FPS, 0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-27 17:16:21 +01:00
|
|
|
return getFormattedChallengeDescription(challengeDescription[c->type], c->value);
|
2015-10-20 13:51:49 +02:00
|
|
|
}
|
|
|
|
|
2016-02-27 17:16:21 +01:00
|
|
|
Challenge *getChallenge(Mission *mission, int type, int value)
|
2015-10-20 13:51:49 +02:00
|
|
|
{
|
2016-03-03 00:20:37 +01:00
|
|
|
int i;
|
2016-02-27 17:16:21 +01:00
|
|
|
Challenge *c;
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-03-03 00:20:37 +01:00
|
|
|
for (i = 0 ; i < MAX_CHALLENGES ; i++)
|
2015-10-20 13:51:49 +02:00
|
|
|
{
|
2016-03-03 00:20:37 +01:00
|
|
|
c = mission->challengeData.challenges[i];
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-02-27 17:16:21 +01:00
|
|
|
if (c->type == type && c->value == value)
|
2015-10-20 13:51:49 +02:00
|
|
|
{
|
2016-02-27 17:16:21 +01:00
|
|
|
return c;
|
2015-10-20 13:51:49 +02:00
|
|
|
}
|
|
|
|
}
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2015-10-20 13:51:49 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *getFormattedChallengeDescription(const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
memset(&descriptionBuffer, '\0', sizeof(descriptionBuffer));
|
|
|
|
|
|
|
|
va_start(args, format);
|
|
|
|
vsprintf(descriptionBuffer, format, args);
|
|
|
|
va_end(args);
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2015-10-20 13:51:49 +02:00
|
|
|
return descriptionBuffer;
|
|
|
|
}
|
2016-02-28 14:02:57 +01:00
|
|
|
|
2016-02-29 11:47:41 +01:00
|
|
|
void updateChallengeMissions(void)
|
|
|
|
{
|
2016-03-03 00:20:37 +01:00
|
|
|
int i;
|
2016-02-29 11:47:41 +01:00
|
|
|
Mission *m;
|
|
|
|
Challenge *c;
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-02-29 11:47:41 +01:00
|
|
|
for (m = game.challengeMissionHead.next ; m != NULL ; m = m->next)
|
|
|
|
{
|
|
|
|
m->totalChallenges = m->completedChallenges = 0;
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-03-03 00:20:37 +01:00
|
|
|
for (i = 0 ; i < MAX_CHALLENGES ; i++)
|
2016-02-29 11:47:41 +01:00
|
|
|
{
|
2016-03-03 08:44:04 +01:00
|
|
|
c = m->challengeData.challenges[i];
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-03-03 08:44:04 +01:00
|
|
|
if (c)
|
2016-02-29 11:47:41 +01:00
|
|
|
{
|
2016-03-03 08:44:04 +01:00
|
|
|
m->totalChallenges++;
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-03-03 08:44:04 +01:00
|
|
|
if (c->passed)
|
|
|
|
{
|
|
|
|
m->completedChallenges++;
|
|
|
|
}
|
2016-02-29 11:47:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-28 14:02:57 +01:00
|
|
|
static void completeChallenge(void)
|
|
|
|
{
|
|
|
|
if (battle.status == MS_IN_PROGRESS)
|
|
|
|
{
|
|
|
|
battle.status = MS_COMPLETE;
|
|
|
|
battle.missionFinishedTimer = FPS;
|
|
|
|
selectWidget("continue", "battleWon");
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-02-28 14:02:57 +01:00
|
|
|
game.stats[STAT_CHALLENGES_COMPLETED]++;
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-05-15 09:19:26 +02:00
|
|
|
player->flags |= EF_IMMORTAL;
|
|
|
|
|
2016-02-28 14:02:57 +01:00
|
|
|
retreatAllies();
|
2016-05-15 09:19:26 +02:00
|
|
|
|
2016-02-28 14:02:57 +01:00
|
|
|
retreatEnemies();
|
2016-05-15 09:19:26 +02:00
|
|
|
|
|
|
|
awardStatsTrophies();
|
2016-06-03 08:41:44 +02:00
|
|
|
|
|
|
|
awardCraftTrophy();
|
2016-02-28 14:02:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void failChallenge(void)
|
|
|
|
{
|
|
|
|
if (battle.status == MS_IN_PROGRESS)
|
|
|
|
{
|
|
|
|
battle.status = MS_FAILED;
|
|
|
|
battle.missionFinishedTimer = FPS;
|
|
|
|
selectWidget("retry", "battleLost");
|
2016-03-04 15:29:50 +01:00
|
|
|
|
2016-02-28 14:02:57 +01:00
|
|
|
player->flags |= EF_IMMORTAL;
|
2016-03-05 16:34:37 +01:00
|
|
|
|
|
|
|
if (alreadyPassed())
|
|
|
|
{
|
|
|
|
battle.status = MS_TIME_UP;
|
|
|
|
}
|
2016-05-15 09:19:26 +02:00
|
|
|
|
|
|
|
retreatAllies();
|
|
|
|
|
|
|
|
retreatEnemies();
|
|
|
|
|
|
|
|
awardStatsTrophies();
|
2016-02-28 14:02:57 +01:00
|
|
|
}
|
|
|
|
}
|
2016-03-05 16:34:37 +01:00
|
|
|
|
|
|
|
static int alreadyPassed(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
Challenge *c;
|
|
|
|
|
|
|
|
for (i = 0 ; i < MAX_CHALLENGES ; i++)
|
|
|
|
{
|
|
|
|
c = game.currentMission->challengeData.challenges[i];
|
|
|
|
|
|
|
|
if (c && c->passed)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|