1 /*
2  * See Licensing and Copyright notice in naev.h
3  */
4 
5 /**
6  * @file space.c
7  *
8  * @brief Handles all the space stuff, namely systems and planets.
9  */
10 
11 #include "space.h"
12 
13 #include "naev.h"
14 
15 #include <stdlib.h>
16 #include <math.h>
17 #include <float.h>
18 
19 #include "nxml.h"
20 
21 #include "opengl.h"
22 #include "log.h"
23 #include "rng.h"
24 #include "ndata.h"
25 #include "nfile.h"
26 #include "pilot.h"
27 #include "player.h"
28 #include "pause.h"
29 #include "weapon.h"
30 #include "toolkit.h"
31 #include "spfx.h"
32 #include "ntime.h"
33 #include "nebula.h"
34 #include "sound.h"
35 #include "music.h"
36 #include "gui.h"
37 #include "fleet.h"
38 #include "mission.h"
39 #include "conf.h"
40 #include "queue.h"
41 #include "nlua.h"
42 #include "nluadef.h"
43 #include "nlua_pilot.h"
44 #include "nlua_planet.h"
45 #include "npng.h"
46 #include "background.h"
47 #include "map_overlay.h"
48 #include "menu.h"
49 #include "nstring.h"
50 #include "nmath.h"
51 #include "map.h"
52 #include "damagetype.h"
53 #include "hook.h"
54 #include "dev_uniedit.h"
55 
56 
57 #define XML_PLANET_TAG        "asset" /**< Individual planet xml tag. */
58 #define XML_SYSTEM_TAG        "ssys" /**< Individual systems xml tag. */
59 
60 #define PLANET_GFX_EXTERIOR_PATH_W 400 /**< Planet exterior graphic width. */
61 #define PLANET_GFX_EXTERIOR_PATH_H 400 /**< Planet exterior graphic height. */
62 
63 #define CHUNK_SIZE            32 /**< Size to allocate by. */
64 #define CHUNK_SIZE_SMALL       8 /**< Smaller size to allocate chunks by. */
65 
66 /* used to overcome warnings due to 0 values */
67 #define FLAG_XSET             (1<<0) /**< Set the X position value. */
68 #define FLAG_YSET             (1<<1) /**< Set the Y position value. */
69 #define FLAG_INTERFERENCESET  (1<<3) /**< Set the interference value. */
70 #define FLAG_SERVICESSET      (1<<4) /**< Set the service value. */
71 #define FLAG_FACTIONSET       (1<<5) /**< Set the faction value. */
72 
73 #define DEBRIS_BUFFER         1000 /**< Buffer to smooth appearance of debris */
74 
75 /*
76  * planet <-> system name stack
77  */
78 static char** planetname_stack = NULL; /**< Planet name stack corresponding to system. */
79 static char** systemname_stack = NULL; /**< System name stack corresponding to planet. */
80 static int spacename_nstack = 0; /**< Size of planet<->system stack. */
81 static int spacename_mstack = 0; /**< Size of memory in planet<->system stack. */
82 
83 
84 /*
85  * Star system stack.
86  */
87 StarSystem *systems_stack = NULL; /**< Star system stack. */
88 int systems_nstack = 0; /**< Number of star systems. */
89 static int systems_mstack = 0; /**< Number of memory allocated for star system stack. */
90 
91 /*
92  * Planet stack.
93  */
94 static Planet *planet_stack = NULL; /**< Planet stack. */
95 static int planet_nstack = 0; /**< Planet stack size. */
96 static int planet_mstack = 0; /**< Memory size of planet stack. */
97 
98 /*
99  * Asteroid types stack.
100  */
101 static AsteroidType *asteroid_types = NULL; /**< Asteroid types stack. */
102 static int asteroid_ntypes = 0; /**< Asteroid types stack size. */
103 
104 /*
105  * Misc.
106  */
107 static int systems_loading = 1; /**< Systems are loading. */
108 StarSystem *cur_system = NULL; /**< Current star system. */
109 glTexture *jumppoint_gfx = NULL; /**< Jump point graphics. */
110 static glTexture *jumpbuoy_gfx = NULL; /**< Jump buoy graphics. */
111 static nlua_env landing_env = LUA_NOREF; /**< Landing lua env. */
112 static int space_fchg = 0; /**< Faction change counter, to avoid unnecessary calls. */
113 static int space_simulating = 0; /**< Are we simulating space? */
114 glTexture **asteroid_gfx = NULL;
115 uint32_t nasterogfx = 0; /**< Nb of asteroid gfx. */
116 
117 
118 /*
119  * fleet spawn rate
120  */
121 int space_spawn = 1; /**< Spawn enabled by default. */
122 extern int pilot_nstack;
123 extern Pilot** pilot_stack;
124 
125 
126 /*
127  * Interference.
128  */
129 extern double interference_alpha; /* gui.c */
130 static double interference_target = 0.; /**< Target alpha level. */
131 static double interference_timer  = 0.; /**< Interference timer. */
132 
133 
134 /*
135  * Internal Prototypes.
136  */
137 /* planet load */
138 static int planet_parse( Planet* planet, const xmlNodePtr parent );
139 static int space_parseAssets( xmlNodePtr parent, StarSystem* sys );
140 /* system load */
141 static void system_init( StarSystem *sys );
142 static void asteroid_init( Asteroid *ast, AsteroidAnchor *field );
143 static void debris_init( Debris *deb );
144 static int systems_load (void);
145 static int asteroidTypes_load (void);
146 static StarSystem* system_parse( StarSystem *system, const xmlNodePtr parent );
147 static int system_parseJumpPoint( const xmlNodePtr node, StarSystem *sys );
148 static int system_parseAsteroidField( const xmlNodePtr node, StarSystem *sys );
149 static int system_parseJumpPointDiff( const xmlNodePtr node, StarSystem *sys );
150 static void system_parseJumps( const xmlNodePtr parent );
151 static void system_parseAsteroids( const xmlNodePtr parent, StarSystem *sys );
152 /* misc */
153 static int getPresenceIndex( StarSystem *sys, int faction );
154 static void presenceCleanup( StarSystem *sys );
155 static void system_scheduler( double dt, int init );
156 /* Render. */
157 static void space_renderJumpPoint( JumpPoint *jp, int i );
158 static void space_renderPlanet( Planet *p );
159 static void space_renderAsteroid( Asteroid *a );
160 static void space_renderDebris( Debris *d, double x, double y );
161 /*
162  * Externed prototypes.
163  */
164 int space_sysSave( xmlTextWriterPtr writer );
165 int space_sysLoad( xmlNodePtr parent );
166 /*
167  * External prototypes.
168  */
169 extern credits_t economy_getPrice( const Commodity *com,
170       const StarSystem *sys, const Planet *p ); /**< from economy.c */
171 
172 
planet_getServiceName(int service)173 char* planet_getServiceName( int service )
174 {
175    switch (service) {
176       case PLANET_SERVICE_LAND:        return "Land";
177       case PLANET_SERVICE_INHABITED:   return "Inhabited";
178       case PLANET_SERVICE_REFUEL:      return "Refuel";
179       case PLANET_SERVICE_BAR:         return "Bar";
180       case PLANET_SERVICE_MISSIONS:    return "Missions";
181       case PLANET_SERVICE_COMMODITY:   return "Commodity";
182       case PLANET_SERVICE_OUTFITS:     return "Outfits";
183       case PLANET_SERVICE_SHIPYARD:    return "Shipyard";
184       case PLANET_SERVICE_BLACKMARKET: return "Blackmarket";
185    }
186    return NULL;
187 }
188 
planet_getService(char * name)189 int planet_getService( char *name )
190 {
191    if (strcmp(name,"Land")==0)
192       return PLANET_SERVICE_LAND;
193    else if (strcmp(name,"Inhabited")==0)
194       return PLANET_SERVICE_INHABITED;
195    else if (strcmp(name,"Refuel")==0)
196       return PLANET_SERVICE_REFUEL;
197    else if (strcmp(name,"Bar")==0)
198       return PLANET_SERVICE_BAR;
199    else if (strcmp(name,"Missions")==0)
200       return PLANET_SERVICE_MISSIONS;
201    else if (strcmp(name,"Commodity")==0)
202       return PLANET_SERVICE_COMMODITY;
203    else if (strcmp(name,"Outfits")==0)
204       return PLANET_SERVICE_OUTFITS;
205    else if (strcmp(name,"Shipyard")==0)
206       return PLANET_SERVICE_SHIPYARD;
207    else if (strcmp(name,"Blackmarket")==0)
208       return PLANET_SERVICE_BLACKMARKET;
209    return -1;
210 }
211 
212 
213 /**
214  * @brief Gets the price of a commodity at a planet.
215  *
216  *    @param p Planet to get price at.
217  *    @param c Commodity to get price of.
218  */
planet_commodityPrice(const Planet * p,const Commodity * c)219 credits_t planet_commodityPrice( const Planet *p, const Commodity *c )
220 {
221    char *sysname;
222    StarSystem *sys;
223 
224    sysname = planet_getSystem( p->name );
225    sys = system_get( sysname );
226 
227    return economy_getPrice( c, sys, p );
228 }
229 
230 
231 /**
232  * @brief Changes the planets faction.
233  *
234  *    @param p Planet to change faction of.
235  *    @param faction Faction to change to.
236  *    @return 0 on success.
237  */
planet_setFaction(Planet * p,int faction)238 int planet_setFaction( Planet *p, int faction )
239 {
240    p->faction = faction;
241    return 0;
242 }
243 
244 
245 /**
246  * @brief Checks to make sure if pilot is far enough away to hyperspace.
247  *
248  *    @param p Pilot to check if he can hyperspace.
249  *    @return 1 if he can hyperspace, 0 else.
250  */
space_canHyperspace(Pilot * p)251 int space_canHyperspace( Pilot* p )
252 {
253    double d, r;
254    JumpPoint *jp;
255 
256    /* Must not have the nojump flag. */
257    if (pilot_isFlag(p, PILOT_NOJUMP))
258       return 0;
259 
260    /* Must have fuel. */
261    if (p->fuel < p->fuel_consumption)
262       return 0;
263 
264    /* Must have hyperspace target. */
265    if (p->nav_hyperspace < 0)
266       return 0;
267 
268    /* Get the jump. */
269    jp = &cur_system->jumps[ p->nav_hyperspace ];
270 
271    /* Check distance. */
272    r = jp->radius;
273    d = vect_dist2( &p->solid->pos, &jp->pos );
274    if (d > r*r)
275       return 0;
276    return 1;
277 }
278 
279 
280 /**
281  * @brief Tries to get the pilot into hyperspace.
282  *
283  *    @param p Pilot to try to start hyperspacing.
284  *    @return 0 on success.
285  */
space_hyperspace(Pilot * p)286 int space_hyperspace( Pilot* p )
287 {
288    if (pilot_isFlag(p, PILOT_NOJUMP))
289       return -2;
290    if (p->fuel < p->fuel_consumption)
291       return -3;
292    if (!space_canHyperspace(p))
293       return -1;
294 
295    /* pilot is now going to get automatically ready for hyperspace */
296    pilot_setFlag(p, PILOT_HYP_PREP);
297    return 0;
298 }
299 
300 
301 /**
302  * @brief Calculates the jump in pos for a pilot.
303  *
304  *    @param in Star system entering.
305  *    @param out Star system exiting.
306  *    @param[out] pos Position calculated.
307  *    @param[out] vel Velocity calculated.
308  */
space_calcJumpInPos(StarSystem * in,StarSystem * out,Vector2d * pos,Vector2d * vel,double * dir)309 int space_calcJumpInPos( StarSystem *in, StarSystem *out, Vector2d *pos, Vector2d *vel, double *dir )
310 {
311    int i;
312    JumpPoint *jp;
313    double a, d, x, y;
314    double ea, ed;
315 
316    /* Find the entry system. */
317    jp = NULL;
318    for (i=0; i<in->njumps; i++)
319       if (in->jumps[i].target == out)
320          jp = &in->jumps[i];
321 
322    /* Must have found the jump. */
323    if (jp == NULL) {
324       WARN("Unable to find jump in point for '%s' in '%s': not connected", out->name, in->name);
325       return -1;
326    }
327 
328    /* Base position target. */
329    x = jp->pos.x;
330    y = jp->pos.y;
331 
332    /* Calculate offset from target position. */
333    a = 2*M_PI - jp->angle;
334    d = RNGF()*(HYPERSPACE_ENTER_MAX-HYPERSPACE_ENTER_MIN) + HYPERSPACE_ENTER_MIN;
335 
336    /* Calculate new position. */
337    x += d*cos(a);
338    y += d*sin(a);
339 
340    /* Add some error. */
341    ea = 2*M_PI*RNGF();
342    ed = jp->radius/2.;
343    x += ed*cos(ea);
344    y += ed*sin(ea);
345 
346    /* Set new position. */
347    vect_cset( pos, x, y );
348 
349    /* Set new velocity. */
350    a += M_PI;
351    vect_cset( vel, HYPERSPACE_VEL*cos(a), HYPERSPACE_VEL*sin(a) );
352 
353    /* Set direction. */
354    *dir = a;
355 
356    return 0;
357 }
358 
359 /**
360  * @brief Gets the name of all the planets that belong to factions.
361  *
362  *    @param[out] nplanets Number of planets found.
363  *    @param factions Factions to check against.
364  *    @param nfactions Number of factions in factions.
365  *    @return An array of faction names.  Individual names are not allocated.
366  */
space_getFactionPlanet(int * nplanets,int * factions,int nfactions,int landable)367 char** space_getFactionPlanet( int *nplanets, int *factions, int nfactions, int landable )
368 {
369    int i,j,k, f;
370    Planet* planet;
371    char **tmp;
372    int ntmp;
373    int mtmp;
374 
375    ntmp = 0;
376    mtmp = CHUNK_SIZE;
377    tmp = malloc(sizeof(char*) * mtmp);
378 
379    for (i=0; i<systems_nstack; i++) {
380       for (j=0; j<systems_stack[i].nplanets; j++) {
381          planet = systems_stack[i].planets[j];
382 
383          /* Important to ignore virtual assets. */
384          if (planet->real != ASSET_REAL)
385             continue;
386 
387          /* Check if it's in factions. */
388          f = 0;
389          for (k=0; k<nfactions; k++) {
390             if (planet->faction == factions[k]) {
391                f = 1;
392                break;
393             }
394          }
395          if (!f)
396             continue;
397 
398          /* Check landable. */
399          if (landable) {
400             planet_updateLand( planet );
401             if (!planet->can_land)
402                continue;
403          }
404 
405          /* This is expensive so we probably want to do it last. */
406          if (!space_sysReallyReachable( systems_stack[i].name ))
407             continue;
408 
409          ntmp++;
410          if (ntmp > mtmp) { /* need more space */
411             mtmp *= 2;
412             tmp = realloc(tmp, sizeof(char*) * mtmp);
413          }
414          tmp[ntmp-1] = planet->name;
415          break; /* no need to check all factions */
416       }
417    }
418 
419    (*nplanets) = ntmp;
420    return tmp;
421 }
422 
423 
424 /**
425  * @brief Gets the name of a random planet.
426  *
427  *    @param landable Whether the planet must let the player land normally.
428  *    @param services Services the planet must have.
429  *    @param filter Filter function for including planets.
430  *    @return The name of a random planet.
431  */
space_getRndPlanet(int landable,unsigned int services,int (* filter)(Planet * p))432 char* space_getRndPlanet( int landable, unsigned int services,
433       int (*filter)(Planet *p))
434 {
435    int i,j;
436    Planet **tmp;
437    char *res;
438    int ntmp, mtmp;
439    Planet *pnt;
440 
441    ntmp  = 0;
442    res   = NULL;
443    mtmp  = CHUNK_SIZE;
444    tmp   = malloc( sizeof(Planet*) * mtmp );
445 
446    for (i=0; i<systems_nstack; i++) {
447       for (j=0; j<systems_stack[i].nplanets; j++) {
448          pnt = systems_stack[i].planets[j];
449 
450          if (pnt->real != ASSET_REAL)
451             continue;
452 
453          if (services && planet_hasService(pnt, services) != services)
454             continue;
455 
456          if (filter != NULL && !filter(pnt))
457             continue;
458 
459          ntmp++;
460          if (ntmp > mtmp) { /* need more space */
461             mtmp *= 2;
462             tmp = realloc(tmp, sizeof(Planet*) * mtmp);
463          }
464          tmp[ntmp-1] = pnt;
465       }
466    }
467 
468    /* Second filter. */
469    tmp = (Planet**)arrayShuffle( (void**)tmp, ntmp);
470    for (i=0; i < ntmp; i++) {
471       pnt = tmp[i];
472 
473       /* We put expensive calculations here to minimize executions. */
474       if (landable) {
475          planet_updateLand( pnt );
476          if (!pnt->can_land)
477             continue;
478       }
479       if (!space_sysReallyReachable( planet_getSystem(pnt->name) ))
480          continue;
481 
482       /* We want the name, not the actual planet. */
483       res = tmp[i]->name;
484       break;
485    }
486    free(tmp);
487 
488    return res;
489 }
490 
491 
492 /**
493  * @brief Gets the closest feature to a position in the system.
494  *
495  *    @param sys System to get closest feature from a position.
496  *    @param[out] pnt ID of closest planet or -1 if a jump point is closer (or none is close).
497  *    @param[out] jp ID of closest jump point or -1 if a planet is closer (or none is close).
498  *    @param x X position to get closest from.
499  *    @param y Y position to get closest from.
500  */
system_getClosest(const StarSystem * sys,int * pnt,int * jp,double x,double y)501 double system_getClosest( const StarSystem *sys, int *pnt, int *jp, double x, double y )
502 {
503    int i;
504    double d, td;
505    Planet *p;
506    JumpPoint *j;
507 
508    /* Default output. */
509    *pnt = -1;
510    *jp  = -1;
511    d    = INFINITY;
512 
513    /* Planets. */
514    for (i=0; i<sys->nplanets; i++) {
515       p  = sys->planets[i];
516       if (p->real != ASSET_REAL)
517          continue;
518       td = pow2(x-p->pos.x) + pow2(y-p->pos.y);
519       if (td < d) {
520          *pnt  = i;
521          d     = td;
522       }
523    }
524 
525    /* Jump points. */
526    for (i=0; i<sys->njumps; i++) {
527       j  = &sys->jumps[i];
528       td = pow2(x-j->pos.x) + pow2(y-j->pos.y);
529       if (td < d) {
530          *pnt  = -1; /* We must clear planet target as jump point is closer. */
531          *jp   = i;
532          d     = td;
533       }
534    }
535    return d;
536 }
537 
538 
539 /**
540  * @brief Gets the closest feature to a position in the system.
541  *
542  *    @param sys System to get closest feature from a position.
543  *    @param[out] pnt ID of closest planet or -1 if a jump point is closer (or none is close).
544  *    @param[out] jp ID of closest jump point or -1 if a planet is closer (or none is close).
545  *    @param x X position to get closest from.
546  *    @param y Y position to get closest from.
547  */
system_getClosestAng(const StarSystem * sys,int * pnt,int * jp,double x,double y,double ang)548 double system_getClosestAng( const StarSystem *sys, int *pnt, int *jp, double x, double y, double ang )
549 {
550    int i;
551    double a, ta;
552    Planet *p;
553    JumpPoint *j;
554 
555    /* Default output. */
556    *pnt = -1;
557    *jp  = -1;
558    a    = ang + M_PI;
559 
560    /* Planets. */
561    for (i=0; i<sys->nplanets; i++) {
562       p  = sys->planets[i];
563       if (p->real != ASSET_REAL)
564          continue;
565       ta = atan2( y - p->pos.y, x - p->pos.x);
566       if ( ABS(angle_diff(ang, ta)) < ABS(angle_diff(ang, a))) {
567          *pnt  = i;
568          a     = ta;
569       }
570    }
571 
572    /* Jump points. */
573    for (i=0; i<sys->njumps; i++) {
574       j  = &sys->jumps[i];
575       ta = atan2( y - j->pos.y, x - j->pos.x);
576       if ( ABS(angle_diff(ang, ta)) < ABS(angle_diff(ang, a))) {
577          *pnt  = -1; /* We must clear planet target as jump point is closer. */
578          *jp   = i;
579          a     = ta;
580       }
581    }
582    return a;
583 }
584 
585 
586 /**
587  * @brief Sees if a system is reachable.
588  *
589  *    @return 1 if target system is reachable, 0 if it isn't.
590  */
space_sysReachable(StarSystem * sys)591 int space_sysReachable( StarSystem *sys )
592 {
593    int i;
594    JumpPoint *jp;
595 
596    if (sys_isKnown(sys))
597       return 1; /* it is known */
598 
599    /* check to see if it is adjacent to known */
600    for (i=0; i<sys->njumps; i++) {
601       jp = jump_getTarget( sys, sys->jumps[i].target );
602       if (jp && jp_isKnown( jp ))
603          return 1;
604    }
605 
606    return 0;
607 }
608 
609 
610 /**
611  * @brief Sees if a system can be reached via jumping.
612  *
613  *    @return 1 if target system is reachable, 0 if it isn't.
614  */
space_sysReallyReachable(char * sysname)615 int space_sysReallyReachable( char* sysname )
616 {
617    int njumps;
618    StarSystem** path;
619 
620    if (strcmp(sysname,cur_system->name)==0)
621       return 1;
622    path = map_getJumpPath( &njumps, cur_system->name, sysname, 1, 1, NULL );
623    if (path != NULL) {
624       free(path);
625       return 1;
626    }
627    return 0;
628 }
629 
630 /**
631  * @brief Sees if a system is reachable from another system.
632  *
633  *    @return 1 if target system is reachable, 0 if it isn't.
634  */
space_sysReachableFromSys(StarSystem * target,StarSystem * sys)635 int space_sysReachableFromSys( StarSystem *target, StarSystem *sys )
636 {
637    JumpPoint *jp;
638 
639    /* check to see if sys contains a known jump point to target */
640    jp = jump_getTarget( target, sys );
641    if ( jp == NULL )
642       return 0;
643    else if ( jp_isKnown( jp ))
644       return 1;
645    return 0;
646 }
647 
648 /**
649  * @brief Gets all the star systems.
650  *
651  *    @param[out] Number of star systems gotten.
652  *    @return The star systems gotten.
653  */
system_getAll(int * nsys)654 StarSystem* system_getAll( int *nsys )
655 {
656    *nsys = systems_nstack;
657    return systems_stack;
658 }
659 
660 
661 /**
662  * @brief Checks to see if a system exists.
663  *
664  *    @param sysname Name of the system to match.
665  *    @return 1 if the system exists.
666  */
system_exists(const char * sysname)667 int system_exists( const char* sysname )
668 {
669    int i;
670    for (i=0; i<systems_nstack; i++)
671       if (strcmp(sysname, systems_stack[i].name)==0)
672          return 1;
673    return 0;
674 }
675 
676 
677 /**
678  * @brief Checks to see if a system exists case insensitively.
679  *
680  *    @param sysname Name of the system to match (case insensitive).
681  *    @return The actual name of the system of NULL if not found.
682  */
system_existsCase(const char * sysname)683 const char *system_existsCase( const char* sysname )
684 {
685    int i;
686    for (i=0; i<systems_nstack; i++)
687       if (strcasecmp(sysname, systems_stack[i].name)==0)
688          return systems_stack[i].name;
689    return NULL;
690 }
691 
692 
693 /**
694  * @brief Does a fuzzy case matching.
695  */
system_searchFuzzyCase(const char * sysname,int * n)696 char **system_searchFuzzyCase( const char* sysname, int *n )
697 {
698    int i, len;
699    char **names;
700 
701    /* Overallocate to maximum. */
702    names = malloc( sizeof(char*) * systems_nstack );
703 
704    /* Do fuzzy search. */
705    len = 0;
706    for (i=0; i<systems_nstack; i++) {
707       if (nstrcasestr( systems_stack[i].name, sysname ) != NULL) {
708          names[len] = systems_stack[i].name;
709          len++;
710       }
711    }
712 
713    /* Free if empty. */
714    if (len == 0) {
715       free(names);
716       names = NULL;
717    }
718 
719    *n = len;
720    return names;
721 }
722 
723 
724 
725 
726 /**
727  * @brief Get the system from its name.
728  *
729  *    @param sysname Name to match.
730  *    @return System matching sysname.
731  */
system_get(const char * sysname)732 StarSystem* system_get( const char* sysname )
733 {
734    int i;
735 
736    for (i=0; i<systems_nstack; i++)
737       if (strcmp(sysname, systems_stack[i].name)==0)
738          return &systems_stack[i];
739 
740    WARN("System '%s' not found in stack", sysname);
741    return NULL;
742 }
743 
744 
745 /**
746  * @brief Get the system by its index.
747  *
748  *    @param id Index to match.
749  *    @return System matching index.
750  */
system_getIndex(int id)751 StarSystem* system_getIndex( int id )
752 {
753    return &systems_stack[ id ];
754 }
755 
756 
757 /**
758  * @brief Gets the index of a star system.
759  *
760  *    @param sys System to get index of.
761  *    @return The index of the system.
762  */
system_index(StarSystem * sys)763 int system_index( StarSystem *sys )
764 {
765    return sys->id;
766 }
767 
768 
769 /**
770  * @brief Get the name of a system from a planetname.
771  *
772  *    @param planetname Planet name to match.
773  *    @return Name of the system planet belongs to.
774  */
planet_getSystem(const char * planetname)775 char* planet_getSystem( const char* planetname )
776 {
777    int i;
778 
779    for (i=0; i<spacename_nstack; i++)
780       if (strcmp(planetname_stack[i],planetname)==0)
781          return systemname_stack[i];
782 
783    DEBUG("Planet '%s' not found in planetname stack", planetname);
784    return NULL;
785 }
786 
787 
788 /**
789  * @brief Gets a planet based on its name.
790  *
791  *    @param planetname Name to match.
792  *    @return Planet matching planetname.
793  */
planet_get(const char * planetname)794 Planet* planet_get( const char* planetname )
795 {
796    int i;
797 
798    if (planetname==NULL) {
799       WARN("Trying to find NULL planet...");
800       return NULL;
801    }
802 
803    for (i=0; i<planet_nstack; i++)
804       if (strcmp(planet_stack[i].name,planetname)==0)
805          return &planet_stack[i];
806 
807    WARN("Planet '%s' not found in the universe", planetname);
808    return NULL;
809 }
810 
811 
812 /**
813  * @brief Gets planet by index.
814  *
815  *    @param ind Index of the planet to get.
816  *    @return The planet gotten.
817  */
planet_getIndex(int ind)818 Planet* planet_getIndex( int ind )
819 {
820    /* Sanity check. */
821    if ((ind < 0) || (ind >= planet_nstack)) {
822       WARN("Planet index '%d' out of range (max %d)", ind, planet_nstack);
823       return NULL;
824    }
825 
826    return &planet_stack[ ind ];
827 }
828 
829 
830 /**
831  * @brief Gets the ID of a planet.
832  *
833  *    @param p Planet to get ID of.
834  *    @return The ID of the planet.
835  */
planet_index(const Planet * p)836 int planet_index( const Planet *p )
837 {
838    return p->id;
839 }
840 
841 
842 /**
843  * @brief Gets all the planets.
844  *
845  *    @param n Number of planets gotten.
846  *    @return Array of gotten planets.
847  */
planet_getAll(int * n)848 Planet* planet_getAll( int *n )
849 {
850    *n = planet_nstack;
851    return planet_stack;
852 }
853 
854 
855 /**
856  * @brief Sets a planet's known status, if it's real.
857  */
planet_setKnown(Planet * p)858 void planet_setKnown( Planet *p )
859 {
860    if (p->real == ASSET_REAL)
861       planet_setFlag(p, PLANET_KNOWN);
862 }
863 
864 
865 /**
866  * @brief Check to see if a planet exists.
867  *
868  *    @param planetname Name of the planet to see if it exists.
869  *    @return 1 if planet exists.
870  */
planet_exists(const char * planetname)871 int planet_exists( const char* planetname )
872 {
873    int i;
874    for (i=0; i<planet_nstack; i++)
875       if (strcmp(planet_stack[i].name,planetname)==0)
876          return 1;
877    return 0;
878 }
879 
880 
881 /**
882  * @brief Check to see if a planet exists (case insensitive).
883  *
884  *    @param planetname Name of the planet to see if it exists.
885  *    @return The actual name of the planet or NULL if not found.
886  */
planet_existsCase(const char * planetname)887 const char* planet_existsCase( const char* planetname )
888 {
889    int i;
890    for (i=0; i<planet_nstack; i++)
891       if (strcasecmp(planet_stack[i].name,planetname)==0)
892          return planet_stack[i].name;
893    return NULL;
894 }
895 
896 
897 /**
898  * @brief Does a fuzzy case matching.
899  */
planet_searchFuzzyCase(const char * planetname,int * n)900 char **planet_searchFuzzyCase( const char* planetname, int *n )
901 {
902    int i, len;
903    char **names;
904 
905    /* Overallocate to maximum. */
906    names = malloc( sizeof(char*) * planet_nstack );
907 
908    /* Do fuzzy search. */
909    len = 0;
910    for (i=0; i<planet_nstack; i++) {
911       if (nstrcasestr( planet_stack[i].name, planetname ) != NULL) {
912          names[len] = planet_stack[i].name;
913          len++;
914       }
915    }
916 
917    /* Free if empty. */
918    if (len == 0) {
919       free(names);
920       names = NULL;
921    }
922 
923    *n = len;
924    return names;
925 }
926 
927 /**
928  * @brief Gets a jump point based on its target and system.
929  *
930  *    @param jumpname Name to match.
931  *    @param sys System jump is in.
932  *    @return Jump point matich jumpname in sys or NULL if not found.
933  */
jump_get(const char * jumpname,const StarSystem * sys)934 JumpPoint* jump_get( const char* jumpname, const StarSystem* sys )
935 {
936    int i;
937    JumpPoint *jp;
938 
939    if (jumpname==NULL) {
940       WARN("Trying to find NULL jump point...");
941       return NULL;
942    }
943 
944    for (i=0; i<sys->njumps; i++) {
945       jp = &sys->jumps[i];
946       if (strcmp(jp->target->name,jumpname)==0)
947          return jp;
948    }
949 
950    WARN("Jump point '%s' not found in %s", jumpname, sys->name);
951    return NULL;
952 }
953 
954 
955 /**
956  * @brief Less safe version of jump_get that works with pointers.
957  *
958  *    @param target Target system jump leads to.
959  *    @param sys System to look in.
960  *    @return Jump point in sys to target or NULL if not found.
961  */
jump_getTarget(StarSystem * target,const StarSystem * sys)962 JumpPoint* jump_getTarget( StarSystem* target, const StarSystem* sys )
963 {
964    int i;
965    JumpPoint *jp;
966    for (i=0; i<sys->njumps; i++) {
967       jp = &sys->jumps[i];
968       if (jp->target == target)
969          return jp;
970    }
971    WARN("Jump point to '%s' not found in %s", target->name, sys->name);
972    return NULL;
973 }
974 
975 
976 /**
977  * @brief Controls fleet spawning.
978  *
979  *    @param dt Current delta tick.
980  *    @param init Should be 1 to initialize the scheduler.
981  */
system_scheduler(double dt,int init)982 static void system_scheduler( double dt, int init )
983 {
984    int i, n;
985    nlua_env env;
986    SystemPresence *p;
987    Pilot *pilot;
988 
989    /* Go through all the factions and reduce the timer. */
990    for (i=0; i < cur_system->npresence; i++) {
991       p = &cur_system->presence[i];
992       env = faction_getScheduler( p->faction );
993 
994       /* Must have a valid scheduler. */
995       if (env==LUA_NOREF)
996          continue;
997 
998       /* Spawning is disabled for this faction. */
999       if (p->disabled)
1000          continue;
1001 
1002       /* Run the appropriate function. */
1003       if (init) {
1004          nlua_getenv( env, "create" ); /* f */
1005          if (lua_isnil(naevL,-1)) {
1006             WARN("Lua Spawn script for faction '%s' missing obligatory entry point 'create'.",
1007                   faction_name( p->faction ) );
1008             lua_pop(naevL,1);
1009             continue;
1010          }
1011          n = 0;
1012       }
1013       else {
1014          /* Decrement dt, only continue  */
1015          p->timer -= dt;
1016          if (p->timer >= 0.)
1017             continue;
1018 
1019          nlua_getenv( env, "spawn" ); /* f */
1020          if (lua_isnil(naevL,-1)) {
1021             WARN("Lua Spawn script for faction '%s' missing obligatory entry point 'spawn'.",
1022                   faction_name( p->faction ) );
1023             lua_pop(naevL,1);
1024             continue;
1025          }
1026          lua_pushnumber( naevL, p->curUsed ); /* f, presence */
1027          n = 1;
1028       }
1029       lua_pushnumber( naevL, p->value ); /* f, [arg,], max */
1030 
1031       /* Actually run the function. */
1032       if (nlua_pcall(env, n+1, 2)) { /* error has occurred */
1033          WARN("Lua Spawn script for faction '%s' : %s",
1034                faction_name( p->faction ), lua_tostring(naevL,-1));
1035          lua_pop(naevL,1);
1036          continue;
1037       }
1038 
1039       /* Output is handled the same way. */
1040       if (!lua_isnumber(naevL,-2)) {
1041          WARN("Lua spawn script for faction '%s' failed to return timer value.",
1042                faction_name( p->faction ) );
1043          lua_pop(naevL,2);
1044          continue;
1045       }
1046       p->timer    += lua_tonumber(naevL,-2);
1047       /* Handle table if it exists. */
1048       if (lua_istable(naevL,-1)) {
1049          lua_pushnil(naevL); /* tk, k */
1050          while (lua_next(naevL,-2) != 0) { /* tk, k, v */
1051             /* Must be table. */
1052             if (!lua_istable(naevL,-1)) {
1053                WARN("Lua spawn script for faction '%s' returns invalid data (not a table).",
1054                      faction_name( p->faction ) );
1055                lua_pop(naevL,2); /* tk, k */
1056                continue;
1057             }
1058 
1059             lua_getfield( naevL, -1, "pilot" ); /* tk, k, v, p */
1060             if (!lua_ispilot(naevL,-1)) {
1061                WARN("Lua spawn script for faction '%s' returns invalid data (not a pilot).",
1062                      faction_name( p->faction ) );
1063                lua_pop(naevL,2); /* tk, k */
1064                continue;
1065             }
1066             pilot = pilot_get( lua_topilot(naevL,-1) );
1067             if (pilot == NULL) {
1068                lua_pop(naevL,2); /* tk, k */
1069                continue;
1070             }
1071             lua_pop(naevL,1); /* tk, k, v */
1072             lua_getfield( naevL, -1, "presence" ); /* tk, k, v, p */
1073             if (!lua_isnumber(naevL,-1)) {
1074                WARN("Lua spawn script for faction '%s' returns invalid data (not a number).",
1075                      faction_name( p->faction ) );
1076                lua_pop(naevL,2); /* tk, k */
1077                continue;
1078             }
1079             pilot->presence = lua_tonumber(naevL,-1);
1080             p->curUsed     += pilot->presence;
1081             lua_pop(naevL,2); /* tk, k */
1082          }
1083       }
1084       lua_pop(naevL,2);
1085    }
1086 }
1087 
1088 
1089 /**
1090  * @brief Mark when a faction changes.
1091  */
space_factionChange(void)1092 void space_factionChange (void)
1093 {
1094    space_fchg = 1;
1095 }
1096 
1097 
1098 /**
1099  * @brief Controls fleet spawning.
1100  *
1101  *    @param dt Current delta tick.
1102  */
space_update(const double dt)1103 void space_update( const double dt )
1104 {
1105    int i, j;
1106    double x, y;
1107    Pilot *p;
1108    Damage dmg;
1109    HookParam hparam[3];
1110    AsteroidAnchor *ast;
1111    Asteroid *a;
1112    Debris *d;
1113    Pilot *pplayer;
1114    Solid *psolid;
1115 
1116    /* Needs a current system. */
1117    if (cur_system == NULL)
1118       return;
1119 
1120    /* If spawning is enabled, call the scheduler. */
1121    if (space_spawn)
1122       system_scheduler( dt, 0 );
1123 
1124    /*
1125     * Volatile systems.
1126     */
1127    if (cur_system->nebu_volatility > 0.) {
1128       dmg.type          = dtype_get("nebula");
1129       dmg.damage        = pow2(cur_system->nebu_volatility) / 500. * dt;
1130       dmg.penetration   = 1.; /* Full penetration. */
1131       dmg.disable       = 0.;
1132 
1133       /* Damage pilots in volatile systems. */
1134       for (i=0; i<pilot_nstack; i++) {
1135          p = pilot_stack[i];
1136          pilot_hit( p, NULL, 0, &dmg, 0 );
1137       }
1138    }
1139 
1140 
1141    /*
1142     * Interference.
1143     */
1144    if (cur_system->interference > 0.) {
1145       /* Always dark. */
1146       if (cur_system->interference >= 1000.)
1147          interference_alpha = 1.;
1148 
1149       /* Normal scenario. */
1150       else {
1151          interference_timer -= dt;
1152          if (interference_timer < 0.) {
1153             /* 0    ->  [   1,   5   ]
1154              * 250  ->  [ 0.75, 3.75 ]
1155              * 500  ->  [  0.5, 2.5  ]
1156              * 750  ->  [ 0.25, 1.25 ]
1157              * 1000 ->  [   0,   0   ] */
1158             interference_timer += (1000. - cur_system->interference) / 1000. *
1159                   (3. + RNG_2SIGMA() );
1160 
1161             /* 0    ->  [  0,   0  ]
1162              * 250  ->  [-0.5, 1.5 ]
1163              * 500  ->  [ -1,   3  ]
1164              * 1000 ->  [  0,   6  ] */
1165             interference_target = cur_system->interference/1000. * 2. *
1166                   (1. + RNG_2SIGMA() );
1167          }
1168 
1169          /* Head towards target. */
1170          if (fabs(interference_alpha - interference_target) > 1e-05) {
1171             /* Asymptotic. */
1172             interference_alpha += (interference_target - interference_alpha) * dt;
1173 
1174             /* Limit alpha to [0.-1.]. */
1175             if (interference_alpha > 1.)
1176                interference_alpha = 1.;
1177             else if (interference_alpha < 0.)
1178                interference_alpha = 0.;
1179          }
1180       }
1181    }
1182 
1183    /* Faction updates. */
1184    if (space_fchg) {
1185       for (i=0; i<cur_system->nplanets; i++)
1186          planet_updateLand( cur_system->planets[i] );
1187 
1188       /* Verify land authorization is still valid. */
1189       if (player_isFlag(PLAYER_LANDACK))
1190          player_checkLandAck();
1191 
1192       gui_updateFaction();
1193       space_fchg = 0;
1194    }
1195 
1196    if (!space_simulating) {
1197       /* Planet updates */
1198       for (i=0; i<cur_system->nplanets; i++)
1199          if (( !planet_isKnown( cur_system->planets[i] )) && ( pilot_inRangePlanet( player.p, i ))) {
1200             planet_setKnown( cur_system->planets[i] );
1201             player_message( "You discovered \e%c%s\e\0.",
1202                   planet_getColourChar( cur_system->planets[i] ),
1203                   cur_system->planets[i]->name );
1204             hparam[0].type  = HOOK_PARAM_STRING;
1205             hparam[0].u.str = "asset";
1206             hparam[1].type  = HOOK_PARAM_ASSET;
1207             hparam[1].u.la  = cur_system->planets[i]->id;
1208             hparam[2].type  = HOOK_PARAM_SENTINEL;
1209             hooks_runParam( "discover", hparam );
1210          }
1211 
1212       /* Jump point updates */
1213       for (i=0; i<cur_system->njumps; i++)
1214          if (( !jp_isKnown( &cur_system->jumps[i] )) && ( pilot_inRangeJump( player.p, i ))) {
1215             jp_setFlag( &cur_system->jumps[i], JP_KNOWN );
1216             player_message( "You discovered a Jump Point." );
1217             hparam[0].type  = HOOK_PARAM_STRING;
1218             hparam[0].u.str = "jump";
1219             hparam[1].type  = HOOK_PARAM_JUMP;
1220             hparam[1].u.lj.srcid = cur_system->id;
1221             hparam[1].u.lj.destid = cur_system->jumps[i].target->id;
1222             hparam[2].type  = HOOK_PARAM_SENTINEL;
1223             hooks_runParam( "discover", hparam );
1224          }
1225    }
1226 
1227    /* Asteroids/Debris update */
1228    for (i=0; i<cur_system->nasteroids; i++) {
1229       ast = &cur_system->asteroids[i];
1230 
1231       for (j=0; j<ast->nb; j++) {
1232          a = &ast->asteroids[j];
1233 
1234          a->pos.x += a->vel.x * dt;
1235          a->pos.y += a->vel.y * dt;
1236 
1237          /* Grow and shrink */
1238          if (a->appearing == 1) {
1239             a->timer += dt;
1240             if (a->timer >= 2.) {
1241                a->timer = 0.;
1242                a->appearing = 0;
1243             }
1244          }
1245 
1246          if (a->appearing == 2) {
1247             a->timer += dt;
1248             if (a->timer >= 2.) {
1249                /* reinit any disappeared asteroid */
1250                asteroid_init( a, ast );
1251             }
1252          }
1253 
1254          /* Manage the asteroid getting outside the field */
1255          if ( space_isInField(&a->pos)<0 && a->appearing==0 ) {
1256             /* Make it shrink */
1257             a->timer = 0.;
1258             a->appearing = 2;
1259          }
1260       }
1261 
1262       x = 0;
1263       y = 0;
1264       pplayer = pilot_get( PLAYER_ID );
1265       if (pplayer != NULL) {
1266          psolid  = pplayer->solid;
1267          x = psolid->vel.x;
1268          y = psolid->vel.y;
1269       }
1270 
1271       for (j=0; j<ast->ndebris; j++) {
1272          d = &ast->debris[j];
1273 
1274          d->pos.x += (d->vel.x-x) * dt;
1275          d->pos.y += (d->vel.y-y) * dt;
1276 
1277          /* Check boundaries */
1278          if (d->pos.x > SCREEN_W + DEBRIS_BUFFER)
1279             d->pos.x -= SCREEN_W + 2*DEBRIS_BUFFER;
1280          else if (d->pos.y > SCREEN_H + DEBRIS_BUFFER)
1281             d->pos.y -= SCREEN_H + 2*DEBRIS_BUFFER;
1282          else if (d->pos.x < -DEBRIS_BUFFER)
1283             d->pos.x += SCREEN_W + 2*DEBRIS_BUFFER;
1284          else if (d->pos.y < -DEBRIS_BUFFER)
1285             d->pos.y += SCREEN_H + 2*DEBRIS_BUFFER;
1286       }
1287 
1288    }
1289 }
1290 
1291 
1292 /**
1293  * @brief Initializes the system.
1294  *
1295  *    @param sysname Name of the system to initialize.
1296  */
space_init(const char * sysname)1297 void space_init( const char* sysname )
1298 {
1299    char* nt;
1300    int i, j, n, s;
1301    Planet *pnt;
1302    AsteroidAnchor *ast;
1303    Asteroid *a;
1304    Debris *d;
1305 
1306    /* cleanup some stuff */
1307    player_clear(); /* clears targets */
1308    ovr_mrkClear(); /* Clear markers when jumping. */
1309    pilots_clean(); /* destroy all the current pilots, except player */
1310    weapon_clear(); /* get rid of all the weapons */
1311    spfx_clear(); /* get rid of the explosions */
1312    background_clear(); /* Get rid of the background. */
1313    space_spawn = 1; /* spawn is enabled by default. */
1314    interference_timer = 0.; /* Restart timer. */
1315    if (player.p != NULL) {
1316       pilot_lockClear( player.p );
1317       pilot_clearTimers( player.p ); /* Clear timers. */
1318       player_clearEscorts(); /* Must clear escorts to keep deployment sane. */
1319    }
1320 
1321    if ((sysname==NULL) && (cur_system==NULL))
1322       ERR("Cannot reinit system if there is no system previously loaded");
1323    else if (sysname!=NULL) {
1324       for (i=0; i < systems_nstack; i++)
1325          if (strcmp(sysname, systems_stack[i].name)==0)
1326             break;
1327 
1328       if (i>=systems_nstack)
1329          ERR("System %s not found in stack", sysname);
1330       cur_system = &systems_stack[i];
1331 
1332       nt = ntime_pretty(0, 2);
1333       player_message("\epEntering System %s on %s.", sysname, nt);
1334       free(nt);
1335 
1336       /* Handle background */
1337       if (cur_system->nebu_density > 0.) {
1338          /* Background is Nebula */
1339          nebu_prep( cur_system->nebu_density, cur_system->nebu_volatility );
1340 
1341          /* Set up sound. */
1342          sound_env( SOUND_ENV_NEBULA, cur_system->nebu_density );
1343       }
1344       else {
1345          /* Background is starry */
1346          background_initStars( cur_system->stars );
1347 
1348          /* Set up sound. */
1349          sound_env( SOUND_ENV_NORMAL, 0. );
1350       }
1351    }
1352 
1353    /* Set up planets. */
1354    for (i=0; i<cur_system->nplanets; i++) {
1355       pnt = cur_system->planets[i];
1356       pnt->bribed = 0;
1357       pnt->land_override = 0;
1358       planet_updateLand( pnt );
1359    }
1360 
1361    /* Set up asteroids. */
1362    for (i=0; i<cur_system->nasteroids; i++) {
1363       ast = &cur_system->asteroids[i];
1364 
1365       /* Add the asteroids to the anchor */
1366       ast->asteroids = malloc( (ast->nb) * sizeof(Asteroid) );
1367       for (j=0; j<ast->nb; j++) {
1368          a = &ast->asteroids[j];
1369          asteroid_init(a, ast);
1370       }
1371       /* Add the debris to the anchor */
1372       ast->debris = malloc( (ast->ndebris) * sizeof(Debris) );
1373       for (j=0; j<ast->ndebris; j++) {
1374          d = &ast->debris[j];
1375          debris_init(d);
1376       }
1377    }
1378 
1379    /* Clear interference if you leave system with interference. */
1380    if (cur_system->interference == 0.)
1381       interference_alpha = 0.;
1382 
1383    /* See if we should get a new music song. */
1384    if (player.p != NULL)
1385       music_choose(NULL);
1386 
1387    /* Reset player enemies. */
1388    player.enemies = 0;
1389 
1390    /* Update the pilot sensor range. */
1391    pilot_updateSensorRange();
1392 
1393    /* Reset any schedules and used presence. */
1394    for (i=0; i<cur_system->npresence; i++) {
1395       cur_system->presence[i].curUsed  = 0;
1396       cur_system->presence[i].timer    = 0.;
1397       cur_system->presence[i].disabled = 0;
1398    }
1399 
1400    /* Load graphics. */
1401    space_gfxLoad( cur_system );
1402 
1403    /* Call the scheduler. */
1404    system_scheduler( 0., 1 );
1405 
1406    /* we now know this system */
1407    sys_setFlag(cur_system,SYSTEM_KNOWN);
1408 
1409    /* Simulate system. */
1410    space_simulating = 1;
1411    if (player.p != NULL)
1412       pilot_setFlag( player.p, PILOT_INVISIBLE );
1413    player_messageToggle( 0 );
1414    s = sound_disabled;
1415    sound_disabled = 1;
1416    ntime_allowUpdate( 0 );
1417    n = SYSTEM_SIMULATE_TIME / fps_min;
1418    for (i=0; i<n; i++)
1419       update_routine( fps_min, 1 );
1420    ntime_allowUpdate( 1 );
1421    sound_disabled = s;
1422    player_messageToggle( 1 );
1423    if (player.p != NULL)
1424       pilot_rmFlag( player.p, PILOT_INVISIBLE );
1425    space_simulating = 0;
1426 
1427    /* Refresh overlay if necessary (player kept it open). */
1428    ovr_refresh();
1429 
1430    /* Update gui. */
1431    gui_setSystem();
1432 
1433    /* Start background. */
1434    background_load( cur_system->background );
1435 }
1436 
1437 
1438 /**
1439  * @brief Initializes an asteroid.
1440  *    @param ast Asteroid to initialize.
1441  *    @param field Asteroid field the asteroid belongs to.
1442  */
asteroid_init(Asteroid * ast,AsteroidAnchor * field)1443 void asteroid_init( Asteroid *ast, AsteroidAnchor *field )
1444 {
1445    int i, j, k;
1446    double mod, theta, x, y;
1447    AsteroidSubset *sub;
1448    AsteroidType *at;
1449 
1450    /* Get a random position:
1451        * choose a convex subset
1452        * choose a triangle
1453        * get a random position in a reference 1X1 rectangle
1454        * transform it into a triangle (by folding)
1455        * apply a linear transformation to the reference triangle */
1456    k = RNG( 0, field->nsubsets-1 );
1457    sub = &field->subsets[k];
1458 
1459    i = RNG( 0, sub->ncorners-1 );
1460    if (i<sub->ncorners-1)
1461       j = i+1;
1462    else
1463       j = 0;
1464    x = RNGF();
1465    y = RNGF();
1466    if (x+y > 1) {
1467       x = 1-x;
1468       y = 1-y;
1469    }
1470    ast->pos.x = sub->pos.x*(1-x-y)
1471                 + sub->corners[i].x*x + sub->corners[j].x*y;
1472    ast->pos.y = sub->pos.y*(1-x-y)
1473                 + sub->corners[i].y*x + sub->corners[j].y*y;
1474 
1475    /* And a random velocity */
1476    theta = RNGF()*2.*M_PI;
1477    mod = RNGF() * 20;
1478    vect_pset( &ast->vel, mod, theta );
1479 
1480    /* randomly init the gfx ID */
1481    at = &asteroid_types[0];
1482    ast->type = 0;
1483    ast->gfxID = RNG(0,at->ngfx-1);
1484 
1485    /* Grow effect stuff */
1486    ast->appearing = 1;
1487    ast->timer = 0.;
1488 }
1489 
1490 
1491 /**
1492  * @brief Initializes a debris.
1493  *    @param deb Debris to initialize.
1494  *    @param field Asteroid field the asteroid belongs to.
1495  */
debris_init(Debris * deb)1496 void debris_init( Debris *deb )
1497 {
1498    double theta, mod;
1499 
1500    /* Position */
1501    deb->pos.x = (double)RNG(-DEBRIS_BUFFER, SCREEN_W + DEBRIS_BUFFER);
1502    deb->pos.y = (double)RNG(-DEBRIS_BUFFER, SCREEN_H + DEBRIS_BUFFER);
1503 
1504    /* And a random velocity */
1505    theta = RNGF()*2.*M_PI;
1506    mod = RNGF() * 20;
1507    vect_pset( &deb->vel, mod, theta );
1508 
1509    /* randomly init the gfx ID */
1510    deb->gfxID = RNG(0,(int)nasterogfx-1);
1511 }
1512 
1513 
1514 /**
1515  * @brief Creates a new planet.
1516  */
planet_new(void)1517 Planet *planet_new (void)
1518 {
1519    Planet *p;
1520    int realloced;
1521 
1522    /* See if stack must grow. */
1523    planet_nstack++;
1524    realloced = 0;
1525    if (planet_nstack > planet_mstack) {
1526       planet_mstack *= 2;
1527       planet_stack   = realloc( planet_stack, sizeof(Planet) * planet_mstack );
1528       realloced      = 1;
1529    }
1530 
1531    /* Clean up memory. */
1532    p           = &planet_stack[ planet_nstack-1 ];
1533    memset( p, 0, sizeof(Planet) );
1534    p->id       = planet_nstack-1;
1535    p->faction  = -1;
1536 
1537    /* Reconstruct the jumps. */
1538    if (!systems_loading && realloced)
1539       systems_reconstructPlanets();
1540 
1541    return p;
1542 }
1543 
1544 
1545 /**
1546  * @brief Loads all the planets in the game.
1547  *
1548  *    @return 0 on success.
1549  */
planets_load(void)1550 static int planets_load ( void )
1551 {
1552    uint32_t bufsize;
1553    char *buf, **planet_files, *file;
1554    xmlNodePtr node;
1555    xmlDocPtr doc;
1556    Planet *p;
1557    uint32_t nfiles;
1558    int i, len;
1559 
1560    /* Load landing stuff. */
1561    landing_env = nlua_newEnv(0);
1562    nlua_loadStandard(landing_env);
1563    buf         = ndata_read( LANDING_DATA_PATH, &bufsize );
1564    if (nlua_dobufenv(landing_env, buf, bufsize, LANDING_DATA_PATH) != 0) {
1565       WARN( "Failed to load landing file: %s\n"
1566             "%s\n"
1567             "Most likely Lua file has improper syntax, please check",
1568             LANDING_DATA_PATH, lua_tostring(naevL,-1));
1569    }
1570    free(buf);
1571 
1572    /* Initialize stack if needed. */
1573    if (planet_stack == NULL) {
1574       planet_mstack = CHUNK_SIZE;
1575       planet_stack = malloc( sizeof(Planet) * planet_mstack );
1576       planet_nstack = 0;
1577    }
1578 
1579    /* Load XML stuff. */
1580    planet_files = ndata_list( PLANET_DATA_PATH, &nfiles );
1581    for (i=0; i<(int)nfiles; i++) {
1582       len  = (strlen(PLANET_DATA_PATH)+strlen(planet_files[i])+2);
1583       file = malloc( len );
1584       nsnprintf( file, len,"%s%s",PLANET_DATA_PATH,planet_files[i]);
1585       buf  = ndata_read( file, &bufsize );
1586       doc  = xmlParseMemory( buf, bufsize );
1587       if (doc == NULL) {
1588          WARN("%s file is invalid xml!",file);
1589          free(file);
1590          free(buf);
1591          continue;
1592       }
1593 
1594       node = doc->xmlChildrenNode; /* first planet node */
1595       if (node == NULL) {
1596          WARN("Malformed %s file: does not contain elements",file);
1597          free(file);
1598          xmlFreeDoc(doc);
1599          free(buf);
1600          continue;
1601       }
1602 
1603       if (xml_isNode(node,XML_PLANET_TAG)) {
1604          p = planet_new();
1605          planet_parse( p, node );
1606       }
1607 
1608       /* Clean up. */
1609       free(file);
1610       xmlFreeDoc(doc);
1611       free(buf);
1612    }
1613 
1614    /* Clean up. */
1615    for (i=0; i<(int)nfiles; i++)
1616       free( planet_files[i] );
1617    free( planet_files );
1618 
1619    return 0;
1620 }
1621 
1622 
1623 /**
1624  * @brief Gets the planet colour char.
1625  */
planet_getColourChar(Planet * p)1626 char planet_getColourChar( Planet *p )
1627 {
1628    if (!planet_hasService( p, PLANET_SERVICE_INHABITED ))
1629       return 'I';
1630 
1631    if (p->can_land || p->bribed) {
1632       if (areAllies(FACTION_PLAYER,p->faction))
1633          return 'F';
1634       return 'N';
1635    }
1636 
1637    if (areEnemies(FACTION_PLAYER,p->faction))
1638       return 'H';
1639    return 'R';
1640 }
1641 
1642 
1643 /**
1644  * @brief Gets the planet colour.
1645  */
planet_getColour(Planet * p)1646 const glColour* planet_getColour( Planet *p )
1647 {
1648    if (!planet_hasService( p, PLANET_SERVICE_INHABITED ))
1649       return &cInert;
1650 
1651    if (p->can_land || p->bribed) {
1652       if (areAllies(FACTION_PLAYER,p->faction))
1653          return &cFriend;
1654       return &cNeutral;
1655    }
1656 
1657    if (areEnemies(FACTION_PLAYER,p->faction))
1658       return &cHostile;
1659    return &cRestricted;
1660 }
1661 
1662 
1663 /**
1664  * @brief Updates the land possibilities of a planet.
1665  *
1666  *    @param p Planet to update land possibilities of.
1667  */
planet_updateLand(Planet * p)1668 void planet_updateLand( Planet *p )
1669 {
1670    char *str;
1671 
1672    /* Must be inhabited. */
1673    if (!planet_hasService( p, PLANET_SERVICE_INHABITED ) ||
1674          (player.p == NULL))
1675       return;
1676 
1677    /* Clean up old stuff. */
1678    free( p->land_msg );
1679    free( p->bribe_msg );
1680    free( p->bribe_ack_msg );
1681    p->can_land    = 0;
1682    p->land_msg    = NULL;
1683    p->bribe_msg   = NULL;
1684    p->bribe_ack_msg = NULL;
1685    p->bribe_price = 0;
1686 
1687    /* Set up function. */
1688    if (p->land_func == NULL)
1689       str = "land";
1690    else
1691       str = p->land_func;
1692    nlua_getenv( landing_env, str );
1693    lua_pushplanet( naevL, p->id );
1694    if (nlua_pcall(landing_env, 1, 5)) { /* error has occurred */
1695       WARN("Landing: '%s' : %s", str, lua_tostring(naevL,-1));
1696       lua_pop(naevL,1);
1697       return;
1698    }
1699 
1700    /* Parse parameters. */
1701    p->can_land = lua_toboolean(naevL,-5);
1702    if (lua_isstring(naevL,-4))
1703       p->land_msg = strdup( lua_tostring(naevL,-4) );
1704    else {
1705       WARN( LANDING_DATA_PATH": %s (%s) -> return parameter 2 is not a string!", str, p->name );
1706       p->land_msg = strdup( "Invalid land message" );
1707    }
1708    /* Parse bribing. */
1709    if (!p->can_land && lua_isnumber(naevL,-3)) {
1710       p->bribe_price = lua_tonumber(naevL,-3);
1711       /* We need the bribe message. */
1712       if (lua_isstring(naevL,-2))
1713          p->bribe_msg = strdup( lua_tostring(naevL,-2) );
1714       else {
1715          WARN( LANDING_DATA_PATH": %s (%s) -> return parameter 4 is not a string!", str, p->name );
1716          p->bribe_msg = strdup( "Invalid bribe message" );
1717       }
1718       /* We also need the bribe ACK message. */
1719       if (lua_isstring(naevL,-1))
1720          p->bribe_ack_msg = strdup( lua_tostring(naevL,-1) );
1721       else {
1722          WARN( LANDING_DATA_PATH": %s -> return parameter 5 is not a string!", str, p->name );
1723          p->bribe_ack_msg = strdup( "Invalid bribe ack message" );
1724       }
1725    }
1726    else if (lua_isstring(naevL,-3))
1727       p->bribe_msg = strdup( lua_tostring(naevL,-3) );
1728    else if (!lua_isnil(naevL,-3))
1729       WARN( LANDING_DATA_PATH": %s (%s) -> return parameter 3 is not a number or string or nil!", str, p->name );
1730 
1731    lua_pop(naevL,5);
1732 
1733    /* Unset bribe status if bribing is no longer possible. */
1734    if (p->bribed && p->bribe_ack_msg == NULL)
1735       p->bribed = 0;
1736 }
1737 
1738 
1739 /**
1740  * @brief Loads all the graphics for a star system.
1741  *
1742  *    @param sys System to load graphics for.
1743  */
space_gfxLoad(StarSystem * sys)1744 void space_gfxLoad( StarSystem *sys )
1745 {
1746    int i;
1747    Planet *planet;
1748    for (i=0; i<sys->nplanets; i++) {
1749       planet = sys->planets[i];
1750 
1751       if (planet->real != ASSET_REAL)
1752          continue;
1753 
1754       if (planet->gfx_space == NULL)
1755          planet->gfx_space = gl_newImage( planet->gfx_spaceName, OPENGL_TEX_MIPMAPS );
1756    }
1757 }
1758 
1759 
1760 /**
1761  * @brief Unloads all the graphics for a star system.
1762  *
1763  *    @param sys System to unload graphics for.
1764  */
space_gfxUnload(StarSystem * sys)1765 void space_gfxUnload( StarSystem *sys )
1766 {
1767    int i;
1768    Planet *planet;
1769    for (i=0; i<sys->nplanets; i++) {
1770       planet = sys->planets[i];
1771       if (planet->gfx_space != NULL) {
1772          gl_freeTexture( planet->gfx_space );
1773          planet->gfx_space = NULL;
1774       }
1775    }
1776 }
1777 
1778 
1779 /**
1780  * @brief Parses a planet from an xml node.
1781  *
1782  *    @param planet Planet to fill up.
1783  *    @param parent Node that contains planet data.
1784  *    @return 0 on success.
1785  */
planet_parse(Planet * planet,const xmlNodePtr parent)1786 static int planet_parse( Planet *planet, const xmlNodePtr parent )
1787 {
1788    int mem;
1789    char str[PATH_MAX], *tmp;
1790    xmlNodePtr node, cur, ccur;
1791    unsigned int flags;
1792 
1793    /* Clear up memory for sane defaults. */
1794    flags          = 0;
1795    planet->real   = ASSET_REAL;
1796    planet->hide   = 0.01;
1797 
1798    /* Get the name. */
1799    xmlr_attr( parent, "name", planet->name );
1800 
1801    node = parent->xmlChildrenNode;
1802    do {
1803 
1804       /* Only handle nodes. */
1805       xml_onlyNodes(node);
1806 
1807       if (xml_isNode(node,"virtual")) {
1808          planet->real   = ASSET_VIRTUAL;
1809          continue;
1810       }
1811       else if (xml_isNode(node,"GFX")) {
1812          cur = node->children;
1813          do {
1814             if (xml_isNode(cur,"space")) { /* load space gfx */
1815                nsnprintf( str, PATH_MAX, PLANET_GFX_SPACE_PATH"%s", xml_get(cur));
1816                planet->gfx_spaceName = strdup(str);
1817                planet->gfx_spacePath = xml_getStrd(cur);
1818                planet_setRadiusFromGFX(planet);
1819             }
1820             else if (xml_isNode(cur,"exterior")) { /* load land gfx */
1821                nsnprintf( str, PATH_MAX, PLANET_GFX_EXTERIOR_PATH"%s", xml_get(cur));
1822                planet->gfx_exterior = strdup(str);
1823                planet->gfx_exteriorPath = xml_getStrd(cur);
1824             }
1825          } while (xml_nextNode(cur));
1826          continue;
1827       }
1828       else if (xml_isNode(node,"pos")) {
1829          cur          = node->children;
1830          do {
1831             if (xml_isNode(cur,"x")) {
1832                flags |= FLAG_XSET;
1833                planet->pos.x = xml_getFloat(cur);
1834             }
1835             else if (xml_isNode(cur,"y")) {
1836                flags |= FLAG_YSET;
1837                planet->pos.y = xml_getFloat(cur);
1838             }
1839          } while (xml_nextNode(cur));
1840          continue;
1841       }
1842       else if (xml_isNode(node, "presence")) {
1843          cur = node->children;
1844          do {
1845             xmlr_float(cur, "value", planet->presenceAmount);
1846             xmlr_int(cur, "range", planet->presenceRange);
1847             if (xml_isNode(cur,"faction")) {
1848                flags |= FLAG_FACTIONSET;
1849                planet->faction = faction_get( xml_get(cur) );
1850                continue;
1851             }
1852          } while (xml_nextNode(cur));
1853          continue;
1854       }
1855       else if (xml_isNode(node,"general")) {
1856          cur = node->children;
1857          do {
1858             /* Direct reads. */
1859             xmlr_strd(cur, "class", planet->class);
1860             xmlr_strd(cur, "bar", planet->bar_description);
1861             xmlr_strd(cur, "description", planet->description );
1862             xmlr_ulong(cur, "population", planet->population );
1863             xmlr_float(cur, "hide", planet->hide );
1864 
1865             if (xml_isNode(cur, "services")) {
1866                flags |= FLAG_SERVICESSET;
1867                ccur = cur->children;
1868                planet->services = 0;
1869                do {
1870                   xml_onlyNodes(ccur);
1871 
1872                   if (xml_isNode(ccur, "land")) {
1873                      planet->services |= PLANET_SERVICE_LAND;
1874                      tmp = xml_get(ccur);
1875                      if (tmp != NULL) {
1876                         planet->land_func = strdup(tmp);
1877 #ifdef DEBUGGING
1878                         if (landing_env != LUA_NOREF) {
1879                            nlua_getenv( landing_env, tmp );
1880                            if (lua_isnil(naevL,-1))
1881                               WARN("Planet '%s' has landing function '%s' which is not found in '%s'.",
1882                                     planet->name, tmp, LANDING_DATA_PATH);
1883                            lua_pop(naevL,1);
1884                         }
1885 #endif /* DEBUGGING */
1886                      }
1887                   }
1888                   else if (xml_isNode(ccur, "refuel"))
1889                      planet->services |= PLANET_SERVICE_REFUEL | PLANET_SERVICE_INHABITED;
1890                   else if (xml_isNode(ccur, "bar"))
1891                      planet->services |= PLANET_SERVICE_BAR | PLANET_SERVICE_INHABITED;
1892                   else if (xml_isNode(ccur, "missions"))
1893                      planet->services |= PLANET_SERVICE_MISSIONS | PLANET_SERVICE_INHABITED;
1894                   else if (xml_isNode(ccur, "commodity"))
1895                      planet->services |= PLANET_SERVICE_COMMODITY | PLANET_SERVICE_INHABITED;
1896                   else if (xml_isNode(ccur, "outfits"))
1897                      planet->services |= PLANET_SERVICE_OUTFITS | PLANET_SERVICE_INHABITED;
1898                   else if (xml_isNode(ccur, "shipyard"))
1899                      planet->services |= PLANET_SERVICE_SHIPYARD | PLANET_SERVICE_INHABITED;
1900                   else if (xml_isNode(ccur, "blackmarket"))
1901                      planet->services |= PLANET_SERVICE_BLACKMARKET;
1902                   else
1903                      WARN("Planet '%s' has unknown services tag '%s'", planet->name, ccur->name);
1904 
1905                } while (xml_nextNode(ccur));
1906             }
1907 
1908             else if (xml_isNode(cur, "commodities")) {
1909                ccur = cur->children;
1910                mem = 0;
1911                do {
1912                   if (xml_isNode(ccur,"commodity")) {
1913                      planet->ncommodities++;
1914                      /* Memory must grow. */
1915                      if (planet->ncommodities > mem) {
1916                         if (mem == 0)
1917                            mem = CHUNK_SIZE_SMALL;
1918                         else
1919                            mem *= 2;
1920                         planet->commodities = realloc(planet->commodities,
1921                               mem * sizeof(Commodity*));
1922                      }
1923                      planet->commodities[planet->ncommodities-1] =
1924                         commodity_get( xml_get(ccur) );
1925                   }
1926                } while (xml_nextNode(ccur));
1927                /* Shrink to minimum size. */
1928                planet->commodities = realloc(planet->commodities,
1929                      planet->ncommodities * sizeof(Commodity*));
1930             }
1931 
1932             else if (xml_isNode(cur, "blackmarket")) {
1933                planet_addService(planet, PLANET_SERVICE_BLACKMARKET);
1934             }
1935          } while (xml_nextNode(cur));
1936          continue;
1937       }
1938       else if (xml_isNode(node, "tech")) {
1939          planet->tech = tech_groupCreateXML( node );
1940          continue;
1941       }
1942 
1943       DEBUG("Unknown node '%s' in planet '%s'",node->name,planet->name);
1944    } while (xml_nextNode(node));
1945 
1946 /*
1947  * verification
1948  */
1949 #define MELEMENT(o,s)   if (o) WARN("Planet '%s' missing '"s"' element", planet->name)
1950    /* Issue warnings on missing items only it the asset is real. */
1951    if (planet->real == ASSET_REAL) {
1952       MELEMENT(planet->gfx_spaceName==NULL,"GFX space");
1953       MELEMENT( planet_hasService(planet,PLANET_SERVICE_LAND) &&
1954             planet->gfx_exterior==NULL,"GFX exterior");
1955       /* MELEMENT( planet_hasService(planet,PLANET_SERVICE_INHABITED) &&
1956             (planet->population==0), "population"); */
1957       MELEMENT((flags&FLAG_XSET)==0,"x");
1958       MELEMENT((flags&FLAG_YSET)==0,"y");
1959       MELEMENT(planet->class==NULL,"class");
1960       MELEMENT( planet_hasService(planet,PLANET_SERVICE_LAND) &&
1961             planet->description==NULL,"description");
1962       MELEMENT( planet_hasService(planet,PLANET_SERVICE_BAR) &&
1963             planet->bar_description==NULL,"bar");
1964       MELEMENT( planet_hasService(planet,PLANET_SERVICE_INHABITED) &&
1965             (flags&FLAG_FACTIONSET)==0,"faction");
1966       MELEMENT((flags&FLAG_SERVICESSET)==0,"services");
1967       MELEMENT( (planet_hasService(planet,PLANET_SERVICE_OUTFITS) ||
1968                planet_hasService(planet,PLANET_SERVICE_SHIPYARD)) &&
1969             (planet->tech==NULL), "tech" );
1970       /*MELEMENT( planet_hasService(planet,PLANET_SERVICE_COMMODITY) &&
1971             (planet->ncommodities==0),"commodity" );*/
1972       MELEMENT( (flags&FLAG_FACTIONSET) && (planet->presenceAmount == 0.),
1973             "presence" );
1974    }
1975 #undef MELEMENT
1976 
1977    /* Square to allow for linear multiplication with squared distances. */
1978    planet->hide = pow2(planet->hide);
1979 
1980    return 0;
1981 }
1982 
1983 
1984 /**
1985  * @brief Sets a planet's radius based on which space gfx it uses.
1986  *
1987  *    @param planet Planet to set radius for.
1988  *    @return 0 on success.
1989  */
planet_setRadiusFromGFX(Planet * planet)1990 int planet_setRadiusFromGFX(Planet* planet)
1991 {
1992    SDL_RWops *rw;
1993    npng_t *npng;
1994    png_uint_32 w, h;
1995    int nbuf;
1996    char *buf, path[PATH_MAX], str[PATH_MAX];
1997 
1998    /* New path. */
1999    nsnprintf( path, sizeof(path), "%s%s", PLANET_GFX_SPACE_PATH, planet->gfx_spacePath );
2000 
2001    rw = ndata_rwops( path );
2002    if (rw == NULL) {
2003       WARN("Planet '%s' has inexisting graphic '%s'!", planet->name, planet->gfx_spacePath );
2004       return -1;
2005    }
2006    else {
2007       npng = npng_open( rw );
2008       if (npng != NULL) {
2009          npng_dim( npng, &w, &h );
2010          nbuf = npng_metadata( npng, "radius", &buf );
2011          if (nbuf > 0) {
2012             strncpy( str, buf, MIN( (unsigned int)nbuf, sizeof(str) ) );
2013             str[ nbuf ] = '\0';
2014             planet->radius = atof( str );
2015          }
2016          else
2017             planet->radius = 0.8 * (double)(w+h)/4.; /* (w+h)/2 is diameter, /2 for radius */
2018 
2019          npng_close( npng );
2020       }
2021       SDL_RWclose( rw );
2022    }
2023    return 0;
2024 }
2025 
2026 
2027 /**
2028  * @brief Adds a planet to a star system.
2029  *
2030  *    @param sys Star System to add planet to.
2031  *    @param planetname Name of the planet to add.
2032  *    @return 0 on success.
2033  */
system_addPlanet(StarSystem * sys,const char * planetname)2034 int system_addPlanet( StarSystem *sys, const char *planetname )
2035 {
2036    Planet *planet;
2037 
2038    if (sys == NULL)
2039       return -1;
2040 
2041    /* Check if need to grow the star system planet stack. */
2042    sys->nplanets++;
2043    if (sys->planets == NULL) {
2044       sys->planets   = malloc( sizeof(Planet*) * CHUNK_SIZE_SMALL );
2045       sys->planetsid = malloc( sizeof(int) * CHUNK_SIZE_SMALL );
2046    }
2047    else if (sys->nplanets > CHUNK_SIZE_SMALL) {
2048       sys->planets   = realloc( sys->planets, sizeof(Planet*) * sys->nplanets );
2049       sys->planetsid = realloc( sys->planetsid, sizeof(int) * sys->nplanets );
2050    }
2051    planet = planet_get(planetname);
2052    if (planet == NULL) {
2053       sys->nplanets--; /* Try to keep sanity if possible. */
2054       return -1;
2055    }
2056    sys->planets[sys->nplanets-1]    = planet;
2057    sys->planetsid[sys->nplanets-1]  = planet->id;
2058 
2059    /* add planet <-> star system to name stack */
2060    spacename_nstack++;
2061    if (spacename_nstack > spacename_mstack) {
2062       if (spacename_mstack == 0)
2063          spacename_mstack = CHUNK_SIZE;
2064       else
2065          spacename_mstack *= 2;
2066       planetname_stack = realloc(planetname_stack,
2067             sizeof(char*) * spacename_mstack);
2068       systemname_stack = realloc(systemname_stack,
2069             sizeof(char*) * spacename_mstack);
2070    }
2071    planetname_stack[spacename_nstack-1] = planet->name;
2072    systemname_stack[spacename_nstack-1] = sys->name;
2073 
2074    economy_addQueuedUpdate();
2075 
2076    /* Add the presence. */
2077    if (!systems_loading) {
2078       system_addPresence( sys, planet->faction, planet->presenceAmount, planet->presenceRange );
2079       system_setFaction(sys);
2080    }
2081 
2082    /* Reload graphics if necessary. */
2083    if (cur_system != NULL)
2084       space_gfxLoad( cur_system );
2085 
2086    return 0;
2087 }
2088 
2089 
2090 /**
2091  * @brief Removes a planet from a star system.
2092  *
2093  *    @param sys Star System to remove planet from.
2094  *    @param planetname Name of the planet to remove.
2095  *    @return 0 on success.
2096  */
system_rmPlanet(StarSystem * sys,const char * planetname)2097 int system_rmPlanet( StarSystem *sys, const char *planetname )
2098 {
2099    int i, found;
2100    Planet *planet ;
2101 
2102    if (sys == NULL) {
2103       WARN("Unable to remove planet '%s' from NULL system.", planetname);
2104       return -1;
2105    }
2106 
2107    /* Try to find planet. */
2108    planet = planet_get( planetname );
2109    for (i=0; i<sys->nplanets; i++)
2110       if (sys->planets[i] == planet)
2111          break;
2112 
2113    /* Planet not found. */
2114    if (i>=sys->nplanets) {
2115       WARN("Planet '%s' not found in system '%s' for removal.", planetname, sys->name);
2116       return -1;
2117    }
2118 
2119    /* Remove planet from system. */
2120    sys->nplanets--;
2121    memmove( &sys->planets[i], &sys->planets[i+1], sizeof(Planet*) * (sys->nplanets-i) );
2122    memmove( &sys->planetsid[i], &sys->planetsid[i+1], sizeof(int) * (sys->nplanets-i) );
2123 
2124    /* Remove the presence. */
2125    system_addPresence( sys, planet->faction, -(planet->presenceAmount), planet->presenceRange );
2126 
2127    /* Remove from the name stack thingy. */
2128    found = 0;
2129    for (i=0; i<spacename_nstack; i++)
2130       if (strcmp(planetname, planetname_stack[i])==0) {
2131          spacename_nstack--;
2132          memmove( &planetname_stack[i], &planetname_stack[i+1],
2133                sizeof(char*) * (spacename_nstack-i) );
2134          memmove( &systemname_stack[i], &systemname_stack[i+1],
2135                sizeof(char*) * (spacename_nstack-i) );
2136          found = 1;
2137          break;
2138       }
2139    if (found == 0)
2140       WARN("Unable to find planet '%s' and system '%s' in planet<->system stack.",
2141             planetname, sys->name );
2142 
2143    system_setFaction(sys);
2144 
2145    economy_addQueuedUpdate();
2146 
2147    return 0;
2148 }
2149 
2150 /**
2151  * @brief Adds a jump point to a star system from a diff.
2152  *
2153  * Note that economy_execQueued should always be run after this.
2154  *
2155  *    @param sys Star System to add jump point to.
2156  *    @param jumpname Name of the jump point to add.
2157  *    @return 0 on success.
2158  */
system_addJumpDiff(StarSystem * sys,xmlNodePtr node)2159 int system_addJumpDiff( StarSystem *sys, xmlNodePtr node )
2160 {
2161    if (system_parseJumpPointDiff(node, sys) <= -1)
2162       return 0;
2163    systems_reconstructJumps();
2164    economy_addQueuedUpdate();
2165 
2166    return 1;
2167 }
2168 
2169 
2170 /**
2171  * @brief Adds a jump point to a star system.
2172  *
2173  * Note that economy_execQueued should always be run after this.
2174  *
2175  *    @param sys Star System to add jump point to.
2176  *    @param jumpname Name of the jump point to add.
2177  *    @return 0 on success.
2178  */
system_addJump(StarSystem * sys,xmlNodePtr node)2179 int system_addJump( StarSystem *sys, xmlNodePtr node )
2180 {
2181    if (system_parseJumpPoint(node, sys) <= -1)
2182       return 0;
2183    systems_reconstructJumps();
2184    economy_refresh();
2185 
2186    return 1;
2187 }
2188 
2189 
2190 /**
2191  * @brief Removes a jump point from a star system.
2192  *
2193  * Note that economy_execQueued should always be run after this.
2194  *
2195  *    @param sys Star System to remove jump point from.
2196  *    @param jumpname Name of the jump point to remove.
2197  *    @return 0 on success.
2198  */
system_rmJump(StarSystem * sys,const char * jumpname)2199 int system_rmJump( StarSystem *sys, const char *jumpname )
2200 {
2201    int i;
2202    JumpPoint *jump;
2203 
2204    if (sys == NULL) {
2205       WARN("Unable to remove jump point '%s' from NULL system.", jumpname);
2206       return -1;
2207    }
2208 
2209    /* Try to find planet. */
2210    jump = jump_get( jumpname, sys );
2211    for (i=0; i<sys->njumps; i++)
2212       if (&sys->jumps[i] == jump)
2213          break;
2214 
2215    /* Planet not found. */
2216    if (i>=sys->njumps) {
2217       WARN("Jump point '%s' not found in system '%s' for removal.", jumpname, sys->name);
2218       return -1;
2219    }
2220 
2221    /* Remove jump from system. */
2222    sys->njumps--;
2223 
2224    /* Refresh presence */
2225    system_setFaction(sys);
2226 
2227    economy_addQueuedUpdate();
2228 
2229    return 0;
2230 }
2231 
2232 
2233 /**
2234  * @brief Adds a fleet to a star system.
2235  *
2236  *    @param sys Star System to add fleet to.
2237  *    @param fleet Fleet to add.
2238  *    @return 0 on success.
2239  */
system_addFleet(StarSystem * sys,Fleet * fleet)2240 int system_addFleet( StarSystem *sys, Fleet *fleet )
2241 {
2242    if (sys == NULL)
2243       return -1;
2244 
2245    /* Add the fleet. */
2246    sys->nfleets++;
2247    sys->fleets = realloc( sys->fleets, sizeof(Fleet*) * sys->nfleets );
2248    sys->fleets[sys->nfleets - 1] = fleet;
2249 
2250    /* Adjust the system average. */
2251    sys->avg_pilot += fleet->npilots;
2252 
2253    return 0;
2254 }
2255 
2256 
2257 /**
2258  * @brief Removes a fleet from a star system.
2259  *
2260  *    @param sys Star System to remove fleet from.
2261  *    @param fleet Fleet to remove.
2262  *    @return 0 on success.
2263  */
system_rmFleet(StarSystem * sys,Fleet * fleet)2264 int system_rmFleet( StarSystem *sys, Fleet *fleet )
2265 {
2266    int i;
2267 
2268    if (sys == NULL)
2269       return -1;
2270 
2271    /* Find a matching fleet (will grab first since can be duplicates). */
2272    for (i=0; i<sys->nfleets; i++)
2273       if (fleet == sys->fleets[i])
2274          break;
2275 
2276    /* Not found. */
2277    if (i >= sys->nfleets)
2278       return -1;
2279 
2280    /* Remove the fleet. */
2281    sys->nfleets--;
2282    memmove(&sys->fleets[i], &sys->fleets[i + 1], sizeof(Fleet*) * (sys->nfleets - i));
2283    sys->fleets = realloc(sys->fleets, sizeof(Fleet*) * sys->nfleets);
2284 
2285    /* Adjust the system average. */
2286    sys->avg_pilot -= fleet->npilots;
2287 
2288    return 0;
2289 }
2290 
2291 
2292 /**
2293  * @brief Initializes a new star system with null memory.
2294  */
system_init(StarSystem * sys)2295 static void system_init( StarSystem *sys )
2296 {
2297    memset( sys, 0, sizeof(StarSystem) );
2298    sys->faction   = -1;
2299 }
2300 
2301 
2302 /**
2303  * @brief Creates a new star system.
2304  */
system_new(void)2305 StarSystem *system_new (void)
2306 {
2307    StarSystem *sys;
2308    int realloced, id;
2309 
2310    /* Protect current system in case of realloc. */
2311    if (cur_system != NULL)
2312       id = system_index( cur_system );
2313 
2314    /* Check if memory needs to grow. */
2315    systems_nstack++;
2316    realloced = 0;
2317    if (systems_nstack > systems_mstack) {
2318       systems_mstack   *= 2;
2319       systems_stack     = realloc( systems_stack, sizeof(StarSystem) * systems_mstack );
2320       realloced         = 1;
2321    }
2322    sys = &systems_stack[ systems_nstack-1 ];
2323 
2324    /* Reset cur_system. */
2325    if (cur_system != NULL)
2326       cur_system = system_getIndex( id );
2327 
2328    /* Initialize system and id. */
2329    system_init( sys );
2330    sys->id = systems_nstack-1;
2331 
2332    /* Reconstruct the jumps. */
2333    if (!systems_loading && realloced)
2334       systems_reconstructJumps();
2335 
2336    return sys;
2337 }
2338 
2339 /**
2340  * @brief Reconstructs the jumps for a single system.
2341  */
system_reconstructJumps(StarSystem * sys)2342 void system_reconstructJumps (StarSystem *sys)
2343 {
2344    double dx, dy;
2345    int j;
2346    JumpPoint *jp;
2347    double a;
2348 
2349    for (j=0; j<sys->njumps; j++) {
2350       jp          = &sys->jumps[j];
2351       jp->target  = system_getIndex( jp->targetid );
2352 
2353       /* Get heading. */
2354       dx = jp->target->pos.x - sys->pos.x;
2355       dy = jp->target->pos.y - sys->pos.y;
2356       a = atan2( dy, dx );
2357       if (a < 0.)
2358          a += 2.*M_PI;
2359 
2360       /* Update position if needed.. */
2361       if (jp->flags & JP_AUTOPOS) {
2362          jp->pos.x   = sys->radius*cos(a);
2363          jp->pos.y   = sys->radius*sin(a);
2364       }
2365 
2366       /* Update jump specific data. */
2367       gl_getSpriteFromDir( &jp->sx, &jp->sy, jumppoint_gfx, a );
2368       jp->angle = 2.*M_PI-a;
2369       jp->cosa  = cos(jp->angle);
2370       jp->sina  = sin(jp->angle);
2371    }
2372 }
2373 
2374 /**
2375  * @brief Reconstructs the jumps.
2376  */
systems_reconstructJumps(void)2377 void systems_reconstructJumps (void)
2378 {
2379    StarSystem *sys;
2380    int i;
2381 
2382    /* So we need to calculate the shortest jump. */
2383    for (i=0; i<systems_nstack; i++) {
2384       sys = &systems_stack[i];
2385       system_reconstructJumps(sys);
2386    }
2387 }
2388 
2389 
2390 /**
2391  * @brief Updates the system planet pointers.
2392  */
systems_reconstructPlanets(void)2393 void systems_reconstructPlanets (void)
2394 {
2395    StarSystem *sys;
2396    int i, j;
2397 
2398    for (i=0; i<systems_nstack; i++) {
2399       sys = &systems_stack[i];
2400       for (j=0; j<sys->nplanets; j++)
2401          sys->planets[j] = &planet_stack[ sys->planetsid[j] ];
2402    }
2403 }
2404 
2405 
2406 /**
2407  * @brief Creates a system from an XML node.
2408  *
2409  *    @param parent XML node to get system from.
2410  *    @return System matching parent data.
2411  */
system_parse(StarSystem * sys,const xmlNodePtr parent)2412 static StarSystem* system_parse( StarSystem *sys, const xmlNodePtr parent )
2413 {
2414    char *ptrc;
2415    xmlNodePtr cur, node;
2416    uint32_t flags;
2417 
2418    /* Clear memory for sane defaults. */
2419    flags          = 0;
2420    sys->presence  = NULL;
2421    sys->npresence = 0;
2422    sys->ownerpresence = 0.;
2423 
2424    sys->name = xml_nodeProp(parent,"name"); /* already mallocs */
2425 
2426    node  = parent->xmlChildrenNode;
2427    do { /* load all the data */
2428 
2429       /* Only handle nodes. */
2430       xml_onlyNodes(node);
2431 
2432       if (xml_isNode(node,"pos")) {
2433          cur = node->children;
2434          do {
2435             if (xml_isNode(cur,"x")) {
2436                flags |= FLAG_XSET;
2437                sys->pos.x = xml_getFloat(cur);
2438             }
2439             else if (xml_isNode(cur,"y")) {
2440                flags |= FLAG_YSET;
2441                sys->pos.y = xml_getFloat(cur);
2442             }
2443          } while (xml_nextNode(cur));
2444          continue;
2445       }
2446       else if (xml_isNode(node,"general")) {
2447          cur = node->children;
2448          do {
2449             xmlr_strd( cur, "background", sys->background );
2450             xmlr_int( cur, "stars", sys->stars );
2451             xmlr_float( cur, "radius", sys->radius );
2452             if (xml_isNode(cur,"interference")) {
2453                flags |= FLAG_INTERFERENCESET;
2454                sys->interference = xml_getFloat(cur);
2455             }
2456             else if (xml_isNode(cur,"nebula")) {
2457                ptrc = xml_nodeProp(cur,"volatility");
2458                if (ptrc != NULL) { /* Has volatility  */
2459                   sys->nebu_volatility = atof(ptrc);
2460                   free(ptrc);
2461                }
2462                sys->nebu_density = xml_getFloat(cur);
2463             }
2464          } while (xml_nextNode(cur));
2465          continue;
2466       }
2467       /* Loads all the assets. */
2468       else if (xml_isNode(node,"assets")) {
2469          cur = node->children;
2470          do {
2471             if (xml_isNode(cur,"asset"))
2472                system_addPlanet( sys, xml_get(cur) );
2473          } while (xml_nextNode(cur));
2474          continue;
2475       }
2476 
2477       /* Avoid warning. */
2478       if (xml_isNode(node,"jumps"))
2479          continue;
2480       if (xml_isNode(node,"asteroids"))
2481          continue;
2482 
2483       DEBUG("Unknown node '%s' in star system '%s'",node->name,sys->name);
2484    } while (xml_nextNode(node));
2485 
2486 #define MELEMENT(o,s)      if (o) WARN("Star System '%s' missing '"s"' element", sys->name)
2487    if (sys->name == NULL) WARN("Star System '%s' missing 'name' tag", sys->name);
2488    MELEMENT((flags&FLAG_XSET)==0,"x");
2489    MELEMENT((flags&FLAG_YSET)==0,"y");
2490    MELEMENT(sys->stars==0,"stars");
2491    MELEMENT(sys->radius==0.,"radius");
2492    MELEMENT((flags&FLAG_INTERFERENCESET)==0,"inteference");
2493 #undef MELEMENT
2494 
2495    return 0;
2496 }
2497 
2498 
2499 /**
2500  * @brief Compares two system presences.
2501  */
sys_cmpSysFaction(const void * a,const void * b)2502 static int sys_cmpSysFaction( const void *a, const void *b )
2503 {
2504    SystemPresence *spa, *spb;
2505 
2506    spa = (SystemPresence*) a;
2507    spb = (SystemPresence*) b;
2508 
2509    /* Compare value. */
2510    if (spa->value < spb->value)
2511       return +1;
2512    else if (spa->value > spb->value)
2513       return -1;
2514 
2515    /* Compare faction id. */
2516    if (spa->faction < spb->faction)
2517       return +1;
2518    else if (spa->faction > spb->faction)
2519       return -1;
2520 
2521    return 0;
2522 }
2523 
2524 
2525 /**
2526  * @brief Sets the system faction based on the planets it has.
2527  *
2528  *    @param sys System to set the faction of.
2529  *    @return Faction that controls the system.
2530  */
system_setFaction(StarSystem * sys)2531 void system_setFaction( StarSystem *sys )
2532 {
2533    int i, j;
2534    Planet *pnt;
2535 
2536    /* Sort presences in descending order. */
2537    qsort( sys->presence, sys->npresence, sizeof(SystemPresence), sys_cmpSysFaction );
2538 
2539    sys->faction = -1;
2540    for (i=0; i<sys->npresence; i++) {
2541       for (j=0; j<sys->nplanets; j++) { /** @todo Handle multiple different factions. */
2542          pnt = sys->planets[j];
2543          if (pnt->real != ASSET_REAL)
2544             continue;
2545 
2546          if (pnt->faction != sys->presence[i].faction)
2547             continue;
2548 
2549          sys->faction = pnt->faction;
2550          return;
2551       }
2552    }
2553 }
2554 
2555 
2556 /**
2557  * @brief Parses a single jump point for a system, from unidiff.
2558  *
2559  *    @param node Parent node containing jump point information.
2560  *    @param sys System to which the jump point belongs.
2561  *    @return 0 on success.
2562  */
system_parseJumpPointDiff(const xmlNodePtr node,StarSystem * sys)2563 static int system_parseJumpPointDiff( const xmlNodePtr node, StarSystem *sys )
2564 {
2565    JumpPoint *j;
2566    char *buf;
2567    double x, y;
2568    StarSystem *target;
2569 
2570    /* Get target. */
2571    xmlr_attr( node, "target", buf );
2572    if (buf == NULL) {
2573       WARN("JumpPoint node for system '%s' has no target attribute.", sys->name);
2574       return -1;
2575    }
2576    target = system_get(buf);
2577    if (target == NULL) {
2578       WARN("JumpPoint node for system '%s' has invalid target '%s'.", sys->name, buf );
2579       free(buf);
2580       return -1;
2581    }
2582 
2583 #ifdef DEBUGGING
2584    int i;
2585    for (i=0; i<sys->njumps; i++) {
2586       j = &sys->jumps[i];
2587       if (j->targetid != target->id)
2588          continue;
2589 
2590       WARN("Star System '%s' has duplicate jump point to '%s'.",
2591             sys->name, target->name );
2592       break;
2593    }
2594 #endif /* DEBUGGING */
2595 
2596    /* Allocate more space. */
2597    sys->jumps = realloc( sys->jumps, (sys->njumps+1)*sizeof(JumpPoint) );
2598    j = &sys->jumps[ sys->njumps ];
2599    memset( j, 0, sizeof(JumpPoint) );
2600 
2601    /* Handle jump point position. We want both x and y, or we autoposition the jump point. */
2602    xmlr_attr( node, "x", buf );
2603    if (buf == NULL)
2604       jp_setFlag(j,JP_AUTOPOS);
2605    else
2606       x = atof(buf);
2607    xmlr_attr( node, "y", buf );
2608    if (buf == NULL)
2609       jp_setFlag(j,JP_AUTOPOS);
2610    else
2611       y = atof(buf);
2612 
2613    /* Handle jump point type. */
2614    xmlr_attr( node, "type", buf );
2615    if (buf == NULL);
2616    else if (!strcmp(buf, "hidden"))
2617       jp_setFlag(j,JP_HIDDEN);
2618    else if (!strcmp(buf, "exitonly"))
2619       jp_setFlag(j,JP_EXITONLY);
2620 
2621    /* Handle jump point hide. */
2622    xmlr_attr( node, "hide", buf );
2623    if (buf == NULL)
2624       j->hide = HIDE_DEFAULT_JUMP;
2625    else
2626       j->hide = atoi(buf);
2627 
2628    /* Set some stuff. */
2629    j->target = target;
2630    free(buf);
2631    j->targetid = j->target->id;
2632    j->radius = 200.;
2633 
2634    if (!jp_isFlag(j,JP_AUTOPOS))
2635       vect_cset( &j->pos, x, y );
2636 
2637    /* Square to allow for linear multiplication with squared distances. */
2638    j->hide = pow2(j->hide);
2639 
2640    /* Added jump. */
2641    sys->njumps++;
2642 
2643    return 0;
2644 }
2645 
2646 
2647 /**
2648  * @brief Parses a single jump point for a system.
2649  *
2650  *    @param node Parent node containing jump point information.
2651  *    @param sys System to which the jump point belongs.
2652  *    @return 0 on success.
2653  */
system_parseJumpPoint(const xmlNodePtr node,StarSystem * sys)2654 static int system_parseJumpPoint( const xmlNodePtr node, StarSystem *sys )
2655 {
2656    JumpPoint *j;
2657    char *buf;
2658    xmlNodePtr cur;
2659    double x, y;
2660    StarSystem *target;
2661    int pos;
2662 
2663    /* Get target. */
2664    xmlr_attr( node, "target", buf );
2665    if (buf == NULL) {
2666       WARN("JumpPoint node for system '%s' has no target attribute.", sys->name);
2667       return -1;
2668    }
2669    target = system_get(buf);
2670    if (target == NULL) {
2671       WARN("JumpPoint node for system '%s' has invalid target '%s'.", sys->name, buf );
2672       free(buf);
2673       return -1;
2674    }
2675 
2676 #ifdef DEBUGGING
2677    int i;
2678    for (i=0; i<sys->njumps; i++) {
2679       j = &sys->jumps[i];
2680       if (j->targetid != target->id)
2681          continue;
2682 
2683       WARN("Star System '%s' has duplicate jump point to '%s'.",
2684             sys->name, target->name );
2685       break;
2686    }
2687 #endif /* DEBUGGING */
2688 
2689    /* Allocate more space. */
2690    sys->jumps = realloc( sys->jumps, (sys->njumps+1)*sizeof(JumpPoint) );
2691    j = &sys->jumps[ sys->njumps ];
2692    memset( j, 0, sizeof(JumpPoint) );
2693 
2694    /* Set some stuff. */
2695    j->target = target;
2696    free(buf);
2697    j->targetid = j->target->id;
2698    j->radius = 200.;
2699 
2700    pos = 0;
2701 
2702    /* Parse data. */
2703    cur = node->xmlChildrenNode;
2704    do {
2705       xmlr_float( cur, "radius", j->radius );
2706 
2707       /* Handle position. */
2708       if (xml_isNode(cur,"pos")) {
2709          pos = 1;
2710          xmlr_attr( cur, "x", buf );
2711          if (buf==NULL) {
2712             WARN("JumpPoint for system '%s' has position node missing 'x' position, using 0.", sys->name);
2713             x = 0.;
2714          }
2715          else {
2716             x = atof(buf);
2717             free(buf);
2718          }
2719          xmlr_attr( cur, "y", buf );
2720          if (buf==NULL) {
2721             WARN("JumpPoint for system '%s' has position node missing 'y' position, using 0.", sys->name);
2722             y = 0.;
2723          }
2724          else {
2725             y = atof(buf);
2726             free(buf);
2727          }
2728 
2729          /* Set position. */
2730          vect_cset( &j->pos, x, y );
2731       }
2732       else if (xml_isNode(cur,"autopos"))
2733          jp_setFlag(j,JP_AUTOPOS);
2734       else if (xml_isNode(cur,"hidden"))
2735          jp_setFlag(j,JP_HIDDEN);
2736       else if (xml_isNode(cur,"exitonly"))
2737          jp_setFlag(j,JP_EXITONLY);
2738       else if (xml_isNode(cur,"hide")) {
2739          xmlr_float( cur,"hide", j->hide );
2740       }
2741    } while (xml_nextNode(cur));
2742 
2743    if (!jp_isFlag(j,JP_AUTOPOS) && !pos)
2744       WARN("JumpPoint in system '%s' is missing pos element but does not have autopos flag.", sys->name);
2745 
2746    /* Square to allow for linear multiplication with squared distances. */
2747    j->hide = pow2(j->hide);
2748 
2749    /* Added jump. */
2750    sys->njumps++;
2751 
2752    return 0;
2753 }
2754 
2755 
2756 /**
2757  * @brief Loads the jumps into a system.
2758  *
2759  *    @param parent System parent node.
2760  */
system_parseJumps(const xmlNodePtr parent)2761 static void system_parseJumps( const xmlNodePtr parent )
2762 {
2763    int i;
2764    StarSystem *sys;
2765    char* name;
2766    xmlNodePtr cur, node;
2767 
2768    name = xml_nodeProp(parent,"name"); /* already mallocs */
2769    sys = NULL;
2770    for (i=0; i<systems_nstack; i++) {
2771       if (strcmp( systems_stack[i].name, name)==0) {
2772          sys = &systems_stack[i];
2773          break;
2774       }
2775    }
2776    if (sys == NULL) {
2777       WARN("System '%s' was not found in the stack for some reason",name);
2778       return;
2779    }
2780    free(name); /* no more need for it */
2781 
2782    node  = parent->xmlChildrenNode;
2783 
2784    do { /* load all the data */
2785       if (xml_isNode(node,"jumps")) {
2786          cur = node->children;
2787          do {
2788             if (xml_isNode(cur,"jump"))
2789                system_parseJumpPoint( cur, sys );
2790          } while (xml_nextNode(cur));
2791       }
2792    } while (xml_nextNode(node));
2793 }
2794 
2795 
2796 /**
2797  * @brief Parses a single asteroid field for a system.
2798  *
2799  *    @param node Parent node containing asteroid field information.
2800  *    @param sys System.
2801  *    @return 0 on success.
2802  */
system_parseAsteroidField(const xmlNodePtr node,StarSystem * sys)2803 static int system_parseAsteroidField( const xmlNodePtr node, StarSystem *sys )
2804 {
2805    int i, j, k, l, m, n, retry, getout;
2806    AsteroidAnchor *a;
2807    AsteroidSubset *sub, *newsub;
2808    xmlNodePtr cur, pcur;
2809    double x, y, pro, prob;
2810 
2811    /* Allocate more space. */
2812    sys->asteroids = realloc( sys->asteroids, (sys->nasteroids+1)*sizeof(AsteroidAnchor) );
2813    a = &sys->asteroids[ sys->nasteroids ];
2814    memset( a, 0, sizeof(AsteroidAnchor) );
2815 
2816    /* Initialize stuff. */
2817    a->density  = .2;
2818    a->ncorners = 0;
2819    a->corners  = NULL;
2820    a->aera     = 0.;
2821    vect_cset( &a->pos, 0., 0. );
2822 
2823    /* Parse data. */
2824    cur = node->xmlChildrenNode;
2825    do {
2826       xmlr_float( cur,"density", a->density );
2827 
2828       /* Handle corners. */
2829       if (xml_isNode(cur,"corner")) {
2830          pcur = cur->children;
2831          do {
2832             xmlr_float( pcur, "x", x );
2833             xmlr_float( pcur, "y", y );
2834          } while (xml_nextNode(pcur));
2835          a->ncorners++;
2836          if (a->corners==NULL)
2837             a->corners = malloc( sizeof(Vector2d) );
2838          else
2839             a->corners = realloc( a->corners, (a->ncorners)*sizeof(Vector2d) );
2840          /* Set position. */
2841          vect_cset( &a->corners[a->ncorners-1], x, y );
2842          /* Increment center. */
2843          a->pos.x += x;
2844          a->pos.y += y;
2845       }
2846 
2847    } while (xml_nextNode(cur));
2848 
2849    if (a->ncorners < 3)
2850        WARN("asteroid field in %d has less than 3 corners.", sys->name);
2851 
2852    /* Normalize the center */
2853    a->pos.x /= a->ncorners;
2854    a->pos.y /= a->ncorners;
2855 
2856    /* Compute the aera as a sum of triangles */
2857    for (i=0; i<a->ncorners-1; i++) {
2858       a->aera += (a->corners[i].x-a->pos.x)*(a->corners[i+1].y-a->pos.y)
2859                - (a->corners[i+1].x-a->pos.x)*(a->corners[i].y-a->pos.y);
2860    }
2861    /* And the last one to loop */
2862    if (a->ncorners > 0) {
2863       i = a->ncorners-1;
2864       a->aera += (a->corners[i].x-a->pos.x)*(a->corners[0].y-a->pos.y)
2865                - (a->corners[0].x-a->pos.x)*(a->corners[i].y-a->pos.y);
2866    }
2867    a->aera /= 2;
2868 
2869    /* Compute number of asteroids */
2870    a->nb      = floor( ABS(a->aera) / 500000 * a->density );
2871    a->ndebris = floor(100*a->density);
2872 
2873    /* Added asteroid. */
2874    sys->nasteroids++;
2875 
2876    /* Initialize the convex subsets. */
2877    a->subsets = malloc( sizeof(AsteroidSubset) );
2878    a->subsets[0].ncorners = a->ncorners;
2879    a->subsets[0].corners = a->corners;
2880    a->nsubsets = 1;
2881 
2882    /* Cut the subsets until they are all convex. */
2883    retry  = 1;
2884    getout = 0;
2885 
2886    while (retry) {
2887       retry = 0;
2888       for (i=0; i<a->nsubsets; i++) {
2889          sub = &a->subsets[i];
2890          for (j=0; j<sub->ncorners; j++) {
2891             /* Find the 2 other corners. */
2892             if (j>0 && j<sub->ncorners-1) {
2893                k = j-1;
2894                l = j+1;
2895             }
2896             else if (j==0) {
2897                k = sub->ncorners-1;
2898                l = j+1;
2899             }
2900             else { /* (j==ncorners-1) */
2901                k = j-1;
2902                l = 0;
2903             }
2904 
2905             /* Test the orientation of the angle towards signed aera. */
2906             pro = (sub->corners[k].x-sub->corners[j].x) *
2907                   (sub->corners[l].y-sub->corners[j].y) -
2908                   (sub->corners[k].y-sub->corners[j].y) *
2909                   (sub->corners[l].x-sub->corners[j].x);
2910 
2911             if (pro * a->aera > 0) {
2912                /* Cut here, find an other point. */
2913                x = (sub->corners[k].x + sub->corners[l].x)/2;
2914                y = (sub->corners[k].y + sub->corners[l].y)/2;
2915 
2916                pro = 1.;
2917                for (m=0; m<sub->ncorners; m++) {
2918                   prob = (x-sub->corners[j].x)*(sub->corners[m].x-sub->corners[j].x) +
2919                          (y-sub->corners[j].y)*(sub->corners[m].y-sub->corners[j].y);
2920                   if ( prob < pro ) {
2921                      pro = prob;
2922                      n = m;
2923                   }
2924                }
2925 
2926                if (pro>=0)
2927                   WARN("Non-convex asteroid polygon seems to be self-intersecting in %d", sys->name);
2928 
2929                /* Split subset i into 2 subsets */
2930 
2931                a->subsets = realloc( a->subsets, (a->nsubsets+1)*sizeof(AsteroidSubset) );
2932 
2933                /* Need to recall who is sub. */
2934                sub = &a->subsets[i];
2935                newsub = &a->subsets[a->nsubsets];
2936 
2937                /* First, make sure n<j: invert j and n if needed */
2938                if (j<n) {k=n; n=j; j=k;}
2939 
2940                newsub->ncorners = j-n+1;
2941                newsub->corners  = malloc( sizeof(Vector2d)*newsub->ncorners );
2942 
2943                /* Add some nodes to the new subset */
2944                k = 0;
2945                for (m=n; m<j+1; m++) {
2946                   newsub->corners[k] = sub->corners[m];
2947                   k++;
2948                }
2949 
2950                /* Remove some nodes to the old subset */
2951                memmove(&sub->corners[n+1], &sub->corners[j], sizeof(AsteroidSubset)*(sub->ncorners - j) );
2952                sub->ncorners = sub->ncorners-(j-n)+1;
2953                sub->corners = realloc(sub->corners, sizeof(AsteroidSubset) * sub->ncorners);
2954 
2955                a->nsubsets++;
2956 
2957                getout = 1;
2958                break;
2959             }
2960          }
2961          if (getout) break;
2962       }
2963    }
2964    for (i=0; i<a->nsubsets; i++) {
2965       /* Get a center of the polygon */
2966       sub = &a->subsets[i];
2967       sub->pos.x = 0;
2968       sub->pos.y = 0;
2969       sub->aera  = 0;
2970       for (j=0; j<sub->ncorners; j++) {
2971          sub->pos.x += sub->corners[j].x;
2972          sub->pos.y += sub->corners[j].y;
2973       }
2974       sub->pos.x /= sub->ncorners;
2975       sub->pos.y /= sub->ncorners;
2976 
2977       /* Compute the aera as a sum of triangles */
2978       for (j=0; j<sub->ncorners-1; j++) {
2979          sub->aera += (sub->corners[j].x-sub->pos.x)*(sub->corners[j+1].y-sub->pos.y)
2980                     - (sub->corners[j+1].x-sub->pos.x)*(sub->corners[j].y-sub->pos.y);
2981       }
2982       /* And the last one to loop */
2983       if (sub->ncorners > 0) {
2984          j = sub->ncorners-1;
2985          sub->aera += (sub->corners[j].x-sub->pos.x)*(sub->corners[0].y-sub->pos.y)
2986                     - (sub->corners[0].x-sub->pos.x)*(sub->corners[j].y-sub->pos.y);
2987       }
2988       sub->aera /= 2;
2989    }
2990 
2991    return 0;
2992 }
2993 
2994 
2995 /**
2996  * @brief Loads the asteroid anchor into a system.
2997  *
2998  *    @param parent System parent node.
2999  *    @param sys System.
3000  */
system_parseAsteroids(const xmlNodePtr parent,StarSystem * sys)3001 static void system_parseAsteroids( const xmlNodePtr parent, StarSystem *sys )
3002 {
3003    xmlNodePtr cur, node;
3004 
3005    node  = parent->xmlChildrenNode;
3006 
3007    do { /* load all the data */
3008       if (xml_isNode(node,"asteroids")) {
3009          cur = node->children;
3010          do {
3011             if (xml_isNode(cur,"asteroid"))
3012                system_parseAsteroidField( cur, sys );
3013          } while (xml_nextNode(cur));
3014       }
3015    } while (xml_nextNode(node));
3016 }
3017 
3018 
3019 /**
3020  * @brief Loads the entire universe into ram - pretty big feat eh?
3021  *
3022  *    @return 0 on success.
3023  */
space_load(void)3024 int space_load (void)
3025 {
3026    int i, j, len;
3027    int ret;
3028    StarSystem *sys;
3029    char **asteroid_files, file[PATH_MAX];
3030 
3031    /* Loading. */
3032    systems_loading = 1;
3033 
3034    /* Load jump point graphic - must be before systems_load(). */
3035    jumppoint_gfx = gl_newSprite(  PLANET_GFX_SPACE_PATH"jumppoint.png", 4, 4, OPENGL_TEX_MIPMAPS );
3036    jumpbuoy_gfx = gl_newImage(  PLANET_GFX_SPACE_PATH"jumpbuoy.png", 0 );
3037 
3038    /* Load planets. */
3039    ret = planets_load();
3040    if (ret < 0)
3041       return ret;
3042 
3043    /* Load systems. */
3044    ret = systems_load();
3045    if (ret < 0)
3046       return ret;
3047 
3048    /* Load asteroid graphics. */
3049    asteroid_files = ndata_list( PLANET_GFX_SPACE_PATH"asteroid/", &nasterogfx );
3050    asteroid_gfx = malloc( sizeof(StarSystem) * systems_mstack );
3051 
3052    for (i=0; i<(int)nasterogfx; i++) {
3053       len  = (strlen(PLANET_GFX_SPACE_PATH)+strlen(asteroid_files[i])+11);
3054       nsnprintf( file, len,"%s%s",PLANET_GFX_SPACE_PATH"asteroid/",asteroid_files[i] );
3055       asteroid_gfx[i] = gl_newImage( file, OPENGL_TEX_MIPMAPS );
3056    }
3057 
3058    /* Load asteroid types. */
3059    ret = asteroidTypes_load();
3060    if (ret < 0)
3061       return ret;
3062 
3063    /* Done loading. */
3064    systems_loading = 0;
3065 
3066    /* Apply all the presences. */
3067    for (i=0; i<systems_nstack; i++)
3068       system_addAllPlanetsPresence(&systems_stack[i]);
3069 
3070    /* Determine dominant faction. */
3071    for (i=0; i<systems_nstack; i++)
3072       system_setFaction( &systems_stack[i] );
3073 
3074    /* Reconstruction. */
3075    systems_reconstructJumps();
3076    systems_reconstructPlanets();
3077 
3078    /* Fine tuning. */
3079    for (i=0; i<systems_nstack; i++) {
3080       sys = &systems_stack[i];
3081 
3082       /* Save jump indexes. */
3083       for (j=0; j<sys->njumps; j++)
3084          sys->jumps[j].targetid = sys->jumps[j].target->id;
3085       sys->ownerpresence = system_getPresence( sys, sys->faction );
3086    }
3087 
3088    return 0;
3089 }
3090 
3091 
3092 /**
3093  * @brief Loads the asteroids types.
3094  *
3095  *    @return 0 on success.
3096  */
asteroidTypes_load(void)3097 static int asteroidTypes_load (void)
3098 {
3099    int i, len;
3100    AsteroidType *at;
3101    uint32_t bufsize;
3102    char *buf, *str, file[PATH_MAX];
3103    xmlNodePtr node, cur;
3104    xmlDocPtr doc;
3105 
3106    /* Load the data. */
3107    buf = ndata_read( ASTERO_DATA_PATH, &bufsize );
3108    if (buf == NULL) {
3109       WARN("Unable to read data from '%s'", ASTERO_DATA_PATH);
3110       return -1;
3111    }
3112 
3113    /* Load the document. */
3114    doc = xmlParseMemory( buf, bufsize );
3115    if (doc == NULL) {
3116       WARN("Unable to parse document '%s'", ASTERO_DATA_PATH);
3117       return -1;
3118    }
3119 
3120    /* Get the root node. */
3121    node = doc->xmlChildrenNode;
3122    if (!xml_isNode(node,"Asteroid_types")) {
3123       WARN("Malformed '"ASTERO_DATA_PATH"' file: missing root element 'Asteroid_types'");
3124       return -1;
3125    }
3126 
3127    /* Get the first node. */
3128    node = node->xmlChildrenNode; /* first event node */
3129    if (node == NULL) {
3130       WARN("Malformed '"ASTERO_DATA_PATH"' file: does not contain elements");
3131       return -1;
3132    }
3133 
3134    do {
3135       if (xml_isNode(node,"asteroid")) {
3136 
3137          /* Grow memory. */
3138          asteroid_types = realloc(asteroid_types, sizeof(AsteroidType)*(asteroid_ntypes+1));
3139 
3140          /* Load it. */
3141          at = &asteroid_types[asteroid_ntypes];
3142          at->gfxs = NULL;
3143 
3144          cur = node->children;
3145          i = 0;
3146          do {
3147             if (xml_isNode(cur,"gfx")) {
3148                at->gfxs = realloc( at->gfxs, sizeof(glTexture)*(i+1) );
3149                str = xml_get(cur);
3150                len  = (strlen(PLANET_GFX_SPACE_PATH)+strlen(str)+14);
3151                nsnprintf( file, len,"%s%s%s",PLANET_GFX_SPACE_PATH"asteroid/",str,".png");
3152                at->gfxs[i] = gl_newImage( file, OPENGL_TEX_MIPMAPS );
3153                i++;
3154             }
3155             else if (xml_isNode(cur,"id"))
3156                at->ID = xml_getStrd(cur);
3157          } while (xml_nextNode(cur));
3158 
3159          if (i==0)
3160             WARN("Asteroid type has no gfx associated.");
3161 
3162          at->ngfx = i;
3163          asteroid_ntypes++;
3164       }
3165    } while (xml_nextNode(node));
3166 
3167    /* Shrink to minimum. */
3168    asteroid_types = realloc(asteroid_types, sizeof(AsteroidType)*asteroid_ntypes);
3169 
3170    /* Clean up. */
3171    xmlFreeDoc(doc);
3172    free(buf);
3173 
3174    return 0;
3175 }
3176 
3177 
3178 /**
3179  * @brief Loads the entire systems, needs to be called after planets_load.
3180  *
3181  * Does multiple passes to load:
3182  *
3183  *  - First loads the star systems.
3184  *  - Next sets the jump routes.
3185  *
3186  *    @return 0 on success.
3187  */
systems_load(void)3188 static int systems_load (void)
3189 {
3190    uint32_t bufsize;
3191    char *buf, **system_files, *file;
3192    xmlNodePtr node;
3193    xmlDocPtr doc;
3194    StarSystem *sys;
3195    int i, len;
3196    uint32_t nfiles;
3197 
3198    /* Allocate if needed. */
3199    if (systems_stack == NULL) {
3200       systems_mstack = CHUNK_SIZE;
3201       systems_stack = malloc( sizeof(StarSystem) * systems_mstack );
3202       systems_nstack = 0;
3203    }
3204 
3205    system_files = ndata_list( SYSTEM_DATA_PATH, &nfiles );
3206 
3207    /*
3208     * First pass - loads all the star systems_stack.
3209     */
3210    for (i=0; i<(int)nfiles; i++) {
3211 
3212       len  = strlen(SYSTEM_DATA_PATH)+strlen(system_files[i])+2;
3213       file = malloc( len );
3214       nsnprintf( file, len, "%s%s", SYSTEM_DATA_PATH, system_files[i] );
3215       /* Load the file. */
3216       buf = ndata_read( file, &bufsize );
3217       doc = xmlParseMemory( buf, bufsize );
3218       if (doc == NULL) {
3219          WARN("%s file is invalid xml!",file);
3220          free(buf);
3221          continue;
3222       }
3223 
3224       node = doc->xmlChildrenNode; /* first planet node */
3225       if (node == NULL) {
3226          WARN("Malformed %s file: does not contain elements",file);
3227          xmlFreeDoc(doc);
3228          free(buf);
3229          continue;
3230       }
3231 
3232       sys = system_new();
3233       system_parse( sys, node );
3234       system_parseAsteroids(node, sys); /* load the asteroids anchors */
3235 
3236       /* Clean up. */
3237       xmlFreeDoc(doc);
3238       free(buf);
3239       free( file );
3240    }
3241 
3242    /*
3243     * Second pass - loads all the jump routes.
3244     */
3245    for (i=0; i<(int)nfiles; i++) {
3246 
3247       len  = strlen(SYSTEM_DATA_PATH)+strlen(system_files[i])+2;
3248       file = malloc( len );
3249       nsnprintf( file, len, "%s%s", SYSTEM_DATA_PATH, system_files[i] );
3250       /* Load the file. */
3251       buf = ndata_read( file, &bufsize );
3252       free( file );
3253       doc = xmlParseMemory( buf, bufsize );
3254       if (doc == NULL) {
3255          free(buf);
3256          continue;
3257       }
3258 
3259       node = doc->xmlChildrenNode; /* first planet node */
3260       if (node == NULL) {
3261          xmlFreeDoc(doc);
3262          free(buf);
3263          continue;
3264       }
3265 
3266       system_parseJumps(node); /* will automatically load the jumps into the system */
3267 
3268       /* Clean up. */
3269       xmlFreeDoc(doc);
3270       free(buf);
3271    }
3272 
3273    DEBUG("Loaded %d Star System%s with %d Planet%s",
3274          systems_nstack, (systems_nstack==1) ? "" : "s",
3275          planet_nstack, (planet_nstack==1) ? "" : "s" );
3276 
3277    /* Clean up. */
3278    for (i=0; i<(int)nfiles; i++)
3279       free( system_files[i] );
3280    free( system_files );
3281 
3282    return 0;
3283 }
3284 
3285 
3286 /**
3287  * @brief Renders the system.
3288  *
3289  *    @param dt Current delta tick.
3290  */
space_render(const double dt)3291 void space_render( const double dt )
3292 {
3293    if (cur_system == NULL)
3294       return;
3295 
3296    if (cur_system->nebu_density > 0.)
3297       nebu_render(dt);
3298    else
3299       background_render(dt);
3300 }
3301 
3302 
3303 /**
3304  * @brief Renders the system overlay.
3305  *
3306  *    @param dt Current delta tick.
3307  */
space_renderOverlay(const double dt)3308 void space_renderOverlay( const double dt )
3309 {
3310    if (cur_system == NULL)
3311       return;
3312 
3313    if ((cur_system->nebu_density > 0.) &&
3314          !menu_isOpen( MENU_MAIN ))
3315       nebu_renderOverlay(dt);
3316 }
3317 
3318 
3319 /**
3320  * @brief Renders the current systemsplanets.
3321  */
planets_render(void)3322 void planets_render (void)
3323 {
3324    int i, j;
3325    double x, y;
3326    AsteroidAnchor *ast;
3327    Pilot *pplayer;
3328    Solid *psolid;
3329 
3330    /* Must be a system. */
3331    if (cur_system==NULL)
3332       return;
3333 
3334    /* Render the jumps. */
3335    for (i=0; i < cur_system->njumps; i++)
3336       space_renderJumpPoint( &cur_system->jumps[i], i );
3337 
3338    /* Render the planets. */
3339    for (i=0; i < cur_system->nplanets; i++)
3340       if (cur_system->planets[i]->real == ASSET_REAL)
3341          space_renderPlanet( cur_system->planets[i] );
3342 
3343    /* Get the player in order to compute the offset for debris. */
3344    pplayer = pilot_get( PLAYER_ID );
3345    if (pplayer != NULL)
3346       psolid  = pplayer->solid;
3347 
3348    /* Render the asteroids & debris. */
3349    for (i=0; i < cur_system->nasteroids; i++) {
3350       ast = &cur_system->asteroids[i];
3351       for (j=0; j < ast->nb; j++)
3352         space_renderAsteroid( &ast->asteroids[j] );
3353 
3354       if (pplayer != NULL) {
3355          x = psolid->pos.x - SCREEN_W/2;
3356          y = psolid->pos.y - SCREEN_H/2;
3357          for (j=0; j < ast->ndebris; j++)
3358            space_renderDebris( &ast->debris[j], x, y );
3359       }
3360    }
3361 
3362 }
3363 
3364 
3365 /**
3366  * @brief Renders a jump point.
3367  */
space_renderJumpPoint(JumpPoint * jp,int i)3368 static void space_renderJumpPoint( JumpPoint *jp, int i )
3369 {
3370    const glColour *c;
3371 
3372    if (!jp_isUsable(jp))
3373       return;
3374 
3375    if ((player.p != NULL) && (i==player.p->nav_hyperspace) &&
3376          (pilot_isFlag(player.p, PILOT_HYPERSPACE) || space_canHyperspace(player.p)))
3377       c = &cGreen;
3378    else if (jp_isFlag(jp, JP_HIDDEN))
3379       c = &cRed;
3380    else
3381       c = NULL;
3382 
3383    gl_blitSprite( jumppoint_gfx, jp->pos.x, jp->pos.y, jp->sx, jp->sy, c );
3384 
3385    /* Draw buoys next to "highway" jump points. */
3386    if (jp->hide == 0.) {
3387       gl_blitSprite( jumpbuoy_gfx, jp->pos.x + 200 * jp->sina, jp->pos.y + 200 * jp->cosa, 0, 0, NULL ); /* Left */
3388       gl_blitSprite( jumpbuoy_gfx, jp->pos.x + -200 * jp->sina, jp->pos.y + -200 * jp->cosa, 0, 0, NULL ); /* Right */
3389    }
3390 }
3391 
3392 
3393 /**
3394  * @brief Renders a planet.
3395  */
space_renderPlanet(Planet * p)3396 static void space_renderPlanet( Planet *p )
3397 {
3398    gl_blitSprite( p->gfx_space, p->pos.x, p->pos.y, 0, 0, NULL );
3399 }
3400 
3401 
3402 /**
3403  * @brief Renders an asteroid.
3404  */
space_renderAsteroid(Asteroid * a)3405 static void space_renderAsteroid( Asteroid *a )
3406 {
3407    double scale;
3408    AsteroidType *at;
3409 
3410    /* Check if needs scaling. */
3411    if (a->appearing == 1)
3412       scale = CLAMP( 0., 1., a->timer / 2. );
3413    else if (a->appearing == 2)
3414       scale = CLAMP( 0., 1., 1. - a->timer / 2. );
3415    else
3416       scale = 1.;
3417 
3418    at = &asteroid_types[a->type];
3419 
3420    gl_blitSpriteInterpolateScale( at->gfxs[a->gfxID], at->gfxs[a->gfxID], 1,
3421                                   a->pos.x, a->pos.y, scale, scale, 0, 0, NULL );
3422 }
3423 
3424 
3425 /**
3426  * @brief Renders a debris.
3427  */
space_renderDebris(Debris * d,double x,double y)3428 static void space_renderDebris( Debris *d, double x, double y )
3429 {
3430    double scale;
3431    Vector2d *testVect;
3432 
3433    scale = .5;
3434 
3435    testVect = malloc(sizeof(Vector2d));
3436    testVect->x = d->pos.x + x;
3437    testVect->y = d->pos.y + y;
3438 
3439    if ( space_isInField( testVect ) == 0 )
3440       gl_blitSpriteInterpolateScale( asteroid_gfx[d->gfxID], asteroid_gfx[d->gfxID], 1,
3441                                      d->pos.x + x, d->pos.y + y, scale, scale, 0, 0, NULL );
3442    free(testVect);
3443 }
3444 
3445 
3446 /**
3447  * @brief Cleans up the system.
3448  */
space_exit(void)3449 void space_exit (void)
3450 {
3451    int i, j;
3452    Planet *pnt;
3453    AsteroidAnchor *ast;
3454    StarSystem *sys;
3455    AsteroidType *at;
3456 
3457    /* Free jump point graphic. */
3458    if (jumppoint_gfx != NULL)
3459       gl_freeTexture(jumppoint_gfx);
3460    jumppoint_gfx = NULL;
3461    if (jumpbuoy_gfx != NULL)
3462       gl_freeTexture(jumpbuoy_gfx);
3463    jumpbuoy_gfx = NULL;
3464 
3465    /* Free asteroid graphics. */
3466    for (i=0; i<(int)nasterogfx; i++)
3467       gl_freeTexture(asteroid_gfx[i]);
3468    free(asteroid_gfx);
3469 
3470    /* Free the names. */
3471    if (planetname_stack != NULL)
3472       free(planetname_stack);
3473    if (systemname_stack != NULL)
3474       free(systemname_stack);
3475    spacename_nstack = 0;
3476 
3477    /* Free the planets. */
3478    for (i=0; i < planet_nstack; i++) {
3479       pnt = &planet_stack[i];
3480 
3481       free(pnt->name);
3482       free(pnt->class);
3483       free(pnt->description);
3484       free(pnt->bar_description);
3485 
3486       /* graphics */
3487       if (pnt->gfx_spaceName != NULL) {
3488          if (pnt->gfx_space != NULL)
3489             gl_freeTexture( pnt->gfx_space );
3490          free(pnt->gfx_spaceName);
3491          free(pnt->gfx_spacePath);
3492       }
3493       if (pnt->gfx_exterior != NULL) {
3494          free(pnt->gfx_exterior);
3495          free(pnt->gfx_exteriorPath);
3496       }
3497 
3498       /* Landing. */
3499       free(pnt->land_func);
3500       free(pnt->land_msg);
3501       free(pnt->bribe_msg);
3502       free(pnt->bribe_ack_msg);
3503 
3504       /* tech */
3505       if (pnt->tech != NULL)
3506          tech_groupDestroy( pnt->tech );
3507 
3508       /* commodities */
3509       free(pnt->commodities);
3510    }
3511    free(planet_stack);
3512    planet_stack = NULL;
3513    planet_nstack = 0;
3514    planet_mstack = 0;
3515 
3516    /* Free the systems. */
3517    for (i=0; i < systems_nstack; i++) {
3518       free(systems_stack[i].name);
3519       if (systems_stack[i].fleets)
3520          free(systems_stack[i].fleets);
3521       if (systems_stack[i].jumps)
3522          free(systems_stack[i].jumps);
3523       if (systems_stack[i].background)
3524          free(systems_stack[i].background);
3525 
3526       if(systems_stack[i].presence)
3527          free(systems_stack[i].presence);
3528 
3529       if (systems_stack[i].planets != NULL)
3530          free(systems_stack[i].planets);
3531       if (systems_stack[i].planetsid != NULL)
3532          free(systems_stack[i].planetsid);
3533 
3534       /* Free the asteroids. */
3535       sys = &systems_stack[i];
3536 
3537       for (j=0; j < sys->nasteroids; j++) {
3538          ast = &sys->asteroids[j];
3539          free(ast->asteroids);
3540          free(ast->debris);
3541          free(ast->subsets);
3542       }
3543       free(sys->asteroids);
3544 
3545    }
3546    free(systems_stack);
3547    systems_stack = NULL;
3548    systems_nstack = 0;
3549    systems_mstack = 0;
3550 
3551    /* Free the asteroid types. */
3552    for (i=0; i < asteroid_ntypes; i++) {
3553       at = &asteroid_types[i];
3554       free(at[i].ID);
3555       for (j=0; j<at->ngfx; j++) {
3556          gl_freeTexture(at->gfxs[j]);
3557       }
3558    }
3559    free(asteroid_types);
3560    asteroid_types = NULL;
3561    asteroid_ntypes = 0;
3562 
3563    /* Free landing lua. */
3564    if (landing_env != LUA_NOREF)
3565       nlua_freeEnv( landing_env );
3566    landing_env = LUA_NOREF;
3567 }
3568 
3569 
3570 /**
3571  * @brief Clears all system knowledge.
3572  */
space_clearKnown(void)3573 void space_clearKnown (void)
3574 {
3575    int i, j;
3576    StarSystem *sys;
3577    for (i=0; i<systems_nstack; i++) {
3578       sys = &systems_stack[i];
3579       sys_rmFlag(sys,SYSTEM_KNOWN);
3580       for (j=0; j<sys->njumps; j++)
3581          jp_rmFlag(&sys->jumps[j],JP_KNOWN);
3582    }
3583    for (j=0; j<planet_nstack; j++)
3584       planet_rmFlag(&planet_stack[j],PLANET_KNOWN);
3585 }
3586 
3587 
3588 /**
3589  * @brief Clears all system markers.
3590  */
space_clearMarkers(void)3591 void space_clearMarkers (void)
3592 {
3593    int i;
3594    for (i=0; i<systems_nstack; i++) {
3595       sys_rmFlag(&systems_stack[i], SYSTEM_MARKED);
3596       systems_stack[i].markers_computer = 0;
3597       systems_stack[i].markers_plot  = 0;
3598       systems_stack[i].markers_high  = 0;
3599       systems_stack[i].markers_low   = 0;
3600    }
3601 }
3602 
3603 
3604 /**
3605  * @brief Clears all the system computer markers.
3606  */
space_clearComputerMarkers(void)3607 void space_clearComputerMarkers (void)
3608 {
3609    int i;
3610    for (i=0; i<systems_nstack; i++)
3611       sys_rmFlag(&systems_stack[i],SYSTEM_CMARKED);
3612 }
3613 
3614 
3615 /**
3616  * @brief Adds a marker to a system.
3617  *
3618  *    @param sys ID of the system to add marker to.
3619  *    @param type Type of the marker to add.
3620  *    @return 0 on success.
3621  */
space_addMarker(int sys,SysMarker type)3622 int space_addMarker( int sys, SysMarker type )
3623 {
3624    StarSystem *ssys;
3625    int *markers;
3626 
3627    /* Get the system. */
3628    ssys = system_getIndex(sys);
3629    if (ssys == NULL)
3630       return -1;
3631 
3632    /* Get the marker. */
3633    switch (type) {
3634       case SYSMARKER_COMPUTER:
3635          markers = &ssys->markers_computer;
3636          break;
3637       case SYSMARKER_LOW:
3638          markers = &ssys->markers_low;
3639          break;
3640       case SYSMARKER_HIGH:
3641          markers = &ssys->markers_high;
3642          break;
3643       case SYSMARKER_PLOT:
3644          markers = &ssys->markers_plot;
3645          break;
3646       default:
3647          WARN("Unknown marker type.");
3648          return -1;
3649    }
3650 
3651    /* Decrement markers. */
3652    (*markers)++;
3653    sys_setFlag(ssys, SYSTEM_MARKED);
3654 
3655    return 0;
3656 }
3657 
3658 
3659 /**
3660  * @brief Removes a marker from a system.
3661  *
3662  *    @param sys ID of the system to remove marker from.
3663  *    @param type Type of the marker to remove.
3664  *    @return 0 on success.
3665  */
space_rmMarker(int sys,SysMarker type)3666 int space_rmMarker( int sys, SysMarker type )
3667 {
3668    StarSystem *ssys;
3669    int *markers;
3670 
3671    /* Get the system. */
3672    ssys = system_getIndex(sys);
3673    if (ssys == NULL)
3674       return -1;
3675 
3676    /* Get the marker. */
3677    switch (type) {
3678       case SYSMARKER_COMPUTER:
3679          markers = &ssys->markers_computer;
3680          break;
3681       case SYSMARKER_LOW:
3682          markers = &ssys->markers_low;
3683          break;
3684       case SYSMARKER_HIGH:
3685          markers = &ssys->markers_high;
3686          break;
3687       case SYSMARKER_PLOT:
3688          markers = &ssys->markers_plot;
3689          break;
3690       default:
3691          WARN("Unknown marker type.");
3692          return -1;
3693    }
3694 
3695    /* Decrement markers. */
3696    (*markers)--;
3697    if (*markers <= 0) {
3698       sys_rmFlag(ssys, SYSTEM_MARKED);
3699       (*markers) = 0;
3700    }
3701 
3702    return 0;
3703 }
3704 
3705 
3706 /**
3707  * @brief Saves what is needed to be saved for space.
3708  *
3709  *    @param writer XML writer to use.
3710  *    @return 0 on success.
3711  */
space_sysSave(xmlTextWriterPtr writer)3712 int space_sysSave( xmlTextWriterPtr writer )
3713 {
3714    int i;
3715    int j;
3716    StarSystem *sys;
3717 
3718    xmlw_startElem(writer,"space");
3719 
3720    for (i=0; i<systems_nstack; i++) {
3721 
3722       if (!sys_isKnown(&systems_stack[i])) continue; /* not known */
3723 
3724       xmlw_startElem(writer,"known");
3725 
3726       xmlw_attr(writer,"sys","%s",systems_stack[i].name);
3727 
3728       sys = &systems_stack[i];
3729 
3730       for (j=0; j<sys->nplanets; j++) {
3731          if (!planet_isKnown(sys->planets[j])) continue; /* not known */
3732          xmlw_elem(writer,"planet","%s",(sys->planets[j])->name);
3733       }
3734 
3735       for (j=0; j<sys->njumps; j++) {
3736          if (!jp_isKnown(&sys->jumps[j])) continue; /* not known */
3737          xmlw_elem(writer,"jump","%s",(&sys->jumps[j])->target->name);
3738       }
3739 
3740       xmlw_endElem(writer);
3741    }
3742 
3743    xmlw_endElem(writer); /* "space" */
3744 
3745    return 0;
3746 }
3747 
3748 
3749 /**
3750  * @brief Loads player's space properties from an XML node.
3751  *
3752  *    @param parent Parent node for space.
3753  *    @return 0 on success.
3754  */
space_sysLoad(xmlNodePtr parent)3755 int space_sysLoad( xmlNodePtr parent )
3756 {
3757    xmlNodePtr node, cur;
3758    StarSystem *sys;
3759    char *str;
3760 
3761    space_clearKnown();
3762 
3763    node = parent->xmlChildrenNode;
3764    do {
3765       if (xml_isNode(node,"space")) {
3766          cur = node->xmlChildrenNode;
3767 
3768          do {
3769             if (xml_isNode(cur,"known")) {
3770                xmlr_attr(cur,"sys",str);
3771                if (str != NULL) { /* check for 5.0 saves */
3772                   sys = system_get(str);
3773                   free(str);
3774                }
3775                else /* load from 5.0 saves */
3776                   sys = system_get(xml_get(cur));
3777                if (sys != NULL) { /* Must exist */
3778                   sys_setFlag(sys,SYSTEM_KNOWN);
3779                   space_parseAssets(cur, sys);
3780                }
3781             }
3782          } while (xml_nextNode(cur));
3783       }
3784    } while (xml_nextNode(node));
3785 
3786    return 0;
3787 }
3788 
3789 /**
3790  * @brief Parses assets in a system.
3791  *
3792  *    @param parent Node of the system.
3793  *    @return 0 on success.
3794  */
space_parseAssets(xmlNodePtr parent,StarSystem * sys)3795 static int space_parseAssets( xmlNodePtr parent, StarSystem* sys )
3796 {
3797    xmlNodePtr node;
3798    Planet *planet;
3799    JumpPoint *jp;
3800 
3801    node = parent->xmlChildrenNode;
3802 
3803    do {
3804       if (xml_isNode(node,"planet")) {
3805          planet = planet_get(xml_get(node));
3806          if (planet != NULL) /* Must exist */
3807             planet_setKnown(planet);
3808       }
3809       else if (xml_isNode(node,"jump")) {
3810          jp = jump_get(xml_get(node), sys);
3811          if (jp != NULL) /* Must exist */
3812             jp_setFlag(jp,JP_KNOWN);
3813       }
3814    } while (xml_nextNode(node));
3815 
3816    return 0;
3817 }
3818 
3819 /**
3820  * @brief Gets the index of the presence element for a faction.
3821  *          Creates one if it doesn't exist.
3822  *
3823  *    @param sys Pointer to the system to check.
3824  *    @param faction The index of the faction to search for.
3825  *    @return The index of the presence array for faction.
3826  */
getPresenceIndex(StarSystem * sys,int faction)3827 static int getPresenceIndex( StarSystem *sys, int faction )
3828 {
3829    int i;
3830 
3831    /* Check for NULL and display a warning. */
3832    if(sys == NULL) {
3833       WARN("sys == NULL");
3834       return 0;
3835    }
3836 
3837    /* If there is no array, create one and return 0 (the index). */
3838    if (sys->presence == NULL) {
3839       sys->npresence = 1;
3840       sys->presence  = malloc( sizeof(SystemPresence) );
3841 
3842       /* Set the defaults. */
3843       sys->presence[0].faction   = faction;
3844       sys->presence[0].value     = 0 ;
3845       sys->presence[0].curUsed   = 0 ;
3846       sys->presence[0].timer     = 0.;
3847       return 0;
3848    }
3849 
3850    /* Go through the array, looking for the faction. */
3851    for (i = 0; i < sys->npresence; i++)
3852       if (sys->presence[i].faction == faction)
3853          return i;
3854 
3855    /* Grow the array. */
3856    i = sys->npresence;
3857    sys->npresence++;
3858    sys->presence = realloc(sys->presence, sizeof(SystemPresence) * sys->npresence);
3859    sys->presence[i].faction = faction;
3860    sys->presence[i].value = 0;
3861 
3862    return i;
3863 }
3864 
3865 
3866 /**
3867  * @brief Do some cleanup work after presence values have been adjusted.
3868  *
3869  *    @param sys Pointer to the system to cleanup.
3870  */
presenceCleanup(StarSystem * sys)3871 static void presenceCleanup( StarSystem *sys )
3872 {
3873    int i;
3874 
3875    /* Reset the spilled variable for the entire universe. */
3876    for (i=0; i < systems_nstack; i++)
3877       systems_stack[i].spilled = 0;
3878 
3879    /* Check for NULL and display a warning. */
3880    if (sys == NULL) {
3881       WARN("sys == NULL");
3882       return;
3883    }
3884 
3885    /* Check the system for 0 and negative-value presences. */
3886    for (i=0; i < sys->npresence; i++) {
3887       if (sys->presence[i].value > 0.)
3888          continue;
3889 
3890       /* Remove the element with invalid value. */
3891       memmove(&sys->presence[i], &sys->presence[i + 1],
3892               sizeof(SystemPresence) * (sys->npresence - (i + 1)));
3893       sys->npresence--;
3894       sys->presence = realloc(sys->presence, sizeof(SystemPresence) * sys->npresence);
3895       i--;  /* We'll want to check the new value we just copied in. */
3896    }
3897 }
3898 
3899 
3900 /**
3901  * @brief Sloppily sanitize invalid presences across all systems.
3902  */
system_presenceCleanupAll(void)3903 void system_presenceCleanupAll( void )
3904 {
3905    int i;
3906 
3907    for (i=0; i<systems_nstack; i++)
3908       presenceCleanup( &systems_stack[i] );
3909 }
3910 
3911 
3912 /**
3913  * @brief Adds (or removes) some presence to a system.
3914  *
3915  *    @param sys Pointer to the system to add to or remove from.
3916  *    @param faction The index of the faction to alter presence for.
3917  *    @param amount The amount of presence to add (negative to subtract).
3918  *    @param range The range of spill of the presence.
3919  */
system_addPresence(StarSystem * sys,int faction,double amount,int range)3920 void system_addPresence( StarSystem *sys, int faction, double amount, int range )
3921 {
3922    int i, x, curSpill;
3923    Queue q, qn;
3924    StarSystem *cur;
3925 
3926    /* Check for NULL and display a warning. */
3927    if (sys == NULL) {
3928       WARN("sys == NULL");
3929       return;
3930    }
3931 
3932    /* Check that we have a sane faction. (-1 == bobbens == insane)*/
3933    if (faction_isFaction(faction) == 0)
3934       return;
3935 
3936    /* Check that we're actually adding any. */
3937    if (amount == 0)
3938       return;
3939 
3940    /* Add the presence to the current system. */
3941    i = getPresenceIndex(sys, faction);
3942    sys->presence[i].value += amount;
3943 
3944    /* If there's no range, we're done here. */
3945    if (range < 1)
3946       return;
3947 
3948    /* Add the spill. */
3949    sys->spilled   = 1;
3950    curSpill       = 0;
3951    q              = q_create();
3952    qn             = q_create();
3953 
3954    /* Create the initial queue consisting of sys adjacencies. */
3955    for (i=0; i < sys->njumps; i++) {
3956       if (sys->jumps[i].target->spilled == 0 && !jp_isFlag( &sys->jumps[i], JP_HIDDEN ) && !jp_isFlag( &sys->jumps[i], JP_EXITONLY )) {
3957          q_enqueue( q, sys->jumps[i].target );
3958          sys->jumps[i].target->spilled = 1;
3959       }
3960    }
3961 
3962    /* If it's empty, something's wrong. */
3963    if (q_isEmpty(q)) {
3964       /* Means system isn't connected. */
3965       /*WARN("q is empty after getting adjacencies of %s.", sys->name);*/
3966       q_destroy(q);
3967       q_destroy(qn);
3968       presenceCleanup(sys);
3969       return;
3970    }
3971 
3972    while (curSpill < range) {
3973       /* Pull one off the current range queue. */
3974       cur = q_dequeue(q);
3975 
3976       /* Ran out of candidates before running out of spill range! */
3977       if (cur == NULL)
3978          break;
3979 
3980       /* Enqueue all its adjacencies to the next range queue. */
3981       for (i=0; i<cur->njumps; i++) {
3982          if (cur->jumps[i].target->spilled == 0 && !jp_isFlag( &cur->jumps[i], JP_HIDDEN ) && !jp_isFlag( &cur->jumps[i], JP_EXITONLY )) {
3983             q_enqueue( qn, cur->jumps[i].target );
3984             cur->jumps[i].target->spilled = 1;
3985          }
3986       }
3987 
3988       /* Spill some presence. */
3989       x = getPresenceIndex(cur, faction);
3990       cur->presence[x].value += amount / (2 + curSpill);
3991 
3992       /* Check to see if we've finished this range and grab the next queue. */
3993       if (q_isEmpty(q)) {
3994          curSpill++;
3995          q_destroy(q);
3996          q  = qn;
3997          qn = q_create();
3998       }
3999    }
4000 
4001    /* Destroy the queues. */
4002    q_destroy(q);
4003    q_destroy(qn);
4004 
4005    /* Clean up our mess. */
4006    presenceCleanup(sys);
4007 
4008    return;
4009 }
4010 
4011 
4012 /**
4013  * @brief Get the presence of a faction in a system.
4014  *
4015  *    @param sys Pointer to the system to process.
4016  *    @param faction The faction to get the presence for.
4017  *    @return The amount of presence the faction has in the system.
4018  */
system_getPresence(StarSystem * sys,int faction)4019 double system_getPresence( StarSystem *sys, int faction )
4020 {
4021    int i;
4022 
4023    /* Check for NULL and display a warning. */
4024    if(sys == NULL) {
4025       WARN("sys == NULL");
4026       return 0;
4027    }
4028 
4029    /* If there is no array, there is no presence. */
4030    if (sys->presence == NULL)
4031       return 0;
4032 
4033    /* Go through the array, looking for the faction. */
4034    for (i = 0; i < sys->npresence; i++) {
4035       if (sys->presence[i].faction == faction)
4036          return sys->presence[i].value;
4037    }
4038 
4039    /* If it's not in there, it's zero. */
4040    return 0;
4041 }
4042 
4043 
4044 /**
4045  * @brief Go through all the assets and call system_addPresence().
4046  *
4047  *    @param sys Pointer to the system to process.
4048  */
system_addAllPlanetsPresence(StarSystem * sys)4049 void system_addAllPlanetsPresence( StarSystem *sys )
4050 {
4051    int i;
4052 
4053    /* Check for NULL and display a warning. */
4054    if(sys == NULL) {
4055       WARN("sys == NULL");
4056       return;
4057    }
4058 
4059    for(i=0; i<sys->nplanets; i++)
4060       system_addPresence(sys, sys->planets[i]->faction, sys->planets[i]->presenceAmount, sys->planets[i]->presenceRange);
4061 }
4062 
4063 
4064 /**
4065  * @brief Reset the presence of all systems.
4066  */
space_reconstructPresences(void)4067 void space_reconstructPresences( void )
4068 {
4069    int i;
4070 
4071    /* Reset the presence in each system. */
4072    for (i=0; i<systems_nstack; i++) {
4073       if (systems_stack[i].presence)
4074          free(systems_stack[i].presence);
4075       systems_stack[i].presence  = NULL;
4076       systems_stack[i].npresence = 0;
4077       systems_stack[i].ownerpresence = 0.;
4078    }
4079 
4080    /* Re-add presence to each system. */
4081    for (i=0; i<systems_nstack; i++)
4082       system_addAllPlanetsPresence(&systems_stack[i]);
4083 
4084    /* Determine dominant faction. */
4085    for (i=0; i<systems_nstack; i++) {
4086       system_setFaction( &systems_stack[i] );
4087       systems_stack[i].ownerpresence = system_getPresence( &systems_stack[i], systems_stack[i].faction );
4088    }
4089 }
4090 
4091 
4092 /**
4093  * @brief See if the position is in an asteroid field.
4094  *
4095  *    @param p pointer to the position.
4096  *    @return -1 If false; index of the field otherwise.
4097  */
space_isInField(Vector2d * p)4098 int space_isInField ( Vector2d *p )
4099 {
4100    int i, j, k, isin, istotin;
4101    AsteroidAnchor *a;
4102    AsteroidSubset *sub;
4103    double aera;
4104 
4105    istotin = -1;
4106    for (i=0; i < cur_system->nasteroids; i++) {
4107       a = &cur_system->asteroids[i];
4108 
4109       for (k=0; k < a->nsubsets; k++) {
4110          sub = &a->subsets[k];
4111          isin = 1;
4112          /* test every signed aera */
4113          for (j=0; j < sub->ncorners-1; j++) {
4114             aera = (sub->corners[j].x-p->x)*(sub->corners[j+1].y-p->y)
4115                  - (sub->corners[j+1].x-p->x)*(sub->corners[j].y-p->y);
4116             if (sub->aera*aera <= 0) {
4117                isin = 0;
4118                break;
4119             }
4120          }
4121          /* And the last one to loop */
4122          if (sub->ncorners > 0) {
4123             j = sub->ncorners-1;
4124             aera = (sub->corners[j].x-p->x)*(sub->corners[0].y-p->y)
4125                  - (sub->corners[0].x-p->x)*(sub->corners[j].y-p->y);
4126             if (sub->aera*aera <= 0)
4127                isin = 0;
4128          }
4129 
4130          if (isin) {
4131             istotin = i;
4132             break;
4133          }
4134       }
4135    }
4136 
4137    return istotin;
4138 }
4139 
4140 
4141 /**
4142  * @brief See if the system has a planet or station.
4143  *
4144  *    @param sys Pointer to the system to process.
4145  *    @return 0 If empty; otherwise 1.
4146  */
system_hasPlanet(const StarSystem * sys)4147 int system_hasPlanet( const StarSystem *sys )
4148 {
4149    int i;
4150 
4151    /* Check for NULL and display a warning. */
4152    if (sys == NULL) {
4153       WARN("sys == NULL");
4154       return 0;
4155    }
4156 
4157    /* Go through all the assets and look for a real one. */
4158    for (i = 0; i < sys->nplanets; i++)
4159       if (sys->planets[i]->real == ASSET_REAL)
4160          return 1;
4161 
4162    return 0;
4163 }
4164 
4165 
4166 /**
4167  * @brief Removes active presence.
4168  */
system_rmCurrentPresence(StarSystem * sys,int faction,double amount)4169 void system_rmCurrentPresence( StarSystem *sys, int faction, double amount )
4170 {
4171    int id;
4172    nlua_env env;
4173    SystemPresence *presence;
4174 
4175    /* Remove the presence. */
4176    id = getPresenceIndex( cur_system, faction );
4177    sys->presence[id].curUsed -= amount;
4178 
4179    /* Sanity. */
4180    presence = &sys->presence[id];
4181    presence->curUsed = MAX( 0, sys->presence[id].curUsed );
4182 
4183    /* Run lower hook. */
4184    env = faction_getScheduler( faction );
4185 
4186    /* Run decrease function if applicable. */
4187    nlua_getenv( env, "decrease" ); /* f */
4188    if (lua_isnil(naevL,-1)) {
4189       lua_pop(naevL,1);
4190       return;
4191    }
4192    lua_pushnumber( naevL, presence->curUsed ); /* f, cur */
4193    lua_pushnumber( naevL, presence->value );   /* f, cur, max */
4194    lua_pushnumber( naevL, presence->timer );   /* f, cur, max, timer */
4195 
4196    /* Actually run the function. */
4197    if (nlua_pcall(env, 3, 1)) { /* error has occurred */
4198       WARN("Lua decrease script for faction '%s' : %s",
4199             faction_name( faction ), lua_tostring(naevL,-1));
4200       lua_pop(naevL,1);
4201       return;
4202    }
4203 
4204    /* Output is handled the same way. */
4205    if (!lua_isnumber(naevL,-1)) {
4206       WARN("Lua spawn script for faction '%s' failed to return timer value.",
4207             faction_name( presence->faction ) );
4208       lua_pop(naevL,1);
4209       return;
4210    }
4211    presence->timer = lua_tonumber(naevL,-1);
4212    lua_pop(naevL,1);
4213 }
4214 
4215 
4216 
4217