tbftss/src/galaxy/galacticMap.c

900 lines
18 KiB
C

/*
Copyright (C) 2015-2019 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 "galacticMap.h"
static void logic(void);
static void draw(void);
static void handleKeyboard(void);
static void handleMouse(void);
static void scrollGalaxy(void);
static void drawStarSystemDetail(void);
static void selectStarSystem(void);
static void drawGalaxy(void);
static void centerOnSelectedStarSystem(void);
static void doStarSystems(void);
void destroyGalacticMap(void);
static void drawPulses(void);
static void drawInfoBars(void);
static void doPulses(void);
static void addPulses(void);
static void drawMenu(void);
static void resume(void);
static void stats(void);
static void trophies(void);
static void options(void);
static void ok(void);
static void quit(void);
static void startMission(void);
static void returnFromOptions(void);
static void doStarSystemView(void);
static void updatePandoranAdvance(void);
static void fallenOK(void);
static void updateCampaignProgress(void);
static void campaignCompleteOK(void);
static Mission *nextAvailableMission(StarSystem *starSystem);
static void fighterDatabase(void);
static StarSystem *selectedStarSystem;
static SDL_Texture *background;
static AtlasImage *starSystemTexture;
static AtlasImage *arrowTexture;
static SDL_Point camera;
static Pulse pulseHead = {0};
static Pulse *pulseTail;
static int pulseTimer;
static float ssx, ssy;
static float arrowPulse;
static int show;
static int scrollingMap;
static int campaignComplete = 0;
static PointF cameraMin, cameraMax;
static Widget *startMissionButton;
static Mission *hoverMission;
static char *MISSIONS_TEXT;
static char *PILOT_TEXT;
static char *CRAFT_TEXT;
static char *SQUADRON_TEXT;
static char *COMPLETED_TEXT;
static char *EPIC_TEXT;
static char *OPTIONAL_TEXT;
void initGalacticMap(void)
{
show = SHOW_GALAXY;
startSectionTransition();
stopMusic();
app.delegate.logic = &logic;
app.delegate.draw = &draw;
memset(&app.keyboard, 0, sizeof(int) * MAX_KEYBOARD_KEYS);
MISSIONS_TEXT = _("Missions: %d / %d");
PILOT_TEXT = _("Pilot: %s");
CRAFT_TEXT = _("Craft: %s");
SQUADRON_TEXT = _("Squadron: %s");
COMPLETED_TEXT = _("This mission has been completed.");
EPIC_TEXT = _("Note: this is an epic mission.");
OPTIONAL_TEXT = _("Note: this is an optional mission.");
background = getTexture("gfx/backgrounds/background02.jpg");
starSystemTexture = getAtlasImage("gfx/galaxy/starSystem.png");
arrowTexture = getAtlasImage("gfx/galaxy/arrow.png");
selectedStarSystem = getStarSystem(game.selectedStarSystem);
centerOnSelectedStarSystem();
updateAllMissions();
updatePandoranAdvance();
awardCampaignTrophies();
awardStatsTrophies();
updateCampaignProgress();
hoverMission = NULL;
app.saveGame = 1;
pulseTimer = 0;
arrowPulse = 0;
selectedStarSystem = NULL;
/* clear the pulses */
destroyGalacticMap();
getWidget("close", "starSystem")->action = resume;
startMissionButton = getWidget("startMission", "starSystem");
startMissionButton->action = startMission;
getWidget("resume", "galacticMap")->action = resume;
getWidget("stats", "galacticMap")->action = stats;
getWidget("trophies", "galacticMap")->action = trophies;
getWidget("fighterDB", "galacticMap")->action = fighterDatabase;
getWidget("options", "galacticMap")->action = options;
getWidget("quit", "galacticMap")->action = quit;
getWidget("ok", "stats")->action = ok;
getWidget("ok", "trophies")->action = ok;
getWidget("ok", "fighterDB")->action = ok;
getWidget("ok", "fallen")->action = fallenOK;
autoSizeWidgetButtons("galacticMap", 1);
endSectionTransition();
SDL_SetWindowGrab(app.window, 0);
playMusic("music/main/Pressure.ogg", 1);
}
static void updateCampaignProgress(void)
{
StarSystem *starSystem;
if (!campaignComplete && game.completedMissions == game.totalMissions)
{
for (starSystem = game.starSystemHead.next ; starSystem != NULL ; starSystem = starSystem->next)
{
if (starSystem->missionHead.next && starSystem->missionHead.next->available)
{
starSystem->activeMission = starSystem->missionHead.next;
}
}
campaignComplete = 1;
showOKDialog(&campaignCompleteOK, _("Congratulations, you have completed the campaign! You may now replay past missions, for fun, to boost stats, or to earn missing trophies!"));
}
}
static void updatePandoranAdvance(void)
{
StarSystem *starSystem, *fallenStarSystem;
fallenStarSystem = NULL;
for (starSystem = game.starSystemHead.next ; starSystem != NULL ; starSystem = starSystem->next)
{
if (starSystem->side != SIDE_PANDORAN && starSystem->fallsToPandorans && starSystem->completedMissions == starSystem->totalMissions && starSystem->totalMissions > 0)
{
starSystem->side = SIDE_PANDORAN;
fallenStarSystem = starSystem;
}
}
if (fallenStarSystem)
{
showOKDialog(&fallenOK, _("%s has fallen to the Pandorans"), fallenStarSystem->name);
}
}
static void logic(void)
{
handleKeyboard();
handleMouse();
switch (show)
{
case SHOW_GALAXY:
doStarSystems();
scrollGalaxy();
scrollBackground(-ssx, -ssy);
doStars(ssx, ssy);
break;
case SHOW_STAR_SYSTEM:
doStarSystemView();
break;
}
doPulses();
if (pulseTimer % FPS == 0)
{
addPulses();
}
pulseTimer++;
pulseTimer %= (FPS * 60);
arrowPulse += 0.1;
doWidgets();
if (show == SHOW_FIGHTER_DB)
{
doFighterDatabase();
}
app.doTrophyAlerts = 1;
}
static void doStarSystems(void)
{
StarSystem *starSystem;
int cx, cy;
if (!scrollingMap)
{
cx = app.mouse.x - 32;
cy = app.mouse.y - 32;
cameraMin.x = cameraMin.y = 99999;
cameraMax.x = cameraMax.y = -99999;
selectedStarSystem = NULL;
for (starSystem = game.starSystemHead.next ; starSystem != NULL ; starSystem = starSystem->next)
{
cameraMin.x = MIN(cameraMin.x, starSystem->x);
cameraMin.y = MIN(cameraMin.y, starSystem->y);
cameraMax.x = MAX(cameraMax.x, starSystem->x);
cameraMax.y = MAX(cameraMax.y, starSystem->y);
if (starSystem->availableMissions > 0 && collision(cx, cy, 64, 64, starSystem->x - camera.x, starSystem->y - camera.y, 4, 4))
{
if (selectedStarSystem != starSystem)
{
selectedStarSystem = starSystem;
if (app.mouse.button[SDL_BUTTON_LEFT])
{
selectStarSystem();
app.mouse.button[SDL_BUTTON_LEFT] = 0;
}
}
}
}
cameraMin.x -= app.winWidth / 2;
cameraMin.y -= app.winHeight / 2;
cameraMax.x -= app.winWidth / 2;
cameraMax.y -= app.winHeight / 2;
}
}
static void scrollGalaxy(void)
{
int lastX, lastY;
lastX = camera.x;
lastY = camera.y;
ssx = ssy = 0;
if (scrollingMap)
{
camera.x -= app.mouse.dx * 1.5;
camera.y -= app.mouse.dy * 1.5;
ssx = -app.mouse.dx;
ssx /= 3;
ssy = -app.mouse.dy;
ssy /= 3;
camera.x = MAX(cameraMin.x, MIN(camera.x, cameraMax.x));
camera.y = MAX(cameraMin.y, MIN(camera.y, cameraMax.y));
if (lastX == camera.x)
{
ssx = 0;
}
if (lastY == camera.y)
{
ssy = 0;
}
}
}
static void doStarSystemView(void)
{
Mission *mission;
for (mission = selectedStarSystem->missionHead.next ; mission != NULL ; mission = mission->next)
{
if (mission->available && collision(app.uiMouse.x - app.mouse.w / 2, app.uiMouse.y - app.mouse.h / 2, app.mouse.w, app.mouse.h, mission->rect.x, mission->rect.y, mission->rect.w, mission->rect.h))
{
hoverMission = mission;
if (app.mouse.button[SDL_BUTTON_LEFT])
{
if (game.currentMission != mission)
{
playSound(SND_GUI_CLICK);
}
game.currentMission = mission;
return;
}
}
}
/* allow closing by pressing the right mouse button */
if (app.mouse.button[SDL_BUTTON_RIGHT])
{
show = SHOW_GALAXY;
playSound(SND_GUI_CLOSE);
}
}
static void addPulses(void)
{
Pulse *pulse;
StarSystem *starSystem;
for (starSystem = game.starSystemHead.next ; starSystem != NULL ; starSystem = starSystem->next)
{
if (starSystem->completedMissions < starSystem->availableMissions || (campaignComplete && starSystem->activeMission))
{
pulse = malloc(sizeof(Pulse));
memset(pulse, 0, sizeof(Pulse));
pulse->x = starSystem->x;
pulse->y = starSystem->y;
pulse->life = 255;
switch (starSystem->type)
{
case SS_NORMAL:
if (!starSystem->activeMission->isOptional)
{
pulse->r = 255;
}
else
{
pulse->r = 128;
pulse->g = 128;
pulse->b = 255;
}
break;
case SS_SOL:
pulse->g = 255;
break;
case SS_PANDORAN:
pulse->r = 128;
pulse->g = 128;
pulse->b = 255;
break;
}
pulseTail->next = pulse;
pulseTail = pulse;
}
}
}
static void doPulses(void)
{
Pulse *pulse, *prev;
prev = &pulseHead;
for (pulse = pulseHead.next ; pulse != NULL ; pulse = pulse->next)
{
pulse->size += 0.5;
pulse->life--;
if (pulse->life <= 0)
{
if (pulse == pulseTail)
{
pulseTail = prev;
}
prev->next = pulse->next;
free(pulse);
pulse = prev;
}
prev = pulse;
}
}
static void draw(void)
{
drawBackground(background);
drawStars();
drawGalaxy();
drawPulses();
drawInfoBars();
switch (show)
{
case SHOW_STAR_SYSTEM:
drawStarSystemDetail();
break;
case SHOW_MENU:
drawMenu();
break;
case SHOW_STATS:
drawStats();
break;
case SHOW_TROPHIES:
drawTrophies();
break;
case SHOW_OPTIONS:
drawOptions();
break;
case SHOW_FIGHTER_DB:
drawFighterDatabase();
break;
}
}
static void drawPulses(void)
{
Pulse *pulse;
for (pulse = pulseHead.next ; pulse != NULL ; pulse = pulse->next)
{
drawCircle(pulse->x - camera.x, pulse->y - camera.y, pulse->size, pulse->r, pulse->g, pulse->b, pulse->life);
}
}
static void centerOnSelectedStarSystem(void)
{
camera.x = selectedStarSystem->x;
camera.x -= app.winWidth / 2;
camera.y = selectedStarSystem->y;
camera.y -= app.winHeight / 2;
}
static void drawGalaxy(void)
{
SDL_Rect r;
StarSystem *starSystem;
SDL_Color color;
float ax, ay, aa;
for (starSystem = game.starSystemHead.next ; starSystem != NULL ; starSystem = starSystem->next)
{
r.x = starSystem->x - camera.x;
r.y = starSystem->y - camera.y;
blit(starSystemTexture, r.x, r.y, 1);
switch (starSystem->side)
{
case SIDE_CSN:
color = colors.cyan;
break;
case SIDE_UNF:
color = colors.white;
break;
case SIDE_PANDORAN:
color = colors.red;
break;
}
drawText(r.x, r.y + 12, 14, TA_CENTER, color, starSystem->name);
if (starSystem->completedMissions < starSystem->availableMissions)
{
ax = r.x;
ay = r.y;
aa = -1;
ax = MAX(MIN(app.winWidth - 64, ax), 64);
ay = MAX(MIN(app.winHeight - 64, ay), 64);
if (r.x < 0)
{
ax = 64 + (sin(arrowPulse) * 10);
aa = 270;
}
else if (r.x > app.winWidth)
{
ax = app.winWidth - 64 + (sin(arrowPulse) * 10);
aa = 90;
}
else if (r.y < 0)
{
ay = 64 + (sin(arrowPulse) * 10);
aa = 0;
}
else if (r.y > app.winHeight)
{
ay = app.winHeight - 64 + (sin(arrowPulse) * 10);
aa = 180;
}
if (aa != -1)
{
switch (starSystem->type)
{
case SS_NORMAL:
setAtlasColor(255, 0, 0, 255);
break;
case SS_SOL:
setAtlasColor(0, 255, 0, 255);
break;
case SS_PANDORAN:
setAtlasColor(64, 128, 255, 255);
break;
}
blitRotated(arrowTexture, ax, ay, aa);
setAtlasColor(255, 255, 255, 255);
}
}
}
}
static void drawInfoBars(void)
{
SDL_Rect r;
if (show != SHOW_STAR_SYSTEM && selectedStarSystem != NULL)
{
r.x = 0;
r.y = app.winHeight - 35;
r.w = app.winWidth;
r.h = 35;
SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 200);
SDL_RenderFillRect(app.renderer, &r);
SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE);
drawText(app.winWidth / 2, app.winHeight - 30, 18, TA_CENTER, colors.white, selectedStarSystem->description);
}
r.x = 0;
r.y = 0;
r.w = app.winWidth;
r.h = 35;
SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 200);
SDL_RenderFillRect(app.renderer, &r);
SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE);
drawText((app.winWidth / 2), 5, 18, TA_CENTER, colors.white, MISSIONS_TEXT, game.completedMissions, game.availableMissions);
}
static void selectStarSystem(void)
{
if (selectedStarSystem->availableMissions > 0)
{
show = SHOW_STAR_SYSTEM;
STRNCPY(game.selectedStarSystem, selectedStarSystem->name, MAX_NAME_LENGTH);
game.currentMission = nextAvailableMission(selectedStarSystem);
playSound(SND_GUI_SELECT);
}
}
static Mission *nextAvailableMission(StarSystem *starSystem)
{
Mission *m;
for (m = starSystem->missionHead.next ; m != NULL ; m = m->next)
{
if (m->available && !m->completed)
{
return m;
}
}
return starSystem->missionHead.next;
}
static void drawStarSystemDetail(void)
{
int y;
Mission *mission;
SDL_Rect r;
SDL_SetRenderTarget(app.renderer, app.uiBuffer);
r.w = 900;
r.h = 600;
r.x = (UI_WIDTH / 2) - (r.w / 2);
r.y = (UI_HEIGHT / 2) - (r.h / 2);
SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 225);
SDL_RenderFillRect(app.renderer, &r);
SDL_SetRenderDrawColor(app.renderer, 255, 255, 255, 200);
SDL_RenderDrawRect(app.renderer, &r);
SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE);
y = 70;
drawText(UI_WIDTH / 2, y, 28, TA_CENTER, colors.cyan, selectedStarSystem->name);
SDL_RenderDrawLine(app.renderer, r.x, 120, r.x + r.w - 1, 120);
SDL_RenderDrawLine(app.renderer, 515, 120, 515, 659);
y += 80;
for (mission = selectedStarSystem->missionHead.next ; mission != NULL ; mission = mission->next)
{
mission->rect.x = 200;
mission->rect.y = y - 2;
mission->rect.w = 300;
mission->rect.h = 38;
if (mission == hoverMission)
{
SDL_SetRenderDrawColor(app.renderer, 16, 32, 64, 255);
SDL_RenderFillRect(app.renderer, &mission->rect);
SDL_SetRenderDrawColor(app.renderer, 32, 64, 128, 255);
SDL_RenderDrawRect(app.renderer, &mission->rect);
}
if (mission == game.currentMission)
{
SDL_SetRenderDrawColor(app.renderer, 32, 64, 128, 255);
SDL_RenderFillRect(app.renderer, &mission->rect);
SDL_SetRenderDrawColor(app.renderer, 64, 96, 196, 255);
SDL_RenderDrawRect(app.renderer, &mission->rect);
}
if (mission->available)
{
drawText(210, y, 22, TA_LEFT, mission->completed ? colors.lightGrey : colors.yellow, mission->name);
y += 42;
}
}
if (game.currentMission->available)
{
drawText(525, 135, 18, TA_LEFT, colors.lightGrey, PILOT_TEXT, game.currentMission->pilot);
drawText(525, 160, 18, TA_LEFT, colors.lightGrey, CRAFT_TEXT, game.currentMission->craft);
drawText(525, 185, 18, TA_LEFT, colors.lightGrey, SQUADRON_TEXT, game.currentMission->squadron);
app.textWidth = 525;
drawText(525, 230, 22, TA_LEFT, colors.white, game.currentMission->description);
app.textWidth = 0;
}
if (game.currentMission->completed)
{
drawText(525, UI_HEIGHT - 95, 18, TA_LEFT, colors.green, COMPLETED_TEXT);
}
else if (game.currentMission->epic)
{
drawText(525, UI_HEIGHT - 95, 18, TA_LEFT, colors.yellow, EPIC_TEXT);
}
else if (game.currentMission->isOptional)
{
drawText(525, UI_HEIGHT - 95, 18, TA_LEFT, colors.cyan, OPTIONAL_TEXT);
}
startMissionButton->enabled = (!game.currentMission->completed || selectedStarSystem->type == SS_SOL || campaignComplete);
drawWidgets("starSystem");
SDL_SetRenderTarget(app.renderer, app.backBuffer);
}
static void fallenOK(void)
{
show = SHOW_GALAXY;
app.modalDialog.type = MD_NONE;
}
static void campaignCompleteOK(void)
{
show = SHOW_GALAXY;
app.modalDialog.type = MD_NONE;
campaignComplete = 2;
}
static void handleKeyboard(void)
{
if (app.keyboard[SDL_SCANCODE_ESCAPE] && !app.awaitingWidgetInput)
{
switch (show)
{
case SHOW_GALAXY:
selectWidget("resume", "galacticMap");
show = SHOW_MENU;
break;
case SHOW_STAR_SYSTEM:
show = SHOW_GALAXY;
break;
case SHOW_MENU:
show = SHOW_GALAXY;
break;
case SHOW_OPTIONS:
case SHOW_STATS:
case SHOW_TROPHIES:
case SHOW_FIGHTER_DB:
show = SHOW_MENU;
selectWidget("resume", "galacticMap");
break;
}
playSound(SND_GUI_CLOSE);
clearInput();
}
}
static void handleMouse(void)
{
if (app.mouse.button[SDL_BUTTON_LEFT])
{
if (app.mouse.dx != 0 || app.mouse.dy != 0)
{
scrollingMap = 1;
}
}
else
{
scrollingMap = 0;
}
setMouseCursor(app.mouse.button[SDL_BUTTON_LEFT] && show == SHOW_GALAXY);
}
static void startMission(void)
{
initBattle();
loadMission(game.currentMission->filename);
}
static void drawMenu(void)
{
SDL_Rect r;
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);
SDL_SetRenderTarget(app.renderer, app.uiBuffer);
r.w = 400;
r.h = 500;
r.x = (UI_WIDTH / 2) - r.w / 2;
r.y = (UI_HEIGHT / 2) - r.h / 2;
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);
drawWidgets("galacticMap");
SDL_SetRenderTarget(app.renderer, app.backBuffer);
}
static void resume(void)
{
show = SHOW_GALAXY;
}
static void options(void)
{
show = SHOW_OPTIONS;
initOptions(returnFromOptions);
}
static void fighterDatabase(void)
{
show = SHOW_FIGHTER_DB;
initFighterDatabaseDisplay();
}
static void stats(void)
{
selectWidget("ok", "stats");
show = SHOW_STATS;
initStatsDisplay();
}
static void trophies(void)
{
selectWidget("ok", "trophies");
show = SHOW_TROPHIES;
initTrophiesDisplay();
}
static void ok(void)
{
selectWidget("resume", "galacticMap");
show = SHOW_MENU;
}
static void returnFromOptions(void)
{
show = SHOW_MENU;
selectWidget("resume", "galacticMap");
}
static void quit(void)
{
initTitle();
}
void destroyGalacticMap(void)
{
Pulse *pulse;
while (pulseHead.next)
{
pulse = pulseHead.next;
pulseHead.next = pulse->next;
free(pulse);
}
pulseTail = &pulseHead;
}