355 lines
10 KiB
C
355 lines
10 KiB
C
#include <libxml/parser.h>
|
|
#include <malloc.h>
|
|
#include <math.h>
|
|
|
|
#include "log.h"
|
|
#include "physics.h"
|
|
#include "opengl.h"
|
|
#include "rng.h"
|
|
#include "pilot.h"
|
|
#include "pack.h"
|
|
#include "space.h"
|
|
|
|
#define XML_NODE_START 1
|
|
#define XML_NODE_TEST 3
|
|
|
|
#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 "../gfx/planet/"
|
|
|
|
// 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)
|
|
|
|
// Planet types. I didn't take them from Star Trek, I promise.
|
|
typedef enum {
|
|
PLANET_CLASS_A, // Geothermal.
|
|
PLANET_CLASS_B, // Geomorteus.
|
|
PLANET_CLASS_C, // Geoinactive.
|
|
PLANET_CLASS_D, // Asteroid/Moon.
|
|
PLANET_CLASS_E, // Geoplastic.
|
|
PLANET_CLASS_F, // Geometallic.
|
|
PLANET_CLASS_G, // GroCrystaline.
|
|
PLANET_CLASS_H, // Desert.
|
|
PLANET_CLASS_I, // Gas Supergiant.
|
|
PLANET_CLASS_J, // Gas Giant.
|
|
PLANET_CLASS_K, // Adaptable.
|
|
PLANET_CLASS_L, // Marginal.
|
|
PLANET_CLASS_M, // Terrestrial.
|
|
PLANET_CLASS_N, // Reducing.
|
|
PLANET_CLASS_O, // Pelagic.
|
|
PLANET_CLASS_P, // Glaciated.
|
|
PLANET_CLASS_Q, // Variable.
|
|
PLANET_CLASS_R, // Rogue.
|
|
PLANET_CLASS_S, // Ultragiant.
|
|
PLANET_CLASS_T, // Ultragiant.
|
|
PLANET_CLASS_X, // Demon.
|
|
PLANET_CLASS_Y, // Demon.
|
|
PLANER_CLASS_Z // Demon.
|
|
} PlanetClass;
|
|
|
|
typedef struct {
|
|
char* name;
|
|
Vec2 pos; // Position in star system.
|
|
PlanetClass class;
|
|
gl_texture* gfx_space; // Graphics in space.
|
|
} Planet;
|
|
|
|
typedef struct {
|
|
char* name;
|
|
Vec2 pos; // Position.
|
|
int stars, asteroids; // Un numero!
|
|
double interference; // Un uh.. Percentage.
|
|
|
|
// Factions.
|
|
Planet* planets; // Planets.
|
|
int nplanets;
|
|
|
|
// TODO: Throw some fleets here.
|
|
} StarSystem;
|
|
|
|
static StarSystem* systems = NULL;
|
|
static int nsystems = 0;
|
|
static StarSystem* cur_system = NULL; // Current star system.
|
|
|
|
#define STAR_BUF 100 // Area to leave around screen.
|
|
typedef struct {
|
|
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 Planet* planet_get(const char* name);
|
|
static StarSystem* system_parse(const xmlNodePtr parent);
|
|
|
|
// Init the system.
|
|
void space_init(const char* sysname) {
|
|
int i;
|
|
for(i = 0; i < nsystems; i++)
|
|
if(strcmp(sysname, systems[i].name)==0)
|
|
break;
|
|
if(i == nsystems) ERR("System %s not found in stack", sysname);
|
|
cur_system = systems+i;
|
|
|
|
nstars = (cur_system->stars*gl_screen.w*gl_screen.h+STAR_BUF*STAR_BUF)/(800*640);
|
|
stars = malloc(sizeof(Star)*nstars);
|
|
for(i = 0; i < nstars; i++) {
|
|
stars[i].brightness = (double)RNG(50, 200)/256.;
|
|
stars[i].x = (double)RNG(-STAR_BUF, gl_screen.w + STAR_BUF);
|
|
stars[i].y = (double)RNG(-STAR_BUF, gl_screen.h + STAR_BUF);
|
|
}
|
|
}
|
|
|
|
// Load the planets of name 'name'.
|
|
static Planet* planet_get(const char* name) {
|
|
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;
|
|
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(node->type == XML_NODE_START && strcmp((char*)node->name, XML_PLANET_TAG)==0) {
|
|
tstr = (char*)xmlGetProp(node, (xmlChar*)"name");
|
|
if(strcmp(tstr, name)==0) { // Found.
|
|
tmp = CALLOC_L(Planet);
|
|
tmp->name = tstr;
|
|
|
|
node = node->xmlChildrenNode;
|
|
|
|
while((node = node->next)) {
|
|
if(strcmp((char*)node->name, "GFX")==0) {
|
|
cur = node->children;
|
|
if(strcmp((char*)cur->name, "text")==0) {
|
|
snprintf(str, strlen((char*)cur->content)+sizeof(PLANET_GFX),
|
|
PLANET_GFX"%s", (char*)cur->content);
|
|
tmp->gfx_space = gl_newSprite(str, 1, 1);
|
|
}
|
|
}
|
|
else if(strcmp((char*)node->name, "pos")==0) {
|
|
cur = node->children;
|
|
while((cur = cur->next)) {
|
|
if(strcmp((char*)cur->name, "x")==0) {
|
|
flags |= FLAG_XSET;
|
|
tmp->pos.x = atof((char*)cur->children->content);
|
|
}
|
|
else if(strcmp((char*)cur->name, "y")==0) {
|
|
flags |= FLAG_YSET;
|
|
tmp->pos.y = atof((char*)cur->children->content);
|
|
}
|
|
}
|
|
}
|
|
else if(strcmp((char*)node->name, "general")==0) {
|
|
cur = node->children;
|
|
while((cur = cur->next)) {
|
|
if(strcmp((char*)cur->name, "class")==0)
|
|
tmp->class = atoi((char*)cur->children->content);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
} else
|
|
free(tstr); // xmlGetProp mallocs the string.
|
|
}
|
|
} while((node = node->next));
|
|
|
|
xmlFreeDoc(doc);
|
|
free(buf);
|
|
xmlCleanupParser();
|
|
|
|
// Check elements.
|
|
if(tmp) {
|
|
#define MELEMENT(o,s) if((o) == 0) WARN("Planet '%s' missing '"s"' element", tmp->name)
|
|
MELEMENT(flags&FLAG_XSET, "x");
|
|
MELEMENT(flags&FLAG_YSET, "y");
|
|
MELEMENT(tmp->class, "class");
|
|
#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;
|
|
StarSystem* tmp = CALLOC_L(StarSystem);
|
|
xmlNodePtr cur, node;
|
|
|
|
uint32_t flags;
|
|
|
|
tmp->name = (char*)xmlGetProp(parent, (xmlChar*)"name"); // Already mallocs.
|
|
|
|
node = parent->xmlChildrenNode;
|
|
|
|
while((node = node->next)) {
|
|
// Load all the things!
|
|
if(strcmp((char*)node->name, "pos")==0) {
|
|
cur = node->children;
|
|
while((cur = cur->next)) {
|
|
if(strcmp((char*)cur->name, "x")==0) {
|
|
flags |= FLAG_XSET;
|
|
tmp->pos.x = atof((char*)cur->children->content);
|
|
}
|
|
if(strcmp((char*)cur->name, "y")==0) {
|
|
flags |= FLAG_YSET;
|
|
tmp->pos.y = atof((char*)cur->children->content);
|
|
}
|
|
}
|
|
}
|
|
else if(strcmp((char*)node->name, "general")==0) {
|
|
cur = node->children;
|
|
while((cur = cur->next)) {
|
|
if(strcmp((char*)cur->name, "stars")==0) // Non-zero.
|
|
tmp->stars = atoi((char*)cur->children->content);
|
|
else if(strcmp((char*)cur->name, "asteroids")==0) {
|
|
flags |= FLAG_ASTEROIDSSET;
|
|
tmp->asteroids = atoi((char*)cur->children->content);
|
|
}
|
|
else if(strcmp((char*)cur->name, "interference")==0) {
|
|
flags |= FLAG_INTEFERENCESET;
|
|
tmp->interference = atof((char*)cur->children->content);
|
|
}
|
|
}
|
|
}
|
|
else if(strcmp((char*)node->name, "planets")==0) {
|
|
cur = node->children;
|
|
while((cur = cur->next)) {
|
|
if(strcmp((char*)cur->name, "planet")==0) {
|
|
planet = planet_get((const char*)cur->children->content);
|
|
tmp->planets = realloc(tmp->planets, sizeof(Planet)*(++tmp->nplanets));
|
|
memcpy(tmp->planets+tmp->nplanets-1, planet, sizeof(Planet));
|
|
free(planet);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// 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
|
|
DEBUG("Loaded Star System '%s' with %d Planets%s", tmp->name, tmp->nplanets, (tmp->nplanets > 1) ? "s" : "");
|
|
|
|
return tmp;
|
|
}
|
|
|
|
// Load the ENTIRE universe into RAM. -- WOAH! -- Wasn't that bad. :P
|
|
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(strcmp((char*)node->name, 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;
|
|
}
|
|
do {
|
|
if(node->type == XML_NODE_START && strcmp((char*)node->name, XML_SYSTEM_TAG)==0) {
|
|
tmp = system_parse(node);
|
|
systems = realloc(systems, sizeof(StarSystem)*(++nsystems));
|
|
memcpy(systems+nsystems-1, tmp, sizeof(StarSystem));
|
|
free(tmp);
|
|
}
|
|
} while((node = node->next));
|
|
|
|
xmlFreeDoc(doc);
|
|
free(buf);
|
|
xmlCleanupParser();
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Render the system. -- Just playing god now.
|
|
void space_render(double dt) {
|
|
int i;
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPushMatrix(); // Projection translation matrix.
|
|
glTranslated(-(double)gl_screen.w/2., -(double)gl_screen.h/2., 0.);
|
|
glBegin(GL_POINTS);
|
|
for(i = 0; i < nstars; i++) {
|
|
// Update the position.
|
|
stars[i].x -= VX(player->solid->vel)/(15.-10.*stars[i].brightness)*dt;
|
|
stars[i].y -= VY(player->solid->vel)/(15.-10.*stars[i].brightness)*dt;
|
|
// Scroll those stars bitch!
|
|
if(stars[i].x > gl_screen.w + STAR_BUF) stars[i].x = -STAR_BUF;
|
|
else if(stars[i].x < -STAR_BUF) stars[i].x = gl_screen.w + STAR_BUF;
|
|
if(stars[i].y > gl_screen.h + STAR_BUF) stars[i].y = -STAR_BUF;
|
|
else if(stars[i].y < -STAR_BUF) stars[i].y = gl_screen.h + STAR_BUF;
|
|
// Render.
|
|
glColor4d(1., 1., 1., stars[i].brightness);
|
|
glVertex2d(stars[i].x, stars[i].y);
|
|
}
|
|
glEnd();
|
|
glPopMatrix(); // Projection translation matrix.
|
|
}
|
|
|
|
// Render the planets.
|
|
void planets_render(void) {
|
|
int i;
|
|
for(i = 0; i < cur_system->nplanets; i++)
|
|
gl_blitSprite(cur_system->planets[i].gfx_space, &cur_system->planets[i].pos, 0, 0);
|
|
}
|
|
|
|
// Clean up the system.
|
|
void space_exit(void) {
|
|
int i,j;
|
|
for(i = 0; i < nsystems; i++) {
|
|
free(systems[i].name);
|
|
for(j = 0; j < systems[i].nplanets; j++) {
|
|
free(systems[i].planets[j].name);
|
|
if(systems[i].planets[j].gfx_space)
|
|
gl_freeTexture(systems[i].planets[j].gfx_space);
|
|
}
|
|
free(systems[i].planets);
|
|
}
|
|
free(systems);
|
|
if(stars) free(stars);
|
|
}
|
|
|