Lephisto/src/map.c

419 lines
11 KiB
C

#include "log.h"
#include "lephisto.h"
#include "toolkit.h"
#include "space.h"
#include "opengl.h"
#include "map.h"
#define WINDOW_WIDTH 550
#define WINDOW_HEIGHT 440
#define MAP_WIDTH (WINDOW_WIDTH-150)
#define MAP_HEIGHT (WINDOW_HEIGHT-100)
#define BUTTON_WIDTH 60
#define BUTTON_HEIGHT 40
static int map_wid = 0;
static double map_zoom = 1.; /* Zoom of the map. */
static double map_xpos = 0.; /* Map position. */
static double map_ypos = 0.;
static int map_selected = -1;
static StarSystem** map_path = NULL; /* The path to current selected system. */
static int map_npath = 0;
static int map_drag = 0; /* Is the user dragging the map? */
/* Extern. */
/* space.c */
extern StarSystem* systems_stack;
extern int systems_nstack;
/* player.c */
extern int planet_target;
extern int hyperspace_target;
static void map_close(char* str);
static void map_update(void);
static int map_inPath(StarSystem* sys);
static int map_sysReachable(StarSystem* sys);
static int map_sysReachable(StarSystem* sys);
static void map_render(double bx, double by, double w, double h);
static void map_mouse(SDL_Event* event, double mx, double my);
static void map_buttonZoom(char* str);
static void map_selectCur(void);
/* Open the map window. */
void map_open(void) {
if(map_wid) {
map_close(NULL);
return;
}
/* Set the position to focus on current system. */
map_xpos = cur_system->pos.x;
map_ypos = cur_system->pos.y;
map_wid = window_create("Star Map", -1, -1, WINDOW_WIDTH, WINDOW_HEIGHT);
window_addText(map_wid, -20, -20, 100, 20, 1, "txtSysname",
&gl_defFont, &cDConsole, systems_stack[map_selected].name);
window_addText(map_wid, -20, -60, 90, 20, 0, "txtSFaction",
&gl_smallFont, &cDConsole, "Faction:");
window_addText(map_wid, -20, -60-gl_smallFont.h-5, 80, 100, 0, "txtFaction",
&gl_smallFont, &cBlack, NULL);
window_addText(map_wid, -20, -110, 90, 20, 0, "txtSPlanets",
&gl_smallFont, &cDConsole, "Planets:");
window_addText(map_wid, -20, -110-gl_smallFont.h-5, 80, 100, 0, "txtPlanets",
&gl_smallFont, &cBlack, NULL);
window_addCust(map_wid, 20, -40, MAP_WIDTH, MAP_HEIGHT,
"cstMap", 1, map_render, map_mouse);
window_addButton(map_wid, -20, 20, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnClose", "Close", map_close);
window_addButton(map_wid, 40, 20, 30, 30, "btnZoomIn", "+", map_buttonZoom);
window_addButton(map_wid, 80, 20, 30, 30, "btnZoomOut", "-", map_buttonZoom);
map_update();
}
static void map_close(char* str) {
(void)str;
if(map_wid) {
window_destroy(map_wid);
map_wid = 0;
}
}
static void map_update(void) {
int i;
StarSystem* sys;
int f;
char buf[100];
sys = &systems_stack[map_selected];
window_modifyText(map_wid, "txtSysname", sys->name);
if(sys->nplanets == 0)
/* No planets -> no factions. */
snprintf(buf, 100, "NA");
else {
f = -1;
for(i = 0; i < sys->nplanets; i++) {
if((f == -1) && (sys->planets[i].faction != 0))
f = sys->planets[i].faction;
else if(f != sys->planets[i].faction &&
(sys->planets[i].faction!=0)) {
/* TODO: more verbosity. */
snprintf(buf, 100, "Multiple");
break;
}
}
if(i == sys->nplanets)
/* Saw them all, and all the same. */
snprintf(buf, 100, "%s", faction_name(f));
}
window_modifyText(map_wid, "txtFaction", buf);
buf[0] = '\0';
if(sys->nplanets == 0)
snprintf(buf, 100, "None");
else {
if(sys->nplanets > 0)
strcat(buf, sys->planets[0].name);
for(i = 1; i < sys->nplanets; i++) {
strcat(buf, ",\n");
strcat(buf, sys->planets[i].name);
}
}
window_modifyText(map_wid, "txtPlanets", buf);
}
/* Return 1 if sys is part of the map_path. */
static int map_inPath(StarSystem* sys) {
int i, f;
f = pilot_getJumps(player) - 1;
for(i = 0; i < map_npath; i++)
if(map_path[i] == sys) {
if(i > f) {
return 2;
} else return 1;
}
return 0;
}
/* Return 1 if player can reach the system. */
static int map_sysReachable(StarSystem* sys) {
int i;
if(sys->known != 0) return 1; /* It is known. */
/* Check to see if it is adjacent to known. */
for(i = 0; i < sys->njumps; i++)
if(systems_stack[sys->jumps[i]].known == 1)
return 1;
return 0;
}
/* Render the map as a custom widget. */
static void map_render(double bx, double by, double w, double h) {
int i, j, n, m;
double x, y, r, tx, ty;
StarSystem* sys;
glColour* col;
r = 5.;
x = (bx - map_xpos + w/2) * 1.;
y = (by - map_ypos + h/2) * 1.;
/* Background */
COLOUR(cBlack);
glBegin(GL_QUADS);
glVertex2d(bx, by);
glVertex2d(bx, by+h);
glVertex2d(bx+w, by+h);
glVertex2d(bx+w, by);
glEnd();
/* Render the star systems. */
for(i = 0; i < systems_nstack; i++) {
sys = &systems_stack[i];
/* Check ot make sure system is known of adjacent to known. */
if(!map_sysReachable(sys)) {
for(j = 0; j < sys->njumps; j++)
if(systems_stack[sys->jumps[j]].known == 1)
break;
if(j == sys->njumps) /* None found. */
continue;
}
/* System Colours. */
if(sys == cur_system) COLOUR(cRadar_targ);
else if((sys->known == 0) || (sys->nplanets==0)) COLOUR(cInert);
else if(areEnemies(player->faction, sys->faction)) COLOUR(cRed);
else COLOUR(cYellow);
gl_drawCircleInRect(x + sys->pos.x*map_zoom,
y + sys->pos.y*map_zoom,
r, bx, by, w, h);
/* Draw the system name. */
if(sys->known != 0) {
tx = x + 7. + sys->pos.x * map_zoom;
ty = y - 5. + sys->pos.y * map_zoom;
gl_print(&gl_smallFont,
tx + SCREEN_W/2., ty + SCREEN_H/2.,
&cWhite, sys->name);
}
if(sys->known == 0) continue; /* We dont't draw hyperspace lines. */
/* Draw the hyperspace paths. */
glShadeModel(GL_SMOOTH);
/* Cheaply use transparency instead of actually */
/* calculating from x to y the line must go. :) */
for(j = 0; j < sys->njumps; j++) {
n = map_inPath(&systems_stack[sys->jumps[j]]);
m = map_inPath(sys);
/* Set the colours, is the route the current one? */
if((hyperspace_target != -1) &&
(((cur_system == sys) && (j == hyperspace_target)) ||
((cur_system == &systems_stack[sys->jumps[j]]) &&
(sys == &systems_stack[cur_system->jumps[hyperspace_target]]))))
col = &cGreen;
else if((n > 0) && (m > 0)) {
if((n == 2) || (m == 2)) /* Out of fuel. */
col = &cRed;
else
col = &cYellow;
}
else col = &cDarkBlue;
glBegin(GL_LINE_STRIP);
ACOLOUR(*col, 0.);
tx = x + sys->pos.x * map_zoom;
ty = y + sys->pos.y * map_zoom;
glVertex2d(tx, ty);
COLOUR(*col);
tx += (systems_stack[sys->jumps[j]].pos.x - sys->pos.x)/2. * map_zoom;
ty += (systems_stack[sys->jumps[j]].pos.y - sys->pos.y)/2. * map_zoom;
glVertex2d(tx, ty);
ACOLOUR(*col, 0.);
tx = x + systems_stack[sys->jumps[j]].pos.x * map_zoom;
ty = y + systems_stack[sys->jumps[j]].pos.y * map_zoom;
glVertex2d(tx, ty);
glEnd();
}
glShadeModel(GL_FLAT);
}
/* Selected planet. */
if(map_selected != -1) {
sys = &systems_stack[map_selected];
COLOUR(cRed);
gl_drawCircleInRect(x + sys->pos.x*map_zoom, y + sys->pos.y*map_zoom,
r+3., bx, by, w, h);
}
}
/* Map event handling. */
static void map_mouse(SDL_Event* event, double mx, double my) {
int i, j;
double x, y, t;
StarSystem* sys;
t = 13.*15.; /* Threshold. */
mx -= MAP_WIDTH/2 - map_xpos;
my -= MAP_HEIGHT/2 - map_ypos;
switch(event->type) {
case SDL_MOUSEBUTTONDOWN:
if(event->button.button == SDL_BUTTON_WHEELUP)
map_buttonZoom("btnZoomOut");
else if(event->button.button == SDL_BUTTON_WHEELDOWN)
map_buttonZoom("btnZoomIn");
/* Selecting star system. */
else {
for(i = 0; i < systems_nstack; i++) {
sys = &systems_stack[i];
/* Must be reachable. */
if(!map_sysReachable(sys))
continue;
/* Get position. */
x = systems_stack[i].pos.x * map_zoom;
y = systems_stack[i].pos.y * map_zoom;
if((pow2(mx-x)+pow2(my-y)) < t) {
/* Select the current system and make a path to it. */
map_selected = i;
if(map_path)
free(map_path);
map_path = system_getJumpPath(&map_npath,
cur_system->name, sys->name);
if(map_npath == 0)
hyperspace_target = -1;
else
/* See if it a valid hyperspace target. */
for(j = 0; j < cur_system->njumps; j++) {
if(map_path[0] == &systems_stack[cur_system->jumps[j]]) {
planet_target = -1; /* Override planet_target. */
hyperspace_target = j;
break;
}
}
map_update();
break;
}
}
map_drag = 1;
}
break;
case SDL_MOUSEBUTTONUP:
if(map_drag) map_drag = 0;
break;
case SDL_MOUSEMOTION:
if(map_drag) {
/* Axis is inverted. */
map_xpos -= event->motion.xrel;
map_ypos += event->motion.yrel;
}
break;
}
}
static void map_buttonZoom(char* str) {
if(strcmp(str, "btnZoomIn")==0) {
map_zoom += (map_zoom >= 1.) ? 0.5 : 0.25;
map_zoom = MIN(2.5, map_zoom);
}
else if(strcmp(str, "btnZoomOut")==0) {
map_zoom -= (map_zoom > 1.) ? 0.5 : 0.25;
map_zoom = MAX(0.5, map_zoom);
}
}
/* Set the map to sane defaults. */
void map_clear(void) {
map_zoom = 1.;
if(cur_system != NULL) {
map_xpos = cur_system->pos.x;
map_ypos = cur_system->pos.y;
} else {
map_xpos = 0.;
map_ypos = 0.;
}
if(map_path != NULL) {
free(map_path);
map_path = NULL;
map_npath = 0;
}
/* Default system is current system. */
map_selectCur();
}
static void map_selectCur(void) {
int i;
if(cur_system != NULL) {
for(i = 0; i < systems_nstack; i++) {
if(&systems_stack[i] == cur_system) {
map_selected = i;
break;
}
}
} else {
/* Probably going to seg fault now.. */
map_selected = -1;
}
}
/* Update the map after a jump. */
void map_jump(void) {
int j;
/* Set selected system to self. */
map_selectCur();
map_xpos = cur_system->pos.x;
map_ypos = cur_system->pos.y;
/* Update path if set. */
if(map_path != NULL) {
map_npath--;
if(map_npath == 0) {
/* Path is empty. */
free(map_path);
map_path = NULL;
} else {
/* Get rid of bottom of the path. */
memcpy(&map_path[0], &map_path[1], sizeof(StarSystem*) * map_npath);
map_path = realloc(map_path, sizeof(StarSystem*) * map_npath);
/* Set the next jump to be the next in path. */
for(j = 0; j < cur_system->njumps; j++) {
if(map_path[0] == &systems_stack[cur_system->jumps[j]]) {
planet_target = -1; /* Override planet_target. */
hyperspace_target = j;
break;
}
}
}
}
}