2254 lines
62 KiB
C
2254 lines
62 KiB
C
/**
|
|
* @file player.c
|
|
*
|
|
* @brief Contains all the player related stuff.
|
|
*/
|
|
|
|
#include <malloc.h>
|
|
|
|
#include "lephisto.h"
|
|
#include "pilot.h"
|
|
#include "log.h"
|
|
#include "opengl.h"
|
|
#include "font.h"
|
|
#include "pack.h"
|
|
#include "xml.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 "misn_lua.h"
|
|
#include "ltime.h"
|
|
#include "hook.h"
|
|
#include "map.h"
|
|
#include "lfile.h"
|
|
#include "spfx.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. */
|
|
|
|
/* Player stuff. */
|
|
Pilot* player = NULL; /**< The player. */
|
|
static Ship* player_ship = NULL; /**< Temp ship to hold when naming it. */
|
|
static double player_px, player_py, player_vx, player_vy, player_dir; /**< More hack. */
|
|
static int player_credits = 0; /**< Temp hack on create. */
|
|
|
|
/*
|
|
* Player sounds.
|
|
*/
|
|
static int snd_target = -1;
|
|
static int snd_jump = -1;
|
|
|
|
/* Player pilot stack - Ships she owns. */
|
|
static Pilot** player_stack = NULL; /**< Stack of ships player has. */
|
|
static char** player_lstack = NULL; /**< Names of the planet the ships are at. */
|
|
static int player_nstack = 0; /**< Number of ships player has. */
|
|
|
|
/* Player global properties. */
|
|
char* player_name = NULL; /**< Player name. */
|
|
int player_crating = 0; /**< Player combar rating. */
|
|
unsigned int player_flags = 0; /**< Player flags. */
|
|
|
|
/* Input.c */
|
|
double player_turn = 0.; /**< Turn velocity from input. */
|
|
static double player_acc = 0.; /**< Accel velocity from input. */
|
|
unsigned int player_target = PLAYER_ID; /**< Targetted pilot. */
|
|
|
|
/* Internal */
|
|
int planet_target = -1; /**< Targetted planet. */
|
|
int hyperspace_target = -1; /**< Target hyperspace route. */
|
|
|
|
/* For death etc. */
|
|
static unsigned int player_timer = 0; /**< For death and such. */
|
|
static Vec2 player_cam; /**< Again, for death etc. */
|
|
|
|
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 Pilot** pilot_stack;
|
|
extern int pilot_nstack;
|
|
|
|
/* Space stuff for GUI. */
|
|
extern StarSystem* systems_stack;
|
|
|
|
/* GUI crap. */
|
|
typedef struct Radar_ {
|
|
double x,y; /**< Position. */
|
|
double w,h; /**< Dimensions. */
|
|
RadarShape shape; /**< Shape. */
|
|
double res; /**< Resolution. */
|
|
} 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. */
|
|
|
|
typedef struct Rect_ {
|
|
double x,y;
|
|
double w,h;
|
|
} Rect;
|
|
|
|
typedef struct GUI_ {
|
|
/* Graphics. */
|
|
glTexture* gfx_frame; /**< Frame of the GUI. */
|
|
glTexture* gfx_targetPilot, *gfx_targetPlanet; /**< Graphics used to target planets. */
|
|
|
|
Radar radar; /**< The radar. */
|
|
Rect nav; /**< Navigation computer. */
|
|
Rect shield, armour, energy; /**< Health bars. */
|
|
Rect weapon; /**< Weapon targeting system. */
|
|
Rect target_health, target_name, target_faction; /**< Target stuff. */
|
|
Rect misc; /**< Misc stuff: credits, cargo.. */
|
|
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.;
|
|
double gui_yoff = 0.;
|
|
|
|
/* Messages. */
|
|
#define MSG_SIZE_MAX 80
|
|
int msg_timeout = 5000; /**< How long it takes for a message to timeout. */
|
|
int msg_max = 5; /**< Max messages on screen. */
|
|
typedef struct Msg_ {
|
|
char str[MSG_SIZE_MAX];
|
|
unsigned int t;
|
|
} Msg;
|
|
static Msg* msg_stack; /**< Stack of messages. */
|
|
|
|
/* 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. */
|
|
|
|
/* Creation. */
|
|
static void player_newMake(void);
|
|
static void player_newShipMake(char* name);
|
|
/* Sound. */
|
|
static void player_initSound(void);
|
|
/*static void player_stopSound(void) */
|
|
/* Gui. */
|
|
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 glColour* c, const Rect* r, const double w);
|
|
/* Save/Load. */
|
|
static int player_saveShip(xmlTextWriterPtr writer, Pilot* ship, char* loc);
|
|
static int player_parse(xmlNodePtr parent);
|
|
static int player_parseDone(xmlNodePtr parent);
|
|
static int player_parseShip(xmlNodePtr parent, int is_player);
|
|
|
|
/* Externed. */
|
|
void player_dead(void);
|
|
void player_destroyed(void);
|
|
int player_save(xmlTextWriterPtr writer);
|
|
int player_load(xmlNodePtr parent);
|
|
void player_think(Pilot* player);
|
|
void player_brokeHyperspace(void);
|
|
double player_faceHyperspace(void);
|
|
|
|
/**
|
|
* @fn void player_new(void)
|
|
*
|
|
* @brief Create a new player.
|
|
*
|
|
* - Cleans up after old player.
|
|
* - Prompts for name.
|
|
*
|
|
* @sa player_newMake
|
|
*/
|
|
void player_new(void) {
|
|
int r;
|
|
/* Let's not seg fault due to a lack of environment. */
|
|
player_flags = 0;
|
|
player_setFlag(PLAYER_CREATING);
|
|
vectnull(&player_cam);
|
|
gl_bindCamera(&player_cam);
|
|
|
|
/* Setup sound. */
|
|
player_initSound();
|
|
|
|
/* Cleanup player stuff if we'll be re-creating. */
|
|
player_cleanup();
|
|
|
|
var_cleanup();
|
|
missions_cleanup();
|
|
space_clearKnown();
|
|
|
|
player_name = dialogue_input("Player Name", 3, 20,
|
|
"Please tell me your name:");
|
|
|
|
/* Player cancelled dialogue. */
|
|
if(player_name == NULL) {
|
|
menu_main();
|
|
return;
|
|
}
|
|
|
|
if(lfile_fileExists("saves/%s.ls", player_name)) {
|
|
r = dialogue_YesNo("Overwrite",
|
|
"You already have a pilot named %s. Overwrite?", player_name);
|
|
if(r == 0) {
|
|
/* Nupe. */
|
|
player_new();
|
|
return;
|
|
}
|
|
}
|
|
|
|
player_newMake();
|
|
}
|
|
|
|
/**
|
|
* @fn static void player_newMake(void)
|
|
*
|
|
* @brief Actually create a new player.
|
|
*/
|
|
static void player_newMake(void) {
|
|
Ship* ship;
|
|
char* sysname;
|
|
uint32_t bufsize;
|
|
char* buf;
|
|
int l, h, tl, th;
|
|
double x, y;
|
|
|
|
sysname = NULL;
|
|
|
|
buf = pack_readfile(DATA, START_DATA, &bufsize);
|
|
|
|
xmlNodePtr node, cur, tmp;
|
|
xmlDocPtr doc = xmlParseMemory(buf, bufsize);
|
|
|
|
node = doc->xmlChildrenNode;
|
|
if(!xml_isNode(node, XML_START_ID)) {
|
|
ERR("Malformed '"START_DATA"' file: missing root element '"XML_START_ID"'");
|
|
return;
|
|
}
|
|
|
|
node = node->xmlChildrenNode; /* First system node. */
|
|
if(node == NULL) {
|
|
ERR("Malformed '"START_DATA"' file: does not contain elements");
|
|
return;
|
|
}
|
|
do {
|
|
/* We are interested in the player. */
|
|
if(xml_isNode(node, "player")) {
|
|
cur = node->children;
|
|
do {
|
|
if(xml_isNode(cur, "ship")) ship = ship_get(xml_get(cur));
|
|
else if(xml_isNode(cur, "credits")) {
|
|
/* MONIEZZ!!!! -- We call these SCred AKA SaraCred. Lame right? */
|
|
tmp = cur->children;
|
|
do {
|
|
xmlr_int(tmp, "low", l);
|
|
xmlr_int(tmp, "high", h);
|
|
} while(xml_nextNode(tmp));
|
|
}
|
|
else if(xml_isNode(cur, "system")) {
|
|
tmp = cur->children;
|
|
do {
|
|
/* System name. TODO: Chance based on percentage. */
|
|
if(xml_isNode(tmp, "name"))
|
|
sysname = strdup(xml_get(tmp));
|
|
/* Position. */
|
|
xmlr_float(tmp, "x", x);
|
|
xmlr_float(tmp, "y", y);
|
|
} while(xml_nextNode(tmp));
|
|
}
|
|
xmlr_int(cur, "player_crating", player_crating);
|
|
if(xml_isNode(cur, "date")) {
|
|
tmp = cur->children;
|
|
do {
|
|
xmlr_int(tmp, "low", tl);
|
|
xmlr_int(tmp, "high", th);
|
|
} while(xml_nextNode(tmp));
|
|
}
|
|
} while((cur = cur->next));
|
|
}
|
|
}while((node = node->next));
|
|
|
|
xmlFreeDoc(doc);
|
|
free(buf);
|
|
xmlCleanupParser();
|
|
|
|
/* Money. */
|
|
player_credits = RNG(l, h);
|
|
|
|
/* Time. */
|
|
ltime_set(RNG(tl*1000*LTIME_UNIT_LENGTH, th*1000*LTIME_UNIT_LENGTH));
|
|
|
|
/* Welcome message. */
|
|
player_message("Welcome to "APPNAME"!");
|
|
player_message("v%d.%d.%d", VMAJOR, VMINOR, VREV);
|
|
|
|
/* Try to create the pilot, if fails re-ask for player name. */
|
|
if(player_newShip(ship, x, y, 0., 0., RNG(0, 369)/180.*M_PI) != 0) {
|
|
player_new();
|
|
return;
|
|
}
|
|
|
|
space_init(sysname);
|
|
free(sysname);
|
|
|
|
/* Clear the map. */
|
|
map_clear();
|
|
}
|
|
|
|
/**
|
|
* @fn int player_newShip(Ship* ship, double px, double py,
|
|
* double vx, double vy, double dir)
|
|
*
|
|
* @brief Create a new ship for player.
|
|
* @return 0 indicates success, -1 means dialogue was cancelled.
|
|
*
|
|
* @sa player_newShipMake
|
|
*/
|
|
int player_newShip(Ship* ship, double px, double py,
|
|
double vx, double vy, double dir) {
|
|
|
|
char* ship_name;
|
|
|
|
/* Temp values while player doesn't exist. */
|
|
player_ship = ship;
|
|
player_px = px;
|
|
player_py = py;
|
|
player_vx = vx;
|
|
player_vy = vy;
|
|
player_dir = dir;
|
|
|
|
ship_name = dialogue_input("Ship Name", 3, 20,
|
|
"Please name your shiny new %s", ship->name);
|
|
|
|
/* Dialogue cancelled. */
|
|
if(ship_name == NULL)
|
|
return -1;
|
|
|
|
player_newShipMake(ship_name);
|
|
|
|
free(ship_name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @fn static void player_newShipMake(char* name)
|
|
*
|
|
* @brief Actually create the new ship.
|
|
*/
|
|
static void player_newShipMake(char* name) {
|
|
Vec2 vp, vv;
|
|
|
|
/* Store the current ship if it exists. */
|
|
if(player != NULL) {
|
|
player_stack = realloc(player_stack, sizeof(Pilot*)*(player_nstack+1));
|
|
player_stack[player_nstack] = pilot_copy(player);
|
|
player_lstack = realloc(player_lstack, sizeof(char*)*(player_nstack+1));
|
|
player_lstack[player_nstack] = strdup(land_planet->name);
|
|
player_nstack++;
|
|
if(!player_credits) player_credits = player->credits;
|
|
pilot_destroy(player);
|
|
}
|
|
|
|
/* In case we're respawning. */
|
|
player_rmFlag(PLAYER_CREATING);
|
|
|
|
/* Hackish position setting. */
|
|
vect_cset(&vp, player_px, player_py);
|
|
vect_cset(&vv, player_vx, player_vy);
|
|
|
|
/* Create the player. */
|
|
pilot_create(player_ship, name, faction_get("Player"), NULL,
|
|
player_dir, &vp, &vv, PILOT_PLAYER);
|
|
|
|
gl_bindCamera(&player->solid->pos); /* Set opengl camera. */
|
|
|
|
/* Moniez!! */
|
|
player->credits = player_credits;
|
|
player_credits = 0;
|
|
}
|
|
|
|
/**
|
|
* @fn void player_swapShip(char* shipname)
|
|
*
|
|
* @brief Swap players current ship with her ship named 'shipname'.
|
|
* @param shipname Ship to change to.
|
|
*/
|
|
void player_swapShip(char* shipname) {
|
|
int i, j;
|
|
Pilot* ship;
|
|
|
|
for(i = 0; i < player_nstack; i++) {
|
|
if(strcmp(shipname, player_stack[i]->name)==0) {
|
|
/* Swap player and ship. */
|
|
ship = player_stack[i];
|
|
|
|
/* Move credits over. */
|
|
ship->credits = player->credits;
|
|
|
|
/* Move cargo over. */
|
|
for(j = 0; j < player->ncommodities; j++) {
|
|
pilot_addCargo(ship, player->commodities[j].commodity,
|
|
player->commodities[j].quantity);
|
|
pilot_rmCargo(player, player->commodities[j].commodity,
|
|
player->commodities[j].quantity);
|
|
}
|
|
|
|
/* Extra pass to calculate stats. */
|
|
pilot_calcStats(ship);
|
|
pilot_calcStats(player);
|
|
|
|
/* Now swap the players. */
|
|
player_stack[i] = player;
|
|
for(j = 0; j < pilot_nstack; j++) /* Find pilot in stack to swap. */
|
|
if(pilot_stack[j] == player) {
|
|
pilot_stack[j] = player = ship;
|
|
break;
|
|
}
|
|
gl_bindCamera(&player->solid->pos); /* Let's not forget the camera. */
|
|
return;
|
|
}
|
|
}
|
|
WARN("Unable to swap player with ship '%s': Ship does not exist!", shipname);
|
|
}
|
|
|
|
/**
|
|
* @fn int player_shipPrice(char* shipname)
|
|
*
|
|
* @brief Calculate the price of one of the players ships.
|
|
* @param shipname Name of the ship.
|
|
* @return The price of the ship in credits.
|
|
*/
|
|
int player_shipPrice(char* shipname) {
|
|
int i;
|
|
int price;
|
|
Pilot* ship;
|
|
|
|
/* Find ship. */
|
|
for(i = 0; i < player_nstack; i++) {
|
|
if(strcmp(shipname, player_stack[i]->name)==0) {
|
|
ship = player_stack[i];
|
|
|
|
/* Ship price is base price + outfit prices. */
|
|
price = ship_basePrice(ship->ship);
|
|
for(i = 0; i < ship->noutfits; i++)
|
|
price += ship->outfits[i].quantity * ship->outfits[i].outfit->price;
|
|
|
|
return price;
|
|
}
|
|
}
|
|
|
|
WARN("Unable to find price for players ship '%s': ship does not exist!", shipname);
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* @fn void player_rmShip(char* shipname)
|
|
*
|
|
* @brief Remove one of the players ships.
|
|
* @param shipname Name of the ship to remove.
|
|
*/
|
|
void player_rmShip(char* shipname) {
|
|
int i;
|
|
|
|
for(i = 0; i < player_nstack; i++) {
|
|
if(strcmp(shipname, player_stack[i]->name)==0) {
|
|
/* Free player ship and location. */
|
|
pilot_free(player_stack[i]);
|
|
free(player_lstack[i]);
|
|
|
|
/* Move memory to make adjacent. */
|
|
memmove(player_stack+i, player_stack+i+1,
|
|
sizeof(Pilot*) * (player_nstack-i-1));
|
|
memmove(player_lstack+i, player_lstack+i+1,
|
|
sizeof(char*) * (player_nstack-i-1));
|
|
|
|
player_nstack--; /* Shrink stack. */
|
|
|
|
/* Realloc memory to smaller size. */
|
|
player_stack = realloc(player_stack,
|
|
sizeof(Pilot*) * (player_nstack));
|
|
player_lstack = realloc(player_lstack,
|
|
sizeof(char*) * (player_nstack));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief void player_cleanup(void
|
|
*
|
|
* @brief Clean up player stuff like player_stack.
|
|
*/
|
|
void player_cleanup(void) {
|
|
int i;
|
|
|
|
player_clear();
|
|
|
|
/* 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 the stack. */
|
|
if(player_stack != NULL) {
|
|
for(i = 0; i < player_nstack; i++) {
|
|
pilot_free(player_stack[i]);
|
|
free(player_lstack[i]);
|
|
}
|
|
free(player_stack);
|
|
player_stack = NULL;
|
|
free(player_lstack);
|
|
player_lstack = NULL;
|
|
/* Nothing left. */
|
|
player_nstack = 0;
|
|
}
|
|
|
|
/* Clean up missions. */
|
|
if(missions_done != NULL) {
|
|
free(missions_done);
|
|
missions_done = NULL;
|
|
missions_ndone = 0;
|
|
missions_mdone = 0;
|
|
}
|
|
|
|
pilots_cleanAll();
|
|
if(player != NULL) {
|
|
pilot_free(player);
|
|
player = NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @fn static void player_initSound(void)
|
|
*
|
|
* @brief Initialize the player sounds.
|
|
*/
|
|
static int player_soundReserved = 0; /**< Has the player already reserved sound? */
|
|
static void player_initSound(void) {
|
|
if(player_soundReserved) return;
|
|
|
|
/* Allocated channels. */
|
|
sound_reserve(PLAYER_RESERVED_CHANNELS);
|
|
sound_createGroup(PLAYER_ENGINE_CHANNEL, 0, 1); /* Channel for engine noises. */
|
|
sound_createGroup(PLAYER_GUI_CHANNEL, 1, PLAYER_RESERVED_CHANNELS-1);
|
|
player_soundReserved = 1;
|
|
|
|
/* Get sounds. */
|
|
snd_target = sound_get("target");
|
|
snd_jump = sound_get("jump");
|
|
}
|
|
|
|
/**
|
|
* @fn static void player_playSound(int sound, int once)
|
|
*
|
|
* @brief Play a sound at the player.
|
|
* @param sound ID of the sound to play.
|
|
* @param once Play only once?
|
|
*/
|
|
void player_playSound(int sound, int once) {
|
|
sound_playGroup(PLAYER_GUI_CHANNEL, sound, once);
|
|
}
|
|
|
|
#if 0
|
|
/**
|
|
* @fn static void player_stopSound(void)
|
|
*
|
|
* @brief Stop playing player sounds.
|
|
*/
|
|
static void player_stopSound(void) {
|
|
sound_stopGroup(PLAYER_GUI_CHANNEL);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @fn void player_message(const char* fmt, ...)
|
|
*
|
|
* @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 = SDL_GetTicks() + msg_timeout;
|
|
}
|
|
|
|
/**
|
|
* @fn void player_warp(const double x, const double y)
|
|
*
|
|
* @brief Warp the player to the new position.
|
|
* @param x X value of the position to warp to.
|
|
* @param y Y value of the position to warp to.
|
|
*/
|
|
void player_warp(const double x, const double y) {
|
|
vect_cset(&player->solid->pos, x, y);
|
|
}
|
|
|
|
/**
|
|
* @fn player_clear(void)
|
|
*
|
|
* @brief Clear the targets.
|
|
*/
|
|
void player_clear(void) {
|
|
player_target = PLAYER_ID;
|
|
planet_target = -1;
|
|
hyperspace_target = -1;
|
|
}
|
|
|
|
/**
|
|
* @fn const char* player_rating(void)
|
|
*
|
|
* @brief Get the players combat rating in a human readable string.
|
|
*
|
|
* @return The players combat rating in a human readable string.
|
|
*/
|
|
static char* player_ratings[] = {
|
|
"None",
|
|
"Smallfry",
|
|
"Minor",
|
|
"Average",
|
|
"Major",
|
|
"Fearsome",
|
|
"Godlike"
|
|
};
|
|
|
|
const char* player_rating(void) {
|
|
if(player_crating == 0) return player_ratings[0];
|
|
else if(player_crating < 50) return player_ratings[1];
|
|
else if(player_crating < 200) return player_ratings[2];
|
|
else if(player_crating < 500) return player_ratings[3];
|
|
else if(player_crating < 1000) return player_ratings[4];
|
|
else if(player_crating < 2500) return player_ratings[5];
|
|
else if(player_crating < 10000) return player_ratings[6];
|
|
else return player_ratings[7];
|
|
}
|
|
|
|
/**
|
|
* @fn int player_outfitOwned(const char* outfitname)
|
|
*
|
|
* @brief Get how many of the outfits the player owns.
|
|
* @param outfitname Outfit to check how many the player owns.
|
|
* @return The number of outfits matching outfitname owned.
|
|
*/
|
|
int player_outfitOwned(const char* outfitname) {
|
|
int i;
|
|
|
|
for(i = 0; i < player->noutfits; i++)
|
|
if(strcmp(outfitname, player->outfits[i].outfit->name)==0)
|
|
return player->outfits[i].quantity;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @fn int player_cargoOwned(const char* commodityname)
|
|
*
|
|
* @brief Get how many of the commodity the player has.
|
|
* @param commodityname Commodity to check how many the player has.
|
|
* @return The number of commodities owned matching commodityname.
|
|
*/
|
|
int player_cargoOwned(const char* commodityname) {
|
|
int i;
|
|
|
|
for(i = 0; i < player->ncommodities; i++)
|
|
if(!player->commodities[i].id &&
|
|
strcmp(commodityname, player->commodities[i].commodity->name)==0)
|
|
return player->commodities[i].quantity;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void player_rmMissionCargo(unsigned int cargo_id) {
|
|
int i;
|
|
|
|
/* Check if already done. */
|
|
if((player != NULL) && !pilot_rmMissionCargo(player, cargo_id)) return;
|
|
|
|
for(i = 0; i < player_nstack; i++)
|
|
if(!pilot_rmMissionCargo(player_stack[i], cargo_id))
|
|
return; /* Success. */
|
|
}
|
|
|
|
/**
|
|
* @fn void player_renderBG(void)
|
|
*
|
|
* @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. */
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @fn void player_render(void)
|
|
*
|
|
* @brief Render the player.
|
|
*/
|
|
static int can_jump = 0; /**< Store whether or not the player is able to jump. */
|
|
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_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. */
|
|
}
|
|
|
|
/* Player is ontop of targeting graphic. */
|
|
pilot_render(player);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @fn void player_renderGUI(void)
|
|
*
|
|
* @brief Render the players GUI.
|
|
*/
|
|
void player_renderGUI(void) {
|
|
int i, j;
|
|
double x, y;
|
|
char str[10];
|
|
Pilot* p;
|
|
glColour* c, c2;
|
|
glFont* f;
|
|
StarSystem* sys;
|
|
unsigned int t;
|
|
|
|
t = SDL_GetTicks();
|
|
|
|
/* Pilot is dead or being created, just render her and stop. */
|
|
if(player_isFlag(PLAYER_DESTROYED) || player_isFlag(PLAYER_CREATING) ||
|
|
pilot_isFlag(player, PILOT_DEAD)) {
|
|
|
|
if(player_isFlag(PLAYER_DESTROYED)) {
|
|
if(!toolkit && !player_isFlag(PLAYER_CREATING) &&
|
|
(t > player_timer)) {
|
|
menu_death();
|
|
}
|
|
}
|
|
/* Fancy cinematic scene borders. */
|
|
spfx_cinematic();
|
|
|
|
return;
|
|
}
|
|
|
|
if(player == NULL) return;
|
|
|
|
/* Lockon warning. */
|
|
if(player->lockons > 0)
|
|
gl_printMid(NULL, SCREEN_W, 0., SCREEN_H-gl_defFont.h-25.,
|
|
&cRed, "LOCKON 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);
|
|
|
|
/* Weapons. */
|
|
glBegin(GL_POINTS);
|
|
COLOUR(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 < 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]);
|
|
|
|
/* 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]];
|
|
|
|
i = space_canHyperspace(player);
|
|
if((i != 0) && (i != can_jump))
|
|
if(!pilot_isFlag(player, PILOT_HYPERSPACE))
|
|
player_playSound(snd_jump, 1);
|
|
can_jump = i;
|
|
|
|
c = can_jump ? &cConsole : 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_renderBar(&cShield, &gui.shield, player->shield / player->shield_max);
|
|
gui_renderBar(&cArmour, &gui.armour, player->armour / player->armour_max);
|
|
gui_renderBar(&cEnergy, &gui.energy, player->energy / player->energy_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;
|
|
if(outfit_isLauncher(player->secondary->outfit)) {
|
|
/* Use the ammunitions name. */
|
|
i = gl_printWidth(f, "%s", player->secondary->outfit->u.lau.ammo);
|
|
if(i > gui.weapon.w)
|
|
/* Font is too big. */
|
|
f = &gl_smallFont;
|
|
|
|
gl_printMid(f, (int)gui.weapon.w, gui.weapon.x,
|
|
gui.weapon.y - 5,
|
|
&cConsole, "%s", player->secondary->outfit->u.lau.ammo);
|
|
|
|
/* 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);
|
|
} 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.,
|
|
&cConsole, "%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", player->ship->cap_cargo);
|
|
gl_print(&gl_smallFont,
|
|
gui.misc.x + gui.misc.w - 8 - i, j,
|
|
NULL, "%d", pilot_cargoFree(player));
|
|
|
|
/* Messages. */
|
|
x = gui.msg.x;
|
|
y = gui.msg.y + (double)(gl_defFont.h * msg_max)*1.2;
|
|
c2.r = c2.g = c2.b = 1.;
|
|
for(i = 0; i < msg_max; i++) {
|
|
y -= (double)gl_defFont.h*1.2;
|
|
if(msg_stack[msg_max-i-1].str[0] != '\0') {
|
|
if(msg_stack[msg_max-i-1].t < t)
|
|
msg_stack[msg_max-i-1].str[0] = '\0';
|
|
else {
|
|
if(msg_stack[msg_max-i-1].t - msg_timeout/2 < t)
|
|
c2.a = (double)(msg_stack[msg_max-i-1].t - t) / (double)(msg_timeout/2);
|
|
else
|
|
c2.a = 1.;
|
|
gl_print(NULL, x, y, &c2, "%s", msg_stack[msg_max-i-1].str);
|
|
}
|
|
}
|
|
}
|
|
/* Hyperspace FLASH BANG!!! */
|
|
if(pilot_isFlag(player, PILOT_HYPERSPACE) && !paused) {
|
|
i = (int)player->ptimer - HYPERSPACE_FADEOUT;
|
|
j = (int) t;
|
|
if(i < j) {
|
|
x = (double)(j-i) / 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();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Renders a pilot. */
|
|
static void gui_renderPilot(const Pilot* p) {
|
|
int x, y, sx, sy;
|
|
double w, h;
|
|
glColour* col;
|
|
|
|
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);
|
|
/* Colours. */
|
|
if(p->id == player_target) col = &cRadar_targ;
|
|
else if(pilot_isDisabled(p)) col = &cInert;
|
|
else if(pilot_isFlag(p, PILOT_HOSTILE)) col = &cHostile;
|
|
else col = faction_getColour(p->faction);
|
|
COLOUR(*col);
|
|
|
|
/* 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 glColour* c, const Rect* r, const double w) {
|
|
int x, y, sx, sy;
|
|
|
|
glBegin(GL_QUADS);
|
|
COLOUR(*c);
|
|
x = r->x - SCREEN_W/2.;
|
|
y = r->y - 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();
|
|
}
|
|
|
|
/**
|
|
* @fn int gui_init(void)
|
|
*
|
|
* @brief Initialize the GUI system.
|
|
* @return 0 on success.
|
|
*/
|
|
int gui_init(void) {
|
|
/* Set graphics to NULL. */
|
|
gui.gfx_frame = NULL;
|
|
gui.gfx_targetPilot = NULL;
|
|
gui.gfx_targetPlanet = NULL;
|
|
|
|
/* -- 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;
|
|
}
|
|
|
|
/**
|
|
* @fn int gui_load(const char* name)
|
|
*
|
|
* @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 = 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 = xml_getFloat(cur);
|
|
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 = xml_getFloat(cur);
|
|
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 = xml_getFloat(cur);
|
|
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 = xml_getFloat(cur);
|
|
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. */
|
|
#define RELATIVIZE(a) \
|
|
{ (a).x += VX(gui.frame); (a).y = VY(gui.frame) + gui.gfx_frame->h-(a).y; }
|
|
static int gui_parse(const xmlNodePtr parent, const char* name) {
|
|
xmlNodePtr cur, node;
|
|
char* tmp, buf[PATH_MAX];
|
|
|
|
/* 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);
|
|
if(gui.gfx_frame) gl_freeTexture(gui.gfx_frame); /* Free if needed. */
|
|
gui.gfx_frame = gl_newImage(buf);
|
|
/* Pilot. */
|
|
snprintf(buf, PATH_MAX, GUI_GFX"%s_pilot.png", tmp);
|
|
if(gui.gfx_targetPilot) gl_freeTexture(gui.gfx_targetPilot); /* Free if needed. */
|
|
gui.gfx_targetPilot = gl_newSprite(buf, 2, 2);
|
|
/* Planet. */
|
|
snprintf(buf, PATH_MAX, GUI_GFX"%s_planet.png", tmp);
|
|
if(gui.gfx_targetPlanet) gl_freeTexture(gui.gfx_targetPlanet); /* Free if needed. */
|
|
gui.gfx_targetPlanet = gl_newSprite(buf, 2, 2);
|
|
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);
|
|
RELATIVIZE(gui.shield);
|
|
}
|
|
|
|
if(xml_isNode(cur, "armour")) {
|
|
rect_parse(cur, &gui.armour.x, &gui.armour.y,
|
|
&gui.armour.w, &gui.armour.h);
|
|
RELATIVIZE(gui.armour);
|
|
}
|
|
|
|
if(xml_isNode(cur, "energy")) {
|
|
rect_parse(cur, &gui.energy.x, &gui.energy.y,
|
|
&gui.energy.w, &gui.energy.h);
|
|
RELATIVIZE(gui.energy);
|
|
}
|
|
} 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((node = node->next));
|
|
|
|
return 0;
|
|
}
|
|
#undef RELATIVIZE
|
|
|
|
/**
|
|
* @fn void gui_free(void)
|
|
*
|
|
* @brief Free the GUI.
|
|
*/
|
|
void gui_free(void) {
|
|
if(gui.gfx_frame) gl_freeTexture(gui.gfx_frame);
|
|
if(gui.gfx_targetPilot) gl_freeTexture(gui.gfx_targetPilot);
|
|
if(gui.gfx_targetPlanet) gl_freeTexture(gui.gfx_targetPlanet);
|
|
|
|
free(msg_stack);
|
|
}
|
|
|
|
/**
|
|
* @fn void player_think(Pilot* pplayer)
|
|
*
|
|
* @brief Basically uses keyboard input instead of AI input. Used in pilot.c.
|
|
* @param pplayer Player to think.
|
|
*/
|
|
void player_think(Pilot* pplayer) {
|
|
/* Last I checked, the dead didn't think.. */
|
|
if(pilot_isFlag(pplayer, PILOT_DEAD)) {
|
|
/* No point in accelerating or turning. */
|
|
pplayer->solid->dir_vel = 0.;
|
|
vect_pset(&player->solid->force, 0., 0.);
|
|
return;
|
|
}
|
|
|
|
/* PLAYER_FACE will take over navigation. */
|
|
if(player_isFlag(PLAYER_FACE)) {
|
|
if(player_target != PLAYER_ID)
|
|
pilot_face(pplayer,
|
|
vect_angle(&player->solid->pos,
|
|
&pilot_get(player_target)->solid->pos));
|
|
else if(planet_target != -1)
|
|
pilot_face(pplayer,
|
|
vect_angle(&player->solid->pos,
|
|
&cur_system->planets[planet_target].pos));
|
|
}
|
|
|
|
/* PLAYER_REVERSE will take over navigation. */
|
|
else if(player_isFlag(PLAYER_REVERSE) && (VMOD(pplayer->solid->vel) > 0.))
|
|
pilot_face(pplayer, VANGLE(player->solid->vel) + M_PI);
|
|
|
|
/* Normal navigation sheme. */
|
|
else {
|
|
pplayer->solid->dir_vel = 0.;
|
|
if(player_turn)
|
|
pplayer->solid->dir_vel -= player->turn * player_turn;
|
|
}
|
|
|
|
/* Weapon shooting stuff. */
|
|
|
|
/* Primary weapon. */
|
|
if(player_isFlag(PLAYER_PRIMARY)) {
|
|
pilot_shoot(pplayer, player_target, 0);
|
|
player_setFlag(PLAYER_PRIMARY_L);
|
|
}
|
|
else if(player_isFlag(PLAYER_PRIMARY_L)) {
|
|
pilot_shootStop(pplayer, 0);
|
|
player_rmFlag(PLAYER_PRIMARY_L);
|
|
}
|
|
|
|
/* Secondary. */
|
|
if(player_isFlag(PLAYER_SECONDARY)) {
|
|
/* Double tap stops beams. */
|
|
if(!player_isFlag(PLAYER_SECONDARY_L) &&
|
|
(pplayer->secondary != NULL) &&
|
|
outfit_isBeam(pplayer->secondary->outfit)) {
|
|
pilot_shootStop(pplayer, 1);
|
|
} else
|
|
pilot_shoot(pplayer, player_target, 1);
|
|
player_setFlag(PLAYER_SECONDARY_L);
|
|
}
|
|
else if(player_isFlag(PLAYER_SECONDARY_L)) {
|
|
player_rmFlag(PLAYER_SECONDARY_L);
|
|
}
|
|
|
|
/* Afterburn! */
|
|
if(player_isFlag(PLAYER_AFTERBURNER)) {
|
|
if(pilot_isFlag(player, PILOT_AFTERBURNER))
|
|
vect_pset(&pplayer->solid->force,
|
|
pplayer->thrust * pplayer->afterburner->outfit->u.afb.thrust_perc +
|
|
pplayer->afterburner->outfit->u.afb.thrust_abs, pplayer->solid->dir);
|
|
else /* Ran out of energy. */
|
|
player_afterburnOver();
|
|
} else
|
|
vect_pset(&pplayer->solid->force, pplayer->thrust * player_acc,
|
|
pplayer->solid->dir);
|
|
|
|
/* Sound. */
|
|
sound_updateListener(pplayer->solid->dir,
|
|
pplayer->solid->pos.x, pplayer->solid->pos.y);
|
|
|
|
}
|
|
|
|
/*
|
|
* For use in keybindings.
|
|
*/
|
|
/**
|
|
* @fn void player_setRadarRel(int mod)
|
|
*
|
|
* @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);
|
|
}
|
|
|
|
/**
|
|
* @fn void player_secondaryNext(void)
|
|
*
|
|
* @brief Get the next secondary weapon.
|
|
*/
|
|
void player_secondaryNext(void) {
|
|
int i = 0;
|
|
|
|
/* Get the current secondary weapon pos. */
|
|
if(player->secondary != NULL)
|
|
for(i = 0; i < player->noutfits; i++)
|
|
if(&player->outfits[i] == player->secondary) {
|
|
i++;
|
|
break;
|
|
}
|
|
/* Get the next secondary weapon. */
|
|
for(; i < player->noutfits; i++)
|
|
if(outfit_isProp(player->outfits[i].outfit, OUTFIT_PROP_WEAP_SECONDARY)) {
|
|
pilot_switchSecondary(player, i);
|
|
break;
|
|
}
|
|
/* We didn't find an outfit. */
|
|
if(i >= player->noutfits)
|
|
pilot_switchSecondary(player, -1);
|
|
/* Set ammo. */
|
|
pilot_setAmmo(player);
|
|
}
|
|
|
|
/**
|
|
* @fn void player_targetPlanet(void)
|
|
*
|
|
* @brief Cycle through planet targets.
|
|
*/
|
|
void player_targetPlanet(void) {
|
|
hyperspace_target = -1;
|
|
player_rmFlag(PLAYER_LANDACK);
|
|
|
|
if((planet_target == -1) && (cur_system->nplanets > 0)) {
|
|
/* No target. */
|
|
planet_target = 0;
|
|
return;
|
|
}
|
|
|
|
planet_target++;
|
|
|
|
if(planet_target >= cur_system->nplanets)
|
|
/* Last system. */
|
|
planet_target = -1;
|
|
}
|
|
|
|
/**
|
|
* fn void player_land(void)
|
|
*
|
|
* @brief Try to land or target closest planet if no land target.
|
|
*/
|
|
void player_land(void) {
|
|
int i;
|
|
int tp;
|
|
double td, d;
|
|
|
|
if(landed) {
|
|
/* Player is already landed. */
|
|
takeoff();
|
|
return;
|
|
}
|
|
Planet* planet = &cur_system->planets[planet_target];
|
|
if(planet_target >= 0) {
|
|
if(!planet_hasService(planet, PLANET_SERVICE_LAND)) {
|
|
player_message("You can't land here.");
|
|
return;
|
|
}
|
|
else if(!player_isFlag(PLAYER_LANDACK)) {
|
|
/* No landing authorization. */
|
|
if(planet_hasService(planet, PLANET_SERVICE_BASIC)) { /* Basic services. */
|
|
if(!areEnemies(player->faction, planet->faction)) { /* Friendly. */
|
|
player_message("%s> Permission to land granted.", planet->name);
|
|
player_setFlag(PLAYER_LANDACK);
|
|
} else /* Hostile. */
|
|
player_message("%s> Land request denied.", planet->name);
|
|
} else { /* No shoes, no shirt, no lifeforms, no services. */
|
|
player_message("Ready to land on %s.", planet->name);
|
|
player_setFlag(PLAYER_LANDACK);
|
|
}
|
|
return;
|
|
}
|
|
else if(vect_dist(&player->solid->pos, &planet->pos) > planet->gfx_space->sw) {
|
|
player_message("You are too far away to land on %s.", planet->name);
|
|
return;
|
|
}
|
|
else if((pow2(VX(player->solid->vel)) + pow2(VY(player->solid->vel))) >
|
|
(double)pow2(MAX_HYPERSPACE_VEL)) {
|
|
player_message("You are going too fast to land on %s.", planet->name);
|
|
return;
|
|
}
|
|
land(planet); /* Land the player. */
|
|
} else {
|
|
/* Get nearest planet target. */
|
|
if(cur_system->nplanets == 0) {
|
|
player_message("There are no planets to land on.");
|
|
return;
|
|
}
|
|
|
|
td = -1; /* Temp distance. */
|
|
tp = -1; /* Temp planet. */
|
|
for(i = 0; i < cur_system->nplanets; i++) {
|
|
d = vect_dist(&player->solid->pos, &cur_system->planets[i].pos);
|
|
if(planet_hasService(&cur_system->planets[i], PLANET_SERVICE_LAND) &&
|
|
((tp == -1) || ((td == -1) || (td > d)))) {
|
|
tp = i;
|
|
td = d;
|
|
}
|
|
}
|
|
planet_target = tp;
|
|
player_rmFlag(PLAYER_LANDACK);
|
|
|
|
/* No landable planet. */
|
|
if(planet_target < 0) return;
|
|
|
|
player_land(); /* Re-run land protocol. */
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @fn void player_targetHyperspace(void)
|
|
*
|
|
* @brief Get a hyperspace target.
|
|
*/
|
|
void player_targetHyperspace(void) {
|
|
planet_target = -1; /* Remove planet target. */
|
|
player_rmFlag(PLAYER_LANDACK); /* Get rid of landing permission. */
|
|
hyperspace_target++;
|
|
map_clear(); /* Clear the current map path. */
|
|
|
|
if(hyperspace_target >= cur_system->njumps)
|
|
hyperspace_target = -1;
|
|
}
|
|
|
|
/**
|
|
* @fn void player_jump(void)
|
|
*
|
|
* @brief Actually attempt to jump in hyperspace.
|
|
*/
|
|
void player_jump(void) {
|
|
int i;
|
|
if((hyperspace_target == -1) ||
|
|
pilot_isFlag(player, PILOT_HYP_PREP) ||
|
|
pilot_isFlag(player, PILOT_HYP_BEGIN) ||
|
|
pilot_isFlag(player, PILOT_HYPERSPACE))
|
|
return;
|
|
|
|
i = space_hyperspace(player);
|
|
|
|
if(i == -1)
|
|
player_message("You are too close to gravity centers to initiate hyperspace.");
|
|
else if(i == -2)
|
|
player_message("You are moving too fast to enter hyperspace.");
|
|
else if(i == -3)
|
|
player_message("You do not have enough fuel to hyperspace jump.");
|
|
else
|
|
player_message("Preparing for hyperspace.");
|
|
}
|
|
|
|
/**
|
|
* @fn void player_brokeHyperspace(void)
|
|
*
|
|
* @brief Player actually broke hyperspace (entering new system).
|
|
*/
|
|
void player_brokeHyperspace(void) {
|
|
unsigned int tl, th;
|
|
|
|
/* Calculate the time it takes, call before space_init. */
|
|
tl = (unsigned int) floor(sqrt((double)player->solid->mass)/5.);
|
|
th = (unsigned int) ceil(sqrt((double)player->solid->mass)/5.);
|
|
tl *= LTIME_UNIT_LENGTH;
|
|
th *= LTIME_UNIT_LENGTH;
|
|
ltime_inc(RNG(tl, th));
|
|
|
|
/* Enter the new system. */
|
|
space_init(systems_stack[cur_system->jumps[hyperspace_target]].name);
|
|
|
|
/* Set position, pilot_update will handle the lowering of velocity. */
|
|
player_warp(-cos(player->solid->dir) * MIN_HYPERSPACE_DIST * 2.5,
|
|
-sin(player->solid->dir) * MIN_HYPERSPACE_DIST * 2.5);
|
|
|
|
/* Reduce fuel. */
|
|
player->fuel -= HYPERSPACE_FUEL;
|
|
|
|
/* Stop hyperspace. */
|
|
pilot_rmFlag(player, PILOT_HYPERSPACE | PILOT_HYP_BEGIN | PILOT_HYP_PREP);
|
|
|
|
/* Update the map. */
|
|
map_jump();
|
|
|
|
/* Run the jump hooks. */
|
|
hooks_run("jump");
|
|
hooks_run("enter");
|
|
|
|
player_message("BANG!");
|
|
}
|
|
|
|
/**
|
|
* @fn double player_faceHyperspace(void)
|
|
*
|
|
* @brief Make player face her hyperspace target.
|
|
* @return direction to face.
|
|
*/
|
|
double player_faceHyperspace(void) {
|
|
double a;
|
|
a = ANGLE(systems_stack[cur_system->jumps[hyperspace_target]].pos.x -
|
|
cur_system->pos.x,
|
|
systems_stack[cur_system->jumps[hyperspace_target]].pos.y -
|
|
cur_system->pos.y);
|
|
|
|
return pilot_face(player, a);
|
|
}
|
|
|
|
/**
|
|
* @fn void player_afterburn(void)
|
|
*
|
|
* @brief Activate the afterburner.
|
|
*/
|
|
void player_afterburn(void) {
|
|
/* TODO: Fancy effects. */
|
|
if((player != NULL) && (player->afterburner != NULL)) {
|
|
player_setFlag(PLAYER_AFTERBURNER);
|
|
pilot_setFlag(player, PILOT_AFTERBURNER);
|
|
spfx_shake(player->afterburner->outfit->u.afb.rumble * SHAKE_MAX);
|
|
sound_stopGroup(PLAYER_ENGINE_CHANNEL);
|
|
sound_playGroup(PLAYER_ENGINE_CHANNEL,
|
|
player->afterburner->outfit->u.afb.sound, 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @fn void player_afterburnOver(void)
|
|
*
|
|
* @brief Deactivate the afterburner.
|
|
*/
|
|
void player_afterburnOver(void) {
|
|
if((player != NULL) && (player->afterburner != NULL)) {
|
|
player_rmFlag(PLAYER_AFTERBURNER);
|
|
pilot_rmFlag(player, PILOT_AFTERBURNER);
|
|
sound_stopGroup(PLAYER_ENGINE_CHANNEL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @fn void player_accel(double acc)
|
|
*
|
|
* @brief Start accelerating.
|
|
* @param acc How much thrust should be applied of maximum (0 - 1).
|
|
*/
|
|
void player_accel(double acc) {
|
|
if(player != NULL) {
|
|
player_acc = acc;
|
|
sound_stopGroup(PLAYER_ENGINE_CHANNEL);
|
|
sound_playGroup(PLAYER_ENGINE_CHANNEL,
|
|
player->ship->sound, 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @fn void player_accelOver(void)
|
|
*
|
|
* @brief Done accelerating.
|
|
*/
|
|
void player_accelOver(void) {
|
|
player_acc = 0;
|
|
sound_stopGroup(PLAYER_ENGINE_CHANNEL);
|
|
}
|
|
|
|
/**
|
|
* @fn void player_targetHostile(void)
|
|
*
|
|
* @brief Target the nearest hostile enemy to the player.
|
|
*/
|
|
void player_targetHostile(void) {
|
|
unsigned int tp;
|
|
int i;
|
|
double d, td;
|
|
|
|
tp = PLAYER_ID;
|
|
d = 0;
|
|
for(i = 0; i < pilot_nstack; i++)
|
|
if(pilot_isFlag(pilot_stack[i], PILOT_HOSTILE) ||
|
|
areEnemies(FACTION_PLAYER, pilot_stack[i]->faction)) {
|
|
td = vect_dist(&pilot_stack[i]->solid->pos, &player->solid->pos);
|
|
if(!pilot_isDisabled(pilot_stack[i]) && ((tp == PLAYER_ID) || (td < d))) {
|
|
d = td;
|
|
tp = pilot_stack[i]->id;
|
|
}
|
|
}
|
|
|
|
if((tp != PLAYER_ID) && (tp != player_target))
|
|
player_playSound(snd_target, 1);
|
|
|
|
player_target = tp;
|
|
}
|
|
|
|
/**
|
|
* @fn void player_targetNext(void)
|
|
*
|
|
* @brief Cycles to next target.
|
|
*/
|
|
void player_targetNext(void) {
|
|
player_target = pilot_getNextID(player_target);
|
|
|
|
if(player_target != PLAYER_ID)
|
|
player_playSound(snd_target, 1);
|
|
}
|
|
|
|
/**
|
|
* @fn player_targetNearest(void)
|
|
*
|
|
* @brief Player targets nearest pilot.
|
|
*/
|
|
void player_targetNearest(void) {
|
|
unsigned int t;
|
|
|
|
t = player_target;
|
|
player_target = pilot_getNearestPilot(player);
|
|
|
|
if((player_target != PLAYER_ID) && (t != player_target))
|
|
player_playSound(snd_target, 1);
|
|
}
|
|
|
|
/**
|
|
* @fn void player_screenshot(void)
|
|
*
|
|
* @brief Take a screenshot.
|
|
*/
|
|
static int screenshot_cur = 0; /**< Current screenshot at. */
|
|
void player_screenshot(void) {
|
|
FILE* fp;
|
|
int done;
|
|
char filename[PATH_MAX];
|
|
|
|
if(lfile_dirMakeExist("screenshots")) {
|
|
WARN("Aborting screenshots");
|
|
return;
|
|
}
|
|
|
|
done = 0;
|
|
|
|
do {
|
|
if(screenshot_cur >= 999) {
|
|
/* Just incase I fucked up. :) */
|
|
WARN("You have reached the maximum amount of screenshots [999]");
|
|
return;
|
|
}
|
|
snprintf(filename, PATH_MAX, "%sscreenshots/screenshot%03d.png",
|
|
lfile_basePath(), screenshot_cur);
|
|
fp = fopen(filename, "r"); /* Myeah, I know it's a horrible way to check. */
|
|
if(fp == NULL) done = 1;
|
|
else {
|
|
/* Next. */
|
|
screenshot_cur++;
|
|
fclose(fp);
|
|
}
|
|
fp = NULL;
|
|
} while(!done);
|
|
|
|
/* Now gief me that screenshot. */
|
|
DEBUG("Taking screenshot [%03d]..", screenshot_cur);
|
|
gl_screenshot(filename);
|
|
}
|
|
|
|
/**
|
|
* @fn void player_dead(void)
|
|
*
|
|
* @brief Player got killed.
|
|
*/
|
|
void player_dead(void) {
|
|
gui_xoff = 0.;
|
|
gui_yoff = 0.;
|
|
}
|
|
|
|
/**
|
|
* @fn void player_destroyed(void)
|
|
*
|
|
* @brief Player blew up in a nice fireball.
|
|
*/
|
|
void player_destroyed(void) {
|
|
if(player_isFlag(PLAYER_DESTROYED)) return;
|
|
|
|
vectcpy(&player_cam, &player->solid->pos);
|
|
gl_bindCamera(&player_cam);
|
|
player_setFlag(PLAYER_DESTROYED);
|
|
player_timer = SDL_GetTicks() + 5000;
|
|
}
|
|
|
|
/**
|
|
* @fn char** player_ships(int* nships)
|
|
*
|
|
* @brief Return a buffer with all the players ship names
|
|
* or "None" if there are no ships.
|
|
* @param nships Stores the number of ships.
|
|
* @return Freshly allocated array with allocated ship names.
|
|
*/
|
|
char** player_ships(int* nships) {
|
|
int i;
|
|
char** shipnames;
|
|
|
|
if(player_nstack==0) {
|
|
(*nships) = 1;
|
|
shipnames = malloc(sizeof(char*));
|
|
shipnames[0] = strdup("None");
|
|
} else {
|
|
(*nships) = player_nstack;
|
|
shipnames = malloc(sizeof(char*) * player_nstack);
|
|
for(i = 0; i < player_nstack; i++)
|
|
shipnames[i] = strdup(player_stack[i]->name);
|
|
}
|
|
|
|
return shipnames;
|
|
}
|
|
|
|
/**
|
|
* @fn int player_nships(void)
|
|
*
|
|
* @brief Get the ammount of ships player has in storage.
|
|
* @return The number of ships the player has.
|
|
*/
|
|
int player_nships(void) {
|
|
return player_nstack;
|
|
}
|
|
|
|
/* Return a specific ship. */
|
|
Pilot* player_getShip(char* shipname) {
|
|
int i;
|
|
|
|
for(i = 0; i < player_nstack; i++)
|
|
if(strcmp(player_stack[i]->name, shipname)==0)
|
|
return player_stack[i];
|
|
|
|
WARN("Player ship '%s' not found in stack", shipname);
|
|
return NULL;
|
|
}
|
|
|
|
/* Return location of a specific ship. */
|
|
char* player_getLoc(char* shipname) {
|
|
int i;
|
|
|
|
for(i = 0; i < player_nstack; i++)
|
|
if(strcmp(player_stack[i]->name, shipname)==0)
|
|
return player_lstack[i];
|
|
|
|
WARN("Player ship '%s' not found in stack", shipname);
|
|
return NULL;
|
|
}
|
|
|
|
void player_setLoc(char* shipname, char* loc) {
|
|
int i;
|
|
|
|
for(i = 0; i < player_nstack; i++) {
|
|
if(strcmp(player_stack[i]->name, shipname)==0) {
|
|
free(player_lstack[i]);
|
|
player_lstack[i] = strdup(loc);
|
|
return;
|
|
}
|
|
}
|
|
WARN("Player ship '%s' not found in stack", shipname);
|
|
}
|
|
|
|
/* Marks a mission as completed. */
|
|
void player_missionFinished(int id) {
|
|
missions_ndone++;
|
|
if(missions_ndone > missions_mdone) {
|
|
/* Need to grow. */
|
|
missions_mdone += 25;
|
|
missions_done = realloc(missions_done, sizeof(int) * missions_mdone);
|
|
}
|
|
missions_done[missions_ndone-1] = id;
|
|
}
|
|
|
|
/* has player already completed a mission? */
|
|
int player_missionAlreadyDone(int id) {
|
|
int i;
|
|
|
|
for(i = 0; i < missions_ndone; i++)
|
|
if(missions_done[i] == id)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* Save the player in a freaking xmlfile. */
|
|
int player_save(xmlTextWriterPtr writer) {
|
|
int i;
|
|
MissionData* m;
|
|
|
|
xmlw_startElem(writer, "player");
|
|
xmlw_attr(writer, "name", player_name);
|
|
|
|
xmlw_elem(writer, "rating", "%d", player_crating);
|
|
xmlw_elem(writer, "scred", "%d", player->credits);
|
|
xmlw_elem(writer, "time", "%d", ltime_get());
|
|
|
|
xmlw_elem(writer, "location", land_planet->name);
|
|
player_saveShip(writer, player, NULL); /* Current ship. */
|
|
|
|
xmlw_startElem(writer, "ships");
|
|
for(i = 0; i < player_nstack; i++)
|
|
player_saveShip(writer, player_stack[i], player_lstack[i]);
|
|
|
|
xmlw_endElem(writer); /* Player. */
|
|
|
|
/* Mission the player has done. */
|
|
xmlw_startElem(writer, "missions_done");
|
|
|
|
for(i = 0; i < missions_ndone; i++) {
|
|
m = mission_get(missions_done[i]);
|
|
if(m != NULL) /* In case mission name changes between versions. */
|
|
xmlw_elem(writer, "done", mission_get(missions_done[i])->name);
|
|
}
|
|
|
|
xmlw_endElem(writer); /* missions_done. */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int player_saveShip(xmlTextWriterPtr writer, Pilot* ship, char* loc) {
|
|
int i;
|
|
|
|
xmlw_startElem(writer, "ship");
|
|
xmlw_attr(writer, "name", ship->name);
|
|
xmlw_attr(writer, "model", ship->ship->name);
|
|
|
|
if(loc != NULL) xmlw_elem(writer, "location", loc);
|
|
|
|
/* Save the fuel. */
|
|
xmlw_elem(writer, "fuel", "%f", ship->fuel);
|
|
|
|
/* Save the outfits. */
|
|
xmlw_startElem(writer, "outfits");
|
|
for(i = 0; i < ship->noutfits; i++) {
|
|
xmlw_startElem(writer, "outfit");
|
|
|
|
xmlw_attr(writer, "quantity", "%d", ship->outfits[i].quantity);
|
|
xmlw_str(writer, ship->outfits[i].outfit->name);
|
|
|
|
xmlw_endElem(writer); /* Outfit. */
|
|
}
|
|
xmlw_endElem(writer); /* Outfits. */
|
|
|
|
/* Save the commodities. */
|
|
xmlw_startElem(writer, "commodities");
|
|
for(i = 0; i < ship->ncommodities; i++) {
|
|
|
|
xmlw_startElem(writer, "commodity");
|
|
|
|
xmlw_attr(writer, "quantity", "%d", ship->commodities[i].quantity);
|
|
if(ship->commodities[i].id > 0)
|
|
xmlw_attr(writer, "id", "%d", ship->commodities[i].id);
|
|
xmlw_str(writer, ship->commodities[i].commodity->name);
|
|
|
|
xmlw_endElem(writer); /* Commodity. */
|
|
}
|
|
xmlw_endElem(writer); /* Commodities. */
|
|
xmlw_endElem(writer); /* Ship. */
|
|
|
|
return 0;
|
|
}
|
|
|
|
int player_load(xmlNodePtr parent) {
|
|
xmlNodePtr node;
|
|
|
|
/* Some cleanup. */
|
|
player_flags = 0;
|
|
player_cleanup();
|
|
var_cleanup();
|
|
missions_cleanup();
|
|
|
|
node = parent->xmlChildrenNode;
|
|
|
|
do {
|
|
if(xml_isNode(node, "player"))
|
|
player_parse(node);
|
|
else if(xml_isNode(node, "missions_done"))
|
|
player_parseDone(node);
|
|
} while(xml_nextNode(node));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int player_parse(xmlNodePtr parent) {
|
|
unsigned int player_time;
|
|
char* planet;
|
|
Planet* pnt;
|
|
int sw, sh;
|
|
xmlNodePtr node, cur;
|
|
|
|
node = parent->xmlChildrenNode;
|
|
|
|
xmlr_attr(parent, "name", player_name);
|
|
|
|
do {
|
|
/* Global stuff. */
|
|
xmlr_int(node, "rating", player_crating);
|
|
xmlr_int(node, "credits", player_credits);
|
|
xmlr_long(node, "time", player_time);
|
|
xmlr_str(node, "location", planet);
|
|
|
|
if(xml_isNode(node, "ship"))
|
|
player_parseShip(node, 1);
|
|
|
|
if(xml_isNode(node, "ships")) {
|
|
cur = node->xmlChildrenNode;
|
|
do {
|
|
if(xml_isNode(cur, "ship"))
|
|
player_parseShip(cur, 0);
|
|
} while(xml_nextNode(cur));
|
|
}
|
|
if(xml_isNode(node, "missions_done"))
|
|
player_parseDone(node);
|
|
} while(xml_nextNode(node));
|
|
|
|
/* Set global thingies. */
|
|
player->credits = player_credits;
|
|
ltime_set(player_time);
|
|
|
|
/* Set player in system. */
|
|
pnt = planet_get(planet);
|
|
sw = pnt->gfx_space->sw;
|
|
sh = pnt->gfx_space->sh;
|
|
player_warp(pnt->pos.x + RNG(-sw/2, sw/2),
|
|
pnt->pos.y + RNG(-sh/2, sh/2));
|
|
player->solid->dir = RNG(0, 359) * M_PI/180.;
|
|
gl_bindCamera(&player->solid->pos);
|
|
|
|
/* Initialize the system. */
|
|
space_init(planet_getSystem(planet));
|
|
map_clear(); /* Sets the map up. */
|
|
|
|
/* Initialize the sound. */
|
|
player_initSound();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int player_parseDone(xmlNodePtr parent) {
|
|
xmlNodePtr node;
|
|
|
|
node = parent->xmlChildrenNode;
|
|
|
|
do {
|
|
if(xml_isNode(node, "done"))
|
|
player_missionFinished(mission_getID(xml_get(node)));
|
|
} while(xml_nextNode(node));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int player_parseShip(xmlNodePtr parent, int is_player) {
|
|
char* name, *model, *loc, *q, *id;
|
|
int i, n;
|
|
double fuel;
|
|
Pilot* ship;
|
|
Outfit* o;
|
|
xmlNodePtr node, cur;
|
|
|
|
xmlr_attr(parent, "name", name);
|
|
xmlr_attr(parent, "model", model);
|
|
|
|
/* Player is currently on this ship. */
|
|
if(is_player != 0) {
|
|
pilot_create(ship_get(model), name, faction_get("Player"), NULL, 0., NULL,
|
|
NULL, PILOT_PLAYER | PILOT_NO_OUTFITS);
|
|
ship = player;
|
|
} else
|
|
ship = pilot_createEmpty(ship_get(model), name, faction_get("Player"), NULL,
|
|
PILOT_PLAYER | PILOT_NO_OUTFITS);
|
|
|
|
free(name);
|
|
free(model);
|
|
|
|
node = parent->xmlChildrenNode;
|
|
|
|
fuel = 0;
|
|
|
|
do {
|
|
if(!is_player == 0) xmlr_str(node, "location", loc);
|
|
|
|
/* Get fuel. */
|
|
xmlr_float(node, "fuel", fuel);
|
|
|
|
if(xml_isNode(node, "outfits")) {
|
|
cur = node->xmlChildrenNode;
|
|
do {
|
|
/* Load each outfit. */
|
|
if(xml_isNode(cur, "outfits")) {
|
|
xmlr_attr(cur, "quantity", q);
|
|
n = atoi(q);
|
|
free(q);
|
|
/* Add the outfit. */
|
|
o = outfit_get(xml_get(cur));
|
|
if(o != NULL)
|
|
pilot_addOutfit(ship, o, n);
|
|
}
|
|
} while(xml_nextNode(cur));
|
|
}
|
|
if(xml_isNode(node, "commodities")) {
|
|
cur = node->xmlChildrenNode;
|
|
do {
|
|
if(xml_isNode(cur, "commodity")) {
|
|
xmlr_attr(cur, "quantity", q);
|
|
xmlr_attr(cur, "id", id);
|
|
n = atoi(q);
|
|
if(id == NULL) i = 0;
|
|
else i = atoi(id);
|
|
free(q);
|
|
if(id != NULL) free(id);
|
|
|
|
/* Actually add the cargo with id hack. */
|
|
pilot_addCargo(ship, commodity_get(xml_get(cur)), n);
|
|
if(i != 0) ship->commodities[ship->ncommodities-1].id = i;
|
|
}
|
|
} while(xml_nextNode(node));
|
|
}
|
|
} while(xml_nextNode(node));
|
|
|
|
/* Set fuel. */
|
|
if(fuel != 0)
|
|
ship->fuel = MIN(ship->fuel_max, fuel);
|
|
|
|
/* Add it to the stack if it's not what the player is in. */
|
|
if(is_player == 0) {
|
|
player_stack = realloc(player_stack, sizeof(Pilot*)*(player_nstack+1));
|
|
player_stack[player_nstack] = ship;
|
|
player_lstack = realloc(player_lstack, sizeof(char*)*(player_nstack+1));
|
|
player_lstack[player_nstack] = strdup(loc);
|
|
player_nstack++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|