tbftss/src/challenges/challenges.c

413 lines
8.8 KiB
C
Raw Normal View History

2015-10-20 13:51:49 +02:00
/*
2016-02-21 16:50:27 +01:00
Copyright (C) 2015-2016 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);
static void updateAccuracyChallenge(Challenge *c);
static void updateArmourChallenge(Challenge *c);
static void updateLossesChallenge(Challenge *c);
static void updatePlayerKillsChallenge(Challenge *c);
static void updateDisabledChallenge(Challenge *c);
2016-03-07 13:29:23 +01:00
static void updateItemsChallenge(Challenge *c);
2016-02-28 14:02:57 +01:00
static void completeChallenge(void);
static void failChallenge(void);
static void 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);
static int alreadyPassed(void);
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-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");
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-02-27 13:14:29 +01:00
tail = &game.challengeMissionHead;
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-02-27 13:14:29 +01:00
mission = loadMissionMeta(path);
tail->next = mission;
tail = mission;
2016-02-27 13:14:29 +01:00
free(filenames[i]);
}
2016-02-27 13:14:29 +01:00
free(filenames);
}
2016-02-28 14:02:57 +01:00
void doChallenges(void)
{
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
{
if (battle.stats[STAT_TIME] >= game.currentMission->challengeData.timeLimit)
2016-03-03 15:19:29 +01:00
{
failChallenge();
}
else
{
updateChallenges();
2016-03-03 15:19:29 +01:00
completeChallenge();
}
2016-02-28 14:02:57 +01:00
}
2016-03-04 23:59:16 +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 */
if (game.currentMission->challengeData.killLimit > 0 && (battle.stats[STAT_ENEMIES_KILLED_PLAYER] + battle.stats[STAT_ENEMIES_DISABLED]) >= game.currentMission->challengeData.killLimit)
{
return 1;
}
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;
}
if (game.currentMission->challengeData.scriptedEnd)
2016-03-04 23:59:16 +01:00
{
return 1;
}
return 0;
2016-03-03 15:19:29 +01:00
}
2016-02-28 14:02:57 +01:00
static void updateChallenges(void)
2015-10-20 13:51:49 +02:00
{
int i;
2015-10-20 13:51:49 +02:00
Challenge *c;
updateAccuracyStats(battle.stats);
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];
if (c && !c->passed)
2015-10-20 13:51:49 +02:00
{
switch (c->type)
{
case CHALLENGE_TIME:
updateTimeChallenge(c);
break;
case CHALLENGE_SHOT_ACCURACY:
case CHALLENGE_ROCKET_ACCURACY:
case CHALLENGE_MISSILE_ACCURACY:
2015-10-20 13:51:49 +02:00
updateAccuracyChallenge(c);
break;
2015-10-20 13:51:49 +02:00
case CHALLENGE_ARMOUR:
updateArmourChallenge(c);
break;
2015-10-20 13:51:49 +02:00
case CHALLENGE_NO_LOSSES:
case CHALLENGE_1_LOSS:
case CHALLENGE_LOSSES:
updateLossesChallenge(c);
break;
2015-10-20 13:51:49 +02:00
case CHALLENGE_PLAYER_KILLS:
updatePlayerKillsChallenge(c);
break;
case CHALLENGE_DISABLE:
updateDisabledChallenge(c);
break;
2016-03-07 13:29:23 +01:00
case CHALLENGE_ITEMS:
updateItemsChallenge(c);
break;
2015-10-20 13:51:49 +02:00
}
}
}
if (dev.debug)
{
printStats();
}
}
static void printStats(void)
{
int i;
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
}
}
static void updateAccuracyChallenge(Challenge *c)
{
float percent;
switch (c->type)
{
case CHALLENGE_SHOT_ACCURACY:
percent = battle.stats[STAT_SHOT_ACCURACY];
break;
case CHALLENGE_ROCKET_ACCURACY:
percent = battle.stats[STAT_ROCKET_ACCURACY];
break;
case CHALLENGE_MISSILE_ACCURACY:
percent = battle.stats[STAT_MISSILE_ACCURACY];
break;
default:
percent = 0;
break;
}
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;
2015-10-20 13:51:49 +02:00
percent = player->health;
percent /= player->maxHealth;
percent *= 100;
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
}
}
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;
}
}
2016-03-07 13:29:23 +01:00
static void updateItemsChallenge(Challenge *c)
{
if (!c->passed)
{
c->passed = battle.stats[STAT_ITEMS_COLLECTED] + battle.stats[STAT_ITEMS_COLLECTED_PLAYER] >= c->value;
}
}
2015-10-20 13:51:49 +02:00
char *getChallengeDescription(Challenge *c)
{
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
{
int i;
2016-02-27 17:16:21 +01:00
Challenge *c;
for (i = 0 ; i < MAX_CHALLENGES ; i++)
2015-10-20 13:51:49 +02:00
{
c = mission->challengeData.challenges[i];
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
}
}
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);
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)
{
int i;
2016-02-29 11:47:41 +01:00
Mission *m;
Challenge *c;
2016-02-29 11:47:41 +01:00
for (m = game.challengeMissionHead.next ; m != NULL ; m = m->next)
{
m->totalChallenges = m->completedChallenges = 0;
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-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-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-02-28 14:02:57 +01:00
game.stats[STAT_CHALLENGES_COMPLETED]++;
2016-02-28 14:02:57 +01:00
retreatAllies();
2016-02-28 14:02:57 +01:00
retreatEnemies();
2016-02-28 14:02:57 +01:00
player->flags |= EF_IMMORTAL;
}
}
static void failChallenge(void)
{
if (battle.status == MS_IN_PROGRESS)
{
battle.status = MS_FAILED;
battle.missionFinishedTimer = FPS;
selectWidget("retry", "battleLost");
2016-02-28 14:02:57 +01:00
retreatAllies();
2016-02-28 14:02:57 +01:00
retreatEnemies();
2016-02-28 14:02:57 +01:00
player->flags |= EF_IMMORTAL;
if (alreadyPassed())
{
battle.status = MS_TIME_UP;
}
2016-02-28 14:02:57 +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;
}