Lephisto/src/player.c

853 lines
26 KiB
C

#include <malloc.h>
#include "main.h"
#include "pilot.h"
#include "log.h"
#include "opengl.h"
#include "pack.h"
#include "xml.h"
#include "player.h"
#define XML_GUI_ID "GUIs" // XML section identifier.
#define XML_GUI_TAG "gui"
#define GUI_DATA "../dat/gui.xml"
#define GUI_GFX "../gfx/gui/"
#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",
"target_nearest", "mapzoomin", "mapzoomout", "end" }; // Need to terminate in "end".
// Player stuff.
Pilot* player = NULL; // extern in pilot.h
unsigned int credits = 0;
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 int player_secondary = 0; // layer is shooting secondary weapon.
static unsigned int player_target = PLAYER_ID; // Targetted pilot.
static int planet_target = -1; // Targetted planet.
static int weapon = -1; // Secondary weapon is in use.
// Pilot stuff for GUI.
extern Pilot** pilot_stack;
extern int pilots;
// GUI crap.
// -- Colors.
// Standard colors.
glColor cConsole = { .r = 0.5, .g = 0.8, .b = 0.5, .a = 1. };
glColor cInert = { .r = 0.6, .g = 0.6, .b = 0.6, .a = 1. };
glColor cNeutral = { .r = 0.9, .g = 1.0, .b = 0.3, .a = 1. };
glColor cFriend = { .r = 0.0, .g = 1.0, .b = 0.0, .a = 1. };
glColor cHostile = { .r = 0.9, .g = 0.2, .b = 0.2, .a = 1. };
glColor cRadar_player = { .r = 0.4, .g = 0.8, .b = 0.4, .a = 1. };
glColor cRadar_targ = { .r = 0.0, .g = 0.7, .b = 1.0, .a = 1. };
glColor cRadar_weap = { .r = 0.8, .g = 0.2, .b = 0.2, .a = 1. };
// Bars.
glColor cShield = { .r = 0.2, .g = 0.2, .b = 0.8, .a = 1. };
glColor cArmor = { .r = 0.5, .g = 0.5, .b = 0.5, .a = 1. };
glColor cEnergy = { .r = 0.2, .g = 0.8, .b = 0.2, .a = 1. };
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_font smallFont;
gl_texture* gfx_frame;
gl_texture* gfx_targetPilot, *gfx_targetPlanet;
Radar radar;
Rect nav;
Rect shield, armor, energy;
Rect weapon;
Rect misc;
// Positions.
Vec2 pos_frame;
Vec2 pos_radar;
Vec2 pos_nav;
Vec2 pos_shield, pos_armor, pos_energy;
Vec2 pos_weapon;
Vec2 pos_target, pos_target_health, pos_target_name, pos_target_faction;
Vec2 pos_misc;
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;
// 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); // weapon.c
extern void planets_minimap(const double res, const double w, const double h, const RadarShape shape); // space.c
// Internal.
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_renderPilot(const Pilot* p);
static void gui_renderBar(const glColor* c, const Vec2* p, const Rect* r, const double w);
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 = SDL_GetTicks() + msg_timeout;
}
// Render the player.
void player_render(void) {
int i, j;
char str[10];
Pilot* p;
Vec2 v;
glColor* c;
// Render the player target graphics.
if(player_target != PLAYER_ID) {
p = pilot_get(player_target);
if(pilot_isFlag(p, PILOT_HOSTILE)) c = &cHostile;
else c = &cNeutral;
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, c); // Top left.
VX(v) += p->ship->gfx_space->sw * PILOT_SIZE_APROX;
gl_blitSprite(gui.gfx_targetPilot, &v, 1, 0, c); // Top right.
VY(v) -= p->ship->gfx_space->sh * PILOT_SIZE_APROX;
gl_blitSprite(gui.gfx_targetPilot, &v, 1, 1, c); // Bottom right.
VX(v) -= p->ship->gfx_space->sw * PILOT_SIZE_APROX;
gl_blitSprite(gui.gfx_targetPilot, &v, 0, 1, c); // Bottom left.
}
// Render the player.
pilot_render(player);
// GUI!
// -- Frame.
gl_blitStatic(gui.gfx_frame, &gui.pos_frame, NULL);
// -- Radar.
glMatrixMode(GL_PROJECTION);
glPushMatrix();
if(gui.radar.shape == RADAR_RECT)
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.);
else if(gui.radar.shape == RADAR_CIRCLE)
glTranslated(VX(gui.pos_radar) - gl_screen.w/2.,
VY(gui.pos_radar) - gl_screen.h/2., 0.);
// Planets.
COLOR(cFriend);
planets_minimap(gui.radar.res, gui.radar.w, gui.radar.h, gui.radar.shape);
// Weapons.
glBegin(GL_POINTS);
COLOR(cRadar_weap);
weapon_minimap(gui.radar.res, gui.radar.w, gui.radar.h, gui.radar.shape);
glEnd();
// Render the pilots.
for(j = 0, i = 1; i < pilots; 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]);
// Player.
glBegin(GL_POINTS);
// 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.
// Nav.
if(planet_target != -1) {
} else {
i = gl_printWidth(NULL, "NAV");
vect_csetmin(&v, VX(gui.pos_nav) + (gui.nav.w - i)/2., VY(gui.pos_nav) - 5);
gl_print(NULL, &v, &cConsole, "NAV");
i = gl_printWidth(&gui.smallFont, "No Target");
vect_csetmin(&v, VX(gui.pos_nav) + (gui.nav.w - i)/2., VY(gui.pos_nav) - 10 - gui.smallFont.h);
gl_print(&gui.smallFont, &v, &cGrey, "No Target");
}
// Health
gui_renderBar(&cShield, &gui.pos_shield, &gui.shield, player->shield / player->shield_max);
gui_renderBar(&cArmor, &gui.pos_armor, &gui.armor, player->armor / player->armor_max);
gui_renderBar(&cEnergy, &gui.pos_energy, &gui.energy, player->energy / player->energy_max);
// Weapon.
if(weapon == -1) {
i = gl_printWidth(NULL, "Secondary");
vect_csetmin(&v, VX(gui.pos_weapon) + (gui.weapon.w - i)/2., VY(gui.pos_weapon) - 5);
gl_print(NULL, &v, &cConsole, "Secondary");
i = gl_printWidth(&gui.smallFont, "None");
vect_csetmin(&v, VX(gui.pos_weapon) + (gui.weapon.w - i)/2., VY(gui.pos_weapon) - 10 - gl_defFont.h);
gl_print(&gui.smallFont, &v, &cGrey, "None");
} else {
}
// Target.
if(player_target != PLAYER_ID) {
p = pilot_get(player_target);
gl_blitStatic(p->ship->gfx_target, &gui.pos_target, NULL);
// Target name.
gl_print(NULL, &gui.pos_target_name, NULL, "%s", p->name);
gl_print(&gui.smallFont, &gui.pos_target_faction, NULL, "%s", p->faction->name);
// Target status.
if(p->armor < p->armor_max * PILOT_DISABLED)
// Disable the pilot.
gl_print(&gui.smallFont, &gui.pos_target_health, NULL, "Disabled");
else if(p->shield > p->shield_max / 100.)
// On shields.
gl_print(&gui.smallFont, &gui.pos_target_health, NULL, "%s: %.0f%%", "Shield", p->shield/p->shield_max*100.);
else
// On armor.
gl_print(&gui.smallFont, &gui.pos_target_health, NULL, "%s: %.0f%%", "Armor", p->armor/p->armor_max*100.);
} else {
// No target.
i = gl_printWidth(NULL, "No Target");
vect_csetmin(&v, VX(gui.pos_target) + (SHIP_TARGET_W - i)/2., VY(gui.pos_target) +
(SHIP_TARGET_H - gl_defFont.h)/2.);
gl_print(NULL, &v, &cGrey, "No Target");
}
// Misc.
vect_csetmin(&v, VX(gui.pos_misc) + 10, VY(gui.pos_misc) - 10 - gl_defFont.h);
gl_print(NULL, &v, &cConsole, "SCred:");
if(credits >= 1000000)
snprintf(str, 10, "%.2fM", (double)credits / 1000000.);
else if(credits >= 1000)
snprintf(str, 10, "%.2fK", (double) credits / 1000.);
else
snprintf(str, 10, "%d", credits);
i = gl_printWidth(&gui.smallFont, "%s", str);
vect_csetmin(&v, VX(gui.pos_misc) + gui.misc.w - 10 - i, VY(gui.pos_misc) - 10 - gl_defFont.h);
gl_print(&gui.smallFont, &v, NULL, "%s", str);
// Messages.
vect_csetmin(&v, VX(gui.pos_msg), 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, NULL, "%s", msg_stack[msg_max-i-1].str);
}
}
}
// Renders a pilot.
static void gui_renderPilot(const Pilot* p) {
int x, y, sx, sy;
double w, h;
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(((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))))
return; // Pilot isn't in range.
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);
// Colors.
if(p->id == player_target) COLOR(cRadar_targ);
else if(pilot_isFlag(p, PILOT_HOSTILE)) COLOR(cHostile);
else COLOR(cNeutral);
// 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();
}
// Render a bar.
static void gui_renderBar(const glColor* c, const Vec2* p, const Rect* r, const double w) {
int x, y, sx, sy;
glBegin(GL_QUADS);
COLOR(*c);
x = VX(*p) - gl_screen.w/2.;
y = VY(*p) - gl_screen.h/2.;
sx = w * r->w;
sy = r->h;
glVertex2d(x, y);
glVertex2d(x+sx, y);
glVertex2d(x+sx, y-sy);
glVertex2d(x, y-sy);
glEnd();
}
// Init GUI.
int gui_init(void) {
// Set graphics to NULL.
gui.gfx_frame = NULL;
gui.gfx_targetPilot = NULL;
gui.gfx_targetPlanet = NULL;
// Font.
gl_fontInit(&gui.smallFont, NULL, 10);
// -- Radar.
gui.radar.res = RADAR_RES_DEFAULT;
// -- messages.
vect_csetmin(&gui.pos_msg, 20,30);
msg_stack = calloc(msg_max, sizeof(Msg));
return 0;
}
// Attempts to load the actual gui.
int gui_load(const char* name) {
uint32_t bufsize;
char* buf = pack_readfile(DATA, 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);
xmlCleanupParser();
if(!found) {
WARN("GUI '%s' not found in '"GUI_DATA"'",name);
return -1;
}
return 0;
}
static void rect_parse(const xmlNodePtr parent, double* x, double* y, double* w, double* h) {
xmlNodePtr cur;
int param;
param = 0;
cur = parent->children;
do {
if(xml_isNode(cur, "x")) {
if(x != NULL) {
*x = (double)atoi((char*)cur->children->content);
param |= (1<<0);
} else
WARN("Extra parameter 'x' found for GUI node '%s'", parent->name);
}
else if(xml_isNode(cur, "y")) {
if(y != NULL) {
*y = (double)atoi((char*)cur->children->content);
param |= (1<<1);
} else
WARN("Extra parameter 'y' found for GUI node '%s'", parent->name);
}
else if(xml_isNode(cur, "w")) {
if(w != NULL) {
*w = (double)atoi((char*)cur->children->content);
param |= (1<<2);
} else
WARN("Extra parameter 'w' found for GUI node '%s'", parent->name);
}
else if(xml_isNode(cur, "h")) {
if(h != NULL) {
*h = (double)atoi((char*)cur->children->content);
param |= (1<<3);
} else
WARN("Extra parameter 'h' found for GUI node '%s'", parent->name);
}
} while((cur = cur->next));
// Check to see if we got everything we asked for.
if(x && !(param & (1<<0)))
WARN("Missing parameter 'x' for GUI node '%s'", parent->name);
else if(y && !(param & (1<<1)))
WARN("Missing parameter 'y' for GUI node '%s'", parent->name);
else if(w && !(param & (1<<2)))
WARN("Missing parameter 'w' for GUI node '%s'", parent->name);
else if(h && !(param & (1<<3)))
WARN("Missing parameter 'h' for GUI node '%s'", parent->name);
}
// Parse a gui node.
static int gui_parse(const xmlNodePtr parent, const char* name) {
xmlNodePtr cur, node;
double x, y;
char* tmp, *tmp2;
// Gfx.
// Set a property and not a node because it must be loaded first.
tmp2 = xml_nodeProp(parent, "gfx");
if(tmp2 == NULL) {
ERR("GUI '%s' has no gfx property", name);
return -1;
}
// Load gfx.
tmp = malloc((strlen(tmp2)+strlen(GUI_GFX)+12) * sizeof(char));
// Frame.
snprintf(tmp, strlen(tmp2)+strlen(GUI_GFX)+5, GUI_GFX"%s.png", tmp2);
if(gui.gfx_frame) gl_freeTexture(gui.gfx_frame); // Free if needed.
gui.gfx_frame = gl_newImage(tmp);
// Pilot.
snprintf(tmp, strlen(tmp2)+strlen(GUI_GFX)+11, GUI_GFX"%s_pilot.png", tmp2);
if(gui.gfx_targetPilot) gl_freeTexture(gui.gfx_targetPilot); // Free if needed.
gui.gfx_targetPilot = gl_newSprite(tmp, 2, 2);
// Planet.
snprintf(tmp, strlen(tmp2)+strlen(GUI_GFX)+12, GUI_GFX"%s_planet.png", tmp2);
if(gui.gfx_targetPlanet) gl_freeTexture(gui.gfx_targetPlanet); // Free if needed.
gui.gfx_targetPlanet = gl_newSprite(tmp, 2, 2);
free(tmp);
free(tmp2);
// Frame (based on gfx).
vect_csetmin(&gui.pos_frame,
gl_screen.w - gui.gfx_frame->w, // x.
gl_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, &x, &y, NULL, NULL);
gui_xoff = x;
gui_yoff = y;
}
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, &x, &y, &gui.radar.w, &gui.radar.h);
else if(gui.radar.shape == RADAR_CIRCLE)
rect_parse(node, &x, &y, &gui.radar.w, NULL);
vect_csetmin(&gui.pos_radar,
VX(gui.pos_frame) + x,
VY(gui.pos_frame) + gui.gfx_frame->h - y);
}
// Nav computer.
else if(xml_isNode(node, "nav")) {
rect_parse(node, &x, &y, &gui.nav.w, &gui.nav.h);
vect_csetmin(&gui.pos_nav,
VX(gui.pos_frame) + x,
VY(gui.pos_frame) + gui.gfx_frame->h - y - gl_defFont.h);
}
// Health bars.
else if(xml_isNode(node, "health")) {
cur = node->children;
do {
if(xml_isNode(cur, "shield")) {
rect_parse(cur, &x, &y, &gui.shield.w, &gui.shield.h);
vect_csetmin(&gui.pos_shield,
VX(gui.pos_frame) + x,
VY(gui.pos_frame) + gui.gfx_frame->h - y);
}
if(xml_isNode(cur, "armor")) {
rect_parse(cur, &x, &y, &gui.armor.w, &gui.armor.h);
vect_csetmin(&gui.pos_armor,
VX(gui.pos_frame) + x,
VY(gui.pos_frame) + gui.gfx_frame->h - y);
}
if(xml_isNode(cur, "energy")) {
rect_parse(cur, &x, &y, &gui.energy.w, &gui.energy.h);
vect_csetmin(&gui.pos_energy,
VX(gui.pos_frame) + x,
VY(gui.pos_frame) + gui.gfx_frame->h - y);
}
} while((cur = cur->next));
}
// Secondary weapon.
else if(xml_isNode(node, "weapon")) {
rect_parse(node, &x, &y, &gui.weapon.w, &gui.weapon.h);
vect_csetmin(&gui.pos_weapon,
VX(gui.pos_frame) + x,
VY(gui.pos_frame) + gui.gfx_frame->h - y - gl_defFont.h);
}
// Target.
else if(xml_isNode(node, "target")) {
cur = node->children;
do {
if(xml_isNode(cur, "gfx")) {
rect_parse(cur, &x, &y, NULL, NULL);
vect_csetmin(&gui.pos_target,
VX(gui.pos_frame) + x,
VY(gui.pos_frame) + gui.gfx_frame->h - y - SHIP_TARGET_H);
}
if(xml_isNode(cur, "name")) {
rect_parse(cur, &x, &y, NULL, NULL);
vect_csetmin(&gui.pos_target_name,
VX(gui.pos_frame) + x,
VY(gui.pos_frame) + gui.gfx_frame->h - y - gl_defFont.h);
}
if(xml_isNode(cur, "faction")) {
rect_parse(cur, &x, &y, NULL, NULL);
vect_csetmin(&gui.pos_target_faction,
VX(gui.pos_frame) + x,
VY(gui.pos_frame) + gui.gfx_frame->h - y - gui.smallFont.h);
}
if(xml_isNode(cur, "health")) {
rect_parse(cur, &x, &y, NULL, NULL);
vect_csetmin(&gui.pos_target_health,
VX(gui.pos_frame) + x,
VY(gui.pos_frame) + gui.gfx_frame->h - y - gui.smallFont.h);
}
} while((cur = cur->next));
} else if(xml_isNode(node, "misc")) {
rect_parse(node, &x, &y, &gui.misc.w, &gui.misc.h);
vect_csetmin(&gui.pos_misc, VX(gui.pos_frame) + x, VY(gui.pos_frame) + gui.gfx_frame->h - y);
}
} while((node = node->next));
return 0;
}
// Free the GUI.
void gui_free(void) {
gl_freeFont(&gui.smallFont);
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!
// ================
// Set the default input keys.
void input_setDefault(void) {
input_setKeybind("accel", KEYBIND_KEYBOARD, SDLK_w, 0);
input_setKeybind("left", KEYBIND_KEYBOARD, SDLK_a, 0);
input_setKeybind("right", KEYBIND_KEYBOARD, SDLK_d, 0);
input_setKeybind("primary", KEYBIND_KEYBOARD, SDLK_SPACE, 0);
input_setKeybind("target", KEYBIND_KEYBOARD, SDLK_TAB, 0);
input_setKeybind("target_nearest", KEYBIND_KEYBOARD, SDLK_r, 0);
input_setKeybind("mapzoomin", KEYBIND_KEYBOARD, SDLK_UP, 0);
input_setKeybind("mapzoomout", KEYBIND_KEYBOARD, SDLK_DOWN, 0);
}
// Initialization/exit functions (does not assign keys).
void input_init(void) {
Keybind* tmp;
int i;
for(i = 0; strcmp(keybindNames[i], "end"); i++); // Get number of bindings.
player_input = malloc(i*sizeof(Keybind*));
// Create a null keybinding for each.
for(i = 0; strcmp(keybindNames[i], "end"); 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; strcmp(keybindNames[i], "end"); 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; strcmp(keybindNames[i], "end"); 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;
}
WARN("Unable to set keybind '%s', That command does not exist.", keybind);
}
// == 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;
}
// Targetting.
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, "target_nearest")==0) {
if(value == KEY_PRESS) player_target = pilot_getHostile();
}
// Zoom in.
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;
}
// Zoom out.
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; strcmp(keybindNames[i], "end"); 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; strcmp(keybindNames[i], "end");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; strcmp(keybindNames[i], "end"); 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; strcmp(keybindNames[i], "end"); 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; strcmp(keybindNames[i], "end"); 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;
}
}