1 /*
2  * See Licensing and Copyright notice in naev.h
3  */
4 
5 /**
6  * @file gui.c
7  *
8  * @brief Contains the GUI stuff for the player.
9  */
10 
11 
12 #include "gui.h"
13 
14 #include "naev.h"
15 
16 #include <stdlib.h>
17 
18 #include "player.h"
19 #include "nxml.h"
20 #include "pilot.h"
21 #include "log.h"
22 #include "opengl.h"
23 #include "font.h"
24 #include "ndata.h"
25 #include "space.h"
26 #include "rng.h"
27 #include "land.h"
28 #include "sound.h"
29 #include "economy.h"
30 #include "pause.h"
31 #include "menu.h"
32 #include "toolkit.h"
33 #include "dialogue.h"
34 #include "mission.h"
35 #include "nlua_misn.h"
36 #include "ntime.h"
37 #include "hook.h"
38 #include "map.h"
39 #include "nfile.h"
40 #include "spfx.h"
41 #include "unidiff.h"
42 #include "comm.h"
43 #include "intro.h"
44 #include "perlin.h"
45 #include "ai.h"
46 #include "music.h"
47 #include "nmath.h"
48 #include "gui_osd.h"
49 #include "conf.h"
50 #include "nebula.h"
51 #include "camera.h"
52 #include "pilot.h"
53 #include "nlua.h"
54 #include "nluadef.h"
55 #include "nlua_gfx.h"
56 #include "nlua_gui.h"
57 #include "nlua_tex.h"
58 #include "gui_omsg.h"
59 #include "nstring.h"
60 
61 
62 #define XML_GUI_ID   "GUIs" /**< XML section identifier for GUI document. */
63 #define XML_GUI_TAG  "gui" /**<  XML Section identifier for GUI tags. */
64 
65 #define INTERFERENCE_LAYERS      16 /**< Number of interference layers. */
66 #define INTERFERENCE_CHANGE_DT   0.1 /**< Speed to change at. */
67 
68 #define RADAR_BLINK_PILOT        1. /**< Blink rate of the pilot target on radar. */
69 #define RADAR_BLINK_PLANET       1. /**< Blink rate of the planet target on radar. */
70 
71 
72 /* for interference. */
73 static int interference_layer = 0; /**< Layer of the current interference. */
74 double interference_alpha     = 0.; /**< Alpha of the current interference layer. */
75 static double interference_t  = 0.; /**< Interference timer to control transitions. */
76 
77 /* some blinking stuff. */
78 static double blink_pilot     = 0.; /**< Timer on target blinking on radar. */
79 static double blink_planet    = 0.; /**< Timer on planet blinking on radar. */
80 
81 /* for VBO. */
82 static gl_vbo *gui_vbo = NULL; /**< GUI VBO. */
83 static GLsizei gui_vboColourOffset = 0; /**< Offset of colour pixels. */
84 
85 static int gui_getMessage     = 1; /**< Whether or not the player should receive messages. */
86 
87 /*
88  * pilot stuff for GUI
89  */
90 extern Pilot** pilot_stack; /**< @todo remove */
91 extern int pilot_nstack; /**< @todo remove */
92 
93 
94 extern int land_wid; /**< From land.c */
95 
96 
97 /**
98  * GUI Lua stuff.
99  */
100 static nlua_env gui_env = LUA_NOREF; /**< Current GUI Lua environment. */
101 static int gui_L_mclick = 0; /**< Use mouse click callback. */
102 static int gui_L_mmove = 0; /**< Use mouse movement callback. */
103 
104 
105 /**
106  * Cropping.
107  */
108 static double gui_viewport_x = 0.; /**< GUI Viewport X offset. */
109 static double gui_viewport_y = 0.; /**< GUI Viewport Y offset. */
110 static double gui_viewport_w = 0.; /**< GUI Viewport width. */
111 static double gui_viewport_h = 0.; /**< GUI Viewport height. */
112 
113 
114 /**
115  * @struct Radar
116  *
117  * @brief Represents the player's radar.
118  */
119 typedef struct Radar_ {
120    double w; /**< Width. */
121    double h; /**< Height. */
122    double x; /**< X position. */
123    double y; /**< Y position. */
124    RadarShape shape; /**< Shape */
125    double res; /**< Resolution */
126    glTexture *interference[INTERFERENCE_LAYERS]; /**< Interference texture. */
127 } Radar;
128 /* radar resolutions */
129 #define RADAR_RES_MAX      100. /**< Maximum radar resolution. */
130 #define RADAR_RES_MIN      10. /**< Minimum radar resolution. */
131 #define RADAR_RES_INTERVAL 10. /**< Steps used to increase/decrease resolution. */
132 #define RADAR_RES_DEFAULT  50. /**< Default resolution. */
133 static Radar gui_radar;
134 
135 /* needed to render properly */
136 static double gui_xoff = 0.; /**< X Offset that GUI introduces. */
137 static double gui_yoff = 0.; /**< Y offset that GUI introduces. */
138 
139 /* messages */
140 #define MESG_SIZE_MAX        256 /**< Maxmimu message length. */
141 static int mesg_max        = 128; /**< Maximum messages onscreen */
142 static int mesg_pointer    = 0; /**< Current pointer message is at (for when scrolling. */
143 static int mesg_viewpoint  = -1; /**< Position of viewing. */
144 static double mesg_timeout = 15.; /**< Timeout length. */
145 static double mesg_fade    = 5.; /**< Fade length. */
146 /**
147  * @struct Mesg
148  *
149  * @brief On screen player message.
150  */
151 typedef struct Mesg_ {
152    char str[MESG_SIZE_MAX]; /**< The message. */
153    double t; /**< Time to live for the message. */
154    glFontRestore restore; /**< Hack for font restoration. */
155 } Mesg;
156 static Mesg* mesg_stack = NULL; /**< Stack of messages, will be of mesg_max size. */
157 static int gui_mesg_w   = 0; /**< Width of messages. */
158 static int gui_mesg_x   = 0; /**< X positioning of messages. */
159 static int gui_mesg_y   = 0; /**< Y positioning of messages. */
160 
161 /* Calculations to speed up borders. */
162 static double gui_tr = 0.; /**< Border top-right. */
163 static double gui_br = 0.; /**< Border bottom-right. */
164 static double gui_tl = 0.; /**< Border top-left. */
165 static double gui_bl = 0.; /**< Border bottom-left. */
166 
167 /* Intrinsic graphical stuff. */
168 static glTexture *gui_ico_hail      = NULL; /**< Hailing icon. */
169 static glTexture *gui_target_planet = NULL; /**< Planet targeting icon. */
170 static glTexture *gui_target_pilot  = NULL; /**< Pilot targeting icon. */
171 
172 
173 /*
174  * prototypes
175  */
176 /*
177  * external
178  */
179 extern void weapon_minimap( const double res, const double w, const double h,
180       const RadarShape shape, double alpha ); /**< from weapon.c */
181 /*
182  * internal
183  */
184 /* gui */
185 static void gui_createInterference( Radar *radar );
186 static void gui_borderIntersection( double *cx, double *cy, double rx, double ry, double hw, double hh );
187 /* Render GUI. */
188 static void gui_renderPilotTarget( double dt );
189 static void gui_renderPlanetTarget( double dt );
190 static void gui_renderBorder( double dt );
191 static void gui_renderMessages( double dt );
192 static const glColour *gui_getPlanetColour( int i );
193 static void gui_renderRadarOutOfRange( RadarShape sh, int w, int h, int cx, int cy, const glColour *col );
194 static void gui_planetBlink( int w, int h, int rc, int cx, int cy, GLfloat vr, RadarShape shape );
195 static const glColour* gui_getPilotColour( const Pilot* p );
196 static void gui_renderInterference (void);
197 static void gui_calcBorders (void);
198 /* Lua GUI. */
199 static int gui_doFunc( const char* func );
200 static int gui_prepFunc( const char* func );
201 static int gui_runFunc( const char* func, int nargs, int nret );
202 
203 
204 
205 /**
206  * Sets the GUI to defaults.
207  */
gui_setDefaults(void)208 void gui_setDefaults (void)
209 {
210    gui_radar.res = RADAR_RES_DEFAULT;
211    memset( mesg_stack, 0, sizeof(Mesg)*mesg_max );
212 }
213 
214 
215 /**
216  * @brief Initializes the message system.
217  *
218  *    @param x X position to set at.
219  *    @param y Y position to set at.
220  */
gui_messageInit(int width,int x,int y)221 void gui_messageInit( int width, int x, int y )
222 {
223    gui_mesg_w = width;
224    gui_mesg_x = x;
225    gui_mesg_y = y;
226 }
227 
228 
229 /**
230  * @brief Scrolls up the message box.
231  *
232  *    @param lines Number of lines to scroll up.
233  */
gui_messageScrollUp(int lines)234 void gui_messageScrollUp( int lines )
235 {
236    int o;
237 
238    /* Handle hacks. */
239    if (mesg_viewpoint == -1) {
240       mesg_viewpoint = mesg_pointer;
241       return;
242    }
243 
244    /* Get offset. */
245    o  = mesg_pointer - mesg_viewpoint;
246    if (o < 0)
247       o += mesg_max;
248    o = mesg_max - 2*conf.mesg_visible - o;
249 
250 
251    /* Calculate max line movement. */
252    if (lines > o)
253       lines = o;
254 
255    /* Move viewpoint. */
256    mesg_viewpoint = (mesg_viewpoint - lines) % mesg_max;
257 }
258 
259 
260 /**
261  * @brief Scrolls up the message box.
262  *
263  *    @param lines Number of lines to scroll up.
264  */
gui_messageScrollDown(int lines)265 void gui_messageScrollDown( int lines )
266 {
267    int o;
268 
269    /* Handle hacks. */
270    if (mesg_viewpoint == mesg_pointer) {
271       mesg_viewpoint = -1;
272       return;
273    }
274    else if (mesg_viewpoint == -1)
275       return;
276 
277    /* Get offset. */
278    o  = mesg_pointer - mesg_viewpoint;
279    if (o < 0)
280       o += mesg_max;
281 
282    /* Calculate max line movement. */
283    if (lines > o)
284       lines = o;
285 
286    /* Move viewpoint. */
287    mesg_viewpoint = (mesg_viewpoint + lines) % mesg_max;
288 }
289 
290 
291 /**
292  * @brief Toggles if player should receive messages.
293  *
294  *    @param enable Whether or not to enable player receiving messages.
295  */
player_messageToggle(int enable)296 void player_messageToggle( int enable )
297 {
298    gui_getMessage = enable;
299 }
300 
301 
302 /**
303  * @brief Adds a mesg to the queue to be displayed on screen.
304  *
305  *    @param str Message to add.
306  */
player_messageRaw(const char * str)307 void player_messageRaw( const char *str )
308 {
309    int i, p, l;
310 
311    /* Must be receiving messages. */
312    if (!gui_getMessage)
313       return;
314 
315    /* Must be non-null. */
316    if (str == NULL)
317       return;
318 
319    /* Get length. */
320    l = strlen(str);
321    i = gl_printWidthForText( NULL, str, gui_mesg_w - ((str[0] == '\t') ? 45. : 15.) );
322    p = 0;
323    while (p < l) {
324       /* Move pointer. */
325       mesg_pointer   = (mesg_pointer + 1) % mesg_max;
326       if (mesg_viewpoint != -1)
327          mesg_viewpoint++;
328 
329       /* Buffer overrun safety. */
330       if (i > MESG_SIZE_MAX-1)
331          i = MESG_SIZE_MAX-1;
332 
333       /* Add the new one */
334       if (p == 0) {
335          nsnprintf( mesg_stack[mesg_pointer].str, i+1, "%s", &str[p] );
336          gl_printRestoreInit( &mesg_stack[mesg_pointer].restore );
337       }
338       else {
339          mesg_stack[mesg_pointer].str[0] = '\t'; /* Hack to indent. */
340          nsnprintf( &mesg_stack[mesg_pointer].str[1], i+1, "%s", &str[p] );
341          gl_printStoreMax( &mesg_stack[mesg_pointer].restore, str, p );
342       }
343       mesg_stack[mesg_pointer].t = mesg_timeout;
344 
345       /* Get length. */
346       p += i;
347       if ((str[p] == '\n') || (str[p] == ' '))
348          p++; /* Skip "empty char". */
349       i  = gl_printWidthForText( NULL, &str[p], gui_mesg_w - 45. ); /* They're tabbed so it's shorter. */
350    }
351 }
352 
353 /**
354  * @brief Adds a mesg to the queue to be displayed on screen.
355  *
356  *    @param fmt String with formatting like printf.
357  */
player_message(const char * fmt,...)358 void player_message( const char *fmt, ... )
359 {
360    va_list ap;
361    char buf[1024];
362 
363    /* Must be receiving messages. */
364    if (!gui_getMessage)
365       return;
366 
367    /* Must be non-null. */
368    if (fmt == NULL)
369       return;
370 
371    /* Add the new one */
372    va_start(ap, fmt);
373    vsnprintf( buf, sizeof(buf), fmt, ap );
374    va_end(ap);
375 
376    /* Add the message. */
377    player_messageRaw( buf );
378 }
379 
380 
381 /**
382  * @brief Sets up rendering of planet and jump point targeting reticles.
383  *
384  *    @param dt Current delta tick.
385  */
gui_renderPlanetTarget(double dt)386 static void gui_renderPlanetTarget( double dt )
387 {
388    (void) dt;
389    double x,y, w,h;
390    const glColour *c;
391    Planet *planet;
392    JumpPoint *jp;
393 
394    /* no need to draw if pilot is dead */
395    if (player_isFlag(PLAYER_DESTROYED) || player_isFlag(PLAYER_CREATING) ||
396       (player.p == NULL) || pilot_isFlag(player.p,PILOT_DEAD))
397       return;
398 
399    /* Make sure target exists. */
400    if ((player.p->nav_planet < 0) && (player.p->nav_hyperspace < 0))
401       return;
402 
403    /* Make sure targets are still in range. */
404 #if 0
405    if (!pilot_inRangePlanet( player.p, player.p->nav_planet )) {
406       player_targetPlanetSet( -1 );
407       return;
408    }
409 #endif
410 
411    /* Draw planet and jump point target graphics. */
412    if (player.p->nav_hyperspace >= 0) {
413       jp = &cur_system->jumps[player.p->nav_hyperspace];
414 
415       c = &cGreen;
416 
417       x = jp->pos.x - jumppoint_gfx->sw/2.;
418       y = jp->pos.y + jumppoint_gfx->sh/2.;
419       w = jumppoint_gfx->sw;
420       h = jumppoint_gfx->sh;
421       gui_renderTargetReticles( x, y, w, h, c );
422    }
423    if (player.p->nav_planet >= 0) {
424       planet = cur_system->planets[player.p->nav_planet];
425       c = planet_getColour( planet );
426 
427       x = planet->pos.x - planet->gfx_space->w / 2.;
428       y = planet->pos.y + planet->gfx_space->h / 2.;
429       w = planet->gfx_space->w;
430       h = planet->gfx_space->h;
431       gui_renderTargetReticles( x, y, w, h, c );
432    }
433 }
434 
435 
436 /**
437  * @brief Renders planet and jump point targeting reticles.
438  *
439  *    @param x X position of reticle segment.
440  *    @param y Y position of reticle segment.
441  *    @param w Width.
442  *    @param h Height.
443  *    @param c Colour.
444  */
gui_renderTargetReticles(int x,int y,int w,int h,const glColour * c)445 void gui_renderTargetReticles( int x, int y, int w, int h, const glColour* c )
446 {
447    /* Must not be NULL. */
448    if (gui_target_planet == NULL)
449       return;
450 
451    gl_blitSprite( gui_target_planet, x, y, 0, 0, c ); /* top left */
452 
453    x += w;
454    gl_blitSprite( gui_target_planet, x, y, 1, 0, c ); /* top right */
455 
456    y -= h;
457    gl_blitSprite( gui_target_planet, x, y, 1, 1, c ); /* bottom right */
458 
459    x -= w;
460    gl_blitSprite( gui_target_planet, x, y, 0, 1, c ); /* bottom left */
461 }
462 
463 
464 /**
465  * @brief Renders the players pilot target.
466  *
467  *    @double dt Current delta tick.
468  */
gui_renderPilotTarget(double dt)469 static void gui_renderPilotTarget( double dt )
470 {
471    (void) dt;
472    Pilot *p;
473    const glColour *c;
474    double x, y;
475 
476    /* Player is most likely dead. */
477    if (gui_target_pilot == NULL)
478       return;
479 
480    /* Get the target. */
481    if (player.p->target != PLAYER_ID)
482       p = pilot_get(player.p->target);
483    else p = NULL;
484 
485    /* Make sure pilot exists and is still alive. */
486    if ((p==NULL) || pilot_isFlag(p,PILOT_DEAD)) {
487       pilot_setTarget( player.p, player.p->id );
488       gui_setTarget();
489       return;
490    }
491 
492    /* Make sure target is still in range. */
493    if (!pilot_inRangePilot( player.p, p )) {
494       pilot_setTarget( player.p, player.p->id );
495       gui_setTarget();
496       return;
497    }
498 
499    /* Draw the pilot target. */
500    if (pilot_isDisabled(p))
501       c = &cInert;
502    else if (pilot_isFlag(p,PILOT_BRIBED))
503       c = &cNeutral;
504    else if (pilot_isHostile(p))
505       c = &cHostile;
506    else if (pilot_isFriendly(p))
507       c = &cFriend;
508    else
509       c = faction_getColour(p->faction);
510 
511    x = p->solid->pos.x - p->ship->gfx_space->sw * PILOT_SIZE_APROX/2.;
512    y = p->solid->pos.y + p->ship->gfx_space->sh * PILOT_SIZE_APROX/2.;
513    gl_blitSprite( gui_target_pilot, x, y, 0, 0, c ); /* top left */
514 
515    x += p->ship->gfx_space->sw * PILOT_SIZE_APROX;
516    gl_blitSprite( gui_target_pilot, x, y, 1, 0, c ); /* top right */
517 
518    y -= p->ship->gfx_space->sh * PILOT_SIZE_APROX;
519    gl_blitSprite( gui_target_pilot, x, y, 1, 1, c ); /* bottom right */
520 
521    x -= p->ship->gfx_space->sw * PILOT_SIZE_APROX;
522    gl_blitSprite( gui_target_pilot, x, y, 0, 1, c ); /* bottom left */
523 }
524 
525 
526 /**
527  * @brief Gets the intersection with the border.
528  *
529  * http://en.wikipedia.org/wiki/Intercept_theorem
530  *
531  *    @param[out] cx X intersection.
532  *    @param[out] cy Y intersection.
533  *    @param rx Center X position of intersection.
534  *    @param ry Center Y position of intersection.
535  *    @param hw Screen half-width.
536  *    @param hh Screen half-height.
537  */
gui_borderIntersection(double * cx,double * cy,double rx,double ry,double hw,double hh)538 static void gui_borderIntersection( double *cx, double *cy, double rx, double ry, double hw, double hh )
539 {
540    double a;
541    double w, h;
542 
543    /* Get angle. */
544    a = atan2( ry, rx );
545    if (a < 0.)
546       a += 2.*M_PI;
547 
548    /* Helpers. */
549    w = hw-7.;
550    h = hh-7.;
551 
552    /* Handle by quadrant. */
553    if ((a > gui_tr) && (a < gui_tl)) { /* Top. */
554       *cx = h * (rx/ry);
555       *cy = h;
556    }
557    else if ((a > gui_tl) && (a < gui_bl)) { /* Left. */
558       *cx = -w;
559       *cy = -w * (ry/rx);
560    }
561    else if ((a > gui_bl) && (a < gui_br)) { /* Bottom. */
562       *cx = -h * (rx/ry);
563       *cy = -h;
564    }
565    else { /* Right. */
566       *cx = w;
567       *cy = w * (ry/rx);
568    }
569 
570    /* Translate. */
571    *cx += hw;
572    *cy += hh;
573 }
574 
575 
576 /**
577  * @brief Renders the ships/planets in the border.
578  *
579  *    @param dt Current delta tick.
580  */
gui_renderBorder(double dt)581 static void gui_renderBorder( double dt )
582 {
583    (void) dt;
584    int i, j;
585    Pilot *plt;
586    Planet *pnt;
587    JumpPoint *jp;
588    int hw, hh;
589    double rx,ry;
590    double cx,cy;
591    const glColour *col;
592    double int_a;
593    GLfloat vertex[5*2], colours[5*4];
594 
595    /* Get player position. */
596    hw    = SCREEN_W/2;
597    hh    = SCREEN_H/2;
598 
599    /* Interference. */
600    int_a = 1. - interference_alpha;
601 
602    /* Render borders to enhance contrast. */
603    gl_renderRect( 0., 0., 15., SCREEN_H, &cBlackHilight );
604    gl_renderRect( SCREEN_W - 15., 0., 15., SCREEN_H, &cBlackHilight );
605    gl_renderRect( 15., 0., SCREEN_W - 30., 15., &cBlackHilight );
606    gl_renderRect( 15., SCREEN_H - 15., SCREEN_W - 30., 15., &cBlackHilight );
607 
608    /* Draw planets. */
609    for (i=0; i<cur_system->nplanets; i++) {
610       /* Check that it's real. */
611       if(cur_system->planets[i]->real != ASSET_REAL)
612          continue;
613 
614       pnt = cur_system->planets[i];
615 
616       /* Skip if unknown. */
617       if (!planet_isKnown( pnt ))
618          continue;
619 
620       /* Check if out of range. */
621       if (!gui_onScreenAsset( &rx, &ry, NULL, pnt )) {
622 
623          /* Get border intersection. */
624          gui_borderIntersection( &cx, &cy, rx, ry, hw, hh );
625 
626          /* Set up colours. */
627          col = gui_getPlanetColour(i);
628          for (j=0; j<5; j++) {
629             colours[4*j + 0] = col->r;
630             colours[4*j + 1] = col->g;
631             colours[4*j + 2] = col->b;
632             colours[4*j + 3] = 1;
633          }
634          gl_vboSubData( gui_vbo, gui_vboColourOffset,
635                sizeof(GLfloat) * 5*4, colours );
636          /* Set up vertex. */
637          vertex[0] = cx-5.;
638          vertex[1] = cy-5.;
639          vertex[2] = cx-5.;
640          vertex[3] = cy+5.;
641          vertex[4] = cx+5.;
642          vertex[5] = cy+5.;
643          vertex[6] = cx+5.;
644          vertex[7] = cy-5.;
645          vertex[8] = cx-5.;
646          vertex[9] = cy-5.;
647          gl_vboSubData( gui_vbo, 0, sizeof(GLfloat) * 5*2, vertex );
648          /* Draw tho VBO. */
649          gl_vboActivateOffset( gui_vbo, GL_VERTEX_ARRAY, 0, 2, GL_FLOAT, 0 );
650          gl_vboActivateOffset( gui_vbo, GL_COLOR_ARRAY,
651                gui_vboColourOffset, 4, GL_FLOAT, 0 );
652          glDrawArrays( GL_LINE_STRIP, 0, 5 );
653       }
654    }
655 
656    /* Draw jump routes. */
657    for (i=0; i<cur_system->njumps; i++) {
658       jp  = &cur_system->jumps[i];
659 
660       /* Skip if unknown or exit-only. */
661       if (!jp_isKnown( jp ) || jp_isFlag( jp, JP_EXITONLY ))
662          continue;
663 
664       /* Check if out of range. */
665       if (!gui_onScreenAsset( &rx, &ry, jp, NULL )) {
666 
667          /* Get border intersection. */
668          gui_borderIntersection( &cx, &cy, rx, ry, hw, hh );
669 
670          /* Set up colours. */
671          if (i==player.p->nav_hyperspace)
672             col = &cGreen;
673          else
674             col = &cWhite;
675          for (j=0; j<4; j++) {
676             colours[4*j + 0] = col->r;
677             colours[4*j + 1] = col->g;
678             colours[4*j + 2] = col->b;
679             colours[4*j + 3] = 1;
680          }
681          gl_vboSubData( gui_vbo, gui_vboColourOffset,
682                sizeof(GLfloat) * 4*4, colours );
683          /* Set up vertex. */
684          vertex[0] = cx-5.;
685          vertex[1] = cy-5.;
686          vertex[2] = cx+5.;
687          vertex[3] = cy-5.;
688          vertex[4] = cx;
689          vertex[5] = cy+5.;
690          vertex[6] = cx-5.;
691          vertex[7] = cy-5.;
692          gl_vboSubData( gui_vbo, 0, sizeof(GLfloat) * 4*2, vertex );
693          /* Draw tho VBO. */
694          gl_vboActivateOffset( gui_vbo, GL_VERTEX_ARRAY, 0, 2, GL_FLOAT, 0 );
695          gl_vboActivateOffset( gui_vbo, GL_COLOR_ARRAY,
696                gui_vboColourOffset, 4, GL_FLOAT, 0 );
697          glDrawArrays( GL_LINE_STRIP, 0, 4 );
698       }
699    }
700 
701    /* Draw pilots. */
702    for (i=1; i<pilot_nstack; i++) { /* skip the player */
703       plt = pilot_stack[i];
704 
705       /* See if in sensor range. */
706       if (!pilot_inRangePilot(player.p, plt))
707          continue;
708 
709       /* Check if out of range. */
710       if (!gui_onScreenPilot( &rx, &ry, plt )) {
711 
712          /* Get border intersection. */
713          gui_borderIntersection( &cx, &cy, rx, ry, hw, hh );
714 
715          /* Set up colours. */
716          col = gui_getPilotColour(plt);
717          for (j=0; j<4; j++) {
718             colours[4*j + 0] = col->r;
719             colours[4*j + 1] = col->g;
720             colours[4*j + 2] = col->b;
721             colours[4*j + 3] = int_a;
722          }
723          gl_vboSubData( gui_vbo, gui_vboColourOffset,
724                sizeof(GLfloat) * 4*4, colours );
725          /* Set up vertex. */
726          vertex[0] = cx-5.;
727          vertex[1] = cy-5.;
728          vertex[2] = cx+5.;
729          vertex[3] = cy+5.;
730          vertex[4] = cx+5.;
731          vertex[5] = cy-5.;
732          vertex[6] = cx-5.;
733          vertex[7] = cy+5.;
734          gl_vboSubData( gui_vbo, 0, sizeof(GLfloat) * 4*2, vertex );
735          /* Draw tho VBO. */
736          gl_vboActivateOffset( gui_vbo, GL_VERTEX_ARRAY, 0, 2, GL_FLOAT, 0 );
737          gl_vboActivateOffset( gui_vbo, GL_COLOR_ARRAY,
738                gui_vboColourOffset, 4, GL_FLOAT, 0 );
739          glDrawArrays( GL_LINES, 0, 4 );
740       }
741    }
742 
743    /* Deactivate the VBO. */
744    gl_vboDeactivate();
745 }
746 
747 
748 /**
749  * @brief Takes a pilot and returns whether it's on screen, plus its relative position.
750  *
751  * @param[out] rx Relative X position (factoring in viewport offset)
752  * @param[out] ry Relative Y position (factoring in viewport offset)
753  * @param pilot Pilot to determine the visibility and position of
754  * @return Whether or not the pilot is on-screen.
755  */
gui_onScreenPilot(double * rx,double * ry,Pilot * pilot)756 int gui_onScreenPilot( double *rx, double *ry, Pilot *pilot )
757 {
758    double z;
759    int cw, ch;
760    glTexture *tex;
761 
762    z = cam_getZoom();
763 
764    tex = pilot->ship->gfx_space;
765 
766    /* Get relative positions. */
767    *rx = (pilot->solid->pos.x - player.p->solid->pos.x)*z;
768    *ry = (pilot->solid->pos.y - player.p->solid->pos.y)*z;
769 
770    /* Correct for offset. */
771    *rx -= gui_xoff;
772    *ry -= gui_yoff;
773 
774    /* Compare dimensions. */
775    cw = SCREEN_W/2 + tex->sw/2;
776    ch = SCREEN_H/2 + tex->sh/2;
777 
778    if ((ABS(*rx) > cw) || (ABS(*ry) > ch))
779       return  0;
780 
781    return 1;
782 }
783 
784 
785 /**
786  * @brief Takes a planet or jump point and returns whether it's on screen, plus its relative position.
787  *
788  * @param[out] rx Relative X position (factoring in viewport offset)
789  * @param[out] ry Relative Y position (factoring in viewport offset)
790  * @param jp Jump point to determine the visibility and position of
791  * @param pnt Planet to determine the visibility and position of
792  * @return Whether or not the given asset is on-screen.
793  */
gui_onScreenAsset(double * rx,double * ry,JumpPoint * jp,Planet * pnt)794 int gui_onScreenAsset( double *rx, double *ry, JumpPoint *jp, Planet *pnt )
795 {
796    double z;
797    int cw, ch;
798    glTexture *tex;
799 
800    z = cam_getZoom();
801 
802    if (jp == NULL) {
803       tex = pnt->gfx_space;
804       *rx = (pnt->pos.x - player.p->solid->pos.x)*z;
805       *ry = (pnt->pos.y - player.p->solid->pos.y)*z;
806    }
807    else {
808       tex = jumppoint_gfx;
809       *rx = (jp->pos.x - player.p->solid->pos.x)*z;
810       *ry = (jp->pos.y - player.p->solid->pos.y)*z;
811    }
812 
813    /* Correct for offset. */
814    *rx -= gui_xoff;
815    *ry -= gui_yoff;
816 
817    /* Compare dimensions. */
818    cw = SCREEN_W/2 + tex->sw/2;
819    ch = SCREEN_H/2 + tex->sh/2;
820 
821    if ((ABS(*rx) > cw) || (ABS(*ry) > ch))
822       return  0;
823 
824    return 1;
825 }
826 
827 
828 /**
829  * @brief Renders the gui targeting reticles.
830  *
831  * @param dt Current deltatick.
832  */
gui_renderReticles(double dt)833 void gui_renderReticles( double dt )
834 {
835    /* Player must be alive. */
836    if (player.p == NULL)
837       return;
838 
839    gui_renderPlanetTarget(dt);
840    gui_renderPilotTarget(dt);
841 }
842 
843 
844 static int can_jump = 0; /**< Stores whether or not the player is able to jump. */
845 /**
846  * @brief Renders the player's GUI.
847  *
848  *    @param dt Current delta tick.
849  */
gui_render(double dt)850 void gui_render( double dt )
851 {
852    int i;
853    double x;
854    glColour col;
855 
856    /* If player is dead just render the cinematic mode. */
857    if (!menu_isOpen(MENU_MAIN) &&
858          (player_isFlag(PLAYER_DESTROYED) || player_isFlag(PLAYER_CREATING) ||
859             ((player.p != NULL) && pilot_isFlag(player.p,PILOT_DEAD)))) {
860       spfx_cinematic();
861       return;
862    }
863 
864    /* Make sure player is valid. */
865    if (player.p == NULL)
866       return;
867 
868    /* Cinematics mode. */
869    if (player_isFlag( PLAYER_CINEMATICS_GUI ))
870       return;
871 
872    /*
873     * Countdown timers.
874     */
875    blink_pilot    -= dt / dt_mod;
876    if (blink_pilot < 0.)
877       blink_pilot += RADAR_BLINK_PILOT;
878    blink_planet   -= dt / dt_mod;
879    if (blink_planet < 0.)
880       blink_planet += RADAR_BLINK_PLANET;
881    if (interference_alpha > 0.)
882       interference_t += dt;
883 
884    /* Render the border ships and targets. */
885    gui_renderBorder(dt);
886 
887    /* Set viewport. */
888    gl_viewport( 0., 0., gl_screen.rw, gl_screen.rh );
889 
890    /* Run Lua. */
891    if (gui_env != LUA_NOREF) {
892       gui_prepFunc( "render" );
893       lua_pushnumber( naevL, dt );
894       lua_pushnumber( naevL, dt_mod );
895       gui_runFunc( "render", 2, 0 );
896       if (pilot_isFlag(player.p, PILOT_COOLDOWN)) {
897          gui_prepFunc( "render_cooldown" );
898          lua_pushnumber( naevL, player.p->ctimer / player.p->cdelay  );
899          lua_pushnumber( naevL, player.p->ctimer );
900          gui_runFunc( "render_cooldown", 2, 0 );
901       }
902    }
903 
904    /* Messages. */
905    gui_renderMessages(dt);
906 
907 
908    /* OSD. */
909    osd_render();
910 
911    /* Noise when getting near a jump. */
912    if (player.p->nav_hyperspace >= 0) { /* hyperspace target */
913 
914       /* Determine if we have to play the "enter hyperspace range" sound. */
915       i = space_canHyperspace(player.p);
916       if ((i != 0) && (i != can_jump))
917          if (!pilot_isFlag(player.p, PILOT_HYPERSPACE))
918             player_soundPlayGUI(snd_jump, 1);
919       can_jump = i;
920    }
921 
922    /* Hyperspace. */
923    if (pilot_isFlag(player.p, PILOT_HYPERSPACE) &&
924          (player.p->ptimer < HYPERSPACE_FADEOUT)) {
925       x = (HYPERSPACE_FADEOUT-player.p->ptimer) / HYPERSPACE_FADEOUT;
926       col.r = 1.;
927       col.g = 1.;
928       col.b = 1.;
929       col.a = x;
930       gl_renderRect( 0., 0., SCREEN_W, SCREEN_H, &col );
931    }
932 
933    /* Reset viewport. */
934    gl_defViewport();
935 
936    /* Render messages. */
937    omsg_render( dt );
938 }
939 
940 
941 /**
942  * @brief Initializes the radar.
943  *
944  *    @param circle Whether or not the radar is circular.
945  */
gui_radarInit(int circle,int w,int h)946 int gui_radarInit( int circle, int w, int h )
947 {
948    gui_radar.shape   = circle ? RADAR_CIRCLE : RADAR_RECT;
949    gui_radar.res     = RADAR_RES_DEFAULT;
950    gui_radar.w       = w;
951    gui_radar.h       = h;
952    gui_createInterference( &gui_radar );
953    return 0;
954 }
955 
956 
957 /**
958  * @brief Renders the GUI radar.
959  *
960  *    @param x X position to render at.
961  *    @param y Y position to render at.
962  */
gui_radarRender(double x,double y)963 void gui_radarRender( double x, double y )
964 {
965    int i, j;
966    Radar *radar;
967    AsteroidAnchor *ast;
968 
969    /* The global radar. */
970    radar = &gui_radar;
971    gui_radar.x = x;
972    gui_radar.y = y;
973 
974    gl_matrixPush();
975    if (radar->shape==RADAR_RECT) {
976       gl_clipRect( x, y, radar->w, radar->h );
977       gl_matrixTranslate( x + radar->w/2., y + radar->h/2. );
978    }
979    else if (radar->shape==RADAR_CIRCLE)
980       gl_matrixTranslate( x, y );
981 
982    /*
983     * planets
984     */
985    for (i=0; i<cur_system->nplanets; i++)
986       if ((cur_system->planets[ i ]->real == ASSET_REAL) && (i != player.p->nav_planet))
987          gui_renderPlanet( i, radar->shape, radar->w, radar->h, radar->res, 0 );
988    if (player.p->nav_planet > -1)
989       gui_renderPlanet( player.p->nav_planet, radar->shape, radar->w, radar->h, radar->res, 0 );
990 
991    /*
992     * Jump points.
993     */
994    for (i=0; i<cur_system->njumps; i++)
995       if (i != player.p->nav_hyperspace && jp_isUsable(&cur_system->jumps[i]))
996          gui_renderJumpPoint( i, radar->shape, radar->w, radar->h, radar->res, 0 );
997    if (player.p->nav_hyperspace > -1)
998       gui_renderJumpPoint( player.p->nav_hyperspace, radar->shape, radar->w, radar->h, radar->res, 0 );
999 
1000    /*
1001     * weapons
1002     */
1003    weapon_minimap( radar->res, radar->w, radar->h,
1004          radar->shape, 1.-interference_alpha );
1005 
1006 
1007    /* render the pilot_nstack */
1008    j = 0;
1009    for (i=1; i<pilot_nstack; i++) { /* skip the player */
1010       if (pilot_stack[i]->id == player.p->target)
1011          j = i;
1012       else
1013          gui_renderPilot( pilot_stack[i], radar->shape, radar->w, radar->h, radar->res, 0 );
1014    }
1015    /* render the targeted pilot */
1016    if (j!=0)
1017       gui_renderPilot( pilot_stack[j], radar->shape, radar->w, radar->h, radar->res, 0 );
1018 
1019    /* render the asteroids */
1020    for (i=0; i<cur_system->nasteroids; i++) {
1021       ast = &cur_system->asteroids[i];
1022       for (j=0; j<ast->nb; j++)
1023          gui_renderAsteroid( &ast->asteroids[j], radar->w, radar->h, radar->res, 0 );
1024    }
1025 
1026    /* Interference. */
1027    gui_renderInterference();
1028 
1029    /* Render the player cross. */
1030    gui_renderPlayer( radar->res, 0 );
1031 
1032    gl_matrixPop();
1033    if (radar->shape==RADAR_RECT)
1034       gl_unclipRect();
1035 }
1036 
1037 
1038 /**
1039  * @brief Gets the radar's position.
1040  *
1041  *    @param[out] x X position.
1042  *    @param[out] y Y position.
1043  */
gui_radarGetPos(int * x,int * y)1044 void gui_radarGetPos( int *x, int *y )
1045 {
1046    *x = gui_radar.x;
1047    *y = gui_radar.y;
1048 }
1049 
1050 
1051 /**
1052  * @brief Gets the radar's dimensions.
1053  *
1054  *    @param[out] w Width.
1055  *    @param[out] h Height.
1056  */
gui_radarGetDim(int * w,int * h)1057 void gui_radarGetDim( int *w, int *h )
1058 {
1059    *w = gui_radar.w;
1060    *h = gui_radar.h;
1061 }
1062 
1063 
1064 /**
1065  * @brief Outputs the radar's resolution.
1066  *
1067  *    @param[out] res Current zoom ratio.
1068  */
gui_radarGetRes(int * res)1069 void gui_radarGetRes( int *res )
1070 {
1071    *res = gui_radar.res;
1072 }
1073 
1074 
1075 /**
1076  * @brief Clears the GUI messages.
1077  */
gui_clearMessages(void)1078 void gui_clearMessages (void)
1079 {
1080    memset( mesg_stack, 0, sizeof(Mesg)*mesg_max );
1081 }
1082 
1083 
1084 /**
1085  * @brief Renders the player's messages on screen.
1086  *
1087  *    @param dt Current delta tick.
1088  */
gui_renderMessages(double dt)1089 static void gui_renderMessages( double dt )
1090 {
1091    double x, y, h, hs, vx, vy;
1092    int v, i, m, o;
1093    glColour c;
1094 
1095    /* Coordinate translation. */
1096    x = gui_mesg_x;
1097    y = gui_mesg_y;
1098 
1099    /* Handle viewpoint hacks. */
1100    v = mesg_viewpoint;
1101    if (v == -1)
1102       v = mesg_pointer;
1103 
1104    /* Colour. */
1105    c.r = 1.;
1106    c.g = 1.;
1107    c.b = 1.;
1108 
1109    /* Render background. */
1110    h  = conf.mesg_visible*gl_defFont.h*1.2;
1111    gl_renderRect( x-2., y-2., gui_mesg_w-13., h+4., &cBlackHilight );
1112 
1113    /* Set up position. */
1114    vx = x;
1115    vy = y;
1116 
1117    /* Must be run here. */
1118    if (mesg_viewpoint != -1) {
1119       /* Data. */
1120       hs = h*(double)conf.mesg_visible/(double)mesg_max;
1121       o  = mesg_pointer - mesg_viewpoint;
1122       if (o < 0)
1123          o += mesg_max;
1124    }
1125 
1126    /* Render text. */
1127    for (i=0; i<conf.mesg_visible; i++) {
1128       /* Reference translation. */
1129       m  = (v - i) % mesg_max;
1130       if (m < 0)
1131          m += mesg_max;
1132 
1133       /* Timer handling. */
1134       if ((mesg_viewpoint != -1) || (mesg_stack[m].t >= 0.)) {
1135          /* Decrement timer. */
1136          if (mesg_viewpoint == -1) {
1137             mesg_stack[m].t -= dt / dt_mod;
1138 
1139             /* Handle fading out. */
1140             if (mesg_stack[m].t - mesg_fade < 0.)
1141                c.a = mesg_stack[m].t / mesg_fade;
1142             else
1143                c.a = 1.;
1144          }
1145          else
1146             c.a = 1.;
1147 
1148          /* Only handle non-NULL messages. */
1149          if (mesg_stack[m].str[0] != '\0') {
1150             if (mesg_stack[m].str[0] == '\t') {
1151                gl_printRestore( &mesg_stack[m].restore );
1152                gl_printMaxRaw( NULL, gui_mesg_w - 45., x + 30, y, &c, &mesg_stack[m].str[1] );
1153             }
1154             else
1155                gl_printMaxRaw( NULL, gui_mesg_w - 15., x, y, &c, mesg_stack[m].str );
1156          }
1157       }
1158 
1159       /* Increase position. */
1160       y += (double)gl_defFont.h*1.2;
1161    }
1162 
1163    /* Render position. */
1164    if (mesg_viewpoint != -1) {
1165       /* Border. */
1166       c.a = 0.2;
1167       gl_renderRect( vx + gui_mesg_w-10., vy, 10, h, &c );
1168 
1169       /* Inside. */
1170       c.a = 0.5;
1171       gl_renderRect( vx + gui_mesg_w-10., vy + hs/2. + (h-hs)*((double)o/(double)(mesg_max-conf.mesg_visible)), 10, hs, &c );
1172    }
1173 }
1174 
1175 
1176 /**
1177  * @brief Renders interference if needed.
1178  */
gui_renderInterference(void)1179 static void gui_renderInterference (void)
1180 {
1181    glColour c;
1182    glTexture *tex;
1183    int t;
1184 
1185    /* Must be displaying interference. */
1186    if (interference_alpha <= 0.)
1187       return;
1188 
1189    /* Calculate frame to draw. */
1190    if (interference_t > INTERFERENCE_CHANGE_DT) { /* Time to change */
1191       t = RNG(0, INTERFERENCE_LAYERS-1);
1192       if (t != interference_layer)
1193          interference_layer = t;
1194       else
1195          interference_layer = (interference_layer == INTERFERENCE_LAYERS-1) ?
1196                0 : interference_layer+1;
1197       interference_t -= INTERFERENCE_CHANGE_DT;
1198    }
1199 
1200    /* Render the interference. */
1201    c.r = c.g = c.b = 1.;
1202    c.a = interference_alpha;
1203    tex = gui_radar.interference[interference_layer];
1204    if (gui_radar.shape == RADAR_CIRCLE)
1205       gl_blitStatic( tex, -gui_radar.w, -gui_radar.w, &c );
1206    else if (gui_radar.shape == RADAR_RECT)
1207       gl_blitStatic( tex, -gui_radar.w, -gui_radar.h, &c );
1208 }
1209 
1210 
1211 /**
1212  * @brief Gets a pilot's colour, with a special colour for targets.
1213  *
1214  *    @param p Pilot to get colour of.
1215  *    @return The colour of the pilot.
1216  *
1217  * @sa pilot_getColour
1218  */
gui_getPilotColour(const Pilot * p)1219 static const glColour* gui_getPilotColour( const Pilot* p )
1220 {
1221    const glColour *col;
1222 
1223    if (p->id == player.p->target)
1224       col = &cRadar_tPilot;
1225    else
1226       col = pilot_getColour(p);
1227 
1228    return col;
1229 }
1230 
1231 
1232 /**
1233  * @brief Renders a pilot in the GUI radar.
1234  *
1235  *    @param p Pilot to render.
1236  */
1237 #define CHECK_PIXEL(x,y)   \
1238 (shape==RADAR_RECT && ABS(x)<w/2. && ABS(y)<h/2.) || \
1239    (shape==RADAR_CIRCLE && (((x)*(x)+(y)*(y)) < rc))
gui_renderPilot(const Pilot * p,RadarShape shape,double w,double h,double res,int overlay)1240 void gui_renderPilot( const Pilot* p, RadarShape shape, double w, double h, double res, int overlay )
1241 {
1242    int i, curs;
1243    int x, y, sx, sy;
1244    double px, py;
1245    const glColour *col;
1246    glColour ccol;
1247    GLfloat vertex[2*8], colours[4*8];
1248    GLfloat cx, cy;
1249    int rc;
1250 
1251    /* Make sure is in range. */
1252    if (!pilot_inRangePilot( player.p, p ))
1253       return;
1254 
1255    /* Get position. */
1256    if (overlay) {
1257       x = (int)(p->solid->pos.x / res);
1258       y = (int)(p->solid->pos.y / res);
1259    }
1260    else {
1261       x = (int)((p->solid->pos.x - player.p->solid->pos.x) / res);
1262       y = (int)((p->solid->pos.y - player.p->solid->pos.y) / res);
1263    }
1264    /* Get size. */
1265    sx = (int)(PILOT_SIZE_APROX/2. * p->ship->gfx_space->sw / res);
1266    sy = (int)(PILOT_SIZE_APROX/2. * p->ship->gfx_space->sh / res);
1267    if (sx < 1.)
1268       sx = 1.;
1269    if (sy < 1.)
1270       sy = 1.;
1271 
1272    /* Check if pilot in range. */
1273    if ( ((shape==RADAR_RECT) &&
1274             ((ABS(x) > w/2+sx) || (ABS(y) > h/2.+sy)) ) ||
1275          ((shape==RADAR_CIRCLE) &&
1276             ((x*x+y*y) > (int)(w*w))) ) {
1277 
1278       /* Draw little targeted symbol. */
1279       if (p->id == player.p->target && !overlay)
1280          gui_renderRadarOutOfRange( shape, w, h, x, y, &cRadar_tPilot );
1281       return;
1282    }
1283 
1284    /* Transform coordinates into the 0,0 -> SCREEN_W, SCREEN_H range. */
1285    if (overlay) {
1286       x += SCREEN_W / 2;
1287       y += SCREEN_H / 2;
1288       w *= 2.;
1289       h *= 2.;
1290    }
1291 
1292    if (shape==RADAR_RECT)
1293       rc = 0;
1294    else if (shape==RADAR_CIRCLE)
1295       rc = (int)(w*w);
1296    else
1297       return;
1298 
1299    /* Draw selection if targeted. */
1300    if (p->id == player.p->target) {
1301       if (blink_pilot < RADAR_BLINK_PILOT/2.) {
1302          /* Set up colours. */
1303          for (i=0; i<8; i++) {
1304             colours[4*i + 0] = cRadar_tPilot.r;
1305             colours[4*i + 1] = cRadar_tPilot.g;
1306             colours[4*i + 2] = cRadar_tPilot.b;
1307             colours[4*i + 3] = 1.-interference_alpha;
1308          }
1309          gl_vboSubData( gui_vbo, gui_vboColourOffset,
1310                sizeof(GLfloat) * 8*4, colours );
1311          /* Set up vertex. */
1312          curs = 0;
1313          cx = x-sx;
1314          cy = y+sy;
1315          if (CHECK_PIXEL(cx-3.3,cy+3.3)) {
1316             vertex[curs++] = cx-1.5;
1317             vertex[curs++] = cy+1.5;
1318             vertex[curs++] = cx-3.3;
1319             vertex[curs++] = cy+3.3;
1320          }
1321          cx = x+sx;
1322          if (CHECK_PIXEL(cx+3.3,cy+3.3)) {
1323             vertex[curs++] = cx+1.5;
1324             vertex[curs++] = cy+1.5;
1325             vertex[curs++] = cx+3.3;
1326             vertex[curs++] = cy+3.3;
1327          }
1328          cy = y-sy;
1329          if (CHECK_PIXEL(cx+3.3,cy-3.3)) {
1330             vertex[curs++] = cx+1.5;
1331             vertex[curs++] = cy-1.5;
1332             vertex[curs++] = cx+3.3;
1333             vertex[curs++] = cy-3.3;
1334          }
1335          cx = x-sx;
1336          if (CHECK_PIXEL(cx-3.3,cy-3.3)) {
1337             vertex[curs++] = cx-1.5;
1338             vertex[curs++] = cy-1.5;
1339             vertex[curs++] = cx-3.3;
1340             vertex[curs++] = cy-3.3;
1341          }
1342          gl_vboSubData( gui_vbo, 0, sizeof(GLfloat) * (curs/2)*2, vertex );
1343          /* Draw tho VBO. */
1344          gl_vboActivateOffset( gui_vbo, GL_VERTEX_ARRAY, 0, 2, GL_FLOAT, 0 );
1345          gl_vboActivateOffset( gui_vbo, GL_COLOR_ARRAY,
1346                gui_vboColourOffset, 4, GL_FLOAT, 0 );
1347          glDrawArrays( GL_LINES, 0, curs/2 );
1348       }
1349    }
1350 
1351    /* Deactivate VBO. */
1352    gl_vboDeactivate();
1353 
1354    /* Draw square. */
1355    px     = MAX(x-sx,-w);
1356    py     = MAX(y-sy, -h);
1357    if (pilot_isFlag(p, PILOT_HILIGHT) && (blink_pilot < RADAR_BLINK_PILOT/2.))
1358       col = &cRadar_hilight;
1359    else
1360       col = gui_getPilotColour(p);
1361    ccol.r = col->r;
1362    ccol.g = col->g;
1363    ccol.b = col->b;
1364    ccol.a = 1.-interference_alpha;
1365    gl_renderRect( px, py, MIN( 2*sx, w-px ), MIN( 2*sy, h-py ), &ccol );
1366 
1367    /* Draw name. */
1368    if (overlay && pilot_isFlag(p, PILOT_HILIGHT))
1369       gl_printRaw( &gl_smallFont, x+2*sx+5., y-gl_smallFont.h/2., col, p->name );
1370 }
1371 
1372 
1373 /**
1374  * @brief Renders an asteroid in the GUI radar.
1375  *
1376  *    @param a Asteroid to render.
1377  */
gui_renderAsteroid(const Asteroid * a,double w,double h,double res,int overlay)1378 void gui_renderAsteroid( const Asteroid* a, double w, double h, double res, int overlay )
1379 {
1380    int x, y, sx, sy;
1381    double px, py;
1382    const glColour *col;
1383    glColour ccol;
1384 
1385    /* Make sure is in range. TODO: real detection system for asteroids */
1386    if ( MOD( a->pos.x - player.p->solid->pos.x,
1387              a->pos.y - player.p->solid->pos.y ) > 4000. )
1388       return;
1389 
1390    /* Get position. */
1391    if (overlay) {
1392       x = (int)(a->pos.x / res);
1393       y = (int)(a->pos.y / res);
1394    }
1395    else {
1396       x = (int)((a->pos.x - player.p->solid->pos.x) / res);
1397       y = (int)((a->pos.y - player.p->solid->pos.y) / res);
1398    }
1399    /* Get size. */
1400    sx = 1.;
1401    sy = 1.;
1402 
1403    /* Transform coordinates into the 0,0 -> SCREEN_W, SCREEN_H range. */
1404    if (overlay) {
1405       x += SCREEN_W / 2;
1406       y += SCREEN_H / 2;
1407       w *= 2.;
1408       h *= 2.;
1409    }
1410 
1411    /* Deactivate VBO. */
1412    gl_vboDeactivate();
1413 
1414    /* Draw square. */
1415    px     = MAX(x-sx,-w);
1416    py     = MAX(y-sy, -h);
1417 
1418    col = &cWhite;
1419    ccol.r = col->r;
1420    ccol.g = col->g;
1421    ccol.b = col->b;
1422    ccol.a = 1.-interference_alpha;
1423    gl_renderRect( px, py, MIN( 2*sx, w-px ), MIN( 2*sy, h-py ), &ccol );
1424 }
1425 
1426 
1427 /**
1428  * @brief Renders the player cross on the radar or whatever.
1429  */
gui_renderPlayer(double res,int overlay)1430 void gui_renderPlayer( double res, int overlay )
1431 {
1432    double x, y, r;
1433 
1434    if (overlay) {
1435       x = player.p->solid->pos.x / res + SCREEN_W / 2;
1436       y = player.p->solid->pos.y / res + SCREEN_H / 2;
1437       r = 5.;
1438    }
1439    else {
1440       x = 0.;
1441       y = 0.;
1442       r = 3.;
1443    }
1444 
1445    /* Render the cross. */
1446    gl_renderCross( x, y, r, &cRadar_player );
1447 
1448    if (overlay)
1449       gl_printRaw( &gl_smallFont, x+r+5., y-gl_smallFont.h/2., &cRadar_player, "You" );
1450 }
1451 
1452 
1453 /**
1454  * @brief Gets the colour of a planet.
1455  *
1456  *    @param i Index of the planet to get colour of.
1457  *    @return Colour of the planet.
1458  */
gui_getPlanetColour(int i)1459 static const glColour *gui_getPlanetColour( int i )
1460 {
1461    const glColour *col;
1462    Planet *planet;
1463 
1464    planet = cur_system->planets[i];
1465 
1466    if (i == player.p->nav_planet)
1467       col = &cRadar_tPlanet;
1468    else
1469       col = planet_getColour( planet );
1470 
1471    return col;
1472 }
1473 
1474 
1475 /**
1476  * @brief Force sets the planet and pilot radar blink.
1477  */
gui_forceBlink(void)1478 void gui_forceBlink (void)
1479 {
1480    blink_pilot  = 0.;
1481    blink_planet = 0.;
1482 }
1483 
1484 
1485 /**
1486  * @brief Renders the planet blink around a position on the minimap.
1487  */
gui_planetBlink(int w,int h,int rc,int cx,int cy,GLfloat vr,RadarShape shape)1488 static void gui_planetBlink( int w, int h, int rc, int cx, int cy, GLfloat vr, RadarShape shape )
1489 {
1490    GLfloat vx, vy;
1491    GLfloat vertex[8*2], colours[8*4];
1492    int i, curs;
1493 
1494    if (blink_planet < RADAR_BLINK_PLANET/2.) {
1495       curs = 0;
1496       vx = cx-vr;
1497       vy = cy+vr;
1498       if (CHECK_PIXEL(vx-3.3, vy+3.3)) {
1499          vertex[curs++] = vx-1.5;
1500          vertex[curs++] = vy+1.5;
1501          vertex[curs++] = vx-3.3;
1502          vertex[curs++] = vy+3.3;
1503       }
1504       vx = cx+vr;
1505       if (CHECK_PIXEL(vx+3.3, vy+3.3)) {
1506          vertex[curs++] = vx+1.5;
1507          vertex[curs++] = vy+1.5;
1508          vertex[curs++] = vx+3.3;
1509          vertex[curs++] = vy+3.3;
1510       }
1511       vy = cy-vr;
1512       if (CHECK_PIXEL(vx+3.3, vy-3.3)) {
1513          vertex[curs++] = vx+1.5;
1514          vertex[curs++] = vy-1.5;
1515          vertex[curs++] = vx+3.3;
1516          vertex[curs++] = vy-3.3;
1517       }
1518       vx = cx-vr;
1519       if (CHECK_PIXEL(vx-3.3, vy-3.3)) {
1520          vertex[curs++] = vx-1.5;
1521          vertex[curs++] = vy-1.5;
1522          vertex[curs++] = vx-3.3;
1523          vertex[curs++] = vy-3.3;
1524       }
1525       gl_vboSubData( gui_vbo, 0, sizeof(GLfloat) * (curs/2)*2, vertex );
1526       /* Set the colours. */
1527       for (i=0; i<curs/2; i++) {
1528          colours[4*i + 0] = cRadar_tPlanet.r;
1529          colours[4*i + 1] = cRadar_tPlanet.g;
1530          colours[4*i + 2] = cRadar_tPlanet.b;
1531          colours[4*i + 3] = 1.-interference_alpha;
1532       }
1533       gl_vboSubData( gui_vbo, gui_vboColourOffset,
1534             sizeof(GLfloat) * (curs/2)*4, colours );
1535       /* Draw tho VBO. */
1536       gl_vboActivateOffset( gui_vbo, GL_VERTEX_ARRAY, 0, 2, GL_FLOAT, 0 );
1537       gl_vboActivateOffset( gui_vbo, GL_COLOR_ARRAY,
1538             gui_vboColourOffset, 4, GL_FLOAT, 0 );
1539       glDrawArrays( GL_LINES, 0, curs/2 );
1540    }
1541 
1542    /* Deactivate the VBO. */
1543    gl_vboDeactivate();
1544 }
1545 
1546 
1547 /**
1548  * @brief Renders an out of range marker for the planet.
1549  */
gui_renderRadarOutOfRange(RadarShape sh,int w,int h,int cx,int cy,const glColour * col)1550 static void gui_renderRadarOutOfRange( RadarShape sh, int w, int h, int cx, int cy, const glColour *col )
1551 {
1552    GLfloat vertex[2*2], colours[8*2];
1553    double a;
1554    int i;
1555 
1556    /* Set the colour. */
1557    for (i=0; i<2; i++) {
1558       colours[4*i + 0] = col->r;
1559       colours[4*i + 1] = col->g;
1560       colours[4*i + 2] = col->b;
1561       colours[4*i + 3] = 1.-interference_alpha;
1562    }
1563    gl_vboSubData( gui_vbo, gui_vboColourOffset,
1564          sizeof(GLfloat) * 2*4, colours );
1565 
1566    /* Draw a line like for pilots. */
1567    a = ANGLE(cx,cy);
1568    if (sh == RADAR_CIRCLE) {
1569       vertex[0] = w*cos(a);
1570       vertex[1] = w*sin(a);
1571       vertex[2] = 0.85*vertex[0];
1572       vertex[3] = 0.85*vertex[1];
1573    }
1574    else {
1575       int cxa, cya;
1576       cxa = ABS(cx);
1577       cya = ABS(cy);
1578       /* Determine position. */
1579       if (cy >= cxa) { /* Bottom */
1580          vertex[0] = w/2. * (cx*1./cy);
1581          vertex[1] = h/2.;
1582       } else if (cx >= cya) { /* Left */
1583          vertex[0] = w/2.;
1584          vertex[1] = h/2. * (cy*1./cx);
1585       } else if (cya >= cxa) { /* Top */
1586          vertex[0] = -w/2. * (cx*1./cy);
1587          vertex[1] = -h/2.;
1588       } else { /* Right */
1589          vertex[0] = -w/2.;
1590          vertex[1] = -h/2. * (cy*1./cx);
1591       }
1592       /* Calculate rest. */
1593       vertex[2] = vertex[0] - 0.15*w*cos(a);
1594       vertex[3] = vertex[1] - 0.15*w*sin(a);
1595    }
1596    /* Set the vertex. */
1597    gl_vboSubData( gui_vbo, 0, sizeof(GLfloat) * 2*2, vertex );
1598    /* Draw tho VBO. */
1599    gl_vboActivateOffset( gui_vbo, GL_VERTEX_ARRAY, 0, 2, GL_FLOAT, 0 );
1600    gl_vboActivateOffset( gui_vbo, GL_COLOR_ARRAY,
1601          gui_vboColourOffset, 4, GL_FLOAT, 0 );
1602    glDrawArrays( GL_LINES, 0, 2 );
1603 
1604    /* Deactivate the VBO. */
1605    gl_vboDeactivate();
1606 }
1607 
1608 
1609 /**
1610  * @brief Draws the planets in the minimap.
1611  *
1612  * Matrix mode is already displaced to center of the minimap.
1613  */
gui_renderPlanet(int ind,RadarShape shape,double w,double h,double res,int overlay)1614 void gui_renderPlanet( int ind, RadarShape shape, double w, double h, double res, int overlay )
1615 {
1616    int i;
1617    int x, y;
1618    int cx, cy, r, rc;
1619    GLfloat vx, vy, vr;
1620    GLfloat a;
1621    const glColour *col;
1622    Planet *planet;
1623    GLfloat vertex[5*2], colours[5*4];
1624 
1625    /* Make sure is known. */
1626    if ( !planet_isKnown( cur_system->planets[ind] ))
1627       return;
1628 
1629    /* Default values. */
1630    planet = cur_system->planets[ind];
1631    r     = (int)(planet->radius*2. / res);
1632    vr    = MAX( r, 3. ); /* Make sure it's visible. */
1633    if (overlay) {
1634       cx    = (int)(planet->pos.x / res);
1635       cy    = (int)(planet->pos.y / res);
1636    }
1637    else {
1638       cx    = (int)((planet->pos.x - player.p->solid->pos.x) / res);
1639       cy    = (int)((planet->pos.y - player.p->solid->pos.y) / res);
1640    }
1641    if (shape==RADAR_CIRCLE)
1642       rc = (int)(w*w);
1643    else
1644       rc = 0;
1645 
1646    /* Check if in range. */
1647    if (shape == RADAR_RECT) {
1648       /* Out of range. */
1649       if ((ABS(cx) - r > w/2.) || (ABS(cy) - r  > h/2.)) {
1650          if ((player.p->nav_planet == ind) && !overlay)
1651             gui_renderRadarOutOfRange( RADAR_RECT, w, h, cx, cy, &cRadar_tPlanet );
1652          return;
1653       }
1654    }
1655    else if (shape == RADAR_CIRCLE) {
1656       x = ABS(cx)-r;
1657       y = ABS(cy)-r;
1658       /* Out of range. */
1659       if (x*x + y*y > pow2(w-r)) {
1660          if ((player.p->nav_planet == ind) && !overlay)
1661             gui_renderRadarOutOfRange( RADAR_CIRCLE, w, w, cx, cy, &cRadar_tPlanet );
1662          return;
1663       }
1664    }
1665 
1666    if (overlay) {
1667       /* Transform coordinates. */
1668       cx += SCREEN_W / 2;
1669       cy += SCREEN_H / 2;
1670       w  *= 2.;
1671       h  *= 2.;
1672    }
1673 
1674    /* Do the blink. */
1675    if (ind == player.p->nav_planet)
1676       gui_planetBlink( w, h, rc, cx, cy, vr, shape );
1677 
1678    /* Get the colour. */
1679    col = gui_getPlanetColour(ind);
1680    if (overlay)
1681       a = 1.;
1682    else
1683       a   = 1.-interference_alpha;
1684    for (i=0; i<5; i++) {
1685       colours[4*i + 0] = col->r;
1686       colours[4*i + 1] = col->g;
1687       colours[4*i + 2] = col->b;
1688       colours[4*i + 3] = a;
1689    }
1690    gl_vboSubData( gui_vbo, gui_vboColourOffset,
1691       sizeof(GLfloat) * 5*4, colours );
1692    /* Now load the data. */
1693    vx = cx;
1694    vy = cy;
1695    vertex[0] = vx;
1696    vertex[1] = vy + vr;
1697    vertex[2] = vx + vr;
1698    vertex[3] = vy;
1699    vertex[4] = vx;
1700    vertex[5] = vy - vr;
1701    vertex[6] = vx - vr;
1702    vertex[7] = vy;
1703    vertex[8] = vertex[0];
1704    vertex[9] = vertex[1];
1705    gl_vboSubData( gui_vbo, 0, sizeof(GLfloat) * 5*2, vertex );
1706    /* Draw tho VBO. */
1707    gl_vboActivateOffset( gui_vbo, GL_VERTEX_ARRAY, 0, 2, GL_FLOAT, 0 );
1708    gl_vboActivateOffset( gui_vbo, GL_COLOR_ARRAY,
1709          gui_vboColourOffset, 4, GL_FLOAT, 0 );
1710    glDrawArrays( GL_LINE_STRIP, 0, 5 );
1711 
1712    /* Deactivate the VBO. */
1713    gl_vboDeactivate();
1714 
1715    /* Render name. */
1716    if (overlay)
1717       gl_printRaw( &gl_smallFont, cx+vr+5., cy, col, planet->name );
1718 }
1719 
1720 
1721 /**
1722  * @brief Renders a jump point on the minimap.
1723  *
1724  *    @param i Jump point to render.
1725  */
gui_renderJumpPoint(int ind,RadarShape shape,double w,double h,double res,int overlay)1726 void gui_renderJumpPoint( int ind, RadarShape shape, double w, double h, double res, int overlay )
1727 {
1728    int i;
1729    int cx, cy, x, y, r, rc;
1730    GLfloat a;
1731    GLfloat ca, sa;
1732    GLfloat vx, vy, vr;
1733    const glColour *col;
1734    GLfloat vertex[4*2], colours[4*4];
1735    JumpPoint *jp;
1736 
1737    /* Default values. */
1738    jp    = &cur_system->jumps[ind];
1739    r     = (int)(jumppoint_gfx->sw / res);
1740    vr    = MAX( r, 3. ); /* Make sure it's visible. */
1741    if (overlay) {
1742       cx    = (int)(jp->pos.x / res);
1743       cy    = (int)(jp->pos.y / res);
1744    }
1745    else {
1746       cx    = (int)((jp->pos.x - player.p->solid->pos.x) / res);
1747       cy    = (int)((jp->pos.y - player.p->solid->pos.y) / res);
1748    }
1749    if (shape==RADAR_CIRCLE)
1750       rc = (int)(w*w);
1751    else
1752       rc = 0;
1753 
1754    /* Check if known */
1755    if (!jp_isKnown(jp))
1756       return;
1757 
1758    /* Check if in range. */
1759    if (shape == RADAR_RECT) {
1760       /* Out of range. */
1761       if ((ABS(cx) - r > w/2.) || (ABS(cy) - r  > h/2.)) {
1762          if ((player.p->nav_hyperspace == ind) && !overlay)
1763             gui_renderRadarOutOfRange( RADAR_RECT, w, h, cx, cy, &cRadar_tPlanet );
1764          return;
1765       }
1766    }
1767    else if (shape == RADAR_CIRCLE) {
1768       x = ABS(cx)-r;
1769       y = ABS(cy)-r;
1770       /* Out of range. */
1771       if (x*x + y*y > pow2(w-r)) {
1772          if ((player.p->nav_hyperspace == ind) && !overlay)
1773             gui_renderRadarOutOfRange( RADAR_CIRCLE, w, w, cx, cy, &cRadar_tPlanet );
1774          return;
1775       }
1776    }
1777 
1778    if (overlay) {
1779       /* Transform coordinates. */
1780       cx += SCREEN_W / 2;
1781       cy += SCREEN_H / 2;
1782       w  *= 2.;
1783       h  *= 2.;
1784    }
1785 
1786    /* Do the blink. */
1787    if (ind == player.p->nav_hyperspace) {
1788       gui_planetBlink( w, h, rc, cx, cy, vr, shape );
1789       col = &cGreen;
1790    }
1791    else if (jp_isFlag(jp, JP_HIDDEN))
1792       col = &cRed;
1793    else
1794       col = &cWhite;
1795 
1796    if (overlay)
1797       a = 1.;
1798    else
1799       a = 1.-interference_alpha;
1800 
1801    /* Get the colour. */
1802    for (i=0; i<4; i++) {
1803       colours[4*i + 0] = col->r;
1804       colours[4*i + 1] = col->g;
1805       colours[4*i + 2] = col->b;
1806       colours[4*i + 3] = a;
1807    }
1808    gl_vboSubData( gui_vbo, gui_vboColourOffset,
1809       sizeof(GLfloat) * 5*4, colours );
1810    /* Now load the data. */
1811    vx = cx;
1812    vy = cy;
1813    ca = jp->cosa;
1814    sa = jp->sina;
1815    /* Must rotate around triangle center which with our calculations is shifted vr/3 to the left. */
1816    vertex[0] = vx + (4./3.*vr)*ca;
1817    vertex[1] = vy - (4./3.*vr)*sa;
1818    vertex[2] = vx - (2./3.*vr)*ca + vr*sa;
1819    vertex[3] = vy + (2./3.*vr)*sa + vr*ca;
1820    vertex[4] = vx - (2./3.*vr)*ca - vr*sa;
1821    vertex[5] = vy + (2./3.*vr)*sa - vr*ca;
1822    vertex[6] = vertex[0];
1823    vertex[7] = vertex[1];
1824    gl_vboSubData( gui_vbo, 0, sizeof(GLfloat) * 4*2, vertex );
1825    /* Draw tho VBO. */
1826    gl_vboActivateOffset( gui_vbo, GL_VERTEX_ARRAY, 0, 2, GL_FLOAT, 0 );
1827    gl_vboActivateOffset( gui_vbo, GL_COLOR_ARRAY,
1828          gui_vboColourOffset, 4, GL_FLOAT, 0 );
1829    glDrawArrays( GL_LINE_STRIP, 0, 4 );
1830 
1831    /* Deactivate the VBO. */
1832    gl_vboDeactivate();
1833 
1834    /* Render name. */
1835    if (overlay)
1836       gl_printRaw( &gl_smallFont, cx+vr+5., cy, col, sys_isKnown(jp->target) ? jp->target->name : "Unknown" );
1837 }
1838 #undef CHECK_PIXEL
1839 
1840 
1841 /**
1842  * @brief Sets the viewport.
1843  */
gui_setViewport(double x,double y,double w,double h)1844 void gui_setViewport( double x, double y, double w, double h )
1845 {
1846    gui_viewport_x = x;
1847    gui_viewport_y = y;
1848    gui_viewport_w = w;
1849    gui_viewport_h = h;
1850 
1851    /* We now set the viewport. */
1852    gl_setDefViewport( gui_viewport_x, gui_viewport_y, gui_viewport_w, gui_viewport_h );
1853    gl_defViewport();
1854 
1855    /* Run border calculations. */
1856    gui_calcBorders();
1857 
1858    /* Regenerate the Nebula stuff. */
1859    if ((cur_system != NULL) && (cur_system->nebu_density > 0.))
1860       nebu_genOverlay();
1861 }
1862 
1863 
1864 /**
1865  * @brief Resets the viewport.
1866  */
gui_clearViewport(void)1867 void gui_clearViewport (void)
1868 {
1869    gl_setDefViewport( 0., 0., gl_screen.nw, gl_screen.nh );
1870    gl_defViewport();
1871 }
1872 
1873 
1874 /**
1875  * @brief Calculates and sets the GUI borders.
1876  */
gui_calcBorders(void)1877 static void gui_calcBorders (void)
1878 {
1879    double w,h;
1880 
1881    /* Precalculations. */
1882    w  = SCREEN_W/2.;
1883    h  = SCREEN_H/2.;
1884 
1885    /*
1886     * Borders.
1887     */
1888    gui_tl = atan2( +h, -w );
1889    if (gui_tl < 0.)
1890       gui_tl += 2*M_PI;
1891    gui_tr = atan2( +h, +w );
1892    if (gui_tr < 0.)
1893       gui_tr += 2*M_PI;
1894    gui_bl = atan2( -h, -w );
1895    if (gui_bl < 0.)
1896       gui_bl += 2*M_PI;
1897    gui_br = atan2( -h, +w );
1898    if (gui_br < 0.)
1899       gui_br += 2*M_PI;
1900 }
1901 
1902 
1903 /**
1904  * @brief Initializes the GUI system.
1905  *
1906  *    @return 0 on success;
1907  */
gui_init(void)1908 int gui_init (void)
1909 {
1910    /*
1911     * radar
1912     */
1913    gui_radar.res = RADAR_RES_DEFAULT;
1914 
1915    /*
1916     * messages
1917     */
1918    gui_mesg_x = 20;
1919    gui_mesg_y = 30;
1920    gui_mesg_w = SCREEN_W - 400;
1921    if (mesg_stack == NULL) {
1922       mesg_stack = calloc(mesg_max, sizeof(Mesg));
1923       if (mesg_stack == NULL) {
1924          ERR("Out of memory!");
1925          return -1;
1926       }
1927    }
1928 
1929    /*
1930     * VBO.
1931     */
1932    if (gui_vbo == NULL) {
1933       gui_vbo = gl_vboCreateStream( sizeof(GLfloat) * 8*(2+4), NULL );
1934       gui_vboColourOffset = sizeof(GLfloat) * 8*2;
1935    }
1936 
1937    /*
1938     * OSD
1939     */
1940    osd_setup( 30., SCREEN_H-90., 150., 300. );
1941 
1942    /*
1943     * Set viewport.
1944     */
1945    gui_setViewport( 0., 0., gl_screen.w, gl_screen.h );
1946 
1947    /*
1948     * Icons.
1949     */
1950    gui_ico_hail = gl_newSprite( GUI_GFX_PATH"hail.png", 5, 2, 0 );
1951 
1952    return 0;
1953 }
1954 
1955 
1956 /**
1957  * @brief Runs a GUI Lua function.
1958  *
1959  *    @param func Name of the function to run.
1960  *    @return 0 on success.
1961  */
gui_doFunc(const char * func)1962 static int gui_doFunc( const char* func )
1963 {
1964    gui_prepFunc( func );
1965    return gui_runFunc( func, 0, 0 );
1966 }
1967 
1968 
1969 /**
1970  * @brief Prepares to run a function.
1971  */
gui_prepFunc(const char * func)1972 static int gui_prepFunc( const char* func )
1973 {
1974 #if DEBUGGING
1975    if (gui_env == LUA_NOREF) {
1976       WARN( "Trying to run GUI func '%s' but no GUI is loaded!", func );
1977       return -1;
1978    }
1979 #endif /* DEBUGGING */
1980 
1981    /* Set up function. */
1982    nlua_getenv( gui_env, func );
1983    return 0;
1984 }
1985 
1986 
1987 /**
1988  * @brief Runs a function.
1989  * @note Function must be prepared beforehand.
1990  *    @param func Name of the function to run.
1991  *    @param nargs Arguments to the function.
1992  *    @param nret Parameters to get returned from the function.
1993  */
gui_runFunc(const char * func,int nargs,int nret)1994 static int gui_runFunc( const char* func, int nargs, int nret )
1995 {
1996    int ret;
1997    const char* err;
1998 
1999    /* Run the function. */
2000    ret = nlua_pcall( gui_env, nargs, nret );
2001    if (ret != 0) { /* error has occurred */
2002       err = (lua_isstring(naevL,-1)) ? lua_tostring(naevL,-1) : NULL;
2003       WARN("GUI Lua -> '%s': %s",
2004             func, (err) ? err : "unknown error");
2005       lua_pop(naevL,2);
2006       return ret;
2007    }
2008 
2009    return ret;
2010 }
2011 
2012 
2013 /**
2014  * @brief Reloads the GUI.
2015  */
gui_reload(void)2016 void gui_reload (void)
2017 {
2018    if (gui_env == LUA_NOREF)
2019       return;
2020 
2021    gui_load( gui_pick() );
2022 }
2023 
2024 
2025 /**
2026  * @brief Player just changed their cargo.
2027  */
gui_setCargo(void)2028 void gui_setCargo (void)
2029 {
2030    if (gui_env != LUA_NOREF)
2031       gui_doFunc( "update_cargo" );
2032 }
2033 
2034 
2035 /**
2036  * @brief PlNULLayer just changed their nav computer target.
2037  */
gui_setNav(void)2038 void gui_setNav (void)
2039 {
2040    if (gui_env != LUA_NOREF)
2041       gui_doFunc( "update_nav" );
2042 }
2043 
2044 
2045 /**
2046  * @brief Player just changed their pilot target.
2047  */
gui_setTarget(void)2048 void gui_setTarget (void)
2049 {
2050    if (gui_env != LUA_NOREF)
2051       gui_doFunc( "update_target" );
2052 }
2053 
2054 
2055 /**
2056  * @brief Player just upgraded their ship or modified it.
2057  */
gui_setShip(void)2058 void gui_setShip (void)
2059 {
2060    if (gui_env != LUA_NOREF)
2061       gui_doFunc( "update_ship" );
2062 }
2063 
2064 
2065 /**
2066  * @brief Player just changed their system.
2067  */
gui_setSystem(void)2068 void gui_setSystem (void)
2069 {
2070    if (gui_env != LUA_NOREF)
2071       gui_doFunc( "update_system" );
2072 }
2073 
2074 
2075 /**
2076  * @brief Player's relationship with a faction was modified.
2077  */
gui_updateFaction(void)2078 void gui_updateFaction (void)
2079 {
2080    if (gui_env != LUA_NOREF && player.p->nav_planet != -1)
2081       gui_doFunc( "update_faction" );
2082 }
2083 
2084 
2085 /**
2086  * @brief Calls trigger functions depending on who the pilot is.
2087  *
2088  *    @param The pilot to act based upon.
2089  */
gui_setGeneric(Pilot * pilot)2090 void gui_setGeneric( Pilot* pilot )
2091 {
2092    if (gui_env == LUA_NOREF)
2093       return;
2094 
2095    if ((player.p->target != PLAYER_ID) && (pilot->id == player.p->target))
2096       gui_setTarget();
2097    else if (pilot_isPlayer(pilot)) {
2098       gui_setCargo();
2099       gui_setShip();
2100    }
2101 }
2102 
2103 
2104 /**
2105  * @brief Determines which GUI should be used.
2106  */
gui_pick(void)2107 char* gui_pick (void)
2108 {
2109    char* gui;
2110 
2111    if (player.gui && (player.guiOverride == 1 || strcmp(player.p->ship->gui,"default")==0))
2112       gui = player.gui;
2113    else
2114       gui = player.p->ship->gui;
2115    return gui;
2116 }
2117 
2118 
2119 /**
2120  * @brief Attempts to load the actual GUI.
2121  *
2122  *    @param name Name of the GUI to load.
2123  *    @return 0 on success.
2124  */
gui_load(const char * name)2125 int gui_load( const char* name )
2126 {
2127    (void) name;
2128    char *buf, path[PATH_MAX];
2129    uint32_t bufsize;
2130 
2131    /* Set defaults. */
2132    gui_cleanup();
2133 
2134    /* Open file. */
2135    nsnprintf( path, sizeof(path), "dat/gui/%s.lua", name );
2136    buf = ndata_read( path, &bufsize );
2137    if (buf == NULL) {
2138       WARN("Unable to find GUI '%s'.", path );
2139       return -1;
2140    }
2141 
2142    /* Clean up. */
2143    if (gui_env != LUA_NOREF) {
2144       nlua_freeEnv(gui_env);
2145       gui_env = LUA_NOREF;
2146    }
2147 
2148    /* Create Lua state. */
2149    gui_env = nlua_newEnv(1);
2150    if (nlua_dobufenv( gui_env, buf, bufsize, path ) != 0) {
2151       WARN("Failed to load GUI Lua: %s\n"
2152             "%s\n"
2153             "Most likely Lua file has improper syntax, please check",
2154             path, lua_tostring(naevL,-1));
2155       nlua_freeEnv( gui_env );
2156       gui_env = LUA_NOREF;
2157       free(buf);
2158       return -1;
2159    }
2160    free(buf);
2161    nlua_loadStandard( gui_env );
2162    nlua_loadGFX( gui_env );
2163    nlua_loadGUI( gui_env );
2164 
2165    /* Run create function. */
2166    if (gui_doFunc( "create" )) {
2167       nlua_freeEnv( gui_env );
2168       gui_env = LUA_NOREF;
2169    }
2170 
2171    /* Recreate land window if landed. */
2172    if (landed) {
2173       land_genWindows( 0, 1 );
2174       window_lower( land_wid );
2175    }
2176 
2177    return 0;
2178 }
2179 
2180 
2181 /**
2182  * @brief Creates the interference map for the current gui.
2183  */
gui_createInterference(Radar * radar)2184 static void gui_createInterference( Radar *radar )
2185 {
2186    uint8_t raw;
2187    int i, j, k;
2188    float *map;
2189    uint32_t *pix;
2190    SDL_Surface *sur;
2191    int w,h, hw,hh;
2192    float c;
2193    int r;
2194 
2195    /* Dimension shortcuts. */
2196    if (radar->shape == RADAR_CIRCLE) {
2197       w = radar->w*2.;
2198       h = w;
2199    }
2200    else if (radar->shape == RADAR_RECT) {
2201       w = radar->w*2.;
2202       h = radar->h*2.;
2203    }
2204    else {
2205       WARN("Radar shape is invalid.");
2206       return;
2207    }
2208 
2209    for (k=0; k<INTERFERENCE_LAYERS; k++) {
2210 
2211       /* Free the old texture. */
2212       if (radar->interference[k] != NULL)
2213          gl_freeTexture(radar->interference[k]);
2214 
2215       /* Create the temporary surface. */
2216       sur = SDL_CreateRGBSurface( SDL_SWSURFACE, w, h, 32, RGBAMASK );
2217       pix = sur->pixels;
2218 
2219       /* Clear pixels. */
2220       memset( pix, 0, sizeof(uint32_t)*w*h );
2221 
2222       /* Load the interference map. */
2223       map = noise_genRadarInt( w, h, (w+h)/2*1.2 );
2224 
2225       /* Create the texture. */
2226       SDL_LockSurface( sur );
2227       if (radar->shape == RADAR_CIRCLE) {
2228          r = pow2((int)radar->w);
2229          hw = w/2;
2230          hh = h/2;
2231          for (i=0; i<h; i++) {
2232             for (j=0; j<w; j++) {
2233                /* Must be in circle. */
2234                if (pow2(i-hh) + pow2(j-hw) > r)
2235                   continue;
2236                c = map[i*w + j];
2237                raw = 0xff & (uint8_t)((float)0xff * c);
2238                memset( &pix[i*w + j], raw, sizeof(uint32_t) );
2239                pix[i*w + j] |= AMASK;
2240             }
2241          }
2242       }
2243       else if (radar->shape == RADAR_RECT) {
2244          for (i=0; i<h*w; i++) {
2245             /* Process pixels. */
2246             c = map[i];
2247             raw = 0xff & (uint8_t)((float)0xff * c);
2248             memset( &pix[i], raw, sizeof(uint32_t) );
2249             pix[i] |= AMASK;
2250          }
2251       }
2252       SDL_UnlockSurface( sur );
2253 
2254       /* Set the interference. */
2255       radar->interference[k] = gl_loadImage( sur, 0 );
2256 
2257       /* Clean up. */
2258       free(map);
2259    }
2260 }
2261 
2262 
2263 /**
2264  * @brief Cleans up the GUI.
2265  */
gui_cleanup(void)2266 void gui_cleanup (void)
2267 {
2268    int i;
2269 
2270    /* Disable mouse voodoo. */
2271    gui_mouseClickEnable( 0 );
2272    gui_mouseMoveEnable( 0 );
2273 
2274    /* Interference. */
2275    for (i=0; i<INTERFERENCE_LAYERS; i++) {
2276       if (gui_radar.interference[i] != NULL) {
2277          gl_freeTexture(gui_radar.interference[i]);
2278          gui_radar.interference[i] = NULL;
2279       }
2280    }
2281 
2282    /* Set the viewport. */
2283    gui_clearViewport();
2284 
2285    /* Reset FPS. */
2286    fps_setPos( 15., (double)(gl_screen.h-15-gl_defFont.h) );
2287 
2288    /* Clean up interference. */
2289    interference_alpha = 0.;
2290    interference_layer = 0;
2291    interference_t     = 0.;
2292 
2293    /* Destroy offset. */
2294    gui_xoff = 0.;
2295    gui_yoff = 0.;
2296 
2297    /* Destroy lua. */
2298    if (gui_env != LUA_NOREF) {
2299       nlua_freeEnv( gui_env );
2300       gui_env = LUA_NOREF;
2301    }
2302 
2303    /* OMSG */
2304    omsg_position( SCREEN_W/2., SCREEN_H*2./3., SCREEN_W*2./3. );
2305 }
2306 
2307 
2308 /**
2309  * @brief Frees the gui stuff.
2310  */
gui_free(void)2311 void gui_free (void)
2312 {
2313    /* Clean up gui. */
2314    gui_cleanup();
2315 
2316    /* Free messages. */
2317    if (mesg_stack != NULL) {
2318       free(mesg_stack);
2319       mesg_stack = NULL;
2320    }
2321 
2322    /* Free VBO. */
2323    if (gui_vbo != NULL) {
2324       gl_vboDestroy( gui_vbo );
2325       gui_vbo = NULL;
2326    }
2327 
2328    /* Clean up the osd. */
2329    osd_exit();
2330 
2331    /* Free icons. */
2332    if (gui_ico_hail != NULL)
2333       gl_freeTexture( gui_ico_hail );
2334    gui_ico_hail = NULL;
2335    if (gui_target_planet != NULL)
2336       gl_freeTexture( gui_target_planet );
2337    gui_target_planet = NULL;
2338    if (gui_target_pilot != NULL)
2339       gl_freeTexture( gui_target_pilot );
2340    gui_target_pilot = NULL;
2341 
2342    /* Free overlay messages. */
2343    omsg_cleanup();
2344 }
2345 
2346 
2347 /**
2348  * @brief Modifies the radar resolution.
2349  *
2350  *    @param mod Number of intervals to jump (up or down).
2351  */
gui_setRadarRel(int mod)2352 void gui_setRadarRel( int mod )
2353 {
2354    gui_radar.res += mod * RADAR_RES_INTERVAL;
2355    gui_radar.res = CLAMP( RADAR_RES_MIN, RADAR_RES_MAX, gui_radar.res );
2356 
2357    player_message( "\epRadar set to %dx.", (int)gui_radar.res );
2358 }
2359 
2360 
2361 /**
2362  * @brief Gets the GUI offset.
2363  *
2364  *    @param x X offset.
2365  *    @param y Y offset.
2366  */
gui_getOffset(double * x,double * y)2367 void gui_getOffset( double *x, double *y )
2368 {
2369    *x = gui_xoff;
2370    *y = gui_yoff;
2371 }
2372 
2373 
2374 /**
2375  * @brief Gets the hail icon texture.
2376  */
gui_hailIcon(void)2377 glTexture* gui_hailIcon (void)
2378 {
2379    return gui_ico_hail;
2380 }
2381 
2382 
2383 /**
2384  * @brief Sets the planet target GFX.
2385  */
gui_targetPlanetGFX(glTexture * gfx)2386 void gui_targetPlanetGFX( glTexture *gfx )
2387 {
2388    if (gui_target_planet != NULL)
2389       gl_freeTexture( gui_target_planet );
2390    gui_target_planet = gl_dupTexture( gfx );
2391 }
2392 
2393 
2394 /**
2395  * @brief Sets the pilot target GFX.
2396  */
gui_targetPilotGFX(glTexture * gfx)2397 void gui_targetPilotGFX( glTexture *gfx )
2398 {
2399    if (gui_target_pilot != NULL)
2400       gl_freeTexture( gui_target_pilot );
2401    gui_target_pilot = gl_dupTexture( gfx );
2402 }
2403 
2404 
2405 /**
2406  * @brief Handles GUI events.
2407  */
gui_handleEvent(SDL_Event * evt)2408 int gui_handleEvent( SDL_Event *evt )
2409 {
2410    int ret;
2411    int x, y;
2412 
2413    if (player.p == NULL)
2414       return 0;
2415    if ((evt->type == SDL_MOUSEBUTTONDOWN) &&
2416          (pilot_isFlag(player.p,PILOT_HYP_PREP) ||
2417          pilot_isFlag(player.p,PILOT_HYP_BEGIN) ||
2418          pilot_isFlag(player.p,PILOT_HYPERSPACE)))
2419       return 0;
2420 
2421    ret = 0;
2422    switch (evt->type) {
2423       /* Mouse motion. */
2424       case SDL_MOUSEMOTION:
2425          if (!gui_L_mmove)
2426             break;
2427          gui_prepFunc( "mouse_move" );
2428          gl_windowToScreenPos( &x, &y, evt->motion.x, evt->motion.y );
2429          lua_pushnumber( naevL, x );
2430          lua_pushnumber( naevL, y );
2431          gui_runFunc( "mouse_move", 2, 0 );
2432          break;
2433 
2434       /* Mouse click. */
2435       case SDL_MOUSEBUTTONDOWN:
2436       case SDL_MOUSEBUTTONUP:
2437          if (!gui_L_mclick)
2438             break;
2439          gui_prepFunc( "mouse_click" );
2440          lua_pushnumber( naevL, evt->button.button+1 );
2441          gl_windowToScreenPos( &x, &y, evt->button.x, evt->button.y );
2442          lua_pushnumber( naevL, x );
2443          lua_pushnumber( naevL, y );
2444          lua_pushboolean( naevL, (evt->type==SDL_MOUSEBUTTONDOWN) );
2445          gui_runFunc( "mouse_click", 4, 1 );
2446          ret = lua_toboolean( naevL, -1 );
2447          lua_pop( naevL, 1 );
2448          break;
2449 
2450       /* Not interested in the rest. */
2451       default:
2452          break;
2453    }
2454    return ret;
2455 }
2456 
2457 
2458 /**
2459  * @brief Enables the mouse click callback.
2460  */
gui_mouseClickEnable(int enable)2461 void gui_mouseClickEnable( int enable )
2462 {
2463    gui_L_mclick = enable;
2464 }
2465 
2466 
2467 /**
2468  * @brief Enables the mouse movement callback.
2469  */
gui_mouseMoveEnable(int enable)2470 void gui_mouseMoveEnable( int enable )
2471 {
2472    gui_L_mmove = enable;
2473 }
2474