From 0573336e27564cb4aac55c316fbb5c2d89dd9704 Mon Sep 17 00:00:00 2001 From: Allanis <allanis@saracraft.net> Date: Sun, 20 Jul 2014 20:48:13 +0100 Subject: [PATCH] [Change] Ripped out all the GUI code and gave it, its own file. --- src/gui.c | 1241 ++++++++++++++++++++++++++++++++++++++++++++++++ src/gui.h | 27 ++ src/input.c | 5 +- src/lephisto.c | 8 +- src/map.c | 4 +- src/nebulae.c | 2 +- src/opengl.c | 8 +- src/player.c | 1175 +-------------------------------------------- src/player.h | 17 +- src/space.c | 7 +- src/weapon.c | 1 + 11 files changed, 1313 insertions(+), 1182 deletions(-) create mode 100644 src/gui.c create mode 100644 src/gui.h diff --git a/src/gui.c b/src/gui.c new file mode 100644 index 0000000..ae35f7b --- /dev/null +++ b/src/gui.c @@ -0,0 +1,1241 @@ +/** + * @file gui.c + * + * @brief Contains the GUI stuff for the player. + */ +#include <stdlib.h> + +#include "player.h" +#include "lxml.h" +#include "lephisto.h" +#include "pilot.h" +#include "log.h" +#include "opengl.h" +#include "font.h" +#include "ldata.h" +#include "space.h" +#include "rng.h" +#include "land.h" +#include "sound.h" +#include "economy.h" +#include "pause.h" +#include "menu.h" +#include "toolkit.h" +#include "dialogue.h" +#include "mission.h" +#include "llua_misn.h" +#include "ltime.h" +#include "hook.h" +#include "map.h" +#include "lfile.h" +#include "spfx.h" +#include "unidiff.h" +#include "comm.h" +#include "intro.h" +#include "perlin.h" +#include "ai.h" +#include "ai.h" +#include "gui.h" + +#define XML_GUI_ID "GUIs" /**< XML section identifier for GUI document. */ +#define XML_GUI_TAG "gui" /**< XML section identifier for gui tags. */ + +#define GUI_DATA "../dat/gui.xml" /**< Global GUI configuration file. */ +#define GUI_GFX "../gfx/gui/" /**< Location of the GUI graphics. */ + +#define TARGET_WIDTH 128 /**< Width of the target graphic. */ +#define TARGET_HEIGHT 96 /**< Height of the target graphic. */ + +#define INTERFERENCE_LAYERS 16 /**< Number of interference layers. */ +#define INTERFERENCE_CHANGE_DT 0.1 /** Speed to change at. */ + +/* For interference. */ +static int interference_layer = 0; /**< Layer of the current interference. */ +double interference_alpha = 0.; /**< Alpha of the current interference layer. */ +static double interference_t = 0.; /**< Interference timer to control transitions. */ + +/* Pilot stuff for GUI. */ +extern Pilot** pilot_stack; +extern int pilot_nstack; + +/* Space stuff for GUI. */ +extern StarSystem* systems_stack; + +/* Map stuff for autonav. */ +extern int map_npath; + +/** + * @struct Radar + * + * @brief Represents the players radar. + */ +typedef struct Radar_ { + double x; /**< X position. */ + double y; /**< Y position. */ + double w; /**< Width. */ + double h; /**< Height. */ + RadarShape shape; /**< Shape. */ + double res; /** Resolution. */ + glTexture* interference[INTERFERENCE_LAYERS]; /**< Interference texture. */ +} Radar; +/* Radar resolutions. */ +#define RADAR_RES_MAX 100. /**< Maximum radar resolution. */ +#define RADAR_RES_MIN 10. /**< Minimum radar resolution. */ +#define RADAR_RES_INTERVAL 10. /**< Steps used to increase/decrease resolution. */ +#define RADAR_RES_DEFAULT 40. /**< Default resolution. */ + +/** + * @struct Rect + * + * @brief Represents a rectangle. + */ +typedef struct Rect_ { + double x; /**< X position. */ + double y; /**< Y position. */ + double w; /**< Width. */ + double h; /**< Height. */ +} Rect; + +/** + * @struct GUI + * + * @brief Represents the ingame player graphical user interface. + */ +typedef struct GUI_ { + /* Graphics. */ + glTexture* gfx_frame; /**< Frame of the GUI. */ + glTexture* gfx_targetPilot; /**< Graphics used to target pilot. */ + glTexture* gfx_targetPlanet; /**< Graphics used to target planets. */ + + /* + * Rects. + */ + /* Radar. */ + Radar radar; /**< The radar. */ + /* Navigation. */ + Rect nav; /**< Navigation computer. */ + /* Health. */ + Rect shield; /**< Shield bar. */ + glTexture* gfx_shield; /**< Shield bar texture if applicable. */ + Rect armour; /**< Armour bar. */ + glTexture* gfx_armour; /**< Armour bar texture if applicable. */ + Rect energy; /**< Energy bar. */ + glTexture* gfx_energy; /**< Energy bar texture if applicable. */ + Rect fuel; /**< Fuel bar. */ + glTexture* gfx_fuel; /**< Fuel bar texture if applicable. */ + /* Weapon. */ + Rect weapon; /**< Weapon targetting system. */ + /* Targetting. */ + Rect target_health; /**< Target health. */ + Rect target_name; /**< Name of the target. */ + Rect target_faction; /**< Faction of the target. */ + /* Misc. */ + Rect misc; /**< Misc stuff: credits, cargo etc.. */ + /* Messages. */ + Rect msg; /**< Where messages go. */ + + /* Positions. */ + Vec2 frame; /**< Global frame position. */ + Vec2 target; /**< Global target position. */ +} GUI; + +static GUI gui = { + .gfx_frame = NULL, + .gfx_targetPilot = NULL, + .gfx_targetPlanet = NULL +}; + +/* Needed to render properly. */ +double gui_xoff = 0.; /**< X offset that GUI introduces. */ +double gui_yoff = 0.; /**< Y offset that GUI introduces. */ + +/* Messages. */ +#define MSG_SIZE_MAX 120 /**< Maximum message length. */ +double msg_timeout = 5.; /**< Time it takes for a message to timeout. */ +int msg_max = 5; /**< Maximum messages onscreen. */ + +/** + * @struct Msg. + * + * @brief On screen player messages. + */ +typedef struct Msg_ { + char str[MSG_SIZE_MAX]; /**< The message. */ + double t; /**< Timer related to messages. */ +} Msg; +static Msg* msg_stack; /**< Stack of messages, will be of msg_max size. */ + +/* External. */ +extern void weapon_minmap(const double res, const double w, const double h, + const RadarShape shape, double alpha); /**< From weapon.c */ +extern void planets_minimap(const double res, const double w, const double h, + const RadarShape shape, double alpha); /**< From space.c */ + +/* Gui. */ +static void gui_createInterference(void); +static void rect_parseParam(const xmlNodePtr parent, + char* name, double* param); +static void rect_parse(const xmlNodePtr parent, + double* x, double* y, double* w, double* h); +static int gui_parse(const xmlNodePtr parent, const char* name); +/* Render GUI. */ +static void gui_renderRadar(double dt); +static void gui_renderMessages(double dt); +static void gui_renderPilot(const Pilot* p); +static void gui_renderHealth(const glColour* c, + const Rect* r, const glTexture* tex, const double w); +static void gui_renderInterference(double dt); + +/** + * @brief Set the GUI to defaults. + */ +void gui_setDefaults(void) { + gui.radar.res = RADAR_RES_DEFAULT; + memset(msg_stack, 0, msg_max * sizeof(Msg)); +} + +/** + * @brief Add a msg to the queue to be displayed on screen. + * @param fmt String with formatting like printf. + */ +void player_message(const char* fmt, ...) { + va_list ap; + int i; + + if(fmt == NULL) return; /* Message not valid. */ + + /* Copy old messages back. */ + for(i = 1; i < msg_max; i++) { + if(msg_stack[msg_max-i-1].str[0] != '\0') { + strcpy(msg_stack[msg_max-i].str, msg_stack[msg_max-i-1].str); + msg_stack[msg_max-i].t = msg_stack[msg_max-i-1].t; + } + } + + /* Add the new one. */ + va_start(ap, fmt); + vsnprintf(msg_stack[0].str, MSG_SIZE_MAX, fmt, ap); + va_end(ap); + + msg_stack[0].t = msg_timeout; +} + +/** + * @brief Render the background GUI stuff, namely planet target gfx. + * @param dt Current delta tick. + */ +void gui_renderBG(double dt) { + (void) dt; + double x, y; + glColour* c; + Planet* planet; + + /* NJo need to draw if pilot is dead. */ + if(player_isFlag(PLAYER_DESTROYED) || player_isFlag(PLAYER_CREATING) || + ((player != NULL) && pilot_isFlag(player, PILOT_DEAD))) + return; + + if(planet_target >= 0) { + planet = cur_system->planets[planet_target]; + + c = faction_getColour(planet->faction); + + x = planet->pos.x - planet->gfx_space->sw/2.; + y = planet->pos.y + planet->gfx_space->sh/2.; + gl_blitSprite(gui.gfx_targetPlanet, x, y, 0, 0, c); /* Top left. */ + + x += planet->gfx_space->sw; + gl_blitSprite(gui.gfx_targetPlanet, x, y, 1, 0, c); /* Top right. */ + + y -= planet->gfx_space->sh; + gl_blitSprite(gui.gfx_targetPlanet, x, y, 1, 1, c); /* Bottom right. */ + + x -= planet->gfx_space->sw; + gl_blitSprite(gui.gfx_targetPlanet, x, y, 0, 1, c); /* Bottom left. */ + } +} + +/** + * @brief Render the players pilot target. + * @param dt Current delta tick. + */ +void gui_renderTarget(double dt) { + (void)dt; + Pilot* p; + glColour* c; + double x, y; + + /* Get the target. */ + if(player->target != PLAYER_ID) + p = pilot_get(player->target); + else p = NULL; + + if((p == NULL) || pilot_isFlag(p, PILOT_DEAD)) + player->target = PLAYER_ID; /* No more pilot_target. */ + else { /* Still is a pilot_target. */ + if(pilot_isDisabled(p)) + c = &cInert; + else if(pilot_isFlag(p, PILOT_BRIBED)) + c = &cNeutral; + else if(pilot_isFlag(p, PILOT_HOSTILE)) + c = &cHostile; + else + c = faction_getColour(p->faction); + + x = p->solid->pos.x - p->ship->gfx_space->sw * PILOT_SIZE_APROX/2.; + y = p->solid->pos.y + p->ship->gfx_space->sh * PILOT_SIZE_APROX/2.; + gl_blitSprite(gui.gfx_targetPilot, x, y, 0, 0, c); /* Top left. */ + + x += p->ship->gfx_space->sw * PILOT_SIZE_APROX; + gl_blitSprite(gui.gfx_targetPilot, x, y, 1, 0, c); /* Top right. */ + + y -= p->ship->gfx_space->sh * PILOT_SIZE_APROX; + gl_blitSprite(gui.gfx_targetPilot, x, y, 1, 1, c); /* Bottom right. */ + + y -= p->ship->gfx_space->sw * PILOT_SIZE_APROX; + gl_blitSprite(gui.gfx_targetPilot, x, y, 0, 1, c); /* Bottom left. */ + } +} + +static int can_jump = 0; /**< Store whether or not the player is able to jump. */ +/** + * @brief Render the players GUI. + * @param dt Current delta tick. + */ +void gui_render(double dt) { + int i, j; + double x; + char str[10]; + Pilot* p; + glColour* c; + glFont* f; + StarSystem* sys; + int quantity, delay; + + /* If player is dead, just render the cinematic mode. */ + if(player_isFlag(PLAYER_DESTROYED) || player_isFlag(PLAYER_CREATING) || + ((player != NULL) && pilot_isFlag(player, PILOT_DEAD))) { + + spfx_cinematic(); + return; + } + + /* Make sure player is valid. */ + if(player == NULL) return; + + /* Lockon warning. */ + if(player->lockons > 0) + gl_printMid(NULL, SCREEN_W - gui_xoff, 0., SCREEN_H-gl_defFont.h-25., + &cRed, "LOCKON DETECTED"); + + /* Volatile environment. */ + if(cur_system->nebu_volatility > 0.) + gl_printMid(NULL, SCREEN_W - gui_xoff, 0., SCREEN_H-gl_defFont.h*2.-35., + &cRed, "VOLATILE ENVIRONMENT DETECTED"); + + /* + * GUI. + */ + /* Frame. */ + gl_blitStatic(gui.gfx_frame, gui.frame.x, gui.frame.y, NULL); + + /* Radar. */ + gui_renderRadar(dt); + + /* + * NAV + */ + if(planet_target >= 0) { /* Planet landing target. */ + gl_printMid(NULL, (int)gui.nav.w, + gui.nav.x, gui.nav.y - 5, + &cConsole, "Land"); + + gl_printMid(&gl_smallFont, (int)gui.nav.w, + gui.nav.x, gui.nav.y - 10 - gl_smallFont.h, + NULL, "%s", cur_system->planets[planet_target]->name); + } + else if(hyperspace_target >= 0) { /* Hyperspace target. */ + sys = &systems_stack[cur_system->jumps[hyperspace_target]]; + + /* Determine if we have to play the "enter hyperspace range" sound. */ + i = space_canHyperspace(player); + if((i != 0) && (i != can_jump)) + if(!pilot_isFlag(player, PILOT_HYPERSPACE)) + player_playSound(snd_jump, 1); + can_jump = i; + + /* Determine the colour of the NAV text. */ + if(can_jump || pilot_isFlag(player, PILOT_HYPERSPACE) || + pilot_isFlag(player, PILOT_HYP_PREP) || + pilot_isFlag(player, PILOT_HYP_BEGIN)) + c = &cConsole; + else c = NULL; + + gl_printMid(NULL, (int)gui.nav.w, + gui.nav.x, gui.nav.y - 5, + c, "Hyperspace"); + + gl_printMid(&gl_smallFont, (int)gui.nav.w, + gui.nav.x, gui.nav.y - 10 - gl_smallFont.h, + NULL, "%d - %s", pilot_getJumps(player), + (sys_isKnown(sys)) ? sys->name : "Unknown"); + } else { /* No NAV target. */ + gl_printMid(NULL, (int)gui.nav.w, + gui.nav.x, gui.nav.y - 5, + &cConsole, "Navigation"); + + gl_printMid(&gl_smallFont, (int)gui.nav.w, + gui.nav.x, gui.nav.y - 10 - gl_smallFont.h, + &cGrey, "Off"); + } + + /* Health. */ + gui_renderHealth(&cShield, &gui.shield, gui.gfx_shield, + player->shield / player->shield_max); + gui_renderHealth(&cArmour, &gui.armour, gui.gfx_armour, + player->armour / player->armour_max); + gui_renderHealth(&cEnergy, &gui.energy, gui.gfx_energy, + player->energy / player->energy_max); + gui_renderHealth(&cFuel, &gui.fuel, gui.gfx_fuel, + player->fuel / player->fuel_max); + + /* Weapon. */ + if(player->secondary == NULL) { /* No secondary weapon. */ + gl_printMid(NULL, (int)gui.weapon.w, + gui.weapon.x, gui.weapon.y - 5, + &cConsole, "Secondary"); + + gl_printMid(&gl_smallFont, (int)gui.weapon.w, + gui.weapon.x, gui.weapon.y - 10 - gl_defFont.h, + &cGrey, "None"); + } else { + f = &gl_defFont; + + quantity = pilot_oquantity(player, player->secondary); + delay = outfit_delay(player->secondary->outfit); + + /* Check to see if weapon is ready. */ + if(player->secondary->timer > 0.) + c = &cGrey; + else + c = &cConsole; + + /* Launcher. */ + if(player->ammo != NULL) { + /* Use the ammunitions name. */ + i = gl_printWidth(f, "%s", outfit_ammo(player->secondary->outfit)->name); + if(i > gui.weapon.w) /* Font is too big. */ + f = &gl_smallFont; + + /* Weapon name. */ + gl_printMid(f, (int)gui.weapon.w, + gui.weapon.x, gui.weapon.y - 5, + (player->ammo) ? c : &cGrey, "%s", + outfit_ammo(player->secondary->outfit)->name); + + /* Print ammo left underneath. */ + gl_printMid(&gl_smallFont, (int)gui.weapon.w, + gui.weapon.x, gui.weapon.y - 10 - gl_defFont.h, + NULL, "%d", (player->ammo) ? player->ammo->quantity : 0); + } else { /* Other. */ + i = gl_printWidth(f, "%s", player->secondary->outfit->name); + if(i > (int)gui.weapon.w) /* Font is too big. */ + f = &gl_smallFont; + gl_printMid(f, (int)gui.weapon.w, + gui.weapon.x, gui.weapon.y - (gui.weapon.h - f->h)/2., + c, "%s", player->secondary->outfit->name); + } + } + + /* Target. */ + if(player->target != PLAYER_ID) { + p = pilot_get(player->target); + + /* Blit the pilot target. */ + gl_blitStatic(p->ship->gfx_target, gui.target.x, gui.target.y, NULL); + /* Blit the pilot space image. */ + /*x = gui.target.x + (TARGET_WIDTH - p->ship->gfx_space->sw)/2.; + y = gui.target.y + (TARGET_HEIGHT - p->ship->gfx_space->sh)/2.; + gl_blitStaticSprite(p->ship->gfx_space, + x, y, p->tsx, p->tsy, NULL); */ + + /* Target name. */ + gl_print(NULL, + gui.target_name.x, + gui.target_name.y, + NULL, "%s", p->name); + gl_print(&gl_smallFont, + gui.target_faction.x, + gui.target_faction.y, + NULL, "%s", faction_name(p->faction)); + + /* Target status. */ + if(pilot_isDisabled(p)) /* Pilot is disabled. */ + gl_print(&gl_smallFont, + gui.target_health.x, + gui.target_health.y, + NULL, "Disabled"); + else if(p->shield > p->shield_max/100.) /* On shields. */ + gl_print(&gl_smallFont, + gui.target_health.x, + gui.target_health.y, NULL, + "%s: %.0f%%", "Shield", p->shield/p->armour_max*100.); + } else { /* No target. */ + gl_printMid(NULL, SHIP_TARGET_W, + gui.target.x, gui.target.y + (SHIP_TARGET_H - gl_defFont.h)/2., + &cGrey, "No Target"); + } + + /* Misc. */ + + /* Money. */ + j = gui.misc.y - 8 - gl_smallFont.h; + gl_print(&gl_smallFont, + gui.misc.x + 8, j, + &cConsole, "Creds:"); + credits2str(str, player->credits, 2); + i = gl_printWidth(&gl_smallFont, str); + gl_print( &gl_smallFont, + gui.misc.x + gui.misc.w - 8 - i, j, + NULL, str ); + /* Cargo and friends. */ + if(player->ncommodities > 0) { + j -= gl_smallFont.h + 5; + gl_print(&gl_smallFont, + gui.misc.x + 8, j, + &cConsole, "Cargo:"); + for(i = 0; i < MIN(player->ncommodities, 3); i++) { + j -= gl_smallFont.h + 3; + if(player->commodities[i].quantity) /* Quantity is over. */ + gl_printMax(&gl_smallFont, gui.misc.w - 15, + gui.misc.x + 13, j, + NULL, "%d %s%s", player->commodities[i].quantity, + player->commodities[i].commodity->name, + (player->commodities[i].id) ? "*" : ""); + else /* Basically for weightless mission stuff. */ + gl_printMax(&gl_smallFont, gui.misc.w - 15, + gui.misc.x + 13, j, + NULL, "%s%s", player->commodities[i].commodity->name, + (player->commodities[i].id) ? "*" : ""); + } + } + + j -= gl_smallFont.h + 5; + gl_print(&gl_smallFont, + gui.misc.x + 8, j, + &cConsole, "Free:"); + i = gl_printWidth(&gl_smallFont, "%d", pilot_cargoFree(player)); + gl_print(&gl_smallFont, + gui.misc.x + gui.misc.w - 8 - i, j, + NULL, "%d", pilot_cargoFree(player)); + + /* Messages. */ + gui_renderMessages(dt); + + /* Hyperspace. */ + if(pilot_isFlag(player, PILOT_HYPERSPACE) && + (player->ptimer < HYPERSPACE_FADEOUT)) { + if(i < j) { + x = (HYPERSPACE_FADEOUT-player->ptimer) / HYPERSPACE_FADEOUT; + glColor4d(1.,1.,1., x); + glBegin(GL_QUADS); + glVertex2d( -SCREEN_W/2., -SCREEN_H/2.); + glVertex2d( -SCREEN_W/2., SCREEN_H/2.); + glVertex2d( SCREEN_W/2., SCREEN_H/2.); + glVertex2d( SCREEN_W/2., -SCREEN_H/2.); + glEnd(); + } + } +} + +/** + * @brief Render the GUI Radar. + * @param dt Current deltatick. + */ + +static void gui_renderRadar(double dt) { + int i, j; + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + if(gui.radar.shape == RADAR_RECT) + glTranslated(gui.radar.x - SCREEN_W/2. + gui.radar.w/2., + gui.radar.y - SCREEN_H/2. - gui.radar.h/2., 0); + else if(gui.radar.shape == RADAR_CIRCLE) + glTranslated(gui.radar.x - SCREEN_W/2., + gui.radar.y - SCREEN_H/2, 0.); + + /* Planets. */ + planets_minimap(gui.radar.res, gui.radar.w, gui.radar.h, + gui.radar.shape, 1.-interference_alpha); + + /* Weapons. */ + weapon_minimap(gui.radar.res, gui.radar.w, gui.radar.h, + gui.radar.shape, 1.-interference_alpha); + + /* Render the pilot_nstack. */ + j = 0; + for(i = 1; j < pilot_nstack; i++) { /* Skip the player. */ + if(pilot_stack[i]->id == player->target) + j = i; + else + gui_renderPilot(pilot_stack[i]); + } + /* Render the targetted pilot. */ + if(j != 0) + gui_renderPilot(pilot_stack[j]); + + glPopMatrix(); /* GL_PROJECTION. */ + + /* Interference. */ + gui_renderInterference(dt); + + glPushMatrix(); + if(gui.radar.shape == RADAR_RECT) + glTranslated(gui.radar.x - SCREEN_W/2. + gui.radar.w/2., + gui.radar.y - SCREEN_H/2. - gui.radar.h/2., 0.); + else if(gui.radar.shape == RADAR_CIRCLE) + glTranslated(gui.radar.x - SCREEN_W/2., + gui.radar.y - SCREEN_H/2., 0.); + + /* The + sign in the center of the radar represents the player. */ + glBegin(GL_LINES); + glVertex2d( 0., -3.); + glVertex2d( 0., 3.); + glVertex2d( -3., 0.); + glVertex2d( 3., 0.); + glEnd(); + + glPopMatrix(); /* GL_PROJECTION. */ +} + +/** + * @brief Render the players messages on screen. + * @param dt Current delta tick. + */ + +static void gui_renderMessages(double dt) { + double x, y; + glColour c; + int i; + + x = gui.msg.x; + y = gui.msg.y + (double)(gl_defFont.h*msg_max)*1.2; + c.r = c.g = c.b = 1.; + + for(i = msg_max-1; i >= 0; i--) { + y -= (double)gl_defFont.h*1.2; + + if(msg_stack[i].str[0] != '\0') { + + /* Decrement timer. */ + msg_stack[i].t -= dt; + + /* Set to NULL if timer is up. */ + if(msg_stack[i].t < 0.) + msg_stack[i].str[0] = '\0'; + else { /* Draw with variable alpha. */ + if(msg_stack[i].t - msg_timeout/2 < 0.) + c.a = msg_stack[i].t / (msg_timeout/2.); + else + c.a = 1.; + gl_print(NULL, x, y, &c, "%s", msg_stack[i].str); + } + } + } +} + +/** + * @brief Render interference if needed. + * @param dt Current deltatick. + */ +static void gui_renderInterference(double dt) { + glColour c; + glTexture* tex; + int t; + + /* Must be displaying interference. */ + if(interference_alpha <= 0.) + return; + + /* Calculate frame to draw. */ + interference_t += dt; + if(interference_t > INTERFERENCE_CHANGE_DT) { /* Time to change. */ + t = RNG(0, INTERFERENCE_LAYERS-1); + if(t != interference_layer) + interference_layer = t; + else + interference_layer = (interference_layer == INTERFERENCE_LAYERS-1) ? + 0 : interference_layer+1; + interference_t -= INTERFERENCE_CHANGE_DT; + } + + /* Render the interference. */ + c.r = c.g = c.b = 1.; + c.a = interference_alpha; + tex = gui.radar.interference[interference_layer]; + if(gui.radar.shape == RADAR_CIRCLE) + gl_blitStatic(tex, + gui.radar.x - gui.radar.w, + gui.radar.y - gui.radar.w, &c); + else if(gui.radar.shape == RADAR_RECT) + gl_blitStatic(tex, + gui.radar.x - gui.radar.w/2, + gui.radar.y - gui.radar.h/2, &c); +} + +/** + * @brief Render a pilot in the GUI radar. + * @param p Pilot to render. + */ +static void gui_renderPilot(const Pilot* p) { + int x, y, sx, sy; + double w, h; + glColour* col; + double a; + + /* Get position. */ + x = (p->solid->pos.x - player->solid->pos.x) / gui.radar.res; + y = (p->solid->pos.y - player->solid->pos.y) / gui.radar.res; + /* Get size. */ + sx = PILOT_SIZE_APROX/2. * p->ship->gfx_space->sw / gui.radar.res; + sy = PILOT_SIZE_APROX/2. * p->ship->gfx_space->sh / gui.radar.res; + if(sx < 1.) + sx = 1.; + if(sy < 1.) + sy = 1.; + + /* Check if the pilot is in range. */ + if(((gui.radar.shape == RADAR_RECT) && + ((ABS(x) > gui.radar.w/2.+sx) || (ABS(y) > gui.radar.h/2.+sy))) || + ((gui.radar.shape == RADAR_CIRCLE) && + ((x*x+y*y) > (int)(gui.radar.w*gui.radar.w)))) { + /* Draw a little targetted symbol. */ + if(p->id == player->target) { + /* Circle radars have it easy. */ + if(gui.radar.shape == RADAR_CIRCLE) { + /* We'll create a line. */ + a = ANGLE(x,y); + x = gui.radar.w * cos(a); + y = gui.radar.w * sin(a); + sx = 0.85 * x; + sy = 0.85 * y; + + COLOUR(cRadar_tPilot); + glBegin(GL_LINES); + glVertex2d( x, y); + glVertex2d(sx, sy); + glEnd(); /* GL_LINES. */ + } + } + return; + } + + if(gui.radar.shape == RADAR_RECT) { + w = gui.radar.w/2.; + h = gui.radar.h/2.; + } + else if(gui.radar.shape == RADAR_CIRCLE) { + w = gui.radar.w; + h = gui.radar.w; + } + glBegin(GL_QUADS); + /* Colours. */ + if(p->id == player->target) col = &cRadar_tPilot; + else if(pilot_isDisabled(p)) col = &cInert; + else if(pilot_isFlag(p, PILOT_BRIBED)) col = &cNeutral; + else if(pilot_isFlag(p, PILOT_HOSTILE)) col = &cHostile; + else col = faction_getColour(p->faction); + ACOLOUR(*col, 1-interference_alpha); /**< Makes it much harder to see. */ + + /* Image. */ + glVertex2d( MAX(x-sx,-w), MIN(y+sy, h) ); /* Top left. */ + glVertex2d( MIN(x+sx, w), MIN(y+sy, h) ); /* Top right. */ + glVertex2d( MIN(x+sx, w), MAX(y-sy,-h) ); /* Bottom right. */ + glVertex2d( MAX(x-sx,-w), MAX(y-sy,-h) ); /* Bottom left. */ + glEnd(); /* GL_QUADS. */ +} + +/** + * @brief Render a health bar. + */ +static void gui_renderHealth(const glColour* c, + const Rect* r, const glTexture* tex, const double w) { + + double x,y, sx,sy, tx,ty; + + /* Set the colour. */ + COLOUR(*c); + + if(tex == NULL) { + x = r->x - SCREEN_W/2.; + y = r->y - SCREEN_H/2.; + sx = w * r->w; + sy = r->h; + + glBegin(GL_QUADS); + glVertex2d(x, y); + glVertex2d(x+sx, y); + glVertex2d(x+sx, y-sy); + glVertex2d(x, y-sy); + glEnd(); /* GL_QUADS. */ + } else { /* Render the texture. */ + x = r->x - SCREEN_W/2.; + y = r->y - SCREEN_H/2. + tex->sh; + sx = w * tex->sw; + sy = tex->sh; + tx = tex->sw / tex->rw; + ty = tex->sh / tex->rh; + + /* Draw the image. */ + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, tex->texture); + glBegin(GL_QUADS); + glTexCoord2d(0., ty); + glVertex2d(x, y); + + glTexCoord2d(w*tx, ty); + glVertex2d(x+sx, y); + + glTexCoord2d(w*tx, 0.); + glVertex2d(x+sx, y-sy); + + glTexCoord2d(0., 0.); + glVertex2d(x, y-sy); + glEnd(); /* GL_QUADS. */ + glDisable(GL_TEXTURE_2D); + } +} + +/** + * brief Initialises the GUI system. + * @return 0 on success. + */ +int gui_init(void) { + /* Set gfx to NULL. */ + memset(&gui, 0, sizeof(GUI)); + + /* Radar. */ + gui.radar.res = RADAR_RES_DEFAULT; + + /* Messages. */ + gui.msg.x = 20; + gui.msg.y = 30; + msg_stack = calloc(msg_max, sizeof(Msg)); + if(msg_stack == NULL) { + ERR("Out of memory!"); + return -1; + } + return 0; +} + +/** + * @brief Attempts to load the actual GUI. + * @param name Name of the GUI to load. + * @return 0 on success. + */ +int gui_load(const char* name) { + uint32_t bufsize; + char* buf = ldata_read(GUI_DATA, &bufsize); + char* tmp; + int found = 0; + + xmlNodePtr node; + xmlDocPtr doc = xmlParseMemory(buf, bufsize); + + node = doc->xmlChildrenNode; + if(!xml_isNode(node, XML_GUI_ID)) { + ERR("Malformed '"GUI_DATA"' file: missing root element '"XML_GUI_ID"'"); + return -1; + } + + node = node->xmlChildrenNode; /* First system node. */ + if(node == NULL) { + ERR("Malformed '"GUI_DATA"' file: does not contain elements"); + return -1; + } + + do { + if(xml_isNode(node, XML_GUI_TAG)) { + tmp = xml_nodeProp(node, "name"); /* Mallocs. */ + /* Is this the GUI we are looking for? */ + if(strcmp(tmp, name) == 0) { + found = 1; + + /* Parse the xml node. */ + if(gui_parse(node, name)) + WARN("Trouble loading GUI '%s'", name); + free(tmp); + break; + } + free(tmp); + } + } while(xml_nextNode(node)); + + xmlFreeDoc(doc); + free(buf); + + if(!found) { + WARN("GUI '%s' not found in '"GUI_DATA"'", name); + return -1; + } + + return 0; +} + +/** + * @brief Parse a parameter of the rect node. + */ +static void rect_parseParam(const xmlNodePtr parent, + char* name, double* param) { + + char* buf; + + /* Get the attribute. */ + xmlr_attr(parent, name, buf); + + /* Wants attribute. */ + if(param != NULL) { + if(buf == NULL) + WARN("Node '%s' mising 'x' parameter.", parent->name); + else if(buf != NULL) + *param = atoi(buf); + } + /* Doesn't want it. */ + else if(buf != NULL) + WARN("Node '%s' has superfluous 'x' parameter.", parent->name); + + /* Clean it up. */ + if(buf != NULL) + free(buf); +} + +/** + * @brief Used to pull out a rect from an xml node. + */ +static void rect_parse(const xmlNodePtr parent, + double* x, double* y, double* w, double* h) { + rect_parseParam(parent, "w", w); + rect_parseParam(parent, "h", h); + rect_parseParam(parent, "x", x); + rect_parseParam(parent, "y", y); +} + +/** + * @brief Create the interference map for the current gui. + */ +static void gui_createInterference(void) { + uint8_t raw; + int i, j, k; + float* map; + uint32_t* pix; + SDL_Surface* sur; + int w, h, hw, hh; + float c; + int r; + + /* Dimension shortcuts. */ + if(gui.radar.shape == RADAR_CIRCLE) { + w = gui.radar.w*2.; + h = w; + } + else if(gui.radar.shape == RADAR_RECT) { + w = gui.radar.w; + h = gui.radar.h; + } + + for(k = 0; k < INTERFERENCE_LAYERS; k++) { + /* free the old texture. */ + if(gui.radar.interference[k] != NULL) + gl_freeTexture(gui.radar.interference[k]); + + /* Create the temporary surface. */ + sur = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, RGBAMASK); + pix = sur->pixels; + + /* Load the interference map. */ + map = noise_genRadarInt(w, h, 100.); + + /* Create the texture. */ + SDL_LockSurface(sur); + if(gui.radar.shape == RADAR_CIRCLE) { + r = pow2((int)gui.radar.w); + hw = w/2; + hh = h/2; + for(i = 0; i < h; i++) { + for(j = 0; j < w; j++) { + /* Must be in circle. */ + if(pow2(i-hh) + pow2(j-hw) > r) + continue; + c = map[i*w + j]; + raw = 0xff &(uint8_t)((float)0xff * c); + memset(&pix[i*w + j], raw, sizeof(uint32_t)); + pix[i*w + j] |= AMASK; + } + } + } + else if(gui.radar.shape == RADAR_RECT) { + for(i = 0; i < h*w; i++) { + /* Process pixels. */ + c = map[i]; + raw = 0xff &(uint8_t)((float)0xff * c); + memset(&pix[i], raw, sizeof(uint32_t)); + pix[i] |= AMASK; + } + } + SDL_UnlockSurface(sur); + + /* Set the interference. */ + gui.radar.interference[k] = gl_loadImage(sur); + + /* Clean up. */ + free(map); + } +} + + +#define RELATIVIZE(a) \ +{(a).x += VX(gui.frame); (a).y = VY(gui.frame)+gui.gfx_frame->h-(a).y;} +/**< Converts a rect to absolute coords. */ + +/** + * @brief Parses a GUI node. + * @param parent node to parse from. + * @param name Name of the GUI to load. + */ +static int gui_parse(const xmlNodePtr parent, const char* name) { + xmlNodePtr cur, node; + char* tmp, buf[PATH_MAX]; + + /* Clean up. */ + gui_cleanup(); + + /* + * -- gfx -- + * Set as a property and not a node because it must be loaded first. + */ + tmp = xml_nodeProp(parent, "gfx"); + if(tmp == NULL) { + ERR("GUI '%s' has no gfx property", name); + return -1; + } + + /* Load gfx. */ + /* Frame. */ + snprintf(buf, PATH_MAX, GUI_GFX"%s.png", tmp); + gui.gfx_frame = gl_newImage(buf, 0); + /* Pilot. */ + snprintf(buf, PATH_MAX, GUI_GFX"%s_pilot.png", tmp); + gui.gfx_targetPilot = gl_newSprite(buf, 2, 2, 0); + /* Planet. */ + snprintf(buf, PATH_MAX, GUI_GFX"%s_planet.png", tmp); + gui.gfx_targetPlanet = gl_newSprite(buf, 2, 2, 0); + free(tmp); + + /* Frame (based on gfx). */ + vect_csetmin(&gui.frame, + SCREEN_W - gui.gfx_frame->w, /* x */ + SCREEN_H - gui.gfx_frame->h); /* y */ + + /* Now actually parse the data. */ + node = parent->children; + + do { /* Load all the data. */ + /* Offset. */ + if(xml_isNode(node, "offset")) + rect_parse(node, &gui_xoff, &gui_yoff, NULL, NULL); + + /* Radar. */ + else if(xml_isNode(node, "radar")) { + tmp = xml_nodeProp(node, "type"); + + if(strcmp(tmp, "rectangle")==0) gui.radar.shape = RADAR_RECT; + else if(strcmp(tmp, "circle")==0) gui.radar.shape = RADAR_CIRCLE; + else { + WARN("Radar for GUI '%s' is missing 'type' tag or has invalid 'type' tag", name); + gui.radar.shape = RADAR_RECT; + } + free(tmp); + + /* Load the appropriate measurements. */ + if(gui.radar.shape == RADAR_RECT) + rect_parse(node, &gui.radar.x, &gui.radar.y, &gui.radar.w, &gui.radar.h); + else if(gui.radar.shape == RADAR_CIRCLE) + rect_parse(node, &gui.radar.x, &gui.radar.y, &gui.radar.w, NULL); + RELATIVIZE(gui.radar); + } + + /* Nav computer. */ + else if(xml_isNode(node, "nav")) { + rect_parse(node, &gui.nav.x, &gui.nav.y, &gui.nav.w, &gui.nav.h); + RELATIVIZE(gui.nav); + gui.nav.y -= gl_defFont.h; + } + + /* Health bars. */ + else if(xml_isNode(node, "health")) { + cur = node->children; + do { + if(xml_isNode(cur, "shield")) { + rect_parse(cur, &gui.shield.x, &gui.shield.y, + &gui.shield.w, &gui.shield.h); + tmp = xml_get(cur); + if(tmp != NULL) { + snprintf(buf, PATH_MAX, GUI_GFX"%s.png", tmp); + gui.gfx_shield = gl_newImage(buf, 0); + } + RELATIVIZE(gui.shield); + } + if(xml_isNode(cur, "armour")) { + rect_parse(cur, &gui.armour.x, &gui.armour.y, + &gui.armour.w, &gui.armour.h); + tmp = xml_get(cur); + if(tmp != NULL) { + snprintf(buf, PATH_MAX, GUI_GFX"%s.png", tmp); + gui.gfx_armour = gl_newImage(buf, 0); + } + RELATIVIZE(gui.armour); + } + if(xml_isNode(cur, "energy")) { + rect_parse(cur, &gui.energy.x, &gui.energy.y, + &gui.energy.w, &gui.energy.h); + tmp = xml_get(cur); + if(tmp != NULL) { + snprintf(buf, PATH_MAX, GUI_GFX"%s.png", tmp); + gui.gfx_energy = gl_newImage(buf, 0); + } + RELATIVIZE(gui.energy); + } + if(xml_isNode(cur, "fuel")) { + rect_parse(cur, &gui.fuel.x, &gui.fuel.y, + &gui.fuel.w, &gui.fuel.h); + tmp = xml_get(cur); + if(tmp != NULL) { + snprintf(buf, PATH_MAX, GUI_GFX"%s.png", tmp); + gui.gfx_fuel = gl_newImage(buf, 0); + } + RELATIVIZE(gui.fuel); + } + } while(xml_nextNode(cur)); + } + + /* Secondary weapon. */ + else if(xml_isNode(node, "weapon")) { + rect_parse(node, &gui.weapon.x, &gui.weapon.y, + &gui.weapon.w, &gui.weapon.h); + RELATIVIZE(gui.weapon); + gui.weapon.y -= gl_defFont.h; + } + + /* Target. */ + else if(xml_isNode(node, "target")) { + cur = node->children; + do { + if(xml_isNode(cur, "gfx")) { + rect_parse(cur, &gui.target.x, &gui.target.y, NULL, NULL); + RELATIVIZE(gui.target); + gui.target.y -= SHIP_TARGET_H; + } + else if(xml_isNode(cur, "name")) { + rect_parse(cur, &gui.target_name.x, &gui.target_name.y, NULL, NULL); + RELATIVIZE(gui.target_name); + gui.target_name.y -= gl_defFont.h; + } + else if(xml_isNode(cur, "faction")) { + rect_parse(cur, &gui.target_faction.x, &gui.target_faction.y, NULL, NULL); + RELATIVIZE(gui.target_faction); + gui.target_faction.y -= gl_smallFont.h; + } + else if(xml_isNode(cur, "health")) { + rect_parse(cur, &gui.target_health.x, &gui.target_health.y, NULL, NULL); + RELATIVIZE(gui.target_health); + gui.target_health.y -= gl_defFont.h; + } + } while(xml_nextNode(cur)); + } + + /* Misc. */ + else if(xml_isNode(node, "misc")) { + rect_parse(node, &gui.misc.x, &gui.misc.y, &gui.misc.w, &gui.misc.h); + RELATIVIZE(gui.misc); + } + } while(xml_nextNode(node)); + + /* Some postprocessing. */ + gui_createInterference(); + + return 0; +} +#undef RELATIVIZE + +/* + * @brief Clean up the GUI. + */ +void gui_cleanup(void) { + int i; + /* free textures. */ + if( gui.gfx_frame != NULL) { + gl_freeTexture(gui.gfx_frame); + gui.gfx_frame = NULL; + } + if(gui.gfx_targetPilot != NULL) { + gl_freeTexture(gui.gfx_targetPilot); + gui.gfx_targetPilot = NULL; + } + if(gui.gfx_targetPlanet != NULL) { + gl_freeTexture(gui.gfx_targetPlanet); + gui.gfx_targetPlanet = NULL; + } + /* Health textures. */ + if(gui.gfx_shield != NULL) { + gl_freeTexture(gui.gfx_shield); + gui.gfx_shield = NULL; + } + if(gui.gfx_armour != NULL) { + gl_freeTexture(gui.gfx_armour); + gui.gfx_armour = NULL; + } + if(gui.gfx_energy != NULL) { + gl_freeTexture(gui.gfx_energy); + gui.gfx_energy = NULL; + } + if(gui.gfx_fuel != NULL) { + gl_freeTexture(gui.gfx_fuel); + gui.gfx_fuel = NULL; + } + for(i = 0; i < INTERFERENCE_LAYERS; i++) { + if(gui.radar.interference[i] != NULL) { + gl_freeTexture(gui.radar.interference[i]); + gui.radar.interference[i] = NULL; + } + } + + /* Clean up interference. */ + interference_alpha = 0.; + interference_layer = 0; + interference_t = 0; +} + +/** + * @brief Free the gui stuff. + */ +void gui_free(void) { + /* Clean up gui. */ + gui_cleanup(); + + /* Free messages. */ + free(msg_stack); +} + +/** + * @brief Modifies the radar resolution. + * @param mod Number of intervals to jump (up or down). + */ +void gui_setRadarRel(int mod) { + gui.radar.res += mod * RADAR_RES_INTERVAL; + if(gui.radar.res > RADAR_RES_MAX) gui.radar.res = RADAR_RES_MAX; + else if(gui.radar.res < RADAR_RES_MIN) gui.radar.res = RADAR_RES_MIN; + + player_message("Radar set to %dx.", (int)gui.radar.res); +} + diff --git a/src/gui.h b/src/gui.h new file mode 100644 index 0000000..761d8b0 --- /dev/null +++ b/src/gui.h @@ -0,0 +1,27 @@ +#pragma once + +/* Enums. */ +typedef enum RadarShape_ { + RADAR_RECT, /**< Rectangular radar. */ + RADAR_CIRCLE /**< Circular radar. */ +} RadarShape; /**< Plaers radar shape. */ + +extern double gui_xoff; /**< GUI X center offset. */ +extern double gui_yoff; /**< GUI Y center offset. */ + +/* Loading/cleaning up. */ +int gui_init(void); +void gui_free(void); +int gui_load(const char* name); +void gui_cleanup(void); + +/* Render. */ +void gui_renderBG(double dt); +void gui_renderTarget(double dt); +void gui_render(double dt); + +/* Misc. */ +void gui_setDefaults(void); +void player_message(const char* fmt, ...); +void gui_setRadarRel(int mod); + diff --git a/src/input.c b/src/input.c index 9439b07..a41c806 100644 --- a/src/input.c +++ b/src/input.c @@ -15,6 +15,7 @@ #include "escort.h" #include "land.h" #include "lstd.h" +#include "gui.h" #include "input.h" #define KEY_PRESS ( 1.) /**< Key is pressed. */ @@ -552,11 +553,11 @@ static void input_key(int keynum, double value, double kabs) { } /* Zoom in. */ else if(KEY("mapzoomin") && INGAME()) { - if(value == KEY_PRESS) player_setRadarRel(1); + if(value == KEY_PRESS) gui_setRadarRel(1); } /* Zoom out. */ else if(KEY("mapzoomout") && INGAME()) { - if(value == KEY_PRESS) player_setRadarRel(-1); + if(value == KEY_PRESS) gui_setRadarRel(-1); } /* Take a screenshot. */ else if(KEY("screenshot") && INGAME()) { diff --git a/src/lephisto.c b/src/lephisto.c index 4b58f0b..6d53c2c 100644 --- a/src/lephisto.c +++ b/src/lephisto.c @@ -52,6 +52,7 @@ #include "llua_misn.h" #include "lfile.h" #include "unidiff.h" +#include "gui.h" #include "nebulae.h" @@ -512,22 +513,23 @@ static void render_all(void) { double dt; dt = (paused) ? 0. : cur_dt; + /* Setup. */ spfx_start(dt); /* BG. */ space_render(dt); planets_render(); - player_renderBG(); + gui_renderBG(dt); weapons_render(WEAPON_LAYER_BG, dt); /* N. */ pilots_render(); weapons_render(WEAPON_LAYER_FG, dt); spfx_render(SPFX_LAYER_BACK); /* FG. */ - player_render(); + player_render(dt); spfx_render(SPFX_LAYER_FRONT); space_renderOverlay(dt); - player_renderGUI(dt); + gui_render(dt); display_fps(dt); /* Exception. */ } diff --git a/src/map.c b/src/map.c index a1d0bc5..8ff5bfe 100644 --- a/src/map.c +++ b/src/map.c @@ -9,6 +9,7 @@ #include "opengl.h" #include "mission.h" #include "colour.h" +#include "player.h" #include "map.h" #define MAP_WDWNAME "Star Map" /**< Map window name. */ @@ -37,9 +38,6 @@ static int map_drag = 0; /**< Is the user dragging the map? */ /* space.c */ extern StarSystem* systems_stack; extern int systems_nstack; -/* player.c */ -extern int planet_target; -extern int hyperspace_target; static void map_update(unsigned int wid); static int map_inPath(StarSystem* sys); diff --git a/src/nebulae.c b/src/nebulae.c index 1df4f9a..737f8c9 100644 --- a/src/nebulae.c +++ b/src/nebulae.c @@ -17,6 +17,7 @@ #include "menu.h" #include "player.h" #include "pause.h" +#include "gui.h" #include "perlin.h" #define NEBULAE_Z 16 /**< Z plane. */ @@ -26,7 +27,6 @@ #define NEBULAE_PUFF_BUFFER 300 /**< Nebulae buffer. */ /* Extern. */ -extern double gui_xoff, gui_yoff; /**< From player.c */ extern Vec2 shake_pos; /**< From spfx.c. */ extern void loadscreen_render(double done, const char* msg); /**< From lephisto.c. */ diff --git a/src/opengl.c b/src/opengl.c index 3f498bd..dea3609 100644 --- a/src/opengl.c +++ b/src/opengl.c @@ -40,6 +40,7 @@ #include "lephisto.h" #include "log.h" #include "ldata.h" +#include "gui.h" #include "opengl.h" /* Requirements. */ @@ -48,13 +49,6 @@ glInfo gl_screen; /**< Give data of current opengl settings. */ Vec2* gl_camera; /**< Camera we are using. */ -/* - * Used to adjust the pilots place onscreen to be in the middle - * even with the GUI. - */ -extern double gui_xoff; /**< GUI X offset. */ -extern double gui_yoff; /**< GUI Y offset. */ - /* Graphic list. */ /** * @brief Represents a node in the texture list. diff --git a/src/player.c b/src/player.c index e18774d..23f09e9 100644 --- a/src/player.c +++ b/src/player.c @@ -35,28 +35,17 @@ #include "perlin.h" #include "ai.h" #include "music.h" +#include "gui.h" #include "player.h" -#define XML_GUI_ID "GUIs" /**< XML section identifier for GUI document. */ -#define XML_GUI_TAG "gui" /**< XML section identifier for GUI tags. */ - #define XML_START_ID "Start" /**< Module start xml document identifier. */ -#define GUI_DATA "../dat/gui.xml" /**< Global GUI configuration file. */ -#define GUI_GFX "../gfx/gui/" /**< Location of the GUI graphics. */ - #define START_DATA "../dat/start.xml" /**< Module start information file. */ -#define TARGET_WIDTH 128 /**< Width of target graphics. */ -#define TARGET_HEIGHT 96 /**< Height of target graphics. */ - #define PLAYER_RESERVED_CHANNELS 6 /**< Number of channels to reserve for player sounds. */ #define PLAYER_ENGINE_CHANNEL 9 /**< Player channel for engine noises. */ #define PLAYER_GUI_CHANNEL 9 /**< Player channel. */ -#define INTERFERENCE_LAYERS 16 /**< Number of interference layers. */ -#define INTERFERENCE_CHANGE_DT 0.1 /**< Speed to change at. */ - /* Player stuff. */ Pilot* player = NULL; /**< The player. */ static Ship* player_ship = NULL; /**< Temp ship to hold when naming it. */ @@ -77,9 +66,9 @@ static int player_nlicenses = 0; /**< Number of licenses player has. */ /* * Player sounds. */ -static int snd_target = -1; -static int snd_jump = -1; -static int snd_nav = -1; +int snd_target = -1; /**< Sound when targetting. */ +int snd_jump = -1; /**< Sound when can jump. */ +int snd_nav = -1; /**< Sound when changing nav computer. */ /* Player pilot stack - Ships she owns. */ static Pilot** player_stack = NULL; /**< Stack of ships player has. */ @@ -103,131 +92,23 @@ int hyperspace_target = -1; /**< Target hyperspace route. -1 is none /* For death etc. */ static double player_timer = 0; /**< For death and such. */ static Vec2 player_cam; /**< Again, for death etc. */ -/* For interference. */ -static int interference_layer = 0; /**< Layer of the current interference. */ -double interference_alpha = 0.; /**< Alpha of the current interference layer. */ -static double interference_t = 0.; /**< Interference timer to control transitions. */ static int* missions_done = NULL; /**< Saves position of completed missions. */ static int missions_mdone = 0; /**< Memory size of completed missions. */ static int missions_ndone = 0; /**< Number of completed missions. */ -/* Pilot stuff for GUI. */ +/* Extern stuff for player ships. */ extern Pilot** pilot_stack; extern int pilot_nstack; -/* Space stuff for GUI. */ +/* Stuff for autonav etc. */ extern StarSystem* systems_stack; /* Map stuff for autonav. */ extern int map_npath; -/** - * @struct Radar - * - * @brief Represents the player's radar. - */ -typedef struct Radar_ { - double x; /**< X position. */ - double y; /**< Y position. */ - double w; /**< Width. */ - double h; /**< Height. */ - RadarShape shape; /**< Shape. */ - double res; /**< Resolution. */ - glTexture* interference[INTERFERENCE_LAYERS]; /**< Interference texture. */ -} Radar; - -/* Radar res. */ -#define RADAR_RES_MAX 100. /**< Max radar resolution. */ -#define RADAR_RES_MIN 10. /**< Min radar resolution. */ -#define RADAR_RES_INTERVAL 10. /**< Steps used to increase/decrease resolution. */ -#define RADAR_RES_DEFAULT 40. /**< Default resolution. */ - -/** - * @struct Rect - * - * @brief Represents a rectangle. - */ -typedef struct Rect_ { - double x; /**< X position. */ - double y; /**< Y position. */ - double w; /**< Width. */ - double h; /**< Height. */ -} Rect; - -/** - * @struct GUI - * - * @brief Represents the ingame player graphical user interface. - */ -typedef struct GUI_ { - /* Graphics. */ - glTexture* gfx_frame; /**< Frame of the GUI. */ - glTexture* gfx_targetPilot, *gfx_targetPlanet; /**< Graphics used to target planets. */ - - /* Rects. */ - - /* Radar. */ - Radar radar; /**< The radar. */ - /* Navigation. */ - Rect nav; /**< Navigation computer. */ - /* Health. */ - Rect shield; /**< Shield bar. */ - glTexture* gfx_shield; /**< Shield bar texture if applicable. */ - Rect armour; /**< Armour bar. */ - glTexture* gfx_armour; /**M Armour bar texture if applicable. */ - Rect energy; /**< Energy bar. */ - glTexture* gfx_energy; /**< Energy bar texture if applicable. */ - Rect fuel; /**< Fuel bar. */ - glTexture* gfx_fuel; /**< Fuel bar texture if applicable. */ - /* Weapon. */ - Rect weapon; /**< Weapon targeting system. */ - /* Targetting. */ - Rect target_health; /**< Target health. */ - Rect target_name; /**< Name of the target. */ - Rect target_faction; /**< Faction of the target. */ - /* Misc. */ - Rect misc; /**< Misc stuff: credits, cargo.. */ - /* Messages. */ - Rect msg; /**< Where messages go. */ - - /* Positions. */ - Vec2 frame; /**< Global frame position. */ - Vec2 target; /**< Global target position. */ -} GUI; - -static GUI gui = { - .gfx_frame = NULL, - .gfx_targetPilot = NULL, - .gfx_targetPlanet = NULL -}; /**< The GUI. */ - -/* Needed to render properly. */ -double gui_xoff = 0.; /**< X offset that GUI introduces. */ -double gui_yoff = 0.; /**< Y offset that GUI introduces. */ - -/* Messages. */ -#define MSG_SIZE_MAX 120 /**< Max message length. */ -double msg_timeout = 5.; /**< How long it takes for a message to timeout. */ -int msg_max = 5; /**< Max messages on screen. */ - -/** - * @struct Mesg - * - * @brief On screen player message. - */ -typedef struct Msg_ { - char str[MSG_SIZE_MAX]; /**< The message. */ - double t; /**< Time related to creation. */ -} Msg; -static Msg* msg_stack; /**< Stack of messages, will be of msg_max size. */ - /* External. */ extern void pilot_render(const Pilot* pilot); /* Extern is in Pilot.* */ -extern void weapon_minimap(const double res, const double w, const double h, - const RadarShape shape, double alpha); /* weapon.c */ -extern void planets_minimap(const double res, const double w, const double h, - const RadarShape shape, double alpha); /* space.c */ /* Internal. */ /* Creation. */ @@ -235,19 +116,6 @@ static int player_newMake(void); static void player_newShipMake(char* name); /* Sound. */ static void player_initSound(void); -/* Gui. */ -static void gui_createInterference(void); -static void rect_parseParam(const xmlNodePtr parent, char* name, double* param); -static void rect_parse(const xmlNodePtr parent, - double* x, double* y, double* w, double* h); -static int gui_parse(const xmlNodePtr parent, const char* name); -static void gui_cleanup(void); -/* Render GUI. */ -static void gui_renderMessages(double dt); -static void gui_renderPilot(const Pilot* p); -static void gui_renderHealth(const glColour* c, const Rect* r, - const glTexture* tex, const double w); -static void gui_renderInterference(double dt); /* Save/Load. */ static int player_saveShip(xmlTextWriterPtr writer, Pilot* ship, char* loc); static int player_parse(xmlNodePtr parent); @@ -280,8 +148,8 @@ void player_new(void) { vectnull(&player_cam); gl_bindCamera(&player_cam); - gui.radar.res = RADAR_RES_DEFAULT; - memset(msg_stack, 0, msg_max * sizeof(Msg)); + /* Set up GUI. */ + gui_setDefaults(); /* Setup sound. */ player_initSound(); @@ -623,9 +491,8 @@ void player_cleanup(void) { /* Clean up name. */ if(player_name != NULL) free(player_name); - /* Clean up messages. */ - for(i = 0; i < msg_max; i++) - memset(msg_stack[i].str, '\0', MSG_SIZE_MAX); + /* Clean up GUI. */ + gui_cleanup(); /* Clean up the stack. */ if(player_nstack > 0) { @@ -663,11 +530,6 @@ void player_cleanup(void) { pilot_free(player); player = NULL; } - - /* Clean up interference. */ - interference_alpha = 0.; - interference_layer = 0; - interference_t = 0.; } static int player_soundReserved = 0; /**< Has the player already reserved sound? */ @@ -706,31 +568,6 @@ void player_stopSound(void) { sound_stopGroup(PLAYER_ENGINE_CHANNEL); } -/** - * @brief Add a msg to the queue to be displayed on screen. - * @param fmt String with formatting like prinf. - */ -void player_message(const char* fmt, ...) { - va_list ap; - int i; - - if(fmt == NULL) return; /* Message not valid. */ - - /* Copy old messages back. */ - for(i = 1; i < msg_max; i++) { - if(msg_stack[msg_max-i-1].str[0] != '\0') { - strcpy(msg_stack[msg_max-i].str, msg_stack[msg_max-i-1].str); - msg_stack[msg_max-i].t = msg_stack[msg_max-i-1].t; - } - } - /* Add the new one. */ - va_start(ap, fmt); - vsprintf(msg_stack[0].str, fmt, ap); - va_end(ap); - - msg_stack[0].t = msg_timeout; -} - /** * @brief Warp the player to the new position. * @param x X value of the position to warp to. @@ -806,983 +643,29 @@ int player_cargoOwned(const char* commodityname) { return 0; } -/** - * @brief Render the background player stuff, namely planet target gfx. - */ -void player_renderBG(void) { - double x, y; - glColour* c; - Planet* planet; - - if(player_isFlag(PLAYER_DESTROYED) || player_isFlag(PLAYER_CREATING) || - pilot_isFlag(player, PLAYER_DESTROYED)) return; - - if(planet_target >= 0) { - planet = cur_system->planets[planet_target]; - - c = faction_getColour(planet->faction); - - x = planet->pos.x - planet->gfx_space->sw/2.; - y = planet->pos.y + planet->gfx_space->sh/2.; - gl_blitSprite(gui.gfx_targetPlanet, x, y, 0, 0, c); /* Top left. */ - - x += planet->gfx_space->sw; - gl_blitSprite(gui.gfx_targetPlanet, x, y, 1, 0, c); /* Top right. */ - - y -= planet->gfx_space->sh; - gl_blitSprite(gui.gfx_targetPlanet, x, y, 1, 1, c); /* Bottom right. */ - - x -= planet->gfx_space->sw; - gl_blitSprite(gui.gfx_targetPlanet, x, y, 0, 1, c); /* Bottom left. */ - } -} - /** * @brief Render the player. */ -void player_render(void) { - Pilot* p; - glColour* c; - double x, y; - - if((player != NULL) && !player_isFlag(PLAYER_CREATING)) { - /* Render the player target graphics. */ - if(player->target != PLAYER_ID) p = pilot_get(player->target); - else p = NULL; - if((p == NULL) || pilot_isFlag(p, PILOT_DEAD)) - player->target = PLAYER_ID; /* No more pilot target. */ - else { - /* There is still a pilot target. */ - if(pilot_isDisabled(p)) c = &cInert; - else if(pilot_isFlag(p, PILOT_BRIBED)) c = &cNeutral; - else if(pilot_isFlag(p, PILOT_HOSTILE)) c = &cHostile; - else c = faction_getColour(p->faction); - - x = p->solid->pos.x - p->ship->gfx_space->sw * PILOT_SIZE_APROX/2.; - y = p->solid->pos.y + p->ship->gfx_space->sh * PILOT_SIZE_APROX/2.; - gl_blitSprite(gui.gfx_targetPilot, x, y, 0, 0, c); /* Top left. */ - - x += p->ship->gfx_space->sw * PILOT_SIZE_APROX; - gl_blitSprite(gui.gfx_targetPilot, x, y, 1, 0, c); /* Top right. */ - - y -= p->ship->gfx_space->sh * PILOT_SIZE_APROX; - gl_blitSprite(gui.gfx_targetPilot, x, y, 1, 1, c); /* Bottom right. */ - - x -= p->ship->gfx_space->sw * PILOT_SIZE_APROX; - gl_blitSprite(gui.gfx_targetPilot, x, y, 0, 1, c); /* Bottom left. */ +void player_render(double dt) { + /* Check to see if the death menu should pop up. */ + if(player_isFlag(PLAYER_DESTROYED)) { + player_timer -= dt; + if(!toolkit && !player_isFlag(PLAYER_CREATING) && + (player_timer < 0.)) { + menu_death(); } + } + + /* Render the player. */ + if((player != NULL) && !player_isFlag(PLAYER_CREATING)) { + /* Render players target first. */ + gui_renderTarget(dt); /* Player is ontop of targeting graphic. */ pilot_render(player); } } -static int can_jump = 0; /**< Store whether or not the player is able to jump. */ -/** - * @brief Render the players GUI. - */ -void player_renderGUI(double dt) { - int i, j; - double x; - char str[10]; - Pilot* p; - glColour* c; - glFont* f; - StarSystem* sys; - int quantity, delay; - - /* If player is dead, just render the cinematic mode. */ - if(player_isFlag(PLAYER_DESTROYED) || player_isFlag(PLAYER_CREATING) || - ((player != NULL) && pilot_isFlag(player, PILOT_DEAD))) { - - if(player_isFlag(PLAYER_DESTROYED)) { - player_timer -= dt; - if(!toolkit && !player_isFlag(PLAYER_CREATING) && - (player_timer < 0.)) { - menu_death(); - } - } - /* Fancy cinematic scene borders. */ - spfx_cinematic(); - - return; - } - - if(player == NULL) return; - - /* Lockon warning. */ - if(player->lockons > 0) - gl_printMid(NULL, SCREEN_W - gui_xoff, 0., SCREEN_H-gl_defFont.h-25., - &cRed, "LOCKON DETECTED"); - - /* Volatile Environment. */ - if(cur_system->nebu_volatility > 0.) - gl_printMid(NULL, SCREEN_W - gui_xoff, 0., SCREEN_H-gl_defFont.h*2.-35., - &cRed, "VOLATILE ENVIRONMENT DETECTED"); - - /* GUI! */ - /* -- Frame. */ - gl_blitStatic(gui.gfx_frame, gui.frame.x, gui.frame.y, NULL); - /* -- Radar. */ - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - if(gui.radar.shape == RADAR_RECT) - glTranslated(gui.radar.x - SCREEN_W/2. + gui.radar.w/2., - gui.radar.y - SCREEN_H/2. - gui.radar.h/2., 0.); - else if(gui.radar.shape == RADAR_CIRCLE) - glTranslated(gui.radar.x - SCREEN_W/2., - gui.radar.y - SCREEN_H/2., 0.); - - /* Planets. */ - planets_minimap(gui.radar.res, gui.radar.w, gui.radar.h, - gui.radar.shape, 1.-interference_alpha); - - /* Weapons. */ - weapon_minimap(gui.radar.res, gui.radar.w, gui.radar.h, - gui.radar.shape, 1.-interference_alpha); - - /* Render the pilot_nstack. */ - j = 0; - for(i = 1; i < pilot_nstack; i++) { /* Skip the player. */ - if(pilot_stack[i]->id == player->target) - j = i; - else - gui_renderPilot(pilot_stack[i]); - } - /* Render the targetted pilot. */ - if(j != 0) gui_renderPilot(pilot_stack[j]); - - glPopMatrix(); - - /* Interference. */ - gui_renderInterference(dt); - - glPushMatrix(); - if(gui.radar.shape == RADAR_RECT) - glTranslated(gui.radar.x - SCREEN_W/2. + gui.radar.w/2., - gui.radar.y - SCREEN_H/2. - gui.radar.h/2., 0.); - else if(gui.radar.shape == RADAR_CIRCLE) - glTranslated(gui.radar.x - SCREEN_W/2., - gui.radar.y - SCREEN_H/2., 0.); - - /* The + sign in the middle of the radar represents the player. */ - glBegin(GL_LINES); - COLOUR(cRadar_player); - glVertex2d( 0., -3.); - glVertex2d( 0., 3.); - glVertex2d(-3., 0.); - glVertex2d( 3., 0.); - glEnd(); - - glPopMatrix(); /* GL_PROJECTION. */ - - /* Nav. */ - if(planet_target >= 0) { - /* Planet landing target. */ - gl_printMid(NULL, (int)gui.nav.w, - gui.nav.x, gui.nav.y - 5, &cConsole, "Land"); - - gl_printMid(&gl_smallFont, (int)gui.nav.w, gui.nav.x, - gui.nav.y - 10 - gl_smallFont.h, NULL, "%s", - cur_system->planets[planet_target]->name); - } - else if(hyperspace_target >= 0) { - /* Hyperspace target. */ - sys = &systems_stack[cur_system->jumps[hyperspace_target]]; - - /* Determine if we have to play the "enter hyperspace range" sound. */ - i = space_canHyperspace(player); - if((i != 0) && (i != can_jump)) - if(!pilot_isFlag(player, PILOT_HYPERSPACE)) - player_playSound(snd_jump, 1); - can_jump = i; - - /* Determine the colour of the NAV text. */ - if(can_jump || pilot_isFlag(player, PILOT_HYPERSPACE) || - pilot_isFlag(player, PILOT_HYP_PREP) || - pilot_isFlag(player, PILOT_HYP_BEGIN)) - c = &cConsole; - else c = NULL; - gl_printMid(NULL, (int)gui.nav.w, gui.nav.x, gui.nav.y - 5, - c, "Hyperspace"); - - gl_printMid(&gl_smallFont, (int)gui.nav.w, gui.nav.x, - gui.nav.y - 10 - gl_smallFont.h, - NULL, "%d - %s", pilot_getJumps(player), - (sys_isKnown(sys)) ? sys->name : "Unknown"); - } - else { - /* No NAV target. */ - gl_printMid(NULL, (int)gui.nav.w, gui.nav.x, - gui.nav.y - 5, &cConsole, "Navigation"); - gl_printMid(&gl_smallFont, (int)gui.nav.w, gui.nav.x, - gui.nav.y - 10 - gl_smallFont.h, &cGrey, "Off"); - } - - /* Health */ - gui_renderHealth(&cShield, &gui.shield, gui.gfx_shield, player->shield / player->shield_max); - gui_renderHealth(&cArmour, &gui.armour, gui.gfx_armour, player->armour / player->armour_max); - gui_renderHealth(&cEnergy, &gui.energy, gui.gfx_energy, player->energy / player->energy_max); - gui_renderHealth(&cFuel, &gui.fuel, gui.gfx_fuel, player->fuel / player->fuel_max); - - /* Weapon. */ - if(player->secondary == NULL) { - gl_printMid(NULL, (int)gui.weapon.w, gui.weapon.x, - gui.weapon.y - 5, &cConsole, "Secondary"); - gl_printMid(&gl_smallFont, (int)gui.weapon.w, gui.weapon.x, - gui.weapon.y - 10 - gl_defFont.h, &cGrey, "None"); - } else { - f = &gl_defFont; - - quantity = pilot_oquantity(player, player->secondary); - delay = outfit_delay(player->secondary->outfit); - - /* Check to see if weapon is ready. */ - if(player->secondary->timer > 0.) - c = &cGrey; - else - c = &cConsole; - - /* Launcher. */ - if(player->ammo != NULL) { - /* Use the ammunitions name. */ - i = gl_printWidth(f, "%s", outfit_ammo(player->secondary->outfit)->name); - if(i > gui.weapon.w) - /* Font is too big. */ - f = &gl_smallFont; - - /* Weapon name. */ - gl_printMid(f, (int)gui.weapon.w, gui.weapon.x, - gui.weapon.y - 5, - (player->ammo) ? c : &cGrey, "%s", - outfit_ammo(player->secondary->outfit)->name); - - /* Print ammo underneath to the left. */ - gl_printMid(&gl_smallFont, (int)gui.weapon.w, gui.weapon.x, - gui.weapon.y - 10 - gl_defFont.h, - NULL, "%d", (player->ammo) ? player->ammo->quantity : 0); - /* Other. */ - } else { - /* Just print the item name. */ - i = gl_printWidth(f, "%s", player->secondary->outfit->name); - if(i > (int)gui.weapon.w) - /* Font is too big. */ - f = &gl_smallFont; - gl_printMid(f, (int)gui.weapon.w, - gui.weapon.x, gui.weapon.y - (gui.weapon.h - f->h)/2., - c, "%s", player->secondary->outfit->name); - } - } - - /* Target. */ - if(player->target != PLAYER_ID) { - p = pilot_get(player->target); - - /* Blit the pilot target. */ - gl_blitStatic(p->ship->gfx_target, gui.target.x, gui.target.y, NULL); - /* Blit the pilot space image. */ - /*x = gui.target.x + (TARGET_WIDTH - p->ship->gfx_space->sw)/2.; - y = gui.target.y + (TARGET_HEIGHT - p->ship->gfx_space->sh)/2.; - gl_blitStaticSprite(p->ship->gfx_space, - x, y, p->tsx, p->tsy, NULL);*/ - - /* Target name. */ - gl_print(NULL, gui.target_name.x, gui.target_name.y, - NULL, "%s", p->name); - gl_print(&gl_smallFont, gui.target_faction.x, gui.target_faction.y, - NULL, "%s", faction_name(p->faction)); - - /* Target status. */ - if(pilot_isDisabled(p)) - /* Disable the pilot. */ - gl_print(&gl_smallFont, gui.target_health.x, gui.target_health.y, - NULL, "Disabled"); - else if(p->shield > p->shield_max / 100.) - /* On shields. */ - gl_print(&gl_smallFont, gui.target_health.x, gui.target_health.y, - NULL, "%s: %.0f%%", "Shield", p->shield/p->shield_max*100.); - else - /* On armour. */ - gl_print(&gl_smallFont, gui.target_health.x, gui.target_health.y, - NULL, "%s: %.0f%%", "Armor", p->armour/p->armour_max*100.); - } else { - /* No target. */ - gl_printMid(NULL, SHIP_TARGET_W, gui.target.x, - gui.target.y + (SHIP_TARGET_H - gl_defFont.h)/2., - &cGrey80, "No Target"); - } - - /* Misc. */ - j = gui.misc.y - 8 - gl_smallFont.h; - gl_print(NULL, gui.misc.x + 8, j, - &cConsole, "SCreds:"); - - credits2str(str, player->credits, 2); - - i = gl_printWidth(&gl_smallFont, str); - gl_print(&gl_smallFont, gui.misc.x + gui.misc.w - 8 - i, j, - NULL, str); - - /* Cargo and co. */ - if(player->ncommodities > 0) { - j -= gl_smallFont.h + 5; - gl_print(&gl_smallFont, - gui.misc.x + 8, j, &cConsole, "Cargo"); - - for(i = 0; i < MIN(player->ncommodities,3); i++) { - j -= gl_smallFont.h + 3; - if(player->commodities[i].quantity) /* Quantity is over. */ - gl_printMax(&gl_smallFont, gui.misc.w - 15, - gui.misc.x + 13, j, - NULL, "%d %s%s", player->commodities[i].quantity, - player->commodities[i].commodity->name, - (player->commodities[i].id) ? "*" : ""); - else /* Basically for weightless mission stuff. */ - gl_printMax(&gl_smallFont, gui.misc.w - 15, - gui.misc.x + 13, j, - NULL, "%s%s", player->commodities[i].commodity->name, - (player->commodities[i].id) ? "*" : ""); - } - } - - j -= gl_smallFont.h + 5; - gl_print(&gl_smallFont, - gui.misc.x + 8, j, &cConsole, "Free:"); - - i = gl_printWidth(&gl_smallFont, "%d", pilot_cargoFree(player)); - gl_print(&gl_smallFont, - gui.misc.x + gui.misc.w - 8 - i, j, - NULL, "%d", pilot_cargoFree(player)); - - /* Messages. */ - gui_renderMessages(dt); - - /* Hyperspace FLASH BANG!!! */ - if(pilot_isFlag(player, PILOT_HYPERSPACE) && - (player->ptimer < HYPERSPACE_FADEOUT)) { - if(i < j) { - x = (HYPERSPACE_FADEOUT-player->ptimer) / HYPERSPACE_FADEOUT; - glColor4d(1.,1.,1., x); /* We'll | I'll, make this more effiecent later. */ - glBegin(GL_QUADS); - glVertex2d(-SCREEN_W/2., -SCREEN_H/2.); - glVertex2d(-SCREEN_W/2., SCREEN_H/2.); - glVertex2d( SCREEN_W/2., SCREEN_H/2.); - glVertex2d( SCREEN_W/2., -SCREEN_H/2.); - glEnd(); - } - } -} - -/** - * @brief Renders the players messages on screen. - * @param dt Current delta tick. - */ -static void gui_renderMessages(double dt) { - double x, y; - glColour c; - int i; - - x = gui.msg.x; - y = gui.msg.y + (double)(gl_defFont.h*msg_max)*1.2; - c.r = c.g = c.b = 1.; - - for(i = msg_max-1; i >= 0; i--) { - y -= (double)gl_defFont.h*1.2; - - /* Only handle non-NULL messages. */ - if(msg_stack[i].str[0] != '\0') { - /* Decrement timer. */ - msg_stack[i].t -= dt; - - /* Set to NULL if timer is up. */ - if(msg_stack[i].t < 0.) - msg_stack[i]..str[0] = '\0'; - - /* Draw with variable alpha. */ - else { - if(msg_stack[i].t - msg_timeout/2 < 0.) - c.a = msg_stack[i].t / (msg_timeout/2.); - else - c.a = 1.; - gl_print(NULL, x, y, &c, "%s", msg_stack[i].str); - } - } - } -} - -/** - * @brief Render interference if needed. - * @param dt Current deltatick. - */ -static void gui_renderInterference(double dt) { - glColour c; - glTexture* tex; - int t; - - /* Must be displaying inteference. */ - if(interference_alpha <= 0.) - return; - - /* Calculate frame to draw. */ - interference_t += dt; - if(interference_t > INTERFERENCE_CHANGE_DT) { /* Time to change. */ - t = RNG(0, INTERFERENCE_LAYERS-1); - if(t != interference_layer) - interference_layer = t; - else - interference_layer = (interference_layer == INTERFERENCE_LAYERS-1) ? - 0 : interference_layer+1; - interference_t -= INTERFERENCE_CHANGE_DT; - } - - /* Render the interference. */ - c.r = c.g = c.b = 1.; - c.a = interference_alpha; - tex = gui.radar.interference[interference_layer]; - if(gui.radar.shape == RADAR_CIRCLE) - gl_blitStatic(tex, - gui.radar.x - gui.radar.w, - gui.radar.y - gui.radar.w, &c); - else if(gui.radar.shape == RADAR_RECT) - gl_blitStatic(tex, - gui.radar.x - gui.radar.w / 2, - gui.radar.y - gui.radar.h/2, &c); -} - -/* Renders a pilot. */ -static void gui_renderPilot(const Pilot* p) { - int x, y, sx, sy; - double w, h; - glColour* col; - double a; - - /* Get position. */ - x = (p->solid->pos.x - player->solid->pos.x) / gui.radar.res; - y = (p->solid->pos.y - player->solid->pos.y) / gui.radar.res; - /* Get size. */ - sx = PILOT_SIZE_APROX/2. * p->ship->gfx_space->sw / gui.radar.res; - sy = PILOT_SIZE_APROX/2. * p->ship->gfx_space->sh / gui.radar.res; - if(sx < 1.) sx = 1.; - if(sy < 1.) sy = 1.; - - /* Check if pilot in range. */ - if(((gui.radar.shape == RADAR_RECT) && ((ABS(x) > gui.radar.w/2.+sx) - || (ABS(y) > gui.radar.h/2.+sy))) || - ((gui.radar.shape == RADAR_CIRCLE) && - ((x*x + y*y) > (int)(gui.radar.w*gui.radar.w)))) { - - /* Draw a little targetted symbol. */ - if(p->id == player->target) { - /* Circle radars have it easy. */ - if(gui.radar.shape == RADAR_CIRCLE) { - /* We'll create a line. */ - a = ANGLE(x, y); - x = gui.radar.w*cos(a); - y = gui.radar.w*sin(a); - sx = 0.85 * x; - sy = 0.85 * y; - - COLOUR(cRadar_tPilot); - glBegin(GL_LINES); - glVertex2d(x, y); - glVertex2d(sx, sy); - glEnd(); - } - } - return; - } - - if(gui.radar.shape == RADAR_RECT) { - w = gui.radar.w/2.; - h = gui.radar.h/2.; - } - else if(gui.radar.shape == RADAR_CIRCLE) { - w = gui.radar.w; - h = gui.radar.w; - } - - glBegin(GL_QUADS); - /* Colours. */ - if(p->id == player->target) col = &cRadar_tPilot; - else if(pilot_isDisabled(p)) col = &cInert; - else if(pilot_isFlag(p, PILOT_BRIBED)) col = &cNeutral; - else if(pilot_isFlag(p, PILOT_HOSTILE)) col = &cHostile; - else col = faction_getColour(p->faction); - ACOLOUR(*col, 1-interference_alpha); /**< Makes it much harder to see. */ - - /* Image. */ - glVertex2d(MAX(x-sx, -w), MIN(y+sy, h)); /* Top left. */ - glVertex2d(MIN(x+sx, w), MIN(y+sy, h)); /* Top right. */ - glVertex2d(MIN(x+sx, w), MAX(y-sy, -h)); /* Bottom right. */ - glVertex2d(MAX(x-sx, -w), MAX(y-sy, -h)); /* Bottom left. */ - glEnd(); -} - -/** - * @brief Render a health bar. - */ -static void gui_renderHealth(const glColour* c, const Rect* r, const glTexture* tex, - const double w) { - - double x, y, sx, sy, tx, ty; - - /* Set colour. */ - COLOUR(*c); - - /* Just create a bar. */ - if(tex == NULL) { - /* Set the position values. */ - x = r->x - SCREEN_W/2.; - y = r->y - SCREEN_H/2.; - sx = w * r->w; - sy = r->h; - - glBegin(GL_QUADS); - glVertex2d(x, y); - glVertex2d(x+sx, y); - glVertex2d(x+sx, y-sy); - glVertex2d(x, y-sy); - glEnd(); - } else { /* Render the texture. */ - /* Set the position values. */ - x = r->x - SCREEN_W/2.; - y = r->y - SCREEN_H/2. + tex->sh; - sx = w * tex->sw; - sy = tex->sh; - tx = tex->sw / tex->rw; - ty = tex->sh / tex->rh; - - /* Draw the image. */ - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, tex->texture); - glBegin(GL_QUADS); - COLOUR(*c); - glTexCoord2d(0., ty); - glVertex2d(x, y); - - glTexCoord2d(w*tx, ty); - glVertex2d(x + sx, y); - - glTexCoord2d(w*tx, 0.); - glVertex2d(x+sx, y - sy); - - glTexCoord2d(0., 0.); - glVertex2d(x, y - sy); - glEnd(); - glDisable(GL_TEXTURE_2D); - } -} - -/** - * @brief Initialize the GUI system. - * @return 0 on success. - */ -int gui_init(void) { - /* Set graphics to NULL. */ - memset(&gui, 0, sizeof(GUI)); - - /* -- Radar. */ - gui.radar.res = RADAR_RES_DEFAULT; - /* -- messages. */ - gui.msg.x = 20; - gui.msg.y = 30; - msg_stack = calloc(msg_max, sizeof(Msg)); - - if(msg_stack == NULL) { - ERR("Out of memory!"); - return -1; - } - - return 0; -} - -/** - * @brief Attempt to load the actual GUI. - * @brief name Name of the GUI to load. - * @brief 0 on success. - */ -int gui_load(const char* name) { - uint32_t bufsize; - char* buf = ldata_read(GUI_DATA, &bufsize); - char* tmp; - int found = 0; - - xmlNodePtr node; - xmlDocPtr doc = xmlParseMemory(buf, bufsize); - - node = doc->xmlChildrenNode; - if(!xml_isNode(node, XML_GUI_ID)) { - ERR("Malformed '"GUI_DATA"' file: missing root element '"XML_GUI_ID"'"); - return -1; - } - - node = node->xmlChildrenNode; /* First system node. */ - if(node == NULL) { - ERR("Malformed '"GUI_DATA"' file: does not contain elements"); - return -1; - } - - do { - if(xml_isNode(node, XML_GUI_TAG)) { - tmp = xml_nodeProp(node, "name"); /* Mallocs. */ - - /* Is this the gui we are looking for? */ - if(strcmp(tmp, name)==0) { - found = 1; - - /* Parse the xml node. */ - if(gui_parse(node, name)) WARN("Trouble loading GUI '%s'", name); - free(tmp); - break; - } - free(tmp); - } - } while((node = node->next)); - - xmlFreeDoc(doc); - free(buf); - - if(!found) { - WARN("GUI '%s' not found in '"GUI_DATA"'",name); - return -1; - } - return 0; -} - -/** - * @brief Parse a parameter of the rect node. - */ -static void rect_parseParam(const xmlNodePtr parent, char* name, double* param) { - char* buf; - - /* Get the attribute. */ - xmlr_attr(parent, name, buf); - - /* Wants attribute. */ - if(param != NULL) { - if(buf == NULL) - WARN("Node '%s' missing 'x' parameter.", parent->name); - else if(buf != NULL) - *param = atoi(buf); - } - /* Doesn't want it. */ - else if(buf != NULL) - WARN("Node '%s' has superfluous 'x' parameter.", parent->name); - - /* Clean up. */ - if(buf != NULL) - free(buf); -} - -/** - * @brief Used to pull out a rect from an xml node. - */ -static void rect_parse(const xmlNodePtr parent, - double* x, double* y, double* w, double* h) { - rect_parseParam(parent, "w", w); - rect_parseParam(parent, "h", h); - rect_parseParam(parent, "x", x); - rect_parseParam(parent, "y", y); -} - -/** - * @brief Create the interference map for the current gui. - */ -static void gui_createInterference(void) { - uint8_t raw; - int i, j, k; - float* map; - uint32_t* pix; - SDL_Surface* sur; - int w, h, hw, hh; - float c; - int r; - - /* Dimension shortcuts. */ - if(gui.radar.shape == RADAR_CIRCLE) { - w = gui.radar.w*2.; - h = w; - } - else if(gui.radar.shape == RADAR_RECT) { - w = gui.radar.w; - h = gui.radar.h; - } - - for(k = 0; k < INTERFERENCE_LAYERS; k++) { - /* Free the old texture. */ - if(gui.radar.interference[k] != NULL) - gl_freeTexture(gui.radar.interference[k]); - - /* Create the temp surface. */ - sur = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, RGBAMASK); - pix = sur->pixels; - - /* Load the interference map. */ - map = noise_genRadarInt(w, h, 100.); - - /* Create the texture. */ - SDL_LockSurface(sur); - if(gui.radar.shape == RADAR_CIRCLE) { - r = pow2((int)gui.radar.w); - hw = w/2; - hh = h/2; - for(i = 0; i < h; i++) { - for(j = 0; j < w; j++) { - /* Must be in circle. */ - if(pow2(i-hh) + pow2(j-hw) > r) - continue; - c = map[i*w + j]; - raw = 0xff & (uint8_t)((float)0xff * c); - memset(&pix[i*w + j], raw, sizeof(uint32_t)); - pix[i*w + j] |= AMASK; - } - } - } - else if(gui.radar.shape == RADAR_RECT) { - for(i = 0; i < h*w; i++) { - /* Process pixels. */ - c = map[i]; - raw = 0xff & (uint8_t)((float)0xff * c); - memset(&pix[i], raw, sizeof(uint32_t)); - pix[i] |= AMASK; - } - } - SDL_UnlockSurface(sur); - - /* Set the interference. */ - gui.radar.interference[k] = gl_loadImage(sur); - - /* Clean up. */ - free(map); - } -} - -#define RELATIVIZE(a) \ -{ (a).x += VX(gui.frame); (a).y = VY(gui.frame) + gui.gfx_frame->h-(a).y; } -/**< Converts a rect to absolute coords. */ - -/** - * @brief Parses a gui node. - * @param parent Node to parse from. - * @param name Name of the GUI to load. - */ -static int gui_parse(const xmlNodePtr parent, const char* name) { - xmlNodePtr cur, node; - char* tmp, buf[PATH_MAX]; - - /* Clean up. */ - gui_cleanup(); - - /* Gfx. */ - /* Set a property and not a node because it must be loaded first. */ - tmp = xml_nodeProp(parent, "gfx"); - if(tmp == NULL) { - ERR("GUI '%s' has no gfx property", name); - return -1; - } - - /* Load gfx. */ - /* Frame. */ - snprintf(buf, PATH_MAX, GUI_GFX"%s.png", tmp); - gui.gfx_frame = gl_newImage(buf, 0); - /* Pilot. */ - snprintf(buf, PATH_MAX, GUI_GFX"%s_pilot.png", tmp); - gui.gfx_targetPilot = gl_newSprite(buf, 2, 2, 0); - /* Planet. */ - snprintf(buf, PATH_MAX, GUI_GFX"%s_planet.png", tmp); - gui.gfx_targetPlanet = gl_newSprite(buf, 2, 2, 0); - free(tmp); - - /* Frame (based on gfx). */ - vect_csetmin(&gui.frame, - SCREEN_W - gui.gfx_frame->w, /* x. */ - SCREEN_H - gui.gfx_frame->h); /* h. */ - - /* Let's parse the data now. */ - node = parent->children; - do { - /* Offset. */ - if(xml_isNode(node, "offset")) - rect_parse(node, &gui_xoff, &gui_yoff, NULL, NULL); - /* Radar. */ - else if(xml_isNode(node, "radar")) { - tmp = xml_nodeProp(node,"type"); - /* Make sure type is valid. */ - if(strcmp(tmp, "rectangle")==0) gui.radar.shape = RADAR_RECT; - else if(strcmp(tmp, "circle")==0) gui.radar.shape = RADAR_CIRCLE; - else { - WARN("Radar for GUI '%s' is missing 'type' tag or has invalid 'type' tag", name); - gui.radar.shape = RADAR_RECT; - } - free(tmp); - - /* Load the appropriate measurements. */ - if(gui.radar.shape == RADAR_RECT) - rect_parse(node, &gui.radar.x, &gui.radar.y, &gui.radar.w, &gui.radar.h); - else if(gui.radar.shape == RADAR_CIRCLE) - rect_parse(node, &gui.radar.x, &gui.radar.y, &gui.radar.w, NULL); - RELATIVIZE(gui.radar); - } - /* Nav computer. */ - else if(xml_isNode(node, "nav")) { - rect_parse(node, &gui.nav.x, &gui.nav.y, &gui.nav.w, &gui.nav.h); - RELATIVIZE(gui.nav); - gui.nav.y -= gl_defFont.h; - } - /* Health bars. */ - else if(xml_isNode(node, "health")) { - cur = node->children; - do { - if(xml_isNode(cur, "shield")) { - rect_parse(cur, &gui.shield.x, &gui.shield.y, - &gui.shield.w, &gui.shield.h); - tmp = xml_get(cur); - if(tmp != NULL) { - snprintf(buf, PATH_MAX, GUI_GFX"%s.png", tmp); - gui.gfx_shield = gl_newImage(buf, 0); - } - RELATIVIZE(gui.shield); - } - - if(xml_isNode(cur, "armour")) { - rect_parse(cur, &gui.armour.x, &gui.armour.y, - &gui.armour.w, &gui.armour.h); - tmp = xml_get(cur); - if(tmp != NULL) { - snprintf(buf, PATH_MAX, GUI_GFX"%s.png", tmp); - gui.gfx_armour = gl_newImage(buf, 0); - } - RELATIVIZE(gui.armour); - } - - if(xml_isNode(cur, "energy")) { - rect_parse(cur, &gui.energy.x, &gui.energy.y, - &gui.energy.w, &gui.energy.h); - tmp = xml_get(cur); - if(tmp != NULL) { - snprintf(buf, PATH_MAX, GUI_GFX"%s.png", tmp); - gui.gfx_energy = gl_newImage(buf, 0); - } - RELATIVIZE(gui.energy); - } - if(xml_isNode(cur, "fuel")) { - rect_parse(cur, &gui.fuel.x, &gui.fuel.y, - &gui.fuel.w, &gui.fuel.h); - tmp = xml_get(cur); - if(tmp != NULL) { - snprintf(buf, PATH_MAX, GUI_GFX"%s.png", tmp); - gui.gfx_fuel = gl_newImage(buf, 0); - } - RELATIVIZE(gui.fuel); - } - } while((cur = cur->next)); - } - /* Secondary weapon. */ - else if(xml_isNode(node, "weapon")) { - rect_parse(node, &gui.weapon.x, &gui.weapon.y, - &gui.weapon.w, &gui.weapon.h); - RELATIVIZE(gui.weapon); - gui.weapon.y -= gl_defFont.h; - } - /* Target. */ - else if(xml_isNode(node, "target")) { - cur = node->children; - do { - if(xml_isNode(cur, "gfx")) { - rect_parse(cur, &gui.target.x, &gui.target.y, NULL, NULL); - RELATIVIZE(gui.target); - gui.target.y -= SHIP_TARGET_H; - } - - if(xml_isNode(cur, "name")) { - rect_parse(cur, &gui.target_name.x, &gui.target_name.y, NULL, NULL); - RELATIVIZE(gui.target_name); - gui.target_name.y -= gl_defFont.h; - } - - if(xml_isNode(cur, "faction")) { - rect_parse(cur, &gui.target_faction.x, &gui.target_faction.y, - NULL, NULL); - RELATIVIZE(gui.target_faction); - gui.target_faction.y -= gl_smallFont.h; - } - - if(xml_isNode(cur, "health")) { - rect_parse(cur, &gui.target_health.x, &gui.target_health.y, - NULL, NULL); - RELATIVIZE(gui.target_health); - gui.target_health.y -= gl_smallFont.h; - } - } while((cur = cur->next)); - } else if(xml_isNode(node, "misc")) { - rect_parse(node, &gui.misc.x, &gui.misc.y, &gui.misc.w, &gui.misc.h); - RELATIVIZE(gui.misc); - } - } while(xml_nextNode(node)); - - /* Some postprocessing. */ - gui_createInterference(); - - return 0; -} -#undef RELATIVIZE - -/** - * @brief Clean up the GUI. - */ -static void gui_cleanup(void) { - int i; - - /* Free textures. */ - if(gui.gfx_frame != NULL) { - gl_freeTexture(gui.gfx_frame); - gui.gfx_frame = NULL; - } - if(gui.gfx_targetPilot != NULL) { - gl_freeTexture(gui.gfx_targetPilot); - gui.gfx_targetPilot = NULL; - } - if(gui.gfx_targetPlanet != NULL) { - gl_freeTexture(gui.gfx_targetPlanet); - gui.gfx_targetPlanet = NULL; - } - /* Health textures. */ - if(gui.gfx_shield != NULL) { - gl_freeTexture(gui.gfx_shield); - gui.gfx_shield = NULL; - } - if(gui.gfx_armour != NULL) { - gl_freeTexture(gui.gfx_armour); - gui.gfx_armour = NULL; - } - if(gui.gfx_energy != NULL) { - gl_freeTexture(gui.gfx_energy); - gui.gfx_energy = NULL; - } - if(gui.gfx_fuel != NULL) { - gl_freeTexture(gui.gfx_fuel); - gui.gfx_fuel = NULL; - } - - for(i = 0; i < INTERFERENCE_LAYERS; i++) { - if(gui.radar.interference[i] != NULL) { - gl_freeTexture(gui.radar.interference[i]); - gui.radar.interference[i] = NULL; - } - } -} - -/** - * @brief Free the GUI. - */ -void gui_free(void) { - /* Clean up gui. */ - gui_cleanup(); - - /* Free messages. */ - free(msg_stack); -} - /** * @brief Start autonav. */ @@ -1950,18 +833,6 @@ void player_think(Pilot* pplayer) { /* * For use in keybindings. */ -/** - * @brief Modify the radar resolution. - * @param mod Number of intervals to jump (up or down). - */ - -void player_setRadarRel(int mod) { - gui.radar.res += mod * RADAR_RES_INTERVAL; - if(gui.radar.res > RADAR_RES_MAX) gui.radar.res = RADAR_RES_MAX; - else if(gui.radar.res < RADAR_RES_MIN) gui.radar.res = RADAR_RES_MIN; - - player_message("Radar set to %dx.", (int)gui.radar.res); -} /** * @brief Get the next secondary weapon. diff --git a/src/player.h b/src/player.h index 7cb1fe2..257955a 100644 --- a/src/player.h +++ b/src/player.h @@ -28,10 +28,14 @@ extern unsigned int player_flags; /**< Player's flags. */ extern double player_crating; /**< Player's combat rating. */ extern int player_enemies; /**< Amount of enemies player has. */ -/* Enums. */ +/* Targetting. */ +extern int planet_target; /**< Targetted planet. -1 is none. */ +extern int hyperspace_target; /**< Targetted hyperspace route. -1 is none. */ -/* For render functions. */ -typedef enum RadarShape_ { RADAR_RECT, RADAR_CIRCLE } RadarShape; /**< Player's radar shape. */ +/* Common player sounds. */ +extern int snd_target; /**< Sound when targetting. */ +extern int snd_jump; /**< Sound when can jump. */ +extern int snd_nav; /**< Sound when changing nav computer. */ /* Creation/Cleanup. */ void player_new(void); @@ -41,11 +45,7 @@ void player_cleanup(void); int gui_load(const char* name); /* Render. */ -int gui_init(void); -void gui_free(void); -void player_render(void); -void player_renderBG(void); /* Render BG layer. */ -void player_renderGUI(double dt); /* Render the GUI stuff. */ +void player_render(double dt); /* Misc. */ void player_message(const char* fmt, ...); @@ -85,7 +85,6 @@ void player_targetHostile(void); void player_targetNext(void); void player_targetPrev(void); void player_targetNearest(void); -void player_setRadarRel(int mod); void player_secondaryNext(void); void player_secondaryPrev(void); void player_targetPlanet(void); diff --git a/src/space.c b/src/space.c index 0f12bb1..5f809c4 100644 --- a/src/space.c +++ b/src/space.c @@ -16,6 +16,7 @@ #include "ltime.h" #include "nebulae.h" #include "music.h" +#include "gui.h" #include "space.h" #define XML_PLANET_ID "Planets" @@ -80,12 +81,10 @@ static int nstars = 0; /* Total stars. */ static int mstars = 0; /* Memory stars are taking. */ /* Interference. */ -extern double interference_alpha; /* player.c */ +extern double interference_alpha; /* gui.c */ static double interference_target; /**< Target alpha level .*/ static double interference_timer = 0.; /**< Interference timer. */ -extern int planet_target; /* player.c */ - /* Intern. */ /* Planet load. */ static int planet_parse(Planet* planet, const xmlNodePtr parent); @@ -98,8 +97,6 @@ static void system_setFaction(StarSystem* sys); static void space_renderStars(const double dt); static void space_addFleet(Fleet* fleet, int init); static PlanetClass planetclass_get(const char a); -/* Extern. */ -extern void player_message(const char* fmt, ...); void planets_minimap(const double res, const double w, const double h, const RadarShape shape, double alpha); diff --git a/src/weapon.c b/src/weapon.c index 96d7143..fac3872 100644 --- a/src/weapon.c +++ b/src/weapon.c @@ -22,6 +22,7 @@ #include "spfx.h" #include "opengl.h" #include "explosion.h" +#include "gui.h" #include "weapon.h" #define weapon_isSmart(w) (w->think != NULL) /**< Checks if the weapon w is smart. */