Merge branch 'master' of git://github.com/BTAxis/naev into testmission
[naev.git] / src / map.c
blobb6612d3c33ec0d277f5f27db2f6ae5aac5510021
1 /*
2 * See Licensing and Copyright notice in naev.h
3 */
6 #include "map.h"
8 #include "naev.h"
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <math.h>
13 #include <float.h>
15 #include "log.h"
16 #include "toolkit.h"
17 #include "space.h"
18 #include "opengl.h"
19 #include "mission.h"
20 #include "colour.h"
21 #include "player.h"
24 #define MAP_WDWNAME "Star Map" /**< Map window name. */
26 #define BUTTON_WIDTH 80 /**< Map button width. */
27 #define BUTTON_HEIGHT 30 /**< Map button height. */
30 #define MAP_LOOP_PROT 1000 /**< Number of iterations max in pathfinding before
31 aborting. */
34 static double map_zoom = 1.; /**< Zoom of the map. */
35 static double map_xpos = 0.; /**< Map X position. */
36 static double map_ypos = 0.; /**< Map Y .osition. */
37 static int map_drag = 0; /**< Is the user dragging the map? */
38 static int map_selected = -1; /**< What system is selected on the map. */
39 static StarSystem **map_path = NULL; /**< The path to current selected system. */
40 int map_npath = 0; /**< Number of systems in map_path. */
41 glTexture *gl_faction_disk = NULL; /**< Texture of the disk representing factions. */
43 /* VBO. */
44 static gl_vbo *map_vbo = NULL; /**< Map VBO. */
48 * extern
50 /* space.c */
51 extern StarSystem *systems_stack;
52 extern int systems_nstack;
56 * prototypes
58 static void map_update( unsigned int wid );
59 static void map_render( double bx, double by, double w, double h, void *data );
60 static void map_mouse( unsigned int wid, SDL_Event* event, double mx, double my,
61 double w, double h, void *data );
62 static void map_setZoom( double zoom );
63 static void map_buttonZoom( unsigned int wid, char* str );
64 static void map_selectCur (void);
65 static void map_drawMarker( double x, double y, double r,
66 int num, int cur, int type );
69 /**
70 * @brief Initializes the map subsystem.
72 * @return 0 on success.
74 int map_init (void)
76 /* Create the VBO. */
77 map_vbo = gl_vboCreateStream( sizeof(GLfloat) * 3*(2+4), NULL );
78 return 0;
82 /**
83 * @brief Destroys the map subsystem.
85 void map_exit (void)
87 /* Destroy the VBO. */
88 if (map_vbo != NULL) {
89 gl_vboDestroy(map_vbo);
90 map_vbo = NULL;
95 /**
96 * @brief Opens the map window.
98 void map_open (void)
100 unsigned int wid;
101 StarSystem *cur;
102 int w,h;
104 /* Destroy window if exists. */
105 wid = window_get(MAP_WDWNAME);
106 if (wid > 0) {
107 window_destroy( wid );
108 return;
111 /* set position to focus on current system */
112 map_xpos = cur_system->pos.x;
113 map_ypos = cur_system->pos.y;
115 /* mark systems as needed */
116 mission_sysMark();
118 /* Attempt to select current map if none is selected */
119 if (map_selected == -1)
120 map_selectCur();
122 /* get the selected system. */
123 cur = system_getIndex( map_selected );
125 /* Set up window size. */
126 w = MAX(600, SCREEN_W - 100);
127 h = MAX(540, SCREEN_H - 100);
129 /* create the window. */
130 wid = window_create( MAP_WDWNAME, -1, -1, w, h );
131 window_setCancel( wid, window_close );
134 * SIDE TEXT
136 * $System
138 * Faction:
139 * $Faction (or Multiple)
141 * Status:
142 * $Status
144 * Planets:
145 * $Planet1, $Planet2, ...
147 * Services:
148 * $Services
150 * ...
151 * [Autonav]
152 * [ Close ]
155 /* System Name */
156 window_addText( wid, -20, -20, 100, 20, 1, "txtSysname",
157 &gl_defFont, &cDConsole, cur->name );
158 /* Faction */
159 window_addImage( wid, -20-64, -60-64, "imgFaction", NULL, 0 );
160 window_addText( wid, -20, -60, 90, 20, 0, "txtSFaction",
161 &gl_smallFont, &cDConsole, "Faction:" );
162 window_addText( wid, -20, -60-gl_smallFont.h-5, 80, 100, 0, "txtFaction",
163 &gl_smallFont, &cBlack, NULL );
164 /* Standing */
165 window_addText( wid, -20, -100, 90, 20, 0, "txtSStanding",
166 &gl_smallFont, &cDConsole, "Standing:" );
167 window_addText( wid, -20, -100-gl_smallFont.h-5, 80, 100, 0, "txtStanding",
168 &gl_smallFont, &cBlack, NULL );
169 /* Security. */
170 window_addText( wid, -20, -140, 90, 20, 0, "txtSSecurity",
171 &gl_smallFont, &cDConsole, "Security:" );
172 window_addText( wid, -20, -140-gl_smallFont.h-5, 80, 100, 0, "txtSecurity",
173 &gl_smallFont, &cBlack, NULL );
174 /* Planets */
175 window_addText( wid, -20, -180, 90, 20, 0, "txtSPlanets",
176 &gl_smallFont, &cDConsole, "Planets:" );
177 window_addText( wid, -20, -180-gl_smallFont.h-5, 80, 100, 0, "txtPlanets",
178 &gl_smallFont, &cBlack, NULL );
179 /* Services */
180 window_addText( wid, -20, -220, 90, 20, 0, "txtSServices",
181 &gl_smallFont, &cDConsole, "Services:" );
182 window_addText( wid, -20, -220-gl_smallFont.h-5, 80, 100, 0, "txtServices",
183 &gl_smallFont, &cBlack, NULL );
184 /* Close button */
185 window_addButton( wid, -20, 20, BUTTON_WIDTH, BUTTON_HEIGHT,
186 "btnClose", "Close", window_close );
187 /* Autonav button */
188 window_addButton( wid, -20, 60, BUTTON_WIDTH, BUTTON_HEIGHT,
189 "btnAutonav", "Autonav", player_startAutonavWindow );
192 * Bottom stuff
194 * [+] [-] Nebula, Asteroids, Interference
196 /* Zoom buttons */
197 window_addButton( wid, 40, 20, 30, 30, "btnZoomIn", "+", map_buttonZoom );
198 window_addButton( wid, 80, 20, 30, 30, "btnZoomOut", "-", map_buttonZoom );
199 /* Situation text */
200 window_addText( wid, 140, 10, w - 80 - 30 - 30, 30, 0,
201 "txtSystemStatus", &gl_smallFont, &cBlack, NULL );
204 * The map itself.
206 map_show( wid, 20, -40, w-150, h-100, 1. ); /* Reset zoom. */
208 map_update( wid );
211 * Disable Autonav button if player lacks fuel.
213 if (player->fuel < HYPERSPACE_FUEL)
214 window_disableButton( wid, "btnAutonav" );
218 * @brief Updates the map window.
220 * @param wid Window id.
222 static void map_update( unsigned int wid )
224 int i;
225 StarSystem* sys;
226 int f, y, h, multiple_faction;
227 double standing, nstanding;
228 unsigned int services;
229 char buf[PATH_MAX];
230 int p;
231 glTexture *logo;
233 /* Needs map to update. */
234 if (!map_isOpen())
235 return;
237 sys = system_getIndex( map_selected );
239 /* Not known and no markers. */
240 if (!(sys_isFlag(sys, SYSTEM_MARKED | SYSTEM_CMARKED)) &&
241 !sys_isKnown(sys) && !space_sysReachable(sys)) {
242 map_selectCur();
243 sys = system_getIndex( map_selected );
247 * Right Text
249 if (!sys_isKnown(sys)) { /* System isn't known, erase all */
251 * Right Text
253 if (sys_isFlag(sys, SYSTEM_MARKED | SYSTEM_CMARKED))
254 window_modifyText( wid, "txtSysname", sys->name );
255 else
256 window_modifyText( wid, "txtSysname", "Unknown" );
257 window_modifyImage( wid, "imgFaction", NULL );
258 window_moveWidget( wid, "txtSFaction", -20, -60 );
259 window_moveWidget( wid, "txtFaction", -20, -60-gl_smallFont.h-5 );
260 window_modifyText( wid, "txtFaction", "Unknown" );
261 /* Standing */
262 window_moveWidget( wid, "txtSStanding", -20, -100 );
263 window_moveWidget( wid, "txtStanding", -20, -100-gl_smallFont.h-5 );
264 window_modifyText( wid, "txtStanding", "Unknown" );
265 /* Security. */
266 window_moveWidget( wid, "txtSSecurity", -20, -140 );
267 window_moveWidget( wid, "txtSecurity", -20, -140-gl_smallFont.h-5 );
268 window_modifyText( wid, "txtSecurity", "Unknown" );
269 /* Planets */
270 window_moveWidget( wid, "txtSPlanets", -20, -180 );
271 window_moveWidget( wid, "txtPlanets", -20, -180-gl_smallFont.h-5 );
272 window_modifyText( wid, "txtPlanets", "Unknown" );
273 /* Services */
274 window_moveWidget( wid, "txtSServices", -20, -220 );
275 window_moveWidget( wid, "txtServices", -20, -220-gl_smallFont.h-5 );
276 window_modifyText( wid, "txtServices", "Unknown" );
279 * Bottom Text
281 window_modifyText( wid, "txtSystemStatus", NULL );
282 return;
285 /* System is known */
286 window_modifyText( wid, "txtSysname", sys->name );
288 standing = 0.;
289 nstanding = 0.;
290 f = -1;
291 multiple_faction = 0;
292 for (i=0; i<sys->nplanets; i++) {
293 if ((f==-1) && (sys->planets[i]->faction>0)) {
294 f = sys->planets[i]->faction;
295 standing += faction_getPlayer( f );
296 nstanding++;
298 else if (f != sys->planets[i]->faction && /** @todo more verbosity */
299 (sys->planets[i]->faction>0)) {
300 snprintf( buf, PATH_MAX, "Multiple" );
301 multiple_faction = 1;
302 break;
305 if (f == -1) {
306 window_modifyImage( wid, "imgFaction", NULL );
307 window_moveWidget( wid, "txtSFaction", -20, -60 );
308 window_moveWidget( wid, "txtFaction", -20, -60-gl_smallFont.h-5 );
309 window_modifyText( wid, "txtFaction", "NA" );
310 window_moveWidget( wid, "txtSStanding", -20, -100 );
311 window_moveWidget( wid, "txtStanding", -20, -100-gl_smallFont.h-5 );
312 window_modifyText( wid, "txtStanding", "NA" );
313 y = -100;
316 else {
317 if (i==sys->nplanets) /* saw them all and all the same */
318 snprintf( buf, PATH_MAX, "%s", faction_longname(f) );
320 y = -60;
322 /* Modify the image. */
323 logo = faction_logoSmall(f);
324 window_modifyImage( wid, "imgFaction", logo );
325 if (logo != NULL) {
326 window_moveWidget( wid, "imgFaction",
327 -(90-logo->w)/2-20-logo->w, y-(64-logo->h)/2-logo->h );
328 y -= 64 + 10;
331 /* Modify the text */
332 window_modifyText( wid, "txtFaction", buf );
333 window_modifyText( wid, "txtStanding",
334 faction_getStanding( standing / nstanding ) );
336 /* Lower text if needed */
337 window_moveWidget( wid, "txtSFaction", -20, y );
338 window_moveWidget( wid, "txtFaction", -20, y-gl_smallFont.h-5 );
339 h = gl_printHeightRaw( &gl_smallFont, 80, buf );
340 window_moveWidget( wid, "txtSStanding", -20, y );
341 window_moveWidget( wid, "txtStanding", -20, y-gl_smallFont.h-5 );
342 y -= 40 + (h - gl_smallFont.h);
343 window_moveWidget( wid, "txtSStanding", -20, y );
344 window_moveWidget( wid, "txtStanding", -20, y-gl_smallFont.h-5 );
347 /* Get security. */
348 y -= 40;
349 if (sys->nfleets == 0)
350 snprintf(buf, PATH_MAX, "NA" );
351 else
352 snprintf(buf, PATH_MAX, "%.0f %%", sys->security * 100.);
353 window_moveWidget( wid, "txtSSecurity", -20, y );
354 window_moveWidget( wid, "txtSecurity", -20, y-gl_smallFont.h-5 );
355 window_modifyText( wid, "txtSecurity", buf );
357 /* Get planets */
358 if (sys->nplanets == 0) {
359 strncpy( buf, "None", PATH_MAX );
360 window_modifyText( wid, "txtPlanets", buf );
362 else {
363 p = 0;
364 buf[0] = '\0';
365 if (sys->nplanets > 0)
366 p += snprintf( &buf[p], PATH_MAX-p, "%s", sys->planets[0]->name );
367 for (i=1; i<sys->nplanets; i++) {
368 p += snprintf( &buf[p], PATH_MAX-p, ",\n%s", sys->planets[i]->name );
371 window_modifyText( wid, "txtPlanets", buf );
373 y -= 40;
374 window_moveWidget( wid, "txtSPlanets", -20, y );
375 window_moveWidget( wid, "txtPlanets", -20, y-gl_smallFont.h-5 );
377 /* Get the services */
378 h = gl_printHeightRaw( &gl_smallFont, 80, buf );
379 y -= 40 + (h - gl_smallFont.h);
380 window_moveWidget( wid, "txtSServices", -20, y );
381 window_moveWidget( wid, "txtServices", -20, y-gl_smallFont.h-5 );
382 services = 0;
383 for (i=0; i<sys->nplanets; i++)
384 services |= sys->planets[i]->services;
385 buf[0] = '\0';
386 p = 0;
387 /*snprintf(buf, sizeof(buf), "%f\n", sys->prices[0]);*/ /*Hack to control prices. */
388 if (services & PLANET_SERVICE_COMMODITY)
389 p += snprintf( &buf[p], PATH_MAX-p, "Commodity\n");
390 if (services & PLANET_SERVICE_OUTFITS)
391 p += snprintf( &buf[p], PATH_MAX-p, "Outfits\n");
392 if (services & PLANET_SERVICE_SHIPYARD)
393 p += snprintf( &buf[p], PATH_MAX-p, "Shipyard\n");
394 if (buf[0] == '\0')
395 p += snprintf( &buf[p], PATH_MAX-p, "None");
396 window_modifyText( wid, "txtServices", buf );
400 * System Status
402 buf[0] = '\0';
403 p = 0;
404 /* Nebula. */
405 if (sys->nebu_density > 0.) {
407 /* Volatility */
408 if (sys->nebu_volatility > 700.)
409 p += snprintf(&buf[p], PATH_MAX-p, " Volatile");
410 else if (sys->nebu_volatility > 300.)
411 p += snprintf(&buf[p], PATH_MAX-p, " Dangerous");
412 else if (sys->nebu_volatility > 0.)
413 p += snprintf(&buf[p], PATH_MAX-p, " Unstable");
415 /* Density */
416 if (sys->nebu_density > 700.)
417 p += snprintf(&buf[p], PATH_MAX-p, " Dense");
418 else if (sys->nebu_density < 300.)
419 p += snprintf(&buf[p], PATH_MAX-p, " Light");
420 p += snprintf(&buf[p], PATH_MAX-p, " Nebula");
422 /* Interference. */
423 if (sys->interference > 0.) {
425 if (buf[0] != '\0')
426 p += snprintf(&buf[p], PATH_MAX-p, ",");
428 /* Density. */
429 if (sys->interference > 700.)
430 p += snprintf(&buf[p], PATH_MAX-p, " Dense");
431 else if (sys->interference < 300.)
432 p += snprintf(&buf[p], PATH_MAX-p, " Light");
434 p += snprintf(&buf[p], PATH_MAX-p, " Interference");
436 window_modifyText( wid, "txtSystemStatus", buf );
441 * @brief Checks to see if the map is open.
443 * @return 0 if map is closed, non-zero if it's open.
445 int map_isOpen (void)
447 return window_exists(MAP_WDWNAME);
452 * @brief Draws a mission marker on the map.
454 * @param x X position to draw at.
455 * @param y Y position to draw at.
456 * @param r Radius of system.
457 * @param num Total number of markers.
458 * @param cur Current marker to draw.
459 * @param type Type to draw.
461 static void map_drawMarker( double x, double y, double r,
462 int num, int cur, int type )
464 const double beta = M_PI / 9;
465 static const glColour* colours[] = {
466 &cGreen, &cBlue, &cRed, &cOrange
469 int i;
470 double alpha, cos_alpha, sin_alpha;
471 GLfloat vertex[3*(2+4)];
474 /* Calculate the angle. */
475 if ((num == 1) || (num == 2) || (num == 4))
476 alpha = M_PI/4.;
477 else if (num == 3)
478 alpha = M_PI/6.;
479 else if (num == 5)
480 alpha = M_PI/10.;
481 else
482 alpha = M_PI/2.;
484 alpha += M_PI*2. * (double)cur/(double)num;
485 cos_alpha = r * cos(alpha);
486 sin_alpha = r * sin(alpha);
487 r = 3 * r;
489 /* Draw the marking triangle. */
490 vertex[0] = x + cos_alpha;
491 vertex[1] = y + sin_alpha;
492 vertex[2] = x + cos_alpha + r * cos(beta + alpha);
493 vertex[3] = y + sin_alpha + r * sin(beta + alpha);
494 vertex[4] = x + cos_alpha + r * cos(beta - alpha);
495 vertex[5] = y + sin_alpha - r * sin(beta - alpha);
497 for (i=0; i<3; i++) {
498 vertex[6 + 4*i + 0] = colours[type]->r;
499 vertex[6 + 4*i + 1] = colours[type]->g;
500 vertex[6 + 4*i + 2] = colours[type]->b;
501 vertex[6 + 4*i + 3] = colours[type]->a;
504 glEnable(GL_POLYGON_SMOOTH);
505 gl_vboSubData( map_vbo, 0, sizeof(GLfloat) * 3*(2+4), vertex );
506 gl_vboActivateOffset( map_vbo, GL_VERTEX_ARRAY, 0, 2, GL_FLOAT, 0 );
507 gl_vboActivateOffset( map_vbo, GL_COLOR_ARRAY,
508 sizeof(GLfloat) * 2*3, 4, GL_FLOAT, 0 );
509 glDrawArrays( GL_TRIANGLES, 0, 3 );
510 gl_vboDeactivate();
511 glDisable(GL_POLYGON_SMOOTH);
515 * @brief Generates a texture to represent factions
517 * @param radius radius of the disk
518 * @return the texture
520 static glTexture *gl_genFactionDisk( int radius )
522 int i, j;
523 uint8_t *pixels;
524 SDL_Surface *sur;
525 int dist;
526 double alpha;
528 /* Calculate parameters. */
529 const int w = 2 * radius + 1;
530 const int h = 2 * radius + 1;
532 /* Create the surface. */
533 sur = SDL_CreateRGBSurface( SDL_SRCALPHA | SDL_HWSURFACE, w, h, 32, RGBAMASK );
535 pixels = sur->pixels;
536 memset(pixels, 0xff, sizeof(uint8_t) * 4 * h * w);
538 /* Generate the circle. */
539 SDL_LockSurface( sur );
541 /* Draw the circle with filter. */
542 for (i=0; i<h; i++) {
543 for (j=0; j<w; j++) {
544 /* Calculate blur. */
545 dist = (i - radius) * (i - radius) + (j - radius) * (j - radius);
546 alpha = 0.;
548 if (dist < radius * radius) {
549 /* Computes alpha with an empirically chosen formula.
550 * This formula accounts for the fact that the eyes
551 * has a logarithmic sensitivity to light */
552 alpha = 1. * dist / (radius * radius);
553 alpha = (exp(1 / (alpha + 1) - 0.5) - 1) * 0xFF;
556 /* Sets the pixel alpha which is the forth byte
557 * in the pixel representation. */
558 pixels[i*sur->pitch + j*4 + 3] = (uint8_t)alpha;
562 SDL_UnlockSurface( sur );
564 /* Return texture. */
565 return gl_loadImage( sur, OPENGL_TEX_MIPMAPS );
569 * @brief Renders the custom map widget.
571 * @param bx Base X position to render at.
572 * @param by Base Y position to render at.
573 * @param w Width of the widget.
574 * @param h Height of the widget.
576 static void map_render( double bx, double by, double w, double h, void *data )
578 (void) data;
579 int i,j, n,m;
580 double x,y,r, tx,ty, fuel;
581 StarSystem *sys, *jsys, *hsys, *lsys;
582 glColour *col, c;
583 GLfloat vertex[8*(2+4)];
584 int sw, sh;
586 /* Parameters. */
587 r = round(CLAMP(5., 15., 6.*map_zoom));
588 x = round((bx - map_xpos + w/2) * 1.);
589 y = round((by - map_ypos + h/2) * 1.);
591 /* background */
592 gl_renderRect( bx, by, w, h, &cBlack );
595 * First pass renders everything almost (except names and markers).
597 for (i=0; i<systems_nstack; i++) {
598 sys = system_getIndex( i );
600 /* check to make sure system is known or adjacent to known (or marked) */
601 if (!sys_isFlag(sys, SYSTEM_MARKED | SYSTEM_CMARKED)
602 && !space_sysReachable(sys))
603 continue;
605 tx = x + sys->pos.x*map_zoom;
606 ty = y + sys->pos.y*map_zoom;
608 /* draws the disk representing the faction */
609 if (sys_isKnown(sys) && (sys->faction != -1)) {
610 sw = gl_faction_disk->sw;
611 sh = gl_faction_disk->sw;
613 col = faction_colour(sys->faction);
614 c.r = col->r;
615 c.g = col->g;
616 c.b = col->b;
617 c.a = 0.7;
619 gl_blitTexture(
620 gl_faction_disk,
621 tx - sw/2, ty - sh/2, sw, sh,
622 0., 0., gl_faction_disk->srw, gl_faction_disk->srw, &c );
625 /* Draw the system. */
626 if (!sys_isKnown(sys) || (sys->nfleets==0)) col = &cInert;
627 else if (sys->security >= 1.) col = &cGreen;
628 else if (sys->security >= 0.6) col = &cOrange;
629 else if (sys->security >= 0.3) col = &cRed;
630 else col = &cDarkRed;
632 gl_drawCircleInRect( tx, ty, r, bx, by, w, h, col, 0 );
634 /* If system is known fill it. */
635 if (sys_isKnown(sys) && (sys->nplanets > 0)) {
636 /* Planet colours */
637 if (!sys_isKnown(sys)) col = &cInert;
638 else if (sys->nplanets==0) col = &cInert;
639 else col = faction_getColour( sys->faction);
641 /* Radius slightly shorter. */
642 gl_drawCircleInRect( tx, ty, 0.5*r, bx, by, w, h, col, 1 );
645 if (!sys_isKnown(sys))
646 continue; /* we don't draw hyperspace lines */
648 /* draw the hyperspace paths */
649 glShadeModel(GL_SMOOTH);
650 col = &cDarkBlue;
651 /* first we draw all of the paths. */
652 for (j=0; j<sys->njumps; j++) {
654 jsys = system_getIndex( sys->jumps[j] );
655 if (hyperspace_target != -1)
656 hsys = system_getIndex( cur_system->jumps[hyperspace_target] );
658 /* Draw the lines. */
659 vertex[0] = x + sys->pos.x * map_zoom;
660 vertex[1] = y + sys->pos.y * map_zoom;
661 vertex[2] = vertex[0] + (jsys->pos.x - sys->pos.x)/2. * map_zoom;
662 vertex[3] = vertex[1] + (jsys->pos.y - sys->pos.y)/2. * map_zoom;
663 vertex[4] = x + jsys->pos.x * map_zoom;
664 vertex[5] = y + jsys->pos.y * map_zoom;
665 vertex[6] = col->r;
666 vertex[7] = col->g;
667 vertex[8] = col->b;
668 vertex[9] = 0.;
669 vertex[10] = col->r;
670 vertex[11] = col->g;
671 vertex[12] = col->b;
672 vertex[13] = col->a;
673 vertex[14] = col->r;
674 vertex[15] = col->g;
675 vertex[16] = col->b;
676 vertex[17] = 0.;
677 gl_vboSubData( map_vbo, 0, sizeof(GLfloat) * 3*(2+4), vertex );
678 gl_vboActivateOffset( map_vbo, GL_VERTEX_ARRAY, 0, 2, GL_FLOAT, 0 );
679 gl_vboActivateOffset( map_vbo, GL_COLOR_ARRAY,
680 sizeof(GLfloat) * 2*3, 4, GL_FLOAT, 0 );
681 glDrawArrays( GL_LINE_STRIP, 0, 3 );
682 gl_vboDeactivate();
684 glShadeModel( GL_FLAT );
687 /* Now we'll draw over the lines with the new pathways. */
688 if (map_path != NULL) {
689 lsys = cur_system;
690 glShadeModel(GL_SMOOTH);
691 col = &cGreen;
692 fuel = player->fuel;
694 for (j=0; j<map_npath; j++) {
695 jsys = map_path[j];
696 if (fuel == player->fuel && fuel > 100.)
697 col = &cGreen;
698 else if (fuel < 100.)
699 col = &cRed;
700 else
701 col = &cYellow;
702 fuel -= 100;
704 /* Draw the lines. */
705 vertex[0] = x + lsys->pos.x * map_zoom;
706 vertex[1] = y + lsys->pos.y * map_zoom;
707 vertex[2] = vertex[0] + (jsys->pos.x - lsys->pos.x)/2. * map_zoom;
708 vertex[3] = vertex[1] + (jsys->pos.y - lsys->pos.y)/2. * map_zoom;
709 vertex[4] = x + jsys->pos.x * map_zoom;
710 vertex[5] = y + jsys->pos.y * map_zoom;
711 vertex[6] = col->r;
712 vertex[7] = col->g;
713 vertex[8] = col->b;
714 vertex[9] = 0.;
715 vertex[10] = col->r;
716 vertex[11] = col->g;
717 vertex[12] = col->b;
718 vertex[13] = col->a;
719 vertex[14] = col->r;
720 vertex[15] = col->g;
721 vertex[16] = col->b;
722 vertex[17] = 0.;
723 gl_vboSubData( map_vbo, 0, sizeof(GLfloat) * 3*(2+4), vertex );
724 gl_vboActivateOffset( map_vbo, GL_VERTEX_ARRAY, 0, 2, GL_FLOAT, 0 );
725 gl_vboActivateOffset( map_vbo, GL_COLOR_ARRAY,
726 sizeof(GLfloat) * 2*3, 4, GL_FLOAT, 0 );
727 glDrawArrays( GL_LINE_STRIP, 0, 3 );
728 gl_vboDeactivate();
730 lsys = jsys;
733 glShadeModel( GL_FLAT );
737 * Second pass - System names
739 for (i=0; i<systems_nstack; i++) {
740 sys = system_getIndex( i );
742 /* Skip system. */
743 if (!sys_isKnown(sys) || (map_zoom <= 0.5 ))
744 continue;
746 tx = x + (sys->pos.x+11.) * map_zoom;
747 ty = y + (sys->pos.y-5.) * map_zoom;
748 gl_print( &gl_smallFont,
749 tx + SCREEN_W/2., ty + SCREEN_H/2.,
750 &cWhite, sys->name );
755 * Third pass - system markers
757 for (i=0; i<systems_nstack; i++) {
758 sys = system_getIndex( i );
760 /* We only care about marked now. */
761 if (!sys_isFlag(sys, SYSTEM_MARKED | SYSTEM_CMARKED))
762 continue;
764 /* Get the position. */
765 tx = x + sys->pos.x*map_zoom;
766 ty = y + sys->pos.y*map_zoom;
768 /* Count markers. */
769 n = (sys_isFlag(sys, SYSTEM_CMARKED)) ? 1 : 0;
770 n += sys->markers_misc;
771 n += sys->markers_cargo;
772 n += sys->markers_rush;
774 /* Draw the markers. */
775 j = 0;
776 if (sys_isFlag(sys, SYSTEM_CMARKED)) {
777 map_drawMarker( tx, ty, r, n, j, 0 );
778 j++;
780 for (m=0; m<sys->markers_misc; m++) {
781 map_drawMarker( tx, ty, r, n, j, 1 );
782 j++;
784 for (m=0; m<sys->markers_rush; m++) {
785 map_drawMarker( tx, ty, r, n, j, 2 );
786 j++;
788 for (m=0; m<sys->markers_cargo; m++) {
789 map_drawMarker( tx, ty, r, n, j, 3 );
790 j++;
794 /* Selected planet. */
795 if (map_selected != -1) {
796 sys = system_getIndex( map_selected );
797 gl_drawCircleInRect( x + sys->pos.x * map_zoom, y + sys->pos.y * map_zoom,
798 1.5*r, bx, by, w, h, &cRed, 0 );
801 /* Current planet. */
802 gl_drawCircleInRect( x + cur_system->pos.x * map_zoom,
803 y + cur_system->pos.y * map_zoom,
804 1.5*r, bx, by, w, h, &cRadar_tPlanet, 0 );
807 * @brief Map custom widget mouse handling.
809 * @param wid Window sending events.
810 * @param event Event window is sending.
811 * @param mx Mouse X position.
812 * @param my Mouse Y position.
813 * @param w Width of the widget.
814 * @param h Height of the widget.
816 static void map_mouse( unsigned int wid, SDL_Event* event, double mx, double my,
817 double w, double h, void *data )
819 (void) wid;
820 (void) data;
821 int i;
822 double x,y, t;
823 StarSystem *sys;
825 t = 15.*15.; /* threshold */
827 switch (event->type) {
829 case SDL_MOUSEBUTTONDOWN:
830 /* Must be in bounds. */
831 if ((mx < 0.) || (mx > w) || (my < 0.) || (my > h))
832 return;
834 /* Zooming */
835 if (event->button.button == SDL_BUTTON_WHEELUP)
836 map_buttonZoom( 0, "btnZoomIn" );
837 else if (event->button.button == SDL_BUTTON_WHEELDOWN)
838 map_buttonZoom( 0, "btnZoomOut" );
840 /* selecting star system */
841 else {
842 mx -= w/2 - map_xpos;
843 my -= h/2 - map_ypos;
845 for (i=0; i<systems_nstack; i++) {
846 sys = system_getIndex( i );
848 /* must be reachable */
849 if (!sys_isFlag(sys, SYSTEM_MARKED | SYSTEM_CMARKED)
850 && !space_sysReachable(sys))
851 continue;
853 /* get position */
854 x = sys->pos.x * map_zoom;
855 y = sys->pos.y * map_zoom;
857 if ((pow2(mx-x)+pow2(my-y)) < t) {
859 map_select( sys, (SDL_GetModState() & KMOD_SHIFT) );
860 break;
863 map_drag = 1;
865 break;
867 case SDL_MOUSEBUTTONUP:
868 if (map_drag)
869 map_drag = 0;
870 break;
872 case SDL_MOUSEMOTION:
873 if (map_drag) {
874 /* axis is inverted */
875 map_xpos -= event->motion.xrel;
876 map_ypos += event->motion.yrel;
878 break;
882 * @brief Handles the button zoom clicks.
884 * @param wid Unused.
885 * @param str Name of the button creating the event.
887 static void map_buttonZoom( unsigned int wid, char* str )
889 (void) wid;
891 /* Transform coords to normal. */
892 map_xpos /= map_zoom;
893 map_ypos /= map_zoom;
895 /* Apply zoom. */
896 if (strcmp(str,"btnZoomIn")==0) {
897 map_zoom += (map_zoom >= 1.) ? 0.5 : 0.25;
898 map_zoom = MIN(2.5, map_zoom);
900 else if (strcmp(str,"btnZoomOut")==0) {
901 map_zoom -= (map_zoom > 1.) ? 0.5 : 0.25;
902 map_zoom = MAX(0.5, map_zoom);
905 map_setZoom(map_zoom);
907 /* Transform coords back. */
908 map_xpos *= map_zoom;
909 map_ypos *= map_zoom;
914 * @brief Cleans up the map stuff.
916 void map_cleanup (void)
918 map_close();
919 map_clear();
924 * @brief Closes the map.
926 void map_close (void)
928 unsigned int wid;
930 wid = window_get(MAP_WDWNAME);
931 if (wid > 0)
932 window_destroy(wid);
937 * @brief Sets the map to sane defaults
939 void map_clear (void)
941 map_setZoom(1.);
943 if (cur_system != NULL) {
944 map_xpos = cur_system->pos.x;
945 map_ypos = cur_system->pos.y;
947 else {
948 map_xpos = 0.;
949 map_ypos = 0.;
951 if (map_path != NULL) {
952 free(map_path);
953 map_path = NULL;
954 map_npath = 0;
957 /* default system is current system */
958 map_selectCur();
963 * @brief Tries to select the current system.
965 static void map_selectCur (void)
967 if (cur_system != NULL)
968 map_selected = cur_system - systems_stack;
969 else
970 /* will probably segfault now */
971 map_selected = -1;
975 * @brief Updates the map after a jump.
977 void map_jump (void)
979 int j;
981 /* set selected system to self */
982 map_selectCur();
984 map_xpos = cur_system->pos.x;
985 map_ypos = cur_system->pos.y;
987 /* update path if set */
988 if (map_path != NULL) {
989 map_npath--;
990 if (map_npath == 0) { /* path is empty */
991 free (map_path);
992 map_path = NULL;
993 planet_target = -1;
994 hyperspace_target = -1;
996 else { /* get rid of bottom of the path */
997 memmove( &map_path[0], &map_path[1], sizeof(StarSystem*) * map_npath );
998 map_path = realloc( map_path, sizeof(StarSystem*) * map_npath );
1000 /* set the next jump to be to the next in path */
1001 for (j=0; j<cur_system->njumps; j++) {
1002 if (map_path[0]==system_getIndex(cur_system->jumps[j])) {
1003 planet_target = -1; /* override planet_target */
1004 hyperspace_target = j;
1005 break;
1015 * @brief Selects the system in the map.
1017 * @param sys System to select.
1019 void map_select( StarSystem *sys, char shifted )
1021 unsigned int wid;
1022 int i;
1024 wid = window_get(MAP_WDWNAME);
1026 if (sys == NULL) {
1027 map_selectCur();
1029 else {
1030 map_selected = sys - systems_stack;
1032 /* select the current system and make a path to it */
1033 if (!shifted) {
1034 if (map_path)
1035 free(map_path);
1036 map_path = NULL;
1037 map_npath = 0;
1040 /* Try to make path if is reachable. */
1041 if (space_sysReachable(sys)) {
1042 if (!shifted) {
1043 map_path = map_getJumpPath( &map_npath,
1044 cur_system->name, sys->name, 0 , NULL );
1046 else {
1047 map_path = map_getJumpPath( &map_npath,
1048 cur_system->name, sys->name, 0 , map_path );
1051 if (map_npath==0) {
1052 hyperspace_target = -1;
1053 player_abortAutonav(NULL);
1055 else {
1056 /* see if it is a valid hyperspace target */
1057 for (i=0; i<cur_system->njumps; i++) {
1058 if (map_path[0] == system_getIndex(cur_system->jumps[i])) {
1059 planet_target = -1; /* override planet_target */
1060 hyperspace_target = i;
1061 player_abortAutonav(NULL);
1062 break;
1067 else { /* unreachable. */
1068 hyperspace_target = -1;
1069 player_abortAutonav(NULL);
1073 map_update(wid);
1077 * A* algorithm for shortest path finding
1080 * @brief Node structure for A* pathfinding.
1082 typedef struct SysNode_ {
1083 struct SysNode_ *next; /**< Next node */
1084 struct SysNode_ *gnext; /**< Next node in the garbage collector. */
1086 struct SysNode_ *parent; /**< Parent node. */
1087 StarSystem* sys; /**< System in node. */
1088 double r; /**< ranking */
1089 int g; /**< step */
1090 } SysNode; /**< System Node for use in A* pathfinding. */
1091 static SysNode *A_gc;
1092 /* prototypes */
1093 static SysNode* A_newNode( StarSystem* sys, SysNode* parent );
1094 static double A_h( StarSystem *n, StarSystem *g );
1095 static double A_g( SysNode* n );
1096 static SysNode* A_add( SysNode *first, SysNode *cur );
1097 static SysNode* A_rm( SysNode *first, StarSystem *cur );
1098 static SysNode* A_in( SysNode *first, StarSystem *cur );
1099 static SysNode* A_lowest( SysNode *first );
1100 static void A_freeList( SysNode *first );
1101 /** @brief Creates a new node linke to star system. */
1102 static SysNode* A_newNode( StarSystem* sys, SysNode* parent )
1104 SysNode* n;
1106 n = malloc(sizeof(SysNode));
1108 n->next = NULL;
1109 n->parent = parent;
1110 n->sys = sys;
1111 n->r = DBL_MAX;
1112 n->g = 0.;
1114 n->gnext = A_gc;
1115 A_gc = n;
1117 return n;
1119 /** @brief Heurestic model to use. */
1120 static double A_h( StarSystem *n, StarSystem *g )
1122 (void)n;
1123 (void)g;
1124 /* Euclidean distance */
1125 /*return sqrt(pow2(n->pos.x - g->pos.x) + pow2(n->pos.y - g->pos.y))/100.;*/
1126 return 0.;
1128 /** @brief Gets the g from a node. */
1129 static double A_g( SysNode* n )
1131 return n->g;
1133 /** @brief Adds a node to the linked list. */
1134 static SysNode* A_add( SysNode *first, SysNode *cur )
1136 SysNode *n;
1138 if (first == NULL)
1139 return cur;
1141 n = first;
1142 while (n->next != NULL)
1143 n = n->next;
1144 n->next = cur;
1146 return first;
1148 /* @brief Removes a node from a linked list. */
1149 static SysNode* A_rm( SysNode *first, StarSystem *cur )
1151 SysNode *n, *p;
1153 if (first->sys == cur) {
1154 n = first->next;
1155 first->next = NULL;
1156 return n;
1159 p = first;
1160 n = p->next;
1161 do {
1162 if (n->sys == cur) {
1163 n->next = NULL;
1164 p->next = n->next;
1165 break;
1167 p = n;
1168 } while ((n=n->next) != NULL);
1170 return first;
1172 /** @brief Checks to see if node is in linked list. */
1173 static SysNode* A_in( SysNode *first, StarSystem *cur )
1175 SysNode *n;
1177 if (first == NULL)
1178 return NULL;
1180 n = first;
1181 do {
1182 if (n->sys == cur)
1183 return n;
1184 } while ((n=n->next) != NULL);
1185 return NULL;
1187 /** @brief Returns the lowest ranking node from a linked list of nodes. */
1188 static SysNode* A_lowest( SysNode *first )
1190 SysNode *lowest, *n;
1192 if (first == NULL)
1193 return NULL;
1195 n = first;
1196 lowest = n;
1197 do {
1198 if (n->r < lowest->r)
1199 lowest = n;
1200 } while ((n=n->next) != NULL);
1201 return lowest;
1203 /** @brief Frees a linked list. */
1204 static void A_freeList( SysNode *first )
1206 SysNode *p, *n;
1208 if (first == NULL)
1209 return;
1211 p = NULL;
1212 n = first;
1213 do {
1214 if (p != NULL)
1215 free(p);
1216 p = n;
1217 } while ((n=n->gnext) != NULL);
1218 free(p);
1221 /** @brief Sets map_zoom to zoom and recreats the faction disk texture. */
1222 void map_setZoom(double zoom)
1224 map_zoom = zoom;
1225 if (gl_faction_disk != NULL)
1226 gl_freeTexture( gl_faction_disk );
1227 gl_faction_disk = gl_genFactionDisk( 50 * zoom );
1231 * @brief Gets the jump path between two systems.
1233 * @param[out] njumps Number of jumps in the path.
1234 * @param sysstart Name of the system to start from.
1235 * @param sysend Name of the system to end at.
1236 * @param ignore_known Whether or not to ignore if systems are known.
1237 * @param the old star system (if we're merely extending the list)
1238 * @return NULL on failure, the list of njumps elements systems in the path.
1240 StarSystem** map_getJumpPath( int* njumps, const char* sysstart,
1241 const char* sysend, int ignore_known, StarSystem** old_data )
1243 int i, j, cost, ojumps;
1245 StarSystem *sys, *ssys, *esys, **res;
1247 SysNode *cur, *neighbour;
1248 SysNode *open, *closed;
1249 SysNode *ocost, *ccost;
1251 A_gc = NULL;
1253 /* initial and target systems */
1254 ssys = system_get(sysstart); /* start */
1255 esys = system_get(sysend); /* goal */
1257 /* Set up. */
1258 ojumps = 0;
1259 if ((old_data != NULL) && (*njumps>0)) {
1260 ssys = system_get( old_data[ (*njumps)-1 ]->name );
1261 ojumps = *njumps;
1264 /* Check self. */
1265 if (ssys == esys) {
1266 (*njumps) = 0;
1267 if (old_data != NULL)
1268 free( old_data );
1269 return NULL;
1272 /* system target must be known and reachable */
1273 if (!ignore_known && !sys_isKnown(esys) && !space_sysReachable(esys)) {
1274 /* can't reach - don't make path */
1275 (*njumps) = 0;
1276 if (old_data != NULL)
1277 free( old_data );
1278 return NULL;
1281 /* start the linked lists */
1282 open = closed = NULL;
1283 cur = A_newNode( ssys, NULL );
1284 open = A_add( open, cur ); /* inital open node is the start system */
1286 j = 0;
1287 while ((cur = A_lowest(open))->sys != esys) {
1289 /* Break if infinite loop. */
1290 j++;
1291 if (j > MAP_LOOP_PROT)
1292 break;
1294 /* get best from open and toss to closed */
1295 open = A_rm( open, cur->sys );
1296 closed = A_add( closed, cur );
1297 cost = A_g(cur) + 1;
1299 for (i=0; i<cur->sys->njumps; i++) {
1300 sys = system_getIndex( cur->sys->jumps[i] );
1302 /* Make sure it's reachable */
1303 if (!ignore_known &&
1304 ((!sys_isKnown(sys) &&
1305 (!sys_isKnown(cur->sys) || !space_sysReachable(esys)))))
1306 continue;
1308 neighbour = A_newNode( sys, NULL );
1310 ocost = A_in(open, sys);
1311 if ((ocost != NULL) && (cost < ocost->g)) {
1312 open = A_rm( open, sys ); /* new path is better */
1315 ccost = A_in(closed, sys);
1316 if (ccost != NULL) {
1317 closed = A_rm( closed, sys ); /* shouldn't happen */
1320 if ((ocost == NULL) && (ccost == NULL)) {
1321 neighbour->g = cost;
1322 neighbour->r = A_g(neighbour) + A_h(cur->sys,sys);
1323 neighbour->parent = cur;
1324 open = A_add( open, neighbour );
1329 /* build path backwards if not broken from loop. */
1330 if (j <= MAP_LOOP_PROT) {
1331 (*njumps) = A_g(cur);
1332 if (old_data == NULL)
1333 res = malloc( sizeof(StarSystem*) * (*njumps) );
1334 else {
1335 *njumps = *njumps + ojumps;
1336 res = realloc( old_data, sizeof(StarSystem*) * (*njumps) );
1338 for (i=0; i<((*njumps)-ojumps); i++) {
1339 res[(*njumps)-i-1] = cur->sys;
1340 cur = cur->parent;
1343 else {
1344 (*njumps) = 0;
1345 res = NULL;
1346 if (old_data != NULL)
1347 free( old_data );
1350 /* free the linked lists */
1351 A_freeList(A_gc);
1352 return res;
1357 * @brief Marks maps around a radius of currenty system as known.
1359 * @param targ_sys System at center of the "known" circle.
1360 * @param r Radius (in jumps) to mark as known.
1361 * @return 0 on success.
1363 int map_map( const char* targ_sys, int r )
1365 int i, dep;
1366 StarSystem *sys, *jsys;
1367 SysNode *closed, *open, *cur, *neighbour;
1369 A_gc = NULL;
1370 open = closed = NULL;
1372 if (targ_sys == NULL) sys = cur_system;
1373 else sys = system_get( targ_sys );
1374 sys_setFlag(sys,SYSTEM_KNOWN);
1375 open = A_newNode( sys, NULL );
1376 open->r = 0;
1378 while ((cur = A_lowest(open)) != NULL) {
1380 /* mark system as known and go to next */
1381 sys = cur->sys;
1382 dep = cur->r;
1383 sys_setFlag(sys,SYSTEM_KNOWN);
1384 open = A_rm( open, sys );
1385 closed = A_add( closed, cur );
1387 /* check it's jumps */
1388 for (i=0; i<sys->njumps; i++) {
1389 jsys = system_getIndex( cur->sys->jumps[i] );
1391 /* System has already been parsed or is too deep */
1392 if ((A_in(closed,jsys) != NULL) || (dep+1 > r))
1393 continue;
1395 /* create new node and such */
1396 neighbour = A_newNode( jsys, NULL );
1397 neighbour->r = dep+1;
1398 open = A_add( open, neighbour );
1402 A_freeList(A_gc);
1403 return 0;
1408 * @brief Check to see if radius is mapped (known).
1410 * @param targ_sys Name of the system in the center of the "known" circle.
1411 * @param r Radius to check (in jumps) if is mapped.
1412 * @return 1 if circle was already mapped, 0 if it wasn't.
1414 int map_isMapped( const char* targ_sys, int r )
1416 int i, dep, ret;
1417 StarSystem *sys, *jsys;
1418 SysNode *closed, *open, *cur, *neighbour;
1420 A_gc = NULL;
1421 open = closed = NULL;
1423 if (targ_sys == NULL)
1424 sys = cur_system;
1425 else
1426 sys = system_get( targ_sys );
1427 open = A_newNode( sys, NULL );
1428 open->r = 0;
1429 ret = 1;
1431 while ((cur = A_lowest(open)) != NULL) {
1433 /* Check if system is known. */
1434 sys = cur->sys;
1435 dep = cur->r;
1436 if (!sys_isFlag(sys,SYSTEM_KNOWN)) {
1437 ret = 0;
1438 break;
1441 /* We close the current system. */
1442 open = A_rm( open, sys );
1443 closed = A_add( closed, cur );
1445 /* System is past the limit. */
1446 if (dep+1 > r)
1447 continue;
1449 /* check it's jumps */
1450 for (i=0; i<sys->njumps; i++) {
1451 jsys = system_getIndex( sys->jumps[i] );
1453 /* SYstem has already been parsed. */
1454 if (A_in(closed,jsys) != NULL)
1455 continue;
1457 /* create new node and such */
1458 neighbour = A_newNode( jsys, NULL );
1459 neighbour->r = dep+1;
1460 open = A_add( open, neighbour );
1464 A_freeList(A_gc);
1465 return ret;
1470 * @brief Shows a map at x, y (relative to wid) with size w,h.
1472 * @param wid Window to show map on.
1473 * @param x X position to put map at.
1474 * @param y Y position to put map at.
1475 * @param w Width of map to open.
1476 * @param h Height of map to open.
1477 * @param zoom Default zoom to use.
1479 void map_show( int wid, int x, int y, int w, int h, double zoom )
1481 StarSystem *sys;
1483 /* mark systems as needed */
1484 mission_sysMark();
1486 /* Set position to focus on current system. */
1487 map_xpos = cur_system->pos.x * zoom;
1488 map_ypos = cur_system->pos.y * zoom;
1490 /* Set zoom. */
1491 map_setZoom(zoom);
1493 /* Make sure selected is sane. */
1494 sys = system_getIndex( map_selected );
1495 if (!(sys_isFlag(sys, SYSTEM_MARKED | SYSTEM_CMARKED)) &&
1496 !sys_isKnown(sys) && !space_sysReachable(sys))
1497 map_selectCur();
1499 window_addCust( wid, x, y, w, h,
1500 "cstMap", 1, map_render, map_mouse, NULL );
1505 * @brief Centers the map on a planet.
1507 * @param sys System to center the map on.
1508 * @return 0 on success.
1510 int map_center( const char *sys )
1512 StarSystem *ssys;
1514 /* Get the system. */
1515 ssys = system_get( sys );
1516 if (sys == NULL)
1517 return -1;
1519 /* Center on the system. */
1520 map_xpos = ssys->pos.x * map_zoom;
1521 map_ypos = ssys->pos.y * map_zoom;
1523 return 0;