blobwarsAttrition/src/world/world.c

964 lines
17 KiB
C
Raw Normal View History

2018-01-26 20:14:18 +01:00
/*
Copyright (C) 2018 Parallel Realities
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 "world.h"
2018-01-27 11:58:47 +01:00
2018-02-03 00:01:51 +01:00
static void logic(void);
static void draw(void);
2018-02-02 20:10:12 +01:00
static void doWorldStart(void);
static void doWorldInProgress(void);
static void doWorldObserving(void);
static void doWorldPaused(void);
static void doWorldComplete(void);
static void doGameComplete(void);
static void doGameOver(void);
static void doCommon(void);
2018-02-07 23:49:42 +01:00
static void drawNormal(void);
2018-02-02 20:10:12 +01:00
static void addHelperItems(void);
static void spawnEnemies(void);
static int canAdd(Unit *u, int mx, int my);
static void startMission(void);
2018-02-15 19:04:37 +01:00
static void drawInGameWidgets(void);
2018-02-15 23:56:22 +01:00
static void handleWidgets(void);
static void resume(void);
static void options(void);
static void stats(void);
static void trophies(void);
2018-02-15 23:56:22 +01:00
static void quit(void);
2018-02-19 23:32:14 +01:00
static void returnFromTrophyStats(void);
2018-02-20 09:14:35 +01:00
static void drawQuit(void);
2018-02-25 13:12:31 +01:00
static void drawGameOver(void);
2018-02-23 20:26:13 +01:00
void quitMission(void);
2018-02-20 18:50:01 +01:00
static void returnFromOptions(void);
2018-02-23 20:26:13 +01:00
void autoCompleteMission(void);
2018-02-25 13:12:31 +01:00
static void retry(void);
static void hub(void);
static void title(void);
2018-02-02 20:10:12 +01:00
2018-02-02 09:00:27 +01:00
static Texture *background;
2018-02-25 13:12:31 +01:00
static Texture *atlasTexture;
static Atlas *missionFailed;
static int observationIndex;
2018-02-19 20:12:55 +01:00
static int showing;
static PointF observePos;
2018-02-02 09:00:27 +01:00
void initWorld(void)
{
2018-02-18 10:29:26 +01:00
startSectionTransition();
2018-02-17 17:53:18 +01:00
loadWorld(game.worldId);
2018-02-05 09:39:02 +01:00
2018-02-22 08:45:57 +01:00
world.currentStatus = getMissionStatus(game.worldId);
2018-02-18 12:53:01 +01:00
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG, "world.currentStatus = %d", world.currentStatus);
2018-02-02 09:00:27 +01:00
background = getTexture(world.background);
2018-02-02 20:10:12 +01:00
2018-02-25 13:12:31 +01:00
atlasTexture = getTexture("gfx/atlas/atlas.png");
missionFailed = getImageFromAtlas("gfx/main/missionFailed.png");
2018-02-08 08:34:35 +01:00
loadMusic(world.music);
2018-02-04 10:50:07 +01:00
initQuadtree(&world.quadtree);
2018-02-02 20:10:12 +01:00
initObjectives();
2018-02-04 08:50:54 +01:00
2018-02-04 17:14:14 +01:00
initParticles();
2018-02-04 08:50:54 +01:00
initHud();
2018-02-04 10:50:07 +01:00
initWeapons();
2018-02-05 09:39:02 +01:00
initEffects();
2018-02-02 20:10:12 +01:00
2018-02-05 23:06:29 +01:00
initItems();
2018-02-17 17:53:18 +01:00
initMap();
initEntities();
2018-02-18 10:29:26 +01:00
addKeysFromStash();
2018-02-17 17:53:18 +01:00
2018-02-02 20:10:12 +01:00
world.enemySpawnTimer = (FPS * rrnd(world.minEnemySpawnTime, world.maxEnemySpawnTime));
world.state = WS_START;
observationIndex = 0;
2018-02-15 23:56:22 +01:00
getWidget("resume", "gamePaused")->action = resume;
getWidget("options", "gamePaused")->action = options;
getWidget("stats", "gamePaused")->action = stats;
getWidget("trophies", "gamePaused")->action = trophies;
2018-02-15 23:56:22 +01:00
getWidget("quit", "gamePaused")->action = quit;
2018-02-20 09:14:35 +01:00
getWidget("ok", "stats")->action = returnFromTrophyStats;
getWidget("ok", "trophies")->action = returnFromTrophyStats;
getWidget("ok", "gameQuit")->action = quitMission;
getWidget("cancel", "gameQuit")->action = returnFromTrophyStats;
2018-02-25 13:12:31 +01:00
getWidget("retry", "gameOver")->action = retry;
getWidget("hub", "gameOver")->action = hub;
getWidget("title", "gameOver")->action = title;
if (world.missionType == MT_BOSS)
2018-02-02 20:10:12 +01:00
{
startMission();
hideAllWidgets();
world.betweenTimer = 0;
}
2018-02-08 08:34:35 +01:00
else
{
world.bob->flags |= EF_GONE;
2018-02-08 08:34:35 +01:00
playMusic(1);
}
2018-02-03 00:01:51 +01:00
2018-02-19 09:28:42 +01:00
game.stats[STAT_MISSIONS_PLAYED]++;
2018-03-15 09:01:04 +01:00
app.delegate.logic = &logic;
app.delegate.draw = &draw;
2018-02-18 10:29:26 +01:00
app.restrictTrophyAlert = 1;
2018-02-18 10:29:26 +01:00
endSectionTransition();
/*
startMission();
world.bob->x = 17 * MAP_TILE_SIZE;
world.bob->y = 89 * MAP_TILE_SIZE;
*/
2018-02-02 09:00:27 +01:00
}
2018-02-03 00:01:51 +01:00
static void logic(void)
2018-02-02 09:00:27 +01:00
{
2018-02-03 00:01:51 +01:00
if (--world.betweenTimer <= 0)
2018-02-02 20:10:12 +01:00
{
2018-02-03 00:01:51 +01:00
world.betweenTimer = 0;
world.saveDelay = limit(world.saveDelay - 1, 0, FPS);
2018-02-02 20:10:12 +01:00
switch (world.state)
{
case WS_START:
doWorldStart();
break;
2018-02-20 09:14:35 +01:00
2018-02-02 20:10:12 +01:00
case WS_IN_PROGRESS:
doWorldInProgress();
break;
2018-02-20 09:14:35 +01:00
2018-02-02 20:10:12 +01:00
case WS_OBSERVING:
doWorldObserving();
break;
2018-02-20 09:14:35 +01:00
2018-02-02 20:10:12 +01:00
case WS_PAUSED:
doWorldPaused();
break;
2018-02-20 09:14:35 +01:00
2018-02-02 20:10:12 +01:00
case WS_COMPLETE:
case WS_QUIT:
2018-02-02 20:10:12 +01:00
doWorldComplete();
break;
2018-02-20 09:14:35 +01:00
2018-02-02 20:10:12 +01:00
case WS_GAME_OVER:
doGameOver();
break;
2018-02-20 09:14:35 +01:00
2018-02-02 20:10:12 +01:00
case WS_GAME_COMPLETE:
doGameComplete();
break;
2018-02-20 09:14:35 +01:00
2018-02-02 20:10:12 +01:00
default:
break;
}
}
2018-02-02 09:00:27 +01:00
if (--world.mapAnimTimer < 0)
{
world.mapAnimTimer = 4;
}
}
2018-02-03 00:01:51 +01:00
static void draw(void)
{
clearScreen();
2018-02-07 23:49:42 +01:00
switch (world.state)
2018-02-03 00:01:51 +01:00
{
2018-02-07 23:49:42 +01:00
case WS_PAUSED:
drawNormal();
drawMissionStatus();
break;
case WS_START:
drawNormal();
drawMissionStatus();
drawText(SCREEN_WIDTH / 2, SCREEN_HEIGHT - 80, 24, TA_CENTER, colors.white, _("Press Fire to Continue"));
break;
2018-02-25 13:12:31 +01:00
case WS_GAME_OVER:
drawNormal();
drawGameOver();
break;
2018-02-07 23:49:42 +01:00
default:
if (world.betweenTimer == 0)
{
drawNormal();
drawHud();
}
break;
2018-02-03 00:01:51 +01:00
}
2018-02-15 19:04:37 +01:00
2018-02-19 20:12:55 +01:00
switch (showing)
2018-02-15 19:04:37 +01:00
{
2018-02-19 20:12:55 +01:00
case SHOW_WIDGETS:
drawInGameWidgets();
break;
case SHOW_STATS:
drawStats();
break;
2018-02-19 23:32:14 +01:00
case SHOW_TROPHIES:
drawTrophies();
break;
2018-02-20 09:14:35 +01:00
case SHOW_QUIT:
drawQuit();
break;
2018-02-15 19:04:37 +01:00
}
}
static void drawInGameWidgets(void)
{
2018-02-19 20:12:55 +01:00
drawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0, 128);
2018-02-15 19:04:37 +01:00
2018-02-25 13:12:31 +01:00
drawWidgetFrame();
2018-02-15 19:04:37 +01:00
drawWidgets();
2018-02-03 00:01:51 +01:00
}
2018-02-07 23:49:42 +01:00
static void drawNormal(void)
{
blitScaled(background->texture, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0);
drawEntities(PLANE_BACKGROUND);
drawParticles(PLANE_BACKGROUND);
drawMap();
drawEntities(PLANE_FOREGROUND);
drawParticles(PLANE_FOREGROUND);
}
static void startMission(void)
2018-02-02 20:10:12 +01:00
{
2018-02-04 17:14:14 +01:00
Entity *self;
SDL_Rect *r;
self = (Entity*)world.bob;
2018-02-02 20:10:12 +01:00
world.state = WS_IN_PROGRESS;
world.betweenTimer = FPS / 2;
2018-02-04 17:14:14 +01:00
r = &self->sprite[self->facing]->frames[self->spriteFrame]->rect;
2018-02-04 17:14:14 +01:00
self->w = r->w;
self->h = r->h;
2018-02-02 20:10:12 +01:00
resetAtCheckpoint();
2018-02-04 17:14:14 +01:00
world.entityToTrack = self;
self->flags &= ~EF_GONE;
2018-02-02 20:10:12 +01:00
}
static void doWorldStart(void)
{
2018-02-03 00:01:51 +01:00
float dist;
if (world.entityToTrack != NULL)
{
dist = cameraChase(world.entityToTrack, 5);
if ((dist <= world.entityToTrack->w && dist <= world.entityToTrack->h) || world.entityChaseTimer <= 0)
{
world.entityToTrack = getRandomObjectiveEntity();
world.entityChaseTimer = FPS * 5;
}
}
else
2018-02-02 20:10:12 +01:00
{
world.entityToTrack = getRandomObjectiveEntity();
cameraTrack(world.entityToTrack);
}
world.entityChaseTimer = MAX(world.entityChaseTimer - 1, 0);
2018-02-03 00:01:51 +01:00
2018-02-02 20:10:12 +01:00
doCommon();
if (isAcceptControl())
{
clearControls();
startMission();
}
2018-02-02 20:10:12 +01:00
}
static void doWorldInProgress(void)
{
2018-02-03 00:01:51 +01:00
cameraTrack(world.entityToTrack);
2018-02-05 23:06:29 +01:00
doPlayer();
2018-02-04 10:50:07 +01:00
2018-02-19 20:12:55 +01:00
if (showing == SHOW_NONE)
2018-02-02 20:10:12 +01:00
{
doBob();
doCommon();
doLocationTriggers();
if (world.allObjectivesComplete && world.state != WS_COMPLETE)
{
world.bob->flags |= EF_IMMUNE;
2018-02-25 13:12:31 +01:00
if (world.bob->stunTimer > 0)
{
world.bob->stunTimer = 0;
}
2018-02-02 20:10:12 +01:00
if (strcmp(world.id, "teeka") == 0)
{
world.state = WS_GAME_COMPLETE;
}
else
{
world.state = WS_COMPLETE;
}
world.missionCompleteTimer = FPS * 3;
if (world.state != WS_GAME_COMPLETE)
{
stopMusic();
}
2018-02-02 20:10:12 +01:00
}
if (isControl(CONTROL_PAUSE))
{
world.state = WS_PAUSED;
pauseSound(1);
clearControl(CONTROL_PAUSE);
}
if (isControl(CONTROL_MAP))
{
pauseSound(1);
initRadar();
clearControl(CONTROL_MAP);
}
2018-02-15 23:56:22 +01:00
if (app.keyboard[SDL_SCANCODE_ESCAPE])
2018-02-02 20:10:12 +01:00
{
2018-02-15 23:56:22 +01:00
app.keyboard[SDL_SCANCODE_ESCAPE] = 0;
showWidgetGroup("gamePaused");
playSound(SND_MENU_BACK, 0);
2018-02-19 20:12:55 +01:00
showing = SHOW_WIDGETS;
2018-02-15 23:56:22 +01:00
}
if (world.observationTimer > 0)
{
2018-03-02 23:21:45 +01:00
if (--world.observationTimer == FPS)
2018-02-15 23:56:22 +01:00
{
world.entityToTrack = world.entitiesToObserve[0];
observePos.x = camera.x;
observePos.y = camera.y;
2018-02-15 23:56:22 +01:00
world.state = WS_OBSERVING;
pauseSound(1);
2018-02-15 23:56:22 +01:00
}
2018-02-02 20:10:12 +01:00
}
}
2018-02-19 20:12:55 +01:00
else if (showing == SHOW_WIDGETS)
2018-02-15 23:56:22 +01:00
{
handleWidgets();
}
2018-02-19 20:12:55 +01:00
else if (showing == SHOW_STATS)
{
doStats();
if (app.keyboard[SDL_SCANCODE_ESCAPE])
{
2018-02-20 18:50:01 +01:00
playSound(SND_MENU_BACK, 0);
2018-02-19 23:32:14 +01:00
returnFromTrophyStats();
}
}
else if (showing == SHOW_TROPHIES)
{
doTrophies();
2018-02-20 09:14:35 +01:00
if (app.keyboard[SDL_SCANCODE_ESCAPE])
{
2018-02-20 18:50:01 +01:00
playSound(SND_MENU_BACK, 0);
2018-02-20 09:14:35 +01:00
returnFromTrophyStats();
}
}
else if (showing == SHOW_QUIT)
{
handleWidgets();
2018-02-19 23:32:14 +01:00
if (app.keyboard[SDL_SCANCODE_ESCAPE])
{
returnFromTrophyStats();
2018-02-19 20:12:55 +01:00
}
}
2018-02-15 23:56:22 +01:00
}
static void handleWidgets(void)
{
doWidgets();
if (app.keyboard[SDL_SCANCODE_ESCAPE])
{
2018-02-20 18:50:01 +01:00
playSound(SND_MENU_BACK, 0);
2018-02-15 23:56:22 +01:00
resume();
}
2018-02-02 20:10:12 +01:00
}
static void doWorldObserving(void)
{
int tx, ty;
2018-03-02 23:21:45 +01:00
float diffX, diffY;
2018-02-02 20:10:12 +01:00
2018-03-02 23:21:45 +01:00
tx = world.entityToTrack->x - (SCREEN_WIDTH / 2);
ty = world.entityToTrack->y - (SCREEN_HEIGHT / 2);
doEntitiesStatic();
2018-03-02 23:21:45 +01:00
diffX = abs(camera.x - tx) / 20;
diffY = abs(camera.y - ty) / 20;
diffX = MAX(3, MIN(50, diffX));
diffY = MAX(3, MIN(50, diffY));
if (observePos.x > tx)
2018-02-02 20:10:12 +01:00
{
observePos.x -= diffX;
2018-03-02 23:21:45 +01:00
}
if (observePos.x < tx)
2018-03-02 23:21:45 +01:00
{
observePos.x += diffX;
2018-03-02 23:21:45 +01:00
}
if (observePos.y > ty)
2018-03-02 23:21:45 +01:00
{
observePos.y -= diffY;
2018-03-02 23:21:45 +01:00
}
if (observePos.y < ty)
2018-03-02 23:21:45 +01:00
{
observePos.y += diffY;
2018-03-02 23:21:45 +01:00
}
2018-03-04 10:49:31 +01:00
camera.x = observePos.x;
camera.y = observePos.y;
2018-03-04 10:49:31 +01:00
clipCamera();
2018-03-02 23:21:45 +01:00
if (collision(observePos.x, observePos.y, MAP_TILE_SIZE, MAP_TILE_SIZE, tx, ty, MAP_TILE_SIZE, MAP_TILE_SIZE))
2018-03-02 23:21:45 +01:00
{
if (--world.observationTimer <= 0)
{
2018-03-02 23:21:45 +01:00
if (++observationIndex < MAX_ENTS_TO_OBSERVE && world.entitiesToObserve[observationIndex] != NULL)
2018-02-03 09:44:02 +01:00
{
2018-03-02 23:21:45 +01:00
world.entityToTrack = world.entitiesToObserve[observationIndex];
world.observationTimer = FPS;
observePos.x = camera.x;
observePos.y = camera.y;
2018-02-03 09:44:02 +01:00
}
2018-03-02 23:21:45 +01:00
else
{
memset(world.entitiesToObserve, 0, sizeof(Entity*) * MAX_ENTS_TO_OBSERVE);
world.entityToTrack = (Entity*)world.bob;
world.state = WS_IN_PROGRESS;
observationIndex = 0;
pauseSound(0);
2018-03-02 23:21:45 +01:00
}
}
2018-02-02 20:10:12 +01:00
}
}
static void doWorldPaused(void)
{
animateSprites();
2018-02-07 23:49:42 +01:00
2018-02-08 22:53:32 +01:00
if (isControl(CONTROL_PAUSE))
2018-02-07 23:49:42 +01:00
{
pauseSound(0);
2018-02-07 23:49:42 +01:00
world.state = WS_IN_PROGRESS;
2018-02-08 22:53:32 +01:00
clearControl(CONTROL_PAUSE);
2018-02-07 23:49:42 +01:00
}
2018-02-02 20:10:12 +01:00
}
static void doWorldComplete(void)
{
world.missionCompleteTimer--;
if (world.missionCompleteTimer <= 0 && world.saveDelay <= 0)
2018-02-02 20:10:12 +01:00
{
dropCarriedItems();
2018-02-18 10:29:26 +01:00
initPostMission();
2018-02-02 20:10:12 +01:00
}
else if (world.missionCompleteTimer == FPS * 1.5)
{
world.bob->flags |= EF_GONE;
addTeleportStars((Entity*)world.bob);
2018-02-25 18:29:44 +01:00
playSound(SND_TELEPORT, world.bob->uniqueId % MAX_SND_CHANNELS);
2018-02-02 20:10:12 +01:00
}
else
{
doBob();
}
doCommon();
}
static void doGameComplete(void)
{
if (--world.missionCompleteTimer <= 0)
2018-02-02 20:10:12 +01:00
{
dropCarriedItems();
2018-03-20 20:26:14 +01:00
initPostMission();
2018-02-02 20:10:12 +01:00
}
else
{
doBob();
}
doCommon();
}
static void doGameOver(void)
{
2018-02-25 13:12:31 +01:00
if (world.gameOverTimer == -FPS)
{
stopMusic();
}
else if (world.gameOverTimer == -FPS * 2)
{
loadMusic("music/Sadness.ogg");
playMusic(0);
showWidgetGroup("gameOver");
}
world.gameOverTimer = MAX(-FPS * 5, world.gameOverTimer - 1);
2018-02-02 20:10:12 +01:00
doCommon();
2018-02-25 13:12:31 +01:00
if (world.gameOverTimer <= -FPS * 3)
2018-02-02 20:10:12 +01:00
{
2018-02-25 13:12:31 +01:00
doWidgets();
2018-02-02 20:10:12 +01:00
}
}
static void doCommon(void)
{
animateSprites();
world.frameCounter++;
2018-02-09 23:41:37 +01:00
world.frameCounter %= (FPS * 10);
2018-02-02 20:10:12 +01:00
doHud();
if (world.minEnemySpawnTime > 0 && !dev.cheatNoEnemies)
{
spawnEnemies();
}
if (world.missionType == MT_BOSS && --world.helperItemTimer <= 0)
2018-02-02 20:10:12 +01:00
{
addHelperItems();
}
doEntities();
doParticles();
}
static void addHelperItems(void)
{
int x, y, w, h;
w = world.map.bounds.w - world.map.bounds.x;
h = world.map.bounds.h - world.map.bounds.y;
x = world.map.bounds.x + (rand() % w);
y = world.map.bounds.y + 1;
if (world.map.data[x / MAP_TILE_SIZE][y / MAP_TILE_SIZE] == 0)
{
dropRandomCherry(x, y);
}
x = world.map.bounds.x + (rand() % w);
y = world.map.bounds.y + (rand() % h);
if (world.map.data[x / MAP_TILE_SIZE][y / MAP_TILE_SIZE] == 0)
{
addRandomWeapon(x, y);
}
world.helperItemTimer = FPS * rrnd(3, 5);
}
static void spawnEnemies(void)
{
char name[MAX_NAME_LENGTH];
int r, x, y;
Unit *u;
if (world.numToSpawn == 0)
{
if (--world.enemySpawnTimer <= 0)
{
world.numToSpawn = 3 + (rand() % 3);
world.spawnInterval = 0;
}
2018-02-09 20:20:37 +01:00
2018-02-02 20:10:12 +01:00
return;
}
if (--world.spawnInterval <= 0)
{
r = (rand() % world.numEnemyTypes);
x = world.bob->x;
x += ((randF() - randF()) * 5) * MAP_TILE_SIZE;
y = world.bob->y;
y += ((randF() - randF()) * 5) * MAP_TILE_SIZE;
if (x >= world.map.bounds.x && y >= world.map.bounds.y && x < world.map.bounds.w + SCREEN_WIDTH - 64 && y < world.map.bounds.h + SCREEN_HEIGHT - 64)
{
2018-02-08 22:53:32 +01:00
sprintf(name, "%s%s", world.enemyTypes[r], (rand() % 2 ? "Blob" : "EyeDroid"));
2018-02-02 20:10:12 +01:00
2018-02-08 23:25:10 +01:00
u = (Unit*)createEntity(name);
self = (Entity*)u;
2018-02-02 20:10:12 +01:00
u->animate();
x /= MAP_TILE_SIZE;
y /= MAP_TILE_SIZE;
if (canAdd(u, x, y))
{
u->x = x * MAP_TILE_SIZE;
u->y = y * MAP_TILE_SIZE;
u->spawnedIn = 1;
u->canCarryItem = 0;
addTeleportStars((Entity*)u);
2018-02-26 19:56:13 +01:00
playBattleSound(SND_APPEAR, u->uniqueId % MAX_SND_CHANNELS, u->x, u->y);
2018-02-02 20:10:12 +01:00
}
}
world.spawnInterval = rrnd(FPS / 4, FPS / 2);
if (--world.numToSpawn <= 0)
{
world.enemySpawnTimer = (FPS * rrnd(world.minEnemySpawnTime, world.maxEnemySpawnTime));
}
}
}
static int canAdd(Unit *u, int mx, int my)
{
int i;
if (isSolid(mx, my))
{
return 0;
}
if (isLiquid(mx, my) && (!(u->flags & EF_WATER_BREATHING)))
{
return 0;
}
if (!(u->flags & EF_WEIGHTLESS))
{
for (i = 0 ; i < 10 ; i++)
{
if (isLiquid(mx, my + i))
{
return 0;
}
if (isWalkable(mx, my + i))
{
return 1;
}
}
/* long drop */
return 0;
}
return 1;
}
2018-01-27 11:58:47 +01:00
void observeActivation(Entity *e)
{
2018-02-02 20:10:12 +01:00
int i;
2018-03-02 23:21:45 +01:00
if (!isOnScreen(e) && (!(e->flags & EF_NO_OBSERVE)))
2018-02-02 20:10:12 +01:00
{
2018-03-02 23:21:45 +01:00
e->flags |= EF_NO_OBSERVE;
2018-02-02 20:10:12 +01:00
for (i = 0 ; i < MAX_ENTS_TO_OBSERVE ; i++)
{
2018-02-03 11:01:14 +01:00
if (world.entitiesToObserve[i] == NULL)
2018-02-02 20:10:12 +01:00
{
2018-02-03 11:01:14 +01:00
world.entitiesToObserve[i] = e;
2018-03-02 23:21:45 +01:00
world.observationTimer = FPS * 1.5;
2018-02-03 16:53:21 +01:00
return;
2018-02-02 20:10:12 +01:00
}
else if (getDistance(e->x, e->y, world.entitiesToObserve[i]->x, world.entitiesToObserve[i]->y) < SCREEN_HEIGHT - 50)
{
return;
}
2018-02-02 20:10:12 +01:00
}
2018-02-03 16:53:21 +01:00
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "Can't observe entity - out of array space");
exit(1);
2018-02-02 20:10:12 +01:00
}
2018-01-27 11:58:47 +01:00
}
2018-02-25 13:12:31 +01:00
void drawGameOver(void)
{
int fadeAmount;
if (world.gameOverTimer <= -FPS)
{
fadeAmount = MIN((world.gameOverTimer + FPS) * -1, 128);
}
drawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0, fadeAmount);
if (world.gameOverTimer <= -FPS * 2)
{
blitRect(atlasTexture->texture, SCREEN_WIDTH / 2, 280, &missionFailed->rect, 1);
if (world.gameOverTimer <= -FPS * 3)
{
drawWidgetFrame();
drawWidgets();
}
}
}
2018-02-20 09:14:35 +01:00
void drawQuit(void)
{
SDL_Rect r;
drawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0, 128);
r.w = 650;
r.h = 325;
r.x = (SCREEN_WIDTH / 2) - r.w / 2;
r.y = (SCREEN_HEIGHT / 2) - r.h / 2;
drawRect(r.x, r.y, r.w, r.h, 0, 0, 0, 192);
drawOutlineRect(r.x, r.y, r.w, r.h, 200, 200, 200, 255);
limitTextWidth(r.w - 100);
drawText(SCREEN_WIDTH / 2, r.y + 10, 26, TA_CENTER, colors.white, "Quit and return to hub?");
if (world.missionType == MT_TRAINING)
{
drawText(SCREEN_WIDTH / 2, r.y + 65, 26, TA_CENTER, colors.white, "As this is a tutorial mission, you can skip it and move onto the main game.");
}
else if (world.isReturnVisit)
{
drawText(SCREEN_WIDTH / 2, r.y + 65, 26, TA_CENTER, colors.white, "Your progress on this mission will be saved.");
}
else
{
drawText(SCREEN_WIDTH / 2, r.y + 65, 26, TA_CENTER, colors.white, "Warning: if you quit now, you will lose all progress on this level.");
}
limitTextWidth(0);
drawWidgets();
}
void exitRadar(void)
{
startSectionTransition();
2018-03-15 09:01:04 +01:00
app.delegate.logic = &logic;
app.delegate.draw = &draw;
endSectionTransition();
}
2018-02-15 23:56:22 +01:00
static void resume(void)
{
app.keyboard[SDL_SCANCODE_ESCAPE] = 0;
hideAllWidgets();
2018-02-19 20:12:55 +01:00
showing = SHOW_NONE;
2018-02-15 23:56:22 +01:00
}
static void options(void)
{
2018-02-20 18:50:01 +01:00
initOptions(returnFromOptions);
2018-02-15 23:56:22 +01:00
}
static void stats(void)
{
2018-02-19 20:12:55 +01:00
showing = SHOW_STATS;
2018-03-02 09:09:04 +01:00
initStatsDisplay();
2018-02-19 20:12:55 +01:00
showWidgetGroup("stats");
}
static void trophies(void)
{
2018-02-19 23:32:14 +01:00
showing = SHOW_TROPHIES;
showWidgetGroup("trophies");
}
2018-02-15 23:56:22 +01:00
static void quit(void)
{
2018-02-20 09:14:35 +01:00
showing = SHOW_QUIT;
showWidgetGroup("gameQuit");
2018-02-15 23:56:22 +01:00
}
2018-02-17 17:53:18 +01:00
2018-02-25 13:12:31 +01:00
static void retry(void)
{
retryMission();
}
static void hub(void)
{
returnToHub();
}
static void title(void)
{
returnToTitle();
}
2018-02-19 23:32:14 +01:00
static void returnFromTrophyStats(void)
2018-02-19 20:12:55 +01:00
{
2018-02-20 09:14:35 +01:00
showWidgetGroup("gamePaused");
2018-02-19 20:12:55 +01:00
showing = SHOW_WIDGETS;
app.keyboard[SDL_SCANCODE_ESCAPE] = 0;
}
2018-02-23 20:26:13 +01:00
void quitMission(void)
2018-02-20 09:14:35 +01:00
{
resume();
stopMusic();
world.state = WS_QUIT;
2018-02-20 09:14:35 +01:00
world.missionCompleteTimer = (FPS * 1.5) + 1;
2018-02-21 23:04:01 +01:00
if (world.missionType == MT_TRAINING)
{
2018-02-23 20:26:13 +01:00
autoCompleteMission();
2018-02-21 23:04:01 +01:00
}
2018-02-20 09:14:35 +01:00
}
2018-02-20 18:50:01 +01:00
static void returnFromOptions(void)
{
2018-03-15 09:01:04 +01:00
app.delegate.logic = &logic;
app.delegate.draw = &draw;
2018-02-20 18:50:01 +01:00
returnFromTrophyStats();
}
2018-02-23 20:26:13 +01:00
void autoCompleteMission(void)
2018-02-21 23:04:01 +01:00
{
Objective *o;
Entity *e;
for (o = world.objectiveHead.next ; o != NULL ; o = o->next)
{
o->currentValue = o->targetValue;
}
for (e = world.entityHead.next ; e != NULL ; e = e->next)
{
switch (e->type)
{
case ET_MIA:
e->alive = ALIVE_DEAD;
game.stats[STAT_MIAS_RESCUED]++;
break;
case ET_KEY:
if (!(e->flags & EF_GONE))
{
e->alive = ALIVE_DEAD;
game.stats[STAT_KEYS_FOUND]++;
}
break;
default:
break;
}
}
world.state = WS_COMPLETE;
2018-02-21 23:04:01 +01:00
}
2018-02-18 10:29:26 +01:00
void destroyWorld(void)
2018-02-17 17:53:18 +01:00
{
int i;
for (i = 0 ; i < world.numEnemyTypes ; i++)
{
free(world.enemyTypes[i]);
}
free(world.enemyTypes);
destroyTriggers();
destroyObjectives();
destroyEntities();
destroyParticles();
destroyQuadtree();
}