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