naev 0.10.4
map.c
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
5#include <float.h>
6#include <math.h>
7#include <stdio.h>
8#include <stdlib.h>
9
10#include "naev.h"
13#include "map.h"
14
15#include "conf.h"
16#include "array.h"
17#include "colour.h"
18#include "dialogue.h"
19#include "faction.h"
20#include "gui.h"
21#include "log.h"
22#include "mapData.h"
23#include "map_find.h"
24#include "map_system.h"
25#include "mission.h"
26#include "nebula.h"
27#include "ndata.h"
28#include "nmath.h"
29#include "nstring.h"
30#include "nxml.h"
31#include "opengl.h"
32#include "player.h"
33#include "space.h"
34#include "toolkit.h"
35#include "utf8.h"
36
37#define BUTTON_WIDTH 100
38#define BUTTON_HEIGHT 30
39#define MAP_LOOP_PROT 1000
40#define MAP_TEXT_INDENT 45
41#define MAP_MARKER_CYCLE 750
42#define MAP_MOVE_THRESHOLD 20.
43#define EASE_ALPHA ease_QuadraticInOut
45static const int RCOL_X = -10;
46static const int RCOL_TEXT_W = 135;
47static const int RCOL_HEADER_W = 140;
48//static const int RCOL_W = RCOL_HEADER_W - RCOL_X*2; /**< Real width of the right column. */
49/* Below is a hack because older GCC claims the above line is not constant... */
50static const int RCOL_W = 140 - (-10*2);
51static const int BBAR_H = 60;
56typedef struct FactionPresence_ {
57 const char *name;
58 double value;
59 int known;
61
65typedef struct CstMapWidget_ {
66 double xoff;
67 double yoff;
68 double zoom;
69 double xpos;
70 double ypos;
71 double xtarget;
72 double ytarget;
73 int drag;
76 double alpha_env;
77 double alpha_path;
78 double alpha_names;
79 double alpha_commod;
81 MapMode mode;
83
84/* map decorator stack */
85static MapDecorator* decorator_stack = NULL;
87static int map_selected = -1;
88static MapMode map_mode = MAPMODE_TRAVEL;
89static StarSystem **map_path = NULL;
90static int cur_commod = -1;
91static int cur_commod_mode = 0;
92static Commodity **commod_known = NULL;
93static char** map_modes = NULL;
94static int listMapModeVisible = 0;
95static double commod_av_gal_price = 0;
96static double map_dt = 0.;
97static int map_minimal_mode = 0;
98static double map_flyto_speed = 1500.;
99static double map_mx = 0.;
100static double map_my = 0.;
101static char map_show_notes = 0;
103/*
104 * extern
105 */
106/* space.c */
107extern StarSystem *systems_stack;
108
109/*land.c*/
110extern int landed;
111extern Spob* land_spob;
112
113/*
114 * prototypes
115 */
116/* Update. */
117static void map_update_autonav( unsigned int wid );
118static void map_update_status( unsigned int wid, const char *buf );
119static void map_update( unsigned int wid );
120/* Render. */
121static void map_render( double bx, double by, double w, double h, void *data );
122static void map_renderPath( double x, double y, double zoom, double radius, double alpha );
123static void map_renderMarkers( double x, double y, double zoom, double r, double a );
124static void map_renderCommod( double bx, double by, double x, double y,
125 double zoom, double w, double h, double r, int editor, double a );
126static void map_renderCommodIgnorance( double x, double y, double zoom,
127 const StarSystem *sys, const Commodity *c, double a );
128static void map_drawMarker( double x, double y, double zoom,
129 double r, double a, int num, int cur, int type );
130/* Mouse. */
131static void map_focusLose( unsigned int wid, const char* wgtname );
132static int map_mouse( unsigned int wid, SDL_Event* event, double mx, double my,
133 double w, double h, double rx, double ry, void *data );
134/* Misc. */
135static void map_setup (void);
136static void map_updateInternal( CstMapWidget *cst, double dt );
137static void map_reset( CstMapWidget* cst, MapMode mode );
138static CstMapWidget* map_globalCustomData( unsigned int wid );
139static int map_keyHandler( unsigned int wid, SDL_Keycode key, SDL_Keymod mod );
140static void map_buttonZoom( unsigned int wid, const char* str );
141static void map_setMinimal( unsigned int wid, int value );
142static void map_buttonMarkSystem( unsigned int wid, const char* str );
143static void map_buttonSystemMap( unsigned int wid, const char* str );
144static void map_buttonMinimal( unsigned int wid, const char* str );
145static void map_buttonCommodity( unsigned int wid, const char* str );
146static void map_selectCur (void);
147static void map_genModeList(void);
148static void map_update_commod_av_price();
149static void map_onClose( unsigned int wid, const char *str );
150
156int map_init (void)
157{
158 return 0;
159}
160
164void map_exit (void)
165{
166 if (decorator_stack != NULL) {
167 for (int i=0; i<array_size(decorator_stack); i++)
168 gl_freeTexture( decorator_stack[i].image );
169 array_free( decorator_stack );
170 decorator_stack = NULL;
171 }
172}
173
177static int map_keyHandler( unsigned int wid, SDL_Keycode key, SDL_Keymod mod )
178{
179 (void) mod;
180
181 if ((key == SDLK_SLASH) || (key == SDLK_f)) {
182 map_inputFind( wid, NULL );
183 return 1;
184 }
185
186 return 0;
187}
188
189static void map_setup (void)
190{
191 /* Mark systems as discovered as necessary. */
192 for (int i=0; i<array_size(systems_stack); i++) {
193 StarSystem *sys = &systems_stack[i];
194 sys_rmFlag( sys, SYSTEM_DISCOVERED | SYSTEM_INTEREST );
195
196 /* Check to see if system has landable spobs. */
197 sys_rmFlag( sys, SYSTEM_HAS_LANDABLE );
198 for (int j=0; j<array_size(sys->spobs); j++) {
199 Spob *p = sys->spobs[j];
200 if (!spob_isKnown(p))
201 continue;
202 if (!spob_hasService(p, SPOB_SERVICE_LAND))
203 continue;
204 sys_setFlag( sys, SYSTEM_HAS_KNOWN_LANDABLE );
205 spob_updateLand( p );
206 if (p->can_land)
207 sys_setFlag( sys, SYSTEM_HAS_LANDABLE );
208 }
209
210 int known = 1;
211 for (int j=0; j<array_size(sys->jumps); j++) {
212 JumpPoint *jp = &sys->jumps[j];
213 if (jp_isFlag(jp, JP_EXITONLY) || jp_isFlag(jp, JP_HIDDEN))
214 continue;
215 if (!jp_isFlag(jp, JP_KNOWN)) {
216 known = 0;
217 break;
218 }
219 }
220 if (known) {
221 /* Check spobs. */
222 for (int j=0; j<array_size(sys->spobs); j++) {
223 Spob *p = sys->spobs[j];
224 if (!spob_isKnown(p)) {
225 known = 0;
226 break;
227 }
228 }
229 }
230
231 if (known)
232 sys_setFlag( sys, SYSTEM_DISCOVERED );
233 }
234
235 /* mark systems as needed */
237}
238
242void map_open (void)
243{
244 unsigned int wid;
245 StarSystem *cur;
246 int w, h, x, y, rw;
247 //int tw, th;
248 CstMapWidget *cst;
249 const char *title = _("Star Map");
250 const glColour cBG = { 0., 0., 0., 0.95 };
251
252 map_minimal_mode = player.map_minimal;
253 listMapModeVisible = 0;
254
255 /* Not under manual control. */
256 if (pilot_isFlag( player.p, PILOT_MANUAL_CONTROL ))
257 return;
258
259 /* Destroy window if exists. */
260 wid = window_get(MAP_WDWNAME);
261 if (wid > 0) {
262 if (window_isTop(wid))
263 window_destroy( wid );
264 return;
265 }
266
267 /* Set up stuff. */
268 map_setup();
269
270 /* Attempt to select current map if none is selected */
271 if (map_selected == -1)
272 map_selectCur();
273
274 /* get the selected system. */
275 cur = system_getIndex( map_selected );
276
277 /* create the window. */
278 wid = window_create( MAP_WDWNAME, title, -1, -1, -1, -1 );
280 window_onClose( wid, map_onClose );
281 window_handleKeys( wid, map_keyHandler );
282 window_dimWindow( wid, &w, &h );
283 window_setBorder( wid, 0 );
284
285 /*
286 * The map itself.
287 */
288 map_show( wid, 0, 0, w, h, 1., RCOL_W/2., BBAR_H/2. ); /* Reset zoom. */
289
290 /* Map title. */
291#if 0
292 rw = gl_printWidthRaw( NULL, title );
293 tw = rw+30;
294 th = gl_defFont.h + 20;
295 window_addRect( wid, (w-tw)/2., h-th, tw, th, "rctTBar", &cBG, 0 );
296 window_addText( wid, (w-rw)/2., h-gl_defFont.h-10., rw, gl_defFont.h, 1, "txtTitle",
297 &gl_defFont, NULL, title );
298#endif
299
300 /* Overlay background. */
301 window_addRect( wid, w-RCOL_W, 0, RCOL_W, h, "rctRCol", &cBG, 0 );
302 window_addRect( wid, 0, 0, w, BBAR_H, "rctBBar", &cBG, 0 );
303
304 /*
305 * SIDE TEXT
306 *
307 * $System
308 *
309 * Faction:
310 * $Faction (or Multiple)
311 *
312 * Status:
313 * $Status
314 *
315 * Spobs:
316 * $Spob1, $Spob2, ...
317 *
318 * Services:
319 * $Services
320 *
321 * ...
322 * [Autonav]
323 * [ Find ]
324 * [ Close ]
325 */
326 x = RCOL_X; /* Right column X offset. */
327 rw = RCOL_TEXT_W; /* Right column indented width maximum. */
328 y = -20;
329
330 /* System Name */
331 window_addText( wid, -90 + 80, y, 160, 20, 1, "txtSysname",
332 &gl_defFont, NULL, _(cur->name) );
333 y -= 10;
334
335 /* Faction image */
336 window_addImage( wid, -90 + 32, y - 32, 0, 0, "imgFaction", NULL, 0 );
337 y -= 64 + 10;
338
339 /* Faction */
340 window_addText( wid, x, y, RCOL_HEADER_W, 20, 0, "txtSFaction",
341 &gl_smallFont, &cFontGrey, _("Faction:") );
342 window_addText( wid, x, y-gl_smallFont.h-5, rw, 300, 0, "txtFaction",
343 &gl_smallFont, NULL, NULL );
344 y -= 2 * gl_smallFont.h + 5 + 15;
345
346 /* Standing */
347 window_addText( wid, x, y, RCOL_HEADER_W, 20, 0, "txtSStanding",
348 &gl_smallFont, &cFontGrey, _("Standing:") );
349 window_addText( wid, x, y-gl_smallFont.h-5, rw, 300, 0, "txtStanding",
350 &gl_smallFont, NULL, NULL );
351 y -= 2 * gl_smallFont.h + 5 + 15;
352
353 /* Presence. */
354 window_addText( wid, x, y, RCOL_HEADER_W, 20, 0, "txtSPresence",
355 &gl_smallFont, &cFontGrey, _("Presence:") );
356 window_addText( wid, x, y-gl_smallFont.h-5, rw, 300, 0, "txtPresence",
357 &gl_smallFont, NULL, NULL );
358 y -= 2 * gl_smallFont.h + 5 + 15;
359
360 /* Spobs */
361 window_addText( wid, x, y, RCOL_HEADER_W, 20, 0, "txtSSpobs",
362 &gl_smallFont, &cFontGrey, _("Space Objects:") );
363 window_addText( wid, x, y-gl_smallFont.h-5, rw, 300, 0, "txtSpobs",
364 &gl_smallFont, NULL, NULL );
365 y -= 2 * gl_smallFont.h + 5 + 15;
366
367 /* Services */
368 window_addText( wid, x, y, RCOL_HEADER_W, 20, 0, "txtSServices",
369 &gl_smallFont, &cFontGrey, _("Services:") );
370 window_addText( wid, x, y-gl_smallFont.h-5, rw, 300, 0, "txtServices",
371 &gl_smallFont, NULL, NULL );
372
373 /* Close button */
374 y = 15;
375 window_addButton( wid, -20, y, BUTTON_WIDTH, BUTTON_HEIGHT,
376 "btnClose", _("Close"), window_close );
377 /* Commodity button */
378 window_addButton( wid, -20 - (BUTTON_WIDTH+20), y, BUTTON_WIDTH, BUTTON_HEIGHT, "btnCommod", _("Mode"), map_buttonCommodity );
379 /* Find button */
380 window_addButtonKey( wid, -20 - 2*(BUTTON_WIDTH+20), y, BUTTON_WIDTH, BUTTON_HEIGHT,
381 "btnFind", _("Find"), map_inputFind, SDLK_f );
382 /* Autonav button */
383 window_addButtonKey( wid, -20 - 3*(BUTTON_WIDTH+20), y, BUTTON_WIDTH, BUTTON_HEIGHT,
384 "btnAutonav", _("Autonav"), player_autonavStartWindow, SDLK_a );
385 /* MInimal button */
386 window_addButtonKey( wid, -20 - 4*(BUTTON_WIDTH+20), y, BUTTON_WIDTH, BUTTON_HEIGHT,
387 "btnMinimal", NULL, map_buttonMinimal, SDLK_v );
388 map_setMinimal( wid, map_minimal_mode );
389 /* System info button */
390 window_addButtonKey( wid, -20 - 5*(BUTTON_WIDTH+20), y, BUTTON_WIDTH, BUTTON_HEIGHT,
391 "btnSystem", _("System Info"), map_buttonSystemMap, SDLK_s );
392 /* Mark this system button */
393 window_addButtonKey( wid, -20 - 6*(BUTTON_WIDTH+20), y, BUTTON_WIDTH, BUTTON_HEIGHT,
394 "btnMarkSystem", _("Toggle Note"), map_buttonMarkSystem, SDLK_n );
395
396 /*
397 * Bottom stuff
398 *
399 * [+] [-] Nebula, Interference
400 */
401 /* Zoom buttons */
402 window_addButtonKey( wid, -60, 30 + BUTTON_HEIGHT, 30, BUTTON_HEIGHT, "btnZoomIn", "+", map_buttonZoom, SDLK_EQUALS );
403 window_addButtonKey( wid, -20, 30 + BUTTON_HEIGHT, 30, BUTTON_HEIGHT, "btnZoomOut", "-", map_buttonZoom, SDLK_MINUS );
404 /* Situation text */
405 window_addText( wid, 20, 15, w - 40 - 7*(BUTTON_WIDTH+20), 30, 0,
406 "txtSystemStatus", &gl_smallFont, NULL, NULL );
407
408 /* Fuel. */
409 window_addText( wid, -20, 40+BUTTON_HEIGHT*2, rw, 30, 0,
410 "txtPlayerStatus", &gl_smallFont, NULL, NULL );
411
412 map_genModeList();
413 cst = map_globalCustomData(wid);
414 map_reset( cst, map_mode );
415 map_update( wid );
416
417 /*
418 * Disable Autonav button if player lacks fuel or if target is not a valid hyperspace target.
419 */
420 if ((player.p->fuel < player.p->fuel_consumption) || pilot_isFlag( player.p, PILOT_NOJUMP)
421 || map_selected == cur_system - systems_stack || array_size(map_path) == 0)
422 window_disableButton( wid, "btnAutonav" );
423}
424
425/*
426 * Prepares economy info for rendering. Called when cur_commod changes.
427 */
428static void map_update_commod_av_price (void)
429{
430 Commodity *c;
431
432 if (cur_commod == -1 || map_selected == -1) {
433 commod_av_gal_price = 0;
434 return;
435 }
436
437 c = commod_known[cur_commod];
438 if (cur_commod_mode == 0) {
439 double totPrice = 0;
440 int totPriceCnt = 0;
441 for (int i=0; i<array_size(systems_stack); i++) {
442 StarSystem *sys = system_getIndex( i );
443
444 /* if system is not known, reachable, or marked. and we are not in the editor */
445 if ((!sys_isKnown(sys) && !sys_isFlag(sys, SYSTEM_MARKED | SYSTEM_CMARKED)
446 && !space_sysReachable(sys)))
447 continue;
448 if ((sys_isKnown(sys)) && (system_hasSpob(sys))) {
449 double sumPrice = 0;
450 int sumCnt = 0;
451 double thisPrice;
452 for (int j=0 ; j<array_size(sys->spobs); j++) {
453 Spob *p = sys->spobs[j];
454 for (int k=0; k<array_size(p->commodities); k++) {
455 if (p->commodities[k] == c) {
456 if (p->commodityPrice[k].cnt > 0) { /*commodity is known about*/
457 thisPrice = p->commodityPrice[k].sum / p->commodityPrice[k].cnt;
458 sumPrice += thisPrice;
459 sumCnt += 1;
460 break;
461 }
462 }
463 }
464 }
465 if (sumCnt>0) {
466 totPrice += sumPrice / sumCnt;
467 totPriceCnt++;
468 }
469 }
470 }
471 if (totPriceCnt > 0)
472 totPrice /= totPriceCnt;
473 commod_av_gal_price = totPrice;
474
475 }
476 else
477 commod_av_gal_price = 0;
478}
479
480static void map_update_autonav( unsigned int wid )
481{
482 char buf[STRMAX];
483 StarSystem *sys;
484 int autonav, th;
485 int jumps = floor(player.p->fuel / player.p->fuel_consumption);
486 int p = 0;
487 int rw = RCOL_HEADER_W;
488 p += scnprintf(&buf[p], sizeof(buf)-p, "#n%s#0", _("Fuel: ") );
489 p += scnprintf(&buf[p], sizeof(buf)-p, n_("%d jump", "%d jumps", jumps), jumps );
490 sys = map_getDestination( &autonav );
491 p += scnprintf(&buf[p], sizeof(buf)-p, "\n#n%s#0", _("Autonav: ") );
492 if (sys==NULL)
493 p += scnprintf(&buf[p], sizeof(buf)-p, _("Off") );
494 else {
495 if (autonav > jumps)
496 p += scnprintf(&buf[p], sizeof(buf)-p, "#r" );
497 p += scnprintf(&buf[p], sizeof(buf)-p, n_("%d jump", "%d jumps", autonav), autonav );
498 if (autonav > jumps)
499 p += scnprintf(&buf[p], sizeof(buf)-p, "#0" );
500 }
501 th = gl_printHeightRaw( &gl_smallFont, rw, buf );
502 window_resizeWidget( wid, "txtPlayerStatus", rw, th );
503 window_moveWidget( wid, "txtPlayerStatus", RCOL_X, 40+BUTTON_HEIGHT*2 );
504 window_modifyText( wid, "txtPlayerStatus", buf );
505}
506
507static void map_update_status( unsigned int wid, const char *buf )
508{
509 int w, h;
510 window_modifyText( wid, "txtSystemStatus", buf );
511 if (buf==NULL)
512 return;
513 window_dimWidget( wid, "txtSystemStatus", &w, NULL );
514 h = gl_printHeightRaw( &gl_smallFont, w, buf );
515 window_moveWidget( wid, "txtSystemStatus", 20, (BBAR_H-h)/2 );
516 window_resizeWidget( wid, "txtSystemStatus", w, h );
517}
518
524static void map_update( unsigned int wid )
525{
526 int found, multiple;
527 StarSystem *sys;
528 int f, fh, h, x, y, logow, logoh;
529 unsigned int services, services_u, services_h, services_f, services_r;
530 int hasSpobs;
531 char t;
532 const char *adj;
533 char buf[STRMAX];
534 int p;
535 const glTexture *logo;
536 double w, dmg, itf;
537
538 /* Needs map to update. */
539 if (!map_isOpen())
540 return;
541
542 /* Propagate updates to map_mode, if an. */
543 map_globalCustomData(wid)->mode = map_mode;
544
545 /* Get selected system. */
546 sys = system_getIndex( map_selected );
547
548 /* Not known and no markers. */
549 if (!(sys_isFlag(sys, SYSTEM_MARKED | SYSTEM_CMARKED)) &&
550 !sys_isKnown(sys) && !space_sysReachable(sys)) {
551 map_selectCur();
552 sys = system_getIndex( map_selected );
553 }
554 /* Average commodity price */
555 map_update_commod_av_price();
556
557 /* Economy button */
558 if (map_mode == MAPMODE_TRADE) {
559 Commodity *c = commod_known[cur_commod];
560 if (cur_commod_mode == 1) {
561 snprintf( buf, sizeof(buf),
562 _("Showing %s prices relative to %s:\n"
563 "Positive/blue indicate profit while negative/orange values indicate loss when sold at the corresponding system."),
564 _(c->name), _(sys->name) );
565 map_update_status( wid, buf );
566 }
567 else {
568 snprintf(buf, sizeof(buf), _("Showing known %s prices.\nGalaxy-wide average: %.2f"), _(c->name), commod_av_gal_price);
569 map_update_status( wid, buf );
570 }
571 }
572 else
573 map_update_status( wid, NULL );
574
575 /*
576 * Right Text
577 */
578 x = RCOL_X; /* Side bar X offset. */
579 w = RCOL_TEXT_W; /* Width of the side bar. */
580 y = -20 - 20 - 64 - gl_defFont.h; /* Initialized to position for txtSFaction. */
581
582 if (!sys_isKnown(sys)) { /* System isn't known, erase all */
583 /*
584 * Right Text
585 */
586 if (sys_isFlag(sys, SYSTEM_MARKED | SYSTEM_CMARKED))
587 window_modifyText( wid, "txtSysname", _(sys->name) );
588 else
589 window_modifyText( wid, "txtSysname", _("Unknown") );
590
591 /* Faction */
592 window_modifyImage( wid, "imgFaction", NULL, 0, 0 );
593 window_moveWidget( wid, "txtSFaction", x, y);
594 window_moveWidget( wid, "txtFaction", x, y - gl_smallFont.h - 5 );
595 window_modifyText( wid, "txtFaction", _("Unknown") );
596 y -= 2 * gl_smallFont.h + 5 + 15;
597
598 /* Standing */
599 window_moveWidget( wid, "txtSStanding", x, y );
600 window_moveWidget( wid, "txtStanding", x, y - gl_smallFont.h - 5 );
601 window_modifyText( wid, "txtStanding", _("Unknown") );
602 y -= 2 * gl_smallFont.h + 5 + 15;
603
604 /* Presence. */
605 window_moveWidget( wid, "txtSPresence", x, y );
606 window_moveWidget( wid, "txtPresence", x, y - gl_smallFont.h - 5 );
607 window_modifyText( wid, "txtPresence", _("Unknown") );
608 y -= 2 * gl_smallFont.h + 5 + 15;
609
610 /* Spobs */
611 window_moveWidget( wid, "txtSSpobs", x, y );
612 window_moveWidget( wid, "txtSpobs", x, y - gl_smallFont.h - 5 );
613 window_modifyText( wid, "txtSpobs", _("Unknown") );
614 y -= 2 * gl_smallFont.h + 5 + 15;
615
616 /* Services */
617 window_moveWidget( wid, "txtSServices", x, y );
618 window_moveWidget( wid, "txtServices", x, y -gl_smallFont.h - 5 );
619 window_modifyText( wid, "txtServices", _("Unknown") );
620
621 /* Update autonav stuff. */
622 map_update_autonav( wid );
623
624 /*
625 * Bottom Text
626 */
627 map_update_status( wid, NULL );
628 return;
629 }
630
631 /* System is known */
632 window_modifyText( wid, "txtSysname", _(sys->name) );
633
634 f = -1;
635 multiple = 0;
636 for (int i=0; i<array_size(sys->spobs); i++) {
637 if (!spob_isKnown(sys->spobs[i]))
638 continue;
639 if ((sys->spobs[i]->presence.faction >= 0)
640 && (!faction_isKnown(sys->spobs[i]->presence.faction)) )
641 continue;
642
643 if ((f == -1) && (sys->spobs[i]->presence.faction >= 0)) {
644 f = sys->spobs[i]->presence.faction;
645 }
646 else if (f != sys->spobs[i]->presence.faction
647 && (sys->spobs[i]->presence.faction >= 0)) {
648 snprintf( buf, sizeof(buf), _("Multiple") );
649 multiple = 1;
650 break;
651 }
652 }
653 if (f == -1) {
654 window_modifyImage( wid, "imgFaction", NULL, 0, 0 );
655 window_modifyText( wid, "txtFaction", _("N/A") );
656 window_modifyText( wid, "txtStanding", _("N/A") );
657 h = gl_smallFont.h;
658 fh = gl_smallFont.h;
659 }
660 else {
661 const char *fcttext;
662 if (!multiple) /* saw them all and all the same */
663 snprintf( buf, sizeof(buf), "%s", faction_longname(f) );
664
665 /* Modify the image. */
666 logo = faction_logo(f);
667 logow = logo == NULL ? 0 : logo->w * (double)FACTION_LOGO_SM / MAX( logo->w, logo->h );
668 logoh = logo == NULL ? 0 : logo->h * (double)FACTION_LOGO_SM / MAX( logo->w, logo->h );
669 window_modifyImage( wid, "imgFaction", logo, logow, logoh );
670 if (logo != NULL)
671 window_moveWidget( wid, "imgFaction",
672 -90 + logow/2, -20 - 32 - 10 - gl_defFont.h + logoh/2);
673 fcttext = faction_getStandingText( f );
674
675 /* Modify the text */
676 window_modifyText( wid, "txtFaction", buf );
677 window_modifyText( wid, "txtStanding", fcttext );
678
679 h = gl_printHeightRaw( &gl_smallFont, w, buf );
680 fh = gl_printHeightRaw( &gl_smallFont, w, fcttext );
681 }
682
683 /* Faction */
684 window_moveWidget( wid, "txtSFaction", x, y);
685 window_moveWidget( wid, "txtFaction", x, y-gl_smallFont.h - 5 );
686 y -= gl_smallFont.h + h + 5 + 15;
687
688 /* Standing */
689 window_moveWidget( wid, "txtSStanding", x, y );
690 window_moveWidget( wid, "txtStanding", x, y-gl_smallFont.h - 5 );
691 y -= gl_smallFont.h + fh + 5 + 15;
692
693 window_moveWidget( wid, "txtSPresence", x, y );
694 window_moveWidget( wid, "txtPresence", x, y-gl_smallFont.h-5 );
695 map_updateFactionPresence( wid, "txtPresence", sys, 0 );
696 /* Scroll down. */
697 h = window_getTextHeight( wid, "txtPresence" );
698 y -= 40 + (h - gl_smallFont.h);
699
700 /* Get spobs */
701 hasSpobs = 0;
702 p = 0;
703 buf[0] = '\0';
704 for (int i=0; i<array_size(sys->spobs); i++) {
705 const char *prefix, *suffix;
706 Spob *s = sys->spobs[i];
707
708 if (!spob_isKnown(s))
709 continue;
710
711 /* Colourize output. */
712 spob_updateLand( s );
713 t = spob_getColourChar( s );
714 prefix = spob_getSymbol( s );
715 //suffix = (spob_isFlag( s, SPOB_MARKED ) ? _(" #o(M)") : "");
716 suffix = "";
717
718 if (!hasSpobs)
719 p += scnprintf( &buf[p], sizeof(buf)-p, "#%c%s%s%s#n",
720 t, prefix, spob_name( s ), suffix );
721 else
722 p += scnprintf( &buf[p], sizeof(buf)-p, ",\n#%c%s%s%s#n",
723 t, prefix, spob_name( s ), suffix );
724 hasSpobs = 1;
725 }
726 if (hasSpobs == 0) {
727 strncpy( buf, _("None"), sizeof(buf)-1 );
728 buf[sizeof(buf)-1] = '\0';
729 }
730 /* Update text. */
731 window_modifyText( wid, "txtSpobs", buf );
732 window_moveWidget( wid, "txtSSpobs", x, y );
733 window_moveWidget( wid, "txtSpobs", x, y-gl_smallFont.h-5 );
734 /* Scroll down. */
735 h = gl_printHeightRaw( &gl_smallFont, w, buf );
736 y -= 40 + (h - gl_smallFont.h);
737
738 /* Get the services */
739 window_moveWidget( wid, "txtSServices", x, y );
740 window_moveWidget( wid, "txtServices", x, y-gl_smallFont.h-5 );
741 services = 0;
742 services_u = 0;
743 services_h = 0;
744 services_f = 0;
745 services_r = 0;
746 for (int i=0; i<array_size(sys->spobs); i++) {
747 Spob *pnt = sys->spobs[i];
748 if (!spob_isKnown(pnt))
749 continue;
750
751 if (!spob_hasService( pnt, SPOB_SERVICE_INHABITED ))
752 services_u |= pnt->services;
753 else if (pnt->can_land) {
754 if (areAllies(pnt->presence.faction,FACTION_PLAYER))
755 services_f |= pnt->services;
756 else
757 services |= pnt->services;
758 }
759 else if (areEnemies(pnt->presence.faction,FACTION_PLAYER))
760 services_h |= pnt->services;
761 else
762 services_r |= pnt->services;
763 }
764 buf[0] = '\0';
765 p = 0;
766 /*snprintf(buf, sizeof(buf), "%f\n", sys->prices[0]);*/ /*Hack to control prices. */
767 for (int i=SPOB_SERVICE_LAND; i<=SPOB_SERVICE_SHIPYARD; i<<=1) {
768 if (services_f & i)
769 p += scnprintf( &buf[p], sizeof(buf)-p, "#F+ %s\n", _(spob_getServiceName(i)) );
770 else if (services & i)
771 p += scnprintf( &buf[p], sizeof(buf)-p, "#N~ %s\n", _(spob_getServiceName(i)) );
772 else if (services_h & i)
773 p += scnprintf( &buf[p], sizeof(buf)-p, "#H!! %s\n", _(spob_getServiceName(i)) );
774 else if (services_r & i)
775 p += scnprintf( &buf[p], sizeof(buf)-p, "#R* %s\n", _(spob_getServiceName(i)) );
776 else if (services_u & i)
777 p += scnprintf( &buf[p], sizeof(buf)-p, "#I= %s\n", _(spob_getServiceName(i)) );
778 }
779 if (buf[0] == '\0')
780 p += scnprintf( &buf[p], sizeof(buf)-p, _("None"));
781
782 window_modifyText( wid, "txtServices", buf );
783
784 /*
785 * System Status, if not showing commodity info
786 */
787 if (map_mode == MAPMODE_TRAVEL) {
788 buf[0] = '\0';
789 p = 0;
790
791 /* Special feature text. */
792 if (sys->features != NULL)
793 p += scnprintf(&buf[p], sizeof(buf)-p, "%s", _(sys->features) );
794
795 /* Mention spob features. */
796 for (int i=0; i<array_size(sys->spobs); i++) {
797 Spob *spob = sys->spobs[i];
798 if (spob->feature == NULL)
799 continue;
800 if (!spob_isKnown(spob))
801 continue;
802 if (buf[0] != '\0')
803 p += scnprintf(&buf[p], sizeof(buf)-p, _(", "));
804 p += scnprintf(&buf[p], sizeof(buf)-p, "%s", _(spob->feature));
805 }
806
807 /* Mention trade lanes if applicable. */
808 found = 0;
809 for (int i=0; i<array_size(sys->jumps); i++) {
810 if (sys->jumps[i].hide<=0.) {
811 found = 1;
812 break;
813 }
814 }
815 if (found) {
816 if (buf[0] != '\0')
817 p += scnprintf(&buf[p], sizeof(buf)-p, _(", "));
818 p += scnprintf(&buf[p], sizeof(buf)-p, "#g%s#0", _("Trade Lane") );
819 }
820
821 /* Nebula. */
822 if (sys->nebu_density > 0.) {
823 if (buf[0] != '\0')
824 p += scnprintf(&buf[p], sizeof(buf)-p, _(", "));
825
826 /* Density. */
827 if (sys->nebu_density > 700.)
828 adj = _("Dense ");
829 else if (sys->nebu_density < 300.)
830 adj = _("Light ");
831 else
832 adj = "";
833
834 /* Volatility */
835 dmg = sys->nebu_volatility;
836 if (sys->nebu_volatility > 50.) {
837 p += scnprintf(&buf[p], sizeof(buf)-p, "#r" );
838 p += scnprintf(&buf[p], sizeof(buf)-p, _("Volatile %sNebula (%.1f MW)"), adj, dmg);
839 }
840 else if (sys->nebu_volatility > 20.) {
841 p += scnprintf(&buf[p], sizeof(buf)-p, "#o" );
842 p += scnprintf(&buf[p], sizeof(buf)-p, _("Dangerous %sNebula (%.1f MW)"), adj, dmg);
843 }
844 else if (sys->nebu_volatility > 0.) {
845 p += scnprintf(&buf[p], sizeof(buf)-p, "#y" );
846 p += scnprintf(&buf[p], sizeof(buf)-p, _("Unstable %sNebula (%.1f MW)"), adj, dmg);
847 }
848 else
849 p += scnprintf(&buf[p], sizeof(buf)-p, _("%sNebula"), adj);
850 p += scnprintf(&buf[p], sizeof(buf)-p, "#0" );
851 }
852 /* Interference. */
853 if (sys->interference > 0.) {
854 if (buf[0] != '\0')
855 p += scnprintf(&buf[p], sizeof(buf)-p, _(", "));
856
857 itf = sys->interference;
858 if (sys->interference > 700.) {
859 p += scnprintf(&buf[p], sizeof(buf)-p, "#r" );
860 p += scnprintf(&buf[p], sizeof(buf)-p, _("Dense Interference (%.0f%%)"), itf);
861 }
862 else if (sys->interference < 300.) {
863 p += scnprintf(&buf[p], sizeof(buf)-p, "#o" );
864 p += scnprintf(&buf[p], sizeof(buf)-p, _("Light Interference (%.0f%%)"), itf);
865 }
866 else {
867 p += scnprintf(&buf[p], sizeof(buf)-p, "#y" );
868 p += scnprintf(&buf[p], sizeof(buf)-p, _("Interference (%.0f%%)"), itf);
869 }
870 p += scnprintf(&buf[p], sizeof(buf)-p, "#0" );
871 }
872 /* Asteroids. */
873 if (array_size(sys->asteroids) > 0) {
874 double density = sys->asteroid_density;
875
876 if (buf[0] != '\0')
877 p += scnprintf(&buf[p], sizeof(buf)-p, _(", "));
878
879 if (density >= 1000.) {
880 p += scnprintf(&buf[p], sizeof(buf)-p, "#o" );
881 p += scnprintf(&buf[p], sizeof(buf)-p, _("Dense Asteroids"));
882 }
883 else if (density <= 300.) {
884 p += scnprintf(&buf[p], sizeof(buf)-p, "#y" );
885 p += scnprintf(&buf[p], sizeof(buf)-p, _("Light Asteroids"));
886 }
887 else
888 p += scnprintf(&buf[p], sizeof(buf)-p, _("Asteroids"));
889 p += scnprintf(&buf[p], sizeof(buf)-p, "#0" );
890 }
891 /* Update the string. */
892 map_update_status( wid, buf );
893 }
894
895 /* Player info. */
896 map_update_autonav( wid );
897}
898
904int map_isOpen (void)
905{
906 return window_exists(MAP_WDWNAME);
907}
908
920static void map_drawMarker( double x, double y, double zoom,
921 double r, double a, int num, int cur, int type )
922{
923 (void) zoom;
924 const glColour* colours[] = {
925 &cMarkerNew, &cMarkerPlot, &cMarkerHigh, &cMarkerLow, &cMarkerComputer, &cMarkerNew
926 };
927 double alpha;
928 glColour col;
929
930 /* Calculate the angle. */
931 if ((num == 1) || (num == 2) || (num == 4))
932 alpha = M_PI/4.;
933 else if (num == 3)
934 alpha = M_PI/6.;
935 else if (num == 5)
936 alpha = M_PI/10.;
937 else
938 alpha = M_PI/2.;
939
940 alpha += M_PI*2. * (double)cur/(double)num;
941
942 x = x + 3.0*r * cos(alpha);
943 y = y + 3.0*r * sin(alpha);
944 r *= 2.0;
945
946 /* Special case notes marker. */
947 if (type==5) {
948 col = cFontOrange;
949 col.a *= a;
950 glUseProgram(shaders.notemarker.program);
951 gl_renderShader( x, y, r, r, alpha, &shaders.notemarker, &col, 1 );
952 return;
953 }
954
955 glUseProgram(shaders.sysmarker.program);
956 if (type==0) {
957 col_blend( &col, colours[type], &cWhite, MIN(1.0, 0.75 + 0.25*sin(2.0*M_PI*map_dt)) );
958 x += 0.25*r * cos(alpha);
959 y += 0.25*r * sin(alpha);
960 r *= 1.25;
961 glUniform1i( shaders.sysmarker.parami, 1 );
962 }
963 else {
964 col_blend( &col, colours[type], &cWhite, MIN(1.0, 1.0 + 0.25*sin(2.0*M_PI*map_dt)) );
965 glUniform1i( shaders.sysmarker.parami, 0 );
966 }
967 col.a *= a;
968 gl_renderShader( x, y, r, r, alpha, &shaders.sysmarker, &col, 1 );
969}
970
979static void map_render( double bx, double by, double w, double h, void *data )
980{
981 CstMapWidget *cst = data;
982 double x,y,z,r;
983 glColour col;
984 StarSystem *sys;
985 double dt = naev_getrealdt();
986
987 /* Update timer. */
988 map_dt += dt;
989 map_updateInternal( cst, dt );
990
991 /* Parameters. */
992 map_renderParams( bx, by, cst->xpos, cst->ypos, w, h, cst->zoom, &x, &y, &r );
993 z = cst->zoom;
994
995 /* background */
996 gl_renderRect( bx, by, w, h, &cBlack );
997
998 if (cst->alpha_decorators > 0.)
999 map_renderDecorators( x, y, z, 0, EASE_ALPHA(cst->alpha_decorators) );
1000
1001 /* Render faction disks. */
1002 if (cst->alpha_faction > 0.)
1003 map_renderFactionDisks( x, y, z, r, 0, EASE_ALPHA(cst->alpha_faction) );
1004
1005 /* Render environmental features. */
1006 if (cst->alpha_env > 0.)
1007 map_renderSystemEnvironment( x, y, z, 0, EASE_ALPHA(cst->alpha_env) );
1008
1009 /* Render jump routes. */
1010 map_renderJumps( x, y, z, r, 0 );
1011
1012 /* Render the player's jump route. */
1013 if (cst->alpha_path > 0.)
1014 map_renderPath( x, y, z, r, EASE_ALPHA(cst->alpha_path) );
1015
1016 /* Render systems. */
1017 map_renderSystems( bx, by, x, y, z, w, h, r, cst->mode );
1018
1019 /* Render system markers and notes. */
1020 if (cst->alpha_markers > 0.)
1021 map_renderMarkers( x, y, z, r, EASE_ALPHA(cst->alpha_markers) );
1022
1023 /* Render system names and notes. */
1024 if (cst->alpha_names > 0.)
1025 map_renderNames( bx, by, x, y, z, w, h, 0, EASE_ALPHA(cst->alpha_names) );
1026
1027 /* Render commodity info. */
1028 if (cst->alpha_commod > 0.)
1029 map_renderCommod( bx, by, x, y, z, w, h, r, 0, EASE_ALPHA(cst->alpha_commod) );
1030
1031 /* We want the notes on top of everything. */
1032 if (cst->alpha_markers > 0.)
1033 map_renderNotes( bx, by, x, y, z, w, h, 0, EASE_ALPHA(cst->alpha_markers) );
1034
1035 /* Values from cRadar_tSpob */
1036 col.r = cRadar_tSpob.r;
1037 col.g = cRadar_tSpob.g;
1038 col.b = cRadar_tSpob.b;
1039
1040 /* Selected system. */
1041 if (map_selected != -1) {
1042 sys = system_getIndex( map_selected );
1043 glUseProgram( shaders.selectspob.program );
1044 glUniform1f( shaders.selectspob.dt, map_dt ); /* good enough. */
1045 gl_renderShader( x + sys->pos.x * z, y + sys->pos.y * z,
1046 1.7*r, 1.7*r, 0., &shaders.selectspob, &cRadar_tSpob, 1 );
1047 }
1048
1049 /* Current spob. */
1050 gl_renderCircle( x + cur_system->pos.x * z,
1051 y + cur_system->pos.y * z,
1052 1.5*r, &col, 0 );
1053
1054 glClear( GL_DEPTH_BUFFER_BIT );
1055}
1056
1060void map_renderParams( double bx, double by, double xpos, double ypos,
1061 double w, double h, double zoom, double *x, double *y, double *r )
1062{
1063 *r = round(CLAMP(6., 20., 8.*zoom));
1064 *x = round((bx - xpos + w/2) * 1.);
1065 *y = round((by - ypos + h/2) * 1.);
1066}
1067
1073void map_renderDecorators( double x, double y, double zoom, int editor, double alpha )
1074{
1075 const glColour ccol = { .r=1., .g=1., .b=1., .a=2./3.*alpha };
1077 /* Fade in the decorators to allow toggling between commodity and nothing */
1078 for (int i=0; i<array_size(decorator_stack); i++) {
1079 int visible;
1080 MapDecorator *decorator = &decorator_stack[i];
1081
1082 /* only if pict couldn't be loaded */
1083 if (decorator->image == NULL)
1084 continue;
1085
1086 visible = 0;
1087 if (!editor) {
1088 for (int j=0; j<array_size(systems_stack) && visible==0; j++) {
1089 StarSystem *sys = system_getIndex( j );
1090
1091 if (sys_isFlag(sys, SYSTEM_HIDDEN))
1092 continue;
1093
1094 if (!sys_isKnown(sys))
1095 continue;
1096
1097 if ((decorator->x < sys->pos.x + decorator->detection_radius) &&
1098 (decorator->x > sys->pos.x - decorator->detection_radius) &&
1099 (decorator->y < sys->pos.y + decorator->detection_radius) &&
1100 (decorator->y > sys->pos.y - decorator->detection_radius)) {
1101 visible = 1;
1102 }
1103 }
1104 }
1105
1106 if (editor || visible==1) {
1107 double tx = x + decorator->x*zoom;
1108 double ty = y + decorator->y*zoom;
1109
1110 int sw = decorator->image->sw*zoom;
1111 int sh = decorator->image->sh*zoom;
1112
1113 gl_renderScale( decorator->image,
1114 tx - sw/2, ty - sh/2, sw, sh, &ccol );
1115 }
1116 }
1117}
1118
1122void map_renderFactionDisks( double x, double y, double zoom, double r, int editor, double alpha )
1123{
1124 for (int i=0; i<array_size(systems_stack); i++) {
1125 glColour c;
1126 double tx, ty;
1127 StarSystem *sys = system_getIndex( i );
1128
1129 if (sys_isFlag(sys,SYSTEM_HIDDEN))
1130 continue;
1131
1132 if ((!sys_isFlag(sys, SYSTEM_HAS_KNOWN_LANDABLE) || !sys_isKnown(sys)) && !editor)
1133 continue;
1134
1135 tx = x + sys->pos.x*zoom;
1136 ty = y + sys->pos.y*zoom;
1137
1138 /* System has faction and is known or we are in editor. */
1139 if (sys->faction != -1) {
1140 const glColour *col;
1141 double presence = sqrt(sys->ownerpresence);
1142
1143 /* draws the disk representing the faction */
1144 double sr = (40. + presence * 3.) * zoom * 0.5;
1145
1146 col = faction_colour(sys->faction);
1147 c.r = col->r;
1148 c.g = col->g;
1149 c.b = col->b;
1150 c.a = 0.6 * alpha;
1151
1152 glUseProgram(shaders.factiondisk.program);
1153 glUniform1f(shaders.factiondisk.paramf, r / sr );
1154 gl_renderShader( tx, ty, sr, sr, 0., &shaders.factiondisk, &c, 1 );
1155 }
1156 }
1157}
1158
1162void map_renderSystemEnvironment( double x, double y, double zoom, int editor, double alpha )
1163{
1164 for (int i=0; i<array_size(systems_stack); i++) {
1165 double tx, ty;
1166 /* Fade in the disks to allow toggling between commodity and nothing */
1167 StarSystem *sys = system_getIndex( i );
1168
1169 if (sys_isFlag(sys,SYSTEM_HIDDEN))
1170 continue;
1171
1172 if (!sys_isKnown(sys) && !editor)
1173 continue;
1174
1175 tx = x + sys->pos.x*zoom;
1176 ty = y + sys->pos.y*zoom;
1177
1178 /* Draw background. */
1179 /* TODO draw asteroids too! */
1180 if (sys->nebu_density > 0.) {
1181 mat4 projection;
1182 double sw, sh;
1183 sw = (50. + sys->nebu_density * 50. / 1000.) * zoom;
1184 sh = sw;
1185
1186 /* Set the vertex. */
1187 projection = gl_view_matrix;
1188 mat4_translate( &projection, tx-sw/2., ty-sh/2., 0. );
1189 mat4_scale( &projection, sw, sh, 1. );
1190
1191 /* Start the program. */
1192 glUseProgram(shaders.nebula_map.program);
1193
1194 /* Set shader uniforms. */
1195 glUniform1f(shaders.nebula_map.hue, sys->nebu_hue);
1196 glUniform1f(shaders.nebula_map.alpha, alpha);
1197 gl_uniformMat4(shaders.nebula_map.projection, &projection);
1198 glUniform1f(shaders.nebula_map.time, map_dt / 10.0);
1199 glUniform2f(shaders.nebula_map.globalpos, sys->pos.x, sys->pos.y );
1200 glUniform1f(shaders.nebula_map.volatility, sys->nebu_volatility );
1201
1202 /* Draw. */
1203 glEnableVertexAttribArray( shaders.nebula_map.vertex );
1204 gl_vboActivateAttribOffset( gl_squareVBO, shaders.nebula_map.vertex, 0, 2, GL_FLOAT, 0 );
1205 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
1206
1207 /* Clean up. */
1208 glDisableVertexAttribArray( shaders.nebula_map.vertex );
1209 glUseProgram(0);
1210 gl_checkErr();
1211 }
1212 else if (sys->map_shader != NULL) {
1213 mat4 projection;
1214 double sw, sh;
1215 sw = 100. * zoom;
1216 sh = sw;
1217
1218 /* Set the vertex. */
1219 projection = gl_view_matrix;
1220 mat4_translate( &projection, tx-sw/2., ty-sh/2., 0. );
1221 mat4_scale( &projection, sw, sh, 1. );
1222
1223 /* Start the program. */
1224 glUseProgram( sys->ms->program );
1225
1226 /* Set shader uniforms. */
1227 gl_uniformMat4(sys->ms->projection, &projection);
1228 glUniform1f(sys->ms->time, map_dt);
1229 glUniform2f(sys->ms->globalpos, sys->pos.x, sys->pos.y );
1230 glUniform1f(sys->ms->alpha, alpha);
1231
1232 /* Draw. */
1233 glEnableVertexAttribArray( sys->ms->vertex );
1234 gl_vboActivateAttribOffset( gl_squareVBO, sys->ms->vertex, 0, 2, GL_FLOAT, 0 );
1235 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
1236
1237 /* Clean up. */
1238 glDisableVertexAttribArray( sys->ms->vertex );
1239 glUseProgram(0);
1240 gl_checkErr();
1241 }
1242 }
1243}
1244
1248void map_renderJumps( double x, double y, double zoom, double radius, int editor )
1249{
1250 for (int i=0; i<array_size(systems_stack); i++) {
1251 double x1,y1;
1252 StarSystem *sys = system_getIndex( i );
1253
1254 if (sys_isFlag(sys,SYSTEM_HIDDEN))
1255 continue;
1256
1257 if (!sys_isKnown(sys) && !editor)
1258 continue; /* we don't draw hyperspace lines */
1259
1260 x1 = x + sys->pos.x * zoom;
1261 y1 = y + sys->pos.y * zoom;
1262
1263 for (int j=0; j < array_size(sys->jumps); j++) {
1264 double x2,y2, rx,ry, r, rw,rh;
1265 const glColour *col, *cole;
1266 StarSystem *jsys = sys->jumps[j].target;
1267 if (sys_isFlag(jsys,SYSTEM_HIDDEN))
1268 continue;
1269 if (!space_sysReachableFromSys(jsys,sys) && !editor)
1270 continue;
1271
1272 /* Choose colours. */
1273 cole = &cAquaBlue;
1274 for (int k=0; k < array_size(jsys->jumps); k++) {
1275 if (jsys->jumps[k].target == sys) {
1276 if (jp_isFlag(&jsys->jumps[k], JP_EXITONLY))
1277 cole = &cGrey80;
1278 else if (jp_isFlag(&jsys->jumps[k], JP_HIDDEN))
1279 cole = &cRed;
1280 break;
1281 }
1282 }
1283 if (jp_isFlag(&sys->jumps[j], JP_EXITONLY))
1284 col = &cGrey80;
1285 else if (jp_isFlag(&sys->jumps[j], JP_HIDDEN))
1286 col = &cRed;
1287 else
1288 col = &cAquaBlue;
1289
1290 x2 = x + jsys->pos.x * zoom;
1291 y2 = y + jsys->pos.y * zoom;
1292 rx = x2-x1;
1293 ry = y2-y1;
1294 r = atan2( ry, rx );
1295 rw = MOD(rx,ry)/2.;
1296
1297 if (sys->jumps[j].hide<=0.) {
1298 col = &cGreen;
1299 rh = 2.5;
1300 }
1301 else {
1302 rh = 1.5;
1303 }
1304
1305 glUseProgram( shaders.jumplane.program );
1306 gl_uniformColor( shaders.jumplane.paramv, cole );
1307 glUniform1f( shaders.jumplane.paramf, radius );
1308 gl_renderShader( (x1+x2)/2., (y1+y2)/2., rw, rh, r, &shaders.jumplane, col, 1 );
1309 }
1310 }
1311}
1312
1316void map_renderSystems( double bx, double by, double x, double y,
1317 double zoom, double w, double h, double r, MapMode mode )
1318{
1319 for (int i=0; i<array_size(systems_stack); i++) {
1320 const glColour *col;
1321 double tx, ty;
1322 StarSystem *sys = system_getIndex( i );
1323
1324 if (sys_isFlag(sys,SYSTEM_HIDDEN))
1325 continue;
1326
1327 /* if system is not known, reachable, or marked. and we are not in the editor */
1328 if ((!sys_isKnown(sys) && !sys_isFlag(sys, SYSTEM_MARKED | SYSTEM_CMARKED)
1329 && !space_sysReachable(sys)) && mode != MAPMODE_EDITOR)
1330 continue;
1331
1332 tx = x + sys->pos.x*zoom;
1333 ty = y + sys->pos.y*zoom;
1334
1335 /* Skip if out of bounds. */
1336 if (!rectOverlap(tx-r, ty-r, 2.*r, 2.*r, bx, by, w, h))
1337 continue;
1338
1339 /* Draw an outer ring. */
1340 if (mode == MAPMODE_EDITOR || mode == MAPMODE_TRAVEL || mode == MAPMODE_TRADE)
1341 gl_renderCircle( tx, ty, r, &cInert, 0 );
1342
1343 /* Ignore not known systems when not in the editor. */
1344 if (mode != MAPMODE_EDITOR && !sys_isKnown(sys))
1345 continue;
1346
1347 if (mode == MAPMODE_EDITOR || mode == MAPMODE_TRAVEL || mode == MAPMODE_TRADE) {
1348 if (!system_hasSpob(sys))
1349 continue;
1350 if (!sys_isFlag(sys, SYSTEM_HAS_KNOWN_LANDABLE) && mode != MAPMODE_EDITOR)
1351 continue;
1352 /* Spob colours */
1353 if (mode != MAPMODE_EDITOR && !sys_isKnown(sys))
1354 col = &cInert;
1355 else if (sys->faction < 0)
1356 col = &cInert;
1357 else if (mode == MAPMODE_EDITOR)
1358 col = &cNeutral;
1359 else if (areEnemies(FACTION_PLAYER,sys->faction))
1360 col = &cHostile;
1361 else if (!sys_isFlag(sys, SYSTEM_HAS_LANDABLE))
1362 col = &cRestricted;
1363 else if (areAllies(FACTION_PLAYER,sys->faction))
1364 col = &cFriend;
1365 else
1366 col = &cNeutral;
1367
1368 if (mode == MAPMODE_EDITOR) {
1369 /* Radius slightly shorter. */
1370 gl_renderCircle( tx, ty, 0.5 * r, col, 1 );
1371 }
1372 else
1373 gl_renderCircle( tx, ty, 0.65 * r, col, 1 );
1374 }
1375 else if (mode == MAPMODE_DISCOVER) {
1376 gl_renderCircle( tx, ty, r, &cInert, 0 );
1377 if (sys_isFlag( sys, SYSTEM_DISCOVERED ))
1378 gl_renderCircle( tx, ty, 0.65 * r, &cGreen, 1 );
1379 }
1380 }
1381}
1382
1386static void map_renderPath( double x, double y, double zoom, double radius, double alpha )
1387{
1388 StarSystem *sys1 = cur_system;
1389 int jmax, jcur;
1390
1391 if (array_size(map_path) == 0)
1392 return;
1393
1394 /* Player must exist. */
1395 if (player.p==NULL)
1396 return;
1397
1398 jmax = pilot_getJumps(player.p); /* Maximum jumps. */
1399 jcur = jmax; /* Jump range remaining. */
1400
1401 for (int j=0; j<array_size(map_path); j++) {
1402 glColour col;
1403 double x1,y1, x2,y2, rx,ry, rw,rh, r;
1404 StarSystem *sys2 = map_path[j];
1405 if (sys_isFlag(sys1,SYSTEM_HIDDEN) || sys_isFlag(sys2,SYSTEM_HIDDEN))
1406 continue;
1407 if (jcur == jmax && jmax > 0)
1408 col = cGreen;
1409 else if (jcur < 1)
1410 col = cRed;
1411 else
1412 col = cYellow;
1413 col.a = alpha;
1414
1415 x1 = x + sys1->pos.x * zoom;
1416 y1 = y + sys1->pos.y * zoom;
1417 x2 = x + sys2->pos.x * zoom;
1418 y2 = y + sys2->pos.y * zoom;
1419 rx = x2-x1;
1420 ry = y2-y1;
1421 r = atan2( ry, rx );
1422 rw = (MOD(rx,ry)+radius)/2.;
1423 rh = 5.;
1424
1425 glUseProgram( shaders.jumplanegoto.program );
1426 glUniform1f( shaders.jumplanegoto.dt, map_dt );
1427 glUniform1f( shaders.jumplanegoto.paramf, radius );
1428 glUniform1i( shaders.jumplanegoto.parami, (jcur >= 1) );
1429 gl_renderShader( (x1+x2)/2., (y1+y2)/2., rw, rh, r, &shaders.jumplanegoto, &col, 1 );
1430
1431 jcur--;
1432 sys1 = sys2;
1433 }
1434}
1435
1439void map_renderNotes( double bx, double by, double x, double y,
1440 double zoom, double w, double h, int editor, double alpha )
1441{
1442 (void) w;
1443 (void) h;
1444
1445 if ((zoom <= 0.5) || editor)
1446 return;
1447
1448 if (map_show_notes)
1449 glClear( GL_DEPTH_BUFFER_BIT );
1450
1451 /* Find mouse over system and draw. */
1452 for (int i=0; i<array_size(systems_stack); i++) {
1453 double tx,ty, tw,th;
1454 glColour col;
1455 glFont *font;
1456 StarSystem *sys = &systems_stack[i];
1457
1458 if (!sys_isFlag(sys,SYSTEM_PMARKED))
1459 continue;
1460
1461 if (sys->note == NULL)
1462 continue;
1463
1464 /* Set up position. */
1465 tx = x + sys->pos.x*zoom;
1466 ty = y + sys->pos.y*zoom;
1467
1468 /* Mouse is over. */
1469 if (!map_show_notes && ((pow2(tx-map_mx-bx)+pow2(ty-map_my-by)) > pow2(MAP_MOVE_THRESHOLD)))
1470 continue;
1471
1472 if (!map_show_notes)
1473 glClear( GL_DEPTH_BUFFER_BIT );
1474
1475 font = (zoom >= 1.5) ? &gl_defFont : &gl_smallFont;
1476 tx += 12.*zoom;
1477 ty -= font->h*2.;
1478 tw = gl_printWidthRaw( font, sys->note )+8.;
1479 th = font->h+8.;
1480
1481 /* Background. */
1482 col = cBlack;
1483 col.a = alpha*0.8;
1484 gl_renderRect( tx-4., ty-4., tw, th, &col );
1485
1486 /* Render note */
1487 col = cFontOrange;
1488 col.a = alpha;
1489 gl_printRaw( font, tx, ty, &col, -1, sys->note );
1490 }
1491}
1492
1496void map_renderNames( double bx, double by, double x, double y,
1497 double zoom, double w, double h, int editor, double alpha )
1498{
1499 double tx,ty, vx,vy, d,n;
1500 int textw;
1501 char buf[32];
1502 glColour col;
1503 glFont *font;
1504
1505 if (zoom <= 0.5)
1506 return;
1507
1508 for (int i=0; i<array_size(systems_stack); i++) {
1509 StarSystem *sys = system_getIndex( i );
1510
1511 if (sys_isFlag(sys,SYSTEM_HIDDEN))
1512 continue;
1513
1514 /* Skip system. */
1515 if (!editor && !sys_isKnown(sys))
1516 continue;
1517
1518 font = (zoom >= 1.5) ? &gl_defFont : &gl_smallFont;
1519
1520 textw = gl_printWidthRaw( font, _(sys->name) );
1521 tx = x + (sys->pos.x+12.) * zoom;
1522 ty = y + (sys->pos.y) * zoom - font->h*0.5;
1523
1524 /* Skip if out of bounds. */
1525 if (!rectOverlap(tx, ty, textw, font->h, bx, by, w, h))
1526 continue;
1527
1528 col = cWhite;
1529 col.a = alpha;
1530 gl_printRaw( font, tx, ty, &col, -1, _(sys->name) );
1531 }
1532
1533 /* Raw hidden values if we're in the editor. */
1534 if (!editor || (zoom <= 1.0))
1535 return;
1536
1537 for (int i=0; i<array_size(systems_stack); i++) {
1538 StarSystem *sys = system_getIndex( i );
1539 for (int j=0; j<array_size(sys->jumps); j++) {
1540 StarSystem *jsys = sys->jumps[j].target;
1541 /* Calculate offset. */
1542 vx = jsys->pos.x - sys->pos.x;
1543 vy = jsys->pos.y - sys->pos.y;
1544 n = sqrt( pow2(vx) + pow2(vy) );
1545 vx /= n;
1546 vy /= n;
1547 d = MAX(n*0.3*zoom, 15);
1548 tx = x + zoom*sys->pos.x + d*vx;
1549 ty = y + zoom*sys->pos.y + d*vy;
1550 /* Display. */
1551 n = sys->jumps[j].hide;
1552 if (n == 0.)
1553 snprintf( buf, sizeof(buf), "#gH: %.2f", n );
1554 else
1555 snprintf( buf, sizeof(buf), "H: %.2f", n );
1556 col = cGrey70;
1557 col.a = alpha;
1558 gl_printRaw( &gl_smallFont, tx, ty, &col, -1, buf );
1559 }
1560 }
1561}
1562
1566static void map_renderMarkers( double x, double y, double zoom, double r, double a )
1567{
1568 for (int i=0; i<array_size(systems_stack); i++) {
1569 double tx, ty;
1570 int j, n, m;
1571 StarSystem *sys = system_getIndex( i );
1572
1573 /* We only care about marked now. */
1574 if (!sys_isFlag(sys, SYSTEM_MARKED | SYSTEM_CMARKED | SYSTEM_PMARKED))
1575 continue;
1576
1577 /* Get the position. */
1578 tx = x + sys->pos.x*zoom;
1579 ty = y + sys->pos.y*zoom;
1580
1581 /* Count markers. */
1582 n = (sys_isFlag(sys, SYSTEM_CMARKED)) ? 1 : 0;
1583 n += (sys_isFlag(sys, SYSTEM_PMARKED)) ? 1 : 0;
1584 n += sys->markers_plot;
1585 n += sys->markers_high;
1586 n += sys->markers_low;
1587 n += sys->markers_computer;
1588
1589 /* Draw the markers. */
1590 j = 0;
1591 if (sys_isFlag(sys, SYSTEM_PMARKED)) { /* Notes have be first. */
1592 map_drawMarker( tx, ty, zoom, r, a, n, j, 5 );
1593 j++;
1594 }
1595 if (sys_isFlag(sys, SYSTEM_CMARKED)) {
1596 map_drawMarker( tx, ty, zoom, r, a, n, j, 0 );
1597 j++;
1598 }
1599 for (m=0; m<sys->markers_plot; m++) {
1600 map_drawMarker( tx, ty, zoom, r, a, n, j, 1 );
1601 j++;
1602 }
1603 for (m=0; m<sys->markers_high; m++) {
1604 map_drawMarker( tx, ty, zoom, r, a, n, j, 2 );
1605 j++;
1606 }
1607 for (m=0; m<sys->markers_low; m++) {
1608 map_drawMarker( tx, ty, zoom, r, a, n, j, 3 );
1609 j++;
1610 }
1611 for (m=0; m<sys->markers_computer; m++) {
1612 map_drawMarker( tx, ty, zoom, r, a, n, j, 4 );
1613 j++;
1614 }
1615 }
1616}
1617
1618/*
1619 * Makes all systems dark grey.
1620 */
1621static void map_renderSysBlack( double bx, double by, double x, double y, double zoom, double w, double h, double r, int editor )
1622{
1623 for (int i=0; i<array_size(systems_stack); i++) {
1624 double tx,ty;
1625 glColour ccol;
1626 StarSystem *sys = system_getIndex( i );
1627
1628 if (sys_isFlag(sys,SYSTEM_HIDDEN))
1629 continue;
1630
1631 /* if system is not known, reachable, or marked. and we are not in the editor */
1632 if ((!sys_isKnown(sys) && !sys_isFlag(sys, SYSTEM_MARKED | SYSTEM_CMARKED)
1633 && !space_sysReachable(sys)) && !editor)
1634 continue;
1635
1636 tx = x + sys->pos.x*zoom;
1637 ty = y + sys->pos.y*zoom;
1638
1639 /* Skip if out of bounds. */
1640 if (!rectOverlap(tx - r, ty - r, r, r, bx, by, w, h))
1641 continue;
1642
1643 /* If system is known fill it. */
1644 if ((sys_isKnown(sys)) && (system_hasSpob(sys))) {
1645 ccol = cGrey10;
1646 gl_renderCircle( tx, ty, r, &ccol, 1 );
1647 }
1648 }
1649}
1650
1651/*
1652 * Renders the economy information
1653 */
1654void map_renderCommod( double bx, double by, double x, double y,
1655 double zoom, double w, double h, double r, int editor, double a )
1656{
1657 Commodity *c;
1658 glColour ccol;
1659 double best,worst,maxPrice,minPrice,curMaxPrice,curMinPrice,thisPrice;
1660
1661 /* If not plotting commodities, return */
1662 if (cur_commod == -1 || map_selected == -1 || commod_known == NULL)
1663 return;
1664
1665 c = commod_known[cur_commod];
1666 if (cur_commod_mode == 1) { /*showing price difference to selected system*/
1667 StarSystem *sys = system_getIndex( map_selected );
1668 /* Get commodity price in selected system. If selected system is current
1669 system, and if landed, then get price of commodity where we are */
1670 curMaxPrice = 0.;
1671 curMinPrice = 0.;
1672 if (sys == cur_system && landed) {
1673 int k;
1674 for (k=0; k<array_size(land_spob->commodities); k++) {
1675 if (land_spob->commodities[k] == c) {
1676 /* current spob has the commodity of interest */
1677 curMinPrice = land_spob->commodityPrice[k].sum / land_spob->commodityPrice[k].cnt;
1678 curMaxPrice = curMinPrice;
1679 break;
1680 }
1681 }
1682 if (k==array_size(land_spob->commodities)) { /* commodity of interest not found */
1683 map_renderCommodIgnorance( x, y, zoom, sys, c, a );
1684 map_renderSysBlack( bx, by, x, y, zoom, w, h, r, editor );
1685 return;
1686 }
1687 }
1688 else {
1689 /* not currently landed, so get max and min price in the selected system. */
1690 if ((sys_isKnown(sys)) && (system_hasSpob(sys))) {
1691 minPrice = HUGE_VAL;
1692 maxPrice = 0;
1693 for (int j=0; j<array_size(sys->spobs); j++) {
1694 Spob *p = sys->spobs[j];
1695 for (int k=0; k<array_size(p->commodities); k++) {
1696 if (p->commodities[k] != c)
1697 continue;
1698 if (p->commodityPrice[k].cnt <= 0) /* commodity is not known about */
1699 continue;
1700 thisPrice = p->commodityPrice[k].sum / p->commodityPrice[k].cnt;
1701 maxPrice = MAX( thisPrice, maxPrice );
1702 minPrice = MIN( thisPrice, minPrice );
1703 break;
1704 }
1705
1706 }
1707 if (maxPrice == 0) { /* no prices are known here */
1708 map_renderCommodIgnorance( x, y, zoom, sys, c, a );
1709 map_renderSysBlack( bx, by, x, y, zoom, w, h, r, editor );
1710 return;
1711 }
1712 curMaxPrice = maxPrice;
1713 curMinPrice = minPrice;
1714 }
1715 else {
1716 map_renderCommodIgnorance( x, y, zoom, sys, c, a );
1717 map_renderSysBlack( bx, by, x, y, zoom, w, h, r, editor );
1718 return;
1719 }
1720 }
1721 for (int i=0; i<array_size(systems_stack); i++) {
1722 double tx, ty;
1723 sys = system_getIndex( i );
1724
1725 if (sys_isFlag(sys,SYSTEM_HIDDEN))
1726 continue;
1727
1728 /* if system is not known, reachable, or marked. and we are not in the editor */
1729 if ((!sys_isKnown(sys) && !sys_isFlag(sys, SYSTEM_MARKED | SYSTEM_CMARKED)
1730 && !space_sysReachable(sys)) && !editor)
1731 continue;
1732
1733 tx = x + sys->pos.x*zoom;
1734 ty = y + sys->pos.y*zoom;
1735
1736 /* Skip if out of bounds. */
1737 if (!rectOverlap(tx - r, ty - r, r, r, bx, by, w, h))
1738 continue;
1739
1740 /* If system is known fill it. */
1741 if ((sys_isKnown(sys)) && (system_hasSpob(sys))) {
1742 minPrice = HUGE_VAL;
1743 maxPrice = 0;
1744 for (int j=0; j<array_size(sys->spobs); j++) {
1745 Spob *p = sys->spobs[j];
1746 for (int k=0; k<array_size(p->commodities); k++) {
1747 if (p->commodities[k] != c)
1748 continue;
1749 if (p->commodityPrice[k].cnt <= 0) /*commodity is not known about */
1750 continue;
1751 thisPrice = p->commodityPrice[k].sum / p->commodityPrice[k].cnt;
1752 maxPrice = MAX( thisPrice, maxPrice );
1753 minPrice = MIN( thisPrice, minPrice );
1754 break;
1755 }
1756 }
1757
1758 /* Calculate best and worst profits */
1759 if (maxPrice > 0) {
1760 /* Commodity sold at this system */
1761 best = maxPrice - curMinPrice;
1762 worst= minPrice - curMaxPrice;
1763 if (best >= 0) { /* draw circle above */
1764 ccol = cLightBlue;
1765 ccol.a = a;
1766 gl_print(&gl_smallFont, x + (sys->pos.x+11) * zoom, y + (sys->pos.y-22)*zoom, &ccol, "%.1f",best);
1767 best = tanh ( 2*best / curMinPrice );
1768 col_blend( &ccol, &cFontBlue, &cFontYellow, best );
1769 ccol.a = a;
1770 gl_renderCircle( tx, ty /*+ r*/ , /*(0.1 + best) **/ r, &ccol, 1 );
1771 }
1772 else {/* draw circle below */
1773 ccol = cOrange;
1774 ccol.a = a;
1775 gl_print(&gl_smallFont, x + (sys->pos.x+12) * zoom, y + (sys->pos.y)*zoom-gl_smallFont.h*0.5, &ccol, _("%.1f ¤"),worst);
1776 worst = tanh ( -2*worst/ curMaxPrice );
1777 col_blend( &ccol, &cFontOrange, &cFontYellow, worst );
1778 ccol.a = a;
1779 gl_renderCircle( tx, ty /*- r*/ , /*(0.1 - worst) **/ r, &ccol, 1 );
1780 }
1781 }
1782 else {
1783 /* Commodity not sold here */
1784 ccol = cGrey10;
1785 ccol.a = a;
1786 gl_renderCircle( tx, ty, r, &ccol, 1 );
1787 }
1788 }
1789 }
1790 }
1791 else { /* cur_commod_mode == 0, showing actual prices */
1792 /* First calculate av price in all systems
1793 * This has already been done in map_update_commod_av_price
1794 * Now display the costs */
1795 for (int i=0; i<array_size(systems_stack); i++) {
1796 double tx, ty;
1797 StarSystem *sys = system_getIndex( i );
1798
1799 if (sys_isFlag(sys,SYSTEM_HIDDEN))
1800 continue;
1801
1802 /* if system is not known, reachable, or marked. and we are not in the editor */
1803 if ((!sys_isKnown(sys) && !sys_isFlag(sys, SYSTEM_MARKED | SYSTEM_CMARKED)
1804 && !space_sysReachable(sys)) && !editor)
1805 continue;
1806
1807 tx = x + sys->pos.x*zoom;
1808 ty = y + sys->pos.y*zoom;
1809
1810 /* Skip if out of bounds. */
1811 if (!rectOverlap(tx - r, ty - r, r, r, bx, by, w, h))
1812 continue;
1813
1814 /* If system is known fill it. */
1815 if ((sys_isKnown(sys)) && (system_hasSpob(sys))) {
1816 double sumPrice = 0;
1817 int sumCnt = 0;
1818 for (int j=0; j<array_size(sys->spobs); j++) {
1819 Spob *p = sys->spobs[j];
1820 for (int k=0; k<array_size(p->commodities); k++) {
1821 if (p->commodities[k] != c)
1822 continue;
1823 if (p->commodityPrice[k].cnt <= 0) /* commodity is not known about */
1824 continue;
1825 thisPrice = p->commodityPrice[k].sum / p->commodityPrice[k].cnt;
1826 sumPrice += thisPrice;
1827 sumCnt += 1;
1828 break;
1829 }
1830 }
1831
1832 if (sumCnt > 0) {
1833 /* Commodity sold at this system */
1834 /* Colour as a % of global average */
1835 double frac;
1836 sumPrice /= sumCnt;
1837 if (sumPrice < commod_av_gal_price) {
1838 frac = tanh(5*(commod_av_gal_price / sumPrice - 1));
1839 col_blend( &ccol, &cFontOrange, &cFontYellow, frac );
1840 }
1841 else {
1842 frac = tanh(5*(sumPrice / commod_av_gal_price - 1));
1843 col_blend( &ccol, &cFontBlue, &cFontYellow, frac );
1844 }
1845 ccol.a = a;
1846 gl_print(&gl_smallFont, x + (sys->pos.x+12)*zoom, y + (sys->pos.y)*zoom - gl_smallFont.h*0.5, &ccol, _("%.1f ¤"),sumPrice);
1847 gl_renderCircle( tx, ty, r, &ccol, 1 );
1848 }
1849 else {
1850 /* Commodity not sold here */
1851 ccol = cGrey10;
1852 ccol.a = a;
1853 gl_renderCircle( tx, ty, r, &ccol, 1 );
1854 }
1855 }
1856 }
1857 }
1858}
1859
1860/*
1861 * Renders the economy information.
1862 */
1863static void map_renderCommodIgnorance( double x, double y, double zoom,
1864 const StarSystem *sys, const Commodity *c, double a )
1865{
1866 int textw;
1867 char buf[80], *line2;
1868 size_t charn;
1869 glColour col = cFontRed;
1870 col.a = a;
1871
1872 snprintf( buf, sizeof(buf), _("No price info for\n%s here"), _(c->name) );
1873 line2 = u8_strchr( buf, '\n', &charn );
1874 if (line2 != NULL) {
1875 *line2++ = '\0';
1876 textw = gl_printWidthRaw( &gl_smallFont, line2 );
1877 gl_printRaw( &gl_smallFont, x + (sys->pos.x)*zoom - textw/2, y + (sys->pos.y-15)*zoom, &col, -1, line2 );
1878 }
1879 textw = gl_printWidthRaw( &gl_smallFont, buf );
1880 gl_printRaw( &gl_smallFont,x + sys->pos.x *zoom- textw/2, y + (sys->pos.y+10)*zoom, &col, -1, buf );
1881}
1882
1883static int factionPresenceCompare( const void *a, const void *b )
1884{
1885 FactionPresence *fpa, *fpb;
1886 fpa = (FactionPresence*) a;
1887 fpb = (FactionPresence*) b;
1888 if (fpa->value < fpb->value)
1889 return 1;
1890 else if (fpb->value < fpa->value)
1891 return -1;
1892 return strcmp( fpa->name, fpb->name );
1893}
1903void map_updateFactionPresence( const unsigned int wid, const char *name, const StarSystem *sys, int omniscient )
1904{
1905 size_t l;
1906 char buf[STRMAX_SHORT] = {'\0'};
1907 FactionPresence *presence;
1908
1909 /* Build the faction presence array. */
1910 presence = array_create( FactionPresence );
1911 for (int i=0; i<array_size(sys->presence); i++) {
1912 int matched;
1913 FactionPresence fp;
1914 if (sys->presence[i].value <= 0.)
1915 continue;
1916
1917 /* Determine properties. */
1918 fp.known = 1;
1919 if (!omniscient && !faction_isKnown( sys->presence[i].faction )) {
1920 fp.name = N_("Unknown");
1921 fp.known = 0;
1922 }
1923 else if (omniscient)
1924 fp.name = faction_name( sys->presence[i].faction );
1925 else
1926 fp.name = faction_mapname( sys->presence[i].faction );
1927 fp.value = sys->presence[i].value;
1928
1929 /* Try to add to existing. */
1930 matched = 0;
1931 for (int j=0; j<array_size(presence); j++) {
1932 if (strcmp(fp.name,presence[j].name)==0) {
1933 presence[j].value += fp.value;
1934 matched = 1;
1935 break;
1936 }
1937 }
1938 /* Insert new. */
1939 if (!matched)
1940 array_push_back( &presence, fp );
1941 }
1942 qsort( presence, array_size(presence), sizeof(FactionPresence), factionPresenceCompare );
1943
1944 l = 0;
1945 for (int i=0; i<array_size(presence); i++) {
1946 char col;
1947 FactionPresence *p = &presence[i];
1948 if (faction_exists( p->name ))
1949 col = faction_getColourChar( faction_get(p->name) );
1950 else
1951 col = 'N';
1952
1953 /* Use map grey instead of default neutral colour */
1954 l += scnprintf( &buf[l], sizeof(buf) - l, "%s#0%s: #%c%.0f", (l==0) ? "" : "\n",
1955 _(p->name), col, p->value );
1956 }
1957
1958 if (array_size(presence)==0)
1959 snprintf( buf, sizeof(buf), _("None") );
1960
1961 window_modifyText( wid, name, buf );
1962
1963 /* Cleanup. */
1964 array_free(presence);
1965}
1966
1970static void map_focusLose( unsigned int wid, const char* wgtname )
1971{
1972 (void) wgtname;
1973 CstMapWidget *cst = map_globalCustomData( wid );
1974 cst->drag = 0;
1975}
1976
1987static int map_mouse( unsigned int wid, SDL_Event* event, double mx, double my,
1988 double w, double h, double rx, double ry, void *data )
1989{
1990 (void) rx;
1991 (void) ry;
1992 CstMapWidget *cst = data;
1993
1994 const double t = 15.*15.; /* threshold */
1995
1996 switch (event->type) {
1997 case SDL_MOUSEWHEEL:
1998 /* Must be in bounds. */
1999 if ((mx < 0.) || (mx > w) || (my < 0.) || (my > h))
2000 return 0;
2001 if (event->wheel.y > 0)
2002 map_buttonZoom( wid, "btnZoomIn" );
2003 else if (event->wheel.y < 0)
2004 map_buttonZoom( wid, "btnZoomOut" );
2005 return 1;
2006
2007 case SDL_MOUSEBUTTONDOWN:
2008 /* Must be in bounds. */
2009 if ((mx < 0.) || (mx > w) || (my < 0.) || (my > h))
2010 return 0;
2011 window_setFocus( wid, "cstMap" );
2012
2013 /* selecting star system */
2014 mx -= w/2 - cst->xpos;
2015 my -= h/2 - cst->ypos;
2016 cst->drag = 1;
2017
2018 for (int i=0; i<array_size(systems_stack); i++) {
2019 double x, y;
2020 StarSystem *sys = system_getIndex( i );
2021
2022 if (sys_isFlag(sys, SYSTEM_HIDDEN))
2023 continue;
2024
2025 /* must be reachable */
2026 if (!sys_isFlag(sys, SYSTEM_MARKED | SYSTEM_CMARKED)
2027 && !space_sysReachable(sys))
2028 continue;
2029
2030 /* get position */
2031 x = sys->pos.x * cst->zoom;
2032 y = sys->pos.y * cst->zoom;
2033
2034 if ((pow2(mx-x)+pow2(my-y)) < t) {
2035 if (map_selected != -1) {
2036 if (sys == system_getIndex( map_selected ) && sys_isKnown(sys)) {
2037 map_system_open( map_selected );
2038 cst->drag = 0;
2039 }
2040 }
2041 map_select( sys, (SDL_GetModState() & KMOD_SHIFT) );
2042 break;
2043 }
2044 }
2045 return 1;
2046
2047 case SDL_MOUSEBUTTONUP:
2048 cst->drag = 0;
2049 break;
2050
2051 case SDL_MOUSEMOTION:
2052 if (cst->drag) {
2053 /* axis is inverted */
2054 cst->xtarget = cst->xpos -= rx;
2055 cst->ytarget = cst->ypos += ry;
2056 }
2057 map_mx = mx;
2058 map_my = my;
2059 break;
2060 }
2061
2062 return 0;
2063}
2070static void map_buttonZoom( unsigned int wid, const char* str )
2071{
2072 CstMapWidget *cst = map_globalCustomData(wid);
2073
2074 /* Transform coords to normal. */
2075 cst->xpos /= cst->zoom;
2076 cst->ypos /= cst->zoom;
2077 cst->xtarget /= cst->zoom;
2078 cst->ytarget /= cst->zoom;
2079
2080 /* Apply zoom. */
2081 if (strcmp(str,"btnZoomIn")==0) {
2082 cst->zoom *= 1.2;
2083 cst->zoom = MIN(2.5, cst->zoom);
2084 }
2085 else if (strcmp(str,"btnZoomOut")==0) {
2086 cst->zoom *= 0.8;
2087 cst->zoom = MAX(0.5, cst->zoom);
2088 }
2089
2090 map_setZoom( wid, cst->zoom );
2091
2092 /* Transform coords back. */
2093 cst->xpos *= cst->zoom;
2094 cst->ypos *= cst->zoom;
2095 cst->xtarget *= cst->zoom;
2096 cst->ytarget *= cst->zoom;
2097}
2098
2102static void map_genModeList(void)
2103{
2104 int totGot = 0;
2105 const char *odd_template, *even_template;
2106
2107 map_onClose( 0, NULL ); /* so commod_known, map_modes are freed */
2108 commod_known = calloc( commodity_getN(), sizeof(Commodity*) );
2109 for (int i=0; i<array_size(systems_stack); i++) {
2110 StarSystem *sys = system_getIndex( i );
2111 for (int j=0 ; j<array_size(sys->spobs); j++) {
2112 Spob *p = sys->spobs[j];
2113 for (int k=0; k<array_size(p->commodities); k++) {
2114 if (p->commodityPrice[k].cnt > 0 ) {/*commodity is known about*/
2115 int l;
2116 /* find out which commodity this is */
2117 for (l=0 ; l<totGot; l++) {
2118 if ( p->commodities[k] == commod_known[l] )
2119 break;
2120 }
2121 if (l == totGot) {
2122 commod_known[totGot] = p->commodities[k];
2123 totGot++;
2124 }
2125 }
2126 }
2127 }
2128 }
2129 map_modes = array_create_size( char*, 2*totGot + 1 );
2130 array_push_back( &map_modes, strdup(_("Travel (Default)")) );
2131 array_push_back( &map_modes, strdup(_("Discovery")) );
2132
2133 even_template = _("%s: Cost");
2134 odd_template = _("%s: Trade");
2135 for (int i=0; i<totGot; i++) {
2136 const char *commod_text = _(commod_known[i]->name);
2137 asprintf( &array_grow( &map_modes ), even_template, commod_text );
2138 asprintf( &array_grow( &map_modes ), odd_template, commod_text );
2139 }
2140}
2141
2148static void map_modeUpdate( unsigned int wid, const char* str )
2149{
2150 (void) str;
2151 if (listMapModeVisible==2) {
2152 listMapModeVisible=1;
2153 }
2154 else if (listMapModeVisible == 1) {
2155 int listpos = toolkit_getListPos( wid, "lstMapMode" );
2156 /* TODO: make this more robust. */
2157 if (listpos == 0) {
2158 map_mode = MAPMODE_TRAVEL;
2159 //cur_commod = -1;
2160 //cur_commod_mode = 0;
2161 }
2162 else if (listpos == 1) {
2163 map_mode = MAPMODE_DISCOVER;
2164 //cur_commod = -1;
2165 //cur_commod_mode = 0;
2166 }
2167 else {
2168 map_mode = MAPMODE_TRADE;
2169 cur_commod = (listpos - MAPMODE_TRADE) / 2;
2170 cur_commod_mode = (listpos - MAPMODE_TRADE) % 2 ; /* if 0, showing cost, if 1 showing difference */
2171 }
2172 }
2173 map_update(wid);
2174}
2175
2179static void map_modeActivate( unsigned int wid, const char* str )
2180{
2181 map_modeUpdate( wid, str );
2182 listMapModeVisible = 0;
2183 window_destroyWidget( wid, str );
2184}
2185
2186static void map_setMinimal( unsigned int wid, int value )
2187{
2188 map_minimal_mode = value;
2189 player.map_minimal = value;
2190 window_buttonCaption( wid, "btnMinimal", (value) ? _("Normal View") : _("Minimal View") );
2191}
2192
2196static void map_buttonMarkSystem( unsigned int wid, const char* str )
2197{
2198 (void) wid;
2199 (void) str;
2200 if (map_selected != -1) {
2201 StarSystem *sys = system_getIndex( map_selected );
2202
2203 /* Remove old note */
2204 if (sys->note != NULL) {
2205 free(sys->note);
2206 sys->note = NULL;
2207 }
2208
2209 /* Switch marking */
2210 if (sys_isFlag(sys, SYSTEM_PMARKED))
2211 sys_rmFlag(sys, SYSTEM_PMARKED);
2212 else {
2213 sys->note = dialogue_input(_("Add System Note"), 0, 60, _("Write a note about the #o%s#0 system:"), sys_isKnown(sys) ? _(sys->name) : _("Unknown") );
2214 if (sys->note != NULL)
2215 sys_setFlag(sys, SYSTEM_PMARKED);
2216 }
2217 }
2218}
2219
2223static void map_buttonSystemMap( unsigned int wid, const char* str )
2224{
2225 (void) wid;
2226 (void) str;
2227 if (map_selected != -1)
2228 if (sys_isKnown(system_getIndex( map_selected )))
2229 map_system_open( map_selected );
2230}
2231
2235static void map_buttonMinimal( unsigned int wid, const char* str )
2236{
2237 (void) str;
2238 map_setMinimal( wid, !map_minimal_mode );
2239}
2240
2247static void map_buttonCommodity( unsigned int wid, const char* str )
2248{
2249 (void) str;
2250 SDL_Keymod mods;
2251 char **this_map_modes;
2252 static int cur_commod_last = 0;
2253 static int cur_commod_mode_last = 0;
2254 static int map_mode_last = MAPMODE_TRAVEL;
2255 int defpos;
2256 /* Clicking the mode button - by default will show (or remove) the list of map modes.
2257 If ctrl is pressed, will toggle between current mode and default */
2258 mods = SDL_GetModState();
2259 if (mods & (KMOD_LCTRL | KMOD_RCTRL)) {/* toggle on/off */
2260 if (map_mode == MAPMODE_TRAVEL) {
2261 map_mode = map_mode_last;
2262 cur_commod = cur_commod_last;
2263 if (cur_commod == -1)
2264 cur_commod = 0;
2265 cur_commod_mode = cur_commod_mode_last;
2266 } else {
2267 map_mode_last = map_mode;
2268 map_mode = MAPMODE_TRAVEL;
2269 cur_commod_last = cur_commod;
2270 cur_commod_mode_last = cur_commod_mode;
2271 cur_commod = -1;
2272 }
2273 if (cur_commod >= (array_size(map_modes)-1)/2 )
2274 cur_commod = -1;
2275 /* And hide the list if it was visible. */
2276 if (listMapModeVisible) {
2277 listMapModeVisible = 0;
2278 window_destroyWidget( wid, "lstMapMode" );
2279 }
2280 map_update(wid);
2281 } else {/* no keyboard modifier */
2282 if (listMapModeVisible) {/* Hide the list widget */
2283 listMapModeVisible = 0;
2284 window_destroyWidget( wid, "lstMapMode" );
2285 } else {/* show the list widget */
2286 this_map_modes = calloc( sizeof(char*), array_size(map_modes) );
2287 for (int i=0; i<array_size(map_modes);i++) {
2288 this_map_modes[i]=strdup(map_modes[i]);
2289 }
2290 listMapModeVisible = 2;
2291 if (map_mode == MAPMODE_TRAVEL)
2292 defpos = 0;
2293 else if (map_mode == MAPMODE_DISCOVER)
2294 defpos = 1;
2295 else
2296 defpos = cur_commod*2 + MAPMODE_TRADE - cur_commod_mode;
2297
2298 window_addList( wid, -10, 60, 200, 200, "lstMapMode",
2299 this_map_modes, array_size(map_modes), defpos, map_modeUpdate, map_modeActivate );
2300 }
2301 }
2302}
2303
2307static void map_onClose( unsigned int wid, const char *str )
2308{
2309 (void) wid;
2310 (void) str;
2311 free( commod_known );
2312 commod_known = NULL;
2313 for (int i=0; i<array_size(map_modes); i++)
2314 free( map_modes[i] );
2315 array_free( map_modes );
2316 map_modes = NULL;
2317}
2318
2319void map_cleanup (void)
2320{
2321 map_close();
2322 map_clear();
2323}
2324
2328void map_close (void)
2329{
2330 unsigned int wid = window_get(MAP_WDWNAME);
2331 if (wid > 0)
2332 window_destroy(wid);
2333}
2334
2338void map_clear (void)
2339{
2340 array_free(map_path);
2341 map_path = NULL;
2342 map_selected = -1;
2343}
2344
2345static void map_updateInternal( CstMapWidget *cst, double dt )
2346{
2347 double dx, dy, mod, angle;
2348 double mapmin = 1.-map_minimal_mode;
2349
2350#define AMAX(x) (x) = MIN( 1., (x) + dt )
2351#define AMIN(x) (x) = MAX( 0., (x) - dt )
2352#define ATAR(x,y) \
2353if ((x) < y) (x) = MIN( y, (x) + dt ); \
2354else (x) = MAX( y, (x) - dt )
2355 switch (cst->mode) {
2356 case MAPMODE_EDITOR: /* fall through */
2357 case MAPMODE_TRAVEL:
2358 ATAR( cst->alpha_decorators, mapmin );
2359 ATAR( cst->alpha_faction, mapmin );
2360 ATAR( cst->alpha_env, mapmin );
2361 AMAX( cst->alpha_path );
2362 AMAX( cst->alpha_names );
2363 AMIN( cst->alpha_commod );
2364 AMAX( cst->alpha_markers );
2365 break;
2366
2367 case MAPMODE_DISCOVER:
2368 ATAR( cst->alpha_decorators, 0.5 * mapmin );
2369 ATAR( cst->alpha_faction, 0.5 * mapmin );
2370 ATAR( cst->alpha_env, mapmin );
2371 AMIN( cst->alpha_path );
2372 AMAX( cst->alpha_names );
2373 AMIN( cst->alpha_commod );
2374 AMAX( cst->alpha_markers );
2375 break;
2376
2377 case MAPMODE_TRADE:
2378 AMIN( cst->alpha_decorators );
2379 AMIN( cst->alpha_faction );
2380 AMIN( cst->alpha_env );
2381 AMIN( cst->alpha_path );
2382 AMIN( cst->alpha_names );
2383 AMAX( cst->alpha_commod );
2384 ATAR( cst->alpha_markers, 0.5 );
2385 break;
2386 }
2387#undef AMAX
2388#undef AMIN
2389#undef ATAR
2390
2391 dx = (cst->xtarget - cst->xpos);
2392 dy = (cst->ytarget - cst->ypos);
2393 mod = MOD(dx,dy);
2394 if (mod > 1e-5) {
2395 angle = ANGLE(dx,dy);
2396 /* TODO we should really do this with some nicer easing. */
2397 mod = MIN( mod, dt*map_flyto_speed);
2398 cst->xpos += mod * cos(angle);
2399 cst->ypos += mod * sin(angle);
2400 }
2401}
2402
2406static void map_reset( CstMapWidget* cst, MapMode mode )
2407{
2408 cst->mode = mode;
2409 map_updateInternal( cst, 1000. );
2410}
2411
2415static CstMapWidget* map_globalCustomData( unsigned int wid )
2416{
2417 if (wid==0)
2418 wid = window_get(MAP_WDWNAME);
2419 return (wid > 0) ? window_custGetData( wid, "cstMap" ) : NULL;
2420}
2421
2425static void map_selectCur (void)
2426{
2427 if (cur_system != NULL)
2428 map_selected = cur_system - systems_stack;
2429 else
2430 /* will probably segfault now */
2431 map_selected = -1;
2432}
2433
2440StarSystem* map_getDestination( int *jumps )
2441{
2442 if (array_size( map_path ) == 0)
2443 return NULL;
2444
2445 if (jumps != NULL)
2446 *jumps = array_size( map_path );
2447
2448 return array_back( map_path );
2449}
2450
2454void map_jump (void)
2455{
2456 /* set selected system to self */
2457 map_selectCur();
2458
2459 /* update path if set */
2460 if (array_size(map_path) != 0) {
2461 array_erase( &map_path, &map_path[0], &map_path[1] );
2462 if (array_size(map_path) == 0)
2464 else { /* get rid of bottom of the path */
2465 int j;
2466 /* set the next jump to be to the next in path */
2467 for (j=0; j<array_size(cur_system->jumps); j++) {
2468 if (map_path[0] == cur_system->jumps[j].target) {
2469 /* Restore selected system. */
2470 map_selected = array_back( map_path ) - systems_stack;
2471
2473 break;
2474 }
2475 }
2476 /* Overrode jump route manually, must clear target. */
2477 if (j>=array_size(cur_system->jumps))
2479 }
2480 }
2481 else
2483
2484 gui_setNav();
2485}
2486
2492void map_select( const StarSystem *sys, char shifted )
2493{
2494 int autonav;
2495 unsigned int wid = 0;
2496
2497 if (window_exists(MAP_WDWNAME))
2498 wid = window_get(MAP_WDWNAME);
2499
2500 if (sys == NULL) {
2501 map_selectCur();
2502 autonav = 0;
2503 }
2504 else {
2505 map_selected = sys - systems_stack;
2506
2507 /* select the current system and make a path to it */
2508 if (!shifted) {
2509 array_free( map_path );
2510 map_path = NULL;
2511 }
2512
2513 /* Try to make path if is reachable. */
2514 if (space_sysReachable(sys)) {
2515 map_path = map_getJumpPath( cur_system->name, sys->name, 0, 1, map_path );
2516
2517 if (array_size(map_path)==0) {
2521 autonav = 0;
2522 }
2523 else {
2524 /* see if it is a valid hyperspace target */
2525 for (int i=0; i<array_size(cur_system->jumps); i++) {
2526 if (map_path[0] == cur_system->jumps[i].target) {
2529 break;
2530 }
2531 }
2532 autonav = 1;
2533 }
2534 }
2535 else { /* unreachable. */
2538 autonav = 0;
2539 }
2540 }
2541
2542 if (wid != 0) {
2543 if (autonav)
2544 window_enableButton( wid, "btnAutonav" );
2545 else
2546 window_disableButton( wid, "btnAutonav" );
2547 }
2548
2549 map_update(wid);
2550 gui_setNav();
2551}
2552
2558void map_cycleMissions(int dir)
2559{
2560 // StarSystem* systems_stack = system_getAll();
2561 StarSystem *dest = map_getDestination( NULL );
2562 StarSystem *target;
2563 int found_next_i = -1;
2564 int found_prev_i = -1;
2565 int found_b = 0;
2566 int i;
2567
2568 /* Select current selection - do nothing */
2569 if (dir==0)
2570 return;
2571
2572 /* Default : points to current system */
2573 if (dest==NULL)
2574 dest = cur_system;
2575
2576 /* Universally find prev and next mission system */
2577 for (i=0;i<array_size(systems_stack);i++) {
2578 if (!sys_isFlag(&systems_stack[i], SYSTEM_MARKED | SYSTEM_PMARKED) || !space_sysReachable(&systems_stack[i]) )
2579 continue;
2580
2581 /* Pre-select first in case we will wrap */
2582 if (found_next_i<0)
2583 found_next_i = i;
2584
2585 /* We found next system */
2586 if (found_b) {
2587 found_next_i = i;
2588 break;
2589 }
2590
2591 /* We found currently selected system */
2592 if (&systems_stack[i]==dest)
2593 found_b=1;
2594 else
2595 found_prev_i = i; /* Follow trail as we go */
2596 }
2597
2598 /* No trail for prev system - current one was first one in list - just finish loop and find last one */
2599 if (found_prev_i<0)
2600 for (;i<array_size(systems_stack);i++)
2601 if (sys_isMarked(&systems_stack[i]) && space_sysReachable(&systems_stack[i]))
2602 found_prev_i = i;
2603
2604 /* Select found system or return if no suitable was found */
2605 if (dir>0 && found_next_i>=0)
2606 target = &systems_stack[found_next_i];
2607 else if (dir<0 && found_prev_i>=0)
2608 target = &systems_stack[found_prev_i];
2609 else
2610 return;
2611
2612 /* Select and center system. */
2613 map_select( target, 0 );
2614 map_center( window_get( MAP_WDWNAME ), target->name );
2615
2616 /* Autonav to selected system */
2617 //player_hyperspacePreempt( 1 );
2618 //player_autonavStart();
2619}
2620
2624void map_toggleNotes()
2625{
2626 map_show_notes = !map_show_notes;
2627}
2628/*
2629 * A* algorithm for shortest path finding
2630 *
2631 * Note since that we can't actually get an admissible heurestic for A* this is
2632 * in reality just Djikstras. I've removed the heurestic bit to make sure I
2633 * don't try to implement an admissible heuristic when I'm pretty sure there is
2634 * none.
2635 */
2639typedef struct SysNode_ {
2640 struct SysNode_ *next;
2641 struct SysNode_ *gnext;
2643 struct SysNode_ *parent;
2644 StarSystem* sys;
2645 int g;
2646} SysNode;
2647static SysNode *A_gc;
2648/* prototypes */
2649static SysNode* A_newNode( StarSystem* sys );
2650static int A_g( SysNode* n );
2651static SysNode* A_add( SysNode *first, SysNode *cur );
2652static SysNode* A_rm( SysNode *first, StarSystem *cur );
2653static SysNode* A_in( SysNode *first, StarSystem *cur );
2654static SysNode* A_lowest( SysNode *first );
2655static void A_freeList( SysNode *first );
2656static int map_decorator_parse( MapDecorator *temp, const char *file );
2658static SysNode* A_newNode( StarSystem* sys )
2659{
2660 SysNode *n = malloc(sizeof(SysNode));
2661
2662 n->next = NULL;
2663 n->sys = sys;
2664
2665 n->gnext = A_gc;
2666 A_gc = n;
2667
2668 return n;
2669}
2671static int A_g( SysNode* n )
2672{
2673 return n->g;
2674}
2676static SysNode* A_add( SysNode *first, SysNode *cur )
2677{
2678 SysNode *n;
2679
2680 if (first == NULL)
2681 return cur;
2682
2683 n = first;
2684 while (n->next != NULL)
2685 n = n->next;
2686 n->next = cur;
2687
2688 return first;
2689}
2690/* @brief Removes a node from a linked list. */
2691static SysNode* A_rm( SysNode *first, StarSystem *cur )
2692{
2693 SysNode *n, *p;
2694
2695 if (first->sys == cur) {
2696 n = first->next;
2697 first->next = NULL;
2698 return n;
2699 }
2700
2701 p = first;
2702 n = p->next;
2703 do {
2704 if (n->sys == cur) {
2705 n->next = NULL;
2706 p->next = n->next;
2707 break;
2708 }
2709 p = n;
2710 } while ((n=n->next) != NULL);
2711
2712 return first;
2713}
2715static SysNode* A_in( SysNode *first, StarSystem *cur )
2716{
2717 SysNode *n;
2718
2719 if (first == NULL)
2720 return NULL;
2721
2722 n = first;
2723 do {
2724 if (n->sys == cur)
2725 return n;
2726 } while ((n=n->next) != NULL);
2727 return NULL;
2728}
2730static SysNode* A_lowest( SysNode *first )
2731{
2732 SysNode *lowest, *n;
2733
2734 if (first == NULL)
2735 return NULL;
2736
2737 n = first;
2738 lowest = n;
2739 do {
2740 if (n->g < lowest->g)
2741 lowest = n;
2742 } while ((n=n->next) != NULL);
2743 return lowest;
2744}
2746static void A_freeList( SysNode *first )
2747{
2748 SysNode *p, *n;
2749
2750 if (first == NULL)
2751 return;
2752
2753 p = NULL;
2754 n = first;
2755 do {
2756 free(p);
2757 p = n;
2758 } while ((n=n->gnext) != NULL);
2759 free(p);
2760}
2761
2763void map_setZoom( unsigned int wid, double zoom )
2764{
2765 CstMapWidget *cst = map_globalCustomData(wid);
2766 cst->zoom = zoom;
2767}
2768
2779StarSystem** map_getJumpPath( const char* sysstart, const char* sysend,
2780 int ignore_known, int show_hidden, StarSystem** old_data )
2781{
2782 int j, cost, njumps, ojumps;
2783 StarSystem *ssys, *esys, **res;
2784
2785 SysNode *cur, *neighbour;
2786 SysNode *open, *closed;
2787 SysNode *ocost, *ccost;
2788
2789 A_gc = NULL;
2790 res = old_data;
2791 ojumps = array_size( old_data );
2792
2793 /* initial and target systems */
2794 ssys = system_get(sysstart); /* start */
2795 esys = system_get(sysend); /* goal */
2796
2797 /* Set up. */
2798 if (ojumps > 0)
2799 ssys = system_get( array_back( old_data )->name );
2800
2801 /* Check self. */
2802 if (ssys==esys || array_size(ssys->jumps)==0) {
2803 array_free( res );
2804 return NULL;
2805 }
2806
2807 /* system target must be known and reachable */
2808 if (!ignore_known && !sys_isKnown(esys) && !space_sysReachable(esys)) {
2809 /* can't reach - don't make path */
2810 array_free( res );
2811 return NULL;
2812 }
2813
2814 /* start the linked lists */
2815 open = closed = NULL;
2816 cur = A_newNode( ssys );
2817 cur->parent = NULL;
2818 cur->g = 0;
2819 open = A_add( open, cur ); /* Initial open node is the start system */
2820
2821 j = 0;
2822 while ((cur = A_lowest(open))) {
2823 /* End condition. */
2824 if (cur->sys == esys)
2825 break;
2826
2827 /* Break if infinite loop. */
2828 j++;
2829 if (j > MAP_LOOP_PROT)
2830 break;
2831
2832 /* Get best from open and toss to closed */
2833 open = A_rm( open, cur->sys );
2834 closed = A_add( closed, cur );
2835 cost = A_g(cur) + 1; /* Base unit is jump and always increases by 1. */
2836
2837 for (int i=0; i<array_size(cur->sys->jumps); i++) {
2838 JumpPoint *jp = &cur->sys->jumps[i];
2839 StarSystem *sys = jp->target;
2840
2841 /* Make sure it's reachable */
2842 if (!ignore_known) {
2843 if (!jp_isKnown(jp))
2844 continue;
2845 if (!sys_isKnown(sys) && !space_sysReachable(sys))
2846 continue;
2847 }
2848 if (jp_isFlag( jp, JP_EXITONLY ))
2849 continue;
2850
2851 /* Skip hidden jumps if they're not specifically requested */
2852 if (!show_hidden && jp_isFlag( jp, JP_HIDDEN ))
2853 continue;
2854
2855 /* Check to see if it's already in the closed set. */
2856 ccost = A_in(closed, sys);
2857 if ((ccost != NULL) && (cost >= A_g(ccost)))
2858 continue;
2859 //closed = A_rm( closed, sys );
2860
2861 /* Remove if it exists and current is better. */
2862 ocost = A_in(open, sys);
2863 if (ocost != NULL) {
2864 if (cost < A_g(ocost))
2865 open = A_rm( open, sys ); /* New path is better */
2866 else
2867 continue; /* This node is worse, so ignore it. */
2868 }
2869
2870 /* Create the node. */
2871 neighbour = A_newNode( sys );
2872 neighbour->parent = cur;
2873 neighbour->g = cost;
2874 open = A_add( open, neighbour );
2875 }
2876
2877 /* Safety check in case not linked. */
2878 if (open == NULL)
2879 break;
2880 }
2881
2882 /* Build path backwards if not broken from loop. */
2883 if (cur != NULL && esys == cur->sys) {
2884 njumps = A_g(cur) + ojumps;
2885 assert( njumps > ojumps );
2886 if (res == NULL)
2887 res = array_create_size( StarSystem*, njumps );
2888 array_resize( &res, njumps );
2889 /* Build path. */
2890 for (int i=0; i<njumps-ojumps; i++) {
2891 res[njumps-i-1] = cur->sys;
2892 cur = cur->parent;
2893 }
2894 }
2895 else {
2896 res = NULL;
2897 array_free( old_data );
2898 }
2899
2900 /* free the linked lists */
2901 A_freeList(A_gc);
2902 return res;
2903}
2904
2912int map_map( const Outfit *map )
2913{
2914 for (int i=0; i<array_size(map->u.map->systems);i++)
2915 sys_setFlag(map->u.map->systems[i], SYSTEM_KNOWN);
2916
2917 for (int i=0; i<array_size(map->u.map->spobs);i++)
2918 spob_setKnown(map->u.map->spobs[i]);
2919
2920 for (int i=0; i<array_size(map->u.map->jumps);i++)
2921 jp_setFlag(map->u.map->jumps[i], JP_KNOWN);
2922
2923 return 1;
2924}
2925
2933int map_isUseless( const Outfit* map )
2934{
2935 for (int i=0; i<array_size(map->u.map->systems);i++)
2936 if (!sys_isKnown(map->u.map->systems[i]))
2937 return 0;
2938
2939 for (int i=0; i<array_size(map->u.map->spobs);i++) {
2940 Spob *p = map->u.map->spobs[i];
2941 if (!spob_hasSystem( p ) )
2942 continue;
2943 if (!spob_isKnown(p))
2944 return 0;
2945 }
2946
2947 for (int i=0; i<array_size(map->u.map->jumps);i++)
2948 if (!jp_isKnown(map->u.map->jumps[i]))
2949 return 0;
2950
2951 return 1;
2952}
2953
2957int localmap_map( const Outfit *lmap )
2958{
2959 double detect, mod;
2960
2961 if (cur_system==NULL)
2962 return 0;
2963
2964 mod = pow2( 200. / (cur_system->interference + 200.) );
2965
2966 detect = lmap->u.lmap.jump_detect;
2967 for (int i=0; i<array_size(cur_system->jumps); i++) {
2968 JumpPoint *jp = &cur_system->jumps[i];
2969 if (jp_isFlag(jp, JP_EXITONLY) || jp_isFlag(jp, JP_HIDDEN))
2970 continue;
2971 if (mod*jp->hide <= detect)
2972 jp_setFlag( jp, JP_KNOWN );
2973 }
2974
2975 detect = lmap->u.lmap.spob_detect;
2976 for (int i=0; i<array_size(cur_system->spobs); i++) {
2977 Spob *p = cur_system->spobs[i];
2978 if (!spob_hasSystem( p ) )
2979 continue;
2980 if (mod*p->hide <= detect)
2981 spob_setKnown( p );
2982 }
2983 return 0;
2984}
2985
2990int localmap_isUseless( const Outfit *lmap )
2991{
2992 double detect, mod;
2993
2994 if (cur_system==NULL)
2995 return 1;
2996
2997 mod = pow2( 200. / (cur_system->interference + 200.) );
2998
2999 detect = lmap->u.lmap.jump_detect;
3000 for (int i=0; i<array_size(cur_system->jumps); i++) {
3001 JumpPoint *jp = &cur_system->jumps[i];
3002 if (jp_isFlag(jp, JP_EXITONLY) || jp_isFlag(jp, JP_HIDDEN))
3003 continue;
3004 if ((mod*jp->hide <= detect) && !jp_isKnown( jp ))
3005 return 0;
3006 }
3007
3008 detect = lmap->u.lmap.spob_detect;
3009 for (int i=0; i<array_size(cur_system->spobs); i++) {
3010 Spob *p = cur_system->spobs[i];
3011 if ((mod*p->hide <= detect) && !spob_isKnown( p ))
3012 return 0;
3013 }
3014 return 1;
3015}
3016
3029void map_show( int wid, int x, int y, int w, int h, double zoom, double xoff, double yoff )
3030{
3031 CstMapWidget *cst = calloc( 1, sizeof(CstMapWidget) );
3032
3033 /* New widget. */
3034 window_addCust( wid, x, y, w, h,
3035 "cstMap", 1, map_render, map_mouse, NULL, map_focusLose, cst );
3036 window_custAutoFreeData( wid, "cstMap" );
3037
3038 /* Set up stuff. */
3039 map_setup();
3040
3041 /* Centering stuff. */
3042 cst->xoff = xoff;
3043 cst->yoff = yoff;
3044
3045 /* Set position to focus on current system. */
3046 cst->xtarget = cst->xpos = cur_system->pos.x * zoom + cst->xoff;
3047 cst->ytarget = cst->ypos = cur_system->pos.y * zoom + cst->yoff;
3048
3049 /* Set zoom. */
3050 map_setZoom( wid, zoom );
3051
3052 map_reset( cst, MAPMODE_TRAVEL );
3053}
3054
3062int map_center( int wid, const char *sys )
3063{
3064 double d;
3065 CstMapWidget *cst = map_globalCustomData( wid );
3066
3067 /* Get the system. */
3068 StarSystem *ssys = system_get( sys );
3069 if (ssys == NULL)
3070 return -1;
3071
3072 /* Center on the system. */
3073 cst->xtarget = ssys->pos.x * cst->zoom + cst->xoff;
3074 cst->ytarget = ssys->pos.y * cst->zoom + cst->yoff;
3075
3076 /* Compute flyto speed. */
3077 d = MOD( cst->xtarget-cst->xpos, cst->ytarget-cst->ypos );
3078 map_flyto_speed = MIN( 2000., d / 0.2 );
3079
3080 return 0;
3081}
3082
3088int map_load (void)
3089{
3090 Uint32 time = SDL_GetTicks();
3091 char **decorator_files = ndata_listRecursive( MAP_DECORATOR_DATA_PATH );
3092
3093 decorator_stack = array_create( MapDecorator );
3094 for (int i=0; i<array_size(decorator_files); i++) {
3095 MapDecorator temp;
3096 int ret = map_decorator_parse( &temp, decorator_files[i] );
3097 if (ret == 0)
3098 array_push_back( &decorator_stack, temp );
3099 free( decorator_files[i] );
3100 }
3101 array_free( decorator_files );
3102
3103 if (conf.devmode) {
3104 time = SDL_GetTicks() - time;
3105 DEBUG( n_( "Loaded %d map decorator in %.3f s", "Loaded %d map decorators in %.3f s", array_size(decorator_stack) ), array_size(decorator_stack), time/1000. );
3106 }
3107 else
3108 DEBUG( n_( "Loaded %d map decorator", "Loaded %d map decorators", array_size(decorator_stack) ), array_size(decorator_stack) );
3109
3110 return 0;
3111}
3112
3113static int map_decorator_parse( MapDecorator *temp, const char *file )
3114{
3115 xmlDocPtr doc;
3116 xmlNodePtr node, parent;
3117
3118 doc = xml_parsePhysFS( file );
3119 if (doc == NULL)
3120 return -1;
3121
3122 parent = doc->xmlChildrenNode; /* map node */
3123 if (strcmp((char*)parent->name,"decorator")) {
3124 ERR(_("Malformed %s file: missing root element 'decorator'"), file );
3125 return -1;
3126 }
3127
3128 /* Clear memory. */
3129 memset( temp, 0, sizeof(MapDecorator) );
3130
3131 temp->detection_radius = 10;
3132
3133 /* Parse body. */
3134 node = parent->xmlChildrenNode;
3135 do {
3136 xml_onlyNodes(node);
3137 xmlr_float(node, "x", temp->x);
3138 xmlr_float(node, "y", temp->y);
3139 xmlr_int(node, "detection_radius", temp->detection_radius);
3140 if (xml_isNode(node,"image")) {
3141 temp->image = xml_parseTexture( node,
3142 MAP_DECORATOR_GFX_PATH"%s", 1, 1, OPENGL_TEX_MIPMAPS );
3143
3144 if (temp->image == NULL)
3145 WARN(_("Could not load map decorator texture '%s'."), xml_get(node));
3146
3147 continue;
3148 }
3149 WARN(_("Map decorator has unknown node '%s'."), node->name);
3150 } while (xml_nextNode(node));
3151
3152 xmlFreeDoc(doc);
3153
3154 return 0;
3155}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition: array.h:158
#define array_resize(ptr_array, new_size)
Resizes the array to accomodate new_size elements.
Definition: array.h:112
#define array_create_size(basic_type, capacity)
Creates a new dynamic array of ‘basic_type’ with an initial capacity.
Definition: array.h:102
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
Definition: array.h:140
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition: array.h:168
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition: array.h:119
#define array_back(ptr_array)
Returns the last element in the array.
Definition: array.h:216
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition: array.h:129
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition: array.h:93
#define BUTTON_HEIGHT
Definition: board.c:33
#define BUTTON_WIDTH
Definition: board.c:32
void col_blend(glColour *blend, const glColour *fg, const glColour *bg, float alpha)
Blends two colours.
Definition: colour.c:192
StarSystem * systems_stack
Definition: space.c:92
char * dialogue_input(const char *title, int min, int max, const char *fmt,...)
Creates a dialogue that allows the player to write a message.
Definition: dialogue.c:436
const char * faction_longname(int f)
Gets the faction's long name (formal, human-readable).
Definition: faction.c:346
int faction_exists(const char *name)
Checks to see if a faction exists by name.
Definition: faction.c:171
int faction_isKnown(int id)
Is the faction known?
Definition: faction.c:273
char faction_getColourChar(int f)
Gets the faction character associated to its standing with the player.
Definition: faction.c:1040
const glTexture * faction_logo(int f)
Gets the faction's logo (ideally 256x256).
Definition: faction.c:451
int areEnemies(int a, int b)
Checks whether two factions are enemies.
Definition: faction.c:1197
const char * faction_name(int f)
Gets a factions "real" (internal) name.
Definition: faction.c:304
const char * faction_getStandingText(int f)
Gets the player's standing in human readable form.
Definition: faction.c:1054
const glColour * faction_colour(int f)
Gets the colour of the faction.
Definition: faction.c:467
const char * faction_mapname(int f)
Gets the faction's map name (translated).
Definition: faction.c:363
int faction_get(const char *name)
Gets a faction ID by name.
Definition: faction.c:182
int areAllies(int a, int b)
Checks whether two factions are allies or not.
Definition: faction.c:1222
int gl_printHeightRaw(const glFont *ft_font, const int width, const char *text)
Gets the height of a non-formatted string.
Definition: font.c:1026
glFont gl_smallFont
Definition: font.c:154
void gl_printRaw(const glFont *ft_font, double x, double y, const glColour *c, double outlineR, const char *text)
Prints text on screen.
Definition: font.c:616
int gl_printWidthRaw(const glFont *ft_font, const char *text)
Gets the width that it would take to print some text.
Definition: font.c:960
glFont gl_defFont
Definition: font.c:153
void gl_print(const glFont *ft_font, const double x, const double y, const glColour *c, const char *fmt,...)
Prints text on screen like printf.
Definition: font.c:690
void gui_setNav(void)
Player just changed their nav computer target.
Definition: gui.c:1749
int landed
Definition: land.c:74
Spob * land_spob
Definition: land.c:82
void mat4_translate(mat4 *m, double x, double y, double z)
Translates a homogenous transformation matrix.
Definition: mat4.c:99
void mat4_scale(mat4 *m, double x, double y, double z)
Scales a homogeneous transformation matrix.
Definition: mat4.c:82
void mission_sysMark(void)
Marks all active systems that need marking.
Definition: mission.c:516
double naev_getrealdt(void)
Gets the last delta-tick.
Definition: naev.c:1122
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition: naev.h:40
#define CLAMP(a, b, x)
Definition: naev.h:41
#define pow2(x)
Definition: naev.h:46
#define MAX(x, y)
Definition: naev.h:39
char ** ndata_listRecursive(const char *path)
Lists all the visible files in a directory, at any depth.
Definition: ndata.c:232
static char buf[NEWS_MAX_LENGTH]
Definition: news.c:45
int rectOverlap(double x, double y, double w, double h, double x2, double y2, double w2, double h2)
Checks whether two rectangles overlap at any point.
Definition: nmath.c:93
int asprintf(char **strp, const char *fmt,...)
Like sprintf(), but it allocates a large-enough string and returns the pointer in the first argument....
Definition: nstring.c:161
int scnprintf(char *text, size_t maxlen, const char *fmt,...)
Like snprintf(), but returns the number of characters ACTUALLY "printed" into the buffer....
Definition: nstring.c:178
glTexture * xml_parseTexture(xmlNodePtr node, const char *path, int defsx, int defsy, const unsigned int flags)
Parses a texture handling the sx and sy elements.
Definition: nxml.c:29
xmlDocPtr xml_parsePhysFS(const char *filename)
Analogous to xmlParseMemory/xmlParseFile.
Definition: nxml.c:75
void gl_renderShader(double x, double y, double w, double h, double r, const SimpleShader *shd, const glColour *c, int center)
Renders a simple shader.
void gl_renderRect(double x, double y, double w, double h, const glColour *c)
Renders a rectangle.
Definition: opengl_render.c:90
void gl_renderScale(const glTexture *texture, double bx, double by, double bw, double bh, const glColour *c)
Blits a texture scaling it.
void gl_renderCircle(double cx, double cy, double r, const glColour *c, int filled)
Draws a circle.
void gl_freeTexture(glTexture *texture)
Frees a texture.
Definition: opengl_tex.c:755
void gl_vboActivateAttribOffset(gl_vbo *vbo, GLuint index, GLuint offset, GLint size, GLenum type, GLsizei stride)
Activates a VBO's offset.
Definition: opengl_vbo.c:228
int pilot_getJumps(const Pilot *p)
Gets the amount of jumps the pilot has left.
Definition: pilot.c:1316
void player_targetHyperspaceSet(int id, int nomsg)
Sets the player's hyperspace target.
Definition: player.c:1778
void player_hyperspacePreempt(int preempt)
Enables or disables jump points preempting spobs in autoface and target clearing.
Definition: player.c:1851
Player_t player
Definition: player.c:73
void player_autonavStartWindow(unsigned int wid, const char *str)
Starts autonav and closes the window.
void player_autonavAbortJump(const char *reason)
Aborts regular interstellar autonav, but not in-system autonav.
static const double c[]
Definition: rng.c:264
static const double a[]
Definition: rng.c:247
static const double d[]
Definition: rng.c:273
StarSystem * system_getIndex(int id)
Get the system by its index.
Definition: space.c:944
int spob_hasSystem(const Spob *spb)
Get whether or not a spob has a system (i.e. is on the map).
Definition: space.c:966
const char * spob_getSymbol(const Spob *p)
Gets the spob symbol.
Definition: space.c:1853
char spob_getColourChar(const Spob *p)
Gets the spob colour char.
Definition: space.c:1834
int space_sysReachable(const StarSystem *sys)
Sees if a system is reachable.
Definition: space.c:794
int space_sysReachableFromSys(const StarSystem *target, const StarSystem *sys)
Sees if a system is reachable from another system.
Definition: space.c:833
StarSystem * system_get(const char *sysname)
Get the system from its name.
Definition: space.c:914
void spob_setKnown(Spob *p)
Sets a spob's known status, if it's real.
Definition: space.c:1071
StarSystem * cur_system
Definition: space.c:105
void spob_updateLand(Spob *p)
Updates the land possibilities of a spob.
Definition: space.c:1896
int system_hasSpob(const StarSystem *sys)
See if the system has a spob.
Definition: space.c:4251
const char * spob_name(const Spob *p)
Gets the translated name of a spob.
Definition: space.c:1705
const char * spob_getServiceName(int service)
Gets the (English) name for a service code.
Definition: space.c:165
double sum
Definition: commodity.h:76
Represents a commodity.
Definition: commodity.h:43
Map widget data.
Definition: map.c:65
double ytarget
Definition: map.c:72
double alpha_faction
Definition: map.c:75
double xtarget
Definition: map.c:71
double alpha_decorators
Definition: map.c:74
double xoff
Definition: map.c:66
double alpha_commod
Definition: map.c:79
double alpha_env
Definition: map.c:76
double alpha_names
Definition: map.c:78
MapMode mode
Definition: map.c:81
double alpha_path
Definition: map.c:77
double ypos
Definition: map.c:70
double yoff
Definition: map.c:67
double xpos
Definition: map.c:69
double zoom
Definition: map.c:68
double alpha_markers
Definition: map.c:80
int drag
Definition: map.c:73
Faction presence container to be used for the map information stuff.
Definition: map.c:56
double value
Definition: map.c:58
const char * name
Definition: map.c:57
int known
Definition: map.c:59
Images to be shown on the map.
Definition: map.h:14
double y
Definition: map.h:16
glTexture * image
Definition: map.h:15
int detection_radius
Definition: map.h:17
double jump_detect
Definition: outfit.h:283
double spob_detect
Definition: outfit.h:284
A ship outfit, depends radically on the type.
Definition: outfit.h:304
OutfitLocalMapData lmap
Definition: outfit.h:378
union Outfit::@22 u
OutfitMapData_t * map
Definition: outfit.h:377
double fuel
Definition: pilot.h:253
double fuel_consumption
Definition: pilot.h:254
int devmode
Definition: conf.h:158
Pilot * p
Definition: player.h:101
int map_minimal
Definition: player.h:127
int faction
Definition: space.h:66
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
Definition: space.h:88
int can_land
Definition: space.h:106
Commodity ** commodities
Definition: space.h:114
unsigned int services
Definition: space.h:113
char * feature
Definition: space.h:92
CommodityPrice * commodityPrice
Definition: space.h:115
SpobPresence presence
Definition: space.h:102
Node structure for A* pathfinding.
Definition: map.c:2639
StarSystem * sys
Definition: map.c:2644
struct SysNode_ * parent
Definition: map.c:2643
struct SysNode_ * next
Definition: map.c:2640
struct SysNode_ * gnext
Definition: map.c:2641
int g
Definition: map.c:2645
Represents a font in memory.
Definition: font.h:16
int h
Definition: font.h:18
Abstraction for rendering sprite sheets.
Definition: opengl_tex.h:34
double sw
Definition: opengl_tex.h:44
double sh
Definition: opengl_tex.h:45
double w
Definition: opengl_tex.h:38
double h
Definition: opengl_tex.h:39
Definition: mat4.h:10
unsigned int window_create(const char *name, const char *displayname, const int x, const int y, const int w, const int h)
Creates a window.
Definition: toolkit.c:696
void window_dimWidget(unsigned int wid, const char *name, int *w, int *h)
Gets the dimensions of a widget.
Definition: toolkit.c:416
void window_setFocus(unsigned int wid, const char *wgtname)
Sets the focused widget in a window.
Definition: toolkit.c:2460
void window_dimWindow(unsigned int wid, int *w, int *h)
Gets the dimensions of a window.
Definition: toolkit.c:371
void window_setCancel(unsigned int wid, void(*cancel)(unsigned int, const char *))
Sets the default cancel function of the window.
Definition: toolkit.c:900
void window_onClose(unsigned int wid, void(*fptr)(unsigned int, const char *))
Sets the default close function of the window.
Definition: toolkit.c:840
void window_moveWidget(unsigned int wid, const char *name, int x, int y)
Moves a widget.
Definition: toolkit.c:467
void window_handleKeys(unsigned int wid, int(*keyhandler)(unsigned int, SDL_Keycode, SDL_Keymod))
Sets the key handler for the window.
Definition: toolkit.c:972
void window_destroyWidget(unsigned int wid, const char *wgtname)
Destroys a widget in a window.
Definition: toolkit.c:1162
int window_isTop(unsigned int wid)
Checks to see if a window is at the top.
Definition: toolkit.c:551
unsigned int window_get(const char *wdwname)
Gets the ID of a window.
Definition: toolkit.c:671
void window_setBorder(unsigned int wid, int enable)
Sets or removes the border of a window.
Definition: toolkit.c:953
void window_resizeWidget(unsigned int wid, const char *name, int w, int h)
Resizes a widget.
Definition: toolkit.c:495
int window_exists(const char *wdwname)
Checks to see if a window exists.
Definition: toolkit.c:598
void window_close(unsigned int wid, const char *str)
Helper function to automatically close the window calling it.
Definition: toolkit.c:1031
void window_destroy(unsigned int wid)
Kills the window.
Definition: toolkit.c:1042