#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; } } } } }