Merge branch 'master' of git://github.com/BTAxis/naev into testmission
[naev.git] / src / space.c
blob56b46c595a5594f245c8cfd207c8a9501a45b03b
1 /*
2 * See Licensing and Copyright notice in naev.h
3 */
5 /**
6 * @file space.c
8 * @brief Handles all the space stuff, namely systems and planets.
9 */
11 #include "space.h"
13 #include "naev.h"
15 #include <stdlib.h>
16 #include <math.h>
18 #include "nxml.h"
20 #include "opengl.h"
21 #include "log.h"
22 #include "rng.h"
23 #include "ndata.h"
24 #include "player.h"
25 #include "pause.h"
26 #include "weapon.h"
27 #include "toolkit.h"
28 #include "spfx.h"
29 #include "ntime.h"
30 #include "nebula.h"
31 #include "sound.h"
32 #include "music.h"
33 #include "gui.h"
34 #include "fleet.h"
35 #include "mission.h"
36 #include "conf.h"
39 #define XML_PLANET_ID "Planets" /**< Planet xml document tag. */
40 #define XML_PLANET_TAG "planet" /**< Individual planet xml tag. */
42 #define XML_SYSTEM_ID "Systems" /**< Systems xml document tag. */
43 #define XML_SYSTEM_TAG "ssys" /**< Individual systems xml tag. */
45 #define PLANET_DATA "dat/planet.xml" /**< XML file containing planets. */
46 #define SYSTEM_DATA "dat/ssys.xml" /**< XML file containing systems. */
48 #define PLANET_GFX_SPACE "gfx/planet/space/" /**< Location of planet space graphics. */
49 #define PLANET_GFX_EXTERIOR "gfx/planet/exterior/" /**< Location of planet exterior graphics (when landed). */
51 #define PLANET_GFX_EXTERIOR_W 400 /**< Planet exterior graphic width. */
52 #define PLANET_GFX_EXTERIOR_H 400 /**< Planet exterior graphic height. */
54 #define CHUNK_SIZE 32 /**< Size to allocate by. */
55 #define CHUNK_SIZE_SMALL 8 /**< Smaller size to allocate chunks by. */
57 /* used to overcome warnings due to 0 values */
58 #define FLAG_XSET (1<<0) /**< Set the X position value. */
59 #define FLAG_YSET (1<<1) /**< Set the Y position value. */
60 #define FLAG_ASTEROIDSSET (1<<2) /**< Set the asteroid value. */
61 #define FLAG_INTERFERENCESET (1<<3) /**< Set the interference value. */
62 #define FLAG_SERVICESSET (1<<4) /**< Set the service value. */
63 #define FLAG_TECHSET (1<<5) /**< Set the tech value. */
64 #define FLAG_FACTIONSET (1<<6) /**< Set the faction value. */
68 * planet <-> system name stack
70 static char** planetname_stack = NULL; /**< Planet name stack corresponding to system. */
71 static char** systemname_stack = NULL; /**< System name stack corresponding to planet. */
72 static int spacename_nstack = 0; /**< Size of planet<->system stack. */
73 static int spacename_mstack = 0; /**< Size of memory in planet<->system stack. */
77 * Star system stack.
79 StarSystem *systems_stack = NULL; /**< Star system stack. */
80 int systems_nstack = 0; /**< Number of star systems. */
81 static int systems_mstack = 0; /**< Number of memory allocated for star system stack. */
84 * Planet stack.
86 static Planet *planet_stack = NULL; /**< Planet stack. */
87 static int planet_nstack = 0; /**< Planet stack size. */
88 static int planet_mstack = 0; /**< Memory size of planet stack. */
91 * Misc.
93 static int systems_loading = 1; /**< Systems are loading. */
94 StarSystem *cur_system = NULL; /**< Current star system. */
98 * fleet spawn rate
100 int space_spawn = 1; /**< Spawn enabled by default. */
101 static double spawn_timer = 0; /**< Timer that controls spawn rate. */
102 extern int pilot_nstack;
106 * star stack and friends
108 #define STAR_BUF 100 /**< Area to leave around screen for stars, more = less repitition */
110 * @struct Star
112 * @brief Represents a background star. */
113 static gl_vbo *star_vertexVBO = NULL; /**< Star Vertex VBO. */
114 static gl_vbo *star_colourVBO = NULL; /**< Star Colour VBO. */
115 static GLfloat *star_vertex = NULL; /**< Vertex of the stars. */
116 static GLfloat *star_colour = NULL; /**< Brightness of the stars. */
117 static unsigned int nstars = 0; /**< total stars */
118 static unsigned int mstars = 0; /**< memory stars are taking */
122 * Interference.
124 extern double interference_alpha; /* gui.c */
125 static double interference_target = 0.; /**< Target alpha level. */
126 static double interference_timer = 0.; /**< Interference timer. */
130 * Internal Prototypes.
132 /* planet load */
133 static int planet_parse( Planet* planet, const xmlNodePtr parent );
134 /* system load */
135 static int systems_load (void);
136 static StarSystem* system_parse( StarSystem *system, const xmlNodePtr parent );
137 static void system_parseJumps( const xmlNodePtr parent );
138 /* misc */
139 static int system_calcSecurity( StarSystem *sys );
140 static void system_setFaction( StarSystem *sys );
141 static void space_addFleet( Fleet* fleet, int init );
142 static PlanetClass planetclass_get( const char a );
144 * Externed prototypes.
146 int space_sysSave( xmlTextWriterPtr writer );
147 int space_sysLoad( xmlNodePtr parent );
151 * @brief Basically returns a PlanetClass integer from a char
153 * @param a Char to get class from.
154 * @return Identifier matching the char.
156 static PlanetClass planetclass_get( const char a )
158 switch (a) {
159 /* planets use letters */
160 case 'A': return PLANET_CLASS_A;
161 case 'B': return PLANET_CLASS_B;
162 case 'C': return PLANET_CLASS_C;
163 case 'D': return PLANET_CLASS_D;
164 case 'E': return PLANET_CLASS_E;
165 case 'F': return PLANET_CLASS_F;
166 case 'G': return PLANET_CLASS_G;
167 case 'H': return PLANET_CLASS_H;
168 case 'I': return PLANET_CLASS_I;
169 case 'J': return PLANET_CLASS_J;
170 case 'K': return PLANET_CLASS_K;
171 case 'L': return PLANET_CLASS_L;
172 case 'M': return PLANET_CLASS_M;
173 case 'N': return PLANET_CLASS_N;
174 case 'O': return PLANET_CLASS_O;
175 case 'P': return PLANET_CLASS_P;
176 case 'Q': return PLANET_CLASS_Q;
177 case 'R': return PLANET_CLASS_R;
178 case 'S': return PLANET_CLASS_S;
179 case 'T': return PLANET_CLASS_T;
180 case 'X': return PLANET_CLASS_X;
181 case 'Y': return PLANET_CLASS_Y;
182 case 'Z': return PLANET_CLASS_Z;
183 /* stations use numbers - not as many types */
184 case '0': return STATION_CLASS_A;
185 case '1': return STATION_CLASS_B;
186 case '2': return STATION_CLASS_C;
187 case '3': return STATION_CLASS_D;
189 default:
190 WARN("Invalid planet class.");
191 return PLANET_CLASS_NULL;
195 * @brief Gets the char representing the planet class from the planet.
197 * @param p Planet to get the class char from.
198 * @return The planet's class char.
200 char planet_getClass( Planet *p )
202 switch (p->class) {
203 case PLANET_CLASS_A: return 'A';
204 case PLANET_CLASS_B: return 'B';
205 case PLANET_CLASS_C: return 'C';
206 case PLANET_CLASS_D: return 'D';
207 case PLANET_CLASS_E: return 'E';
208 case PLANET_CLASS_F: return 'F';
209 case PLANET_CLASS_G: return 'G';
210 case PLANET_CLASS_H: return 'H';
211 case PLANET_CLASS_I: return 'I';
212 case PLANET_CLASS_J: return 'J';
213 case PLANET_CLASS_K: return 'K';
214 case PLANET_CLASS_L: return 'L';
215 case PLANET_CLASS_M: return 'M';
216 case PLANET_CLASS_N: return 'N';
217 case PLANET_CLASS_O: return 'O';
218 case PLANET_CLASS_P: return 'P';
219 case PLANET_CLASS_Q: return 'Q';
220 case PLANET_CLASS_R: return 'R';
221 case PLANET_CLASS_S: return 'S';
222 case PLANET_CLASS_T: return 'T';
223 case PLANET_CLASS_X: return 'X';
224 case PLANET_CLASS_Y: return 'Y';
225 case PLANET_CLASS_Z: return 'Z';
226 /* Stations */
227 case STATION_CLASS_A: return '0';
228 case STATION_CLASS_B: return '1';
229 case STATION_CLASS_C: return '2';
230 case STATION_CLASS_D: return '3';
232 default:
233 WARN("Invalid planet class.");
234 return 0;
240 * @brief Checks to make sure if pilot is far enough away to hyperspace.
242 * @param p Pilot to check if he can hyperspace.
243 * @return 1 if he can hyperspace, 0 else.
245 int space_canHyperspace( Pilot* p)
247 int i;
248 double d;
249 if (p->fuel < HYPERSPACE_FUEL) return 0;
251 for (i=0; i < cur_system->nplanets; i++) {
252 d = vect_dist(&p->solid->pos, &cur_system->planets[i]->pos);
253 if (d < HYPERSPACE_EXIT_MIN)
254 return 0;
256 return 1;
261 * @brief Tries to get the pilot into hyperspace.
263 * @param p Pilot to try to start hyperspacing.
264 * @return 0 on success.
266 int space_hyperspace( Pilot* p )
268 if (p->fuel < HYPERSPACE_FUEL)
269 return -3;
270 if (!space_canHyperspace(p))
271 return -1;
273 /* pilot is now going to get automatically ready for hyperspace */
274 pilot_setFlag(p, PILOT_HYP_PREP);
276 return 0;
281 * @brief Gets the name of all the planets that belong to factions.
283 * @param[out] nplanets Number of planets found.
284 * @param factions Factions to check against.
285 * @param nfactions Number of factions in factions.
286 * @return An array of faction names. Individual names are not allocated.
288 char** space_getFactionPlanet( int *nplanets, int *factions, int nfactions )
290 int i,j,k;
291 Planet* planet;
292 char **tmp;
293 int ntmp;
294 int mtmp;
296 ntmp = 0;
297 mtmp = CHUNK_SIZE;
298 tmp = malloc(sizeof(char*) * mtmp);
300 for (i=0; i<systems_nstack; i++)
301 for (j=0; j<systems_stack[i].nplanets; j++) {
302 planet = systems_stack[i].planets[j];
303 for (k=0; k<nfactions; k++)
304 if (planet->faction == factions[k]) {
305 ntmp++;
306 if (ntmp > mtmp) { /* need more space */
307 mtmp += CHUNK_SIZE;
308 tmp = realloc(tmp, sizeof(char*) * mtmp);
310 tmp[ntmp-1] = planet->name;
311 break; /* no need to check all factions */
315 (*nplanets) = ntmp;
316 return tmp;
321 * @brief Gets the name of a random planet.
323 * @return The name of a random planet.
325 char* space_getRndPlanet (void)
327 int i,j;
328 char **tmp;
329 int ntmp;
330 int mtmp;
331 char *res;
333 ntmp = 0;
334 mtmp = CHUNK_SIZE;
335 tmp = malloc(sizeof(char*) * mtmp);
337 for (i=0; i<systems_nstack; i++)
338 for (j=0; j<systems_stack[i].nplanets; j++) {
339 ntmp++;
340 if (ntmp > mtmp) { /* need more space */
341 mtmp += CHUNK_SIZE;
342 tmp = realloc(tmp, sizeof(char*) * mtmp);
344 tmp[ntmp-1] = systems_stack[i].planets[j]->name;
347 res = tmp[RNG(0,ntmp-1)];
348 free(tmp);
350 return res;
355 * @brief Sees if a system is reachable.
357 * @return 1 if target system is reachable, 0 if it isn't.
359 int space_sysReachable( StarSystem *sys )
361 int i;
363 if (sys_isKnown(sys)) return 1; /* it is known */
365 /* check to see if it is adjacent to known */
366 for (i=0; i<sys->njumps; i++)
367 if (sys_isKnown(system_getIndex( sys->jumps[i] )))
368 return 1;
370 return 0;
375 * @brief Get the system from it's name.
377 * @param sysname Name to match.
378 * @return System matching sysname.
380 StarSystem* system_get( const char* sysname )
382 int i;
384 for (i=0; i<systems_nstack; i++)
385 if (strcmp(sysname, systems_stack[i].name)==0)
386 return &systems_stack[i];
388 DEBUG("System '%s' not found in stack", sysname);
389 return NULL;
394 * @brief Get the system by it's index.
396 * @param id Index to match.
397 * @return System matching index.
399 StarSystem* system_getIndex( int id )
401 return &systems_stack[ id ];
406 * @brief Get the name of a system from a planetname.
408 * @param planetname Planet name to match.
409 * @return Name of the system planet belongs to.
411 char* planet_getSystem( const char* planetname )
413 int i;
415 for (i=0; i<spacename_nstack; i++)
416 if (strcmp(planetname_stack[i],planetname)==0)
417 return systemname_stack[i];
419 DEBUG("Planet '%s' not found in planetname stack", planetname);
420 return NULL;
425 * @brief Gets a planet based on it's name.
427 * @param planetname Name to match.
428 * @return Planet matching planetname.
430 Planet* planet_get( const char* planetname )
432 int i;
434 if (planetname==NULL) {
435 WARN("Trying to find NULL planet...");
436 return NULL;
439 for (i=0; i<planet_nstack; i++)
440 if (strcmp(planet_stack[i].name,planetname)==0)
441 return &planet_stack[i];
443 WARN("Planet '%s' not found in the universe", planetname);
444 return NULL;
449 * @brief Controls fleet spawning.
451 * @param dt Current delta tick.
453 void space_update( const double dt )
455 int i, j, f;
456 double mod;
457 double target;
459 /* Needs a current system. */
460 if (cur_system == NULL)
461 return;
465 * Spawning.
467 if (space_spawn) {
468 spawn_timer -= dt;
470 /* Only check if there are fleets and pilots. */
471 if ((cur_system->nfleets == 0) || (cur_system->avg_pilot == 0.))
472 spawn_timer = 300.;
474 if (spawn_timer < 0.) { /* time to possibly spawn */
476 /* spawn chance is based on overall % */
477 f = RNG(0,100*cur_system->nfleets);
478 j = 0;
479 for (i=0; i < cur_system->nfleets; i++) {
480 j += cur_system->fleets[i].chance;
481 if (f < j) { /* add one fleet */
482 space_addFleet( cur_system->fleets[i].fleet, 0 );
483 break;
487 /* Target is actually half of average pilots. */
488 target = cur_system->avg_pilot/2.;
490 /* Base timer. */
491 spawn_timer = 45./target;
493 /* Calculate ship modifier, it tries to stabilize at avg pilots. */
494 /* First get pilot facton ==> [-1., inf ] */
495 mod = (double)pilot_nstack - target;
496 mod /= target;
497 /* Scale and offset. */
498 /*mod = 2.*mod + 1.5;*/
499 mod = MIN( mod, -0.5 );
500 /* Modify timer. */
501 spawn_timer *= 1. + mod;
507 * Volatile systems.
509 if (cur_system->nebu_volatility > 0.) {
510 /* Player takes damage. */
511 if (player)
512 pilot_hit( player, NULL, 0, DAMAGE_TYPE_RADIATION,
513 pow2(cur_system->nebu_volatility) / 500. * dt );
518 * Interference.
520 if (cur_system->interference > 0.) {
521 /* Always dark. */
522 if (cur_system->interference >= 1000.)
523 interference_alpha = 1.;
525 /* Normal scenario. */
526 else {
527 interference_timer -= dt;
528 if (interference_timer < 0.) {
529 /* 0 -> [ 1, 5 ]
530 * 250 -> [ 0.75, 3.75 ]
531 * 500 -> [ 0.5, 2.5 ]
532 * 750 -> [ 0.25, 1.25 ]
533 * 1000 -> [ 0, 0 ] */
534 interference_timer += (1000. - cur_system->interference) / 1000. *
535 (3. + RNG_2SIGMA() );
537 /* 0 -> [ 0, 0 ]
538 * 250 -> [-0.5, 1.5 ]
539 * 500 -> [ -1, 3 ]
540 * 1000 -> [ 0, 6 ] */
541 interference_target = cur_system->interference/1000. * 2. *
542 (1. + RNG_2SIGMA() );
545 /* Head towards target. */
546 if (fabs(interference_alpha - interference_target) > 1e-05) {
547 /* Assymptotic. */
548 interference_alpha += (interference_target - interference_alpha) * dt;
550 /* Limit alpha to [0.-1.]. */
551 if (interference_alpha > 1.)
552 interference_alpha = 1.;
553 else if (interference_alpha < 0.)
554 interference_alpha = 0.;
562 * @brief Creates a fleet.
564 * @param fleet Fleet to add to the system.
565 * @param init Is being run during the space initialization.
567 static void space_addFleet( Fleet* fleet, int init )
569 FleetPilot *plt;
570 Planet *planet;
571 int i, c;
572 unsigned int flags;
573 double a, d;
574 Vector2d vv,vp, vn;
576 /* Needed to determine angle. */
577 vectnull(&vn);
579 /* c will determino how to create the fleet, only non-zero if it's run in init. */
580 if (init == 1) {
581 if (RNGF() < 0.5) /* 50% chance of starting out en route. */
582 c = 2;
583 else if (RNGF() < 0.5) /* 25% of starting out landed. */
584 c = 1;
585 else /* 25% chance starting out entering hyperspace. */
586 c = 0;
588 else c = 0;
590 /* simulate they came from hyperspace */
591 if (c==0) {
592 d = RNGF()*(HYPERSPACE_ENTER_MAX-HYPERSPACE_ENTER_MIN) + HYPERSPACE_ENTER_MIN;
593 vect_pset( &vp, d, RNGF()*2.*M_PI);
595 /* Starting out landed or heading towards landing.. */
596 else if ((c==1) || (c==2)) {
597 /* Get friendly planet to land on. */
598 planet = NULL;
599 for (i=0; i<cur_system->nplanets; i++)
600 if (planet_hasService(cur_system->planets[i],PLANET_SERVICE_INHABITED) &&
601 !areEnemies(fleet->faction,cur_system->planets[i]->faction)) {
602 planet = cur_system->planets[i];
603 break;
606 /* No suitable planet found. */
607 if (planet == NULL) {
608 d = RNGF()*(HYPERSPACE_ENTER_MAX-HYPERSPACE_ENTER_MIN) + HYPERSPACE_ENTER_MIN;
609 vect_pset( &vp, d, RNGF()*2.*M_PI);
610 c = 0;
612 else {
613 /* Start out landed. */
614 if (c==1)
615 vectcpy( &vp, &planet->pos );
616 /* Start out near landed. */
617 else if (c==2) {
618 d = RNGF()*(HYPERSPACE_ENTER_MAX-HYPERSPACE_ENTER_MIN) + HYPERSPACE_ENTER_MIN;
619 vect_pset( &vp, d, RNGF()*2.*M_PI);
624 for (i=0; i < fleet->npilots; i++) {
625 plt = &fleet->pilots[i];
626 if (RNG(0,100) <= plt->chance) {
627 /* other ships in the fleet should start split up */
628 vect_cadd(&vp, RNG(75,150) * (RNG(0,1) ? 1 : -1),
629 RNG(75,150) * (RNG(0,1) ? 1 : -1));
630 a = vect_angle(&vp, &vn);
631 if (a < 0.)
632 a += 2.*M_PI;
633 flags = 0;
635 /* Entering via hyperspace. */
636 if (c==0) {
637 vect_pset( &vv, HYPERSPACE_VEL, a );
638 flags |= PILOT_HYP_END;
640 /* Starting out landed. */
641 else if (c==1)
642 vectnull(&vv);
643 /* Starting out almost landed. */
644 else if (c==2)
645 /* Put speed at half in case they start very near. */
646 vect_pset( &vv, plt->ship->speed * 0.5, a );
648 /* Create the pilot. */
649 fleet_createPilot( fleet, plt, a, &vp, &vv, NULL, flags );
656 * @brief Initilaizes background stars.
658 * @param n Number of stars to add (stars per 800x640 screen).
660 void space_initStars( int n )
662 unsigned int i;
663 GLfloat w, h, hw, hh;
664 double size;
666 /* Calculate size. */
667 size = SCREEN_W*SCREEN_H+STAR_BUF*STAR_BUF;
668 size /= pow2(conf.zoom_far);
670 /* Calculate star buffer. */
671 w = (SCREEN_W + 2.*STAR_BUF);
672 w += conf.zoom_stars * (w / conf.zoom_far - 1.);
673 h = (SCREEN_H + 2.*STAR_BUF);
674 h += conf.zoom_stars * (h / conf.zoom_far - 1.);
675 hw = w / 2.;
676 hh = h / 2.;
678 /* Calculate stars. */
679 size *= n;
680 nstars = (unsigned int)(size/(800.*600.));
682 if (mstars < nstars) {
683 /* Create data. */
684 star_vertex = realloc( star_vertex, nstars * sizeof(GLfloat) * 4 );
685 star_colour = realloc( star_colour, nstars * sizeof(GLfloat) * 8 );
686 mstars = nstars;
688 for (i=0; i < nstars; i++) {
689 /* Set the position. */
690 star_vertex[4*i+0] = RNGF()*w - hw;
691 star_vertex[4*i+1] = RNGF()*h - hh;
692 star_vertex[4*i+2] = 0.;
693 star_vertex[4*i+3] = 0.;
694 /* Set the colour. */
695 star_colour[8*i+0] = 1.;
696 star_colour[8*i+1] = 1.;
697 star_colour[8*i+2] = 1.;
698 star_colour[8*i+3] = RNGF()*0.6 + 0.2;
699 star_colour[8*i+4] = 1.;
700 star_colour[8*i+5] = 1.;
701 star_colour[8*i+6] = 1.;
702 star_colour[8*i+7] = 0.;
705 /* Destroy old VBO. */
706 if (star_vertexVBO != NULL) {
707 gl_vboDestroy( star_vertexVBO );
708 star_vertexVBO = NULL;
710 if (star_colourVBO != NULL) {
711 gl_vboDestroy( star_colourVBO );
712 star_colourVBO = NULL;
715 /* Create now VBO. */
716 star_vertexVBO = gl_vboCreateStream(
717 nstars * sizeof(GLfloat) * 4, star_vertex );
718 star_colourVBO = gl_vboCreateStatic(
719 nstars * sizeof(GLfloat) * 8, star_colour );
724 * @brief Initializes the system.
726 * @param sysname Name of the system to initialize.
728 void space_init ( const char* sysname )
730 char* nt;
731 int i;
733 /* cleanup some stuff */
734 player_clear(); /* clears targets */
735 pilot_clearTimers(player); /* Clear timers. */
736 pilots_clean(); /* destroy all the current pilots, except player */
737 weapon_clear(); /* get rid of all the weapons */
738 spfx_clear(); /* get rid of the explosions */
739 space_spawn = 1; /* spawn is enabled by default. */
740 interference_timer = 0.; /* Restart timer. */
742 /* Must clear escorts to keep deployment sane. */
743 player_clearEscorts();
745 if ((sysname==NULL) && (cur_system==NULL))
746 ERR("Cannot reinit system if there is no system previously loaded");
747 else if (sysname!=NULL) {
748 for (i=0; i < systems_nstack; i++)
749 if (strcmp(sysname, systems_stack[i].name)==0)
750 break;
752 if (i>=systems_nstack)
753 ERR("System %s not found in stack", sysname);
754 cur_system = systems_stack+i;
756 nt = ntime_pretty(0);
757 player_message("\epEntering System %s on %s.", sysname, nt);
758 free(nt);
760 /* Handle background */
761 if (cur_system->nebu_density > 0.) {
762 /* Background is Nebula */
763 nebu_prep( cur_system->nebu_density, cur_system->nebu_volatility );
765 /* Set up sound. */
766 sound_env( SOUND_ENV_NEBULA, cur_system->nebu_density );
768 else {
769 /* Backrgound is Stary */
770 space_initStars( cur_system->stars );
772 /* Set up sound. */
773 sound_env( SOUND_ENV_NORMAL, 0. );
777 /* Iterate through planets to clear bribes. */
778 for (i=0; i<cur_system->nplanets; i++)
779 cur_system->planets[i]->bribed = 0;
781 /* Clear interference if you leave system with interference. */
782 if (cur_system->interference == 0.)
783 interference_alpha = 0.;
785 /* See if we should get a new music song. */
786 music_choose(NULL);
788 /* Reset player enemies. */
789 player_enemies = 0;
791 /* Update the pilot sensor range. */
792 pilot_updateSensorRange();
794 /* set up fleets -> pilots */
795 for (i=0; i < cur_system->nfleets; i++) {
796 if (RNG(0,100) <= (cur_system->fleets[i].chance/2)) /* fleet check (50% chance) */
797 space_addFleet( cur_system->fleets[i].fleet, 1 );
800 /* start the spawn timer */
801 spawn_timer = -1.;
803 /* we now know this system */
804 sys_setFlag(cur_system,SYSTEM_KNOWN);
809 * @brief Loads all the planets in the game.
811 * @return 0 on success.
813 static int planets_load ( void )
815 uint32_t bufsize;
816 char *buf;
817 xmlNodePtr node;
818 xmlDocPtr doc;
820 buf = ndata_read( PLANET_DATA, &bufsize );
821 doc = xmlParseMemory( buf, bufsize );
823 node = doc->xmlChildrenNode;
824 if (strcmp((char*)node->name,XML_PLANET_ID)) {
825 ERR("Malformed "PLANET_DATA" file: missing root element '"XML_PLANET_ID"'");
826 return -1;
829 node = node->xmlChildrenNode; /* first system node */
830 if (node == NULL) {
831 ERR("Malformed "PLANET_DATA" file: does not contain elements");
832 return -1;
835 /* Initialize stack if needed. */
836 if (planet_stack == NULL) {
837 planet_mstack = CHUNK_SIZE;
838 planet_stack = malloc( sizeof(Planet) * planet_mstack );
839 planet_nstack = 0;
842 do {
843 if (xml_isNode(node,XML_PLANET_TAG)) {
845 /* See if stack must grow. */
846 planet_nstack++;
847 if (planet_nstack > planet_mstack) {
848 planet_mstack += CHUNK_SIZE;
849 planet_stack = realloc( planet_stack, sizeof(Planet) * planet_mstack );
852 planet_parse( &planet_stack[planet_nstack-1], node );
854 } while (xml_nextNode(node));
857 * free stuff
859 xmlFreeDoc(doc);
860 free(buf);
862 return 0;
867 * @brief Parses a planet from an xml node.
869 * @param planet Planet to fill up.
870 * @param parent Node that contains planet data.
871 * @return 0 on success.
873 static int planet_parse( Planet *planet, const xmlNodePtr parent )
875 int i, mem;
876 char str[PATH_MAX];
877 xmlNodePtr node, cur, ccur;
878 unsigned int flags;
880 /* Clear up memory for sane defaults. */
881 memset( planet, 0, sizeof(Planet) );
882 planet->faction = -1;
883 flags = 0;
885 /* Get the name. */
886 xmlr_attr( parent, "name", planet->name );
888 node = parent->xmlChildrenNode;
889 do {
891 /* Only handle nodes. */
892 xml_onlyNodes(node);
894 if (xml_isNode(node,"GFX")) {
895 cur = node->children;
896 do {
897 if (xml_isNode(cur,"space")) { /* load space gfx */
898 planet->gfx_space = xml_parseTexture( cur,
899 PLANET_GFX_SPACE"%s", 1, 1, OPENGL_TEX_MIPMAPS );
901 else if (xml_isNode(cur,"exterior")) { /* load land gfx */
902 snprintf( str, PATH_MAX, PLANET_GFX_EXTERIOR"%s", xml_get(cur));
903 planet->gfx_exterior = strdup(str);
905 } while (xml_nextNode(cur));
906 continue;
908 else if (xml_isNode(node,"pos")) {
909 cur = node->children;
910 do {
911 if (xml_isNode(cur,"x")) {
912 flags |= FLAG_XSET;
913 planet->pos.x = xml_getFloat(cur);
915 else if (xml_isNode(cur,"y")) {
916 flags |= FLAG_YSET;
917 planet->pos.y = xml_getFloat(cur);
919 } while(xml_nextNode(cur));
920 continue;
922 else if (xml_isNode(node,"general")) {
923 cur = node->children;
924 do {
925 /* Direct reads. */
926 xmlr_strd(cur, "bar", planet->bar_description);
927 xmlr_strd(cur, "description", planet->description );
928 xmlr_long(cur, "population", planet->population );
929 xmlr_float(cur, "prodfactor", planet->prodfactor );
931 if (xml_isNode(cur,"class"))
932 planet->class =
933 planetclass_get(cur->children->content[0]);
934 else if (xml_isNode(cur,"faction")) {
935 flags |= FLAG_FACTIONSET;
936 planet->faction = faction_get( xml_get(cur) );
938 else if (xml_isNode(cur, "services")) {
939 flags |= FLAG_SERVICESSET;
940 ccur = cur->children;
941 planet->services = 0;
942 do {
943 xml_onlyNodes(ccur);
945 if (xml_isNode(ccur, "land"))
946 planet->services |= PLANET_SERVICE_LAND;
947 else if (xml_isNode(ccur, "refuel"))
948 planet->services |= PLANET_SERVICE_REFUEL | PLANET_SERVICE_INHABITED;
949 else if (xml_isNode(ccur, "bar"))
950 planet->services |= PLANET_SERVICE_BAR | PLANET_SERVICE_INHABITED;
951 else if (xml_isNode(ccur, "missions"))
952 planet->services |= PLANET_SERVICE_MISSIONS | PLANET_SERVICE_INHABITED;
953 else if (xml_isNode(ccur, "commodity"))
954 planet->services |= PLANET_SERVICE_COMMODITY | PLANET_SERVICE_INHABITED;
955 else if (xml_isNode(ccur, "outfits"))
956 planet->services |= PLANET_SERVICE_OUTFITS | PLANET_SERVICE_INHABITED;
957 else if (xml_isNode(ccur, "shipyard"))
958 planet->services |= PLANET_SERVICE_SHIPYARD | PLANET_SERVICE_INHABITED;
959 else
960 WARN("Planet '%s' has unknown services tag '%s'", planet->name, ccur->name);
962 } while (xml_nextNode(ccur));
964 else if (xml_isNode(cur, "tech")) {
965 ccur = cur->children;
966 do {
967 if (xml_isNode(ccur,"main")) {
968 flags |= FLAG_TECHSET;
969 planet->tech[0] = xml_getInt(ccur);
971 else if (xml_isNode(ccur,"special")) {
972 for (i=1; i<PLANET_TECH_MAX; i++)
973 if (planet->tech[i]==0) {
974 planet->tech[i] = xml_getInt(ccur);
975 break;
977 if (i==PLANET_TECH_MAX) WARN("Planet '%s' has too many"
978 "'special tech' entries", planet->name);
980 } while (xml_nextNode(ccur));
983 else if (xml_isNode(cur, "commodities")) {
984 ccur = cur->children;
985 mem = 0;
986 do {
987 if (xml_isNode(ccur,"commodity")) {
988 planet->ncommodities++;
989 /* Memory must grow. */
990 if (planet->ncommodities > mem) {
991 mem += CHUNK_SIZE_SMALL;
992 planet->commodities = realloc(planet->commodities,
993 mem * sizeof(Commodity*));
995 planet->commodities[planet->ncommodities-1] =
996 commodity_get( xml_get(ccur) );
998 } while (xml_nextNode(ccur));
999 /* Shrink to minimum size. */
1000 planet->commodities = realloc(planet->commodities,
1001 planet->ncommodities * sizeof(Commodity*));
1003 } while(xml_nextNode(cur));
1004 continue;
1007 DEBUG("Unknown node '%s' in planet '%s'",node->name,planet->name);
1008 } while (xml_nextNode(node));
1011 /* Some postprocessing. */
1012 planet->cur_prodfactor = planet->prodfactor;
1015 * verification
1017 #define MELEMENT(o,s) if (o) WARN("Planet '%s' missing '"s"' element", planet->name)
1018 MELEMENT(planet->gfx_space==NULL,"GFX space");
1019 MELEMENT( planet_hasService(planet,PLANET_SERVICE_LAND) &&
1020 planet->gfx_exterior==NULL,"GFX exterior");
1021 MELEMENT( planet_hasService(planet,PLANET_SERVICE_INHABITED) &&
1022 (planet->population==0), "population");
1023 MELEMENT( planet_hasService(planet,PLANET_SERVICE_INHABITED) &&
1024 (planet->prodfactor==0.), "prodfactor");
1025 MELEMENT((flags&FLAG_XSET)==0,"x");
1026 MELEMENT((flags&FLAG_YSET)==0,"y");
1027 MELEMENT(planet->class==PLANET_CLASS_NULL,"class");
1028 MELEMENT( planet_hasService(planet,PLANET_SERVICE_LAND) &&
1029 planet->description==NULL,"desription");
1030 MELEMENT( planet_hasService(planet,PLANET_SERVICE_BAR) &&
1031 planet->bar_description==NULL,"bar");
1032 MELEMENT( planet_hasService(planet,PLANET_SERVICE_INHABITED) &&
1033 (flags&FLAG_FACTIONSET)==0,"faction");
1034 MELEMENT((flags&FLAG_SERVICESSET)==0,"services");
1035 MELEMENT( (planet_hasService(planet,PLANET_SERVICE_OUTFITS) ||
1036 planet_hasService(planet,PLANET_SERVICE_SHIPYARD)) &&
1037 (flags&FLAG_TECHSET)==0, "tech" );
1038 MELEMENT( planet_hasService(planet,PLANET_SERVICE_COMMODITY) &&
1039 (planet->ncommodities==0),"commodity" );
1040 #undef MELEMENT
1042 return 0;
1047 * @brief Adds a planet to a star system.
1049 * @param sys Star System to add planet to.
1050 * @param planetname Name of the planet to add.
1051 * @return 0 on success.
1053 int system_addPlanet( StarSystem *sys, const char *planetname )
1055 Planet *planet;
1057 if (sys == NULL)
1058 return -1;
1060 /* Check if need to grow the star system planet stack. */
1061 sys->nplanets++;
1062 if (sys->planets == NULL)
1063 sys->planets = malloc( sizeof(Planet*) * CHUNK_SIZE_SMALL );
1064 else if (sys->nplanets > CHUNK_SIZE_SMALL)
1065 sys->planets = realloc( sys->planets, sizeof(Planet*) * sys->nplanets );
1066 planet = planet_get(planetname);
1067 if (planet == NULL)
1068 return -1;
1069 sys->planets[sys->nplanets-1] = planet;
1071 /* add planet <-> star system to name stack */
1072 spacename_nstack++;
1073 if (spacename_nstack > spacename_mstack) {
1074 spacename_mstack += CHUNK_SIZE;
1075 planetname_stack = realloc(planetname_stack,
1076 sizeof(char*) * spacename_mstack);
1077 systemname_stack = realloc(systemname_stack,
1078 sizeof(char*) * spacename_mstack);
1080 planetname_stack[spacename_nstack-1] = planet->name;
1081 systemname_stack[spacename_nstack-1] = sys->name;
1083 system_setFaction(sys);
1085 /* Regenerate the economy stuff. */
1086 economy_refresh();
1088 return 0;
1093 * @brief Removes a planet from a star system.
1095 * @param sys Star System to remove planet from.
1096 * @param planetname Name of the planet to remove.
1097 * @return 0 on success.
1099 int system_rmPlanet( StarSystem *sys, const char *planetname )
1101 int i, found;
1102 Planet *planet ;
1104 if (sys == NULL) {
1105 WARN("Unable to remove planet '%s' from NULL system.", planetname);
1106 return -1;
1109 /* Try to find planet. */
1110 planet = planet_get( planetname );
1111 for (i=0; i<sys->nplanets; i++)
1112 if (sys->planets[i] == planet)
1113 break;
1115 /* Planet not found. */
1116 if (i>=sys->nplanets) {
1117 WARN("Planet '%s' not found in system '%s' for removal.", planetname, sys->name);
1118 return -1;
1121 /* Remove planet from system. */
1122 sys->nplanets--;
1123 memmove( &sys->planets[i], &sys->planets[i+1], sizeof(Planet*) * (sys->nplanets-i) );
1125 /* Remove from the name stack thingy. */
1126 found = 0;
1127 for (i=0; i<spacename_nstack; i++)
1128 if (strcmp(planetname, planetname_stack[i])==0) {
1129 spacename_nstack--;
1130 memmove( &planetname_stack[i], &planetname_stack[i+1],
1131 sizeof(char*) * (spacename_nstack-i) );
1132 memmove( &systemname_stack[i], &systemname_stack[i+1],
1133 sizeof(char*) * (spacename_nstack-i) );
1134 found = 1;
1135 break;
1137 if (found == 0)
1138 WARN("Unable to find planet '%s' and system '%s' in planet<->system stack.",
1139 planetname, sys->name );
1141 system_setFaction(sys);
1143 /* Regenerate the economy stuff. */
1144 economy_refresh();
1146 return 0;
1151 * @brief Adds a fleet to a star system.
1153 * @param sys Star System to add fleet to.
1154 * @param fleet Fleet to add.
1155 * @return 0 on success.
1157 int system_addFleet( StarSystem *sys, SystemFleet *fleet )
1159 int i;
1160 double avg;
1162 if (sys == NULL)
1163 return -1;
1165 /* Add the fleet. */
1166 sys->nfleets++;
1167 sys->fleets = realloc( sys->fleets, sizeof(SystemFleet)*sys->nfleets );
1168 memcpy( &sys->fleets[sys->nfleets-1], fleet, sizeof(SystemFleet) );
1170 /* Adjust the system average. */
1171 avg = 0.;
1172 for (i=0; i < fleet->fleet->npilots; i++)
1173 avg += ((double)fleet->fleet->pilots[i].chance) / 100.;
1174 avg *= ((double)fleet->chance) / 100.;
1175 sys->avg_pilot += avg;
1177 /* Recalculate security. */
1178 system_calcSecurity(sys);
1180 return 0;
1185 * @brief Removes a fleet from a star system.
1187 * @param sys Star System to remove fleet from.
1188 * @param fleet Fleet to remove.
1189 * @return 0 on success.
1191 int system_rmFleet( StarSystem *sys, SystemFleet *fleet )
1193 int i;
1194 double avg;
1196 /* Find a matching fleet (will grab first since can be duplicates). */
1197 for (i=0; i<sys->nfleets; i++)
1198 if ((fleet->fleet == sys->fleets[i].fleet) &&
1199 (fleet->chance == sys->fleets[i].chance))
1200 break;
1202 /* Not found. */
1203 if (i >= sys->nfleets)
1204 return -1;
1206 /* Remove the fleet. */
1207 sys->nfleets--;
1208 memmove(&sys->fleets[i], &sys->fleets[i+1], sizeof(SystemFleet) * (sys->nfleets - i));
1209 sys->fleets = realloc(sys->fleets, sizeof(SystemFleet) * sys->nfleets);
1211 /* Adjust the system average. */
1212 avg = 0.;
1213 for (i=0; i < fleet->fleet->npilots; i++)
1214 avg += ((double)fleet->fleet->pilots[i].chance) / 100.;
1215 avg *= ((double)fleet->chance) / 100.;
1216 sys->avg_pilot -= avg;
1218 /* Recalculate security. */
1219 system_calcSecurity(sys);
1221 return 0;
1226 * @brief Adds a FleetGroup to a star system.
1228 * @param sys Star System to add fleet to.
1229 * @param fltgrp FleetGroup to add.
1230 * @return 0 on success.
1232 int system_addFleetGroup( StarSystem *sys, FleetGroup *fltgrp )
1234 int i;
1235 SystemFleet fleet;
1237 if (sys == NULL)
1238 return -1;
1240 /* Add all the fleets. */
1241 for (i=0; i<fltgrp->nfleets; i++) {
1242 fleet.fleet = fltgrp->fleets[i];
1243 fleet.chance = fltgrp->chance[i];
1244 if (system_addFleet( sys, &fleet ))
1245 return -1;
1248 return 0;
1253 * @brief Removes a fleetgroup from a star system.
1255 * @param sys Star System to remove fleet from.
1256 * @param fltgrp FleetGroup to remove.
1257 * @return 0 on success.
1259 int system_rmFleetGroup( StarSystem *sys, FleetGroup *fltgrp )
1261 int i;
1262 SystemFleet fleet;
1264 if (sys == NULL)
1265 return -1;
1267 /* Add all the fleets. */
1268 for (i=0; i<fltgrp->nfleets; i++) {
1269 fleet.fleet = fltgrp->fleets[i];
1270 fleet.chance = fltgrp->chance[i];
1271 if (system_rmFleet( sys, &fleet ))
1272 return -1;
1275 return 0;
1280 * @brief Creates a system from an XML node.
1282 * @param parent XML node to get system from.
1283 * @return System matching parent data.
1285 static StarSystem* system_parse( StarSystem *sys, const xmlNodePtr parent )
1287 Planet* planet;
1288 SystemFleet fleet;
1289 Fleet *flt;
1290 FleetGroup *fltgrp;
1291 char *ptrc;
1292 xmlNodePtr cur, node;
1293 uint32_t flags;
1294 int size;
1296 /* Clear memory for sane defaults. */
1297 memset( sys, 0, sizeof(StarSystem) );
1298 flags = 0;
1299 sys->faction = -1;
1300 planet = NULL;
1301 size = 0;
1303 sys->name = xml_nodeProp(parent,"name"); /* already mallocs */
1305 node = parent->xmlChildrenNode;
1307 do { /* load all the data */
1309 /* Only handle nodes. */
1310 xml_onlyNodes(node);
1312 if (xml_isNode(node,"pos")) {
1313 cur = node->children;
1314 do {
1315 if (xml_isNode(cur,"x")) {
1316 flags |= FLAG_XSET;
1317 sys->pos.x = xml_getFloat(cur);
1319 else if (xml_isNode(cur,"y")) {
1320 flags |= FLAG_YSET;
1321 sys->pos.y = xml_getFloat(cur);
1323 } while (xml_nextNode(cur));
1324 continue;
1326 else if (xml_isNode(node,"general")) {
1327 cur = node->children;
1328 do {
1329 if (xml_isNode(cur,"stars")) /* non-zero */
1330 sys->stars = xml_getInt(cur);
1331 else if (xml_isNode(cur,"asteroids")) {
1332 flags |= FLAG_ASTEROIDSSET;
1333 sys->asteroids = xml_getInt(cur);
1335 else if (xml_isNode(cur,"interference")) {
1336 flags |= FLAG_INTERFERENCESET;
1337 sys->interference = xml_getFloat(cur);
1339 else if (xml_isNode(cur,"nebula")) {
1340 ptrc = xml_nodeProp(cur,"volatility");
1341 if (ptrc != NULL) { /* Has volatility */
1342 sys->nebu_volatility = atof(ptrc);
1343 free(ptrc);
1345 sys->nebu_density = xml_getFloat(cur);
1347 } while (xml_nextNode(cur));
1348 continue;
1350 /* loads all the planets */
1351 else if (xml_isNode(node,"planets")) {
1352 cur = node->children;
1353 do {
1354 if (xml_isNode(cur,"planet"))
1355 system_addPlanet( sys, xml_get(cur) );
1356 } while (xml_nextNode(cur));
1357 continue;
1359 /* loads all the fleets */
1360 else if (xml_isNode(node,"fleets")) {
1361 cur = node->children;
1362 do {
1363 if (xml_isNode(cur,"fleet")) {
1365 /* Try to load it as a FleetGroup. */
1366 fltgrp = fleet_getGroup(xml_get(cur));
1367 if (fltgrp != NULL) {
1368 /* Try to load it as a FleetGroup. */
1369 fltgrp = fleet_getGroup(xml_get(cur));
1370 if (fltgrp == NULL) {
1371 WARN("Fleet '%s' for Star System '%s' not found",
1372 xml_get(cur), sys->name);
1373 continue;
1376 /* Add the fleetgroup. */
1377 system_addFleetGroup( sys, fltgrp );
1379 else {
1381 /* Try to load it as a fleet. */
1382 flt = fleet_get(xml_get(cur));
1383 if (flt == NULL) {
1384 WARN("Fleet '%s' for Star System '%s' not found",
1385 xml_get(cur), sys->name);
1386 continue;
1388 /* Get the fleet. */
1389 fleet.fleet = flt;
1391 /* Get the chance. */
1392 xmlr_attr(cur,"chance",ptrc); /* mallocs ptrc */
1393 fleet.chance = (ptrc==NULL) ? 0 : atoi(ptrc);
1394 if (fleet.chance == 0)
1395 WARN("Fleet '%s' for Star System '%s' has 0%% chance to appear",
1396 fleet.fleet->name, sys->name);
1397 if (ptrc)
1398 free(ptrc); /* free the ptrc */
1400 /* Add the fleet. */
1401 system_addFleet( sys, &fleet );
1404 } while (xml_nextNode(cur));
1405 continue;
1408 /* Avoid warning. */
1409 if (xml_isNode(node,"jumps"))
1410 continue;
1412 DEBUG("Unknown node '%s' in star system '%s'",node->name,sys->name);
1413 } while (xml_nextNode(node));
1415 #define MELEMENT(o,s) if (o) WARN("Star System '%s' missing '"s"' element", sys->name)
1416 if (sys->name == NULL) WARN("Star System '%s' missing 'name' tag", sys->name);
1417 MELEMENT((flags&FLAG_XSET)==0,"x");
1418 MELEMENT((flags&FLAG_YSET)==0,"y");
1419 MELEMENT(sys->stars==0,"stars");
1420 MELEMENT((flags&FLAG_ASTEROIDSSET)==0,"asteroids");
1421 MELEMENT((flags&FLAG_INTERFERENCESET)==0,"inteference");
1422 #undef MELEMENT
1424 /* post-processing */
1425 system_setFaction( sys );
1427 return 0;
1432 * @brief Sets the system faction based on the planets it has.
1434 * @param sys System to set the faction of.
1436 static void system_setFaction( StarSystem *sys )
1438 int i;
1439 sys->faction = -1;
1440 for (i=0; i<sys->nplanets; i++) /** @todo Handle multiple different factions. */
1441 if (sys->planets[i]->faction > 0) {
1442 sys->faction = sys->planets[i]->faction;
1443 break;
1449 * @brief Loads the jumps into a system.
1451 * @param parent System parent node.
1453 static void system_parseJumps( const xmlNodePtr parent )
1455 int i;
1456 StarSystem *sys;
1457 char* name;
1458 xmlNodePtr cur, node;
1460 name = xml_nodeProp(parent,"name"); /* already mallocs */
1461 for (i=0; i<systems_nstack; i++)
1462 if (strcmp( systems_stack[i].name, name)==0) {
1463 sys = &systems_stack[i];
1464 break;
1466 if (i==systems_nstack) {
1467 WARN("System '%s' was not found in the stack for some reason",name);
1468 return;
1470 free(name); /* no more need for it */
1472 node = parent->xmlChildrenNode;
1474 do { /* load all the data */
1475 if (xml_isNode(node,"jumps")) {
1476 cur = node->children;
1477 do {
1478 if (xml_isNode(cur,"jump")) {
1479 for (i=0; i<systems_nstack; i++)
1480 if (strcmp( systems_stack[i].name, xml_raw(cur))==0) {
1481 sys->njumps++;
1482 sys->jumps = realloc(sys->jumps, sys->njumps*sizeof(int));
1483 sys->jumps[sys->njumps-1] = i;
1484 break;
1486 if (i==systems_nstack)
1487 WARN("System '%s' not found for jump linking",xml_get(cur));
1489 } while (xml_nextNode(cur));
1491 } while (xml_nextNode(node));
1496 * @brief Loads the entire universe into ram - pretty big feat eh?
1498 * @return 0 on success.
1500 int space_load (void)
1502 int i;
1503 int ret;
1505 /* Loading. */
1506 systems_loading = 1;
1508 ret = planets_load();
1509 if (ret < 0)
1510 return ret;
1511 ret = systems_load();
1512 if (ret < 0)
1513 return ret;
1515 /* Done loading. */
1516 systems_loading = 0;
1518 /* Calculate system properties. */
1519 for (i=0; i<systems_nstack; i++)
1520 system_calcSecurity(&systems_stack[i]);
1522 return 0;
1527 * @brief Calculates the security in a star system.
1529 * @param sys System to calculate security in.
1530 * @return 0 on success.
1532 static int system_calcSecurity( StarSystem *sys )
1534 int i;
1535 double guard, hostile, c, mod;
1536 Fleet *f;
1538 /* Do not run while loading to speed up. */
1539 if (systems_loading)
1540 return 0;
1542 /* Defaults. */
1543 guard = 0.;
1544 hostile = 0.;
1546 /* Calculate hostiles/friendlies. */
1547 for (i=0; i<sys->nfleets; i++) {
1548 f = sys->fleets[i].fleet;
1549 c = (double)sys->fleets[i].chance / 100.;
1550 mod = c * f->pilot_avg * pow(f->mass_avg, 1./3.);
1551 if (fleet_isFlag(f, FLEET_FLAG_GUARD))
1552 guard += mod;
1553 else if (faction_getPlayerDef(f->faction) < 0)
1554 hostile += mod;
1557 /* Set security. */
1558 if (guard == 0.)
1559 sys->security = 0.;
1560 else if (hostile == 0.)
1561 sys->security = 1.;
1562 else
1563 sys->security = guard / (hostile + guard);
1565 return 0;
1570 * @brief Loads the entire systems, needs to be called after planets_load.
1572 * Does multiple passes to load:
1574 * - First loads the star systems.
1575 * - Next sets the jump routes.
1577 * @return 0 on success.
1579 static int systems_load (void)
1581 uint32_t bufsize;
1582 char *buf;
1583 xmlNodePtr node;
1584 xmlDocPtr doc;
1586 /* Load the file. */
1587 buf = ndata_read( SYSTEM_DATA, &bufsize );
1588 if (buf == NULL)
1589 return -1;
1591 doc = xmlParseMemory( buf, bufsize );
1592 if (doc == NULL) {
1593 WARN("'%s' is not a valid XML file.", SYSTEM_DATA);
1594 return -1;
1597 node = doc->xmlChildrenNode;
1598 if (!xml_isNode(node,XML_SYSTEM_ID)) {
1599 ERR("Malformed "SYSTEM_DATA" file: missing root element '"XML_SYSTEM_ID"'");
1600 return -1;
1603 node = node->xmlChildrenNode; /* first system node */
1604 if (node == NULL) {
1605 ERR("Malformed "SYSTEM_DATA" file: does not contain elements");
1606 return -1;
1609 /* Allocate if needed. */
1610 if (systems_stack == NULL) {
1611 systems_mstack = CHUNK_SIZE;
1612 systems_stack = malloc( sizeof(StarSystem) * systems_mstack );
1613 systems_nstack = 0;
1618 * First pass - loads all the star systems_stack.
1620 do {
1621 if (xml_isNode(node,XML_SYSTEM_TAG)) {
1622 /* Check if memory needs to grow. */
1623 systems_nstack++;
1624 if (systems_nstack > systems_mstack) {
1625 systems_mstack += CHUNK_SIZE;
1626 systems_stack = realloc(systems_stack, sizeof(StarSystem) * systems_mstack );
1629 system_parse(&systems_stack[systems_nstack-1],node);
1631 } while (xml_nextNode(node));
1635 * Second pass - loads all the jump routes.
1637 node = doc->xmlChildrenNode->xmlChildrenNode;
1638 do {
1639 if (xml_isNode(node,XML_SYSTEM_TAG))
1640 system_parseJumps(node); /* will automatically load the jumps into the system */
1642 } while (xml_nextNode(node));
1646 * cleanup
1648 xmlFreeDoc(doc);
1649 free(buf);
1651 DEBUG("Loaded %d Star System%s with %d Planet%s",
1652 systems_nstack, (systems_nstack==1) ? "" : "s",
1653 planet_nstack, (planet_nstack==1) ? "" : "s" );
1655 return 0;
1660 * @brief Renders the system.
1662 * @param dt Current delta tick.
1664 void space_render( const double dt )
1666 if (cur_system == NULL)
1667 return;
1669 if (cur_system->nebu_density > 0.)
1670 nebu_render(dt);
1671 else
1672 space_renderStars(dt);
1677 * @brief Renders the system overlay.
1679 * @param dt Current delta tick.
1681 void space_renderOverlay( const double dt )
1683 if (cur_system == NULL)
1684 return;
1686 if (cur_system->nebu_density > 0.)
1687 nebu_renderOverlay(dt);
1692 * @brief Renders the starry background.
1694 * @param dt Current delta tick.
1696 void space_renderStars( const double dt )
1698 unsigned int i;
1699 GLfloat hh, hw, h, w;
1700 GLfloat x, y, m, b;
1701 GLfloat brightness;
1702 double z;
1705 * gprof claims it's the slowest thing in the game!
1708 /* Do some scaling for now. */
1709 gl_cameraZoomGet( &z );
1710 z = 1. * (1. - conf.zoom_stars) + z * conf.zoom_stars;
1711 gl_matrixMode( GL_PROJECTION );
1712 gl_matrixPush();
1713 gl_matrixScale( z, z );
1715 if ((player != NULL) && !player_isFlag(PLAYER_DESTROYED) &&
1716 !player_isFlag(PLAYER_CREATING) &&
1717 pilot_isFlag(player,PILOT_HYPERSPACE) && /* hyperspace fancy effects */
1718 (player->ptimer < HYPERSPACE_STARS_BLUR)) {
1720 glShadeModel(GL_SMOOTH);
1722 /* lines will be based on velocity */
1723 m = HYPERSPACE_STARS_BLUR-player->ptimer;
1724 m /= HYPERSPACE_STARS_BLUR;
1725 m *= HYPERSPACE_STARS_LENGTH;
1726 x = m*cos(VANGLE(player->solid->vel)+M_PI);
1727 y = m*sin(VANGLE(player->solid->vel)+M_PI);
1729 /* Generate lines. */
1730 for (i=0; i < nstars; i++) {
1731 brightness = star_colour[8*i+3];
1732 star_vertex[4*i+2] = star_vertex[4*i+0] + x*brightness;
1733 star_vertex[4*i+3] = star_vertex[4*i+1] + y*brightness;
1736 /* Draw the lines. */
1737 gl_vboSubData( star_vertexVBO, 0, nstars * 4 * sizeof(GLfloat), star_vertex );
1738 gl_vboActivate( star_vertexVBO, GL_VERTEX_ARRAY, 2, GL_FLOAT, 0 );
1739 gl_vboActivate( star_colourVBO, GL_COLOR_ARRAY, 4, GL_FLOAT, 0 );
1740 glDrawArrays( GL_LINES, 0, nstars );
1742 glShadeModel(GL_FLAT);
1744 else { /* normal rendering */
1745 if (!paused && (player != NULL) && !player_isFlag(PLAYER_DESTROYED) &&
1746 !player_isFlag(PLAYER_CREATING)) { /* update position */
1748 /* Calculate some dimensions. */
1749 w = (SCREEN_W + 2.*STAR_BUF);
1750 w += conf.zoom_stars * (w / conf.zoom_far - 1.);
1751 h = (SCREEN_H + 2.*STAR_BUF);
1752 h += conf.zoom_stars * (h / conf.zoom_far - 1.);
1753 hw = w/2.;
1754 hh = h/2.;
1756 /* Calculate new star positions. */
1757 for (i=0; i < nstars; i++) {
1759 /* calculate new position */
1760 b = 9. - 10.*star_colour[8*i+3];
1761 star_vertex[4*i+0] = star_vertex[4*i+0] -
1762 (GLfloat)player->solid->vel.x / b*(GLfloat)dt;
1763 star_vertex[4*i+1] = star_vertex[4*i+1] -
1764 (GLfloat)player->solid->vel.y / b*(GLfloat)dt;
1766 /* check boundries */
1767 if (star_vertex[4*i+0] > hw)
1768 star_vertex[4*i+0] -= w;
1769 else if (star_vertex[4*i+0] < -hw)
1770 star_vertex[4*i+0] += w;
1771 if (star_vertex[4*i+1] > hh)
1772 star_vertex[4*i+1] -= h;
1773 else if (star_vertex[4*i+1] < -hh)
1774 star_vertex[4*i+1] += h;
1777 /* Upload the data. */
1778 gl_vboSubData( star_vertexVBO, 0, nstars * 4 * sizeof(GLfloat), star_vertex );
1781 /* Render. */
1782 gl_vboActivate( star_vertexVBO, GL_VERTEX_ARRAY, 2, GL_FLOAT, 2 * sizeof(GLfloat) );
1783 gl_vboActivate( star_colourVBO, GL_COLOR_ARRAY, 4, GL_FLOAT, 4 * sizeof(GLfloat) );
1784 glDrawArrays( GL_POINTS, 0, nstars );
1785 gl_checkErr();
1788 /* Disable vertex array. */
1789 gl_vboDeactivate();
1791 /* Pop matrix. */
1792 gl_matrixPop();
1797 * @brief Renders the current systemsplanets.
1799 void planets_render (void)
1801 if (cur_system==NULL) return;
1803 int i;
1804 for (i=0; i < cur_system->nplanets; i++)
1805 gl_blitSprite( cur_system->planets[i]->gfx_space,
1806 cur_system->planets[i]->pos.x, cur_system->planets[i]->pos.y,
1807 0, 0, NULL );
1812 * @brief Cleans up the system.
1814 void space_exit (void)
1816 int i;
1818 /* Free the names. */
1819 if (planetname_stack)
1820 free(planetname_stack);
1821 if (systemname_stack)
1822 free(systemname_stack);
1823 spacename_nstack = 0;
1825 /* Free the planets. */
1826 for (i=0; i < planet_nstack; i++) {
1827 free(planet_stack[i].name);
1829 if (planet_stack[i].description)
1830 free(planet_stack[i].description);
1831 if (planet_stack[i].bar_description)
1832 free(planet_stack[i].bar_description);
1834 /* graphics */
1835 if (planet_stack[i].gfx_space)
1836 gl_freeTexture(planet_stack[i].gfx_space);
1837 if (planet_stack[i].gfx_exterior)
1838 free(planet_stack[i].gfx_exterior);
1840 /* commodities */
1841 free(planet_stack[i].commodities);
1843 free(planet_stack);
1844 planet_stack = NULL;
1845 planet_nstack = 0;
1846 planet_mstack = 0;
1848 /* Free the systems. */
1849 for (i=0; i < systems_nstack; i++) {
1850 free(systems_stack[i].name);
1851 if (systems_stack[i].fleets)
1852 free(systems_stack[i].fleets);
1853 if (systems_stack[i].jumps)
1854 free(systems_stack[i].jumps);
1856 free(systems_stack[i].planets);
1858 free(systems_stack);
1859 systems_stack = NULL;
1860 systems_nstack = 0;
1861 systems_mstack = 0;
1863 /* stars must be free too */
1864 if (star_vertex) {
1865 free(star_vertex);
1866 star_vertex = NULL;
1868 if (star_colour) {
1869 free(star_colour);
1870 star_colour = NULL;
1872 nstars = 0;
1873 mstars = 0;
1878 * @brief Clears all system knowledge.
1880 void space_clearKnown (void)
1882 int i;
1883 for (i=0; i<systems_nstack; i++)
1884 sys_rmFlag(&systems_stack[i],SYSTEM_KNOWN);
1889 * @brief Clears all system markers.
1891 void space_clearMarkers (void)
1893 int i;
1894 for (i=0; i<systems_nstack; i++) {
1895 sys_rmFlag(&systems_stack[i], SYSTEM_MARKED);
1896 systems_stack[i].markers_misc = 0;
1897 systems_stack[i].markers_rush = 0;
1898 systems_stack[i].markers_cargo = 0;
1904 * @brief Clears all the system computer markers.
1906 void space_clearComputerMarkers (void)
1908 int i;
1909 for (i=0; i<systems_nstack; i++)
1910 sys_rmFlag(&systems_stack[i],SYSTEM_CMARKED);
1915 * @brief Adds a marker to a system.
1917 * @param sys Name of the system to add marker to.
1918 * @param type Type of the marker to add.
1919 * @return 0 on success.
1921 int space_addMarker( const char *sys, SysMarker type )
1923 StarSystem *ssys;
1924 int *markers;
1926 /* Get the system. */
1927 ssys = system_get(sys);
1928 if (ssys == NULL)
1929 return -1;
1931 /* Get the marker. */
1932 switch (type) {
1933 case SYSMARKER_MISC:
1934 markers = &ssys->markers_misc;
1935 break;
1936 case SYSMARKER_RUSH:
1937 markers = &ssys->markers_rush;
1938 break;
1939 case SYSMARKER_CARGO:
1940 markers = &ssys->markers_cargo;
1941 break;
1942 default:
1943 WARN("Unknown marker type.");
1944 return -1;
1947 /* Decrement markers. */
1948 (*markers)++;
1949 sys_setFlag(ssys, SYSTEM_MARKED);
1951 return 0;
1956 * @brief Removes a marker from a system.
1958 * @param sys Name of the system to remove marker from.
1959 * @param type Type of the marker to remove.
1960 * @return 0 on success.
1962 int space_rmMarker( const char *sys, SysMarker type )
1964 StarSystem *ssys;
1965 int *markers;
1967 /* Get the system. */
1968 ssys = system_get(sys);
1969 if (ssys == NULL)
1970 return -1;
1972 /* Get the marker. */
1973 switch (type) {
1974 case SYSMARKER_MISC:
1975 markers = &ssys->markers_misc;
1976 break;
1977 case SYSMARKER_RUSH:
1978 markers = &ssys->markers_rush;
1979 break;
1980 case SYSMARKER_CARGO:
1981 markers = &ssys->markers_cargo;
1982 break;
1983 default:
1984 WARN("Unknown marker type.");
1985 return -1;
1988 /* Decrement markers. */
1989 (*markers)--;
1990 if (*markers <= 0) {
1991 sys_rmFlag(ssys, SYSTEM_MARKED);
1992 (*markers) = 0;
1995 return 0;
2000 * @brief Saves what is needed to be saved for space.
2002 * @param writer XML writer to use.
2003 * @return 0 on success.
2005 int space_sysSave( xmlTextWriterPtr writer )
2007 int i;
2009 xmlw_startElem(writer,"space");
2011 for (i=0; i<systems_nstack; i++) {
2013 if (!sys_isKnown(&systems_stack[i])) continue; /* not known */
2015 xmlw_elem(writer,"known","%s",systems_stack[i].name);
2018 xmlw_endElem(writer); /* "space" */
2020 return 0;
2025 * @brief Loads player's space properties from an XML node.
2027 * @param parent Parent node for space.
2028 * @return 0 on success.
2030 int space_sysLoad( xmlNodePtr parent )
2032 xmlNodePtr node, cur;
2033 StarSystem *sys;
2035 space_clearKnown();
2037 node = parent->xmlChildrenNode;
2038 do {
2039 if (xml_isNode(node,"space")) {
2040 cur = node->xmlChildrenNode;
2042 do {
2043 if (xml_isNode(cur,"known")) {
2044 sys = system_get(xml_get(cur));
2045 if (sys != NULL) /* Must exist */
2046 sys_setFlag(sys,SYSTEM_KNOWN);
2048 } while (xml_nextNode(cur));
2050 } while (xml_nextNode(node));
2052 return 0;