2 * See Licensing and Copyright notice in naev.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
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. */
44 static gl_vbo
*map_vbo
= NULL
; /**< Map VBO. */
51 extern StarSystem
*systems_stack
;
52 extern int systems_nstack
;
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
);
70 * @brief Initializes the map subsystem.
72 * @return 0 on success.
77 map_vbo
= gl_vboCreateStream( sizeof(GLfloat
) * 3*(2+4), NULL
);
83 * @brief Destroys the map subsystem.
87 /* Destroy the VBO. */
88 if (map_vbo
!= NULL
) {
89 gl_vboDestroy(map_vbo
);
96 * @brief Opens the map window.
104 /* Destroy window if exists. */
105 wid
= window_get(MAP_WDWNAME
);
107 window_destroy( wid
);
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 */
118 /* Attempt to select current map if none is selected */
119 if (map_selected
== -1)
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
);
139 * $Faction (or Multiple)
145 * $Planet1, $Planet2, ...
156 window_addText( wid
, -20, -20, 100, 20, 1, "txtSysname",
157 &gl_defFont
, &cDConsole
, cur
->name
);
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
);
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
);
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
);
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
);
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
);
185 window_addButton( wid
, -20, 20, BUTTON_WIDTH
, BUTTON_HEIGHT
,
186 "btnClose", "Close", window_close
);
188 window_addButton( wid
, -20, 60, BUTTON_WIDTH
, BUTTON_HEIGHT
,
189 "btnAutonav", "Autonav", player_startAutonavWindow
);
194 * [+] [-] Nebula, Asteroids, Interference
197 window_addButton( wid
, 40, 20, 30, 30, "btnZoomIn", "+", map_buttonZoom
);
198 window_addButton( wid
, 80, 20, 30, 30, "btnZoomOut", "-", map_buttonZoom
);
200 window_addText( wid
, 140, 10, w
- 80 - 30 - 30, 30, 0,
201 "txtSystemStatus", &gl_smallFont
, &cBlack
, NULL
);
206 map_show( wid
, 20, -40, w
-150, h
-100, 1. ); /* Reset zoom. */
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
)
226 int f
, y
, h
, multiple_faction
;
227 double standing
, nstanding
;
228 unsigned int services
;
233 /* Needs map to update. */
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
)) {
243 sys
= system_getIndex( map_selected
);
249 if (!sys_isKnown(sys
)) { /* System isn't known, erase all */
253 if (sys_isFlag(sys
, SYSTEM_MARKED
| SYSTEM_CMARKED
))
254 window_modifyText( wid
, "txtSysname", sys
->name
);
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" );
262 window_moveWidget( wid
, "txtSStanding", -20, -100 );
263 window_moveWidget( wid
, "txtStanding", -20, -100-gl_smallFont
.h
-5 );
264 window_modifyText( wid
, "txtStanding", "Unknown" );
266 window_moveWidget( wid
, "txtSSecurity", -20, -140 );
267 window_moveWidget( wid
, "txtSecurity", -20, -140-gl_smallFont
.h
-5 );
268 window_modifyText( wid
, "txtSecurity", "Unknown" );
270 window_moveWidget( wid
, "txtSPlanets", -20, -180 );
271 window_moveWidget( wid
, "txtPlanets", -20, -180-gl_smallFont
.h
-5 );
272 window_modifyText( wid
, "txtPlanets", "Unknown" );
274 window_moveWidget( wid
, "txtSServices", -20, -220 );
275 window_moveWidget( wid
, "txtServices", -20, -220-gl_smallFont
.h
-5 );
276 window_modifyText( wid
, "txtServices", "Unknown" );
281 window_modifyText( wid
, "txtSystemStatus", NULL
);
285 /* System is known */
286 window_modifyText( wid
, "txtSysname", sys
->name
);
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
);
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;
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" );
317 if (i
==sys
->nplanets
) /* saw them all and all the same */
318 snprintf( buf
, PATH_MAX
, "%s", faction_longname(f
) );
322 /* Modify the image. */
323 logo
= faction_logoSmall(f
);
324 window_modifyImage( wid
, "imgFaction", logo
);
326 window_moveWidget( wid
, "imgFaction",
327 -(90-logo
->w
)/2-20-logo
->w
, y
-(64-logo
->h
)/2-logo
->h
);
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 );
349 if (sys
->nfleets
== 0)
350 snprintf(buf
, PATH_MAX
, "NA" );
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
);
358 if (sys
->nplanets
== 0) {
359 strncpy( buf
, "None", PATH_MAX
);
360 window_modifyText( wid
, "txtPlanets", buf
);
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
);
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 );
383 for (i
=0; i
<sys
->nplanets
; i
++)
384 services
|= sys
->planets
[i
]->services
;
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");
395 p
+= snprintf( &buf
[p
], PATH_MAX
-p
, "None");
396 window_modifyText( wid
, "txtServices", buf
);
405 if (sys
->nebu_density
> 0.) {
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");
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");
423 if (sys
->interference
> 0.) {
426 p
+= snprintf(&buf
[p
], PATH_MAX
-p
, ",");
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
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))
484 alpha
+= M_PI
*2. * (double)cur
/(double)num
;
485 cos_alpha
= r
* cos(alpha
);
486 sin_alpha
= r
* sin(alpha
);
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 );
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
)
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
);
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
)
580 double x
,y
,r
, tx
,ty
, fuel
;
581 StarSystem
*sys
, *jsys
, *hsys
, *lsys
;
583 GLfloat vertex
[8*(2+4)];
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.);
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
))
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
);
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)) {
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
);
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
;
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 );
684 glShadeModel( GL_FLAT
);
687 /* Now we'll draw over the lines with the new pathways. */
688 if (map_path
!= NULL
) {
690 glShadeModel(GL_SMOOTH
);
694 for (j
=0; j
<map_npath
; j
++) {
696 if (fuel
== player
->fuel
&& fuel
> 100.)
698 else if (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
;
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 );
733 glShadeModel( GL_FLAT
);
737 * Second pass - System names
739 for (i
=0; i
<systems_nstack
; i
++) {
740 sys
= system_getIndex( i
);
743 if (!sys_isKnown(sys
) || (map_zoom
<= 0.5 ))
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
))
764 /* Get the position. */
765 tx
= x
+ sys
->pos
.x
*map_zoom
;
766 ty
= y
+ sys
->pos
.y
*map_zoom
;
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. */
776 if (sys_isFlag(sys
, SYSTEM_CMARKED
)) {
777 map_drawMarker( tx
, ty
, r
, n
, j
, 0 );
780 for (m
=0; m
<sys
->markers_misc
; m
++) {
781 map_drawMarker( tx
, ty
, r
, n
, j
, 1 );
784 for (m
=0; m
<sys
->markers_rush
; m
++) {
785 map_drawMarker( tx
, ty
, r
, n
, j
, 2 );
788 for (m
=0; m
<sys
->markers_cargo
; m
++) {
789 map_drawMarker( tx
, ty
, r
, n
, j
, 3 );
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
)
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
))
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 */
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
))
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
) );
867 case SDL_MOUSEBUTTONUP
:
872 case SDL_MOUSEMOTION
:
874 /* axis is inverted */
875 map_xpos
-= event
->motion
.xrel
;
876 map_ypos
+= event
->motion
.yrel
;
882 * @brief Handles the button zoom clicks.
885 * @param str Name of the button creating the event.
887 static void map_buttonZoom( unsigned int wid
, char* str
)
891 /* Transform coords to normal. */
892 map_xpos
/= map_zoom
;
893 map_ypos
/= map_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)
924 * @brief Closes the map.
926 void map_close (void)
930 wid
= window_get(MAP_WDWNAME
);
937 * @brief Sets the map to sane defaults
939 void map_clear (void)
943 if (cur_system
!= NULL
) {
944 map_xpos
= cur_system
->pos
.x
;
945 map_ypos
= cur_system
->pos
.y
;
951 if (map_path
!= NULL
) {
957 /* default system is current system */
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
;
970 /* will probably segfault now */
975 * @brief Updates the map after a jump.
981 /* set selected system to self */
984 map_xpos
= cur_system
->pos
.x
;
985 map_ypos
= cur_system
->pos
.y
;
987 /* update path if set */
988 if (map_path
!= NULL
) {
990 if (map_npath
== 0) { /* path is empty */
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
;
1015 * @brief Selects the system in the map.
1017 * @param sys System to select.
1019 void map_select( StarSystem
*sys
, char shifted
)
1024 wid
= window_get(MAP_WDWNAME
);
1030 map_selected
= sys
- systems_stack
;
1032 /* select the current system and make a path to it */
1040 /* Try to make path if is reachable. */
1041 if (space_sysReachable(sys
)) {
1043 map_path
= map_getJumpPath( &map_npath
,
1044 cur_system
->name
, sys
->name
, 0 , NULL
);
1047 map_path
= map_getJumpPath( &map_npath
,
1048 cur_system
->name
, sys
->name
, 0 , map_path
);
1052 hyperspace_target
= -1;
1053 player_abortAutonav(NULL
);
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
);
1067 else { /* unreachable. */
1068 hyperspace_target
= -1;
1069 player_abortAutonav(NULL
);
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 */
1090 } SysNode
; /**< System Node for use in A* pathfinding. */
1091 static SysNode
*A_gc
;
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
)
1106 n
= malloc(sizeof(SysNode
));
1119 /** @brief Heurestic model to use. */
1120 static double A_h( StarSystem
*n
, StarSystem
*g
)
1124 /* Euclidean distance */
1125 /*return sqrt(pow2(n->pos.x - g->pos.x) + pow2(n->pos.y - g->pos.y))/100.;*/
1128 /** @brief Gets the g from a node. */
1129 static double A_g( SysNode
* n
)
1133 /** @brief Adds a node to the linked list. */
1134 static SysNode
* A_add( SysNode
*first
, SysNode
*cur
)
1142 while (n
->next
!= NULL
)
1148 /* @brief Removes a node from a linked list. */
1149 static SysNode
* A_rm( SysNode
*first
, StarSystem
*cur
)
1153 if (first
->sys
== cur
) {
1162 if (n
->sys
== cur
) {
1168 } while ((n
=n
->next
) != NULL
);
1172 /** @brief Checks to see if node is in linked list. */
1173 static SysNode
* A_in( SysNode
*first
, StarSystem
*cur
)
1184 } while ((n
=n
->next
) != 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
;
1198 if (n
->r
< lowest
->r
)
1200 } while ((n
=n
->next
) != NULL
);
1203 /** @brief Frees a linked list. */
1204 static void A_freeList( SysNode
*first
)
1217 } while ((n
=n
->gnext
) != NULL
);
1221 /** @brief Sets map_zoom to zoom and recreats the faction disk texture. */
1222 void map_setZoom(double 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
;
1253 /* initial and target systems */
1254 ssys
= system_get(sysstart
); /* start */
1255 esys
= system_get(sysend
); /* goal */
1259 if ((old_data
!= NULL
) && (*njumps
>0)) {
1260 ssys
= system_get( old_data
[ (*njumps
)-1 ]->name
);
1267 if (old_data
!= 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 */
1276 if (old_data
!= 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 */
1287 while ((cur
= A_lowest(open
))->sys
!= esys
) {
1289 /* Break if infinite loop. */
1291 if (j
> MAP_LOOP_PROT
)
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
)))))
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
) );
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
;
1346 if (old_data
!= NULL
)
1350 /* free the linked lists */
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
)
1366 StarSystem
*sys
, *jsys
;
1367 SysNode
*closed
, *open
, *cur
, *neighbour
;
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
);
1378 while ((cur
= A_lowest(open
)) != NULL
) {
1380 /* mark system as known and go to next */
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
))
1395 /* create new node and such */
1396 neighbour
= A_newNode( jsys
, NULL
);
1397 neighbour
->r
= dep
+1;
1398 open
= A_add( open
, neighbour
);
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
)
1417 StarSystem
*sys
, *jsys
;
1418 SysNode
*closed
, *open
, *cur
, *neighbour
;
1421 open
= closed
= NULL
;
1423 if (targ_sys
== NULL
)
1426 sys
= system_get( targ_sys
);
1427 open
= A_newNode( sys
, NULL
);
1431 while ((cur
= A_lowest(open
)) != NULL
) {
1433 /* Check if system is known. */
1436 if (!sys_isFlag(sys
,SYSTEM_KNOWN
)) {
1441 /* We close the current system. */
1442 open
= A_rm( open
, sys
);
1443 closed
= A_add( closed
, cur
);
1445 /* System is past the limit. */
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
)
1457 /* create new node and such */
1458 neighbour
= A_newNode( jsys
, NULL
);
1459 neighbour
->r
= dep
+1;
1460 open
= A_add( open
, neighbour
);
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
)
1483 /* mark systems as needed */
1486 /* Set position to focus on current system. */
1487 map_xpos
= cur_system
->pos
.x
* zoom
;
1488 map_ypos
= cur_system
->pos
.y
* 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
))
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
)
1514 /* Get the system. */
1515 ssys
= system_get( sys
);
1519 /* Center on the system. */
1520 map_xpos
= ssys
->pos
.x
* map_zoom
;
1521 map_ypos
= ssys
->pos
.y
* map_zoom
;