#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" #define TARGET_WIDTH 128 #define TARGET_HEIGHT 96 /* Player stuff. */ Pilot* player = NULL; /* extern in pilot.h */ static Ship* player_ship = NULL; /* Temp ship to hold when naming it. */ static alVoice* player_voice = NULL; /* Player's voice. */ 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. */ 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; 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 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; 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. */ /* Creation. */ static void player_newMake(void); static void player_newShipMake(char* name); /* Sound. */ static void player_initSound(void); static void player_playSound(ALuint sound, int once); 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); /* 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); /* 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:"); 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* 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); /* Create the player and start the game. */ player_newShip(ship, x, y, 0., 0., RNG(0, 359)/180.*M_PI); space_init(sysname); free(sysname); /* 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; /* Store the current ship if it exists. */ 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); /* 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; } /* Swaps the current ship with shipname. */ 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); } /* 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; } } /* Initializes the player sound. */ static void player_initSound(void) { if(player_voice == NULL) { player_voice = sound_addVoice(0, /* Max priority. */ 0., 0., 0., 0., 0., /* No properties. */ VOICE_LOOPING | VOICE_STATIC); } } /* Play a sound. */ static void player_playSound(ALuint sound, int once) { unsigned int flags = VOICE_STATIC; if(once == 0) flags |= VOICE_LOOPING; voice_buffer(player_voice, sound, flags); } static void player_stopSound(void) { voice_stop(player_voice); } 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; /* 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. */ } /* 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; StarSystem* sys; /* 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); /* 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]]; 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), (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; 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* 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; } if(player_isFlag(PLAYER_PRIMARY)) pilot_shoot(pplayer, player_target, 0); if(player_isFlag(PLAYER_SECONDARY)) /* Needs a target. */ pilot_shoot(pplayer, player_target, 1); if(player_isFlag(PLAYER_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 vect_pset(&pplayer->solid->force, pplayer->thrust * player_acc, pplayer->solid->dir); /* Set the listener stuff. */ sound_listener(pplayer->solid->dir, pplayer->solid->pos.x, pplayer->solid->pos.y, pplayer->solid->vel.x, pplayer->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); /* No landable planet. */ if(planet_target < 0) return; 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++; map_clear(); /* Clear the current map path. */ 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 != NULL) && (player->afterburner != NULL)) { player_setFlag(PLAYER_AFTERBURNER); pilot_setFlag(player, PILOT_AFTERBURNER); spfx_shake(player->afterburner->outfit->u.afb.rumble * SHAKE_MAX); player_playSound(player->afterburner->outfit->u.afb.sound, 0); } } void player_afterburnOver(void) { if((player != NULL) && (player->afterburner != NULL)) { player_rmFlag(PLAYER_AFTERBURNER); pilot_rmFlag(player, PILOT_AFTERBURNER); player_stopSound(); } } /* Start accelerating. */ void player_accel(double acc) { if(player != NULL) { player_acc = acc; player_playSound(player->ship->sound, 0); } } void player_accelOver(void) { player_acc = 0; player_stopSound(); } /* 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 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; 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. */ 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)); /* 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; }