1087 lines
31 KiB
C
1087 lines
31 KiB
C
#include <malloc.h>
|
|
#include <math.h>
|
|
#include <float.h>
|
|
|
|
#include "xml.h"
|
|
#include "lephisto.h"
|
|
#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 "weapon.h"
|
|
#include "toolkit.h"
|
|
#include "spfx.h"
|
|
#include "ltime.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)
|
|
#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;
|
|
|
|
// Star system stack and co.
|
|
StarSystem* systems_stack = NULL; // Star system stack.
|
|
int systems_nstack = 0; // Number of star systems.
|
|
static int nplanets = 0; // Total number of loaded planets - A little silly.
|
|
StarSystem* cur_system = NULL; // Current star system.
|
|
|
|
// Fleet spawn rate.
|
|
unsigned int spawn_timer = 0; // 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.
|
|
static StarSystem* system_get(const char* sysname);
|
|
static Planet* planet_pull(const char* name);
|
|
static void space_addFleet(Fleet* fleet);
|
|
static StarSystem* system_parse(const xmlNodePtr parent);
|
|
static void system_parseJumps(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)<w/2. && ABS(y)<h/2.) || \
|
|
(shape == RADAR_CIRCLE && (((x)*(x)+(y)*(y)) < rc))) glVertex2i((x),(y))
|
|
void planets_minimap(const double res,
|
|
const double w, const double h, const RadarShape shape) {
|
|
int i;
|
|
int cx, cy, x, y, r, rc;
|
|
double p;
|
|
|
|
if(shape == RADAR_CIRCLE) rc = (int)(w*w);
|
|
|
|
glBegin(GL_POINTS);
|
|
for(i = 0; i < cur_system->nplanets; i++) {
|
|
if(areEnemies(player->faction, cur_system->planets[i].faction))
|
|
COLOUR(cHostile);
|
|
else if(areAllies(player->faction, cur_system->planets[i].faction))
|
|
COLOUR(cFriend);
|
|
else COLOUR(cNeutral);
|
|
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
|
|
|
|
// A* Algorithm fo shortest path finding.
|
|
|
|
// The node struct.
|
|
typedef struct SysNode_ {
|
|
struct SysNode_* next, *gnext;
|
|
|
|
struct SysNode_* parent;
|
|
StarSystem* sys;
|
|
double r; // Ranking.
|
|
int g; // Step.
|
|
} SysNode;
|
|
|
|
static SysNode* A_gc;
|
|
// Prototypes.
|
|
static SysNode* A_newNode(StarSystem* sys, SysNode* parent);
|
|
static double A_h(StarSystem* n, StarSystem* g);
|
|
static double A_g(SysNode* n);
|
|
static SysNode* A_add(SysNode* first, SysNode* cur);
|
|
static SysNode* A_rm(SysNode* first, StarSystem* cur);
|
|
static int A_in(SysNode* first, StarSystem* cur);
|
|
static SysNode* A_lowest(SysNode* first);
|
|
static void A_freeList(SysNode* first);
|
|
|
|
// Creates a new node link to star system.
|
|
static SysNode* A_newNode(StarSystem* sys, SysNode* parent) {
|
|
SysNode* n;
|
|
|
|
n = malloc(sizeof(SysNode));
|
|
|
|
n->next = NULL;
|
|
n->parent = parent;
|
|
n->sys = sys;
|
|
n->r = DBL_MAX;
|
|
n->g = 0.;
|
|
|
|
n->gnext = A_gc;
|
|
A_gc = n;
|
|
|
|
return n;
|
|
}
|
|
|
|
static double A_h(StarSystem* n, StarSystem* g) {
|
|
// Euclidean distance.
|
|
return sqrt(pow2(n->pos.x - g->pos.x) + pow2(n->pos.y - g->pos.y))/100.;
|
|
}
|
|
|
|
// Get the g from a node.
|
|
static double A_g(SysNode* n) {
|
|
return n->g;
|
|
}
|
|
|
|
// Add a node to the linkes list.
|
|
static SysNode* A_add(SysNode* first, SysNode* cur) {
|
|
SysNode* n;
|
|
|
|
if(first == NULL)
|
|
return cur;
|
|
|
|
n = first;
|
|
while(n->next != NULL)
|
|
n = n->next;
|
|
n->next = cur;
|
|
|
|
return first;
|
|
}
|
|
|
|
// Remove a node from a linked list.
|
|
static SysNode* A_rm(SysNode* first, StarSystem* cur) {
|
|
SysNode* n, *p;
|
|
|
|
if(first->sys == cur) {
|
|
first->next = NULL;
|
|
n = first->next;
|
|
return n;
|
|
}
|
|
|
|
p = first;
|
|
n = p->next;
|
|
do {
|
|
if(n->sys == cur) {
|
|
n->next = NULL;
|
|
p->next = n->next;
|
|
break;
|
|
}
|
|
} while((n=n->next) != NULL);
|
|
|
|
return first;
|
|
}
|
|
|
|
// Check if node is in linked list.
|
|
static int A_in(SysNode* first, StarSystem* cur) {
|
|
SysNode* n;
|
|
|
|
if(first == NULL)
|
|
return 0;
|
|
|
|
n = first;
|
|
do {
|
|
if(n->sys == cur)
|
|
return 1;
|
|
} while((n=n->next) != NULL);
|
|
return 0;
|
|
}
|
|
|
|
// Return the lowest ranking node from a linked list of nodes.
|
|
static SysNode* A_lowest(SysNode* first) {
|
|
SysNode* lowest, *n;
|
|
|
|
if(first == NULL)
|
|
return NULL;
|
|
|
|
n = first;
|
|
lowest = n;
|
|
do {
|
|
if(n->r < lowest->r)
|
|
lowest = n;
|
|
} while((n=n->next) != NULL);
|
|
|
|
return lowest;
|
|
}
|
|
|
|
// Free a linked list.
|
|
static void A_freeList(SysNode* first) {
|
|
SysNode* p, *n;
|
|
|
|
if(first == NULL)
|
|
return;
|
|
|
|
p = NULL;
|
|
n = first;
|
|
do {
|
|
if(p != NULL)
|
|
free(p);
|
|
p = n;
|
|
} while((n=n->gnext) != NULL);
|
|
|
|
free(p);
|
|
}
|
|
|
|
StarSystem** system_getJumpPath(int* njumps, char* sysstart, char* sysend) {
|
|
int i, cost;
|
|
|
|
StarSystem* ssys, *esys, **res;
|
|
|
|
SysNode* cur, *neighbour;
|
|
SysNode* open, *closed;
|
|
|
|
A_gc = NULL;
|
|
|
|
// Initial and target systems.
|
|
ssys = system_get(sysstart); // Start.
|
|
esys = system_get(sysend); // End.
|
|
|
|
// Start the linked lists.
|
|
open = closed = NULL;
|
|
cur = A_newNode(ssys, NULL);
|
|
open = A_add(open, cur); // Initial open node is the start system.
|
|
|
|
while((cur = A_lowest(open))->sys != esys) {
|
|
// Get best from open and toss to closed.
|
|
open = A_rm(open, cur->sys);
|
|
closed = A_add(closed, cur);
|
|
|
|
for(i = 0; i < cur->sys->njumps; i++) {
|
|
neighbour = A_newNode(&systems_stack[cur->sys->jumps[i]], cur);
|
|
cost = A_g(cur) + 1;
|
|
|
|
if(A_in(open, neighbour->sys) && (cost < A_g(neighbour)))
|
|
open = A_rm(open, neighbour->sys); // New path is better.
|
|
|
|
if(A_in(closed, neighbour->sys) && (cost < A_g(neighbour)))
|
|
closed = A_rm(closed, neighbour->sys); // Shouldn't happen.
|
|
|
|
if(!A_in(open, neighbour->sys) && !A_in(closed, neighbour->sys)) {
|
|
neighbour->g = cost;
|
|
open = A_add(open, neighbour);
|
|
neighbour->r = (double)A_g(neighbour) + A_h(neighbour->sys, esys);
|
|
}
|
|
}
|
|
}
|
|
// Build the path backwards.
|
|
(*njumps) = A_g(cur);
|
|
res = malloc(sizeof(StarSystem*) * (*njumps));
|
|
for(i = 0; i < (*njumps); i++) {
|
|
res[(*njumps)-i-1] = cur->sys;
|
|
cur = cur->parent;
|
|
}
|
|
|
|
// Free the linked list.
|
|
//A_freeList(open);
|
|
//A_freeList(closed);
|
|
A_freeList(A_gc);
|
|
return res;
|
|
}
|
|
|
|
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;
|
|
|
|
default: return PLANET_CLASS_NULL;
|
|
};
|
|
}
|
|
|
|
// Check distance to ensure we can go into hyperspace.
|
|
int space_canHyperspace(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 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
// Hyperspace, returns 0 if entering hyperspace, or the distance if not.
|
|
int space_hyperspace(Pilot* p) {
|
|
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((faction_isFaction(factions[k]) &&
|
|
(planet->faction == factions[k])) ||
|
|
(faction_isAlliance(factions[k]) &&
|
|
faction_ofAlliance(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;
|
|
}
|
|
|
|
// Get the system from it's name.
|
|
static 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;
|
|
char* sys;
|
|
StarSystem* system;
|
|
|
|
sys = planet_getSystem(planetname);
|
|
system = system_get(sys);
|
|
|
|
for(i = 0; i < system->nplanets; i++)
|
|
if(strcmp(planetname, system->planets[i].name)==0)
|
|
return &system->planets[i];
|
|
DEBUG("Planet '%s' not found in the universe", planetname);
|
|
return NULL;
|
|
}
|
|
|
|
// Basically used for spawning fleets.
|
|
void space_update(const double dt) {
|
|
unsigned int t;
|
|
int i, j, f;
|
|
|
|
(void)dt; // Don't need it right now.
|
|
|
|
t = SDL_GetTicks();
|
|
|
|
if(cur_system->nfleets == 0)
|
|
// Please stop checking that there are no fleets.
|
|
spawn_timer = t + 300000;
|
|
|
|
if(spawn_timer < t) {
|
|
// Time to possibly spawn.
|
|
|
|
// Spawn chance is based on overall percentage.
|
|
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);
|
|
break;
|
|
}
|
|
}
|
|
spawn_timer = t + 60000./(float)cur_system->nfleets;
|
|
}
|
|
}
|
|
|
|
// Crate a fleet.
|
|
static void space_addFleet(Fleet* fleet) {
|
|
int i;
|
|
double a;
|
|
Vec2 vv, vp, vn;
|
|
|
|
// Simulate them coming from hyperspace.
|
|
vect_pset(&vp, RNG(MIN_HYPERSPACE_DIST, MIN_HYPERSPACE_DIST*1.5),
|
|
RNG(0, 360)*M_PI/180.);
|
|
vectnull(&vn);
|
|
|
|
for(i = 0; i < fleet->npilots; i++)
|
|
if(RNG(0, 100) <= fleet->pilots[i].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);
|
|
vectnull(&vv);
|
|
|
|
pilot_create(fleet->pilots[i].ship,
|
|
fleet->pilots[i].name,
|
|
fleet->faction,
|
|
fleet->ai,
|
|
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.
|
|
|
|
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);
|
|
|
|
// Set up stars.
|
|
nstars = (cur_system->stars*gl_screen.w*gl_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, gl_screen.w + STAR_BUF);
|
|
stars[i].y = (double)RNG(-STAR_BUF, gl_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);
|
|
|
|
// Start the spawn timer.
|
|
spawn_timer = SDL_GetTicks() + 120000./(float)(cur_system->nfleets+1);
|
|
}
|
|
|
|
// Load the planets of name 'name'.
|
|
static Planet* planet_pull(const char* name) {
|
|
int i;
|
|
|
|
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, ccur;
|
|
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")) {
|
|
flags |= FLAG_FACTIONSET;
|
|
tmp->faction = faction_get(xml_get(cur));
|
|
}
|
|
else if(xml_isNode(cur, "description"))
|
|
tmp->description = strdup(xml_get(cur));
|
|
else if(xml_isNode(cur, "bar"))
|
|
tmp->bar_description = strdup(xml_get(cur));
|
|
else if(xml_isNode(cur, "services")) {
|
|
flags |= FLAG_SERVICESET;
|
|
tmp->services = xml_getInt(cur);
|
|
}
|
|
else if(xml_isNode(cur, "tech")) {
|
|
ccur = cur->children;
|
|
do {
|
|
if(xml_isNode(ccur, "main")) {
|
|
flags |= FLAG_TECHSET;
|
|
tmp->tech[0] = xml_getInt(ccur);
|
|
}
|
|
else if(xml_isNode(ccur, "special")) {
|
|
for(i = 1; i < PLANET_TECH_MAX; i++)
|
|
if(tmp->tech[i]==0) {
|
|
tmp->tech[i] = xml_getInt(ccur);
|
|
break;
|
|
}
|
|
if(i == PLANET_TECH_MAX) WARN("Planet '%s' has tooo many"
|
|
"'special tech' entries", tmp->name);
|
|
}
|
|
} while((ccur = ccur->next));
|
|
}
|
|
else if(xml_isNode(cur, "commodities")) {
|
|
ccur = cur->children;
|
|
do {
|
|
if(xml_isNode(ccur, "commodity")) {
|
|
tmp->commodities = realloc(tmp->commodities,
|
|
(tmp->ncommodities+1) * sizeof(Commodity*));
|
|
tmp->commodities[tmp->ncommodities] =
|
|
commodity_get(xml_get(ccur));
|
|
tmp->ncommodities++;
|
|
}
|
|
} while((ccur = ccur->next));
|
|
}
|
|
} 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) WARN("Planet '%s' missing '"s"' element", tmp->name)
|
|
MELEMENT(tmp->gfx_space==NULL, "GFX_space");
|
|
MELEMENT(planet_hasService(tmp, PLANET_SERVICE_LAND) &&
|
|
tmp->gfx_exterior==NULL, "GFX exterior");
|
|
MELEMENT((flags&FLAG_XSET)==0, "x");
|
|
MELEMENT((flags&FLAG_YSET)==0, "y");
|
|
MELEMENT(tmp->class==PLANET_CLASS_NULL, "class");
|
|
MELEMENT(planet_hasService(tmp, PLANET_SERVICE_LAND) &&
|
|
tmp->description==NULL, "description");
|
|
MELEMENT(planet_hasService(tmp, PLANET_SERVICE_BASIC) &&
|
|
tmp->bar_description==NULL, "bar");
|
|
MELEMENT(planet_hasService(tmp, PLANET_SERVICE_BASIC) &&
|
|
(flags & FLAG_FACTIONSET)==0, "faction");
|
|
MELEMENT((flags&FLAG_SERVICESET)==0, "services");
|
|
|
|
MELEMENT((planet_hasService(tmp, PLANET_SERVICE_OUTFITS) ||
|
|
planet_hasService(tmp, PLANET_SERVICE_SHIPYARD)) &&
|
|
(flags&FLAG_TECHSET)==0, "tech");
|
|
MELEMENT(planet_hasService(tmp, PLANET_SERVICE_COMMODITY) &&
|
|
(tmp->ncommodities==0), "commodity");
|
|
#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")) {
|
|
// Add planet to system.
|
|
nplanets++; // Increase planet counter.
|
|
planet = planet_pull(xml_get(cur));
|
|
tmp->planets = realloc(tmp->planets, sizeof(Planet)*(++tmp->nplanets));
|
|
memcpy(tmp->planets+(tmp->nplanets-1), planet, sizeof(Planet));
|
|
|
|
// Add planet <-> star system to name stack.
|
|
spacename_nstack++;
|
|
planetname_stack = realloc(planetname_stack,
|
|
sizeof(char*)*spacename_nstack);
|
|
systemname_stack = realloc(systemname_stack,
|
|
sizeof(char*)*spacename_nstack);
|
|
planetname_stack[spacename_nstack-1] = planet->name;
|
|
systemname_stack[spacename_nstack-1] = tmp->name;
|
|
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
|
|
|
|
// Post processing.
|
|
if(tmp->nplanets > 0)
|
|
// TODO: Make dependant on overall planet faction.
|
|
tmp->faction = tmp->planets[0].faction;
|
|
return tmp;
|
|
}
|
|
|
|
// Load the jumps into a system.
|
|
static void system_parseJumps(const xmlNodePtr parent) {
|
|
int i;
|
|
StarSystem* system;
|
|
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) {
|
|
system = &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) {
|
|
system->njumps++;
|
|
system->jumps = realloc(system->jumps, system->njumps*sizeof(int));
|
|
system->jumps[system->njumps-1] = i;
|
|
break;
|
|
}
|
|
if(i == systems_nstack)
|
|
WARN("System '%s' not found for jump linking", xml_get(cur));
|
|
}
|
|
} while((cur = cur->next));
|
|
}
|
|
} while((node = node->next));
|
|
}
|
|
|
|
// Load the ENTIRE universe into RAM. -- WOAH!
|
|
// -- Used a two system pass to first load the star systems_stack and then set jump routes.
|
|
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;
|
|
}
|
|
// Fist pass - Load all the star systems_stack.
|
|
do {
|
|
if(xml_isNode(node, XML_SYSTEM_TAG)) {
|
|
tmp = system_parse(node);
|
|
systems_stack = realloc(systems_stack, sizeof(StarSystem)*(++systems_nstack));
|
|
memcpy(systems_stack+systems_nstack-1, tmp, sizeof(StarSystem));
|
|
free(tmp);
|
|
}
|
|
} while((node = node->next));
|
|
|
|
// 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((node = node->next));
|
|
|
|
// Cleanup.
|
|
xmlFreeDoc(doc);
|
|
free(buf);
|
|
xmlCleanupParser();
|
|
|
|
DEBUG("Loaded %d star system%s with %d planet%s",
|
|
systems_nstack, (systems_nstack==1) ? "" : "s",
|
|
nplanets, (nplanets==1) ? "" : "s");
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Render the system. -- Just playing god now.
|
|
void space_render(double dt) {
|
|
int i;
|
|
unsigned int t, timer;
|
|
double x, y, m, b;
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPushMatrix(); // Translation matrix.
|
|
glTranslated(-(double)gl_screen.w/2., -(double)gl_screen.h/2., 0);
|
|
|
|
t = SDL_GetTicks();
|
|
if(!player_isFlag(PLAYER_DESTROYED) &&
|
|
pilot_isFlag(player, PILOT_HYPERSPACE) && // Hyperspace fancy effect.
|
|
!paused && (player->ptimer-HYPERSPACE_STARS_BLUR < t)) {
|
|
|
|
timer = player->ptimer - HYPERSPACE_STARS_BLUR;
|
|
|
|
// 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)) { // 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 > 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);
|
|
}
|
|
} else { // Just render.
|
|
for(i = 0; i < nstars; i++) {
|
|
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,j;
|
|
|
|
// Free the names.
|
|
//if(planetname_stack) free(planetname_stack);
|
|
//if(systemname_stack) free(systemname_stack);
|
|
if(planetname_stack) {
|
|
free(planetname_stack);
|
|
planetname_stack = NULL;
|
|
}
|
|
if(systemname_stack) {
|
|
free(systemname_stack);
|
|
systemname_stack = NULL;
|
|
}
|
|
spacename_nstack = 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 some planets.
|
|
for(j = 0; j < systems_stack[i].nplanets; j++) {
|
|
free(systems_stack[i].planets[j].name);
|
|
if(systems_stack[i].planets[j].description)
|
|
free(systems_stack[i].planets[j].description);
|
|
if(systems_stack[i].planets[j].bar_description)
|
|
free(systems_stack[i].planets[j].bar_description);
|
|
|
|
// Graphics.
|
|
if(systems_stack[i].planets[j].gfx_space)
|
|
gl_freeTexture(systems_stack[i].planets[j].gfx_space);
|
|
if(systems_stack[i].planets[j].gfx_exterior)
|
|
gl_freeTexture(systems_stack[i].planets[j].gfx_exterior);
|
|
|
|
// Commodities.
|
|
free(systems_stack[i].planets[j].commodities);
|
|
}
|
|
free(systems_stack[i].planets);
|
|
}
|
|
free(systems_stack);
|
|
systems_stack = NULL;
|
|
systems_nstack = 0;
|
|
|
|
// Stars must be set free too.
|
|
if(stars) free(stars);
|
|
stars = NULL;
|
|
nstars = 0;
|
|
}
|
|
|