tbftss/src/battle/battle.c

580 lines
11 KiB
C
Raw Normal View History

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 "battle.h"
static void logic(void);
static void draw(void);
static void handleKeyboard(void);
static void postBattle(void);
void destroyBattle(void);
static void doBattle(void);
static void optQuitBattle(void);
2015-10-20 13:51:49 +02:00
static void quitBattle(void);
static void drawMenu(void);
static void continueGame(void);
static void resume(void);
static void restart(void);
2015-10-20 13:51:49 +02:00
static void retry(void);
static void start(void);
static void options(void);
static void returnFromOptions(void);
static void checkSuspicionLevel(void);
2016-05-30 12:58:16 +02:00
static void doTorelliFireStorm(void);
static void endCampaign(void);
2015-10-20 13:51:49 +02:00
static int show;
static float ssx, ssy;
2015-10-20 13:51:49 +02:00
void initBattle(void)
{
memset(&battle, 0, sizeof(Battle));
battle.bulletTail = &battle.bulletHead;
2015-12-13 15:50:54 +01:00
battle.debrisTail = &battle.debrisHead;
2015-10-26 18:27:43 +01:00
battle.entityTail = &battle.entityHead;
2015-10-20 13:51:49 +02:00
battle.effectTail = &battle.effectHead;
battle.objectiveTail = &battle.objectiveHead;
battle.locationTail = &battle.locationHead;
2016-03-27 12:21:23 +02:00
battle.spawnerTail = &battle.spawnerHead;
2015-10-20 13:51:49 +02:00
app.delegate.logic = &logic;
app.delegate.draw = &draw;
memset(&app.keyboard, 0, sizeof(int) * MAX_KEYBOARD_KEYS);
2016-05-15 18:28:17 +02:00
battle.hasThreats = 1;
2016-02-21 08:48:22 +01:00
initQuadtree(&battle.quadtree);
2015-12-11 20:06:16 +01:00
initBullets();
initEntities();
2015-10-20 13:51:49 +02:00
initStars();
initEffects();
2015-10-20 13:51:49 +02:00
initHud();
initRadar();
initMessageBox();
2015-12-13 15:50:54 +01:00
initDebris();
resetWaypoints();
2015-10-20 13:51:49 +02:00
show = SHOW_BATTLE;
2015-10-20 13:51:49 +02:00
getWidget("ok", "startBattle")->action = start;
2015-10-20 13:51:49 +02:00
getWidget("resume", "inBattle")->action = resume;
getWidget("options", "inBattle")->action = options;
getWidget("restart", "inBattle")->action = restart;
getWidget("quit", "inBattle")->action = optQuitBattle;
2015-10-20 13:51:49 +02:00
getWidget("continue", "battleWon")->action = continueGame;
getWidget("retry", "battleWon")->action = retry;
2015-10-20 13:51:49 +02:00
getWidget("retry", "battleLost")->action = retry;
getWidget("quit", "battleLost")->action = quitBattle;
2015-10-20 13:51:49 +02:00
selectWidget("ok", "startBattle");
SDL_SetWindowGrab(app.window, 1);
2015-10-20 13:51:49 +02:00
}
static void logic(void)
{
if (battle.status == MS_IN_PROGRESS || battle.status == MS_COMPLETE || battle.status == MS_FAILED || battle.status == MS_TIME_UP)
2015-10-20 13:51:49 +02:00
{
handleKeyboard();
2015-10-20 13:51:49 +02:00
if (show == SHOW_BATTLE)
{
2016-02-28 14:02:57 +01:00
if (!battle.isEpic || (battle.isEpic && !battle.playerSelect))
{
doBattle();
}
2016-02-28 14:02:57 +01:00
else if (battle.isEpic && battle.playerSelect)
{
doPlayerSelect();
}
2015-10-20 13:51:49 +02:00
}
2016-05-15 09:19:26 +02:00
app.doTrophyAlerts = (battle.status != MS_IN_PROGRESS && battle.missionFinishedTimer <= -FPS * 2);
if (battle.campaignFinished)
{
endCampaign();
}
2015-10-20 13:51:49 +02:00
}
2015-10-20 13:51:49 +02:00
doWidgets();
}
static void doBattle(void)
{
2016-05-15 09:19:26 +02:00
if (player->alive == ALIVE_ALIVE)
2015-10-20 13:51:49 +02:00
{
ssx = player->dx;
ssy = player->dy;
2015-10-20 13:51:49 +02:00
}
else
{
ssx = ssy = 0;
2015-10-20 13:51:49 +02:00
}
scrollBackground(-ssx * 0.1, -ssy * 0.1);
battle.planet.x -= ssx * 0.15;
battle.planet.y -= ssy * 0.15;
2016-03-09 12:51:26 +01:00
doObjectives();
2016-03-09 12:51:26 +01:00
2016-02-28 14:02:57 +01:00
doChallenges();
doHud();
doStars(ssx, ssy);
2015-10-20 13:51:49 +02:00
doBullets();
2015-10-26 18:27:43 +01:00
doEntities();
2015-10-20 13:51:49 +02:00
doEffects();
2015-12-13 15:50:54 +01:00
doDebris();
2015-10-20 13:51:49 +02:00
doPlayer();
checkSuspicionLevel();
2016-05-30 12:58:16 +02:00
doTorelliFireStorm();
2016-05-15 09:19:26 +02:00
if (player->alive == ALIVE_ALIVE)
{
2016-03-27 12:21:23 +02:00
doSpawners();
doLocations();
doMessageBox();
if (battle.status == MS_IN_PROGRESS || battle.status == MS_COMPLETE)
{
doScript();
}
if (battle.status == MS_IN_PROGRESS)
{
2016-03-12 19:22:48 +01:00
if (battle.stats[STAT_TIME]++ % FPS == 0)
2016-02-27 13:14:29 +01:00
{
2016-03-27 12:21:23 +02:00
runScriptFunction("TIME %d", battle.stats[STAT_TIME] / FPS);
2016-04-03 15:04:56 +02:00
if (game.currentMission->challengeData.timeLimit && game.currentMission->challengeData.timeLimit - battle.stats[STAT_TIME] < 11 * FPS)
{
playSound(SND_TIME_WARNING);
}
2016-02-27 13:14:29 +01:00
}
}
}
if (battle.status != MS_IN_PROGRESS)
2015-10-20 13:51:49 +02:00
{
battle.missionFinishedTimer--;
2016-05-15 09:19:26 +02:00
if (battle.unwinnable && battle.missionFinishedTimer <= -FPS * 6)
{
battle.status = MS_COMPLETE;
2016-05-15 09:19:26 +02:00
postBattle();
2016-05-15 09:19:26 +02:00
destroyBattle();
2016-05-15 09:19:26 +02:00
initGalacticMap();
}
}
2015-10-20 13:51:49 +02:00
}
static void draw(void)
{
2016-05-15 09:19:26 +02:00
if (player->alive == ALIVE_ALIVE)
{
2018-12-14 09:00:16 +01:00
battle.camera.x = player->x - (app.winWidth / 2);
battle.camera.y = player->y - (app.winHeight / 2);
}
2015-10-20 13:51:49 +02:00
drawBackground(battle.background);
2018-12-06 17:52:13 +01:00
setAtlasColor(255, 255, 255, 255);
blitScaled(battle.planetTexture, battle.planet.x, battle.planet.y, battle.planetWidth, battle.planetHeight, 0);
2016-05-30 12:58:16 +02:00
if (battle.destroyTorelli)
{
SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND);
2018-12-06 09:37:19 +01:00
SDL_SetTextureAlphaMod(battle.fireStormTexture->texture, battle.torelliFireStormAlpha);
blitScaled(battle.fireStormTexture, battle.planet.x, battle.planet.y, battle.planetWidth, battle.planetHeight, 0);
2016-05-30 12:58:16 +02:00
SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE);
}
drawStars();
2015-10-26 18:27:43 +01:00
drawEntities();
drawBullets();
2015-12-13 15:50:54 +01:00
drawDebris();
2015-10-20 13:51:49 +02:00
drawEffects();
if (dev.debug)
{
drawLocations();
}
2015-10-20 13:51:49 +02:00
drawHud();
2016-05-15 09:19:26 +02:00
if (player->alive == ALIVE_ALIVE)
{
drawMessageBox();
}
2015-10-20 13:51:49 +02:00
drawMissionInfo();
2015-10-20 13:51:49 +02:00
switch (show)
{
case SHOW_MENU:
drawMenu();
break;
2015-10-20 13:51:49 +02:00
case SHOW_OPTIONS:
drawOptions();
break;
}
}
static void drawMenu(void)
{
SDL_Rect r;
2018-12-11 09:24:25 +01:00
if (app.modalDialog.type == MD_NONE)
{
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);
2018-12-17 09:30:47 +01:00
SDL_SetRenderTarget(app.renderer, app.uiBuffer);
2018-12-11 09:24:25 +01:00
r.w = 400;
r.h = 400;
2018-12-13 08:59:31 +01:00
r.x = (UI_WIDTH / 2) - r.w / 2;
r.y = (UI_HEIGHT / 2) - r.h / 2;
2018-12-11 09:24:25 +01:00
SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 255);
SDL_RenderFillRect(app.renderer, &r);
SDL_SetRenderDrawColor(app.renderer, 200, 200, 200, 255);
SDL_RenderDrawRect(app.renderer, &r);
2018-12-11 09:24:25 +01:00
drawWidgets("inBattle");
2018-12-13 08:59:31 +01:00
SDL_SetRenderTarget(app.renderer, app.backBuffer);
2018-12-11 09:24:25 +01:00
}
2015-10-20 13:51:49 +02:00
}
static void handleKeyboard(void)
{
if (app.keyboard[SDL_SCANCODE_ESCAPE] && !app.awaitingWidgetInput && battle.status == MS_IN_PROGRESS)
2015-10-20 13:51:49 +02:00
{
switch (show)
{
case SHOW_BATTLE:
case SHOW_OPTIONS:
selectWidget("resume", "inBattle");
show = SHOW_MENU;
2016-08-11 23:47:51 +02:00
SDL_SetWindowGrab(app.window, 0);
2015-10-20 13:51:49 +02:00
break;
2015-10-20 13:51:49 +02:00
case SHOW_MENU:
show = SHOW_BATTLE;
2016-08-11 23:47:51 +02:00
SDL_SetWindowGrab(app.window, 1);
2015-10-20 13:51:49 +02:00
break;
2015-10-20 13:51:49 +02:00
case SHOW_OBJECTIVES:
show = SHOW_BATTLE;
2016-08-11 23:47:51 +02:00
SDL_SetWindowGrab(app.window, 1);
2015-10-20 13:51:49 +02:00
break;
}
clearInput();
2015-10-20 13:51:49 +02:00
playSound(SND_GUI_CLOSE);
}
if (battle.status == MS_IN_PROGRESS && app.keyboard[SDL_SCANCODE_TAB])
2015-10-20 13:51:49 +02:00
{
battle.status = MS_PAUSED;
selectWidget("ok", "startBattle");
2016-08-11 23:47:51 +02:00
SDL_SetWindowGrab(app.window, 0);
2015-10-20 13:51:49 +02:00
}
}
static void start(void)
{
battle.status = MS_IN_PROGRESS;
2016-08-11 23:47:51 +02:00
SDL_SetWindowGrab(app.window, 1);
2015-10-20 13:51:49 +02:00
}
static void resume(void)
{
show = SHOW_BATTLE;
2016-08-11 23:47:51 +02:00
SDL_SetWindowGrab(app.window, 1);
clearInput();
2015-10-20 13:51:49 +02:00
}
static void continueGame(void)
{
postBattle();
2015-10-20 13:51:49 +02:00
destroyBattle();
if (!game.currentMission->challengeData.isChallenge)
2016-02-27 17:16:21 +01:00
{
initGalacticMap();
}
else
{
initChallengeHome();
}
2015-10-20 13:51:49 +02:00
}
static void options(void)
{
show = SHOW_OPTIONS;
2015-10-20 13:51:49 +02:00
initOptions(returnFromOptions);
}
static void returnFromOptions(void)
{
show = SHOW_MENU;
2015-10-20 13:51:49 +02:00
selectWidget("resume", "inBattle");
}
static void ignoreRestartQuit(void)
{
app.modalDialog.type = MD_NONE;
}
static void restart(void)
{
showOKCancelDialog(&retry, &ignoreRestartQuit, _("Are you sure you want to restart? You will lose your current progress."));
}
2015-10-20 13:51:49 +02:00
static void retry(void)
{
app.modalDialog.type = MD_NONE;
2015-10-20 13:51:49 +02:00
postBattle();
2015-10-20 13:51:49 +02:00
destroyBattle();
2015-10-20 13:51:49 +02:00
initBattle();
2015-10-20 13:51:49 +02:00
loadMission(game.currentMission->filename);
}
static void optQuitBattle(void)
{
showOKCancelDialog(&quitBattle, &ignoreRestartQuit, _("Are you sure you want to quit? You will lose your current progress."));
}
2015-10-20 13:51:49 +02:00
static void quitBattle(void)
{
app.modalDialog.type = MD_NONE;
2015-10-20 13:51:49 +02:00
postBattle();
2015-10-20 13:51:49 +02:00
destroyBattle();
if (!game.currentMission->challengeData.isChallenge)
2016-02-27 17:16:21 +01:00
{
initGalacticMap();
}
else
{
initChallengeHome();
}
2015-10-20 13:51:49 +02:00
}
static void postBattle(void)
{
int i;
2015-11-17 08:23:50 +01:00
for (i = 0 ; i < STAT_MAX ; i++)
{
2015-11-17 08:23:50 +01:00
if (i != STAT_TIME && i != STAT_EPIC_KILL_STREAK)
{
game.stats[i] += battle.stats[i];
}
}
2016-05-15 09:19:26 +02:00
game.stats[STAT_EPIC_KILL_STREAK] = MAX(game.stats[STAT_EPIC_KILL_STREAK], battle.stats[STAT_EPIC_KILL_STREAK]);
2016-03-09 12:51:26 +01:00
updateAccuracyStats(game.stats);
2016-05-15 09:19:26 +02:00
game.currentMission->completed = (game.currentMission->completed || battle.status == MS_COMPLETE || !battle.numObjectivesTotal);
2016-05-15 09:19:26 +02:00
app.saveGame = 1;
2015-10-20 13:51:49 +02:00
}
static void checkSuspicionLevel(void)
{
if (battle.hasSuspicionLevel && battle.suspicionLevel >= MAX_SUSPICION_LEVEL)
{
cancelScript();
resetMessageBox();
runScriptFunction("MAX_SUSPICION_LEVEL");
battle.hasSuspicionLevel = 0;
}
}
2016-05-30 12:58:16 +02:00
static void doTorelliFireStorm(void)
{
if (battle.destroyTorelli)
{
battle.torelliFireStormAlpha = MIN(battle.torelliFireStormAlpha + 0.25, 255);
}
}
static void endCampaign(void)
{
awardTrophy("CAMPAIGN_COMPLETE");
postBattle();
destroyBattle();
initCredits();
}
2015-10-20 13:51:49 +02:00
void destroyBattle(void)
{
2015-10-26 18:27:43 +01:00
Entity *ent;
2015-10-20 13:51:49 +02:00
Bullet *b;
2015-12-13 15:50:54 +01:00
Debris *d;
2015-10-20 13:51:49 +02:00
Effect *e;
Objective *o;
Location *l;
2016-03-27 12:21:23 +02:00
Spawner *s;
2015-10-26 18:27:43 +01:00
while (battle.entityHead.next)
{
ent = battle.entityHead.next;
battle.entityHead.next = ent->next;
free(ent);
}
battle.entityTail = &battle.entityHead;
2015-10-20 13:51:49 +02:00
while (battle.bulletHead.next)
{
b = battle.bulletHead.next;
battle.bulletHead.next = b->next;
free(b);
}
battle.bulletTail = &battle.bulletHead;
2015-12-13 15:50:54 +01:00
while (battle.debrisHead.next)
{
d = battle.debrisHead.next;
battle.debrisHead.next = d->next;
free(d);
}
battle.debrisTail = &battle.debrisHead;
2015-10-20 13:51:49 +02:00
while (battle.effectHead.next)
{
e = battle.effectHead.next;
battle.effectHead.next = e->next;
free(e);
}
battle.effectTail = &battle.effectHead;
2015-10-20 13:51:49 +02:00
while (battle.objectiveHead.next)
{
o = battle.objectiveHead.next;
battle.objectiveHead.next = o->next;
free(o);
}
battle.objectiveTail = &battle.objectiveHead;
while (battle.locationHead.next)
{
l = battle.locationHead.next;
battle.locationHead.next = l->next;
free(l);
}
battle.locationTail = &battle.locationHead;
2016-03-27 12:21:23 +02:00
while (battle.spawnerHead.next)
{
s = battle.spawnerHead.next;
battle.spawnerHead.next = s->next;
free(s);
}
battle.spawnerTail = &battle.spawnerHead;
cJSON_Delete(battle.missionJSON);
resetHud();
resetMessageBox();
destroyEntities();
destroyScript();
2016-02-21 08:48:22 +01:00
destroyQuadtree();
destroyDebris();
destroyBullets();
destroyEffects();
memset(&battle, 0, sizeof(Battle));
battle.bulletTail = &battle.bulletHead;
battle.debrisTail = &battle.debrisHead;
battle.entityTail = &battle.entityHead;
battle.effectTail = &battle.effectHead;
battle.objectiveTail = &battle.objectiveHead;
battle.locationTail = &battle.locationHead;
battle.spawnerTail = &battle.spawnerHead;
2015-10-20 13:51:49 +02:00
}