#include #include "main.h" #include "pilot.h" #include "log.h" #include "opengl.h" #include "player.h" #define POW2(x) ((x)*(x)) #define GFX_GUI_FRAME "../gfx/gui/frame.png" #define GFX_GUI_TARG_PILOT "../gfx/gui/pilot.png" #define GFX_GUI_TARG_PLANET "../gfx/gui/planet.png" #define KEY_PRESS 1. #define KEY_RELEASE -1. // Keybind structure. typedef struct { char* name; // Keybinding name, taken from keybindNames[] KeybindType type; // type, defined in player.h. unsigned int key; // Key/axis/button event number. double reverse; // 1. if normal, -1 if reversed, only useful for joystick axis. } Keybind; static Keybind** player_input; // Contains the players keybindings. // Name of each keybinding. const char* keybindNames[] = { "accel", "left", "right", "primary", "target", "mapzoomin", "mapzoomout" }; // Player stuff. Pilot* player = NULL; // extern in pilot.h static double player_turn = 0.; // Turn velocity from input. static double player_acc = 0.; // Accel velocity from input. static int player_primary = 0; // Player is shooting primary weapon. static unsigned int player_target = 0; // Targetted pilot. // Pilot stuff for GUI. extern Pilot** pilot_stack; extern int pilots; // Weapon stuff for GUI. extern void weapon_minimap(double res, double w, double h); extern void planets_minimap(double res, double w, double h); // GUI crap. // Need these offsets to render properly. -- Used in opengl.c // -- Colors. typedef struct { double r, g, b, a; } Color; #define COLOR(x) glColor4d((x).r, (x).g, (x).b, (x).a) // Factions. Color cRadar_player = { .r = 0.4, .g = 0.8, .b = 0.9, .a = 1. }; Color cRadar_inert = { .r = 0.6, .g = 0.6, .b = 0.6, .a = 1. }; Color cRadar_neut = { .r = 0.9, .g = 1.0, .b = 0.3, .a = 1. }; Color cRadar_friend = { .r = 0.0, .g = 1.0, .b = 0.0, .a = 1. }; Color cRadar_targ = { .r = 0.0, .g = 0.7, .b = 1.0, .a = 1. }; Color cRadar_weap = { .r = 0.8, .g = 0.2, .b = 0.2, .a = 1. }; // Bars. Color cShield = { .r = 0.2, .g = 0.2, .b = 0.8, .a = 1. }; Color cArmor = { .r = 0.5, .g = 0.5, .b = 0.5, .a = 1. }; Color cEnergy = { .r = 0.2, .g = 0.8, .b = 0.2, .a = 1. }; typedef enum { RADAR_RECT, RADAR_CIRCLE } RadarShape; typedef struct { double w,h; // Dimensions. RadarShape shape; double res; // Resolution. } Radar; // Radar res. #define RADAR_RES_MAX 100. #define RADAR_RES_MIN 10. #define RADAR_RES_INTERVAL 10. #define RADAR_RES_DEFAULT 40. typedef struct { double w,h; } Rect; typedef struct { // graphics. gl_texture* gfx_frame; gl_texture* gfx_targetPilot, *gfx_targetPlanet; Radar radar; Rect shield, armor, energy; // Positions. Vec2 pos_frame; Vec2 pos_radar; Vec2 pos_shield, pos_armor, pos_energy; Vec2 pos_target, pos_target_health; Vec2 pos_msg; } GUI; GUI gui; // Le Gui! double gui_xoff = 0.; double gui_yoff = 0.; // Messages. #define MSG_SIZE_MAX 80 int msg_timeout = 5000; int msg_max = 5; // Max messages on screen. typedef struct { char str[MSG_SIZE_MAX]; unsigned int t; } Msg; static Msg* msg_stack; extern void pilot_render(Pilot* pilot); // Extern is in Pilot.* void player_message(const char* fmt, ...) { va_list ap; int i; if(fmt == NULL) return; // Message not valid. 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; } va_start(ap, fmt); vsprintf(msg_stack[0].str, fmt, ap); va_end(ap); msg_stack[0].t = SDL_GetTicks() + msg_timeout; } // Render the player. void player_render(void) { int i; double x, y, sx, sy; Pilot* p; Vec2 v; // Render the player target graphics. if(player_target) { p = get_pilot(player_target); vect_csetmin(&v, VX(p->solid->pos) - p->ship->gfx_space->sw * PILOT_SIZE_APROX/2., VY(p->solid->pos) + p->ship->gfx_space->sh * PILOT_SIZE_APROX/2.); gl_blitSprite(gui.gfx_targetPilot, &v, 0, 0); // Top left. VX(v) += p->ship->gfx_space->sw * PILOT_SIZE_APROX; gl_blitSprite(gui.gfx_targetPilot, &v, 1, 0); // Top right. VY(v) -= p->ship->gfx_space->sh * PILOT_SIZE_APROX; gl_blitSprite(gui.gfx_targetPilot, &v, 1, 1); // Bottom right. VX(v) -= p->ship->gfx_space->sw * PILOT_SIZE_APROX; gl_blitSprite(gui.gfx_targetPilot, &v, 0, 1); // Bottom left. } // Render the player. pilot_render(player); // GUI! // -- Frame. gl_blitStatic(gui.gfx_frame, &gui.pos_frame); // -- Radar. glMatrixMode(GL_PROJECTION); glPushMatrix(); glTranslated(VX(gui.pos_radar) - gl_screen.w/2. + gui.radar.w/2., VY(gui.pos_radar) - gl_screen.h/2. - gui.radar.h/2., 0.); switch(gui.radar.shape) { case RADAR_RECT: // Planets. COLOR(cRadar_friend); planets_minimap(gui.radar.res, gui.radar.w, gui.radar.h); // Weapons. glBegin(GL_POINTS); COLOR(cRadar_weap); weapon_minimap(gui.radar.res, gui.radar.w, gui.radar.h); glEnd(); // Put end to those points. for(i = 1; i < pilots; i++) { p = pilot_stack[i]; x = (p->solid->pos.x - player->solid->pos.x) / gui.radar.res; y = (p->solid->pos.y - player->solid->pos.y) / gui.radar.res; 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.; if((ABS(x) > gui.radar.w / 2+sx) || (ABS(y) > gui.radar.h / 2. + sy)) continue; // Pilot isn't in range. glBegin(GL_QUADS); // Colors. if(p->id == player_target) COLOR(cRadar_targ); else COLOR(cRadar_neut); glVertex2d(MAX(x-sx, -gui.radar.w/2.), MIN(y+sx, gui.radar.w/2.)); // top left. glVertex2d(MIN(x+sx, gui.radar.w/2.), MIN(y+sy, gui.radar.h/2.)); // Top right. glVertex2d(MIN(x+sx, gui.radar.w/2.), MAX(y-sy, -gui.radar.h/2.)); // Bottom right. glVertex2d(MAX(x-sx, -gui.radar.w/2.), MAX(y-sy, -gui.radar.h/2.)); // Bottom left. glEnd(); // The Quads. } glBegin(GL_POINTS); // For ze player. break; case RADAR_CIRCLE: glBegin(GL_POINTS); for(i = 1; i < pilots; i++) { p = pilot_stack[i]; COLOR(cRadar_neut); glVertex2d((p->solid->pos.x - player->solid->pos.x) / gui.radar.res, (p->solid->pos.y - player->solid->pos.y) / gui.radar.res); } break; } // Player. -- Drawn last. COLOR(cRadar_player); glVertex2d( 0., 2. ); // We represent the player with a small '+' glVertex2d( 0., 1. ); glVertex2d( 0., 0. ); glVertex2d( 0., -1. ); glVertex2d( 0., -2. ); glVertex2d( 2., 0. ); glVertex2d( 1., 0. ); glVertex2d( -1., 0. ); glVertex2d( -2., 0. ); glEnd(); glPopMatrix(); // GL_PROJECTION. // Health. glBegin(GL_QUADS); // Shield. COLOR(cShield); x = VX(gui.pos_shield) - gl_screen.w/2.; y = VY(gui.pos_shield) - gl_screen.h/2.; sx = player->shield / player->shield_max * gui.shield.w; sy = gui.shield.h; glVertex2d(x, y); glVertex2d(x+sx, y); glVertex2d(x+sx, y-sy); glVertex2d(x, y-sy); glEnd(); glBegin(GL_QUADS); // Armor. COLOR(cArmor); x = VX(gui.pos_armor) - gl_screen.w/2.; y = VY(gui.pos_armor) - gl_screen.h/2.; sx = player->armor / player->armor_max * gui.armor.w; sy = gui.armor.h; glVertex2d(x, y); glVertex2d(x+sx, y); glVertex2d(x+sx, y-sy); glVertex2d(x, y-sy); glEnd(); glBegin(GL_QUADS); // Energy. COLOR(cEnergy); x = VX(gui.pos_energy) - gl_screen.w/2.; y = VY(gui.pos_energy) - gl_screen.h/2.; sx = player->energy / player->energy_max * gui.energy.w; sy = gui.energy.h; glVertex2d(x, y); glVertex2d(x+sx, y); glVertex2d(x+sx, y-sy); glVertex2d(x, y-sy); glEnd(); // Target. if(player_target) { gl_blitStatic(p->ship->gfx_target, &gui.pos_target); if(p->armor < p->armor_max * PILOT_DISABLED) // Disable the pilot. gl_print(NULL, &gui.pos_target_health, "Disabled"); else if(p->shield > p->shield_max / 100.) // On shields. gl_print(NULL, &gui.pos_target_health, "%s: %.0f%%", "Shield", p->shield/p->shield_max*100.); else // On armor. gl_print(NULL, &gui.pos_target_health, "%s: %.0f%%", "Armor", p->armor/p->armor_max*100.); } // Messages. VX(v) = VX(gui.pos_msg); VY(v) = VY(gui.pos_msg) + (double)(gl_defFont.h*msg_max)*1.2; for(i = 0; i < msg_max; i++) { VY(v) -= (double)gl_defFont.h*1.2; if(msg_stack[msg_max-i-1].str[0] != '\0') { if(msg_stack[msg_max-i-1].t < SDL_GetTicks()) msg_stack[msg_max-i-1].str[0] = '\0'; else gl_print(NULL, &v, "%s", msg_stack[msg_max-i-1].str); } } } // Init GUI. int gui_init(void) { // -- Targeting. gui.gfx_targetPilot = gl_newSprite(GFX_GUI_TARG_PILOT, 2, 2); gui.gfx_targetPlanet = gl_newSprite(GFX_GUI_TARG_PLANET, 2, 2); // -- Frame. gui.gfx_frame = gl_newImage(GFX_GUI_FRAME); vect_csetmin(&gui.pos_frame, gl_screen.w - gui.gfx_frame->w, // x. gl_screen.h - gui.gfx_frame->h); // y. gui_xoff = -gui.gfx_frame->w/2.; // Offset is only horizontal and on the right side. // -- Radar. gui.radar.res = RADAR_RES_DEFAULT; gui.radar.w = 128.; gui.radar.h = 128.; gui.radar.shape = RADAR_RECT; //RADAR_CIRCLE; vect_csetmin(&gui.pos_radar, VX(gui.pos_frame) + 11, // x VY(gui.pos_frame) + gui.gfx_frame->h - 10); // y. // -- Bars. gui.shield.w = gui.armor.w = gui.energy.w = 128; gui.shield.h = gui.armor.h = gui.energy.h = 10; vect_csetmin(&gui.pos_shield, VX(gui.pos_frame) + 10, // x VY(gui.pos_frame) + gui.gfx_frame->h - 201); // y. vect_csetmin(&gui.pos_armor, VX(gui.pos_frame) + 10, // x VY(gui.pos_frame) + gui.gfx_frame->h - 218); // y. vect_csetmin(&gui.pos_energy, VX(gui.pos_frame) + 10, // x VY(gui.pos_frame) + gui.gfx_frame->h - 236); // y. // Target. vect_csetmin(&gui.pos_target, VX(gui.pos_frame) + 10, VY(gui.pos_frame) + gui.gfx_frame->h - 256 - SHIP_TARGET_H); vect_csetmin(&gui.pos_target_health, VX(gui.pos_frame) + 10 + 10, VY(gui.pos_frame) + gui.gfx_frame->h - 256 - SHIP_TARGET_H + 10); // Message system. vect_csetmin(&gui.pos_msg, 20, 30); msg_stack = calloc(msg_max, sizeof(Msg)); return 0; } // Free the GUI. void gui_free(void) { gl_freeTexture(gui.gfx_frame); gl_freeTexture(gui.gfx_targetPilot); gl_freeTexture(gui.gfx_targetPlanet); free(msg_stack); } // Used in pilot.c // Basically uses keyboard input instead of AI input. void player_think(Pilot* player) { player->solid->dir_vel = 0.; if(player_turn) player->solid->dir_vel -= player->ship->turn * player_turn; if(player_primary) pilot_shoot(player, 0); vect_pset(&player->solid->force, player->ship->thrust * player_acc, player->solid->dir); } // ================ // INPUT! // ================ // Initialization/exit functions (does not assign keys). void input_init(void) { Keybind* tmp; int i; for(i = 0; keybindNames[i]; i++); // Get number of bindings. player_input = malloc(i*sizeof(Keybind*)); // Create a null keybinding for each. for(i = 0; keybindNames[i]; i++) { tmp = MALLOC_L(Keybind); tmp->name = (char*)keybindNames[i]; tmp->type = KEYBIND_NULL; tmp->key = 0; tmp->reverse = 1.; player_input[i] = tmp; } } void input_exit(void) { int i; for(i = 0; keybindNames[i]; i++) free(player_input[i]); free(player_input); } // Binds key of type [type] to action keybind. void input_setKeybind(char* keybind, KeybindType type, int key, int reverse) { int i; for(i = 0; keybindNames[i]; i++) { if(strcmp(keybind, player_input[i]->name)==0) { player_input[i]->type = type; player_input[i]->key = key; player_input[i]->reverse = reverse ? -1. : 1.; return; } } } // == Run input method. ================================================ // keynum : Index of the player_input keybind. // value : Value of keypress (defined above). // abs : Whether or not it's an abs value (For those pesky joysticks. // ===================================================================== static void input_key(int keynum, double value, int abs) { if(strcmp(player_input[keynum]->name, "accel")==0) { if(abs)player_acc = value; else player_acc += value; } else if(strcmp(player_input[keynum]->name, "left")==0) { if(abs)player_turn = -value; else player_turn -= value; } else if(strcmp(player_input[keynum]->name, "right")==0) { if(abs) player_turn = value; else player_turn += value; } // Shoot primary weapon. BOOM BOOM. else if(strcmp(player_input[keynum]->name, "primary")==0) { if(value == KEY_PRESS) player_primary = 1; else if(value == KEY_RELEASE) player_primary = 0; } else if(strcmp(player_input[keynum]->name, "target")==0) { if(value == KEY_PRESS) player_target = pilot_getNext(player_target); } else if(strcmp(player_input[keynum]->name, "mapzoomin")==0) { if(value == KEY_PRESS && gui.radar.res < RADAR_RES_MAX) gui.radar.res += RADAR_RES_INTERVAL; } else if(strcmp(player_input[keynum]->name, "mapzoomout")==0) { if(value == KEY_PRESS && gui.radar.res > RADAR_RES_MIN) gui.radar.res -= RADAR_RES_INTERVAL; } //Make sure values are sane. player_acc = ABS(player_acc); if(player_acc > 1.) player_acc = 1.; if(player_turn > 1.) player_turn = 1.; else if(player_turn < -1.) player_turn = -1.; } // --Events-- static void input_joyaxis(const unsigned int axis, const int value); static void input_joydown(const unsigned int button); static void input_joyup(const unsigned int button); static void input_keydown(SDLKey key); static void input_keyup(SDLKey key); // Joystick. // Axis. static void input_joyaxis(const unsigned int axis, const int value) { int i; for(i = 0; keybindNames[i]; i++) if(player_input[i]->type == KEYBIND_JAXIS && player_input[i]->key == axis) { input_key(i, -(player_input[i]->reverse) * (double)value / 32767., 1); return; } } // Joystick button down. static void input_joydown(const unsigned int button) { int i; for(i = 0; keybindNames[i]; i++) if(player_input[i]->type == KEYBIND_JBUTTON && player_input[i]->key == button) { input_key(i, KEY_RELEASE, 0); return; } } // Joystick button up. static void input_joyup(const unsigned int button) { int i; for(i = 0; keybindNames[i]; i++) if(player_input[i]->type == KEYBIND_JBUTTON && player_input[i]->key == button) { input_key(i, KEY_RELEASE, 0); return; } } // Keyboard. // Key down. static void input_keydown(SDLKey key) { int i; for(i = 0; keybindNames[i]; i++) if(player_input[i]->type == KEYBIND_KEYBOARD && player_input[i]->key == key) { input_key(i, KEY_PRESS, 0); return; } // Fire Escape. SDL_Event quit; if(key == SDLK_ESCAPE) { quit.type = SDL_QUIT; SDL_PushEvent(&quit); } } // Key up. static void input_keyup(SDLKey key) { int i; for(i = 0; keybindNames[i]; i++) if(player_input[i]->type == KEYBIND_KEYBOARD && player_input[i]->key == key) { input_key(i, KEY_RELEASE, 0); return; } } // Global input. // Just seperates the event types. void input_handle(SDL_Event* event) { switch(event->type) { case SDL_JOYAXISMOTION: input_joyaxis(event->jaxis.axis, event->jaxis.value); break; case SDL_JOYBUTTONDOWN: input_joydown(event->jbutton.button); break; case SDL_JOYBUTTONUP: input_joyup(event->jbutton.button); break; case SDL_KEYDOWN: input_keydown(event->key.keysym.sym); break; case SDL_KEYUP: input_keyup(event->key.keysym.sym); break; } }