Lephisto/src/player.c

1692 lines
47 KiB
C

#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 "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.
#define XML_GUI_TAG "gui"
#define XML_START_ID "Start"
#define GUI_DATA "../dat/gui.xml"
#define GUI_GFX "../gfx/gui/"
#define START_DATA "../dat/start.xml"
// Player stuff.
Pilot* player = NULL; // extern in pilot.h
static Ship* player_ship = NULL; // Temp ship to hold when naming it.
// More hacks.
static double player_px, player_py, player_vx, player_vy, player_dir;
static int player_credits = 0; // Temp hack.
// Player pilot stack - Ships she owns.
static Pilot** player_stack = NULL;
static char** player_lstack = NULL; // Names of the planet the ships are at.
static int player_nstack = 0;
// Player global properties.
char* player_name = NULL; // Player name.
int player_crating = 0; // Ze rating.
unsigned int player_flags = 0; // Player flags.
// Input.c
double player_turn = 0.; // Turn velocity from input.
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;
static Vec2 player_cam;
static int* missions_done = NULL; // Saves position.
static int missions_mdone = 0;
static int missions_ndone = 0;
// Pilot stuff for GUI.
extern Pilot** pilot_stack;
extern int pilots;
// Space stuff for GUI.
extern StarSystem* systems_stack;
// GUI crap.
typedef struct Radar_ {
double x,y; // Position.
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 Rect_ {
double x,y;
double w,h;
} Rect;
typedef struct GUI_ {
// Graphics.
glTexture* gfx_frame;
glTexture* gfx_targetPilot, *gfx_targetPlanet;
Radar radar;
Rect nav;
Rect shield, armour, energy;
Rect weapon;
Rect target_health, target_name, target_faction;
Rect misc;
Rect msg;
// Positions.
Vec2 frame;
Vec2 target;
} GUI;
GUI gui = {
.gfx_frame = NULL,
.gfx_targetPilot = NULL,
.gfx_targetPlanet = NULL
};
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 Msg_ {
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 player_newMake(void);
static void player_newShipMake(char* name);
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);
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);
// Prompt player name.
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);
// Cleanup player stuff if we'll be re-creating.
player_cleanup();
var_cleanup();
missions_cleanup();
player_name = dialogue_input("Player Name", 3, 20,
"Please tell me your name:");
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();
}
// Create a new player.
static void player_newMake(void) {
Ship* ship;
char system[20];
uint32_t bufsize;
char* buf = pack_readfile(DATA, START_DATA, &bufsize);
int l, h, tl, th;
double x, y;
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")) snprintf(system, 20, 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);
// Create the player and start the game.
player_newShip(ship, x, y, 0., 0., RNG(0, 359)/180.*M_PI);
space_init(system);
// Clear the map.
map_clear();
}
// Create a dialogue to name the new ship.
void 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("Player Name", 3, 20,
"Please name your shiny new %s", ship->name);
player_newShipMake(ship_name);
free(ship_name);
}
// Change the players ship.
static void player_newShipMake(char* name) {
Vec2 vp, vv;
if(player) {
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);
vect_cset(&vp, player_px, player_py);
vect_cset(&vv, player_vx, player_vy);
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;
}
// Swaps the current ship with shipname.
void player_swapShip(char* shipname) {
int i;
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];
ship->credits = player->credits;
player_stack[i] = player;
pilot_stack[0] = player = ship;
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);
}
// Clean up player stuff like player_stack.
void player_cleanup(void) {
int i;
player_clear();
// Cleanup name.
if(player_name != NULL) free(player_name);
// Cleanup 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;
}
if(missions_done != NULL) {
free(missions_done);
missions_done = NULL;
missions_ndone = 0;
missions_mdone = 0;
}
}
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;
}
void player_warp(const double x, const double y) {
vect_cset(&player->solid->pos, x, y);
}
// Clear the targets.
void player_clear(void) {
player_target = PLAYER_ID;
planet_target = -1;
hyperspace_target = -1;
}
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];
}
// Return amount of outfits the player owns.
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;
}
// Return how many of the commodity the player has.
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;
if(!pilot_rmMissionCargo(player, cargo_id)) return; // Already done.
for(i = 0; i < player_nstack; i++)
if(!pilot_rmMissionCargo(player_stack[i], cargo_id))
return; // Success.
}
// Render the background player stuff, namely planet target
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];
if(areEnemies(player->faction, planet->faction)) c = &cHostile;
else c = &cNeutral;
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.
}
}
// Render the player.
void player_render(void) {
int i, j;
double x, y;
char str[10];
Pilot* p;
glColour* c;
glFont* f;
// 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) &&
(SDL_GetTicks() > player_timer)) {
menu_death();
}
}
else if(!player_isFlag(PLAYER_CREATING))
pilot_render(player);
// Fancy cinematic scene borders.
spfx_cinematic();
return;
}
if(player == NULL) return;
// 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 = &cNeutral;
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.
}
// Render the player.
pilot_render(player);
// 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 < 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_LINES);
// Player. -- Drawn last.
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.
c = space_canHyperspace(player) ? &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),
systems_stack[cur_system->jumps[hyperspace_target]].name);
}
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);
gl_blitStatic(p->ship->gfx_target, gui.target.x, gui.target.y, 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", player->cargo_free);
// Messages.
x = gui.msg.x;
y = gui.msg.y + (double)(gl_defFont.h * msg_max)*1.2;
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 < SDL_GetTicks())
msg_stack[msg_max-i-1].str[0] = '\0';
else gl_print(NULL, x, y, NULL, "%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)SDL_GetTicks();
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;
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) COLOUR(cRadar_targ);
else if(pilot_isDisabled(p)) COLOUR(cInert);
else if(pilot_isFlag(p, PILOT_HOSTILE)) COLOUR(cHostile);
else COLOUR(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 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();
}
// Init GUI.
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));
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 = 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, *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.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
// 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);
}
// Used in pilot.c
// Basically uses keyboard input instead of AI input.
void player_think(Pilot* player) {
// Last I checked, the dead didn't think..
if(pilot_isFlag(player, PILOT_DEAD)) {
// No point in accelerating or turning.
player->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(player,
vect_angle(&player->solid->pos,
&pilot_get(player_target)->solid->pos));
else if(planet_target != -1)
pilot_face(player,
vect_angle(&player->solid->pos,
&cur_system->planets[planet_target].pos));
}
// PLAYER_REVERSE will take over navigation.
else if(player_isFlag(PLAYER_REVERSE) && (VMOD(player->solid->vel) > 0.))
pilot_face(player, VANGLE(player->solid->vel) + M_PI);
// Normal navigation sheme.
else {
player->solid->dir_vel = 0.;
if(player_turn)
player->solid->dir_vel -= player->turn * player_turn;
}
if(player_isFlag(PLAYER_PRIMARY)) pilot_shoot(player, player_target, 0);
if(player_isFlag(PLAYER_SECONDARY)) // Needs a target.
pilot_shoot(player, player_target, 1);
if(player_isFlag(PLAYER_AFTERBURNER))
vect_pset(&player->solid->force,
player->thrust * player->afterburner->outfit->u.afb.thrust_perc +
player->afterburner->outfit->u.afb.thrust_abs, player->solid->dir);
else
vect_pset(&player->solid->force, player->thrust * player_acc,
player->solid->dir);
// Set the listener stuff.
sound_listener(player->solid->dir,
player->solid->pos.x, player->solid->pos.y,
player->solid->vel.x, player->solid->vel.y);
}
// Modify the radar resolution.
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);
}
// 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)) {
player->secondary = player->outfits + i;
break;
}
// We didn't find an outfit.
if(i >= player->noutfits)
player->secondary = NULL;
// Set ammo.
pilot_setAmmo(player);
}
// 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;
}
// Attempt 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(!areEnemies(player->faction, planet->faction)) {
player_message("%s> Permission to land granted.", planet->name);
player_setFlag(PLAYER_LANDACK);
} else {
player_message("%s> Land request denied.", planet->name);
}
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);
player_land(); // Re-run land protocol.
}
}
void player_targetHyperspace(void) {
planet_target = -1; // Remove planet target.
player_rmFlag(PLAYER_LANDACK); // Get rid of landing permission.
hyperspace_target++;
if(hyperspace_target >= cur_system->njumps)
hyperspace_target = -1;
}
// Actually attempt to jump into hyperspace.
void player_jump(void) {
if((hyperspace_target == -1) ||
pilot_isFlag(player, PILOT_HYP_PREP) ||
pilot_isFlag(player, PILOT_HYP_BEGIN) ||
pilot_isFlag(player, PILOT_HYPERSPACE))
return;
int 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");
}
// Player actually broke hyperspace (Let's enter a 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!");
}
// Make the player face her hyperspace target.
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);
}
// Activate afterburner.
void player_afterburn(void) {
// TODO: Fancy effects.
if(player->afterburner != NULL) {
player_setFlag(PLAYER_AFTERBURNER);
pilot_setFlag(player, PILOT_AFTERBURNER);
spfx_shake(player->afterburner->outfit->u.afb.rumble * SHAKE_MAX);
}
}
void player_afterburnOver(void) {
if(player->afterburner != NULL) {
player_rmFlag(PLAYER_AFTERBURNER);
pilot_rmFlag(player, PILOT_AFTERBURNER);
}
}
// Take a screenshot.
static int screenshot_cur = 0;
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);
}
// Player go pwned.
void player_dead(void) {
gui_xoff = 0.;
gui_yoff = 0.;
}
// 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;
}
// Return a buffer with all the ships names, or none if there
// aren't any.
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;
}
// Return the amount of ships player has in storage.
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;
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.
xmlw_startElem(writer, "missions_done");
for(i = 0; i < missions_ndone; i++)
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 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));
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;
Pilot* ship;
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;
do {
if(!is_player == 0) xmlr_str(node, "location", loc);
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.
pilot_addOutfit(ship, outfit_get(xml_get(cur)), 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));
// 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;
}