Lephisto/src/space.c

1060 lines
31 KiB
C

#include <malloc.h>
#include <math.h>
#include "xml.h"
#include "lephisto.h"
#include "opengl.h"
#include "log.h"
#include "physics.h"
#include "rng.h"
#include "pack.h"
#include "space.h"
#include "faction.h"
#include "xml.h"
#include "pause.h"
#include "weapon.h"
#include "toolkit.h"
#include "spfx.h"
#include "ltime.h"
#include "nebulae.h"
#include "player.h"
#define XML_PLANET_ID "Planets"
#define XML_PLANET_TAG "planet"
#define XML_SYSTEM_ID "Systems"
#define XML_SYSTEM_TAG "ssys"
#define PLANET_DATA "../dat/planet.xml"
#define SYSTEM_DATA "../dat/ssys.xml"
#define PLANET_GFX_SPACE "../gfx/planet/space/"
#define PLANET_GFX_EXTERIOR "../gfx/planet/exterior/"
#define PLANET_GFX_EXTERIOR_W 400
#define PLANET_GFX_EXTERIOR_H 400
/* Overcome warning due to zero value. */
#define FLAG_XSET (1<<0)
#define FLAG_YSET (1<<1)
#define FLAG_ASTEROIDSSET (1<<2)
#define FLAG_INTEFERENCESET (1<<3)
#define FLAG_SERVICESET (1<<4)
#define FLAG_TECHSET (1<<5)
#define FLAG_FACTIONSET (1<<6)
/* Planet <-> system name stack. */
static char** planetname_stack = NULL;
static char** systemname_stack = NULL;
static int spacename_nstack = 0;
/* Star system stack and co. */
StarSystem* systems_stack = NULL; /* Star system stack. */
int systems_nstack = 0; /* Number of star systems. */
static int total_planets = 0; /* Total number of loaded planets - A little silly. */
StarSystem* cur_system = NULL; /* Current star system. */
/* Fleet spawn rate. */
unsigned int spawn_timer = 0; /* Controls spawn rate. */
/* Star stack and co. */
#define STAR_BUF 100 /* Area to leave around screen, more = less repitition. */
typedef struct Star_ {
double x, y; /* Position. It is simpler ligher to use two doubles than the physics. */
double brightness;
} Star;
static Star* stars = NULL; /* Star array. */
static int nstars = 0; /* Total stars. */
static int mstars = 0; /* Memory stars are taking. */
/* Intern. */
static Planet* planet_pull(const char* name);
static void space_renderStars(const double dt);
static void space_addFleet(Fleet* fleet);
static StarSystem* system_parse(const xmlNodePtr parent);
static void system_parseJumps(const xmlNodePtr parent);
static PlanetClass planetclass_get(const char a);
/* Extern. */
extern void player_message(const char* fmt, ...);
void planets_minimap(const double res, const double w,
const double h, const RadarShape shape);
int space_sysSave(xmlTextWriterPtr writer);
int space_sysLoad(xmlNodePtr parent);
/* Draw the planet. Used in planet.c */
/* Matrix mode is already displaced to center of the minimap. */
#define PIXEL(x,y) if((shape == RADAR_RECT && ABS(x)<w/2. && ABS(y)<h/2.) || \
(shape == RADAR_CIRCLE && (((x)*(x)+(y)*(y)) < rc))) glVertex2i((x),(y))
void planets_minimap(const double res, const double w,
const double h, const RadarShape shape) {
int i;
int cx, cy, x, y, r, rc;
double p;
Planet* planet;
glColour* col;
if(shape == RADAR_CIRCLE) rc = (int)(w*w);
glBegin(GL_POINTS);
for(i = 0; i < cur_system->nplanets; i++) {
planet = &cur_system->planets[i];
col = faction_getColour(planet->faction);
if((col != &cHostile) && !planet_hasService(planet, PLANET_SERVICE_BASIC))
col = &cInert; /* Override non-hostile planets without services. */
COLOUR(*col);
r = (int)(cur_system->planets[i].gfx_space->sw / res);
cx = (int)((cur_system->planets[i].pos.x - player->solid->pos.x) / res);
cy = (int)((cur_system->planets[i].pos.y - player->solid->pos.y) / res);
x = 0;
y = r;
p = (5. - (double)(r*3)) / 4.;
PIXEL(cx, cy+y);
PIXEL(cx, cy-y);
PIXEL(cx+y, cy);
PIXEL(cx-y, cy);
while(x < y) {
x++;
if(p < 0) p += 2*(double)(x)+1;
else p += 2*(double)(x-(--y))+1;
if(x == 0) {
PIXEL(cx, cy+y);
PIXEL(cx, cy-y);
PIXEL(cx+y, cy);
PIXEL(cx-y, cy);
} else
if(x == y) {
PIXEL(cx+x, cy+y);
PIXEL(cx-x, cy+y);
PIXEL(cx+x, cy-y);
PIXEL(cx-x, cy-y);
} else
if(x < y) {
PIXEL(cx+x, cy+y);
PIXEL(cx-x, cy+y);
PIXEL(cx+x, cy-y);
PIXEL(cx-x, cy-y);
PIXEL(cx+y, cy+x);
PIXEL(cx-y, cy+x);
PIXEL(cx+y, cy-x);
PIXEL(cx-y, cy-x);
}
}
}
glEnd();
}
#undef PIXEL
/* Basically return a PlanetClass integer from a char. */
static PlanetClass planetclass_get(const char a) {
switch(a) {
/* Planets use letters. */
case 'A': return PLANET_CLASS_A;
case 'B': return PLANET_CLASS_B;
case 'C': return PLANET_CLASS_C;
case 'D': return PLANET_CLASS_D;
case 'E': return PLANET_CLASS_E;
case 'F': return PLANET_CLASS_F;
case 'G': return PLANET_CLASS_G;
case 'H': return PLANET_CLASS_H;
case 'I': return PLANET_CLASS_I;
case 'J': return PLANET_CLASS_J;
case 'K': return PLANET_CLASS_K;
case 'L': return PLANET_CLASS_L;
case 'M': return PLANET_CLASS_M;
case 'N': return PLANET_CLASS_N;
case 'O': return PLANET_CLASS_O;
case 'P': return PLANET_CLASS_P;
case 'Q': return PLANET_CLASS_Q;
case 'R': return PLANET_CLASS_R;
case 'S': return PLANET_CLASS_S;
case 'T': return PLANET_CLASS_T;
case 'X': return PLANET_CLASS_X;
case 'Y': return PLANET_CLASS_Y;
case 'Z': return PLANET_CLASS_Z;
/* Stations use numbers as there isn't as many. */
case '0' : return STATION_CLASS_A;
case '1' : return STATION_CLASS_B;
case '2' : return STATION_CLASS_C;
case '3' : return STATION_CLASS_D;
default:
WARN("Invalid planet class.");
return PLANET_CLASS_NULL;
};
}
char planet_getClass(Planet* p) {
switch(p->class) {
case PLANET_CLASS_A: return 'A';
case PLANET_CLASS_B: return 'B';
case PLANET_CLASS_C: return 'C';
case PLANET_CLASS_D: return 'D';
case PLANET_CLASS_E: return 'E';
case PLANET_CLASS_F: return 'F';
case PLANET_CLASS_G: return 'G';
case PLANET_CLASS_H: return 'H';
case PLANET_CLASS_I: return 'I';
case PLANET_CLASS_J: return 'J';
case PLANET_CLASS_K: return 'K';
case PLANET_CLASS_L: return 'L';
case PLANET_CLASS_M: return 'M';
case PLANET_CLASS_N: return 'N';
case PLANET_CLASS_O: return 'O';
case PLANET_CLASS_P: return 'P';
case PLANET_CLASS_Q: return 'Q';
case PLANET_CLASS_R: return 'R';
case PLANET_CLASS_S: return 'S';
case PLANET_CLASS_T: return 'T';
case PLANET_CLASS_X: return 'X';
case PLANET_CLASS_Y: return 'Y';
case PLANET_CLASS_Z: return 'Z';
/* Stations. */
case STATION_CLASS_A: return '0';
case STATION_CLASS_B: return '1';
case STATION_CLASS_C: return '2';
case STATION_CLASS_D: return '3';
default:
WARN("Invalid planet class.");
return 0;
};
}
/* Check distance to ensure we can go into hyperspace. */
int space_canHyperspace(Pilot* p) {
int i;
double d;
if(p->fuel < HYPERSPACE_FUEL) return 0;
for(i = 0; i < cur_system->nplanets; i++) {
d = vect_dist(&p->solid->pos, &cur_system->planets[i].pos);
if(d < MIN_HYPERSPACE_DIST)
return 0;
}
return 1;
}
/* Hyperspace, returns 0 if entering hyperspace, or the distance if not. */
int space_hyperspace(Pilot* p) {
if(p->fuel < HYPERSPACE_FUEL) return -3;
if(!space_canHyperspace(p)) return -1;
/* Pilot is now going to get automatically ready for hyperspace. */
pilot_setFlag(p, PILOT_HYP_PREP);
return 0;
}
/* Return the name of all the planets that belong to factions. */
char** space_getFactionPlanet(int* nplanets, int* factions, int nfactions) {
int i, j, k;
Planet* planet;
char** tmp;
int ntmp;
int mtmp;
ntmp = 0;
mtmp = 25;
tmp = malloc(sizeof(char*) * mtmp);
for(i = 0; i < systems_nstack; i++)
for(j = 0; j < systems_stack[i].nplanets; j++) {
planet = &systems_stack[i].planets[j];
for(k = 0; k < nfactions; k++)
if(planet->faction == factions[k]) {
ntmp++;
if(ntmp > mtmp) {
mtmp += 25;
tmp = realloc(tmp, sizeof(char*) * mtmp);
}
tmp[ntmp-1] = planet->name;
break; /* No need to check all factions. */
}
}
(*nplanets) = ntmp;
return tmp;
}
/* Return the name of a random planet. */
char* space_getRndPlanet(void) {
int i, j;
char** tmp;
int ntmp;
int mtmp;
char* res;
ntmp = 0;
mtmp = 25;
tmp = malloc(sizeof(char)*mtmp);
for(i = 0; i < systems_nstack; i++)
for(j = 0; j < systems_stack[i].nplanets; j++) {
ntmp++;
if(ntmp > mtmp) {
mtmp += 25;
tmp = realloc(tmp, sizeof(char*) * mtmp);
}
tmp[ntmp-1] = systems_stack[i].planets[j].name;
}
res = tmp[RNG(0, ntmp-1)];
free(tmp);
return res;
}
/* Return 1 if player can reach the system. */
int space_sysReachable(StarSystem* sys) {
int i;
if(sys_isKnown(sys)) return 1; /* It is known. */
/* Check to see if it is adjacent to known. */
for(i = 0; i < sys->njumps; i++)
if(sys_isKnown(&systems_stack[sys->jumps[i]]))
return 1;
return 0;
}
/* Get the system from it's name. */
StarSystem* system_get(const char* sysname) {
int i;
for(i = 0; i < systems_nstack; i++)
if(strcmp(sysname, systems_stack[i].name)==0)
return &systems_stack[i];
DEBUG("System '%s' not found in stack", sysname);
return NULL;
}
/* Get the name of a system from a planetname. */
char* planet_getSystem(char* planetname) {
int i;
for(i = 0; i < spacename_nstack; i++)
if(strcmp(planetname_stack[i], planetname)==0)
return systemname_stack[i];
DEBUG("Planet '%s' not found in planetname stack", planetname);
return NULL;
}
/* Get a planet based on it's name. */
Planet* planet_get(char* planetname) {
int i;
char* sysname;
StarSystem* sys;
sysname = planet_getSystem(planetname);
sys = system_get(sysname);
for(i = 0; i < sys->nplanets; i++)
if(strcmp(planetname, sys->planets[i].name)==0)
return &sys->planets[i];
DEBUG("Planet '%s' not found in the universe", planetname);
return NULL;
}
/* Basically used for spawning fleets. */
void space_update(const double dt) {
unsigned int t;
int i, j, f;
(void)dt; /* Don't need it right now. */
if(cur_system == NULL) return; /* Can't update a null system. */
t = SDL_GetTicks();
if(cur_system->nfleets == 0)
/* Please stop checking that there are no fleets. */
spawn_timer = t + 300000;
if(spawn_timer < t) {
/* Time to possibly spawn. */
/* Spawn chance is based on overall percentage. */
f = RNG(0, 100*cur_system->nfleets);
j = 0;
for(i = 0; i < cur_system->nfleets; i++) {
j += cur_system->fleets[i].chance;
if(f < j) {
/* Add one fleet. */
space_addFleet(cur_system->fleets[i].fleet);
break;
}
}
spawn_timer = t + 60000./(float)cur_system->nfleets;
}
}
/* Crate a fleet. */
static void space_addFleet(Fleet* fleet) {
FleetPilot* plt;
int i;
double a;
Vec2 vv, vp, vn;
/* Simulate them coming from hyperspace. */
vect_pset(&vp, RNG(MIN_HYPERSPACE_DIST, MIN_HYPERSPACE_DIST*3),
RNG(0, 360)*M_PI/180.);
vectnull(&vn);
for(i = 0; i < fleet->npilots; i++)
plt = &fleet->pilots[i];
if(RNG(0, 100) <= plt->chance) {
vect_cadd(&vp, RNG(75, 150) * (RNG(0,1) ? 1 : -1),
RNG(75, 150) * (RNG(0,1) ? 1 : -1));
a = vect_angle(&vp, &vn);
vect_pset(&vv, plt->ship->speed * 2., a);
pilot_create(plt->ship,
plt->name,
fleet->faction,
(plt->ai != NULL) ? fleet->ai : fleet->ai, /* Pilot AI override. */
a,
&vp,
&vv,
0);
}
}
/* Init the system. */
void space_init(const char* sysname) {
char* lt;
int i;
/* Cleanup some stuff. */
player_clear(); /* Clears targets. */
pilots_clean(); /* Destroy all the current pilots, exept player. */
weapon_clear(); /* Get rid of all the weapons. */
spfx_clear(); /* Remove of explosions. */
if((sysname == NULL) && (cur_system == NULL))
ERR("Cannot reinit system if there is no system previously loaded");
else if(sysname != NULL) {
for(i = 0; i < systems_nstack; i++)
if(strcmp(sysname, systems_stack[i].name)==0)
break;
if(i == systems_nstack) ERR("System %s not found in stack", sysname);
cur_system = systems_stack+i;
lt = ltime_pretty(0);
player_message("Entering System %s on %s.", sysname, lt);
free(lt);
/* Handle background. */
if(cur_system->nebu_density > 0.) {
/* Background is nebulae. */
nebu_prep(cur_system->nebu_density, cur_system->nebu_volatility);
} else {
/* Background is stary. */
nstars = (cur_system->stars*SCREEN_W*SCREEN_H+STAR_BUF*STAR_BUF)/(800*640);
if(mstars < nstars)
stars = realloc(stars, sizeof(Star)*nstars); /* Should realloc, not malloc. */
for(i = 0; i < nstars; i++) {
stars[i].brightness = (double)RNG(50, 200)/256.;
stars[i].x = (double)RNG(-STAR_BUF, SCREEN_W + STAR_BUF);
stars[i].y = (double)RNG(-STAR_BUF, SCREEN_H + STAR_BUF);
}
}
}
/* Set up fleets -> pilots. */
for(i = 0; i < cur_system->nfleets; i++)
if(RNG(0,100) <= (cur_system->fleets[i].chance/2)) /* Fleet check (50% chance). */
space_addFleet(cur_system->fleets[i].fleet);
/* Start the spawn timer. */
spawn_timer = SDL_GetTicks() + 120000./(float)(cur_system->nfleets+1);
/* We now know this system. */
sys_setFlag(cur_system, SYSTEM_KNOWN);
}
/* Load the planets of name 'name'. */
static Planet* planet_pull(const char* name) {
int i;
Planet* tmp = NULL;
char str[PATH_MAX] = "\0";
char* tstr;
uint32_t flags = 0;
uint32_t bufsize;
char* buf = pack_readfile(DATA, PLANET_DATA, &bufsize);
xmlNodePtr node, cur, ccur;
xmlDocPtr doc = xmlParseMemory(buf, bufsize);
node = doc->xmlChildrenNode;
if(strcmp((char*)node->name, XML_PLANET_ID)) {
ERR("Malformed "PLANET_DATA" file: missing root element '"XML_PLANET_ID"'");
return NULL;
}
node = node->xmlChildrenNode; /* First system node. */
if(node == NULL) {
ERR("Malformed "PLANET_DATA" file: does not contain elements");
return NULL;
}
do {
if(xml_isNode(node, XML_PLANET_TAG)) {
tstr = xml_nodeProp(node, "name");
if(strcmp(tstr, name)==0) { /* Found. */
tmp = CALLOC_L(Planet);
tmp->faction = -1; /* No faction. */
tmp->name = tstr;
node = node->xmlChildrenNode;
do {
if(xml_isNode(node, "GFX")) {
cur = node->children;
do {
if(xml_isNode(cur, "space")) {
/* Load space gfx. */
snprintf(str, strlen(xml_get(cur))+sizeof(PLANET_GFX_SPACE),
PLANET_GFX_SPACE"%s", xml_get(cur));
tmp->gfx_space = gl_newImage(str);
}
else if(xml_isNode(cur, "exterior")) {
/* Load land gfx. */
tmp->gfx_exterior = malloc(strlen(xml_get(cur))+sizeof(PLANET_GFX_EXTERIOR));
snprintf(tmp->gfx_exterior, strlen(xml_get(cur))+sizeof(PLANET_GFX_EXTERIOR),
PLANET_GFX_EXTERIOR"%s", xml_get(cur));
}
} while(xml_nextNode(cur));
}
else if(xml_isNode(node, "pos")) {
cur = node->children;
do {
if(xml_isNode(cur, "x")) {
flags |= FLAG_XSET;
tmp->pos.x = xml_getFloat(cur);
}
else if(xml_isNode(cur, "y")) {
flags |= FLAG_YSET;
tmp->pos.y = xml_getFloat(cur);
}
} while(xml_nextNode(cur));
}
else if(xml_isNode(node, "general")) {
cur = node->children;
do {
if(xml_isNode(cur, "class"))
tmp->class = planetclass_get(cur->children->content[0]);
else if(xml_isNode(cur, "faction")) {
flags |= FLAG_FACTIONSET;
tmp->faction = faction_get(xml_get(cur));
}
else if(xml_isNode(cur, "description"))
tmp->description = strdup(xml_get(cur));
else if(xml_isNode(cur, "bar"))
tmp->bar_description = strdup(xml_get(cur));
else if(xml_isNode(cur, "services")) {
flags |= FLAG_SERVICESET;
tmp->services = xml_getInt(cur);
}
else if(xml_isNode(cur, "tech")) {
ccur = cur->children;
do {
if(xml_isNode(ccur, "main")) {
flags |= FLAG_TECHSET;
tmp->tech[0] = xml_getInt(ccur);
}
else if(xml_isNode(ccur, "special")) {
for(i = 1; i < PLANET_TECH_MAX; i++)
if(tmp->tech[i]==0) {
tmp->tech[i] = xml_getInt(ccur);
break;
}
if(i == PLANET_TECH_MAX) WARN("Planet '%s' has too many"
"'special tech' entries", tmp->name);
}
} while(xml_nextNode(ccur));
}
else if(xml_isNode(cur, "commodities")) {
ccur = cur->children;
do {
if(xml_isNode(ccur, "commodity")) {
tmp->commodities = realloc(tmp->commodities,
(tmp->ncommodities+1) * sizeof(Commodity*));
tmp->commodities[tmp->ncommodities] =
commodity_get(xml_get(ccur));
tmp->ncommodities++;
}
} while(xml_nextNode(ccur));
}
} while(xml_nextNode(cur));
}
} while(xml_nextNode(node));
break;
} else
free(tstr); /* xmlGetProp mallocs the string. */
}
} while(xml_nextNode(node));
xmlFreeDoc(doc);
free(buf);
xmlCleanupParser();
/* Check elements. */
if(tmp) {
#define MELEMENT(o,s) if(o) WARN("Planet '%s' missing '"s"' element", tmp->name)
MELEMENT(tmp->gfx_space==NULL, "GFX_space");
MELEMENT(planet_hasService(tmp, PLANET_SERVICE_LAND) &&
tmp->gfx_exterior==NULL, "GFX exterior");
MELEMENT((flags&FLAG_XSET)==0, "x");
MELEMENT((flags&FLAG_YSET)==0, "y");
MELEMENT(tmp->class==PLANET_CLASS_NULL, "class");
MELEMENT(planet_hasService(tmp, PLANET_SERVICE_LAND) &&
tmp->description==NULL, "description");
MELEMENT(planet_hasService(tmp, PLANET_SERVICE_BASIC) &&
tmp->bar_description==NULL, "bar");
MELEMENT(planet_hasService(tmp, PLANET_SERVICE_BASIC) &&
(flags & FLAG_FACTIONSET)==0, "faction");
MELEMENT((flags&FLAG_SERVICESET)==0, "services");
MELEMENT((planet_hasService(tmp, PLANET_SERVICE_OUTFITS) ||
planet_hasService(tmp, PLANET_SERVICE_SHIPYARD)) &&
(flags&FLAG_TECHSET)==0, "tech");
MELEMENT(planet_hasService(tmp, PLANET_SERVICE_COMMODITY) &&
(tmp->ncommodities==0), "commodity");
#undef MELEMENT
} else
WARN("No planet found matching name '%s'", name);
return tmp;
}
/* Parse node 'parent' which should be the node of a system. */
/* Return the StarSystem fully loaded. */
static StarSystem* system_parse(const xmlNodePtr parent) {
Planet* planet = NULL;
SystemFleet* fleet = NULL;
StarSystem* tmp = CALLOC_L(StarSystem);
char* ptrc;
xmlNodePtr cur, node;
uint32_t flags;
tmp->name = xml_nodeProp(parent, "name"); /* Already mallocs. */
node = parent->xmlChildrenNode;
do {
/* Load all the things! */
if(xml_isNode(node, "pos")) {
cur = node->children;
do {
if(xml_isNode(cur, "x")) {
flags |= FLAG_XSET;
tmp->pos.x = xml_getFloat(cur);
}
if(xml_isNode(cur, "y")) {
flags |= FLAG_YSET;
tmp->pos.y = xml_getFloat(cur);
}
} while(xml_nextNode(cur));
}
else if(xml_isNode(node, "general")) {
cur = node->children;
do {
if(xml_isNode(cur, "stars")) /* Non-zero. */
tmp->stars = xml_getInt(cur);
else if(xml_isNode(cur, "asteroids")) {
flags |= FLAG_ASTEROIDSSET;
tmp->asteroids = xml_getInt(cur);
}
else if(xml_isNode(cur, "interference")) {
flags |= FLAG_INTEFERENCESET;
tmp->interference = xml_getFloat(cur)/100;
}
else if(xml_isNode(cur, "nebulae")) {
ptrc = xml_nodeProp(cur, "volatility");
if(ptrc != NULL) { /* Has volatility. */
tmp->nebu_volatility = atof(ptrc);
free(ptrc);
}
tmp->nebu_density = xml_getFloat(cur);
}
}while(xml_nextNode(cur));
}
/* Load all the planets. */
else if(xml_isNode(node, "planets")) {
cur = node->children;
do {
if(cur && xml_isNode(cur, "planet")) {
/* Add planet to system. */
total_planets++; /* Increase planet counter. */
planet = planet_pull(xml_get(cur));
tmp->planets = realloc(tmp->planets, sizeof(Planet)*(++tmp->nplanets));
memcpy(tmp->planets+(tmp->nplanets-1), planet, sizeof(Planet));
/* Add planet <-> star system to name stack. */
spacename_nstack++;
planetname_stack = realloc(planetname_stack,
sizeof(char*)*spacename_nstack);
systemname_stack = realloc(systemname_stack,
sizeof(char*)*spacename_nstack);
planetname_stack[spacename_nstack-1] = planet->name;
systemname_stack[spacename_nstack-1] = tmp->name;
free(planet);
}
} while(xml_nextNode(cur));
}
/* Load all the fleets. */
else if(xml_isNode(node, "fleets")) {
cur = node->children;
do {
if(cur && xml_isNode(cur, "fleet")) {
fleet = CALLOC_L(SystemFleet);
fleet->fleet = fleet_get(xml_get(cur));
if(fleet->fleet == NULL)
WARN("Fleet %s for Star System %s not found", xml_get(cur), tmp->name);
ptrc = xml_nodeProp(cur, "chance"); /* Malloc ptrc. */
fleet->chance = atoi(ptrc);
if(fleet->chance == 0)
WARN("Fleet %s for Star System %s has 0%% chance to appear",
fleet->fleet->name, tmp->name);
if(ptrc) free(ptrc); /* Free the ptrc. */
tmp->fleets = realloc(tmp->fleets, sizeof(SystemFleet)*(++tmp->nfleets));
memcpy(tmp->fleets+(tmp->nfleets-1), fleet, sizeof(SystemFleet));
free(fleet);
}
} while(xml_nextNode(cur));
}
} while(xml_nextNode(node));
/* Check elements. */
#define MELEMENT(o,s) if((o) == 0) WARN("Star System '%s' missing '"s"' element", tmp->name)
MELEMENT(flags&FLAG_XSET, "x");
MELEMENT(flags&FLAG_YSET, "y");
MELEMENT(tmp->stars, "stars");
MELEMENT(flags&FLAG_ASTEROIDSSET, "asteroids"); /* Can be 0. */
MELEMENT(flags&FLAG_INTEFERENCESET, "inteference");
#undef MELEMENT
/* Post processing. */
if(tmp->nplanets > 0)
/* TODO: Make dependant on overall planet faction. */
tmp->faction = tmp->planets[0].faction;
return tmp;
}
/* Load the jumps into a system. */
static void system_parseJumps(const xmlNodePtr parent) {
int i;
StarSystem* sys;
char* name;
xmlNodePtr cur, node;
name = xml_nodeProp(parent, "name"); /* Already mallocs. */
for(i = 0; i < systems_nstack; i++)
if(strcmp(systems_stack[i].name, name)==0) {
sys = &systems_stack[i];
break;
}
if(i == systems_nstack)
WARN("System '%s' was not found in the stack for some reason", name);
free(name); /* No need for it now. */
node = parent->xmlChildrenNode;
do {
/* Load the data. */
if(xml_isNode(node, "jumps")) {
cur = node->children;
do {
if(xml_isNode(cur, "jump")) {
for(i = 0; i < systems_nstack; i++)
if(strcmp(systems_stack[i].name, xml_get(cur))==0) {
sys->njumps++;
sys->jumps = realloc(sys->jumps, sys->njumps*sizeof(int));
sys->jumps[sys->njumps-1] = i;
break;
}
if(i == systems_nstack)
WARN("System '%s' not found for jump linking", xml_get(cur));
}
} while(xml_nextNode(cur));
}
} while(xml_nextNode(node));
}
/* Load the ENTIRE universe into RAM. -- WOAH! */
/* -- Used a two system pass to first load the star systems_stack and then set jump routes. */
int space_load(void) {
uint32_t bufsize;
char* buf = pack_readfile(DATA, SYSTEM_DATA, &bufsize);
StarSystem* tmp;
xmlNodePtr node;
xmlDocPtr doc = xmlParseMemory(buf, bufsize);
node = doc->xmlChildrenNode;
if(!xml_isNode(node, XML_SYSTEM_ID)) {
ERR("Malformed "SYSTEM_DATA" file: missing root element '"XML_SYSTEM_ID"'");
return -1;
}
node = node->xmlChildrenNode; /* First system node. */
if(node == NULL) {
ERR("Malformed "SYSTEM_DATA" file: does not contain elements");
return -1;
}
/* Fist pass - Load all the star systems_stack. */
do {
if(xml_isNode(node, XML_SYSTEM_TAG)) {
tmp = system_parse(node);
systems_stack = realloc(systems_stack, sizeof(StarSystem)*(++systems_nstack));
memcpy(systems_stack+systems_nstack-1, tmp, sizeof(StarSystem));
free(tmp);
}
} while(xml_nextNode(node));
/* Second pass - Load all the jump routes. */
node = doc->xmlChildrenNode->xmlChildrenNode;
do {
if(xml_isNode(node, XML_SYSTEM_TAG))
system_parseJumps(node); /* Automatically load the jumps into the system. */
} while(xml_nextNode(node));
/* Cleanup. */
xmlFreeDoc(doc);
free(buf);
xmlCleanupParser();
DEBUG("Loaded %d star system%s with %d planet%s",
systems_nstack, (systems_nstack==1) ? "" : "s",
total_planets, (total_planets==1) ? "" : "s");
return 0;
}
/* Render the system. -- Just playing god now. */
void space_render(const double dt) {
if(cur_system == NULL) return;
if(cur_system->nebu_density > 0.)
nebu_render(dt);
else
space_renderStars(dt);
}
/* Render the overlay. */
void space_renderOverlay(const double dt) {
if(cur_system == NULL) return;
if(cur_system->nebu_density > 0.)
nebu_renderOverlay(dt);
}
/* Render stars. */
static void space_renderStars(const double dt) {
int i;
unsigned int t, timer;
double x, y, m, b;
glMatrixMode(GL_MODELVIEW);
glPushMatrix(); /* Translation matrix. */
glTranslated(-(double)SCREEN_W/2., -(double)SCREEN_H/2., 0);
t = SDL_GetTicks();
if(!player_isFlag(PLAYER_DESTROYED) && !player_isFlag(PLAYER_CREATING) &&
pilot_isFlag(player, PILOT_HYPERSPACE) && /* Hyperspace fancy effect. */
(player->ptimer-HYPERSPACE_STARS_BLUR < t)) {
timer = player->ptimer - HYPERSPACE_STARS_BLUR;
/* Fancy hyperspace effects. */
glShadeModel(GL_SMOOTH);
glBegin(GL_LINES);
/* Lines will be based on velocity. */
m = HYPERSPACE_STARS_LENGTH * (double)(t-timer) / (HYPERSPACE_STARS_BLUR);
x = m*cos(VANGLE(player->solid->vel)+M_PI);
y = m*sin(VANGLE(player->solid->vel)+M_PI);
for(i = 0; i < nstars; i++) {
glColor4d(1., 1., 1., stars[i].brightness);
glVertex2d(stars[i].x, stars[i].y);
glColor4d(1., 1., 1., 0.);
glVertex2d(stars[i].x+x*stars[i].brightness,stars[i].y+y*stars[i].brightness);
}
glEnd();
glShadeModel(GL_FLAT);
} else {
glBegin(GL_POINTS); /* Normal rendering. */
if(!paused && !player_isFlag(PLAYER_DESTROYED) &&
!player_isFlag(PLAYER_CREATING)) { /* Update position. */
for(i = 0; i < nstars; i++) {
b = 13.-10.*stars[i].brightness;
stars[i].x -= player->solid->vel.x/b*dt;
stars[i].y -= player->solid->vel.y/b*dt;
/* Check for boundaries. */
if(stars[i].x > SCREEN_W + STAR_BUF) stars[i].x = -STAR_BUF;
else if(stars[i].x < -STAR_BUF) stars[i].x = SCREEN_W + STAR_BUF;
if(stars[i].y > SCREEN_H + STAR_BUF) stars[i].y = -STAR_BUF;
else if(stars[i].y < -STAR_BUF) stars[i].y = SCREEN_H + STAR_BUF;
/* Render. */
if((stars[i].x < SCREEN_W) && (stars[i].x > 0) &&
(stars[i].y < SCREEN_H) && (stars[i].y > 0)) {
glColor4d(1., 1., 1., stars[i].brightness);
glVertex2d(stars[i].x, stars[i].y);
}
}
} else { /* Just render. */
for(i = 0; i < nstars; i++) {
if((stars[i].x < SCREEN_W) && (stars[i].x > 0) &&
(stars[i].y < SCREEN_H) && (stars[i].y > 0)) {
glColor4d(1., 1., 1., stars[i].brightness);
glVertex2d(stars[i].x, stars[i].y);
}
}
}
glEnd(); /* GL_POINTS */
}
glPopMatrix(); /* Translation matrix. */
}
/* Render the planets. */
void planets_render(void) {
if(cur_system == NULL) return;
int i;
for(i = 0; i < cur_system->nplanets; i++)
gl_blitSprite(cur_system->planets[i].gfx_space,
cur_system->planets[i].pos.x, cur_system->planets[i].pos.y, 0, 0, NULL);
}
/* Clean up the system. */
void space_exit(void) {
int i,j;
/* Free the names. */
/*if(planetname_stack) free(planetname_stack); */
/*if(systemname_stack) free(systemname_stack); */
if(planetname_stack) {
free(planetname_stack);
planetname_stack = NULL;
}
if(systemname_stack) {
free(systemname_stack);
systemname_stack = NULL;
}
spacename_nstack = 0;
/* Free the systems. */
for(i = 0; i < systems_nstack; i++) {
free(systems_stack[i].name);
if(systems_stack[i].fleets)
free(systems_stack[i].fleets);
if(systems_stack[i].jumps)
free(systems_stack[i].jumps);
/* Free some planets. */
for(j = 0; j < systems_stack[i].nplanets; j++) {
free(systems_stack[i].planets[j].name);
if(systems_stack[i].planets[j].description)
free(systems_stack[i].planets[j].description);
if(systems_stack[i].planets[j].bar_description)
free(systems_stack[i].planets[j].bar_description);
/* Graphics. */
if(systems_stack[i].planets[j].gfx_space)
gl_freeTexture(systems_stack[i].planets[j].gfx_space);
if(systems_stack[i].planets[j].gfx_exterior)
free(systems_stack[i].planets[j].gfx_exterior);
/* Commodities. */
free(systems_stack[i].planets[j].commodities);
}
free(systems_stack[i].planets);
}
free(systems_stack);
systems_stack = NULL;
systems_nstack = 0;
/* Stars must be set free too. */
if(stars) free(stars);
stars = NULL;
nstars = 0;
}
/* Clear all system knowledge. */
void space_clearKnown(void) {
int i;
for(i = 0; i < systems_nstack; i++)
sys_rmFlag(&systems_stack[i], SYSTEM_KNOWN);
}
/* Clear all system markers. */
void space_clearMarkers(void) {
int i;
for( i = 0; i < systems_nstack; i++)
sys_rmFlag(&systems_stack[i], SYSTEM_MARKED);
}
/* Save what is needed to be saved for space. */
int space_sysSave(xmlTextWriterPtr writer) {
int i;
xmlw_startElem(writer, "space");
for(i = 0; i < systems_nstack; i++) {
if(!sys_isKnown(&systems_stack[i])) continue; /* Not known. */
xmlw_elem(writer, "known", "%s", systems_stack[i].name);
}
xmlw_endElem(writer); /* Space. */
return 0;
}
/* Load space. */
int space_sysLoad(xmlNodePtr parent) {
xmlNodePtr node, cur;
StarSystem* sys;
space_clearKnown();
node = parent->xmlChildrenNode;
do {
if(xml_isNode(node, "space")) {
cur = node->xmlChildrenNode;
do {
if(xml_isNode(cur, "known")) {
sys = system_get(xml_get(cur));
if(sys != NULL) /* Must exist. */
sys_setFlag(sys, SYSTEM_KNOWN);
}
} while(xml_nextNode(cur));
}
} while(xml_nextNode(node));
return 0;
}