#include #include #include "xml.h" #include "lephisto.h" #include "opengl.h" #include "log.h" #include "physics.h" #include "rng.h" #include "pack.h" #include "player.h" #include "pause.h" #include "weapon.h" #include "toolkit.h" #include "spfx.h" #include "ltime.h" #include "nebulae.h" #include "space.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 #define CHUNK_SIZE 32 /**< Size to allocate by. */ #define CHUNK_SIZE_SMALL 8 /**< Smaller size to allocate chunks by. */ /* 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; static int spacename_mstack = 0; /**< Size of memory in planet <->system stack. */ /* Star system stack. */ StarSystem* systems_stack = NULL; /* Star system stack. */ int systems_nstack = 0; /* Number of star systems. */ static int systems_mstack = 0; /**< Number of memory allocated for star system stack. */ /* Planet Stack. */ static Planet* planet_stack = NULL; /**< Planet stack. */ static int planet_nstack = 0; /**< Planet stack size. */ static int planet_mstack = 0; /**< Memory size of planet stack. */ /* Misc. */ StarSystem* cur_system = NULL; /* Current star system. */ /* Fleet spawn rate. */ int space_spawn = 1; /**< Spawn enabled by default. */ static double spawn_timer = 0; /**< Timer that 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. */ /* Planet load. */ static int planet_parse(Planet* planet, const xmlNodePtr parent); /* System load. */ static int systems_load(void); static StarSystem* system_parse(StarSystem* system, const xmlNodePtr parent); static void system_parseJumps(const xmlNodePtr parent); /* Misc. */ static void system_setFaction(StarSystem* sys); static void space_renderStars(const double dt); static void space_addFleet(Fleet* fleet, int init); 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)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; for(i = 0; i < planet_nstack; i++) if(strcmp(planet_stack[i].name, planetname)==0) return &planet_stack[i]; DEBUG("Planet '%s' not found in the universe", planetname); return NULL; } /* Basically used for spawning fleets. */ void space_update(const double dt) { int i, j, f; if(cur_system == NULL) return; /* Can't update a null system. */ if(space_spawn) { spawn_timer -= dt; if(cur_system->nfleets == 0) /* Stop checking if there are no fleets. */ spawn_timer = 300.; if(spawn_timer < 0.) { /* time to possibly spawn. */ /* Spawn chance is based on overall %. */ 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, 0); break; } } spawn_timer = 60./(float)cur_system->nfleets; } } /* Volatile system. */ if(cur_system->nebu_volatility > 0.) { /* Player takes damage. */ if(player) pilot_hit(player, NULL, 0, DAMAGE_TYPE_RADIATION, pow2(cur_system->nebu_volatility) / 500. * dt); } } /** * @fn static void space_addFleet(Fleet* fleet) * * @brief Create a fleet. * @param fleet Fleet to add to the system. * @param init Is being run during the space initialization. */ static void space_addFleet(Fleet* fleet, int init) { FleetPilot* plt; Planet* planet; int i, c; double a; Vec2 vv, vp, vn; /* Simulate them coming from hyperspace. */ vect_pset(&vp, RNG(MIN_HYPERSPACE_DIST*2, MIN_HYPERSPACE_DIST*3), RNG(0, 360)*M_PI/180.); /* Needed to determine angle. */ vectnull(&vn); /* c will determine how to create the fleet, only non-zero if it's run in init. */ if(init == 1) { if(RNGF() < 0.5) /* 50% chance of starting out en route. */ c = 2; else if(RNGF() < 0.5) /* 25% chance of starting out landed. */ c = 1; else /* 25% chance starting out entering hyperspace. */ c = 0; } else c = 0; /* Simulate they came from hyperspace. */ if(c == 0) { vect_pset(&vp, RNG(MIN_HYPERSPACE_DIST, MIN_HYPERSPACE_DIST*3.), RNG(0,360)*M_PI/180.); } /* Starting out landed or heading towards landing. */ else if((c == 1) || (c == 2)) { /* Get friendly planet to land on. */ planet = NULL; for(i = 0; i < cur_system->nplanets; i++) if(planet_hasService(cur_system->planets[i], PLANET_SERVICE_BASIC) && !areEnemies(fleet->faction, cur_system->planets[i]->faction)) { planet = cur_system->planets[i]; break; } /* No suitable planet found. */ if(planet == NULL) { vect_pset(&vp, RNG(MIN_HYPERSPACE_DIST, MIN_HYPERSPACE_DIST*3.), RNG(0, 360)*M_PI/180.); c = 0; } else /* Set position to be planet position. */ vectcpy(&vp, &planet->pos); } 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); /* Entering via hyperspace. */ if(c == 0) vect_pset(&vv, plt->ship->speed * 3., a); /* Starting out landed. */ else if(c == 1) vectnull(&vv); 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. */ space_spawn = 1; /* Spawn is enabled by default. */ 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, 1); /* Start the spawn timer. */ spawn_timer = 120./(float)(cur_system->nfleets+1); /* We now know this system. */ sys_setFlag(cur_system, SYSTEM_KNOWN); } /** * @fn static int planets_load(void) * * @brief Load all the planets in the game. * @return 0 on success. */ static int planets_load(void) { uint32_t bufsize; char* buf; xmlNodePtr node; xmlDocPtr doc; buf = pack_readfile(DATA, PLANET_DATA, &bufsize); 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 -1; } node = node->xmlChildrenNode; /* First system node. */ if(node == NULL) { ERR("Malformed "PLANET_DATA" file: does not contain elements"); return -1; } /* Initialize stack if needed. */ if(planet_stack == NULL) { planet_mstack = CHUNK_SIZE; planet_stack = malloc(sizeof(Planet) * planet_mstack); planet_nstack = 0; } do { if(xml_isNode(node, XML_PLANET_TAG)) { /* See if stack must grow. */ planet_nstack++; if(planet_nstack > planet_mstack) { planet_mstack += CHUNK_SIZE; planet_stack = realloc(planet_stack, sizeof(Planet) * planet_mstack); } planet_parse(&planet_stack[planet_nstack-1], node); } } while(xml_nextNode(node)); xmlFreeDoc(doc); free(buf); return 0; } /** * @fn static int planet_parse(Planet* planet, const xmlNodePtr parent) * * @brief Parses a planet from an xml node. * @param planet Planet to fill up. * @param parent Node that contains planet data. * @return 0 on success. */ static int planet_parse(Planet* planet, const xmlNodePtr parent) { int i; char str[PATH_MAX]; xmlNodePtr node, cur, ccur; int len; unsigned int flags; /* Clear up memory for sane defaults. */ memset(planet, 0, sizeof(Planet)); planet->faction = -1; str[0] = '\0'; flags = 0; /* Get the name. */ xmlr_attr(parent, "name", planet->name); node = parent->xmlChildrenNode; do { if(xml_isNode(node, "GFX")) { cur = node->children; do { if(xml_isNode(cur, "space")) { /* Load space gfx. */ snprintf(str, PATH_MAX, PLANET_GFX_SPACE"%s", xml_get(cur)); planet->gfx_space = gl_newImage(str); } else if(xml_isNode(cur, "exterior")) { /* Load land gfx. */ len = strlen(xml_get(cur)) + sizeof(PLANET_GFX_EXTERIOR); planet->gfx_exterior = malloc(len); snprintf(planet->gfx_exterior, len, 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; planet->pos.x = xml_getFloat(cur); } else if(xml_isNode(cur, "y")) { flags |= FLAG_YSET; planet->pos.y = xml_getFloat(cur); } } while(xml_nextNode(cur)); } else if(xml_isNode(node, "general")) { cur = node->children; do { if(xml_isNode(cur, "class")) planet->class = planetclass_get(cur->children->content[0]); else if(xml_isNode(cur, "faction")) { flags |= FLAG_FACTIONSET; planet->faction = faction_get(xml_get(cur)); } else if(xml_isNode(cur, "description")) planet->description = strdup(xml_get(cur)); else if(xml_isNode(cur, "bar")) planet->bar_description = strdup(xml_get(cur)); else if(xml_isNode(cur, "services")) { flags |= FLAG_SERVICESET; planet->services = xml_getInt(cur); /* Flags gotten by data. */ } else if(xml_isNode(cur, "tech")) { ccur = cur->children; do { if(xml_isNode(ccur, "main")) { flags |= FLAG_TECHSET; planet->tech[0] = xml_getInt(ccur); } else if(xml_isNode(ccur, "special")) { for(i = 1; i < PLANET_TECH_MAX; i++) if(planet->tech[i] == 0) { planet->tech[i] = xml_getInt(ccur); break; } if(i == PLANET_TECH_MAX) WARN("Planet '%s' has too many" "'special tech' entries", planet->name); } } while(xml_nextNode(ccur)); } else if(xml_isNode(cur, "commodities")) { ccur = cur->children; do { if(xml_isNode(ccur, "commodity")) { planet->commodities = realloc(planet->commodities, (planet->ncommodities+1) * sizeof(Commodity*)); planet->commodities[planet->ncommodities] = commodity_get(xml_get(ccur)); planet->ncommodities++; } } while(xml_nextNode(ccur)); } } while(xml_nextNode(cur)); } } while(xml_nextNode(node)); /* Verification. */ #define MELEMENT(o,s) if(o) WARN("Planet '%s' missing '"s"' element", planet->name) MELEMENT(planet->gfx_space==NULL, "GFX space"); MELEMENT(planet_hasService(planet, PLANET_SERVICE_LAND) && planet->gfx_exterior==NULL, "GFX exterior"); MELEMENT((flags&FLAG_XSET)==0, "x"); MELEMENT((flags&FLAG_YSET)==0, "y"); MELEMENT(planet->class==PLANET_CLASS_NULL, "class"); MELEMENT(planet_hasService(planet, PLANET_SERVICE_LAND) && planet->description==NULL, "description"); MELEMENT(planet_hasService(planet, PLANET_SERVICE_BASIC) && planet->bar_description==NULL, "bar"); MELEMENT(planet_hasService(planet, PLANET_SERVICE_BASIC) && (flags&FLAG_FACTIONSET)==0, "faction"); MELEMENT((flags&FLAG_SERVICESET)==0, "services"); MELEMENT((planet_hasService(planet, PLANET_SERVICE_OUTFITS) || planet_hasService(planet, PLANET_SERVICE_SHIPYARD)) && (flags&FLAG_TECHSET)==0, "tech"); MELEMENT(planet_hasService(planet, PLANET_SERVICE_COMMODITY) && (planet->ncommodities==0), "commodity"); #undef MELEMENT return 0; } /** * @fn int system_addPlanet(StarSystem* sys, char* planetname) * * @brief Add a planet to a star system. * @param sys Star System to add planet to. * @param planetname Name of the planet to add. * @return 0 on success. */ int system_addPlanet(StarSystem* sys, char* planetname) { Planet* planet; if(sys == NULL) return -1; /* Check if need to grow the star system planet stack. */ sys->nplanets++; if(sys->planets == NULL) { sys->planets = malloc(sizeof(Planet*) * CHUNK_SIZE_SMALL); } else if(sys->nplanets > CHUNK_SIZE_SMALL) { sys->planets = realloc(sys->planets, sizeof(Planet*) * sys->nplanets); } planet = planet_get(planetname); sys->planets[sys->nplanets-1] = planet; /* Add planet <-> star system to name stack. */ spacename_nstack++; if(spacename_nstack > spacename_mstack) { spacename_mstack += CHUNK_SIZE; planetname_stack = realloc(planetname_stack, sizeof(char*) * spacename_mstack); systemname_stack = realloc(systemname_stack, sizeof(char*) * spacename_mstack); } planetname_stack[spacename_nstack-1] = planet->name; systemname_stack[spacename_nstack-1] = sys->name; system_setFaction(sys); return 0; } /** * @fn int system_rmPlanet(StarSystem* sys, char* planetname) * * @brief Remove a planet from a star system. * @param sys Star System to remove planet from. * @param planetname Name of the planet to remove. * @return 0 on success. */ int system_rmPlanet(StarSystem* sys, char* planetname) { int i, found; Planet* planet; if(sys == NULL) { WARN("Unable to remove planet '%s' from NULL system.", planetname); return -1; } /* Try to find planet. */ planet = planet_get(planetname); for(i = 0; i < sys->nplanets; i++) if(sys->planets[i] == planet) break; /* Planet not found. */ if(i >= sys->nplanets) { WARN("Planet '%s' not found in system '%s' for removal.", planetname, sys->name); return -1; } /* Remove planet from system. */ sys->nplanets--; memmove(&sys->planets[i], &sys->planets[i+1], sizeof(Planet*) * (sys->nplanets-i)); /* Remove from the name stack thingy. */ found = 0; for(i = 0; i < spacename_nstack; i++) if(strcmp(planetname, planetname_stack[i])==0) { spacename_nstack--; memmove(&planetname_stack[i], &planetname_stack[i+1], sizeof(char*) * (spacename_nstack-i)); memmove(&systemname_stack[i], &systemname_stack[i+1], sizeof(char*) * (spacename_nstack-i)); found = 1; break; } if(found == 0) WARN("Unable to find planet '%s' and system '%s' in planet<->system stack.", planetname, sys->name); system_setFaction(sys); return 0; } /** * @fn int system_addFleet(StarSystem* sys, SystemFleet* fleet) * * @brief Add a fleet to a star system. * @param sys Star system to add fleet to. * @param fleet Fleet to add. * @return 0 on success. */ int system_addFleet(StarSystem* sys, SystemFleet* fleet) { if(sys == NULL) return -1; sys->fleets = realloc(sys->fleets, sizeof(SystemFleet) * (++sys->nfleets)); memcpy(sys->fleets+(sys->nfleets-1), fleet, sizeof(SystemFleet)); return 0; } /** * @fn int system_rmFleet(StarSystem* sys, SystemFleet* fleet) * * @brief Remove a fleet from a star system. * @param sys Star System to remove fleet from. * @param fleet Fleet to remove. * @return 0 on success. */ int system_rmFleet(StarSystem* sys, SystemFleet* fleet) { int i; /* Find a matching fleet (will grab first since can be duplicates). */ for(i = 0; i < sys->nfleets; i++) if((fleet->fleet == sys->fleets[i].fleet) && (fleet->chance == sys->fleets[i].chance)) break; /* Not found. */ if(i >= sys->nfleets) return -1; sys->nfleets--; memmove(&sys->fleets[i], &sys->fleets[i+1], sizeof(SystemFleet) * (sys->nfleets - i)); sys->fleets = realloc(sys->fleets, sizeof(SystemFleet) * sys->nfleets); return 0; } /* Parse node 'parent' which should be the node of a system. */ /* Return the StarSystem fully loaded. */ static StarSystem* system_parse(StarSystem* sys, const xmlNodePtr parent) { Planet* planet; SystemFleet fleet; char* ptrc; xmlNodePtr cur, node; uint32_t flags; int size; /* Clear memory for sane defaults. */ memset(sys, 0, sizeof(StarSystem)); sys->faction = -1; planet = NULL; size = 0; sys->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; sys->pos.x = xml_getFloat(cur); } else if(xml_isNode(cur, "y")) { flags |= FLAG_YSET; sys->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. */ sys->stars = xml_getInt(cur); else if(xml_isNode(cur, "asteroids")) { flags |= FLAG_ASTEROIDSSET; sys->asteroids = xml_getInt(cur); } else if(xml_isNode(cur, "interference")) { flags |= FLAG_INTEFERENCESET; sys->interference = xml_getFloat(cur)/100; } else if(xml_isNode(cur, "nebulae")) { ptrc = xml_nodeProp(cur, "volatility"); if(ptrc != NULL) { /* Has volatility. */ sys->nebu_volatility = atof(ptrc); free(ptrc); } sys->nebu_density = xml_getFloat(cur); } }while(xml_nextNode(cur)); } /* Load all the planets. */ else if(xml_isNode(node, "planets")) { cur = node->children; do { if(xml_isNode(cur, "planet")) system_addPlanet(sys, xml_get(cur)); } while(xml_nextNode(cur)); } /* Load all the fleets. */ else if(xml_isNode(node, "fleets")) { cur = node->children; do { if(xml_isNode(cur, "fleet")) { memset(&fleet, 0, sizeof(SystemFleet)); fleet.fleet = fleet_get(xml_get(cur)); if(fleet.fleet == NULL) WARN("Fleet %s for Star System %s not found", xml_get(cur), sys->name); xmlr_attr(cur, "chance", ptrc); /* mallocs ptrc. */ if(ptrc == NULL) fleet.chance = 0; /* Gives warning. */ else fleet.chance = atoi(ptrc); if(fleet.chance == 0) WARN("Fleet %s for Star System %s has 0%% chance to appear", fleet.fleet->name, sys->name); if(ptrc) free(ptrc); /* Free the ptrc. */ system_addFleet(sys, &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", sys->name) if(sys->name == NULL) WARN("Star System '%s' missing 'name' tag", sys->name); MELEMENT(flags&FLAG_XSET, "x"); MELEMENT(flags&FLAG_YSET, "y"); MELEMENT(sys->stars, "stars"); MELEMENT(flags&FLAG_ASTEROIDSSET, "asteroids"); /* Can be 0. */ MELEMENT(flags&FLAG_INTEFERENCESET, "inteference"); #undef MELEMENT /* Post processing. */ system_setFaction(sys); return 0; } /** * @fn static void system_setFaction(StarSystem* sys) * * @brief Set the system faction based on the planet it has. * @param sys System to set the faction of. */ static void system_setFaction(StarSystem* sys) { int i; sys->faction = -1; for(i = 0; i < sys->nplanets; i++) /** @todo Handle multiple different factions. */ if(sys->planets[i]->faction > 0) { sys->faction = sys->planets[i]->faction; break; } } /* 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)); } /** * @fn int space_load(void) * * @brief Load the ENTIRE universe into RAM. -- WOAH! * @return 0 on success. */ int space_load(void) { int ret; ret = planets_load(); if(ret < 0) return ret; ret = systems_load(); if(ret < 0) return ret; return 0; } /** * @fn int systems_load(void) * * @brief Load the entire systems, needs to be called after planets_load. * * Uses a two system pass to first load the star systems_stack and then set * jump routes. * @return 0 on success. */ static int systems_load(void) { uint32_t bufsize; char* buf = pack_readfile(DATA, SYSTEM_DATA, &bufsize); 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; } /* Allocate if needed. */ if(systems_stack == NULL) { systems_mstack = CHUNK_SIZE; systems_stack = malloc(sizeof(StarSystem) * systems_mstack); systems_nstack = 0; } /* First pass - Load all the star systems_stack. */ do { if(xml_isNode(node, XML_SYSTEM_TAG)) { /* Check if memory needs to grow. */ systems_nstack++; if(systems_nstack > systems_mstack) { systems_mstack += CHUNK_SIZE; systems_stack = realloc(systems_stack, sizeof(StarSystem) * systems_mstack); } system_parse(&systems_stack[systems_nstack-1], node); } } 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); DEBUG("Loaded %d star system%s with %d planet%s", systems_nstack, (systems_nstack==1) ? "" : "s", planet_nstack, (planet_nstack==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. */ ((!paused && (player->ptimer - HYPERSPACE_STARS_BLUR < t)) || (paused && (player->ptimer < HYPERSPACE_STARS_BLUR)))) { timer = player->ptimer - HYPERSPACE_STARS_BLUR; if(paused) timer += t; /* 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; /* Free the names. */ if(planetname_stack) free(planetname_stack); if(systemname_stack) free(systemname_stack); spacename_nstack = 0; /* Free the planets. */ for(i = 0; i < planet_nstack; i++) { free(planet_stack[i].name); if(planet_stack[i].description) free(planet_stack[i].description); if(planet_stack[i].bar_description) free(planet_stack[i].bar_description); /* Graphics. */ if(planet_stack[i].gfx_space) gl_freeTexture(planet_stack[i].gfx_space); if(planet_stack[i].gfx_exterior) free(planet_stack[i].gfx_exterior); /* Commodities. */ free(planet_stack[i].commodities); } free(planet_stack); planet_stack = NULL; planet_nstack = 0; planet_mstack = 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(systems_stack[i].planets); } free(systems_stack); systems_stack = NULL; systems_nstack = 0; systems_mstack = 0; /* Stars must be free too. */ if(stars) free(stars); stars = NULL; nstars = 0; mstars = 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; }