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