Lephisto/src/space.c
2013-02-09 00:53:37 +00:00

448 lines
14 KiB
C

#include <libxml/parser.h>
#include <malloc.h>
#include <math.h>
#include "log.h"
#include "physics.h"
#include "rng.h"
#include "pack.h"
#include "space.h"
#include "faction.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)
static StarSystem* systems = NULL;
static int nsystems = 0;
StarSystem* cur_system = NULL; // Current star system.
#define STAR_BUF 100 // Area to leave around screen, more = less repitition.
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);
static PlanetClass planetclass_get(const char a);
// Draw the planet. Used in planet.c
// Matrix mode is already displaced to center of the minimap.
#define PIXEL(x,y) if(ABS(x)<w/2. && ABS(y)<h/2.) glVertex2i((x),(y))
void planets_minimap(double res, double w, double h) {
int i;
int cx, cy, x, y, r;
double p;
glBegin(GL_POINTS);
glMatrixMode(GL_PROJECTION);
for(i = 0; i < cur_system->nplanets; i++) {
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);
}
}
}
if(ABS(x) < w/2. && ABS(y) < h/2.) {}
glEnd();
}
#undef PIXEL
static PlanetClass planetclass_get(const char a) {
switch(a) {
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;
default: return PLANET_CLASS_NULL;
};
}
// Init the system.
void space_init(const char* sysname) {
int i, j;
Vec2 v, vn;
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;
// Set up stars.
nstars = (cur_system->stars*gl_screen.w*gl_screen.h+STAR_BUF*STAR_BUF)/(800*640);
stars = realloc(stars, sizeof(Star)*nstars); // Should realloc this, not malloc.
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);
}
// Set up fleets -> pilots.
vectnull(&vn);
for(i = 0; i < cur_system->nfleets; i++)
if(RNG(0,100) <= cur_system->fleets[i].chance) {// Check fleet.
vect_pset(&v, 2*RNG(MIN_HYPERSPACE_DIST/2, MIN_HYPERSPACE_DIST), RNG(0, 360)*M_PI/180.);
for(j = 0; j < cur_system->fleets[i].fleet->npilots; j++)
if(RNG(0,100) <= cur_system->fleets[i].fleet->pilots[j].chance) {
vect_cadd(&v, RNG(-50, 50), RNG(-50, 50));
pilot_create(cur_system->fleets[i].fleet->pilots[j].ship,
cur_system->fleets[i].fleet->pilots[j].name,
cur_system->fleets[i].fleet->faction,
cur_system->fleets[i].fleet->ai,
vect_angle(&v,&vn),
&v,
NULL,
0);
}
}
}
// 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_newImage(str);
}
}
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 = planetclass_get(cur->children->content[0]);
else if(strcmp((char*)cur->name, "faction")==0)
tmp->faction = faction_get((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");
MELEMENT(tmp->faction, "faction");
#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 = (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);
}
}
}
// Load all the planets.
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);
}
}
}
// Load all the fleets.
else if(strcmp((char*)node->name, "fleets")==0) {
cur = node->children;
while((cur = cur->next)) {
if(strcmp((char*)cur->name, "fleet")==0) {
fleet = CALLOC_L(SystemFleet);
fleet->fleet = fleet_get((const char*)cur->children->content);
if(fleet->fleet == NULL)
WARN("Fleet %s for Star System %s not found", (char*)cur->children->content, tmp->name);
ptrc = (char*)xmlGetProp(cur, (xmlChar*)"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);
}
}
}
}
// 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);
if(systems[i].fleets)
free(systems[i].fleets);
}
free(systems[i].planets);
}
free(systems);
if(stars) free(stars);
}