#include #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; }