#include #include #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 "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) static StarSystem* systems = NULL; static int nsystems = 0; StarSystem* cur_system = NULL; // Current star system. // Current stardate in nice format. char* stardate = "Stardate"; unsigned int date = 0; // time since epoch. #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. // Intern static Planet* planet_get(const char* name); static StarSystem* system_parse(const xmlNodePtr parent); static PlanetClass planetclass_get(const char a); // Extern. extern void player_message(const char* fmt, ...); // 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)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; }; } // Hyperspaces, return 0 if entering hyperspace, or distance otherwise. int space_hyperspace(Pilot* p) { int i; double d; 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 (int)(MIN_HYPERSPACE_DIST - d); } // Too fast. if(VMOD(p->solid->vel) > MAX_HYPERSPACE_VEL) return -1; // TODO: All hyperspace worky work. if(p == player) { // Player crap. } else { } return 0; } // 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; player_message("Entering System %s on %s", sysname, stardate); // 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. // Simulate ships coming from hyperspace. 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) { // Other ships in the fleet should start split up. vect_cadd(&v, RNG(50, 150) * (RNG(0,1) ? 1 : -1), RNG(50, 150) * (RNG(0,1) ? 1 : -1)); 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(xml_isNode(node, XML_PLANET_TAG)) { tstr = xml_nodeProp(node, "name"); if(strcmp(tstr, name)==0) { // Found. tmp = CALLOC_L(Planet); 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. snprintf(str, strlen(xml_get(cur))+sizeof(PLANET_GFX_EXTERIOR), PLANET_GFX_EXTERIOR"%s", xml_get(cur)); tmp->gfx_exterior = gl_newImage(str); } } while((cur = cur->next)); } 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((cur = cur->next)); } 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")) tmp->faction = faction_get(xml_get(cur)); else if(xml_isNode(cur, "description")) tmp->description = strdup(xml_get(cur)); } while((cur = cur->next)); } } while((node = node->next)); 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(tmp->gfx_space, "GFX_space"); MELEMENT(tmp->gfx_exterior, "GFX_exterior"); 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 = 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((cur = cur->next)); } 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; } }while((cur = cur->next)); } // Load all the planets. else if(xml_isNode(node, "planets")) { cur = node->children; do { if(xml_isNode(cur, "planet")) { planet = planet_get(xml_get(cur)); 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); } } while((cur = cur->next)); } // Load all the fleets. else if(xml_isNode(node, "fleets")) { cur = node->children; do { if(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((cur = cur->next)); } } while((node = node->next)); // 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 Planet%c", 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(!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; } do { if(xml_isNode(node, XML_SYSTEM_TAG)) { 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; glBegin(GL_POINTS); for(i = 0; i < nstars; i++) { if(!paused) { // 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 - (double)gl_screen.w/2., stars[i].y - (double)gl_screen.h/2.); } glEnd(); } // 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.x, cur_system->planets[i].pos.y, 0, 0, NULL); } // Clean up the system. void space_exit(void) { int i,j; for(i = 0; i < nsystems; i++) { free(systems[i].name); if(systems[i].fleets) free(systems[i].fleets); for(j = 0; j < systems[i].nplanets; j++) { free(systems[i].planets[j].name); if(systems[i].planets[j].description) free(systems[i].planets[j].description); if(systems[i].planets[j].gfx_space) gl_freeTexture(systems[i].planets[j].gfx_space); if(systems[i].planets[j].gfx_exterior) gl_freeTexture(systems[i].planets[j].gfx_exterior); } free(systems[i].planets); } free(systems); if(stars) free(stars); }