blobwarsAttrition/src/hub/hub.c

908 lines
19 KiB
C
Raw Normal View History

2018-01-24 08:16:52 +01:00
/*
2019-06-02 17:13:34 +02:00
Copyright (C) 2018-2019 Parallel Realities
2018-01-24 08:16:52 +01: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 "hub.h"
static void unlockAllMissions(void);
2018-01-24 08:16:52 +01:00
static void unlockMission(char *id);
static int requiredMissionUnlocked(char *id);
2018-01-24 08:16:52 +01:00
static void loadMissions(void);
2018-02-16 23:50:55 +01:00
static HubMission *getMissionAt(int x, int y);
2018-02-17 08:56:29 +01:00
static void drawMissions(void);
static void drawInfoBar(void);
static void drawMissionInfo(void);
2018-05-12 12:50:49 +02:00
static void drawPlusSettings(void);
2018-02-16 23:50:55 +01:00
static void logic(void);
static void draw(void);
2018-02-17 17:52:51 +01:00
static void startMission(void);
static void cancel(void);
static void options(void);
static void stats(void);
static void trophies(void);
static void quit(void);
2018-02-19 23:32:14 +01:00
static void returnFromTrophyStats(void);
static void doCursor(void);
static void doMissionSelect(void);
static void doMissionInfo(void);
2018-02-18 23:22:05 +01:00
static void drawHudWidgets(void);
2018-02-19 20:13:29 +01:00
static void awardMissionTrophies(void);
2018-02-21 09:13:28 +01:00
static void returnFromOptions(void);
2018-02-22 20:01:16 +01:00
void destroyHub(void);
2018-05-12 12:50:49 +02:00
static void startMissionPlus(void);
2018-01-24 08:16:52 +01:00
static HubMission hubMissionHead;
static HubMission *hubMissionTail;
2018-02-16 23:50:55 +01:00
static HubMission *selectedMission;
static Atlas *worldMap;
static Atlas *alert;
static Texture *clouds;
2018-02-17 17:52:51 +01:00
static Sprite *cursorSpr;
2018-02-17 08:56:29 +01:00
static Sprite *keySprites[MAX_KEY_TYPES];
2018-02-16 23:50:55 +01:00
static Texture *atlasTexture;
2018-02-18 12:25:14 +01:00
static int unlockedMissions;
2018-02-17 17:52:51 +01:00
static PointF cursor;
2018-02-16 23:50:55 +01:00
static float blipSize;
static float blipValue;
2018-02-19 09:28:42 +01:00
static int showing;
2018-05-12 12:50:49 +02:00
static int doPlusSettings;
2018-02-18 15:22:45 +01:00
static PointF cloudPos;
2018-01-24 08:16:52 +01:00
void initHub(void)
{
2018-02-18 12:25:14 +01:00
int unlockTeeka, i;
2018-02-16 23:50:55 +01:00
HubMission *mission, *teeka;
2018-01-24 08:16:52 +01:00
Tuple *t;
2019-06-02 17:13:34 +02:00
startSectionTransition();
2019-06-02 17:13:34 +02:00
2018-01-24 08:16:52 +01:00
memset(&hubMissionHead, 0, sizeof(HubMission));
hubMissionTail = &hubMissionHead;
2019-06-02 17:13:34 +02:00
2018-02-17 08:56:29 +01:00
memset(&keySprites, 0, sizeof(Sprite*) * MAX_KEY_TYPES);
2019-06-02 17:13:34 +02:00
2018-02-18 10:29:37 +01:00
selectedMission = NULL;
2019-06-02 17:13:34 +02:00
loadMusic("music/61321__mansardian__news-background.ogg");
2019-06-02 17:13:34 +02:00
2018-02-16 23:50:55 +01:00
atlasTexture = getTexture("gfx/atlas/atlas.png");
worldMap = getImageFromAtlas("gfx/hub/worldMap.jpg");
alert = getImageFromAtlas("gfx/hub/alert.png");
clouds = getTexture("gfx/hub/clouds.png");
2018-02-17 17:52:51 +01:00
cursorSpr = getSprite("Cursor");
2019-06-02 17:13:34 +02:00
2018-02-17 08:56:29 +01:00
for (i = 0 ; i < MAX_KEY_TYPES ; i++)
{
if (game.keys[i].value.i > 0)
{
keySprites[i] = getSprite(game.keys[i].key);
}
}
2018-02-17 17:52:51 +01:00
2018-12-22 22:53:19 +01:00
cursor.x = app.config.winWidth / 2;
cursor.y = app.config.winHeight / 2;
2018-02-17 17:52:51 +01:00
getWidget("startMission", "mission")->action = startMission;
getWidget("cancel", "mission")->action = cancel;
getWidget("options", "hub")->action = options;
getWidget("stats", "hub")->action = stats;
getWidget("trophies", "hub")->action = trophies;
getWidget("quit", "hub")->action = quit;
2019-06-02 17:13:34 +02:00
2018-02-19 23:32:14 +01:00
getWidget("ok", "stats")->action = returnFromTrophyStats;
getWidget("ok", "trophies")->action = returnFromTrophyStats;
2019-06-02 17:13:34 +02:00
2018-05-12 12:50:49 +02:00
getWidget("startMission", "missionPlus")->action = startMissionPlus;
getWidget("cancel", "missionPlus")->action = cancel;
2019-06-02 17:13:34 +02:00
2018-01-24 08:16:52 +01:00
loadMissions();
2019-06-02 17:13:34 +02:00
2018-01-24 08:16:52 +01:00
if (dev.cheatLevels)
{
unlockAllMissions();
2018-01-24 08:16:52 +01:00
}
2019-06-02 17:13:34 +02:00
2018-03-02 09:09:04 +01:00
game.totalMissions = 0;
2019-06-02 17:13:34 +02:00
2018-01-24 08:16:52 +01:00
unlockedMissions = 0;
2019-06-02 17:13:34 +02:00
2018-03-02 09:09:04 +01:00
game.stats[STAT_MISSIONS_COMPLETE] = 0;
2019-06-02 17:13:34 +02:00
2018-02-16 23:50:55 +01:00
unlockTeeka = 1;
2019-06-02 17:13:34 +02:00
2018-02-16 23:50:55 +01:00
blipValue = 0;
2019-06-02 17:13:34 +02:00
2018-05-12 12:50:49 +02:00
doPlusSettings = 0;
2019-06-02 17:13:34 +02:00
2018-02-19 09:28:42 +01:00
showing = SHOW_NONE;
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
cursor.x = app.config.winWidth / 2;
cursor.y = app.config.winHeight / 2;
SDL_WarpMouseInWindow(app.window, cursor.x, cursor.y);
2018-12-23 16:32:33 +01:00
game.isComplete = 0;
2019-06-02 17:13:34 +02:00
2018-01-24 08:16:52 +01:00
for (t = game.missionStatusHead.next ; t != NULL ; t = t->next)
{
if (t->value.i != MS_INCOMPLETE)
{
unlockedMissions++;
}
2019-06-02 17:13:34 +02:00
2018-02-16 23:50:55 +01:00
if (t->value.i == MS_COMPLETE)
{
2018-03-02 09:09:04 +01:00
game.stats[STAT_MISSIONS_COMPLETE]++;
if (strcmp(t->key, "teeka") == 0)
{
game.isComplete = 1;
unlockTeeka = 0;
}
2018-02-16 23:50:55 +01:00
}
2018-01-24 08:16:52 +01:00
}
2019-06-02 17:13:34 +02:00
2018-03-13 09:25:08 +01:00
teeka = NULL;
2019-06-02 17:13:34 +02:00
2018-04-15 10:26:29 +02:00
unlockedMissions = 0;
2019-06-02 17:13:34 +02:00
2018-01-24 08:16:52 +01:00
for (mission = hubMissionHead.next ; mission != NULL ; mission = mission->next)
{
if (requiredMissionUnlocked(mission->requires) || dev.cheatLevels || game.isComplete)
2018-01-24 08:16:52 +01:00
{
2018-02-16 23:50:55 +01:00
unlockMission(mission->id);
}
2019-06-02 17:13:34 +02:00
2018-02-16 23:50:55 +01:00
mission->status = getMissionStatus(mission->id);
2019-06-02 17:13:34 +02:00
2018-04-15 10:26:29 +02:00
if (!game.isComplete)
2018-02-16 23:50:55 +01:00
{
2018-04-15 10:26:29 +02:00
if (strcmp(mission->id, "teeka") == 0)
{
teeka = mission;
}
else if (mission->status != MS_COMPLETE)
{
unlockTeeka = 0;
}
2019-06-02 17:13:34 +02:00
2018-04-15 10:26:29 +02:00
if (mission->status == MS_MISSING_HEART_CELL)
{
STRNCPY(mission->description, app.strings[ST_HEART_CELL], MAX_DESCRIPTION_LENGTH);
}
2018-01-24 08:16:52 +01:00
}
2018-04-15 10:26:29 +02:00
else
2018-01-24 08:16:52 +01:00
{
2018-04-15 10:26:29 +02:00
STRNCPY(mission->description, app.strings[ST_FREEPLAY], MAX_DESCRIPTION_LENGTH);
2018-01-24 08:16:52 +01:00
}
2019-06-02 17:13:34 +02:00
2018-03-02 09:09:04 +01:00
game.totalMissions++;
2019-06-02 17:13:34 +02:00
2018-02-18 12:25:14 +01:00
if (mission->status != MS_LOCKED)
{
unlockedMissions++;
}
}
2019-06-02 17:13:34 +02:00
2018-03-17 17:58:25 +01:00
if (teeka != NULL)
2018-02-16 23:50:55 +01:00
{
2018-03-17 17:58:25 +01:00
if (unlockTeeka)
{
unlockMission("teeka");
2019-06-02 17:13:34 +02:00
2018-03-17 17:58:25 +01:00
teeka->status = MS_INCOMPLETE;
}
else
{
teeka->status = MS_LOCKED;
}
2018-02-16 23:50:55 +01:00
}
2018-02-19 20:13:29 +01:00
awardMissionTrophies();
2019-06-02 17:13:34 +02:00
2018-02-18 15:22:45 +01:00
cloudPos.x = randF() - randF();
cloudPos.y = randF() - randF();
2019-06-02 17:13:34 +02:00
2018-02-16 23:50:55 +01:00
app.delegate.logic = &logic;
app.delegate.draw = &draw;
2019-06-02 17:13:34 +02:00
app.restrictTrophyAlert = 0;
2019-06-02 17:13:34 +02:00
playMusic(1);
2019-06-02 17:13:34 +02:00
endSectionTransition();
2018-02-16 23:50:55 +01:00
}
static void logic(void)
{
blipValue += 0.1;
2019-06-02 17:13:34 +02:00
2018-02-16 23:50:55 +01:00
blipSize = 64 + (sin(blipValue) * 16);
2019-06-02 17:13:34 +02:00
2018-02-18 15:22:45 +01:00
scrollBackground(cloudPos.x, cloudPos.y);
2019-06-02 17:13:34 +02:00
2018-02-17 08:56:29 +01:00
animateSprites();
2019-06-02 17:13:34 +02:00
2018-02-19 09:28:42 +01:00
switch (showing)
2018-02-17 08:56:29 +01:00
{
2018-02-19 09:28:42 +01:00
case SHOW_NONE:
doCursor();
if (selectedMission == NULL)
{
doMissionSelect();
}
else
{
doMissionInfo();
}
break;
2019-06-02 17:13:34 +02:00
2018-02-19 09:28:42 +01:00
case SHOW_WIDGETS:
doWidgets();
if (app.keyboard[SDL_SCANCODE_ESCAPE])
{
2018-02-25 13:23:00 +01:00
playSound(SND_MENU_BACK, 0);
2018-02-19 09:28:42 +01:00
showing = SHOW_NONE;
app.keyboard[SDL_SCANCODE_ESCAPE] = 0;
}
break;
2019-06-02 17:13:34 +02:00
2018-02-19 09:28:42 +01:00
case SHOW_STATS:
doStats();
if (app.keyboard[SDL_SCANCODE_ESCAPE])
{
2018-02-25 13:23:00 +01:00
playSound(SND_MENU_BACK, 0);
2018-02-19 23:32:14 +01:00
returnFromTrophyStats();
2018-02-19 09:28:42 +01:00
}
break;
2019-06-02 17:13:34 +02:00
2018-02-19 09:28:42 +01:00
case SHOW_TROPHIES:
2018-02-19 23:32:14 +01:00
doTrophies();
if (app.keyboard[SDL_SCANCODE_ESCAPE])
{
2018-02-25 13:23:00 +01:00
playSound(SND_MENU_BACK, 0);
2018-02-19 23:32:14 +01:00
returnFromTrophyStats();
}
2018-02-19 09:28:42 +01:00
break;
2019-06-02 17:13:34 +02:00
2018-02-19 09:28:42 +01:00
default:
break;
2018-02-16 23:50:55 +01:00
}
}
2018-02-17 17:52:51 +01:00
static void doCursor(void)
{
2018-02-17 17:52:51 +01:00
if (app.mouse.dx != 0 || app.mouse.dy != 0)
{
2018-12-22 22:53:19 +01:00
cursor.x = app.mouse.x;
cursor.y = app.mouse.y;
2018-02-17 17:52:51 +01:00
}
if (isControl(CONTROL_UP) || app.keyboard[SDL_SCANCODE_UP])
{
cursor.y -= CURSOR_SPEED;
2018-12-22 22:53:19 +01:00
SDL_WarpMouseInWindow(app.window, cursor.x, cursor.y);
2018-02-17 17:52:51 +01:00
}
if (isControl(CONTROL_DOWN) || app.keyboard[SDL_SCANCODE_DOWN])
{
cursor.y += CURSOR_SPEED;
2018-12-22 22:53:19 +01:00
SDL_WarpMouseInWindow(app.window, cursor.x, cursor.y);
2018-02-17 17:52:51 +01:00
}
if (isControl(CONTROL_LEFT) || app.keyboard[SDL_SCANCODE_LEFT])
{
cursor.x -= CURSOR_SPEED;
2018-12-22 22:53:19 +01:00
SDL_WarpMouseInWindow(app.window, cursor.x, cursor.y);
2018-02-17 17:52:51 +01:00
}
if (isControl(CONTROL_RIGHT) || app.keyboard[SDL_SCANCODE_RIGHT])
{
cursor.x += CURSOR_SPEED;
2018-12-22 22:53:19 +01:00
SDL_WarpMouseInWindow(app.window, cursor.x, cursor.y);
2018-02-17 17:52:51 +01:00
}
2018-02-16 23:50:55 +01:00
}
static void doMissionSelect(void)
{
HubMission *m;
2019-06-02 17:13:34 +02:00
if (app.keyboard[SDL_SCANCODE_ESCAPE])
{
2018-02-25 13:23:00 +01:00
playSound(SND_MENU_BACK, 0);
showWidgetGroup("hub");
2018-02-19 09:28:42 +01:00
showing = SHOW_WIDGETS;
2018-02-18 23:22:05 +01:00
app.keyboard[SDL_SCANCODE_ESCAPE] = 0;
}
else if (isControl(CONTROL_FIRE) || app.mouse.button[SDL_BUTTON_LEFT])
{
m = getMissionAt(cursor.x, cursor.y);
2019-06-02 17:13:34 +02:00
if (m != NULL)
{
selectedMission = m;
app.mouse.button[SDL_BUTTON_LEFT] = 0;
clearControl(CONTROL_FIRE);
showWidgetGroup("mission");
}
}
}
static void doMissionInfo(void)
{
Widget *w;
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
w = selectWidgetAt(cursor.x - app.uiOffset.x, cursor.y - app.uiOffset.y);
2019-06-02 17:13:34 +02:00
if ((w != NULL) && (isControl(CONTROL_FIRE) || app.mouse.button[SDL_BUTTON_LEFT]))
{
2018-05-12 12:50:49 +02:00
if (w->type == WT_BUTTON)
{
w->action();
}
else if (w->type == WT_SPINNER)
{
/* assuming there are only two options */
w->value[0] = !w->value[0];
}
2019-06-02 17:13:34 +02:00
app.mouse.button[SDL_BUTTON_LEFT] = 0;
clearControl(CONTROL_FIRE);
}
2019-06-02 17:13:34 +02:00
if (app.keyboard[SDL_SCANCODE_ESCAPE])
{
cancel();
}
}
2018-02-16 23:50:55 +01:00
static void draw(void)
{
2018-12-22 22:53:19 +01:00
blitRectScaled(atlasTexture->texture, 0, 0, app.config.winWidth, app.config.winHeight, &worldMap->rect, 0);
2019-06-02 17:13:34 +02:00
drawBackground(clouds->texture);
2019-06-02 17:13:34 +02:00
2018-02-17 08:56:29 +01:00
drawMissions();
2019-06-02 17:13:34 +02:00
2018-02-17 08:56:29 +01:00
drawInfoBar();
2019-06-02 17:13:34 +02:00
2018-02-19 09:28:42 +01:00
switch (showing)
2018-02-17 08:56:29 +01:00
{
2018-02-19 09:28:42 +01:00
case SHOW_NONE:
if (selectedMission != NULL)
{
2018-05-12 12:50:49 +02:00
if (!doPlusSettings)
{
drawMissionInfo();
}
else
{
drawPlusSettings();
}
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
SDL_SetRenderTarget(app.renderer, app.uiBuffer);
2018-02-19 09:28:42 +01:00
drawWidgets();
2018-12-22 22:53:19 +01:00
/* draw on both the UI buffer and main buffer, just to cheat */
blitRect(atlasTexture->texture, cursor.x - app.uiOffset.x, cursor.y - app.uiOffset.y, getCurrentFrame(cursorSpr), 1);
SDL_SetRenderTarget(app.renderer, app.backBuffer);
2018-02-19 09:28:42 +01:00
}
blitRect(atlasTexture->texture, cursor.x, cursor.y, getCurrentFrame(cursorSpr), 1);
break;
2019-06-02 17:13:34 +02:00
2018-02-19 09:28:42 +01:00
case SHOW_WIDGETS:
drawHudWidgets();
break;
2019-06-02 17:13:34 +02:00
2018-02-19 09:28:42 +01:00
case SHOW_STATS:
drawStats();
break;
2019-06-02 17:13:34 +02:00
2018-02-19 09:28:42 +01:00
case SHOW_TROPHIES:
2018-02-19 23:32:14 +01:00
drawTrophies();
2018-02-19 09:28:42 +01:00
break;
2018-02-17 08:56:29 +01:00
}
}
static void drawMissions(void)
{
HubMission *mission;
2018-12-22 22:53:19 +01:00
double ratioX, ratioY;
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
/* the original Attrition is based on 800x600, so multiply up */
ratioX = app.config.winWidth / 800.0;
ratioY = app.config.winHeight / 600.0;
2019-06-02 17:13:34 +02:00
2018-01-24 08:16:52 +01:00
for (mission = hubMissionHead.next ; mission != NULL ; mission = mission->next)
{
2018-02-16 23:50:55 +01:00
switch (mission->status)
2018-01-24 08:16:52 +01:00
{
2018-02-16 23:50:55 +01:00
case MS_INCOMPLETE:
SDL_SetTextureColorMod(atlasTexture->texture, 255, 0, 0);
2018-12-22 22:53:19 +01:00
blitRectScaled(atlasTexture->texture, mission->x * ratioX, mission->y * ratioY, blipSize, blipSize, &alert->rect, 1);
drawText(mission->x * ratioX, (mission->y * ratioY) - 32, 18, TA_CENTER, colors.white, mission->name);
2018-02-16 23:50:55 +01:00
break;
2019-06-02 17:13:34 +02:00
2018-02-16 23:50:55 +01:00
case MS_PARTIAL:
case MS_MISSING_HEART_CELL:
SDL_SetTextureColorMod(atlasTexture->texture, 255, 255, 0);
2018-12-22 22:53:19 +01:00
blitRectScaled(atlasTexture->texture, mission->x * ratioX, mission->y * ratioY, blipSize, blipSize, &alert->rect, 1);
drawText(mission->x * ratioX, (mission->y * ratioY) - 32, 18, TA_CENTER, colors.white, mission->name);
2018-02-16 23:50:55 +01:00
break;
2019-06-02 17:13:34 +02:00
default:
break;
2018-01-24 08:16:52 +01:00
}
}
2018-02-16 23:50:55 +01:00
SDL_SetTextureColorMod(atlasTexture->texture, 255, 255, 255);
2018-02-17 08:56:29 +01:00
}
static void drawInfoBar(void)
{
2018-12-22 22:53:19 +01:00
int x;
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
x = (50 + (app.config.winWidth - 50)) / 5;
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
drawRect(0, 0, app.config.winWidth, 32, 0, 0, 0, 192);
2019-06-02 17:13:34 +02:00
2018-04-01 18:41:45 +02:00
drawText(10, 5, 18, TA_LEFT, colors.white, app.strings[ST_HUB_MISSIONS], game.stats[STAT_MISSIONS_COMPLETE], unlockedMissions);
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
drawText(x, 5, 18, TA_CENTER, colors.white, app.strings[ST_HUB_MIAS], game.stats[STAT_MIAS_RESCUED], game.totalMIAs);
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
drawText(x * 2, 5, 18, TA_CENTER, colors.white, app.strings[ST_HUB_TARGETS], game.stats[STAT_TARGETS_DEFEATED], game.totalTargets);
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
drawText(x * 3, 5, 18, TA_CENTER, colors.white, app.strings[ST_HUB_KEYS], game.stats[STAT_KEYS_FOUND], game.totalKeys);
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
drawText(x * 4, 5, 18, TA_CENTER, colors.white, app.strings[ST_HUB_HEARTS], game.stats[STAT_HEARTS_FOUND], game.totalHearts);
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
drawText(app.config.winWidth - 10, 5, 18, TA_RIGHT, colors.white, app.strings[ST_HUB_CELLS], game.stats[STAT_CELLS_FOUND], game.totalCells);
2018-01-24 08:16:52 +01:00
}
2018-02-18 23:22:05 +01:00
static void drawHudWidgets(void)
{
2018-12-22 22:53:19 +01:00
drawRect(0, 0, app.config.winWidth, app.config.winHeight, 0, 0, 0, 128);
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
SDL_SetRenderTarget(app.renderer, app.uiBuffer);
2019-06-02 17:13:34 +02:00
2018-02-25 13:23:00 +01:00
drawWidgetFrame();
2019-06-02 17:13:34 +02:00
2018-02-18 23:22:05 +01:00
drawWidgets();
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
SDL_SetRenderTarget(app.renderer, app.backBuffer);
2018-02-18 23:22:05 +01:00
}
2018-02-17 08:56:29 +01:00
static void drawMissionInfo(void)
{
int w, h, x, y, size, mid, i;
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
drawRect(0, 0, app.config.winWidth, app.config.winHeight, 0, 0, 0, 128);
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
SDL_SetRenderTarget(app.renderer, app.uiBuffer);
2019-06-02 17:13:34 +02:00
2018-02-17 08:56:29 +01:00
w = 800;
h = 550;
2018-12-22 22:53:19 +01:00
x = (UI_WIDTH - w) / 2;
y = (UI_HEIGHT - h) / 2;
2019-06-02 17:13:34 +02:00
2018-02-17 08:56:29 +01:00
drawRect(x, y, w, h, 0, 0, 0, 192);
drawOutlineRect(x, y, w, h, 255, 255, 255, 255);
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
drawText(UI_WIDTH / 2, y + 25, 32, TA_CENTER, colors.white, selectedMission->name);
2019-06-02 17:13:34 +02:00
2018-12-23 16:32:33 +01:00
app.textWidth = (w - 25);
2018-02-17 08:56:29 +01:00
drawText(x + 15, y + 100, 22, TA_LEFT, colors.white, selectedMission->description);
2018-12-22 23:46:38 +01:00
app.textWidth = 0;
2019-06-02 17:13:34 +02:00
2018-02-17 08:56:29 +01:00
size = 65;
mid = size / 2;
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
y = (((UI_HEIGHT - h) / 2) + h) - 225;
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
drawText(UI_WIDTH / 2, y, 24, TA_CENTER, colors.white, "Keys");
2019-06-02 17:13:34 +02:00
y += 64;
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
x = ((UI_WIDTH - w) / 2) + 30;
2019-06-02 17:13:34 +02:00
2018-02-17 08:56:29 +01:00
for (i = 0 ; i < MAX_KEY_TYPES ; i++)
{
drawRect(x, y, size, size, 0, 0, 0, 128);
2019-06-02 17:13:34 +02:00
2018-02-17 08:56:29 +01:00
drawOutlineRect(x, y, size, size, 255, 255, 255, 255);
2019-06-02 17:13:34 +02:00
2018-02-17 08:56:29 +01:00
if (game.keys[i].value.i > 0)
{
2018-02-17 17:52:51 +01:00
blitRect(atlasTexture->texture, x + mid, y + mid + 7, getCurrentFrame(keySprites[i]), 1);
2019-06-02 17:13:34 +02:00
2018-02-17 08:56:29 +01:00
drawText(x + size - 5, y, 18, TA_RIGHT, colors.white, "%d", game.keys[i].value.i);
}
2019-06-02 17:13:34 +02:00
2018-02-17 08:56:29 +01:00
x += (size + 30);
}
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
SDL_SetRenderTarget(app.renderer, app.backBuffer);
2018-02-17 08:56:29 +01:00
}
2018-05-12 12:50:49 +02:00
static void drawPlusSettings(void)
{
int w, h, x, y;
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
drawRect(0, 0, app.config.winWidth, app.config.winHeight, 0, 0, 0, 128);
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
SDL_SetRenderTarget(app.renderer, app.uiBuffer);
2019-06-02 17:13:34 +02:00
2018-05-12 12:50:49 +02:00
w = 800;
h = 550;
2018-12-22 22:53:19 +01:00
x = (UI_WIDTH - w) / 2;
y = (UI_HEIGHT - h) / 2;
2019-06-02 17:13:34 +02:00
2018-05-12 12:50:49 +02:00
drawRect(x, y, w, h, 0, 0, 0, 192);
drawOutlineRect(x, y, w, h, 255, 255, 255, 255);
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
drawText(UI_WIDTH / 2, y + 25, 32, TA_CENTER, colors.white, selectedMission->name);
drawText(UI_WIDTH / 2, y + 75, 24, TA_CENTER, colors.white, app.strings[ST_MISSION_CONFIG]);
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
SDL_SetRenderTarget(app.renderer, app.backBuffer);
2018-05-12 12:50:49 +02:00
}
2018-01-24 08:16:52 +01:00
static void unlockMission(char *id)
{
Tuple *t;
2019-06-02 17:13:34 +02:00
2018-01-24 08:16:52 +01:00
for (t = game.missionStatusHead.next ; t != NULL ; t = t->next)
{
if (strcmp(t->key, id) == 0)
{
if (t->value.i == MS_LOCKED || game.isComplete)
2018-01-24 08:16:52 +01:00
{
t->value.i = MS_INCOMPLETE;
2019-06-02 17:13:34 +02:00
2018-04-15 10:26:29 +02:00
/* if the game is complete, don't reset these two */
if (game.isComplete && (strcmp(t->key, "teeka") == 0 || strcmp(t->key, "beachApproach") == 0))
{
t->value.i = MS_COMPLETE;
}
2018-01-28 17:14:17 +01:00
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG, "Unlocked mission %s", id);
2018-01-24 08:16:52 +01:00
}
2019-06-02 17:13:34 +02:00
2018-01-24 08:16:52 +01:00
return;
}
}
2019-06-02 17:13:34 +02:00
2018-01-24 08:16:52 +01:00
t = malloc(sizeof(Tuple));
memset(t, 0, sizeof(Tuple));
game.missionStatusTail->next = t;
game.missionStatusTail = t;
2019-06-02 17:13:34 +02:00
2018-01-24 08:16:52 +01:00
STRNCPY(t->key, id, MAX_NAME_LENGTH);
t->value.i = MS_INCOMPLETE;
2019-06-02 17:13:34 +02:00
2018-01-28 17:14:17 +01:00
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG, "Unlocked mission %s", id);
2018-01-24 08:16:52 +01:00
}
static int requiredMissionUnlocked(char *id)
2018-01-24 08:16:52 +01:00
{
Tuple *t;
2019-06-02 17:13:34 +02:00
if (strlen(id) > 0)
2018-01-24 08:16:52 +01:00
{
for (t = game.missionStatusHead.next ; t != NULL ; t = t->next)
2018-01-24 08:16:52 +01:00
{
if (strcmp(t->key, id) == 0)
{
return (t->value.i == MS_PARTIAL || t->value.i == MS_COMPLETE);
}
2018-01-24 08:16:52 +01:00
}
}
else
{
return 1;
}
2019-06-02 17:13:34 +02:00
return 0;
2018-01-24 08:16:52 +01:00
}
static void unlockAllMissions(void)
2018-01-24 08:16:52 +01:00
{
HubMission *mission;
2019-06-02 17:13:34 +02:00
2018-01-24 08:16:52 +01:00
for (mission = hubMissionHead.next ; mission != NULL ; mission = mission->next)
{
if (mission->status == MS_LOCKED || mission->status == MS_INCOMPLETE)
2018-01-24 08:16:52 +01:00
{
mission->status = MS_INCOMPLETE;
unlockMission(mission->id);
}
}
}
2018-02-16 23:50:55 +01:00
HubMission *getMissionAt(int x, int y)
2018-01-24 08:16:52 +01:00
{
HubMission *rtn;
HubMission *mission;
float distance, dist;
2018-12-22 22:53:19 +01:00
double ratioX, ratioY;
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
/* the original Attrition is based on 800x600, so multiply up */
ratioX = app.config.winWidth / 800.0;
ratioY = app.config.winHeight / 600.0;
2019-06-02 17:13:34 +02:00
2018-01-24 08:16:52 +01:00
rtn = NULL;
distance = 32;
for (mission = hubMissionHead.next ; mission != NULL ; mission = mission->next)
{
2018-02-16 23:50:55 +01:00
if (mission->status == MS_INCOMPLETE || mission->status == MS_MISSING_HEART_CELL || mission->status == MS_PARTIAL)
2018-01-24 08:16:52 +01:00
{
2018-12-22 22:53:19 +01:00
dist = getDistance(x, y, mission->x * ratioX, mission->y * ratioY);
2019-06-02 17:13:34 +02:00
2018-02-16 23:50:55 +01:00
if (dist < distance)
{
rtn = mission;
distance = dist;
}
2018-01-24 08:16:52 +01:00
}
}
return rtn;
}
2018-02-17 17:52:51 +01:00
static void startMission(void)
{
2018-05-12 12:50:49 +02:00
if (!game.isComplete)
{
STRNCPY(game.worldId, selectedMission->id, MAX_NAME_LENGTH);
2019-06-02 17:13:34 +02:00
2018-05-12 12:50:49 +02:00
saveGame(0);
2019-06-02 17:13:34 +02:00
2018-05-12 12:50:49 +02:00
stopMusic();
2019-06-02 17:13:34 +02:00
2018-05-12 12:50:49 +02:00
destroyHub();
2019-06-02 17:13:34 +02:00
2018-05-12 12:50:49 +02:00
initWorld();
}
else
{
hideAllWidgets();
2019-06-02 17:13:34 +02:00
2018-05-12 12:50:49 +02:00
showWidgetGroup("missionPlus");
2019-06-02 17:13:34 +02:00
2018-05-12 12:50:49 +02:00
doPlusSettings = 1;
}
}
static void cancel(void)
{
hideAllWidgets();
showing = SHOW_NONE;
selectedMission = NULL;
doPlusSettings = 0;
2018-05-12 12:50:49 +02:00
app.keyboard[SDL_SCANCODE_ESCAPE] = 0;
}
static void startMissionPlus(void)
{
game.plus = 0;
2019-06-02 17:13:34 +02:00
if (getWidget("noDoors", "missionPlus")->value[0])
2018-05-12 12:50:49 +02:00
{
game.plus |= PLUS_NO_DOORS;
2018-05-12 12:50:49 +02:00
}
2019-06-02 17:13:34 +02:00
2018-05-12 12:50:49 +02:00
if (getWidget("randomEnemies", "missionPlus")->value[0])
{
game.plus |= PLUS_RANDOM;
}
2019-06-02 17:13:34 +02:00
2018-05-12 12:50:49 +02:00
if (getWidget("tougherEnemies", "missionPlus")->value[0])
{
game.plus |= PLUS_STRONGER;
}
2019-06-02 17:13:34 +02:00
2018-05-12 12:50:49 +02:00
if (getWidget("defeatAllEnemies", "missionPlus")->value[0])
{
game.plus |= PLUS_KILL_ALL;
}
2019-06-02 17:13:34 +02:00
2018-05-12 12:50:49 +02:00
if (getWidget("mirrorWorld", "missionPlus")->value[0])
{
game.plus |= PLUS_MIRROR;
}
2019-06-02 17:13:34 +02:00
2018-02-17 17:52:51 +01:00
STRNCPY(game.worldId, selectedMission->id, MAX_NAME_LENGTH);
2019-06-02 17:13:34 +02:00
saveGame(0);
2019-06-02 17:13:34 +02:00
stopMusic();
2019-06-02 17:13:34 +02:00
2018-02-22 20:01:16 +01:00
destroyHub();
2019-06-02 17:13:34 +02:00
2018-02-18 10:29:37 +01:00
initWorld();
2018-02-17 17:52:51 +01:00
}
static void options(void)
{
2018-02-21 09:13:28 +01:00
initOptions(returnFromOptions);
2018-02-17 17:52:51 +01:00
}
static void stats(void)
{
2018-02-19 09:28:42 +01:00
showing = SHOW_STATS;
2018-03-02 09:09:04 +01:00
initStatsDisplay();
2018-02-19 09:28:42 +01:00
showWidgetGroup("stats");
2018-02-17 17:52:51 +01:00
}
static void trophies(void)
{
2018-02-19 23:32:14 +01:00
showing = SHOW_TROPHIES;
showWidgetGroup("trophies");
2018-02-17 17:52:51 +01:00
}
static void quit(void)
{
2018-03-20 08:29:05 +01:00
stopMusic();
2019-06-02 17:13:34 +02:00
2018-03-20 08:29:05 +01:00
destroyHub();
2019-06-02 17:13:34 +02:00
2018-03-20 08:29:05 +01:00
initTitle();
2018-02-17 17:52:51 +01:00
}
2018-02-19 23:32:14 +01:00
static void returnFromTrophyStats(void)
2018-02-19 09:28:42 +01:00
{
showWidgetGroup("hub");
showing = SHOW_WIDGETS;
app.keyboard[SDL_SCANCODE_ESCAPE] = 0;
}
2018-02-21 09:13:28 +01:00
static void returnFromOptions(void)
{
2018-03-15 09:01:04 +01:00
app.delegate.logic = &logic;
app.delegate.draw = &draw;
2019-06-02 17:13:34 +02:00
2018-02-21 09:13:28 +01:00
returnFromTrophyStats();
}
2018-02-16 23:50:55 +01:00
static void loadMissions(void)
{
cJSON *root, *node;
char *text;
HubMission *mission;
2019-06-02 17:13:34 +02:00
2018-02-16 23:50:55 +01:00
text = readFile("data/hub/missions.json");
root = cJSON_Parse(text);
2019-06-02 17:13:34 +02:00
2018-02-16 23:50:55 +01:00
for (node = cJSON_GetObjectItem(root, "missions")->child ; node != NULL ; node = node->next)
{
mission = malloc(sizeof(HubMission));
memset(mission, 0, sizeof(HubMission));
hubMissionTail->next = mission;
hubMissionTail = mission;
2019-06-02 17:13:34 +02:00
2018-02-16 23:50:55 +01:00
STRNCPY(mission->id, cJSON_GetObjectItem(node, "id")->valuestring, MAX_NAME_LENGTH);
STRNCPY(mission->name, cJSON_GetObjectItem(node, "name")->valuestring, MAX_NAME_LENGTH);
2018-04-01 18:41:45 +02:00
STRNCPY(mission->description, _(cJSON_GetObjectItem(node, "description")->valuestring), MAX_DESCRIPTION_LENGTH);
STRNCPY(mission->requires, cJSON_GetObjectItem(node, "requires")->valuestring, MAX_NAME_LENGTH);
2018-02-16 23:50:55 +01:00
mission->status = MS_LOCKED;
2019-06-02 17:13:34 +02:00
2018-12-22 22:53:19 +01:00
mission->x = cJSON_GetObjectItem(node, "x")->valuedouble;
mission->y = cJSON_GetObjectItem(node, "y")->valuedouble;
2018-02-16 23:50:55 +01:00
}
2019-06-02 17:13:34 +02:00
2018-02-16 23:50:55 +01:00
cJSON_Delete(root);
2019-06-02 17:13:34 +02:00
2018-02-16 23:50:55 +01:00
free(text);
}
2018-02-19 20:13:29 +01:00
static void awardMissionTrophies(void)
{
int beach, greenlands, underground, outpost, save;
2018-02-19 20:13:29 +01:00
HubMission *mission;
beach = greenlands = underground = outpost = 1;
2019-06-02 17:13:34 +02:00
save = 0;
2018-02-19 20:13:29 +01:00
for (mission = hubMissionHead.next ; mission != NULL ; mission = mission->next)
{
if (mission->status != MS_COMPLETE)
{
if (strstr(mission->id, "beach"))
{
beach = 0;
}
else if (strstr(mission->id, "greenlands"))
{
greenlands = 0;
}
else if (strstr(mission->id, "underground"))
{
underground = 0;
}
else if (strstr(mission->id, "outpost"))
{
outpost = 0;
}
}
}
if (beach)
{
awardTrophy("BEACH");
save = 1;
2018-02-19 20:13:29 +01:00
}
if (greenlands)
{
awardTrophy("GREENLANDS");
save = 1;
2018-02-19 20:13:29 +01:00
}
if (underground)
{
awardTrophy("UNDERGROUND");
save = 1;
2018-02-19 20:13:29 +01:00
}
if (outpost)
{
awardTrophy("OUTPOST");
save = 1;
2018-02-19 20:13:29 +01:00
}
/* ignore training mission */
2018-03-02 09:09:04 +01:00
if (game.stats[STAT_MISSIONS_COMPLETE] == 2)
2018-02-19 20:13:29 +01:00
{
awardTrophy("CLEAN");
save = 1;
2018-02-19 20:13:29 +01:00
}
2019-06-02 17:13:34 +02:00
2018-02-27 09:16:34 +01:00
/* ignore Teeka's */
2018-03-02 09:09:04 +01:00
if (game.totalMissions - game.stats[STAT_MISSIONS_COMPLETE] == 1)
2018-02-27 09:16:34 +01:00
{
awardTrophy("FULLY_CLEAN");
save = 1;
}
2019-06-02 17:13:34 +02:00
if (save)
{
saveGame(0);
2018-02-27 09:16:34 +01:00
}
2018-02-19 20:13:29 +01:00
}
2018-02-16 23:50:55 +01:00
void destroyHub(void)
{
2018-02-17 17:52:51 +01:00
HubMission *m;
while (hubMissionHead.next)
{
m = hubMissionHead.next;
hubMissionHead.next = m->next;
free(m);
}
2019-06-02 17:13:34 +02:00
2018-02-22 20:01:16 +01:00
memset(&hubMissionHead, 0, sizeof(HubMission));
hubMissionTail = &hubMissionHead;
2018-02-16 23:50:55 +01:00
}