naev 0.12.6
space.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
10#include "physfs.h"
11#include <math.h>
12#include <stdlib.h>
13
14#include "naev.h"
16
17#include "space.h"
18
19#include "array.h"
20#include "background.h"
21#include "camera.h"
22#include "conf.h"
23#include "damagetype.h"
24#include "dev_uniedit.h"
25#include "economy.h"
26#include "gatherable.h"
27#include "gui.h"
28#include "hook.h"
29#include "land.h"
30#include "log.h"
31#include "map.h"
32#include "map_overlay.h"
33#include "menu.h"
34#include "mission.h"
35#include "music.h"
36#include "ndata.h"
37#include "nebula.h"
38#include "nlua.h"
39#include "nlua_camera.h"
40#include "nlua_gfx.h"
41#include "nlua_pilot.h"
42#include "nlua_spob.h"
43#include "nlua_tex.h"
44#include "nmath.h"
45#include "nstring.h"
46#include "ntime.h"
47#include "ntracing.h"
48#include "nxml.h"
49#include "pilot.h"
50#include "player.h"
51#include "queue.h"
52#include "rng.h"
53#include "safelanes.h"
54#include "sound.h"
55#include "spfx.h"
56#include "start.h"
57#include "weapon.h"
58
59#define XML_SPOB_TAG "spob"
60#define XML_SYSTEM_TAG "ssys"
61
62#define SPOB_GFX_EXTERIOR_PATH_W 400
63#define SPOB_GFX_EXTERIOR_PATH_H 400
64
65/* used to overcome warnings due to 0 values */
66#define FLAG_POSSET ( 1 << 0 )
67#define FLAG_INTERFERENCESET ( 1 << 1 )
68#define FLAG_SERVICESSET ( 1 << 2 )
69#define FLAG_FACTIONSET ( 1 << 3 )
70
71#define DEBRIS_BUFFER 1000
72
73static const double spob_aa_scale = 2.;
74
75typedef struct spob_lua_file_s {
76 const char *filename;
77 nlua_env env;
78 int lua_mem;
80
82
83/*
84 * spob <-> system name stack
85 */
86static char **spobname_stack =
87 NULL;
88static char **systemname_stack =
89 NULL;
90
91/*
92 * Arrays.
93 */
94StarSystem *systems_stack = NULL;
95static Spob *spob_stack = NULL;
96static VirtualSpob *vspob_stack = NULL;
97/* TODO get rid of the stack_changed stuff, and just redo all the id/pointer
98 * stuff. Main issue will be redoing the Lua system/spob modules to handle such
99 * a case, but can be done with the weapon module as a referenc. */
101 0;
103 0;
104static MapShader **mapshaders = NULL;
105
106/*
107 * Misc.
108 */
109static int systems_loading = 1;
110StarSystem *cur_system = NULL;
112static glTexture *jumpbuoy_gfx = NULL;
113static int space_fchg =
114 0;
115static int space_simulating = 0;
117static Spob *space_landQueueSpob = NULL;
118
119/*
120 * Fleet spawning.
121 */
122int space_spawn = 1;
123
124/*
125 * Internal Prototypes.
126 */
127/* spob load */
128static void spob_initDefaults( Spob *spob );
129static int spob_parse( Spob *spob, const char *filename, Commodity **stdList );
130static int space_parseSaveNodes( xmlNodePtr parent, StarSystem *sys );
131static int spob_parsePresence( xmlNodePtr node, SpobPresence *ap );
132/* system load */
133static void system_init( StarSystem *sys );
134static int systems_load( void );
135static int system_parse( StarSystem *system, const char *filename );
136static int system_parseJumpPoint( const xmlNodePtr node, StarSystem *sys );
137static int system_parseJumps( StarSystem *sys );
138static int system_parseAsteroidField( const xmlNodePtr node, StarSystem *sys );
139static int system_parseAsteroidExclusion( const xmlNodePtr node,
140 StarSystem *sys );
141/* misc */
142static int spob_cmp( const void *p1, const void *p2 );
143static void system_scheduler( double dt, int init );
144static SystemPresence *system_getFactionPresenceGrow( StarSystem *sys,
145 int faction );
146/* Markers. */
147static int space_addMarkerSystem( int sysid, MissionMarkerType type );
148static int space_addMarkerSpob( int pntid, MissionMarkerType type );
149static int space_rmMarkerSystem( int sysid, MissionMarkerType type );
150static int space_rmMarkerSpob( int pntid, MissionMarkerType type );
151/* Render. */
152static void space_renderJumpPoint( const JumpPoint *jp, int i );
153static void space_renderSpob( const Spob *p );
154static void space_updateSpob( const Spob *p, double dt, double real_dt );
155/* Map shaders. */
156static const MapShader *mapshader_get( const char *name );
157/* Lua stuff. */
158static int spob_lua_cmp( const void *a, const void *b );
159static nlua_env spob_lua_get( int *mem, const char *filename );
160static void spob_lua_free( spob_lua_file *lf );
161
169const char *spob_getServiceName( int service )
170{
171 switch ( service ) {
172 case SPOB_SERVICE_LAND:
173 return N_( "Land" );
174 case SPOB_SERVICE_INHABITED:
175 return N_( "Inhabited" );
176 case SPOB_SERVICE_REFUEL:
177 return N_( "Refuel" );
178 case SPOB_SERVICE_BAR:
179 return N_( "Bar" );
180 case SPOB_SERVICE_MISSIONS:
181 return N_( "Missions" );
182 case SPOB_SERVICE_COMMODITY:
183 return N_( "Commodity" );
184 case SPOB_SERVICE_OUTFITS:
185 return N_( "Outfits" );
186 case SPOB_SERVICE_SHIPYARD:
187 return N_( "Shipyard" );
188 case SPOB_SERVICE_BLACKMARKET:
189 return N_( "Blackmarket" );
190 }
191 return NULL;
192}
193
197int spob_getService( const char *name )
198{
199 if ( strcasecmp( name, "Land" ) == 0 )
200 return SPOB_SERVICE_LAND;
201 else if ( strcasecmp( name, "Inhabited" ) == 0 )
202 return SPOB_SERVICE_INHABITED;
203 else if ( strcasecmp( name, "Refuel" ) == 0 )
204 return SPOB_SERVICE_REFUEL;
205 else if ( strcasecmp( name, "Bar" ) == 0 )
206 return SPOB_SERVICE_BAR;
207 else if ( strcasecmp( name, "Missions" ) == 0 )
208 return SPOB_SERVICE_MISSIONS;
209 else if ( strcasecmp( name, "Commodity" ) == 0 )
210 return SPOB_SERVICE_COMMODITY;
211 else if ( strcasecmp( name, "Outfits" ) == 0 )
212 return SPOB_SERVICE_OUTFITS;
213 else if ( strcasecmp( name, "Shipyard" ) == 0 )
214 return SPOB_SERVICE_SHIPYARD;
215 else if ( strcasecmp( name, "Blackmarket" ) == 0 )
216 return SPOB_SERVICE_BLACKMARKET;
217 return -1;
218}
219
226const char *spob_getClassName( const char *class )
227{
228 /* Station Classes. */
229 if ( strcmp( class, "0" ) == 0 )
230 return _( "Civilian Station" );
231 else if ( strcmp( class, "1" ) == 0 )
232 return _( "Military Station" );
233 else if ( strcmp( class, "2" ) == 0 )
234 return _( "Pirate Station" );
235 else if ( strcmp( class, "3" ) == 0 )
236 return _( "Robotic Station" );
237 else if ( strcmp( class, "4" ) == 0 )
238 return _( "Artificial Ecosystem" );
239 /* Planet Classes. */
240 else if ( strcmp( class, "A" ) == 0 )
241 return _( "Geothermal" );
242 else if ( strcmp( class, "B" ) == 0 )
243 return _( "Geomorteus" );
244 else if ( strcmp( class, "C" ) == 0 )
245 return _( "Geoinactive" );
246 else if ( strcmp( class, "D" ) == 0 )
247 return _( "Asteroid/Moon" );
248 else if ( strcmp( class, "E" ) == 0 )
249 return _( "Geoplastic" );
250 else if ( strcmp( class, "F" ) == 0 )
251 return _( "Geometallic" );
252 else if ( strcmp( class, "G" ) == 0 )
253 return _( "Geocrystaline" );
254 else if ( strcmp( class, "H" ) == 0 )
255 return _( "Desert" );
256 else if ( strcmp( class, "I" ) == 0 )
257 return _( "Gas Supergiant" );
258 else if ( strcmp( class, "J" ) == 0 )
259 return _( "Gas Giant" );
260 else if ( strcmp( class, "K" ) == 0 )
261 return _( "Adaptable" );
262 else if ( strcmp( class, "L" ) == 0 )
263 return _( "Marginal" );
264 else if ( strcmp( class, "M" ) == 0 )
265 return _( "Terrestrial" );
266 else if ( strcmp( class, "N" ) == 0 )
267 return _( "Reducing" );
268 else if ( strcmp( class, "O" ) == 0 )
269 return _( "Pelagic" );
270 else if ( strcmp( class, "P" ) == 0 )
271 return _( "Glaciated" );
272 else if ( strcmp( class, "Q" ) == 0 )
273 return _( "Variable" );
274 else if ( strcmp( class, "R" ) == 0 )
275 return _( "Rogue" );
276 else if ( strcmp( class, "S" ) == 0 || strcmp( class, "T" ) == 0 )
277 return _( "Ultragiants" );
278 else if ( strcmp( class, "X" ) == 0 )
279 return _( "Demon" );
280 else if ( strcmp( class, "Y" ) == 0 )
281 return _( "Toxic" );
282 else if ( strcmp( class, "Z" ) == 0 )
283 return _( "Shattered" );
284 return class;
285}
286
293credits_t spob_commodityPrice( const Spob *p, const Commodity *c )
294{
295 const char *sysname = spob_getSystemName( p->name );
296 const StarSystem *sys = system_get( sysname );
297 return economy_getPrice( c, sys, p );
298}
299
307credits_t spob_commodityPriceAtTime( const Spob *p, const Commodity *c,
308 ntime_t t )
309{
310 const char *sysname = spob_getSystemName( p->name );
311 const StarSystem *sys = system_get( sysname );
312 return economy_getPriceAtTime( c, sys, p, t );
313}
314
321void spob_averageSeenPricesAtTime( const Spob *p, const ntime_t tupdate )
322{
323 economy_averageSeenPricesAtTime( p, tupdate );
324}
325
336int spob_averageSpobPrice( const Spob *p, const Commodity *c, credits_t *mean,
337 double *std )
338{
339 return economy_getAverageSpobPrice( c, p, mean, std );
340}
341
347void system_updateAsteroids( StarSystem *sys )
348{
349 double density = 0.;
350 for ( int i = 0; i < array_size( sys->asteroids ); i++ ) {
351 AsteroidAnchor *ast = &sys->asteroids[i];
352 density += ast->area * ast->density / ASTEROID_REF_AREA;
353
354 /* Have to subtract excluded area. */
355 for ( int j = 0; j < array_size( sys->astexclude ); j++ ) {
356 AsteroidExclusion *exc = &sys->astexclude[j];
357 density -= CollideCircleIntersection( &ast->pos, ast->radius,
358 &exc->pos, exc->radius ) *
359 ast->density / ASTEROID_REF_AREA;
360 }
361 }
362 sys->asteroid_density = density;
363}
364
372int spob_setFaction( Spob *p, int faction )
373{
374 p->presence.faction = faction;
375 return 0;
376}
377
386{
387 array_grow( &p->commodities ) = c;
388 array_grow( &p->commodityPrice ).price = c->price;
389 return 0;
390}
391
399int spob_addService( Spob *p, int service )
400{
401 p->services |= service;
402
403 if ( service & SPOB_SERVICE_COMMODITY ) {
404 const char *sysname;
405 StarSystem *sys;
406
407 /* Only try to add standard commodities if there aren't any. */
408 if ( p->commodities != NULL )
409 return 0;
410 Commodity **stdList = standard_commodities();
411 p->commodities = array_create( Commodity * );
412 p->commodityPrice = array_create( CommodityPrice );
413 for ( int i = 0; i < array_size( stdList ); i++ )
414 spob_addCommodity( p, stdList[i] );
415 array_free( stdList );
416
417 /* Clean up economy status. */
420
421 /* Try to figure out the system. */
422 sysname = spob_getSystemName( p->name );
423 if ( sysname == NULL ) {
424 DEBUG( _( "Spob '%s' not in system. Not initializing economy." ),
425 p->name );
426 return 0;
427 }
428 sys = system_get( sysname );
429 economy_initialiseSingleSystem( sys, p );
430 }
431
432 return 0;
433}
434
442int spob_rmService( Spob *p, int service )
443{
444 p->services &= ~service;
445 return 0;
446}
447
455int spob_rename( Spob *p, char *newname )
456{
457 int found = 0;
458 for ( int i = 0; i < array_size( spobname_stack ); i++ ) {
459 if ( strcmp( spobname_stack[i], p->name ) == 0 ) {
460 spobname_stack[i] = newname;
461 found = 1;
462 break;
463 }
464 }
465 if ( !found )
466 WARN( _( "Renaming spob '%s', but not found in name stack!" ), p->name );
467 free( p->name );
468 p->name = newname;
469
470 /* Order changed, so no more bsearch for us. */
472
473 return 0;
474}
475
479int space_jumpDistance( const Pilot *p, const JumpPoint *jp )
480{
481 double r = jp->radius * p->stats.jump_distance;
482 if ( pilot_isFlag(
483 p, PILOT_STEALTH ) ) /* Stealth gives a jump distance bonus. */
484 r *= 3.;
485 return r;
486}
487
495{
496 double d, r;
497 JumpPoint *jp;
498
499 /* Must not have the nojump flag. */
500 if ( pilot_isFlag( p, PILOT_NOJUMP ) )
501 return 0;
502
503 /* Must have fuel. */
504 if ( p->fuel < p->fuel_consumption )
505 return 0;
506
507 /* Must have hyperspace target. */
508 if ( 0 > p->nav_hyperspace ||
509 p->nav_hyperspace >= array_size( cur_system->jumps ) )
510 return 0;
511
512 /* Get the jump. */
513 jp = &cur_system->jumps[p->nav_hyperspace];
514
515 /* Check distance. */
516 r = space_jumpDistance( p, jp );
517 d = vec2_dist2( &p->solid.pos, &jp->pos );
518 if ( d > pow2( r ) )
519 return 0;
520 return 1;
521}
522
530{
531 if ( pilot_isFlag( p, PILOT_NOJUMP ) )
532 return -2;
533 if ( p->fuel < p->fuel_consumption )
534 return -3;
535 if ( !space_canHyperspace( p ) )
536 return -1;
537 if ( pilot_outfitOffAll( p ) > 0 )
538 pilot_calcStats( p );
539
540 /* pilot is now going to get automatically ready for hyperspace */
541 pilot_setFlag( p, PILOT_HYP_PREP );
542 return 0;
543}
544
556int space_calcJumpInPos( const StarSystem *in, const StarSystem *out, vec2 *pos,
557 vec2 *vel, double *dir, const Pilot *p )
558{
559 JumpPoint *jp;
560 double a, d, x, y;
561 double ea, ed;
562
563 /* Find the entry system. */
564 jp = NULL;
565 for ( int i = 0; i < array_size( in->jumps ); i++ )
566 if ( in->jumps[i].target == out )
567 jp = &in->jumps[i];
568
569 /* Must have found the jump. */
570 if ( jp == NULL ) {
571 WARN( _( "Unable to find jump in point for '%s' in '%s': not connected" ),
572 out->name, in->name );
573 return -1;
574 }
575
576 /* Base position target. */
577 x = jp->pos.x;
578 y = jp->pos.y;
579
580 /* Calculate offset from target position. */
581 a = 2. * M_PI - jp->angle;
582 d = RNGF() * ( HYPERSPACE_ENTER_MAX - HYPERSPACE_ENTER_MIN ) +
583 HYPERSPACE_ENTER_MIN;
584 if ( ( p != NULL ) && pilot_isFlag( p, PILOT_STEALTH ) )
585 d *= 1.4; /* Jump in from further out when coming in from stealth. */
586
587 /* Calculate new position. */
588 x += d * cos( a );
589 y += d * sin( a );
590
591 /* Add some error. */
592 ea = 2. * M_PI * RNGF();
593 ed = jp->radius / 2.;
594 if ( p != NULL ) {
595 ed *= p->stats.jump_distance; /* larger variability. */
596 if ( pilot_isFlag( p, PILOT_STEALTH ) )
597 ed *= 2.;
598 }
599 x += ed * cos( ea );
600 y += ed * sin( ea );
601
602 /* Set new position. */
603 vec2_cset( pos, x, y );
604
605 /* Set new velocity. */
606 a += M_PI;
607 vec2_cset( vel, HYPERSPACE_VEL * cos( a ), HYPERSPACE_VEL * sin( a ) );
608
609 /* Set direction. */
610 *dir = angle_clean( a );
611
612 return 0;
613}
614
623char **space_getFactionSpob( const int *factions, int landable )
624{
625 char **tmp = array_create( char * );
626 for ( int i = 0; i < array_size( systems_stack ); i++ ) {
627 for ( int j = 0; j < array_size( systems_stack[i].spobs ); j++ ) {
628 Spob *spob = systems_stack[i].spobs[j];
629 int f = 0;
630 for ( int k = 0; k < array_size( factions ); k++ ) {
631 if ( spob->presence.faction == factions[k] ) {
632 f = 1;
633 break;
634 }
635 }
636 if ( !f )
637 continue;
638
639 /* Check landable. */
640 if ( landable ) {
641 spob_updateLand( spob );
642 if ( !spob->can_land )
643 continue;
644 }
645
646 /* This is expensive so we probably want to do it last. */
647 if ( !space_sysReallyReachable( systems_stack[i].name ) )
648 continue;
649
650 array_push_back( &tmp, spob->name );
651 break; /* no need to check all factions */
652 }
653 }
654
655 return tmp;
656}
657
666const char *space_getRndSpob( int landable, unsigned int services,
667 int ( *filter )( Spob *p ) )
668{
669 char *res = NULL;
670 Spob **tmp = array_create( Spob * );
671
672 for ( int i = 0; i < array_size( systems_stack ); i++ ) {
673 for ( int j = 0; j < array_size( systems_stack[i].spobs ); j++ ) {
674 Spob *pnt = systems_stack[i].spobs[j];
675
676 if ( services && ( spob_hasService( pnt, services ) != services ) )
677 continue;
678
679 if ( ( filter != NULL ) && !filter( pnt ) )
680 continue;
681
682 array_push_back( &tmp, pnt );
683 }
684 }
685
686 /* Second filter. */
687 arrayShuffle( (void **)tmp );
688 for ( int i = 0; i < array_size( tmp ); i++ ) {
689 Spob *pnt = tmp[i];
690
691 /* We put expensive calculations here to minimize executions. */
692 if ( landable ) {
693 spob_updateLand( pnt );
694 if ( !pnt->can_land )
695 continue;
696 }
698 continue;
699
700 /* We want the name, not the actual spob. */
701 res = tmp[i]->name;
702 break;
703 }
704 array_free( tmp );
705
706 return res;
707}
708
723double system_getClosest( const StarSystem *sys, int *pnt, int *jp, int *ast,
724 int *fie, double x, double y )
725{
726 double d = HUGE_VAL;
727
728 /* Default output. */
729 *pnt = -1;
730 *jp = -1;
731 *ast = -1;
732 *fie = -1;
733
734 /* Spobs. */
735 for ( int i = 0; i < array_size( sys->spobs ); i++ ) {
736 double td;
737 Spob *p = sys->spobs[i];
738 if ( !spob_isKnown( p ) )
739 continue;
740 td = pow2( x - p->pos.x ) + pow2( y - p->pos.y );
741 if ( td < d ) {
742 *pnt = i;
743 d = td;
744 }
745 }
746
747 /* Asteroids. */
748 for ( int i = 0; i < array_size( sys->asteroids ); i++ ) {
749 AsteroidAnchor *f = &sys->asteroids[i];
750 for ( int k = 0; k < array_size( f->asteroids ); k++ ) {
751 double td;
752 Asteroid *as = &f->asteroids[k];
753
754 /* Skip non-interactive asteroids. */
755 if ( as->state != ASTEROID_FG )
756 continue;
757
758 /* Skip out of range asteroids */
759 if ( !pilot_inRangeAsteroid( player.p, k, i ) )
760 continue;
761
762 td = pow2( x - as->sol.pos.x ) + pow2( y - as->sol.pos.y );
763 if ( td < d ) {
764 *pnt = -1; /* We must clear spob target as asteroid is closer. */
765 *ast = k;
766 *fie = i;
767 d = td;
768 }
769 }
770 }
771
772 /* Jump points. */
773 for ( int i = 0; i < array_size( sys->jumps ); i++ ) {
774 double td;
775 JumpPoint *j = &sys->jumps[i];
776 if ( !jp_isUsable( j ) )
777 continue;
778 td = pow2( x - j->pos.x ) + pow2( y - j->pos.y );
779 if ( td < d ) {
780 *pnt = -1; /* We must clear spob target as jump point is closer. */
781 *ast = -1;
782 *fie = -1;
783 *jp = i;
784 d = td;
785 }
786 }
787 return d;
788}
789
808double system_getClosestAng( const StarSystem *sys, int *pnt, int *jp, int *ast,
809 int *fie, double x, double y, double ang )
810{
811 double a;
812
813 /* Default output. */
814 *pnt = -1;
815 *jp = -1;
816 a = ang + M_PI;
817
818 /* Spobs. */
819 for ( int i = 0; i < array_size( sys->spobs ); i++ ) {
820 Spob *p = sys->spobs[i];
821 double ta = atan2( y - p->pos.y, x - p->pos.x );
822 if ( ABS( angle_diff( ang, ta ) ) < ABS( angle_diff( ang, a ) ) ) {
823 *pnt = i;
824 a = ta;
825 }
826 }
827
828 /* Asteroids. */
829 for ( int i = 0; i < array_size( sys->asteroids ); i++ ) {
830 AsteroidAnchor *f = &sys->asteroids[i];
831 for ( int k = 0; k < array_size( f->asteroids ); k++ ) {
832 double ta;
833 Asteroid *as = &f->asteroids[k];
834
835 /* Skip non-interactive asteroids. */
836 if ( as->state != ASTEROID_FG )
837 continue;
838
839 ta = atan2( y - as->sol.pos.y, x - as->sol.pos.x );
840 if ( ABS( angle_diff( ang, ta ) ) < ABS( angle_diff( ang, a ) ) ) {
841 *pnt = -1; /* We must clear spob target as asteroid is closer. */
842 *ast = k;
843 *fie = i;
844 a = ta;
845 }
846 }
847 }
848
849 /* Jump points. */
850 for ( int i = 0; i < array_size( sys->jumps ); i++ ) {
851 JumpPoint *j = &sys->jumps[i];
852 double ta = atan2( y - j->pos.y, x - j->pos.x );
853 if ( ABS( angle_diff( ang, ta ) ) < ABS( angle_diff( ang, a ) ) ) {
854 *ast = -1;
855 *fie = -1;
856 *pnt = -1; /* We must clear the rest as jump point is closer. */
857 *jp = i;
858 a = ta;
859 }
860 }
861 return a;
862}
863
869int space_sysReachable( const StarSystem *sys )
870{
871 if ( sys_isKnown( sys ) )
872 return 1; /* it is known */
873
874 /* check to see if it is adjacent to known */
875 for ( int i = 0; i < array_size( sys->jumps ); i++ ) {
876 const JumpPoint *jp = sys->jumps[i].returnJump;
877 if ( jp && jp_isUsable( jp ) )
878 return 1;
879 }
880
881 return 0;
882}
883
889int space_sysReallyReachable( const char *sysname )
890{
891 StarSystem **path, *goal;
892
893 if ( strcmp( sysname, cur_system->name ) == 0 )
894 return 1;
895 goal = system_get( sysname );
896 if ( goal == NULL )
897 return 0;
898 path = map_getJumpPath( cur_system, NULL, goal, 1, 1, NULL, NULL );
899 if ( path != NULL ) {
900 array_free( path );
901 return 1;
902 }
903 return 0;
904}
905
911int space_sysReachableFromSys( const StarSystem *target, const StarSystem *sys )
912{
913 /* check to see if sys contains a known jump point to target */
914 const JumpPoint *jp = jump_getTarget( target, sys );
915 if ( jp == NULL )
916 return 0;
917 else if ( jp_isUsable( jp ) )
918 return 1;
919 return 0;
920}
921
925StarSystem *system_getAll( void )
926{
927 return systems_stack;
928}
929
936const char *system_existsCase( const char *sysname )
937{
938 for ( int i = 0; i < array_size( systems_stack ); i++ )
939 if ( strcasecmp( sysname, systems_stack[i].name ) == 0 )
940 return systems_stack[i].name;
941 return NULL;
942}
943
948char **system_searchFuzzyCase( const char *sysname, int *n )
949{
950 int len;
951 char **names;
952
953 /* Overallocate to maximum. */
954 names = malloc( sizeof( char * ) * array_size( systems_stack ) );
955
956 /* Do fuzzy search. */
957 len = 0;
958 for ( int i = 0; i < array_size( systems_stack ); i++ ) {
959 StarSystem *sys = &systems_stack[i];
960 if ( SDL_strcasestr( system_name( sys ), sysname ) != NULL ) {
961 names[len] = sys->name;
962 len++;
963 } else if ( ( sys->features != NULL ) &&
964 SDL_strcasestr( _( sys->features ), sysname ) != NULL ) {
965 names[len] = sys->name;
966 len++;
967 } else {
968 for ( int j = 0; j < array_size( sys->spobs ); j++ ) {
969 const Spob *spob = sys->spobs[j];
970 if ( ( spob->feature != NULL ) &&
971 SDL_strcasestr( _( spob->feature ), sysname ) != NULL ) {
972 names[len] = sys->name;
973 len++;
974 break;
975 }
976 }
977 }
978 }
979
980 /* Free if empty. */
981 if ( len == 0 ) {
982 free( names );
983 names = NULL;
984 }
985
986 *n = len;
987 return names;
988}
989
993static int system_cmp( const void *p1, const void *p2 )
994{
995 const StarSystem *s1, *s2;
996 s1 = (const StarSystem *)p1;
997 s2 = (const StarSystem *)p2;
998 return strcmp( s1->name, s2->name );
999}
1000
1007StarSystem *system_get( const char *sysname )
1008{
1009 if ( sysname == NULL )
1010 return NULL;
1011
1012 /* Somethig was added, and since we store IDs too, everything can't be sorted
1013 * anymore by name... */
1014 if ( systemstack_changed ) {
1015 for ( int i = 0; i < array_size( systems_stack ); i++ )
1016 if ( strcmp( systems_stack[i].name, sysname ) == 0 )
1017 return &systems_stack[i];
1018 WARN( _( "System '%s' not found in stack" ), sysname );
1019 return NULL;
1020 }
1021
1022 const StarSystem s = { .name = (char *)sysname };
1023 StarSystem *found = bsearch( &s, systems_stack, array_size( systems_stack ),
1024 sizeof( StarSystem ), system_cmp );
1025 if ( found != NULL )
1026 return found;
1027
1028 WARN( _( "System '%s' not found in stack" ), sysname );
1029 return NULL;
1030}
1031
1038StarSystem *system_getIndex( int id )
1039{
1040 return &systems_stack[id];
1041}
1042
1049int system_index( const StarSystem *sys )
1050{
1051 return sys->id;
1052}
1053
1060int spob_hasSystem( const Spob *spb )
1061{
1062 for ( int i = 0; i < array_size( spobname_stack ); i++ )
1063 if ( strcmp( spobname_stack[i], spb->name ) == 0 )
1064 return 1;
1065 return 0;
1066}
1067
1071StarSystem *spob_getSystem( const Spob *spob )
1072{
1073 return system_get( spob_getSystemName( spob->name ) );
1074}
1075
1082const char *spob_getSystemName( const char *spobname )
1083{
1084 for ( int i = 0; i < array_size( spobname_stack ); i++ )
1085 if ( strcmp( spobname_stack[i], spobname ) == 0 )
1086 return systemname_stack[i];
1087 DEBUG( _( "Spob '%s' is not placed in a system" ), spobname );
1088 return NULL;
1089}
1090
1094static int spob_cmp( const void *p1, const void *p2 )
1095{
1096 const Spob *spb1 = p1;
1097 const Spob *spb2 = p2;
1098 return strcmp( spb1->name, spb2->name );
1099}
1100
1107Spob *spob_get( const char *spobname )
1108{
1109 if ( spobname == NULL ) {
1110 WARN( _( "Trying to find NULL spob…" ) );
1111 return NULL;
1112 }
1113
1114 /* Somethig was added, and since we store IDs too, everything can't be sorted
1115 * anymore by name... */
1116 if ( spobstack_changed ) {
1117 for ( int i = 0; i < array_size( spob_stack ); i++ )
1118 if ( strcmp( spob_stack[i].name, spobname ) == 0 )
1119 return &spob_stack[i];
1120 WARN( _( "Spob '%s' not found in the universe" ), spobname );
1121 return NULL;
1122 }
1123
1124 const Spob p = { .name = (char *)spobname };
1125 Spob *found = bsearch( &p, spob_stack, array_size( spob_stack ),
1126 sizeof( Spob ), spob_cmp );
1127 if ( found != NULL )
1128 return found;
1129
1130 WARN( _( "Spob '%s' not found in the universe" ), spobname );
1131 return NULL;
1132}
1133
1141{
1142 /* Validity check. */
1143 if ( ( ind < 0 ) || ( ind >= array_size( spob_stack ) ) ) {
1144 WARN( _( "Spob index '%d' out of range (max %d)" ), ind,
1146 return NULL;
1147 }
1148
1149 return &spob_stack[ind];
1150}
1151
1158int spob_index( const Spob *p )
1159{
1160 return p->id;
1161}
1162
1167{
1168 return spob_stack;
1169}
1170
1175{
1176 spob_setFlag( p, SPOB_KNOWN );
1177}
1178
1185int spob_exists( const char *spobname )
1186{
1187 for ( int i = 0; i < array_size( spob_stack ); i++ )
1188 if ( strcmp( spob_stack[i].name, spobname ) == 0 )
1189 return 1;
1190 return 0;
1191}
1192
1199const char *spob_existsCase( const char *spobname )
1200{
1201 for ( int i = 0; i < array_size( spob_stack ); i++ )
1202 if ( strcasecmp( spob_stack[i].name, spobname ) == 0 )
1203 return spob_stack[i].name;
1204 return NULL;
1205}
1206
1211char **spob_searchFuzzyCase( const char *spobname, int *n )
1212{
1213 /* Overallocate to maximum. */
1214 char **names = malloc( sizeof( char * ) * array_size( spob_stack ) );
1215
1216 /* Do fuzzy search. */
1217 int len = 0;
1218 for ( int i = 0; i < array_size( spob_stack ); i++ ) {
1219 Spob *spob = &spob_stack[i];
1220 if ( SDL_strcasestr( spob_name( spob ), spobname ) != NULL ) {
1221 names[len] = spob->name;
1222 len++;
1223 } else if ( ( spob->feature != NULL ) &&
1224 SDL_strcasestr( _( spob->feature ), spobname ) != NULL ) {
1225 names[len] = spob->name;
1226 len++;
1227 }
1228 }
1229
1230 /* Free if empty. */
1231 if ( len == 0 ) {
1232 free( names );
1233 names = NULL;
1234 }
1235
1236 *n = len;
1237 return names;
1238}
1239
1244{
1245 return vspob_stack;
1246}
1247
1251static int virtualspob_cmp( const void *p1, const void *p2 )
1252{
1253 const VirtualSpob *v1 = p1;
1254 const VirtualSpob *v2 = p2;
1255 return strcmp( v1->name, v2->name );
1256}
1257
1261VirtualSpob *virtualspob_get( const char *name )
1262{
1263 const VirtualSpob va = { .name = (char *)name };
1264 VirtualSpob *found = bsearch( &va, vspob_stack, array_size( vspob_stack ),
1265 sizeof( VirtualSpob ), virtualspob_cmp );
1266 if ( found != NULL )
1267 return found;
1268 WARN( _( "Virtual Spob '%s' not found in the universe" ), name );
1269 return NULL;
1270}
1271
1279JumpPoint *jump_get( const char *jumpname, const StarSystem *sys )
1280{
1281 if ( jumpname == NULL ) {
1282 WARN( _( "Trying to find NULL jump point..." ) );
1283 return NULL;
1284 }
1285
1286 for ( int i = 0; i < array_size( sys->jumps ); i++ ) {
1287 JumpPoint *jp = &sys->jumps[i];
1288 if ( strcmp( jp->target->name, jumpname ) == 0 )
1289 return jp;
1290 }
1291
1292 WARN( _( "Jump point '%s' not found in %s" ), jumpname, sys->name );
1293 return NULL;
1294}
1295
1303JumpPoint *jump_getTarget( const StarSystem *target, const StarSystem *sys )
1304{
1305 for ( int i = 0; i < array_size( sys->jumps ); i++ ) {
1306 JumpPoint *jp = &sys->jumps[i];
1307 if ( jp->target->id == target->id )
1308 return jp;
1309 }
1310 WARN( _( "Jump point to '%s' not found in %s" ), target->name, sys->name );
1311 return NULL;
1312}
1313
1317const char *jump_getSymbol( const JumpPoint *jp )
1318{
1319 if ( jp_isFlag( jp, JP_HIDDEN ) )
1320 return "* ";
1321 return "";
1322}
1323
1330static void system_scheduler( double dt, int init )
1331{
1332 NTracingZone( _ctx, 1 );
1333
1334 /* Go through all the factions and reduce the timer. */
1335 for ( int i = 0; i < array_size( cur_system->presence ); i++ ) {
1336 int n;
1337 nlua_env env;
1338 SystemPresence *p = &cur_system->presence[i];
1339 if ( p->value <= 0. )
1340 continue;
1341
1342 env = faction_getScheduler( p->faction );
1343
1344 /* Must have a valid scheduler. */
1345 if ( env == LUA_NOREF )
1346 continue;
1347
1348 /* Spawning is disabled for this faction. */
1349 if ( p->disabled )
1350 continue;
1351
1352 /* Run the appropriate function. */
1353 if ( init ) {
1354 nlua_getenv( naevL, env, "create" ); /* f */
1355 if ( lua_isnil( naevL, -1 ) ) {
1356 WARN( _( "Lua Spawn script for faction '%s' missing obligatory "
1357 "entry point 'create'." ),
1358 faction_name( p->faction ) );
1359 lua_pop( naevL, 1 );
1360 continue;
1361 }
1362 n = 0;
1363 } else {
1364 /* Decrement dt, only continue */
1365 p->timer -= dt;
1366 if ( p->timer >= 0. )
1367 continue;
1368
1369 nlua_getenv( naevL, env, "spawn" ); /* f */
1370 if ( lua_isnil( naevL, -1 ) ) {
1371 WARN( _( "Lua Spawn script for faction '%s' missing obligatory "
1372 "entry point 'spawn'." ),
1373 faction_name( p->faction ) );
1374 lua_pop( naevL, 1 );
1375 continue;
1376 }
1377 lua_pushnumber( naevL, p->curUsed ); /* f, presence */
1378 n = 1;
1379 }
1380 lua_pushnumber( naevL, p->value ); /* f, [arg,], max */
1381
1382 /* Actually run the function. */
1383 if ( nlua_pcall( env, n + 1, 2 ) ) { /* error has occurred */
1384 WARN( _( "Lua Spawn script for faction '%s' : %s" ),
1385 faction_name( p->faction ), lua_tostring( naevL, -1 ) );
1386 lua_pop( naevL, 1 );
1387 continue;
1388 }
1389
1390 /* Output is handled the same way. */
1391 if ( !lua_isnumber( naevL, -2 ) ) {
1392 WARN( _( "Lua spawn script for faction '%s' failed to return timer "
1393 "value." ),
1394 faction_name( p->faction ) );
1395 lua_pop( naevL, 2 );
1396 continue;
1397 }
1398 p->timer += lua_tonumber( naevL, -2 );
1399 /* Handle table if it exists. */
1400 if ( lua_istable( naevL, -1 ) ) {
1401 lua_pushnil( naevL ); /* tk, k */
1402 while ( lua_next( naevL, -2 ) != 0 ) { /* tk, k, v */
1403 Pilot *pilot;
1404
1405 /* Must be table. */
1406 if ( !lua_istable( naevL, -1 ) ) {
1407 WARN( _( "Lua spawn script for faction '%s' returns invalid "
1408 "data (not a table)." ),
1409 faction_name( p->faction ) );
1410 lua_pop( naevL, 2 ); /* tk, k */
1411 continue;
1412 }
1413
1414 lua_getfield( naevL, -1, "pilot" ); /* tk, k, v, p */
1415 if ( !lua_ispilot( naevL, -1 ) ) {
1416 WARN( _( "Lua spawn script for faction '%s' returns invalid "
1417 "data (not a pilot)." ),
1418 faction_name( p->faction ) );
1419 lua_pop( naevL, 2 ); /* tk, k */
1420 continue;
1421 }
1422 pilot = pilot_get( lua_topilot( naevL, -1 ) );
1423 if ( pilot == NULL ) {
1424 lua_pop( naevL, 2 ); /* tk, k */
1425 continue;
1426 }
1427 lua_pop( naevL, 1 ); /* tk, k, v */
1428 lua_getfield( naevL, -1, "presence" ); /* tk, k, v, p */
1429 if ( !lua_isnumber( naevL, -1 ) ) {
1430 WARN( _( "Lua spawn script for faction '%s' returns invalid "
1431 "data (not a number)." ),
1432 faction_name( p->faction ) );
1433 lua_pop( naevL, 2 ); /* tk, k */
1434 continue;
1435 }
1436 pilot->presence = lua_tonumber( naevL, -1 );
1437 pilot->faction_spawn =
1438 p->faction; /* Save the faction who spawned it. */
1439 if ( pilot->faction != p->faction ) {
1440 WARN( _( "Lua spawn script for faction '%s' actually spawned a "
1441 "'%s' pilot." ),
1442 faction_name( p->faction ),
1443 faction_name( pilot->faction ) );
1444 p = system_getFactionPresence( cur_system, pilot->faction );
1445 }
1446 if ( p != NULL )
1447 p->curUsed += pilot->presence;
1448 lua_pop( naevL, 2 ); /* tk, k */
1449 }
1450 }
1451 lua_pop( naevL, 2 );
1452 }
1453
1454 NTracingZoneEnd( _ctx );
1455}
1456
1461{
1462 space_fchg = 1;
1463}
1464
1469{
1470 if ( space_landQueueSpob != NULL ) {
1471 land( space_landQueueSpob, 0 );
1472 space_landQueueSpob = NULL;
1473 }
1474}
1475
1482void space_update( double dt, double real_dt )
1483{
1484 /* Needs a current system. */
1485 if ( cur_system == NULL )
1486 return;
1487
1488 NTracingZone( _ctx, 1 );
1489
1490 /* If spawning is enabled, call the scheduler. */
1491 if ( space_spawn )
1492 system_scheduler( dt, 0 );
1493
1494 /*
1495 * Nebula.
1496 */
1497 nebu_update( dt );
1498 if ( cur_system->nebu_volatility > 0. ) {
1499 Pilot *const *pilot_stack = pilot_getAll();
1500 Damage dmg;
1501 dmg.type = dtype_get( "nebula" );
1502 dmg.damage = cur_system->nebu_volatility * dt;
1503 dmg.penetration = 1.; /* Full penetration. */
1504 dmg.disable = 0.;
1505
1506 /* Damage pilots in volatile systems. */
1507 for ( int i = 0; i < array_size( pilot_stack ); i++ )
1508 pilot_hit( pilot_stack[i], NULL, NULL, &dmg, NULL, LUA_NOREF, 0 );
1509 }
1510
1511 /* Faction updates. */
1512 if ( space_fchg ) {
1513 for ( int i = 0; i < array_size( cur_system->spobs ); i++ )
1514 spob_updateLand( cur_system->spobs[i] );
1515
1516 /* Verify land authorization is still valid. */
1517 if ( ( player.p != NULL ) && ( player.p->nav_spob >= 0 ) &&
1518 player_isFlag( PLAYER_LANDACK ) )
1520
1522 space_fchg = 0;
1523 }
1524
1525 if ( !space_simulating ) {
1526 int found_something = 0;
1527 /* Spob updates */
1528 for ( int i = 0; i < array_size( cur_system->spobs ); i++ ) {
1529 HookParam hparam[3];
1530 Spob *pnt = cur_system->spobs[i];
1531
1532 /* Must update in some cases. */
1533 space_updateSpob( pnt, dt, real_dt );
1534
1535 /* Discovering is disabled. */
1536 if ( player.discover_off )
1537 continue;
1538
1539 /* Handle discoveries. */
1540 if ( spob_isKnown( pnt ) || !pilot_inRangeSpob( player.p, i ) )
1541 continue;
1542
1543 spob_setKnown( pnt );
1544 player_message( _( "You discovered #%c%s#0." ),
1545 spob_getColourChar( pnt ), spob_name( pnt ) );
1546 hparam[0].type = HOOK_PARAM_STRING;
1547 hparam[0].u.str = "spob";
1548 hparam[1].type = HOOK_PARAM_SPOB;
1549 hparam[1].u.la = pnt->id;
1550 hparam[2].type = HOOK_PARAM_SENTINEL;
1551 hooks_runParam( "discover", hparam );
1552 found_something = 1;
1553 pnt->map_alpha = 0.;
1554 }
1555
1556 /* Jump point updates */
1557 for ( int i = 0; i < array_size( cur_system->jumps ); i++ ) {
1558 HookParam hparam[3];
1559 JumpPoint *jp = &cur_system->jumps[i];
1560
1561 /* Discovering is disabled. */
1562 if ( player.discover_off )
1563 continue;
1564
1565 if ( jp_isKnown( jp ) )
1566 continue;
1567 if ( jp_isFlag( jp, JP_EXITONLY ) )
1568 continue;
1569 if ( !( pilot_inRangeJump( player.p, i ) ) )
1570 continue;
1571
1572 jp_setFlag( jp, JP_KNOWN );
1573 player_message( _( "You discovered a Jump Point." ) );
1574 hparam[0].type = HOOK_PARAM_STRING;
1575 hparam[0].u.str = "jump";
1576 hparam[1].type = HOOK_PARAM_JUMP;
1577 hparam[1].u.lj.srcid = cur_system->id;
1578 hparam[1].u.lj.destid = jp->target->id;
1579 hparam[2].type = HOOK_PARAM_SENTINEL;
1580 hooks_runParam( "discover", hparam );
1581 found_something = 1;
1582 jp->map_alpha = 0.;
1583 }
1584
1585 if ( found_something )
1586 ovr_refresh();
1587 }
1588
1589 /* Update the gatherable objects. */
1590 gatherable_update( dt );
1591
1592 /* Asteroids/Debris update */
1593 asteroids_update( dt );
1594
1595 NTracingZoneEnd( _ctx );
1596}
1597
1602{
1603 return space_simulating;
1604}
1605
1610{
1612}
1613
1620void space_init( const char *sysname, int do_simulate )
1621{
1622 const double fps_min_simulation = fps_min;
1623 const StarSystem *oldsys = cur_system;
1624
1625 NTracingFrameMarkStart( "space_init" );
1626 NTracingZone( _ctx, 1 );
1627#if HAVE_TRACY
1628 char buf[STRMAX_SHORT];
1629 size_t l = snprintf( buf, sizeof( buf ), "Entering system '%s'", sysname );
1630 NTracingMessage( buf, l );
1631#endif /* TRACY */
1632
1633 /* Clean up some stuff and reset some global states. */
1634 player_clear(); /* Clears targets the player has selected. */
1635 ovr_mrkClear(); /* Clear markers when jumping. */
1636 pilots_clean( 1 ); /* Destroy non-persistent pilots */
1637 weapon_clear(); /* get rid of all the weapons */
1638 spfx_clear(); /* get rid of the explosions */
1639 gatherable_free(); /* get rid of gatherable stuff. */
1640 background_clear(); /* Get rid of the background. */
1641 factions_clearDynamic(); /* get rid of dynamic factions. */
1642 space_spawn = 1; /* Spawn is enabled by default. */
1643
1644 /* Clear persistent pilot stuff. */
1645 if ( player.p != NULL ) {
1646 Pilot *const *pilot_stack = pilot_getAll();
1647 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
1648 Pilot *p = pilot_stack[i];
1649 pilot_lockClear( p );
1650 pilot_clearTimers( p ); /* Clear timers. */
1651 }
1652 }
1653
1654 if ( ( sysname == NULL ) && ( cur_system == NULL ) ) {
1655 WARN(
1656 _( "Cannot reinit system if there is no system previously loaded" ) );
1657 /* Who knows what'll happen... */
1658 return;
1659 } else if ( sysname != NULL ) {
1660 char dmgstr[32];
1661
1662 cur_system = system_get( sysname );
1663 if ( cur_system == NULL ) {
1664 WARN( _( "System '%s' not found, trying random system!" ), sysname );
1665 cur_system = &systems_stack[RNG( 0, array_size( systems_stack ) - 1 )];
1666 }
1667 char *nt = ntime_pretty( 0, 2 );
1668
1669 if ( sys_isFlag( cur_system, SYSTEM_HIDENEBULADAMAGE ) )
1670 snprintf( dmgstr, sizeof( dmgstr ),
1671 p_( "nebula_volatility", "??? %s" ), UNIT_POWER );
1672 else
1673 snprintf( dmgstr, sizeof( dmgstr ),
1674 p_( "nebula_volatility", "%.1f %s" ),
1675 cur_system->nebu_volatility, UNIT_POWER );
1676 player_message( _( "#oEntering System %s on %s." ), _( sysname ), nt );
1677 if ( cur_system->nebu_volatility > 0. )
1678 player_message( _( "#rWARNING - Volatile nebula detected in %s! "
1679 "Taking %s damage!" ),
1680 _( sysname ), dmgstr );
1681 free( nt );
1682 }
1683
1684 /* Update after setting cur_system. */
1685 if ( ( oldsys != NULL && oldsys->stats != NULL ) ||
1686 cur_system->stats != NULL ) {
1687 Pilot *const *pilot_stack = pilot_getAll();
1688 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
1689 Pilot *p = pilot_stack[i];
1690 pilot_calcStats( p );
1691 if ( pilot_isWithPlayer( p ) )
1692 pilot_setFlag( p, PILOT_HIDE );
1693 }
1694 }
1695
1696 /* Set up spobs. */
1697 for ( int i = 0; i < array_size( cur_system->spobs ); i++ ) {
1698 Spob *pnt = cur_system->spobs[i];
1699 pnt->land_override = 0;
1700 spob_updateLand( pnt );
1701 }
1702
1703 /* See if we should get a new music song. */
1704 if ( ( player.p != NULL ) && do_simulate )
1705 music_choose( NULL );
1706
1707 /* Reset new trails and set up quadtrees. */
1710
1711 /* Reset any schedules and used presence. */
1712 for ( int i = 0; i < array_size( cur_system->presence ); i++ ) {
1713 cur_system->presence[i].curUsed = 0;
1714 cur_system->presence[i].timer = 0.;
1715 cur_system->presence[i].disabled = 0;
1716 }
1717
1718 /* Load graphics. */
1720
1721 /* Call the scheduler. */
1722 system_scheduler( 0., 1 );
1723
1724 /* we now know this system */
1725 sys_setFlag( cur_system, SYSTEM_KNOWN );
1726
1727 NTracingZoneName( _ctx_simulating, "space_init[simulation]", 1 );
1728 /* Simulate system. */
1729 space_simulating = 1;
1731 asteroids_init(); /* Set up asteroids. */
1732 if ( player.p != NULL ) {
1733 Pilot *const *pilot_stack = pilot_getAll();
1734 pilot_setFlag( player.p, PILOT_HIDE );
1735 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
1736 Pilot *p = pilot_stack[i];
1737 if ( pilot_isWithPlayer( p ) )
1738 pilot_setFlag( p, PILOT_HIDE );
1739 }
1740 }
1742 if ( do_simulate ) {
1743 int n, s;
1744 /* Uint32 time = SDL_GetTicks(); */
1745 s = sound_disabled;
1746 sound_disabled = 1;
1747 ntime_allowUpdate( 0 );
1748 n = SYSTEM_SIMULATE_TIME_PRE / fps_min_simulation;
1749 for ( int i = 0; i < n; i++ )
1750 update_routine( fps_min_simulation, 0 );
1752 n = SYSTEM_SIMULATE_TIME_POST / fps_min_simulation;
1753 for ( int i = 0; i < n; i++ )
1754 update_routine( fps_min_simulation, 0 );
1755 ntime_allowUpdate( 1 );
1756 sound_disabled = s;
1757 }
1759 if ( player.p != NULL ) {
1760 Pilot *const *pilot_stack = pilot_getAll();
1761 pilot_rmFlag( player.p, PILOT_HIDE );
1762 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
1763 Pilot *p = pilot_stack[i];
1764 if ( pilot_isWithPlayer( p ) )
1765 pilot_rmFlag( p, PILOT_HIDE );
1766 }
1767 }
1769 space_simulating = 0;
1770 NTracingZoneEnd( _ctx_simulating );
1771
1772 /* Refresh overlay if necessary (player kept it open). */
1773 ovr_refresh();
1774
1775 /* Update gui. */
1776 gui_setSystem();
1777
1778 /* Handle background */
1779 if ( ( cur_system->nebu_density > 0. ) ||
1780 sys_isFlag( cur_system, SYSTEM_NEBULATRAIL ) ) {
1781 /* Background is Nebula */
1782 nebu_prep( cur_system->nebu_density, cur_system->nebu_volatility,
1783 cur_system->nebu_hue );
1784 }
1785 if ( cur_system->nebu_density > 0. ) {
1786 /* Set up sound. */
1787 sound_env( SOUND_ENV_NEBULA, cur_system->nebu_density );
1788 } else {
1789 /* Background is starry */
1790 background_initDust( cur_system->spacedust );
1791 background_load( cur_system->background );
1792
1793 /* Set up sound. */
1794 sound_env( SOUND_ENV_NORMAL, 0. );
1795 }
1796
1797 NTracingZoneEnd( _ctx );
1798 NTracingFrameMarkEnd( "space_init" );
1799}
1800
1805{
1806 Spob *p, *old_stack;
1807 int realloced;
1808
1809 if ( !systems_loading )
1811
1812 /* Grow and initialize memory. */
1813 old_stack = spob_stack;
1814 p = &array_grow( &spob_stack );
1815 realloced = ( old_stack != spob_stack );
1816 spob_initDefaults( p );
1817 p->id = array_size( spob_stack ) - 1;
1818
1819 /* Reconstruct the jumps. */
1820 if ( !systems_loading && realloced )
1822 // if (!systems_loading)
1823 // safelanes_recalculate();
1824
1825 return p;
1826}
1827
1834const char *spob_name( const Spob *p )
1835{
1836 if ( p->display )
1837 return _( p->display );
1838 return _( p->name );
1839}
1840
1846static int spobs_load( void )
1847{
1848 char **spob_files;
1849 Commodity **stdList;
1850
1851 /* Initialize stack if needed. */
1852 if ( spob_stack == NULL )
1854
1855 /* Extract the list of standard commodities. */
1856 stdList = standard_commodities();
1857
1858 /* Load XML stuff. */
1859 spob_files = ndata_listRecursive( SPOB_DATA_PATH );
1860 for ( int i = 0; i < array_size( spob_files ); i++ ) {
1861 if ( ndata_matchExt( spob_files[i], "xml" ) ) {
1862 Spob s;
1863 int ret = spob_parse( &s, spob_files[i], stdList );
1864 if ( ret == 0 ) {
1865 s.id = array_size( spob_stack );
1867 }
1868
1869 /* Render if necessary. */
1871 }
1872
1873 /* Clean up. */
1874 free( spob_files[i] );
1875 }
1876 qsort( spob_stack, array_size( spob_stack ), sizeof( Spob ), spob_cmp );
1877 for ( int j = 0; j < array_size( spob_stack ); j++ )
1878 spob_stack[j].id = j;
1879
1880 /* Clean up. */
1881 array_free( spob_files );
1882 array_free( stdList );
1883
1884 return 0;
1885}
1886
1892static int virtualspobs_load( void )
1893{
1894 char **spob_files;
1895
1896 /* Initialize stack if needed. */
1897 if ( vspob_stack == NULL )
1899
1900 /* Load XML stuff. */
1901 spob_files = ndata_listRecursive( VIRTUALSPOB_DATA_PATH );
1902 for ( int i = 0; i < array_size( spob_files ); i++ ) {
1903 xmlDocPtr doc;
1904 xmlNodePtr node;
1905
1906 if ( !ndata_matchExt( spob_files[i], "xml" ) ) {
1907 free( spob_files[i] );
1908 continue;
1909 }
1910
1911 doc = xml_parsePhysFS( spob_files[i] );
1912 if ( doc == NULL ) {
1913 free( spob_files[i] );
1914 continue;
1915 }
1916
1917 node = doc->xmlChildrenNode; /* first spob node */
1918 if ( node == NULL ) {
1919 WARN( _( "Malformed %s file: does not contain elements" ),
1920 spob_files[i] );
1921 free( spob_files[i] );
1922 xmlFreeDoc( doc );
1923 continue;
1924 }
1925
1926 if ( xml_isNode( node, XML_SPOB_TAG ) ) {
1927 xmlNodePtr cur;
1928 VirtualSpob va;
1929 memset( &va, 0, sizeof( va ) );
1930 xmlr_attr_strd( node, "name", va.name );
1932
1933 cur = node->children;
1934 do {
1935 xml_onlyNodes( cur );
1936 if ( xml_isNode( cur, "presence" ) ) {
1937 SpobPresence ap;
1938 spob_parsePresence( cur, &ap );
1939 array_push_back( &va.presences, ap );
1940 continue;
1941 }
1942
1943 WARN( _( "Unknown node '%s' in virtual spob '%s'" ), cur->name,
1944 va.name );
1945 } while ( xml_nextNode( cur ) );
1946
1948 }
1949
1950 /* Clean up. */
1951 free( spob_files[i] );
1952 xmlFreeDoc( doc );
1953 }
1954 qsort( vspob_stack, array_size( vspob_stack ), sizeof( VirtualSpob ),
1956
1957 /* Clean up. */
1958 array_free( spob_files );
1959
1960 return 0;
1961}
1962
1966char spob_getColourChar( const Spob *p )
1967{
1968 if ( !spob_hasService( p, SPOB_SERVICE_INHABITED ) )
1969 return 'I';
1970
1971 if ( p->can_land ) {
1972 if ( areAllies( FACTION_PLAYER, p->presence.faction ) )
1973 return 'F';
1974 return 'N';
1975 }
1976
1977 if ( spob_isFlag( p, SPOB_HOSTILE ) ||
1978 areEnemies( FACTION_PLAYER, p->presence.faction ) )
1979 return 'H';
1980 return 'R';
1981}
1982
1986const char *spob_getSymbol( const Spob *p )
1987{
1988 if ( !spob_hasService( p, SPOB_SERVICE_INHABITED ) ) {
1989 if ( spob_hasService( p, SPOB_SERVICE_LAND ) )
1990 return "= ";
1991 return "";
1992 }
1993
1994 if ( p->can_land ) {
1995 if ( areAllies( FACTION_PLAYER, p->presence.faction ) )
1996 return "+ ";
1997 return "~ ";
1998 }
1999
2000 if ( spob_isFlag( p, SPOB_HOSTILE ) ||
2001 areEnemies( FACTION_PLAYER, p->presence.faction ) )
2002 return "!! ";
2003 return "* ";
2004}
2005
2009const glColour *spob_getColour( const Spob *p )
2010{
2011 if ( !spob_hasService( p, SPOB_SERVICE_INHABITED ) )
2012 return &cInert;
2013
2014 if ( p->can_land ) {
2015 if ( areAllies( FACTION_PLAYER, p->presence.faction ) )
2016 return &cFriend;
2017 return &cNeutral;
2018 }
2019
2020 if ( spob_isFlag( p, SPOB_HOSTILE ) ||
2021 areEnemies( FACTION_PLAYER, p->presence.faction ) )
2022 return &cHostile;
2023 return &cRestricted;
2024}
2025
2032{
2033 if ( p->land_override && ( p->land_msg != NULL ) ) {
2034 p->can_land = ( p->land_override > 0 );
2035 return;
2036 }
2037
2038 NTracingZone( _ctx, 1 );
2039
2040 /* Clean up old stuff. */
2041 free( p->land_msg );
2042 p->can_land = 0;
2043 p->land_msg = NULL;
2044
2045 /* Run custom Lua. */
2046 if ( p->lua_can_land != LUA_NOREF ) {
2047 spob_luaInitMem( p );
2048 lua_rawgeti( naevL, LUA_REGISTRYINDEX, p->lua_can_land ); /* f */
2049 if ( nlua_pcall( p->lua_env, 0, 2 ) ) {
2050 WARN( _( "Spob '%s' failed to run '%s':\n%s" ), p->name, "can_land",
2051 lua_tostring( naevL, -1 ) );
2052 lua_pop( naevL, 1 );
2053 NTracingZoneEnd( _ctx );
2054 return;
2055 }
2056
2057 p->can_land = lua_toboolean( naevL, -2 );
2058 if ( lua_isstring( naevL, -1 ) )
2059 p->land_msg = strdup( lua_tostring( naevL, -1 ) );
2060 lua_pop( naevL, 2 );
2061
2062 NTracingZoneEnd( _ctx );
2063 return;
2064 }
2065
2066 /* Some defaults. */
2067 if ( p->land_override < 0 ) {
2068 p->land_msg = strdup( _( "Landing permission denied." ) );
2069 } else if ( spob_hasService( p, SPOB_SERVICE_LAND ) ||
2070 ( p->land_override > 0 ) ) {
2071 p->can_land = 1;
2072 p->land_msg = strdup( _( "Landing permission granted." ) );
2073 }
2074
2075 NTracingZoneEnd( _ctx );
2076}
2077
2081void spob_distress( Spob *spb, const Pilot *p, const Pilot *attacker )
2082{
2083 if ( attacker == NULL )
2084 return;
2085
2086 /* Doesn't have a function defined. */
2087 if ( spb->lua_distress == LUA_NOREF )
2088 return;
2089
2090 /* Run the function. */
2091 spob_luaInitMem( spb );
2092 lua_rawgeti( naevL, LUA_REGISTRYINDEX, spb->lua_distress ); /* f */
2093 lua_pushpilot( naevL, p->id );
2094 lua_pushpilot( naevL, attacker->id );
2095 if ( nlua_pcall( spb->lua_env, 2, 0 ) ) {
2096 WARN( _( "Spob '%s' failed to run '%s':\n%s" ), spb->name, "distress",
2097 lua_tostring( naevL, -1 ) );
2098 lua_pop( naevL, 1 );
2099 return;
2100 }
2101
2102 /* Update land permissions. */
2103 spob_updateLand( spb );
2104}
2105
2109void spob_luaInitMem( const Spob *spob )
2110{
2111 lua_rawgeti( naevL, LUA_REGISTRYINDEX, spob->lua_mem );
2112 nlua_setenv( naevL, spob->lua_env, "mem" );
2113}
2114
2120int spob_luaInit( Spob *spob )
2121{
2122 int mem;
2123
2124 /* Just clear everything. */
2125#define UNREF( x ) \
2126 do { \
2127 if ( ( x ) != LUA_NOREF ) { \
2128 luaL_unref( naevL, LUA_REGISTRYINDEX, ( x ) ); \
2129 ( x ) = LUA_NOREF; \
2130 } \
2131 } while ( 0 )
2132 spob->lua_env = LUA_NOREF; /* Just a pointer to some Lua index. */
2133 UNREF( spob->lua_init );
2134 UNREF( spob->lua_load );
2135 UNREF( spob->lua_unload );
2136 UNREF( spob->lua_land );
2137 UNREF( spob->lua_can_land );
2138 UNREF( spob->lua_render );
2139 UNREF( spob->lua_update );
2140 UNREF( spob->lua_comm );
2141 UNREF( spob->lua_population );
2142 UNREF( spob->lua_barbg );
2143 UNREF( spob->lua_distress );
2144 UNREF( spob->lua_mem );
2145#undef UNREF
2146
2147 /* Initialize. */
2148 if ( spob->lua_file == NULL )
2149 return 0;
2150
2151 /* Try to get the environment, will create a new one as necessary. */
2152 nlua_env env = spob_lua_get( &mem, spob->lua_file );
2153 if ( env == LUA_NOREF )
2154 return -1;
2155
2156 spob->lua_env = env;
2157
2158 /* Grab functions as applicable. */
2159 spob->lua_init = nlua_refenvtype( env, "init", LUA_TFUNCTION );
2160 spob->lua_load = nlua_refenvtype( env, "load", LUA_TFUNCTION );
2161 spob->lua_unload = nlua_refenvtype( env, "unload", LUA_TFUNCTION );
2162 spob->lua_can_land = nlua_refenvtype( env, "can_land", LUA_TFUNCTION );
2163 spob->lua_land = nlua_refenvtype( env, "land", LUA_TFUNCTION );
2164 spob->lua_render = nlua_refenvtype( env, "render", LUA_TFUNCTION );
2165 spob->lua_update = nlua_refenvtype( env, "update", LUA_TFUNCTION );
2166 spob->lua_comm = nlua_refenvtype( env, "comm", LUA_TFUNCTION );
2167 spob->lua_population = nlua_refenvtype( env, "population", LUA_TFUNCTION );
2168 spob->lua_barbg = nlua_refenvtype( env, "barbg", LUA_TFUNCTION );
2169 spob->lua_distress = nlua_refenvtype( env, "distress", LUA_TFUNCTION );
2170
2171 /* Set up local memory. */
2172 lua_newtable( naevL ); /* m */
2173 lua_pushvalue( naevL, -1 ); /* m, m */
2174 spob->lua_mem = luaL_ref( naevL, LUA_REGISTRYINDEX ); /* m */
2175
2176 /* Copy over global memory. */
2177 lua_rawgeti( naevL, LUA_REGISTRYINDEX, mem ); /* m, d */
2178 lua_pushnil( naevL ); /* m, d, nil */
2179 while ( lua_next( naevL, -2 ) != 0 ) { /* m, d, k, v */
2180 lua_pushvalue( naevL, -2 ); /* m, d, k, v, k */
2181 lua_pushvalue( naevL, -2 ); /* m, d, k, v, k, v */
2182 lua_remove( naevL, -3 ); /* m, d, k, k, v */
2183 lua_settable( naevL, -5 ); /* m, d, k */
2184 } /* m, d */
2185 lua_pop( naevL, 2 ); /* */
2186
2187 /* Run init if applicable. */
2188 if ( spob->lua_init != LUA_NOREF ) {
2189 spob_luaInitMem( spob );
2190 lua_rawgeti( naevL, LUA_REGISTRYINDEX, spob->lua_init ); /* f */
2191 lua_pushspob( naevL, spob_index( spob ) );
2192 if ( nlua_pcall( spob->lua_env, 1, 0 ) ) {
2193 WARN( _( "Spob '%s' failed to run '%s':\n%s" ), spob->name, "init",
2194 lua_tostring( naevL, -1 ) );
2195 lua_pop( naevL, 1 );
2196 return -1;
2197 }
2198 }
2199
2200 return 0;
2201}
2202
2206void spob_gfxLoad( Spob *spob )
2207{
2208 if ( spob->lua_load != LUA_NOREF ) {
2209 spob_luaInitMem( spob );
2210 lua_rawgeti( naevL, LUA_REGISTRYINDEX, spob->lua_load ); /* f */
2211 if ( nlua_pcall( spob->lua_env, 0, 2 ) ) {
2212 WARN( _( "Spob '%s' failed to run '%s':\n%s" ), spob->name, "load",
2213 lua_tostring( naevL, -1 ) );
2214 lua_pop( naevL, 1 );
2215 return;
2216 }
2217 if ( lua_istex( naevL, -2 ) ) {
2218 if ( spob->gfx_space )
2219 gl_freeTexture( spob->gfx_space );
2220 spob->gfx_space = gl_dupTexture( lua_totex( naevL, -2 ) );
2221 } else if ( lua_isnil( naevL, -2 ) ) {
2222 /* Have the engine handle it if nil. */
2223 } else
2224 WARN(
2225 _( "Spob '%s' ran '%s' but got non-texture or nil return value!" ),
2226 spob->name, "load" );
2227 spob->radius = luaL_optnumber( naevL, -1, -1. );
2228 lua_pop( naevL, 2 );
2229 }
2230
2231 if ( ( spob->gfx_space3d == NULL ) && ( spob->gfx_space == NULL ) ) {
2232 if ( spob->gfx_space3dName != NULL ) {
2233 GLuint tex;
2234 double s = spob->gfx_space3d_size;
2235 spob->gfx_space3d = gltf_loadFromFile( spob->gfx_space3dName );
2236 /* Create framebuffer texture. */
2237 gl_fboCreate( &spob->gfx_fbo, &tex, s * spob_aa_scale,
2238 s * spob_aa_scale );
2239 gl_fboAddDepth( spob->gfx_fbo, &spob->gfx_dtex, s * spob_aa_scale,
2240 s * spob_aa_scale );
2241 spob->gfx_space = gl_rawTexture( spob->gfx_space3dName, tex, s, s );
2242 /* Do a single render pass to populate the framebuffer. */
2243 glBindFramebuffer( GL_FRAMEBUFFER, spob->gfx_fbo );
2244 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
2245
2246 gltf_renderScene( spob->gfx_fbo, spob->gfx_space3d, 0, NULL,
2247 elapsed_time_mod, s * spob_aa_scale, NULL );
2248
2249 glBindFramebuffer( GL_FRAMEBUFFER, gl_screen.current_fbo );
2250 } else if ( spob->gfx_spaceName != NULL )
2251 spob->gfx_space =
2252 gl_newImage( spob->gfx_spaceName, OPENGL_TEX_MIPMAPS );
2253 }
2254 /* Set default size if applicable. */
2255 if ( ( spob->gfx_space3d != NULL ) && ( spob->radius < 0. ) )
2256 spob->radius = spob->gfx_space3d_size * 0.5;
2257 if ( ( spob->gfx_space != NULL ) && ( spob->radius < 0. ) )
2258 spob->radius = ( spob->gfx_space->w + spob->gfx_space->h ) / 4.;
2259}
2260
2266void space_gfxLoad( StarSystem *sys )
2267{
2268 NTracingZone( _ctx, 1 );
2269
2270 for ( int i = 0; i < array_size( sys->spobs ); i++ )
2271 spob_gfxLoad( sys->spobs[i] );
2272
2273 NTracingZoneEnd( _ctx );
2274}
2275
2281void space_gfxUnload( StarSystem *sys )
2282{
2283 for ( int i = 0; i < array_size( sys->spobs ); i++ ) {
2284 Spob *spob = sys->spobs[i];
2285
2286 if ( spob->lua_unload != LUA_NOREF ) {
2287 spob_luaInitMem( spob );
2288 lua_rawgeti( naevL, LUA_REGISTRYINDEX, spob->lua_unload ); /* f */
2289 if ( nlua_pcall( spob->lua_env, 0, 0 ) ) {
2290 WARN( _( "Spob '%s' failed to run '%s':\n%s" ), spob->name,
2291 "unload", lua_tostring( naevL, -1 ) );
2292 lua_pop( naevL, 1 );
2293 }
2294 }
2295
2296 if ( spob->gfx_space3d != NULL ) {
2297 glDeleteFramebuffers( 1, &spob->gfx_fbo );
2298 glDeleteTextures( 1, &spob->gfx_dtex );
2299 }
2300 gltf_free( spob->gfx_space3d );
2301 spob->gfx_space3d = NULL;
2302 gl_freeTexture( spob->gfx_space );
2303 spob->gfx_space = NULL;
2304 }
2305}
2306
2313static int spob_parsePresence( xmlNodePtr node, SpobPresence *ap )
2314{
2315 xmlNodePtr cur = node->children;
2316 memset( ap, 0, sizeof( SpobPresence ) );
2317 ap->faction = -1;
2318 do {
2319 xml_onlyNodes( cur );
2320 xmlr_float( cur, "base", ap->base );
2321 xmlr_float( cur, "bonus", ap->bonus );
2322 xmlr_int( cur, "range", ap->range );
2323 if ( xml_isNode( cur, "faction" ) ) {
2324 ap->faction = faction_get( xml_get( cur ) );
2325 continue;
2326 }
2327 } while ( xml_nextNode( cur ) );
2328 return 0;
2329}
2330
2334static void spob_initDefaults( Spob *spob )
2335{
2336 /* Clear up memory for safe defaults. */
2337 memset( spob, 0, sizeof( Spob ) );
2338 spob->hide = 0.01;
2339 spob->radius = -1.;
2340 spob->presence.faction = -1;
2341 spob->marker_scale = 1.; /* Default scale. */
2342 /* Lua stuff. */
2343 spob->lua_env = LUA_NOREF;
2344 spob->lua_init = LUA_NOREF;
2345 spob->lua_load = LUA_NOREF;
2346 spob->lua_unload = LUA_NOREF;
2347 spob->lua_land = LUA_NOREF;
2348 spob->lua_can_land = LUA_NOREF;
2349 spob->lua_render = LUA_NOREF;
2350 spob->lua_update = LUA_NOREF;
2351 spob->lua_comm = LUA_NOREF;
2352 spob->lua_population = LUA_NOREF;
2353 spob->lua_barbg = LUA_NOREF;
2354 spob->lua_distress = LUA_NOREF;
2355}
2356
2365static int spob_parse( Spob *spob, const char *filename, Commodity **stdList )
2366{
2367 xmlDocPtr doc;
2368 xmlNodePtr node, parent;
2369 unsigned int flags;
2370 Commodity **comms;
2371
2372 doc = xml_parsePhysFS( filename );
2373 if ( doc == NULL )
2374 return -1;
2375
2376 parent = doc->xmlChildrenNode; /* first spob node */
2377 if ( parent == NULL ) {
2378 WARN( _( "Malformed %s file: does not contain elements" ), filename );
2379 xmlFreeDoc( doc );
2380 return -1;
2381 }
2382
2383 /* Set defaults. */
2384 spob_initDefaults( spob );
2385 flags = 0;
2386 comms = array_create( Commodity * );
2387
2388 /* Get the name. */
2389 xmlr_attr_strd( parent, "name", spob->name );
2390
2391 node = parent->xmlChildrenNode;
2392 do {
2393 /* Only handle nodes. */
2394 xml_onlyNodes( node );
2395
2396 xmlr_strd( node, "display", spob->display );
2397 xmlr_strd( node, "feature", spob->feature );
2398 xmlr_float( node, "radius", spob->radius );
2399 if ( xml_isNode( node, "lua" ) ) {
2400 const char *nstr = xml_get( node );
2401 if ( nstr == NULL ) {
2402 WARN( _( "Spob '%s' has invalid '%s' node." ), spob->name, "lua" );
2403 continue;
2404 }
2405 if ( nstr[0] == '/' )
2406 spob->lua_file = strdup( nstr );
2407 else
2408 SDL_asprintf( &spob->lua_file, SPOB_DATA_LUA_PATH "%s", nstr );
2409 spob->lua_file_raw = strdup( nstr );
2410 continue;
2411 }
2412 if ( xml_isNode( node, "marker" ) ) {
2413 const char *s = xml_get( node );
2414 spob->marker = shaders_getSimple( s );
2415 xmlr_attr_float_def( node, "scale", spob->marker_scale, 1. );
2416 if ( spob->marker == NULL )
2417 WARN( _( "Spob '%s' has unknown marker shader '%s'!" ), spob->name,
2418 s );
2419 continue;
2420 }
2421
2422 if ( xml_isNode( node, "GFX" ) ) {
2423 xmlNodePtr cur = node->children;
2424 do {
2425 xml_onlyNodes( cur );
2426 if ( xml_isNode( cur, "space3d" ) ) { /* load 3D space gfx */
2427 char str[PATH_MAX];
2428 snprintf( str, sizeof( str ), SPOB_GFX_SPACE3D_PATH "%s",
2429 xml_get( cur ) );
2430 spob->gfx_space3dName = strdup( str );
2431 spob->gfx_space3dPath = xml_getStrd( cur );
2432 xmlr_attr_float( cur, "size", spob->gfx_space3d_size );
2433 continue;
2434 }
2435 if ( xml_isNode( cur, "space" ) ) { /* load space gfx */
2436 char str[PATH_MAX];
2437 snprintf( str, sizeof( str ), SPOB_GFX_SPACE_PATH "%s",
2438 xml_get( cur ) );
2439 spob->gfx_spaceName = strdup( str );
2440 spob->gfx_spacePath = xml_getStrd( cur );
2441 continue;
2442 }
2443 if ( xml_isNode( cur, "exterior" ) ) { /* load land gfx */
2444 char str[PATH_MAX];
2445 snprintf( str, sizeof( str ), SPOB_GFX_EXTERIOR_PATH "%s",
2446 xml_get( cur ) );
2447 spob->gfx_exterior = strdup( str );
2448 spob->gfx_exteriorPath = xml_getStrd( cur );
2449 continue;
2450 }
2451 if ( xml_isNode( cur, "comm" ) ) { /* communication gfx */
2452 char str[PATH_MAX];
2453 snprintf( str, sizeof( str ), SPOB_GFX_COMM_PATH "%s",
2454 xml_get( cur ) );
2455 spob->gfx_comm = strdup( str );
2456 spob->gfx_commPath = xml_getStrd( cur );
2457 continue;
2458 }
2459 WARN( _( "Unknown node '%s' in spob '%s'" ), node->name,
2460 spob->name );
2461 } while ( xml_nextNode( cur ) );
2462 continue;
2463 } else if ( xml_isNode( node, "pos" ) ) {
2464 xmlr_attr_float( node, "x", spob->pos.x );
2465 xmlr_attr_float( node, "y", spob->pos.y );
2466 flags |= FLAG_POSSET;
2467 continue;
2468 } else if ( xml_isNode( node, "presence" ) ) {
2469 spob_parsePresence( node, &spob->presence );
2470 if ( spob->presence.faction >= 0 )
2471 flags |= FLAG_FACTIONSET;
2472 continue;
2473 } else if ( xml_isNode( node, "general" ) ) {
2474 xmlNodePtr cur = node->children;
2475 do {
2476 xml_onlyNodes( cur );
2477 /* Direct reads. */
2478 xmlr_strd( cur, "class", spob->class );
2479 xmlr_strd( cur, "bar", spob->bar_description );
2480 xmlr_strd( cur, "description", spob->description );
2481 xmlr_float( cur, "population", spob->population );
2482 xmlr_float( cur, "hide", spob->hide );
2483
2484 if ( xml_isNode( cur, "services" ) ) {
2485 xmlNodePtr ccur = cur->children;
2486 flags |= FLAG_SERVICESSET;
2487 spob->services = 0;
2488 do {
2489 xml_onlyNodes( ccur );
2490
2491 if ( xml_isNode( ccur, "land" ) )
2492 spob->services |= SPOB_SERVICE_LAND;
2493 else if ( xml_isNode( ccur, "refuel" ) )
2494 spob->services |=
2495 SPOB_SERVICE_REFUEL | SPOB_SERVICE_INHABITED;
2496 else if ( xml_isNode( ccur, "bar" ) )
2497 spob->services |=
2498 SPOB_SERVICE_BAR | SPOB_SERVICE_INHABITED;
2499 else if ( xml_isNode( ccur, "missions" ) )
2500 spob->services |=
2501 SPOB_SERVICE_MISSIONS | SPOB_SERVICE_INHABITED;
2502 else if ( xml_isNode( ccur, "commodity" ) )
2503 spob->services |=
2504 SPOB_SERVICE_COMMODITY | SPOB_SERVICE_INHABITED;
2505 else if ( xml_isNode( ccur, "outfits" ) )
2506 spob->services |=
2507 SPOB_SERVICE_OUTFITS | SPOB_SERVICE_INHABITED;
2508 else if ( xml_isNode( ccur, "shipyard" ) )
2509 spob->services |=
2510 SPOB_SERVICE_SHIPYARD | SPOB_SERVICE_INHABITED;
2511 else if ( xml_isNode( ccur, "nomissionspawn" ) )
2512 spob->flags |= SPOB_NOMISNSPAWN;
2513 else if ( xml_isNode( ccur, "uninhabited" ) )
2514 spob->flags |= SPOB_UNINHABITED;
2515 else if ( xml_isNode( ccur, "blackmarket" ) )
2516 spob->services |= SPOB_SERVICE_BLACKMARKET;
2517 else if ( xml_isNode( ccur, "nolanes" ) )
2518 spob->flags |= SPOB_NOLANES;
2519 else
2520 WARN( _( "Spob '%s' has unknown services tag '%s'" ),
2521 spob->name, ccur->name );
2522 } while ( xml_nextNode( ccur ) );
2523 }
2524
2525 else if ( xml_isNode( cur, "commodities" ) ) {
2526 xmlNodePtr ccur = cur->children;
2527 do {
2528 if ( xml_isNode( ccur, "commodity" ) ) {
2529 /* If the commodity is standard, don't re-add it. */
2530 Commodity *com = commodity_get( xml_get( ccur ) );
2531 if ( commodity_isFlag( com, COMMODITY_FLAG_STANDARD ) )
2532 continue;
2533
2534 array_push_back( &comms, com );
2535 }
2536 } while ( xml_nextNode( ccur ) );
2537 } else if ( xml_isNode( cur, "blackmarket" ) ) {
2538 spob_addService( spob, SPOB_SERVICE_BLACKMARKET );
2539 continue;
2540 }
2541 } while ( xml_nextNode( cur ) );
2542 continue;
2543 } else if ( xml_isNode( node, "tech" ) ) {
2544 spob->tech = tech_groupCreateXML( node );
2545 continue;
2546 } else if ( xml_isNode( node, "tags" ) ) {
2547 xmlNodePtr cur = node->children;
2548 if ( spob->tags != NULL )
2549 WARN( _( "Spob '%s' has duplicate '%s' node!" ), spob->name,
2550 "tags" );
2551 else
2552 spob->tags = array_create( char * );
2553 do {
2554 xml_onlyNodes( cur );
2555 if ( xml_isNode( cur, "tag" ) ) {
2556 const char *tmp = xml_get( cur );
2557 if ( tmp != NULL )
2558 array_push_back( &spob->tags, strdup( tmp ) );
2559 continue;
2560 }
2561 WARN( _( "Spob '%s' has unknown node in tags '%s'." ), spob->name,
2562 cur->name );
2563 } while ( xml_nextNode( cur ) );
2564 continue;
2565 }
2566 // cppcheck-suppress nullPointerRedundantCheck
2567 WARN( _( "Unknown node '%s' in spob '%s'" ), node->name, spob->name );
2568 } while ( xml_nextNode( node ) );
2569
2570 /* Allow forcing to be uninhabited. */
2571 if ( spob_isFlag( spob, SPOB_UNINHABITED ) )
2572 spob->services &= ~SPOB_SERVICE_INHABITED;
2573
2574 if ( spob->radius > 0. )
2575 spob_setFlag( spob, SPOB_RADIUS );
2576
2577 /* Set defaults if not set. */
2578 if ( spob->lua_file == NULL ) {
2579 const char *str = start_spob_lua_default();
2580 if ( str != NULL )
2581 spob->lua_file = strdup( str );
2582 }
2583
2584 /* Save the filename. */
2585 spob->filename = strdup( filename );
2586
2587#if DEBUGGING
2588 /* Check for graphics. */
2589 if ( ( spob->gfx_exterior != NULL ) && !PHYSFS_exists( spob->gfx_exterior ) )
2590 WARN( _( "Can not find exterior graphic '%s' for spob '%s'!" ),
2591 spob->gfx_exterior, spob->name );
2592 if ( ( spob->gfx_comm != NULL ) && !PHYSFS_exists( spob->gfx_comm ) )
2593 WARN( _( "Can not find comm graphic '%s' for spob '%s'!" ),
2594 spob->gfx_comm, spob->name );
2595#endif /* DEBUGGING */
2596
2597/*
2598 * Verification
2599 */
2600#define MELEMENT( o, s ) \
2601 if ( o ) \
2602 WARN( _( "Spob '%s' missing '%s' element" ), spob->name, s )
2603 // MELEMENT(spob->gfx_spaceName==NULL,"GFX space");
2604 MELEMENT( spob_hasService( spob, SPOB_SERVICE_LAND ) &&
2605 spob->gfx_exterior == NULL,
2606 "GFX exterior" );
2607 MELEMENT( spob_hasService( spob, SPOB_SERVICE_INHABITED ) &&
2608 ( spob->population == 0 ),
2609 "population" );
2610 MELEMENT( ( flags & FLAG_POSSET ) == 0, "pos" );
2611 MELEMENT( spob->class == NULL, "class" );
2612 MELEMENT( spob_hasService( spob, SPOB_SERVICE_LAND ) &&
2613 spob->description == NULL,
2614 "description" );
2615 MELEMENT( spob_hasService( spob, SPOB_SERVICE_BAR ) &&
2616 spob->bar_description == NULL,
2617 "bar" );
2618 MELEMENT( spob_hasService( spob, SPOB_SERVICE_INHABITED ) &&
2619 ( flags & FLAG_FACTIONSET ) == 0,
2620 "faction" );
2621 MELEMENT( ( flags & FLAG_SERVICESSET ) == 0, "services" );
2622 MELEMENT( spob_hasService( spob, SPOB_SERVICE_INHABITED ) &&
2623 ( spob_hasService( spob, SPOB_SERVICE_OUTFITS ) ||
2624 spob_hasService( spob, SPOB_SERVICE_SHIPYARD ) ) &&
2625 ( spob->tech == NULL ),
2626 "tech" );
2627 /*MELEMENT( spob_hasService(spob,SPOB_SERVICE_COMMODITY) &&
2628 (array_size(spob->commodities)==0),"commodity" );*/
2629 /*MELEMENT( (flags&FLAG_FACTIONSET) && (spob->presenceAmount == 0.),
2630 "presence" );*/
2631#undef MELEMENT
2632
2633 /* Build commodities list */
2634 if ( spob_hasService( spob, SPOB_SERVICE_COMMODITY ) ) {
2636 spob->commodities = array_create( Commodity * );
2637
2638 /* First, store all the standard commodities and prices. */
2639 if ( array_size( stdList ) > 0 ) {
2640 for ( int i = 0; i < array_size( stdList ); i++ )
2641 spob_addCommodity( spob, stdList[i] );
2642 }
2643
2644 /* Now add extra commodities */
2645 for ( int i = 0; i < array_size( comms ); i++ )
2646 spob_addCommodity( spob, comms[i] );
2647
2648 /* Shrink to minimum size. */
2649 array_shrink( &spob->commodities );
2650 array_shrink( &spob->commodityPrice );
2651 }
2652 /* Free temporary comms list. */
2653 array_free( comms );
2654
2655 xmlFreeDoc( doc );
2656
2657 return 0;
2658}
2659
2668int system_addSpob( StarSystem *sys, const char *spobname )
2669{
2670 Spob *spob;
2671
2672 if ( sys == NULL )
2673 return -1;
2674
2675 spob = spob_get( spobname );
2676 if ( spob == NULL )
2677 return -1;
2678 array_push_back( &sys->spobs, spob );
2679 array_push_back( &sys->spobsid, spob->id );
2680
2681 /* add spob <-> star system to name stack */
2683 array_push_back( &systemname_stack, sys->name );
2684
2686 /* This is required to clear the player statistics for this spob */
2688
2689 /* Reload graphics if necessary. */
2690 if ( cur_system != NULL )
2692
2693 /* Initialize economy if applicable. */
2694 if ( spob_hasService( spob, SPOB_SERVICE_COMMODITY ) )
2695 economy_initialiseSingleSystem( sys, spob );
2696
2697 return 0;
2698}
2699
2709int system_rmSpob( StarSystem *sys, const char *spobname )
2710{
2711 int i, found;
2712 Spob *spob;
2713
2714 if ( sys == NULL ) {
2715 WARN( _( "Unable to remove spob '%s' from NULL system." ), spobname );
2716 return -1;
2717 }
2718
2719 /* Try to find spob. */
2720 spob = spob_get( spobname );
2721 for ( i = 0; i < array_size( sys->spobs ); i++ )
2722 if ( sys->spobs[i] == spob )
2723 break;
2724
2725 /* Spob not found. */
2726 if ( i >= array_size( sys->spobs ) ) {
2727 WARN( _( "Spob '%s' not found in system '%s' for removal." ), spobname,
2728 sys->name );
2729 return -1;
2730 }
2731
2732 /* Remove spob from system. */
2733 array_erase( &sys->spobs, &sys->spobs[i], &sys->spobs[i + 1] );
2734 array_erase( &sys->spobsid, &sys->spobsid[i], &sys->spobsid[i + 1] );
2735
2736 /* Remove from the name stack thingy. */
2737 found = 0;
2738 for ( i = 0; i < array_size( spobname_stack ); i++ )
2739 if ( strcmp( spobname, spobname_stack[i] ) == 0 ) {
2741 &spobname_stack[i + 1] );
2743 &systemname_stack[i + 1] );
2744 found = 1;
2745 break;
2746 }
2747 if ( found == 0 )
2748 WARN( _( "Unable to find spob '%s' and system '%s' in spob<->system "
2749 "stack." ),
2750 spobname, sys->name );
2751
2752 system_setFaction( sys );
2753
2755
2756 return 0;
2757}
2758
2765int system_addVirtualSpob( StarSystem *sys, const char *spobname )
2766{
2767 VirtualSpob *va;
2768
2769 if ( sys == NULL )
2770 return -1;
2771
2772 va = virtualspob_get( spobname );
2773 if ( va == NULL )
2774 return -1;
2775 array_push_back( &sys->spobs_virtual, va );
2776
2777 /* Economy is affected by presence. */
2779
2780 return 0;
2781}
2782
2789int system_rmVirtualSpob( StarSystem *sys, const char *spobname )
2790{
2791 int i;
2792
2793 if ( sys == NULL ) {
2794 WARN( _( "Unable to remove virtual spob '%s' from NULL system." ),
2795 spobname );
2796 return -1;
2797 }
2798
2799 /* Try to find virtual spob. */
2800 for ( i = 0; i < array_size( sys->spobs_virtual ); i++ )
2801 if ( strcmp( sys->spobs_virtual[i]->name, spobname ) == 0 )
2802 break;
2803
2804 /* Virtual spob not found. */
2805 if ( i >= array_size( sys->spobs_virtual ) ) {
2806 WARN( _( "Virtual spob '%s' not found in system '%s' for removal." ),
2807 spobname, sys->name );
2808 return -1;
2809 }
2810
2811 /* Remove virtual spob. */
2812 array_erase( &sys->spobs_virtual, &sys->spobs_virtual[i],
2813 &sys->spobs_virtual[i + 1] );
2814
2816
2817 return 0;
2818}
2819
2829int system_addJump( StarSystem *sys, StarSystem *target )
2830{
2831 JumpPoint *j;
2832
2833#ifdef DEBUGGING
2834 for ( int i = 0; i < array_size( sys->jumps ); i++ ) {
2835 JumpPoint *jp = &sys->jumps[i];
2836 if ( jp->targetid != target->id )
2837 continue;
2838
2839 WARN( _( "Star System '%s' has duplicate jump point to '%s'." ),
2840 sys->name, target->name );
2841 return -1;
2842 }
2843#endif /* DEBUGGING */
2844
2845 /* Allocate more space. */
2846 j = &array_grow( &sys->jumps );
2847 memset( j, 0, sizeof( JumpPoint ) );
2848
2849 /* Set some stuff. */
2850 j->target = target;
2851 j->targetid = j->target->id;
2852 j->radius = 200.;
2853 j->hide = HIDE_DEFAULT_JUMP;
2854 jp_setFlag( j, JP_AUTOPOS );
2855
2856 return 0;
2857}
2858
2868int system_rmJump( StarSystem *sys, StarSystem *target )
2869{
2870 int i;
2871
2872 /* Find associated jump. */
2873 for ( i = 0; i < array_size( sys->jumps ); i++ )
2874 if ( sys->jumps[i].target == target )
2875 break;
2876
2877 /* Not found. */
2878 if ( i >= array_size( sys->jumps ) ) {
2879 WARN( _( "Jump for system '%s' not found in system '%s' for removal." ),
2880 target->name, sys->name );
2881 return -1;
2882 }
2883
2884 /* Remove the jump. */
2885 array_erase( &sys->jumps, &sys->jumps[i], &sys->jumps[i + 1] );
2886 return 0;
2887}
2888
2892static void system_init( StarSystem *sys )
2893{
2894 memset( sys, 0, sizeof( StarSystem ) );
2895 sys->spobs = array_create( Spob * );
2896 sys->spobs_virtual = array_create( VirtualSpob * );
2897 sys->spobsid = array_create( int );
2898 sys->jumps = array_create( JumpPoint );
2899 sys->asteroids = array_create( AsteroidAnchor );
2900 sys->astexclude = array_create( AsteroidExclusion );
2901 sys->faction = -1;
2902 sys->presence = array_create( SystemPresence );
2903}
2904
2908StarSystem *system_new( void )
2909{
2910 StarSystem *sys;
2911 int id;
2912
2913 if ( !systems_loading )
2915
2916 /* Protect current system in case of realloc. */
2917 id = -1;
2918 if ( cur_system != NULL )
2919 id = system_index( cur_system );
2920
2921 /* Grow array. */
2922 sys = &array_grow( &systems_stack );
2923
2924 /* Reset cur_system. */
2925 if ( id >= 0 )
2927
2928 /* Initialize system and id. */
2929 system_init( sys );
2930 sys->id = array_size( systems_stack ) - 1;
2931
2932 /* Reconstruct the jumps, only truely necessary if the systems realloced. */
2933 if ( !systems_loading ) {
2936 }
2937
2938 return sys;
2939}
2940
2941const char *system_name( const StarSystem *sys )
2942{
2943 if ( sys->display != NULL )
2944 return _( sys->display );
2945 return _( sys->name );
2946}
2947
2948const char *system_nameKnown( const StarSystem *sys )
2949{
2950 if ( !sys_isKnown( sys ) )
2951 return _( "Unknown" );
2952 return system_name( sys );
2953}
2954
2958void system_reconstructJumps( StarSystem *sys )
2959{
2960 for ( int j = 0; j < array_size( sys->jumps ); j++ ) {
2961 double dx, dy, a;
2962 JumpPoint *jp = &sys->jumps[j];
2963 jp->from = sys;
2964 jp->target = system_getIndex( jp->targetid );
2965 jp->returnJump = jump_getTarget( sys, jp->target );
2966
2967 /* Get heading. */
2968 dx = jp->target->pos.x - sys->pos.x;
2969 dy = jp->target->pos.y - sys->pos.y;
2970 a = atan2( dy, dx );
2971 if ( a < 0. )
2972 a += 2. * M_PI;
2973
2974 /* Update position if needed.. */
2975 if ( jp->flags & JP_AUTOPOS )
2976 vec2_pset( &jp->pos, sys->radius, a );
2977
2978 /* Update jump specific data. */
2979 gl_getSpriteFromDir( &jp->sx, &jp->sy, jumppoint_gfx->sx,
2980 jumppoint_gfx->sy, a );
2981 jp->angle = 2. * M_PI - a;
2982 jp->cosa = cos( jp->angle );
2983 jp->sina = sin( jp->angle );
2984 }
2985}
2986
2991{
2992 NTracingZone( _ctx, 1 );
2993
2994 /* So we need to calculate the shortest jump. */
2995 for ( int i = 0; i < array_size( systems_stack ); i++ ) {
2996 StarSystem *sys = &systems_stack[i];
2998 /* Save jump indexes. */
2999 for ( int j = 0; j < array_size( sys->jumps ); j++ )
3000 sys->jumps[j].targetid = sys->jumps[j].target->id;
3001 }
3002
3003 NTracingZoneEnd( _ctx );
3004}
3005
3010{
3011 NTracingZone( _ctx, 1 );
3012
3013 for ( int i = 0; i < array_size( systems_stack ); i++ ) {
3014 StarSystem *sys = &systems_stack[i];
3015 for ( int j = 0; j < array_size( sys->spobsid ); j++ )
3016 sys->spobs[j] = &spob_stack[sys->spobsid[j]];
3017 }
3018
3019 NTracingZoneEnd( _ctx );
3020}
3021
3029static int system_parseAsteroidField( const xmlNodePtr node, StarSystem *sys )
3030{
3031 AsteroidAnchor *a;
3032 xmlNodePtr cur;
3033 int pos;
3034
3035 /* Allocate more space. */
3036 a = &array_grow( &sys->asteroids );
3037
3038 /* Initialize stuff. */
3039 pos = 1;
3041
3042 /* Parse label if available. */
3043 xmlr_attr_strd( node, "label", a->label );
3044
3045 /* Parse data. */
3046 cur = node->xmlChildrenNode;
3047 do {
3048 xml_onlyNodes( cur );
3049
3050 xmlr_float( cur, "density", a->density );
3051 xmlr_float( cur, "radius", a->radius );
3052 xmlr_float( cur, "maxspeed", a->maxspeed );
3053 xmlr_float( cur, "accel", a->accel );
3054
3055 /* Handle types of asteroids. */
3056 if ( xml_isNode( cur, "group" ) ) {
3057 double w;
3058 const char *name = xml_get( cur );
3059 xmlr_attr_float_def( cur, "weight", w, 1. );
3060 array_push_back( &a->groups, astgroup_getName( name ) );
3061 array_push_back( &a->groupsw, w );
3062 continue;
3063 }
3064
3065 /* Handle position. */
3066 if ( xml_isNode( cur, "pos" ) ) {
3067 double x, y;
3068 pos = 1;
3069 xmlr_attr_float( cur, "x", x );
3070 xmlr_attr_float( cur, "y", y );
3071
3072 /* Set position. */
3073 vec2_cset( &a->pos, x, y );
3074 continue;
3075 }
3076
3077 WARN( _( "Asteroid Field in Star System '%s' has unknown node '%s'" ),
3078 sys->name, node->name );
3079 } while ( xml_nextNode( cur ) );
3080
3081 /* Update internals. */
3083
3084#define MELEMENT( o, s ) \
3085 if ( o ) \
3086 WARN( _( "Asteroid Field in Star System '%s' has missing/invalid '%s' " \
3087 "element" ), \
3088 sys->name, s )
3089 MELEMENT( !pos, "pos" );
3090 MELEMENT( a->radius <= 0., "radius" );
3091 MELEMENT( array_size( a->groups ) == 0, "groups" );
3092#undef MELEMENT
3093
3094 return 0;
3095}
3096
3104static int system_parseAsteroidExclusion( const xmlNodePtr node,
3105 StarSystem *sys )
3106{
3108 xmlNodePtr cur;
3109 double x, y;
3110 int pos;
3111
3112 /* Allocate more space. */
3113 a = &array_grow( &sys->astexclude );
3114 memset( a, 0, sizeof( *a ) );
3115
3116 /* Parse label if available. */
3117 xmlr_attr_strd( node, "label", a->label );
3118
3119 /* Initialize stuff. */
3120 pos = 0;
3121
3122 /* Parse data. */
3123 cur = node->xmlChildrenNode;
3124 do {
3125 xml_onlyNodes( cur );
3126
3127 xmlr_float( cur, "radius", a->radius );
3128
3129 /* Handle position. */
3130 if ( xml_isNode( cur, "pos" ) ) {
3131 pos = 1;
3132 xmlr_attr_float( cur, "x", x );
3133 xmlr_attr_float( cur, "y", y );
3134
3135 /* Set position. */
3136 vec2_cset( &a->pos, x, y );
3137 continue;
3138 }
3139 WARN( _( "Asteroid Exclusion Zone in Star System '%s' has unknown node "
3140 "'%s'" ),
3141 sys->name, node->name );
3142 } while ( xml_nextNode( cur ) );
3143
3144#define MELEMENT( o, s ) \
3145 if ( o ) \
3146 WARN( _( "Asteroid Exclusion Zone in Star System '%s' has missing/invalid " \
3147 "'%s' element" ), \
3148 sys->name, s )
3149 MELEMENT( !pos, "pos" );
3150 MELEMENT( a->radius <= 0., "radius" );
3151#undef MELEMENT
3152
3153 return 0;
3154}
3155
3163static int system_parse( StarSystem *sys, const char *filename )
3164{
3165 xmlNodePtr node, parent;
3166 xmlDocPtr doc;
3167 uint32_t flags;
3168
3169 /* Load the file. */
3170 doc = xml_parsePhysFS( filename );
3171 if ( doc == NULL )
3172 return -1;
3173
3174 parent = doc->xmlChildrenNode; /* first spob node */
3175 if ( parent == NULL ) {
3176 WARN( _( "Malformed %s file: does not contain elements" ), filename );
3177 xmlFreeDoc( doc );
3178 return -1;
3179 }
3180
3181 /* Clear memory for safe defaults. */
3182 system_init( sys );
3183 flags = 0;
3184 sys->ownerpresence = 0.;
3185 sys->nebu_hue = NEBULA_DEFAULT_HUE;
3186 sys->spacedust = -1;
3187
3188 xmlr_attr_strd( parent, "name", sys->name );
3189
3190 node = parent->xmlChildrenNode;
3191 do { /* load all the data */
3192 /* Only handle nodes. */
3193 xml_onlyNodes( node );
3194
3195 xmlr_strd( node, "display", sys->display );
3196 if ( xml_isNode( node, "pos" ) ) {
3197 flags |= FLAG_POSSET;
3198 xmlr_attr_float( node, "x", sys->pos.x );
3199 xmlr_attr_float( node, "y", sys->pos.y );
3200 continue;
3201 } else if ( xml_isNode( node, "general" ) ) {
3202 xmlNodePtr cur = node->children;
3203 do {
3204 xml_onlyNodes( cur );
3205 xmlr_strd( cur, "background", sys->background );
3206 xmlr_strd( cur, "map_shader", sys->map_shader );
3207 xmlr_strd( cur, "features", sys->features );
3208 xmlr_int( cur, "spacedust", sys->spacedust );
3209 if ( xml_isNode(
3210 cur, "stars" ) ) { /* Renamed to "spacedust" in 0.11.0. TODO
3211 remove sometime around 0.13.0. */
3212 sys->spacedust = xml_getInt( cur );
3213 WARN( _( "System '%s' is using deprecated field 'stars'. Use "
3214 "'spacedust' instead!" ),
3215 sys->name );
3216 }
3217 xmlr_float( cur, "radius", sys->radius );
3218 if ( xml_isNode( cur, "interference" ) ) {
3219 flags |= FLAG_INTERFERENCESET;
3220 sys->interference = xml_getFloat( cur );
3221 continue;
3222 }
3223 if ( xml_isNode( cur, "nebula" ) ) {
3224 int trails = 0;
3225 int hidedmg = 0;
3226 xmlr_attr_float( cur, "volatility", sys->nebu_volatility );
3227 xmlr_attr_float_def( cur, "hue", sys->nebu_hue,
3228 NEBULA_DEFAULT_HUE );
3229 xmlr_attr_int( cur, "trails", trails );
3230 xmlr_attr_int( cur, "hidenebuladamage", hidedmg );
3231 sys->nebu_density = xml_getFloat( cur );
3232 if ( trails || ( sys->nebu_density > 0. ) )
3233 sys_setFlag( sys, SYSTEM_NEBULATRAIL );
3234 if ( hidedmg )
3235 sys_setFlag( sys, SYSTEM_HIDENEBULADAMAGE );
3236 continue;
3237 }
3238 if ( xml_isNode( cur, "nolanes" ) ) {
3239 sys_setFlag( sys, SYSTEM_NOLANES );
3240 continue;
3241 }
3242 DEBUG( _( "Unknown node '%s' in star system '%s'" ), node->name,
3243 sys->name );
3244 } while ( xml_nextNode( cur ) );
3245 continue;
3246 }
3247 /* Loads all the spobs. */
3248 else if ( xml_isNode( node, "spobs" ) ) {
3249 xmlNodePtr cur = node->children;
3250 do {
3251 xml_onlyNodes( cur );
3252 if ( xml_isNode( cur, "spob" ) ) {
3253 system_addSpob( sys, xml_get( cur ) );
3254 continue;
3255 }
3256 if ( xml_isNode( cur, "spob_virtual" ) ) {
3257 system_addVirtualSpob( sys, xml_get( cur ) );
3258 continue;
3259 }
3260 DEBUG( _( "Unknown node '%s' in star system '%s'" ), node->name,
3261 sys->name );
3262 } while ( xml_nextNode( cur ) );
3263 continue;
3264 }
3265
3266 if ( xml_isNode( node, "asteroids" ) ) {
3267 xmlNodePtr cur = node->children;
3268 do {
3269 xml_onlyNodes( cur );
3270 if ( xml_isNode( cur, "asteroid" ) )
3271 system_parseAsteroidField( cur, sys );
3272 else if ( xml_isNode( cur, "exclusion" ) )
3274 } while ( xml_nextNode( cur ) );
3275 }
3276
3277 if ( xml_isNode( node, "stats" ) ) {
3278 xmlNodePtr cur = node->children;
3279 do {
3280 xml_onlyNodes( cur );
3281 ShipStatList *ll = ss_listFromXML( cur );
3282 if ( ll != NULL ) {
3283 ll->next = sys->stats;
3284 sys->stats = ll;
3285 continue;
3286 }
3287 WARN( _( "System '%s' has unknown stat '%s'." ), sys->name,
3288 cur->name );
3289 } while ( xml_nextNode( cur ) );
3290 continue;
3291 }
3292
3293 if ( xml_isNode( node, "tags" ) ) {
3294 xmlNodePtr cur = node->children;
3295 sys->tags = array_create( char * );
3296 do {
3297 xml_onlyNodes( cur );
3298 if ( xml_isNode( cur, "tag" ) ) {
3299 const char *tmp = xml_get( cur );
3300 if ( tmp != NULL )
3301 array_push_back( &sys->tags, strdup( tmp ) );
3302 continue;
3303 }
3304 WARN( _( "System '%s' has unknown node in tags '%s'." ), sys->name,
3305 cur->name );
3306 } while ( xml_nextNode( cur ) );
3307 continue;
3308 }
3309
3310 /* Avoid warnings. */
3311 if ( xml_isNode( node, "jumps" ) || xml_isNode( node, "asteroids" ) )
3312 continue;
3313
3314 DEBUG( _( "Unknown node '%s' in star system '%s'" ), node->name,
3315 sys->name );
3316 } while ( xml_nextNode( node ) );
3317
3318 ss_sort( &sys->stats );
3319 array_shrink( &sys->spobs );
3320 array_shrink( &sys->spobsid );
3321 array_shrink( &sys->asteroids );
3322 array_shrink( &sys->astexclude );
3323
3324 /* Convert hue from 0 to 359 value to 0 to 1 value. */
3325 sys->nebu_hue /= 360.;
3326
3327 /* Load the shader. */
3328 if ( sys->map_shader != NULL )
3329 sys->ms = mapshader_get( sys->map_shader );
3330
3331#define MELEMENT( o, s ) \
3332 if ( o ) \
3333 WARN( _( "Star System '%s' missing '%s' element" ), sys->name, s )
3334 if ( sys->name == NULL )
3335 WARN( _( "Star System '%s' missing 'name' tag" ), sys->name );
3336 MELEMENT( ( flags & FLAG_POSSET ) == 0, "pos" );
3337 MELEMENT( sys->spacedust < 0, "spacedust" );
3338 MELEMENT( sys->radius == 0., "radius" );
3339 MELEMENT( ( flags & FLAG_INTERFERENCESET ) == 0, "inteference" );
3340#undef MELEMENT
3341
3342 xmlFreeDoc( doc );
3343
3344 return 0;
3345}
3346
3350static int sys_cmpSysFaction( const void *a, const void *b )
3351{
3352 SystemPresence *spa, *spb;
3353
3354 spa = (SystemPresence *)a;
3355 spb = (SystemPresence *)b;
3356
3357 /* Compare value. */
3358 if ( spa->value < spb->value )
3359 return +1;
3360 else if ( spa->value > spb->value )
3361 return -1;
3362
3363 /* Compare faction id. */
3364 if ( spa->faction < spb->faction )
3365 return +1;
3366 else if ( spa->faction > spb->faction )
3367 return -1;
3368
3369 return 0;
3370}
3371
3377void system_setFaction( StarSystem *sys )
3378{
3379 /* Sort presences in descending order. */
3380 if ( array_size( sys->presence ) != 0 )
3381 qsort( sys->presence, array_size( sys->presence ),
3382 sizeof( SystemPresence ), sys_cmpSysFaction );
3383
3384 sys->faction = -1;
3385 for ( int i = 0; i < array_size( sys->presence ); i++ ) {
3386 for ( int j = 0; j < array_size( sys->spobs );
3387 j++ ) {
3388 Spob *pnt = sys->spobs[j];
3389
3390 if ( pnt->presence.faction != sys->presence[i].faction )
3391 continue;
3392
3393 sys->faction = pnt->presence.faction;
3394 return;
3395 }
3396 }
3397}
3398
3406static int system_parseJumpPoint( const xmlNodePtr node, StarSystem *sys )
3407{
3408 JumpPoint *j;
3409 char *buf;
3410 xmlNodePtr cur;
3411 double x, y;
3412 StarSystem *target;
3413 int pos;
3414
3415 /* Get target. */
3416 xmlr_attr_strd( node, "target", buf );
3417 if ( buf == NULL ) {
3418 WARN( _( "JumpPoint node for system '%s' has no target attribute." ),
3419 sys->name );
3420 return -1;
3421 }
3422 target = system_get( buf );
3423 if ( target == NULL ) {
3424 WARN( _( "JumpPoint node for system '%s' has invalid target '%s'." ),
3425 sys->name, buf );
3426 free( buf );
3427 return -1;
3428 }
3429 free( buf );
3430
3431#ifdef DEBUGGING
3432 for ( int i = 0; i < array_size( sys->jumps ); i++ ) {
3433 JumpPoint *jp = &sys->jumps[i];
3434 if ( jp->targetid != target->id )
3435 continue;
3436
3437 WARN( _( "Star System '%s' has duplicate jump point to '%s'." ),
3438 sys->name, target->name );
3439 break;
3440 }
3441#endif /* DEBUGGING */
3442
3443 /* Allocate more space. */
3444 j = &array_grow( &sys->jumps );
3445 memset( j, 0, sizeof( JumpPoint ) );
3446
3447 /* Set some stuff. */
3448 j->from = sys;
3449 j->target = target;
3450 j->targetid = j->target->id;
3451 j->radius = 200.;
3452
3453 pos = 0;
3454
3455 /* Parse data. */
3456 cur = node->xmlChildrenNode;
3457 do {
3458 xmlr_float( cur, "radius", j->radius );
3459
3460 /* Handle position. */
3461 if ( xml_isNode( cur, "pos" ) ) {
3462 pos = 1;
3463 xmlr_attr_float( cur, "x", x );
3464 xmlr_attr_float( cur, "y", y );
3465
3466 /* Set position. */
3467 vec2_cset( &j->pos, x, y );
3468 } else if ( xml_isNode( cur, "autopos" ) )
3469 jp_setFlag( j, JP_AUTOPOS );
3470 else if ( xml_isNode( cur, "hidden" ) )
3471 jp_setFlag( j, JP_HIDDEN );
3472 else if ( xml_isNode( cur, "exitonly" ) )
3473 jp_setFlag( j, JP_EXITONLY );
3474 else if ( xml_isNode( cur, "nolanes" ) )
3475 jp_setFlag( j, JP_NOLANES );
3476 else if ( xml_isNode( cur, "hide" ) ) {
3477 xmlr_float( cur, "hide", j->hide );
3478 }
3479 } while ( xml_nextNode( cur ) );
3480
3481 if ( !jp_isFlag( j, JP_AUTOPOS ) && !pos )
3482 WARN( _( "JumpPoint in system '%s' is missing pos element but does not "
3483 "have autopos flag." ),
3484 sys->name );
3485
3486 return 0;
3487}
3488
3495static int system_parseJumps( StarSystem *sys )
3496{
3497 xmlNodePtr parent, node;
3498 xmlDocPtr doc;
3499
3500 doc = xml_parsePhysFS( sys->filename );
3501 if ( doc == NULL )
3502 return -1;
3503
3504 parent = doc->xmlChildrenNode; /* first spob node */
3505 if ( parent == NULL ) {
3506 xmlFreeDoc( doc );
3507 return -1;
3508 }
3509
3510 node = parent->xmlChildrenNode;
3511 do { /* load all the data */
3512 if ( xml_isNode( node, "jumps" ) ) {
3513 xmlNodePtr cur = node->children;
3514 do {
3515 if ( xml_isNode( cur, "jump" ) )
3516 system_parseJumpPoint( cur, sys );
3517 } while ( xml_nextNode( cur ) );
3518 }
3519 } while ( xml_nextNode( node ) );
3520
3521 array_shrink( &sys->jumps );
3522
3523 xmlFreeDoc( doc );
3524 return 0;
3525}
3526
3532int space_load( void )
3533{
3534 /* Loading. */
3535 systems_loading = 1;
3536
3537 /* Create some arrays. */
3538 spobname_stack = array_create( char * );
3539 systemname_stack = array_create( char * );
3540
3541 /* Load jump point graphic - must be before systems_load(). */
3542 jumppoint_gfx = gl_newSprite( SPOB_GFX_SPACE_PATH "jumppoint.webp", 4, 4,
3543 OPENGL_TEX_MIPMAPS );
3544 jumpbuoy_gfx = gl_newImage( SPOB_GFX_SPACE_PATH "jumpbuoy.webp", 0 );
3545
3546 /* Load data. */
3547 spobs_load();
3550 systems_load();
3551
3552 /* Done loading. */
3553 systems_loading = 0;
3554
3555 /* Reconstruction. */
3559
3560 /* Calculate commodity prices (sinusoidal model). */
3562
3563 return 0;
3564}
3565
3569int space_loadLua( void )
3570{
3571 int ret = 0;
3572 for ( int i = 0; i < array_size( spob_stack ); i++ )
3573 ret |= spob_luaInit( &spob_stack[i] );
3574 return ret;
3575}
3576
3587static int systems_load( void )
3588{
3589#if DEBUGGING
3590 Uint32 time = SDL_GetTicks();
3591#endif /* DEBUGGING */
3592 char **system_files;
3593
3594 /* Allocate if needed. */
3595 if ( systems_stack == NULL )
3596 systems_stack = array_create( StarSystem );
3597
3598 system_files = ndata_listRecursive( SYSTEM_DATA_PATH );
3599
3600 /*
3601 * First pass - loads all the star systems_stack.
3602 */
3603 for ( int i = 0; i < array_size( system_files ); i++ ) {
3604 StarSystem sys;
3605
3606 if ( !ndata_matchExt( system_files[i], "xml" ) )
3607 continue;
3608
3609 int ret = system_parse( &sys, system_files[i] );
3610 if ( ret == 0 ) {
3611 sys.filename = system_files[i];
3612 sys.id = array_size( systems_stack );
3613
3614 /* Update asteroid info. */
3615 system_updateAsteroids( &sys );
3616
3618
3619 /* Render if necessary. */
3621 }
3622 }
3623 qsort( systems_stack, array_size( systems_stack ), sizeof( StarSystem ),
3624 system_cmp );
3625 for ( int j = 0; j < array_size( systems_stack ); j++ ) {
3626 systems_stack[j].id = j;
3627 systems_stack[j].note = NULL; /* just to be sure */
3628 }
3629
3630 /*
3631 * Second pass - loads all the jump routes.
3632 */
3633 for ( int i = 0; i < array_size( systems_stack ); i++ )
3635
3636 /* Clean up. */
3637 array_free( system_files );
3638
3639#if DEBUGGING
3640 if ( conf.devmode ) {
3641 DEBUG( n_( "Loaded %d Star System", "Loaded %d Star Systems",
3644 DEBUG( n_( " with %d Space Object in %.3f s",
3645 " with %d Space Objects in %.3f s",
3647 array_size( spob_stack ), ( SDL_GetTicks() - time ) / 1000. );
3648 } else {
3649 DEBUG( n_( "Loaded %d Star System", "Loaded %d Star Systems",
3652 DEBUG( n_( " with %d Space Object", " with %d Space Objects",
3655 }
3656#endif /* DEBUGGING */
3657
3658 return 0;
3659}
3660
3666void space_render( const double dt )
3667{
3668 if ( cur_system == NULL )
3669 return;
3670
3671 NTracingZone( _ctx, 1 );
3672
3673 if ( cur_system->nebu_density > 0. )
3674 nebu_render( dt );
3675 else
3676 background_render( dt );
3677
3678 NTracingZoneEnd( _ctx );
3679}
3680
3686void space_renderOverlay( const double dt )
3687{
3688 if ( cur_system == NULL )
3689 return;
3690
3691 NTracingZone( _ctx, 1 );
3692
3693 /* Render the debris. */
3695
3696 /* Render overlay if necessary. */
3698
3699 if ( ( cur_system->nebu_density > 0. ) && !menu_isOpen( MENU_MAIN ) &&
3701 nebu_renderOverlay( dt );
3702
3703 NTracingZoneEnd( _ctx );
3704}
3705
3709void spobs_render( void )
3710{
3711 /* Must be a system. */
3712 if ( cur_system == NULL )
3713 return;
3714
3715 NTracingZone( _ctx, 1 );
3716
3717 /* Render the jumps. */
3718 for ( int i = 0; i < array_size( cur_system->jumps ); i++ )
3719 space_renderJumpPoint( &cur_system->jumps[i], i );
3720
3721 /* Render the spobs. */
3722 for ( int i = 0; i < array_size( cur_system->spobs ); i++ )
3723 space_renderSpob( cur_system->spobs[i] );
3724
3725 /* Render the asteroids & debris. */
3727
3728 /* Render gatherable stuff. */
3730
3731 NTracingZoneEnd( _ctx );
3732}
3733
3737static void space_renderJumpPoint( const JumpPoint *jp, int i )
3738{
3739 const glColour *c;
3740
3741 if ( !jp_isUsable( jp ) )
3742 return;
3743
3744 if ( ( player.p != NULL ) && ( i == player.p->nav_hyperspace ) &&
3745 ( pilot_isFlag( player.p, PILOT_HYPERSPACE ) ||
3747 c = &cGreen;
3748 else if ( jp_isFlag( jp, JP_HIDDEN ) )
3749 c = &cRed;
3750 else
3751 c = NULL;
3752
3753 gl_renderSprite( jumppoint_gfx, jp->pos.x, jp->pos.y, jp->sx, jp->sy, c );
3754
3755 /* Draw buoys next to "highway" jump points. */
3756 if ( jp->hide == 0. ) {
3757 gl_renderSprite( jumpbuoy_gfx, jp->pos.x + 200 * jp->sina,
3758 jp->pos.y + 200 * jp->cosa, 0, 0, NULL ); /* Left */
3759 gl_renderSprite( jumpbuoy_gfx, jp->pos.x + -200 * jp->sina,
3760 jp->pos.y + -200 * jp->cosa, 0, 0, NULL ); /* Right */
3761 }
3762}
3763
3767static void space_renderSpob( const Spob *p )
3768{
3769 if ( p->lua_render != LUA_NOREF ) {
3770 spob_luaInitMem( p );
3771 /* TODO do a clip test first. */
3772 lua_rawgeti( naevL, LUA_REGISTRYINDEX, p->lua_render ); /* f */
3773 if ( nlua_pcall( p->lua_env, 0, 0 ) ) {
3774 WARN( _( "Spob '%s' failed to run '%s':\n%s" ), p->name, "render",
3775 lua_tostring( naevL, -1 ) );
3776 lua_pop( naevL, 1 );
3777 }
3778 } else if ( p->gfx_space3d ) {
3779 double s = p->gfx_space3d_size;
3780 double z = cam_getZoom();
3781 double sz = s * z;
3782 double x, y;
3783
3784 gl_gameToScreenCoords( &x, &y, p->pos.x, p->pos.y );
3785 if ( ( x < -sz ) || ( x > SCREEN_W + sz ) || ( y < -sz ) ||
3786 ( y > SCREEN_H + sz ) )
3787 return;
3788
3789 glBindFramebuffer( GL_FRAMEBUFFER, p->gfx_fbo );
3790 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
3791
3792 gltf_renderScene( p->gfx_fbo, p->gfx_space3d, 0, NULL, elapsed_time_mod,
3793 s * spob_aa_scale, NULL );
3794
3795 glBindFramebuffer( GL_FRAMEBUFFER, gl_screen.current_fbo );
3796
3797 gl_renderSprite( p->gfx_space, p->pos.x, p->pos.y, 0, 0, NULL );
3798 } else if ( p->gfx_space )
3799 gl_renderSprite( p->gfx_space, p->pos.x, p->pos.y, 0, 0, NULL );
3800}
3801
3805static void space_updateSpob( const Spob *p, double dt, double real_dt )
3806{
3807 if ( p->lua_update == LUA_NOREF )
3808 return;
3809 /* TODO do a clip test first. */
3810 spob_luaInitMem( p );
3811 lua_rawgeti( naevL, LUA_REGISTRYINDEX, p->lua_update ); /* f */
3812 lua_pushnumber( naevL, dt ); /* f, dt */
3813 lua_pushnumber( naevL, real_dt ); /* f, real_dt */
3814 if ( nlua_pcall( p->lua_env, 2, 0 ) ) {
3815 WARN( _( "Spob '%s' failed to run '%s':\n%s" ), p->name, "update",
3816 lua_tostring( naevL, -1 ) );
3817 lua_pop( naevL, 1 );
3818 }
3819}
3820
3824void space_exit( void )
3825{
3826 /* Free standalone graphic textures */
3828 jumppoint_gfx = NULL;
3830 jumpbuoy_gfx = NULL;
3831
3832 /* Free the names. */
3835
3836 /* Free the spobs. */
3837 for ( int i = 0; i < array_size( spob_stack ); i++ ) {
3838 Spob *spb = &spob_stack[i];
3839
3840 free( spb->filename );
3841 free( spb->name );
3842 free( spb->display );
3843 free( spb->feature );
3844 free( spb->lua_file );
3845 free( spb->lua_file_raw );
3846 free( spb->class );
3847 free( spb->description );
3848 free( spb->bar_description );
3849 for ( int j = 0; j < array_size( spb->tags ); j++ )
3850 free( spb->tags[j] );
3851 array_free( spb->tags );
3852
3853 /* graphics */
3854 gltf_free( spb->gfx_space3d );
3855 free( spb->gfx_space3dName );
3856 free( spb->gfx_space3dPath );
3857 gl_freeTexture( spb->gfx_space );
3858 free( spb->gfx_spaceName );
3859 free( spb->gfx_spacePath );
3860 free( spb->gfx_exterior );
3861 free( spb->gfx_exteriorPath );
3862 free( spb->gfx_comm );
3863 free( spb->gfx_commPath );
3864
3865 /* Landing. */
3866 free( spb->land_msg );
3867
3868 /* tech */
3869 if ( spb->tech != NULL )
3870 tech_groupDestroy( spb->tech );
3871
3872 /* commodities */
3873 array_free( spb->commodities );
3874 array_free( spb->commodityPrice );
3875
3876 /* Lua. */
3877 nlua_freeEnv( spb->lua_env );
3878 }
3880
3881 for ( int i = 0; i < array_size( spob_lua_stack ); i++ )
3882 spob_lua_free( &spob_lua_stack[i] );
3884
3885 for ( int i = 0; i < array_size( vspob_stack ); i++ ) {
3886 VirtualSpob *va = &vspob_stack[i];
3887 free( va->name );
3888 array_free( va->presences );
3889 }
3891
3892 /* Free the systems. */
3893 for ( int i = 0; i < array_size( systems_stack ); i++ ) {
3894 StarSystem *sys = &systems_stack[i];
3895
3896 free( sys->filename );
3897 free( sys->name );
3898 free( sys->display );
3899 free( sys->background );
3900 free( sys->map_shader );
3901 free( sys->features );
3902 free( sys->note );
3903 array_free( sys->jumps );
3904 array_free( sys->presence );
3905 array_free( sys->spobs );
3906 array_free( sys->spobsid );
3907 array_free( sys->spobs_virtual );
3908
3909 for ( int j = 0; j < array_size( sys->tags ); j++ )
3910 free( sys->tags[j] );
3911 array_free( sys->tags );
3912
3913 /* Free the asteroids. */
3914 for ( int j = 0; j < array_size( sys->asteroids ); j++ )
3915 asteroid_freeAnchor( &sys->asteroids[j] );
3916 for ( int j = 0; j < array_size( sys->astexclude ); j++ )
3917 asteroid_freeExclude( &sys->astexclude[j] );
3918 array_free( sys->asteroids );
3919 array_free( sys->astexclude );
3920
3921 ss_free( sys->stats );
3922 }
3924 systems_stack = NULL;
3925
3926 /* Free asteroids stuff. */
3928
3929 /* Free the gatherable stack. */
3931
3932 /* Free the map shaders. */
3933 for ( int i = 0; i < array_size( mapshaders ); i++ ) {
3934 MapShader *ms = mapshaders[i];
3935 free( ms->name );
3936 glDeleteProgram( ms->program );
3937 free( ms );
3938 }
3940}
3941
3946{
3947 for ( int i = 0; i < array_size( systems_stack ); i++ ) {
3948 StarSystem *sys = &systems_stack[i];
3949 sys_rmFlag( sys, SYSTEM_KNOWN );
3950 sys_rmFlag( sys, SYSTEM_HIDDEN );
3951 sys_rmFlag( sys, SYSTEM_PMARKED );
3952 for ( int j = 0; j < array_size( sys->jumps ); j++ )
3953 jp_rmFlag( &sys->jumps[j], JP_KNOWN );
3954 free( sys->note );
3955 sys->note = NULL;
3956 }
3957 for ( int j = 0; j < array_size( spob_stack ); j++ )
3958 spob_rmFlag( &spob_stack[j], SPOB_KNOWN );
3959}
3960
3965{
3966 for ( int i = 0; i < array_size( systems_stack ); i++ ) {
3967 StarSystem *sys = &systems_stack[i];
3968 sys_rmFlag( sys, SYSTEM_MARKED );
3969 sys->markers_computer = 0;
3970 sys->markers_plot = 0;
3971 sys->markers_high = 0;
3972 sys->markers_low = 0;
3973 }
3974 for ( int i = 0; i < array_size( spob_stack ); i++ ) {
3975 Spob *pnt = &spob_stack[i];
3976 spob_rmFlag( pnt, SPOB_MARKED );
3977 pnt->markers = 0;
3978 }
3979}
3980
3985{
3986 for ( int i = 0; i < array_size( systems_stack ); i++ )
3987 sys_rmFlag( &systems_stack[i], SYSTEM_CMARKED );
3988}
3989
3990static int space_addMarkerSystem( int sysid, MissionMarkerType type )
3991{
3992 StarSystem *ssys;
3993 int *markers;
3994
3995 /* Get the system. */
3996 ssys = system_getIndex( sysid );
3997 if ( ssys == NULL )
3998 return -1;
3999
4000 /* Get the marker. */
4001 switch ( type ) {
4002 case SYSMARKER_COMPUTER:
4003 markers = &ssys->markers_computer;
4004 break;
4005 case SYSMARKER_LOW:
4006 markers = &ssys->markers_low;
4007 break;
4008 case SYSMARKER_HIGH:
4009 markers = &ssys->markers_high;
4010 break;
4011 case SYSMARKER_PLOT:
4012 markers = &ssys->markers_plot;
4013 break;
4014 default:
4015 WARN( _( "Unknown marker type." ) );
4016 return -1;
4017 }
4018
4019 /* Decrement markers. */
4020 ( *markers )++;
4021 sys_setFlag( ssys, SYSTEM_MARKED );
4022
4023 return 0;
4024}
4025
4026static int space_addMarkerSpob( int pntid, MissionMarkerType type )
4027{
4028 const char *sys;
4029 MissionMarkerType stype;
4030 Spob *pnt = spob_getIndex( pntid );
4031 if ( pnt == NULL )
4032 return -1;
4033
4034 /* Mark spob. */
4035 pnt->markers++;
4036 spob_setFlag( pnt, SPOB_MARKED );
4037
4038 /* Now try to mark system. */
4039 sys = spob_getSystemName( pnt->name );
4040 if ( sys == NULL ) {
4041 WARN( _( "Marking spob '%s' that is not in any system!" ), pnt->name );
4042 return 0;
4043 }
4044 stype = mission_markerTypeSpobToSystem( type );
4045 return space_addMarkerSystem( system_index( system_get( sys ) ), stype );
4046}
4047
4055int space_addMarker( int objid, MissionMarkerType type )
4056{
4057 switch ( type ) {
4058 case SYSMARKER_COMPUTER:
4059 case SYSMARKER_LOW:
4060 case SYSMARKER_HIGH:
4061 case SYSMARKER_PLOT:
4062 return space_addMarkerSystem( objid, type );
4063 case SPOBMARKER_COMPUTER:
4064 case SPOBMARKER_LOW:
4065 case SPOBMARKER_HIGH:
4066 case SPOBMARKER_PLOT:
4067 return space_addMarkerSpob( objid, type );
4068 default:
4069 WARN( _( "Unknown marker type." ) );
4070 return -1;
4071 }
4072 return 0;
4073}
4074
4075static int space_rmMarkerSystem( int sys, MissionMarkerType type )
4076{
4077 StarSystem *ssys;
4078 int *markers;
4079
4080 /* Get the system. */
4081 ssys = system_getIndex( sys );
4082 if ( ssys == NULL )
4083 return -1;
4084
4085 /* Get the marker. */
4086 switch ( type ) {
4087 case SYSMARKER_COMPUTER:
4088 markers = &ssys->markers_computer;
4089 break;
4090 case SYSMARKER_LOW:
4091 markers = &ssys->markers_low;
4092 break;
4093 case SYSMARKER_HIGH:
4094 markers = &ssys->markers_high;
4095 break;
4096 case SYSMARKER_PLOT:
4097 markers = &ssys->markers_plot;
4098 break;
4099 default:
4100 WARN( _( "Unknown marker type." ) );
4101 return -1;
4102 }
4103
4104 /* Decrement markers. */
4105 ( *markers )--;
4106 if ( *markers <= 0 ) {
4107 sys_rmFlag( ssys, SYSTEM_MARKED );
4108 ( *markers ) = 0;
4109 }
4110
4111 return 0;
4112}
4113
4114static int space_rmMarkerSpob( int pntid, MissionMarkerType type )
4115{
4116 (void)type;
4117 const char *sys;
4118 MissionMarkerType stype;
4119 Spob *pnt = spob_getIndex( pntid );
4120
4121 /* Remove spob marker. */
4122 pnt->markers--;
4123 if ( pnt->markers <= 0 )
4124 spob_rmFlag( pnt, SPOB_MARKED );
4125
4126 /* Now try to remove system. */
4127 sys = spob_getSystemName( pnt->name );
4128 if ( sys == NULL )
4129 return 0;
4130 stype = mission_markerTypeSpobToSystem( type );
4131 return space_rmMarkerSystem( system_index( system_get( sys ) ), stype );
4132}
4133
4141int space_rmMarker( int objid, MissionMarkerType type )
4142{
4143 switch ( type ) {
4144 case SYSMARKER_COMPUTER:
4145 case SYSMARKER_LOW:
4146 case SYSMARKER_HIGH:
4147 case SYSMARKER_PLOT:
4148 return space_rmMarkerSystem( objid, type );
4149 case SPOBMARKER_COMPUTER:
4150 case SPOBMARKER_LOW:
4151 case SPOBMARKER_HIGH:
4152 case SPOBMARKER_PLOT:
4153 return space_rmMarkerSpob( objid, type );
4154 default:
4155 WARN( _( "Unknown marker type." ) );
4156 return -1;
4157 }
4158}
4159
4166int space_playerSave( xmlTextWriterPtr writer )
4167{
4168 xmlw_startElem( writer, "space" );
4169 for ( int i = 0; i < array_size( systems_stack ); i++ ) {
4170 StarSystem *sys = &systems_stack[i];
4171
4172 xmlw_startElem( writer, "known" );
4173 xmlw_attr( writer, "sys", "%s", sys->name );
4174 if ( sys_isKnown( sys ) )
4175 xmlw_attr( writer, "known", "1" );
4176 if ( sys_isFlag( sys, SYSTEM_PMARKED ) )
4177 xmlw_attr( writer, "pmarked", "1" );
4178 if ( sys->note != NULL )
4179 xmlw_attr( writer, "note", "%s", sys->note );
4180
4181 /* Save known spobs. */
4182 for ( int j = 0; j < array_size( sys->spobs ); j++ ) {
4183 if ( !spob_isKnown( sys->spobs[j] ) )
4184 continue; /* not known */
4185 xmlw_elem( writer, "spob", "%s", sys->spobs[j]->name );
4186 }
4187
4188 /* Save known Jump points. */
4189 for ( int j = 0; j < array_size( sys->jumps ); j++ ) {
4190 if ( !jp_isKnown( &sys->jumps[j] ) )
4191 continue; /* not known */
4192 xmlw_elem( writer, "jump", "%s", ( &sys->jumps[j] )->target->name );
4193 }
4194
4195 /* Save presences if applicable. */
4196 for ( int j = 0; j < array_size( sys->presence ); j++ ) {
4197 const SystemPresence *sp = &sys->presence[j];
4198 if ( faction_isStatic( sp->faction ) )
4199 continue;
4200
4201 xmlw_startElem( writer, "faction" );
4202 xmlw_attr( writer, "name", "%s", faction_name( sp->faction ) );
4203 xmlw_str( writer, "%f", sp->local );
4204 xmlw_endElem( writer );
4205 }
4206
4207 xmlw_endElem( writer );
4208 }
4209 xmlw_endElem( writer ); /* "space" */
4210
4211 return 0;
4212}
4213
4221int space_playerLoad( xmlNodePtr parent, const char *version )
4222{
4223 xmlNodePtr node;
4224
4225 /* Whether or not using the old known system. */
4226 int oldknown = naev_versionCompareTarget( version, "0.12.0-alpha.3" ) < 0;
4227
4230
4231 node = parent->xmlChildrenNode;
4232 do {
4233 xmlNodePtr cur;
4234
4235 xml_onlyNodes( node );
4236 if ( !xml_isNode( node, "space" ) )
4237 continue;
4238
4239 cur = node->xmlChildrenNode;
4240 do {
4241 char *str;
4242 StarSystem *sys;
4243
4244 xml_onlyNodes( cur );
4245 if ( !xml_isNode( cur, "known" ) )
4246 continue;
4247
4248 /* Get the system. */
4249 xmlr_attr_strd( cur, "sys", str );
4250 sys = system_get( str );
4251 if ( sys == NULL ) {
4252 WARN( _( "Save trying to load information about system '%s', which "
4253 "is not found in the universe!" ),
4254 str );
4255 continue;
4256 }
4257 free( str );
4258
4259 if ( oldknown )
4260 sys_setFlag( sys, SYSTEM_KNOWN );
4261 else {
4262 xmlr_attr_strd( cur, "known", str );
4263 if ( str != NULL ) {
4264 sys_setFlag( sys, SYSTEM_KNOWN );
4265 free( str );
4266 }
4267 }
4268
4269 xmlr_attr_strd( cur, "pmarked", str );
4270 if ( str != NULL ) {
4271 sys_setFlag( sys, SYSTEM_PMARKED );
4272 free( str );
4273 }
4274
4275 xmlr_attr_strd( cur, "note", str );
4276 if ( str != NULL ) {
4277 xmlr_attr_strd( cur, "note", sys->note );
4278 free( str );
4279 }
4280 space_parseSaveNodes( cur, sys );
4281
4282 } while ( xml_nextNode( cur ) );
4283 } while ( xml_nextNode( node ) );
4284
4285 /* Update global standing. */
4287
4288 return 0;
4289}
4290
4298static int space_parseSaveNodes( xmlNodePtr parent, StarSystem *sys )
4299{
4300 xmlNodePtr node = parent->xmlChildrenNode;
4301 do {
4302 if ( xml_isNode( node, "spob" ) ) {
4303 Spob *spob = spob_get( xml_get( node ) );
4304 if ( spob != NULL ) /* Must exist */
4305 spob_setKnown( spob );
4306 } else if ( xml_isNode( node, "jump" ) ) {
4307 JumpPoint *jp = jump_get( xml_get( node ), sys );
4308 if ( jp != NULL ) /* Must exist */
4309 jp_setFlag( jp, JP_KNOWN );
4310 } else if ( xml_isNode( node, "faction" ) ) {
4311 char *buf;
4312 int f;
4313 xmlr_attr_strd( node, "name", buf );
4314 f = faction_get( buf );
4315 free( buf );
4316 if ( !faction_isFaction( f ) )
4317 continue;
4318
4319 /* Add presence. */
4320 for ( int i = 0; i < array_size( sys->presence ); i++ ) {
4321 SystemPresence *sp = &sys->presence[i];
4322 if ( sp->faction != f )
4323 continue;
4324 sp->local = xml_getFloat( node );
4325 break;
4326 }
4327 }
4328 } while ( xml_nextNode( node ) );
4329
4330 return 0;
4331}
4332
4339void system_presenceAddSpob( StarSystem *sys, const SpobPresence *ap )
4340{
4341 int curSpill;
4342 Queue q, qn;
4343 double spillfactor;
4344 int faction = ap->faction;
4345 double base = ap->base;
4346 double bonus = ap->bonus;
4347 double range = ap->range;
4348 int usehidden = faction_usesHiddenJumps( faction );
4349 const FactionGenerator *fgens;
4350
4351 /* Check for NULL and display a warning. */
4352 if ( sys == NULL ) {
4353 WARN( "sys == NULL" );
4354 return;
4355 }
4356
4357 /* Check that we have a valid faction. */
4358 if ( faction_isFaction( faction ) == 0 )
4359 return;
4360
4361 /* Check that we're actually adding any. */
4362 if ( ( base == 0. ) && ( bonus == 0. ) )
4363 return;
4364
4365 /* Get secondary if applicable. */
4366 fgens = faction_generators( faction );
4367
4368 /* Add the presence to the current system. */
4369 SystemPresence *sp = system_getFactionPresenceGrow( sys, faction );
4370 sp->base = MAX( sp->base, base );
4371 sp->bonus += bonus;
4372 sp->value = sp->base + sp->bonus;
4373 for ( int i = 0; i < array_size( fgens ); i++ ) {
4374 SystemPresence *spf = system_getFactionPresenceGrow( sys, fgens[i].id );
4375 spf->base = MAX( spf->base, MAX( 0., base * fgens[i].weight ) );
4376 spf->bonus += MAX( 0., bonus * fgens[i].weight );
4377 spf->value = spf->base + spf->bonus;
4378 }
4379
4380 /* If there's no range, we're done here. */
4381 if ( range < 1 )
4382 return;
4383
4384 /* Add the spill. */
4385 sys->spilled = 1;
4386 curSpill = 0;
4387 q = q_create();
4388 qn = q_create();
4389
4390 /* Create the initial queue consisting of sys adjacencies. */
4391 for ( int i = 0; i < array_size( sys->jumps ); i++ ) {
4392 if ( sys->jumps[i].target->spilled == 0 &&
4393 ( usehidden || !jp_isFlag( &sys->jumps[i], JP_HIDDEN ) ) &&
4394 !jp_isFlag( &sys->jumps[i], JP_EXITONLY ) ) {
4395 q_enqueue( q, sys->jumps[i].target );
4396 sys->jumps[i].target->spilled = 1;
4397 }
4398 }
4399
4400 /* If it's empty, something's wrong. */
4401 if ( q_isEmpty( q ) ) {
4402 /* Means system isn't connected. */
4403 /*WARN(_("q is empty after getting adjacencies of %s."), sys->name);*/
4404 q_destroy( q );
4405 q_destroy( qn );
4406 goto sys_cleanup;
4407 }
4408
4409 while ( curSpill < range ) {
4410 /* Pull one off the current range queue. */
4411 StarSystem *cur = q_dequeue( q );
4412
4413 /* Ran out of candidates before running out of spill range! */
4414 if ( cur == NULL )
4415 break;
4416
4417 /* Enqueue all its adjacencies to the next range queue. */
4418 for ( int i = 0; i < array_size( cur->jumps ); i++ ) {
4419 if ( cur->jumps[i].target->spilled == 0 &&
4420 ( usehidden || !jp_isFlag( &cur->jumps[i], JP_HIDDEN ) ) &&
4421 !jp_isFlag( &cur->jumps[i], JP_EXITONLY ) ) {
4422 q_enqueue( qn, cur->jumps[i].target );
4423 cur->jumps[i].target->spilled = 1;
4424 }
4425 }
4426
4427 /* Spill some presence. */
4428 sp = system_getFactionPresenceGrow( cur, faction );
4429 spillfactor = 1. / ( 2. + (double)curSpill );
4430 sp->base = MAX( sp->base, base * spillfactor );
4431 sp->bonus += bonus * spillfactor;
4432 sp->value = sp->base + sp->bonus;
4433
4434 for ( int i = 0; i < array_size( fgens ); i++ ) {
4435 SystemPresence *spf =
4436 system_getFactionPresenceGrow( cur, fgens[i].id );
4437 spf->base =
4438 MAX( spf->base, MAX( 0., base * spillfactor * fgens[i].weight ) );
4439 spf->bonus += MAX( 0., bonus * spillfactor * fgens[i].weight );
4440 spf->value = spf->base + spf->bonus;
4441 }
4442
4443 /* Check to see if we've finished this range and grab the next queue. */
4444 if ( q_isEmpty( q ) ) {
4445 curSpill++;
4446 q_destroy( q );
4447 q = qn;
4448 qn = q_create();
4449 }
4450 }
4451
4452 /* Destroy the queues. */
4453 q_destroy( q );
4454 q_destroy( qn );
4455
4456sys_cleanup:
4457 /* Clean up our mess. */
4458 for ( int i = 0; i < array_size( systems_stack ); i++ )
4459 systems_stack[i].spilled = 0;
4460 return;
4461}
4462
4463static SystemPresence *system_getFactionPresenceGrow( StarSystem *sys,
4464 int faction )
4465{
4466 /* Go through the array, looking for the faction. */
4467 for ( int i = 0; i < array_size( sys->presence ); i++ ) {
4468 if ( sys->presence[i].faction == faction )
4469 return &sys->presence[i];
4470 }
4471 SystemPresence *sp = system_getFactionPresence( sys, faction );
4472 if ( sp == NULL ) {
4473 sp = &array_grow( &sys->presence );
4474 memset( sp, 0, sizeof( SystemPresence ) );
4475 sp->faction = faction;
4476 }
4477 return sp;
4478}
4479SystemPresence *system_getFactionPresence( StarSystem *sys, int faction )
4480{
4481 /* Go through the array, looking for the faction. */
4482 for ( int i = 0; i < array_size( sys->presence ); i++ ) {
4483 if ( sys->presence[i].faction == faction )
4484 return &sys->presence[i];
4485 }
4486 return NULL;
4487}
4488const SystemPresence *system_getFactionPresenceConst( const StarSystem *sys,
4489 int faction )
4490{
4491 /* Go through the array, looking for the faction. */
4492 for ( int i = 0; i < array_size( sys->presence ); i++ ) {
4493 if ( sys->presence[i].faction == faction )
4494 return &sys->presence[i];
4495 }
4496 return NULL;
4497}
4498
4502double system_getReputation( const StarSystem *sys, int faction )
4503{
4504 int set;
4505 double val = faction_reputationOverride( faction, &set );
4506 if ( set )
4507 return val;
4508 const SystemPresence *sp = system_getFactionPresenceConst( sys, faction );
4509 if ( sp != NULL )
4510 return sp->local;
4511 return 0.;
4512}
4513
4518double system_getReputationOrGlobal( const StarSystem *sys, int faction )
4519{
4520 int set;
4521 double val = faction_reputationOverride( faction, &set );
4522 if ( set )
4523 return val;
4524 const SystemPresence *sp = system_getFactionPresenceConst( sys, faction );
4525 if ( sp != NULL )
4526 return sp->local;
4527 return faction_reputation( faction );
4528}
4529
4537double system_getPresence( const StarSystem *sys, int faction )
4538{
4539 /* Check for NULL and display a warning. */
4540#if DEBUGGING
4541 if ( sys == NULL ) {
4542 WARN( "sys == NULL" );
4543 return 0.;
4544 }
4545#endif /* DEBUGGING */
4546
4547 /* Go through the array, looking for the faction. */
4548 for ( int i = 0; i < array_size( sys->presence ); i++ ) {
4549 if ( sys->presence[i].faction == faction )
4550 return MAX( sys->presence[i].value, 0. );
4551 }
4552
4553 /* If it's not in there, it's zero. */
4554 return 0.;
4555}
4556
4566double system_getPresenceFull( const StarSystem *sys, int faction, double *base,
4567 double *bonus )
4568{
4569 /* Check for NULL and display a warning. */
4570#if DEBUGGING
4571 if ( sys == NULL ) {
4572 WARN( "sys == NULL" );
4573 return 0;
4574 }
4575#endif /* DEBUGGING */
4576
4577 /* Go through the array, looking for the faction. */
4578 for ( int i = 0; i < array_size( sys->presence ); i++ ) {
4579 if ( sys->presence[i].faction == faction ) {
4580 *base = sys->presence[i].base;
4581 *bonus = sys->presence[i].bonus;
4582 return MAX( sys->presence[i].value, 0 );
4583 }
4584 }
4585
4586 /* If it's not in there, it's zero. */
4587 *base = 0.;
4588 *bonus = 0.;
4589 return 0.;
4590}
4591
4597void system_addAllSpobsPresence( StarSystem *sys )
4598{
4599 /* Check for NULL and display a warning. */
4600#if DEBUGGING
4601 if ( sys == NULL ) {
4602 WARN( "sys == NULL" );
4603 return;
4604 }
4605#endif /* DEBUGGING */
4606
4607 /* Real spobs. */
4608 for ( int i = 0; i < array_size( sys->spobs ); i++ )
4609 system_presenceAddSpob( sys, &sys->spobs[i]->presence );
4610
4611 /* Virtual spobs. */
4612 for ( int i = 0; i < array_size( sys->spobs_virtual ); i++ )
4613 for ( int j = 0; j < array_size( sys->spobs_virtual[i]->presences ); j++ )
4614 system_presenceAddSpob( sys, &sys->spobs_virtual[i]->presences[j] );
4615}
4616
4621{
4622 /* Reset the presence in each system. */
4623 for ( int i = 0; i < array_size( systems_stack ); i++ ) {
4624 StarSystem *sys = &systems_stack[i];
4625 /* We can't destroy the array or the player's local presence will get
4626 * lost. */
4627 for ( int j = 0; j < array_size( sys->presence ); j++ ) {
4628 SystemPresence *sp = &sys->presence[j];
4629 sp->base = 0.;
4630 sp->bonus = 0.;
4631 sp->value = 0.;
4632 }
4633 systems_stack[i].ownerpresence = 0.;
4634 }
4635
4636 /* Re-add presence to each system. */
4637 for ( int i = 0; i < array_size( systems_stack ); i++ )
4639
4640 /* Determine dominant faction. */
4641 for ( int i = 0; i < array_size( systems_stack ); i++ ) {
4643 systems_stack[i].ownerpresence =
4645 }
4646
4647 /* Have to redo the scheduler because everything changed. */
4648 /* TODO this actually ignores existing presence and will temporarily increase
4649 * system presence more than normal... */
4650 if ( cur_system != NULL )
4651 system_scheduler( 0., 1 );
4652}
4653
4660int system_hasSpob( const StarSystem *sys )
4661{
4662 /* Check for NULL and display a warning. */
4663 if ( sys == NULL ) {
4664 WARN( "sys == NULL" );
4665 return 0;
4666 }
4667
4668 /* Go through all the spobs and look for a real one. */
4669 for ( int i = 0; i < array_size( sys->spobs ); i++ )
4670 return 1;
4671
4672 return 0;
4673}
4674
4678void system_rmCurrentPresence( StarSystem *sys, int faction, double amount )
4679{
4680 nlua_env env;
4681
4682 /* Ignore dynamic factions. */
4683 if ( faction_isDynamic( faction ) )
4684 return;
4685
4686 /* Remove the presence. */
4687 SystemPresence *presence = system_getFactionPresence( sys, faction );
4688 if ( presence == NULL )
4689 return;
4690 presence->curUsed = MAX( 0, presence->curUsed - amount );
4691
4692 /* Run lower hook. */
4693 env = faction_getScheduler( faction );
4694
4695 /* Run decrease function if applicable. */
4696 nlua_getenv( naevL, env, "decrease" ); /* f */
4697 if ( lua_isnil( naevL, -1 ) ) {
4698 lua_pop( naevL, 1 );
4699 return;
4700 }
4701 lua_pushnumber( naevL, presence->curUsed ); /* f, cur */
4702 lua_pushnumber( naevL, presence->value ); /* f, cur, max */
4703 lua_pushnumber( naevL, presence->timer ); /* f, cur, max, timer */
4704
4705 /* Actually run the function. */
4706 if ( nlua_pcall( env, 3, 1 ) ) { /* error has occurred */
4707 WARN( _( "Lua decrease script for faction '%s' : %s" ),
4708 faction_name( faction ), lua_tostring( naevL, -1 ) );
4709 lua_pop( naevL, 1 );
4710 return;
4711 }
4712
4713 /* Output is handled the same way. */
4714 if ( !lua_isnumber( naevL, -1 ) ) {
4715 WARN(
4716 _( "Lua spawn script for faction '%s' failed to return timer value." ),
4717 faction_name( presence->faction ) );
4718 lua_pop( naevL, 1 );
4719 return;
4720 }
4721 presence->timer = lua_tonumber( naevL, -1 );
4722 lua_pop( naevL, 1 );
4723}
4724
4732{
4733 space_landQueueSpob = pnt;
4734}
4735
4743const char *space_populationStr( const Spob *spb )
4744{
4745 static char pop[STRMAX_SHORT];
4746 double p;
4747
4748 if ( spb->lua_population != LUA_NOREF ) {
4749 spob_luaInitMem( spb );
4750 lua_rawgeti( naevL, LUA_REGISTRYINDEX, spb->lua_population ); /* f */
4751 if ( nlua_pcall( spb->lua_env, 0, 1 ) ) {
4752 WARN( _( "Spob '%s' failed to run '%s':\n%s" ), spb->name,
4753 "population", lua_tostring( naevL, -1 ) );
4754 lua_pop( naevL, 1 );
4755 return "";
4756 }
4757
4758 scnprintf( pop, sizeof( pop ), "%s", luaL_checkstring( naevL, -1 ) );
4759 lua_pop( naevL, 1 );
4760 return pop;
4761 }
4762
4763 /* Out of respect for the first version of this, do something fancy and
4764 * human-oriented. However, specifying a thousand/million/billion system
4765 * failed in a few ways: needing 2x as many cases as intended to avoid
4766 * silliness (1.0e10 -> 10000 million), and not being gettext-translatable to
4767 * other number systems like the Japanese one. */
4768 p = spb->population;
4769 if ( p < 1.0e3 )
4770 snprintf( pop, sizeof( pop ), "%.0f", p );
4771 else {
4772 char scratch[STRMAX_SHORT];
4773 const char *digits[] = { "\xe2\x81\xb0", "\xc2\xb9", "\xc2\xb2",
4774 "\xc2\xb3", "\xe2\x81\xb4", "\xe2\x81\xb5",
4775 "\xe2\x81\xb6", "\xe2\x81\xb7", "\xe2\x81\xb8",
4776 "\xe2\x81\xb9" };
4777 int state = 0, COEF = 0, E = 1, EXP = 4;
4778 size_t l = scnprintf( pop, sizeof( pop ), _( "roughly " ) );
4779 snprintf( scratch, sizeof( scratch ), "%.1e", p );
4780 for ( const char *c = scratch; *c; c++ ) {
4781 if ( state == COEF && *c != 'e' )
4782 l += scnprintf( &pop[l], sizeof( pop ) - l, "%c", *c );
4783 else if ( state == COEF ) {
4784 l += scnprintf( &pop[l], sizeof( pop ) - l, "%s",
4785 "\xc2\xb7"
4786 "10" );
4787 state = E;
4788 } else if ( state == E && ( *c == '+' || *c == '0' ) )
4789 state = E;
4790 else {
4791 state = EXP;
4792 l +=
4793 scnprintf( &pop[l], sizeof( pop ) - l, "%s", digits[*c - '0'] );
4794 }
4795 }
4796 }
4797
4798 return pop;
4799}
4800
4807static const MapShader *mapshader_get( const char *name )
4808{
4809 MapShader *ms;
4810
4811 if ( mapshaders == NULL )
4813
4814 for ( int i = 0; i < array_size( mapshaders ); i++ ) {
4815 MapShader *t = mapshaders[i];
4816 if ( strcmp( t->name, name ) == 0 )
4817 return t;
4818 }
4819
4820 /* Allocate and set up. */
4821 ms = malloc( sizeof( MapShader ) );
4823
4824 ms->name = strdup( name );
4825 ms->program = gl_program_vert_frag( "system_map.vert", name );
4826 ms->vertex = glGetAttribLocation( ms->program, "vertex" );
4827 ms->projection = glGetUniformLocation( ms->program, "projection" );
4828 ms->time = glGetUniformLocation( ms->program, "time" );
4829 ms->globalpos = glGetUniformLocation( ms->program, "globalpos" );
4830 ms->alpha = glGetUniformLocation( ms->program, "alpha" );
4831
4832 return ms;
4833}
4834
4835static int spob_lua_cmp( const void *a, const void *b )
4836{
4837 const spob_lua_file *la = (const spob_lua_file *)a;
4838 const spob_lua_file *lb = (const spob_lua_file *)b;
4839 return strcmp( la->filename, lb->filename );
4840}
4841
4842static nlua_env spob_lua_get( int *mem, const char *filename )
4843{
4844 size_t sz;
4845 char *dat;
4846 spob_lua_file *lf;
4847 const spob_lua_file key = { .filename = filename };
4848
4849 if ( spob_lua_stack == NULL )
4851
4852 lf = bsearch( &key, spob_lua_stack, array_size( spob_lua_stack ),
4853 sizeof( spob_lua_file ), spob_lua_cmp );
4854 if ( lf != NULL ) {
4855 *mem = lf->lua_mem;
4856 return lf->env;
4857 }
4858
4859 dat = ndata_read( filename, &sz );
4860 if ( dat == NULL ) {
4861 WARN( _( "Failed to read spob Lua '%s'!" ), filename );
4862 return LUA_NOREF;
4863 }
4864
4865 nlua_env env = nlua_newEnv( filename );
4866 nlua_loadStandard( env );
4867 nlua_loadGFX( env );
4868 nlua_loadCamera( env );
4869
4870 /* Add new entry and sort. */
4871 lf = &array_grow( &spob_lua_stack );
4872 lf->filename = strdup( filename );
4873 lf->env = env;
4874
4875 /* Add the spob memory table. */
4876 lua_newtable( naevL ); /* m */
4877 lua_pushvalue( naevL, -1 ); /* m, m */
4878 lf->lua_mem = luaL_ref( naevL, LUA_REGISTRYINDEX ); /* m */
4879 nlua_setenv( naevL, env, "mem" ); /* */
4880 *mem = lf->lua_mem;
4881
4882 if ( nlua_dobufenv( env, dat, sz, filename ) != 0 ) {
4883 int n;
4884 WARN( _( "Lua Spob '%s' error:\n%s" ), filename,
4885 lua_tostring( naevL, -1 ) );
4886 lua_pop( naevL, 1 );
4887 spob_lua_free( lf );
4888 free( dat );
4891 &spob_lua_stack[n] );
4892 return LUA_NOREF;
4893 }
4894 free( dat );
4895
4897 spob_lua_cmp );
4898 return env;
4899}
4900
4901static void spob_lua_free( spob_lua_file *lf )
4902{
4903 free( (char *)lf->filename );
4904 nlua_freeEnv( lf->env );
4905 luaL_unref( naevL, LUA_REGISTRYINDEX, lf->lua_mem );
4906}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:170
#define array_create_size(basic_type, capacity)
Creates a new dynamic array of ‘basic_type’ with an initial capacity.
Definition array.h:102
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
Definition array.h:148
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:179
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition array.h:122
#define array_shrink(ptr_array)
Shrinks memory to fit only ‘size’ elements.
Definition array.h:160
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition array.h:134
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
void asteroids_render(void)
Renders the current systems' spobs.
Definition asteroid.c:897
void asteroids_computeInternals(AsteroidAnchor *a)
Updates internal alues of an asteroid field.
Definition asteroid.c:508
void asteroids_free(void)
Cleans up the system.
Definition asteroid.c:1059
int asteroids_load(void)
Loads the asteroids.
Definition asteroid.c:532
void asteroid_freeAnchor(AsteroidAnchor *ast)
Frees an asteroid anchor.
Definition asteroid.c:1036
AsteroidTypeGroup * astgroup_getName(const char *name)
Gets an asteroid type group by name.
Definition asteroid.c:1170
void asteroids_init(void)
Initializes the system.
Definition asteroid.c:298
void asteroids_renderOverlay(void)
Renders the system overlay.
Definition asteroid.c:875
void asteroid_initAnchor(AsteroidAnchor *ast)
Initializes an asteroid anchor.
Definition asteroid.c:1019
void asteroid_freeExclude(AsteroidExclusion *exc)
Frees an asteroid exclusion.
Definition asteroid.c:1051
void asteroids_update(double dt)
Controls fleet spawning.
Definition asteroid.c:203
void background_clear(void)
Cleans up the background stuff.
Definition background.c:570
void background_initDust(int n)
Initializes background dust.
Definition background.c:97
void background_renderOverlay(double dt)
Renders the background overlay.
Definition background.c:290
void background_render(double dt)
Render the background.
Definition background.c:245
int background_load(const char *name)
Loads a background script by name.
Definition background.c:502
double cam_getZoom(void)
Gets the camera zoom.
Definition camera.c:101
double CollideCircleIntersection(const vec2 *p1, double r1, const vec2 *p2, double r2)
Calculates the area of intersection between two circles.
Definition collision.c:1159
Commodity * commodity_get(const char *name)
Gets a commodity by name.
Definition commodity.c:151
Commodity ** standard_commodities(void)
Return an array (array.h) of standard commodities. Free with array_free. (Don't free contents....
Definition commodity.c:267
int dtype_get(const char *name)
Gets the id of a dtype based on name.
Definition damagetype.c:157
StarSystem * systems_stack
Definition space.c:94
void economy_addQueuedUpdate(void)
Increments the queued update counter.
Definition economy.c:465
credits_t economy_getPriceAtTime(const Commodity *com, const StarSystem *sys, const Spob *p, ntime_t tme)
Gets the price of a good on a spob in a system.
Definition economy.c:96
void economy_initialiseCommodityPrices(void)
Initialises commodity prices for the sinusoidal economy model.
Definition economy.c:870
void economy_clearSingleSpob(Spob *p)
Clears all economy knowledge of a given spob. Used by the unidiff system.
Definition economy.c:1002
credits_t economy_getPrice(const Commodity *com, const StarSystem *sys, const Spob *p)
Gets the price of a good on a spob in a system.
Definition economy.c:80
int economy_getAverageSpobPrice(const Commodity *com, const Spob *p, credits_t *mean, double *std)
Gets the average price of a good on a spob in a system, using a rolling average over the times the pl...
Definition economy.c:171
int faction_isFaction(int f)
Checks whether or not a faction is valid.
Definition faction.c:1539
void faction_updateGlobal(void)
Computes the global faction standing for each of the factions.
Definition faction.c:1907
int areEnemies(int a, int b)
Checks whether two factions are enemies.
Definition faction.c:1450
void factions_clearDynamic(void)
Clears dynamic factions.
Definition faction.c:2376
const char * faction_name(int f)
Gets a factions "real" (internal) name.
Definition faction.c:331
int faction_isDynamic(int id)
Is faction dynamic.
Definition faction.c:308
void factions_resetLocal(void)
Reset local standing.
Definition faction.c:1873
const FactionGenerator * faction_generators(int f)
Gets the faction's generators.
Definition faction.c:2366
int faction_usesHiddenJumps(int f)
Checks to see if a faction uses hidden jumps.
Definition faction.c:2356
nlua_env faction_getScheduler(int f)
Gets the state associated to the faction scheduler.
Definition faction.c:758
double faction_reputation(int f)
Gets the player's standing with a faction.
Definition faction.c:1129
int faction_isStatic(int id)
Is the faction static?
Definition faction.c:267
int faction_get(const char *name)
Gets a faction ID by name.
Definition faction.c:209
int areAllies(int a, int b)
Checks whether two factions are allies or not.
Definition faction.c:1476
void gatherable_free(void)
Frees all the gatherables.
Definition gatherable.c:147
void gatherable_update(double dt)
Updates all gatherable objects.
Definition gatherable.c:95
void gatherable_render(void)
Renders all the gatherables.
Definition gatherable.c:156
void gui_setSystem(void)
Player just changed their system.
Definition gui.c:1888
void gui_updateFaction(void)
Player's relationship with a faction was modified.
Definition gui.c:1896
void player_message(const char *fmt,...)
Adds a mesg to the queue to be displayed on screen.
Definition gui.c:353
void player_messageToggle(int enable)
Toggles if player should receive messages.
Definition gui.c:283
int hooks_runParam(const char *stack, const HookParam *param)
Runs all the hooks of stack.
Definition hook.c:1029
void land(Spob *p, int load)
Opens up all the land dialogue stuff.
Definition land.c:1413
Handles the important game menus.
#define MENU_MAIN
Definition menu.h:9
#define MENU_EDITORS
Definition menu.h:15
#define menu_isOpen(f)
Definition menu.h:16
int music_choose(const char *situation)
Actually runs the music stuff, based on situation.
Definition music.c:426
void naev_renderLoadscreen(void)
Renders the loadscreen if necessary.
Definition naev.c:640
double elapsed_time_mod
Definition naev.c:117
void update_routine(double dt, int dohooks)
Actually runs the updates.
Definition naev.c:1056
int naev_versionCompareTarget(const char *version, const char *target)
Does a comparison with a specific target.
Definition naev.c:1175
const double fps_min
Definition naev.c:116
static double real_dt
Definition naev.c:111
Header file with generic functions and naev-specifics.
#define ABS(x)
Definition naev.h:32
#define pow2(x)
Definition naev.h:53
#define MAX(x, y)
Definition naev.h:37
#define PATH_MAX
Definition naev.h:57
void * ndata_read(const char *path, size_t *filesize)
Reads a file from the ndata (will be NUL terminated).
Definition ndata.c:207
int ndata_matchExt(const char *path, const char *ext)
Sees if a file matches an extension.
Definition ndata.c:420
char ** ndata_listRecursive(const char *path)
Lists all the visible files in a directory, at any depth.
Definition ndata.c:286
void nebu_update(double dt)
Updates visibility and stuff.
Definition nebula.c:210
void nebu_renderOverlay(const double dt)
Renders the nebula overlay (hides what player can't see).
Definition nebula.c:257
void nebu_render(const double dt)
Renders the nebula.
Definition nebula.c:145
void nebu_prep(double density, double volatility, double hue)
Prepares the nebualae to be rendered.
Definition nebula.c:397
int nlua_loadStandard(nlua_env env)
Loads the standard Naev Lua API.
Definition nlua.c:914
int nlua_refenvtype(nlua_env env, const char *name, int type)
Gets the reference of a global in a lua environment if it matches a type.
Definition nlua.c:1047
lua_State * naevL
Definition nlua.c:54
int nlua_loadCamera(nlua_env env)
Loads the camera library.
Definition nlua_camera.c:48
int nlua_loadGFX(nlua_env env)
Loads the graphics library.
Definition nlua_gfx.c:112
LuaPilot lua_topilot(lua_State *L, int ind)
Lua bindings to interact with pilots.
Definition nlua_pilot.c:535
LuaPilot * lua_pushpilot(lua_State *L, LuaPilot pilot)
Pushes a pilot on the stack.
Definition nlua_pilot.c:576
int lua_ispilot(lua_State *L, int ind)
Checks to see if ind is a pilot.
Definition nlua_pilot.c:591
LuaSpob * lua_pushspob(lua_State *L, LuaSpob spob)
Pushes a spob on the stack.
Definition nlua_spob.c:203
glTexture * lua_totex(lua_State *L, int ind)
Lua bindings to interact with OpenGL textures.
Definition nlua_tex.c:86
int lua_istex(lua_State *L, int ind)
Checks to see if ind is a texture.
Definition nlua_tex.c:144
void arrayShuffle(void **array)
Randomly sorts an array (array.h) of pointers in place with the Fisher-Yates shuffle.
Definition nmath.c:68
int scnprintf(char *text, size_t maxlen, const char *fmt,...)
Like snprintf(), but returns the number of characters ACTUALLY "printed" into the buffer....
Definition nstring.c:102
char * ntime_pretty(ntime_t t, int d)
Gets the time in a pretty human readable format.
Definition ntime.c:178
void ntime_allowUpdate(int enable)
Allows the time to update when the game is updating.
Definition ntime.c:258
xmlDocPtr xml_parsePhysFS(const char *filename)
Analogous to xmlParseMemory/xmlParseFile.
Definition nxml.c:70
glInfo gl_screen
Definition opengl.c:47
void gl_gameToScreenCoords(double *nx, double *ny, double bx, double by)
Converts in-game coordinates to screen coordinates.
void gl_renderSprite(const glTexture *sprite, double bx, double by, int sx, int sy, const glColour *c)
Blits a sprite, position is relative to the player.
glTexture * gl_dupTexture(const glTexture *texture)
Duplicates a texture.
Definition opengl_tex.c:891
glTexture * gl_newSprite(const char *path, const int sx, const int sy, const unsigned int flags)
Loads the texture immediately, but also sets it as a sprite.
Definition opengl_tex.c:785
USE_RESULT glTexture * gl_rawTexture(const char *name, GLuint texid, double w, double h)
Creates a texture from a raw opengl index.
Definition opengl_tex.c:917
int gl_fboCreate(GLuint *fbo, GLuint *tex, GLsizei width, GLsizei height)
Creates a framebuffer and its associated texture.
Definition opengl_tex.c:268
glTexture * gl_newImage(const char *path, const unsigned int flags)
Loads an image as a texture.
Definition opengl_tex.c:587
int gl_fboAddDepth(GLuint fbo, GLuint *tex, GLsizei width, GLsizei height)
Adds a depth attachment to an FBO.
Definition opengl_tex.c:313
void gl_getSpriteFromDir(int *x, int *y, int sx, int sy, double dir)
Sets x and y to be the appropriate sprite for glTexture using dir.
Definition opengl_tex.c:977
void gl_freeTexture(glTexture *texture)
Frees a texture.
Definition opengl_tex.c:835
void pilots_clean(int persist)
Cleans up the pilot stack - leaves the player.
Definition pilot.c:4052
void pilot_clearTimers(Pilot *pilot)
Clears the pilot's timers.
Definition pilot.c:4337
Pilot * pilot_get(unsigned int id)
Pulls a pilot out of the pilot_stack based on ID.
Definition pilot.c:640
static Pilot ** pilot_stack
Definition pilot.c:51
Pilot *const * pilot_getAll(void)
Gets the pilot stack.
Definition pilot.c:93
void pilots_newSystem(void)
Updates pilot state which depends on the system (sensor range, nebula trails...)
Definition pilot.c:4116
double pilot_hit(Pilot *p, const Solid *w, const Pilot *pshooter, const Damage *dmg, const Outfit *outfit, int lua_mem, int reset)
Damages the pilot.
Definition pilot.c:1383
int pilot_inRangeSpob(const Pilot *p, int target)
Check to see if a spob is in sensor range of the pilot.
Definition pilot_ew.c:293
int pilot_inRangeJump(const Pilot *p, int i)
Check to see if a jump point is in sensor range of the pilot.
Definition pilot_ew.c:361
int pilot_inRangeAsteroid(const Pilot *p, int ast, int fie)
Check to see if an asteroid is in sensor range of the pilot.
Definition pilot_ew.c:324
void pilot_calcStats(Pilot *pilot)
Recalculates the pilot's stats based on his outfits.
void pilot_lockClear(Pilot *p)
Clears pilot's missile lockon timers.
int pilot_outfitOffAll(Pilot *p)
Disables all active outfits for a pilot.
void player_checkLandAck(void)
Revokes landing authorization if the player's reputation is too low.
Definition player.c:1791
void player_clear(void)
Clears the targets.
Definition player.c:1019
Player_t player
Definition player.c:77
void * q_dequeue(Queue q)
Dequeues an item.
Definition queue.c:122
int q_isEmpty(Queue q)
Checks if the queue is empty.
Definition queue.c:155
void q_destroy(Queue q)
Destroys a queue.
Definition queue.c:66
Queue q_create(void)
Creates a queue.
Definition queue.c:40
void q_enqueue(Queue q, void *data)
Enqueues an item.
Definition queue.c:91
static const double c[]
Definition rng.c:256
static const double d[]
Definition rng.c:263
void safelanes_recalculate(void)
Update the safe lane locations in response to the universe changing (e.g., diff applied).
Definition safelanes.c:296
void ss_free(ShipStatList *ll)
Frees a list of ship stats.
Definition shipstats.c:934
ShipStatList * ss_listFromXML(xmlNodePtr node)
Creates a shipstat list element from an xml node.
Definition shipstats.c:353
int ss_sort(ShipStatList **ll)
Sorts the ship stats, useful if doing saving stuff.
Definition shipstats.c:442
int sound_disabled
Definition sound.c:130
int sound_env(SoundEnv_t env_type, double param)
Sets up the sound environment.
Definition sound.c:1658
static int spob_cmp(const void *p1, const void *p2)
Comparison function for qsort'ing Spob by name.
Definition space.c:1094
void space_reconstructPresences(void)
Reset the presence of all systems.
Definition space.c:4620
void space_init(const char *sysname, int do_simulate)
Initializes the system.
Definition space.c:1620
double system_getClosestAng(const StarSystem *sys, int *pnt, int *jp, int *ast, int *fie, double x, double y, double ang)
Gets the feature nearest to directly ahead of a position in the system.
Definition space.c:808
void spob_distress(Spob *spb, const Pilot *p, const Pilot *attacker)
Spob is receiving distress from a pilot about an attacker.
Definition space.c:2081
static int spobs_load(void)
Loads all the spobs in the game.
Definition space.c:1846
void spob_averageSeenPricesAtTime(const Spob *p, const ntime_t tupdate)
Adds cost of commodities on spob p to known statistics at time t.
Definition space.c:321
static int system_cmp(const void *p1, const void *p2)
Comparison function for qsort'ing StarSystem by name.
Definition space.c:993
double system_getClosest(const StarSystem *sys, int *pnt, int *jp, int *ast, int *fie, double x, double y)
Gets the closest feature to a position in the system.
Definition space.c:723
int spob_exists(const char *spobname)
Check to see if a spob exists.
Definition space.c:1185
void system_rmCurrentPresence(StarSystem *sys, int faction, double amount)
Removes active presence.
Definition space.c:4678
void space_gfxUnload(StarSystem *sys)
Unloads all the graphics for a star system.
Definition space.c:2281
#define FLAG_POSSET
Definition space.c:66
int space_canHyperspace(const Pilot *p)
Checks to make sure if pilot is far enough away to hyperspace.
Definition space.c:494
void space_render(const double dt)
Renders the system.
Definition space.c:3666
static void space_renderSpob(const Spob *p)
Renders a spob.
Definition space.c:3767
static int sys_cmpSysFaction(const void *a, const void *b)
Compares two system presences.
Definition space.c:3350
int spob_luaInit(Spob *spob)
Updatse the spob's internal Lua stuff.
Definition space.c:2120
static void system_init(StarSystem *sys)
Initializes a new star system with null memory.
Definition space.c:2892
void systems_reconstructJumps(void)
Reconstructs the jumps.
Definition space.c:2990
int space_jumpDistance(const Pilot *p, const JumpPoint *jp)
Distance at which a pilot can jump.
Definition space.c:479
const glColour * spob_getColour(const Spob *p)
Gets the spob colour.
Definition space.c:2009
int space_rmMarker(int objid, MissionMarkerType type)
Removes a marker from a system.
Definition space.c:4141
static int space_parseSaveNodes(xmlNodePtr parent, StarSystem *sys)
Parses spobs in a system.
Definition space.c:4298
int system_addJump(StarSystem *sys, StarSystem *target)
Adds a jump point between two star systems.
Definition space.c:2829
int spob_averageSpobPrice(const Spob *p, const Commodity *c, credits_t *mean, double *std)
Gets the average price of a commodity at a spob that has been seen so far.
Definition space.c:336
static char ** systemname_stack
Definition space.c:88
double system_getPresenceFull(const StarSystem *sys, int faction, double *base, double *bonus)
Get the presence of a faction in a system.
Definition space.c:4566
double system_getReputation(const StarSystem *sys, int faction)
Gets the local reputation of the player in a system or returns 0.
Definition space.c:4502
static int spob_parse(Spob *spob, const char *filename, Commodity **stdList)
Parses a spob from an xml node.
Definition space.c:2365
Spob * spob_getAll(void)
Gets an array (array.h) of all spobs.
Definition space.c:1166
int spob_rename(Spob *p, char *newname)
Renames a spob.
Definition space.c:455
#define XML_SPOB_TAG
Definition space.c:59
int spob_getService(const char *name)
Converts name to spob service flag.
Definition space.c:197
void system_setFaction(StarSystem *sys)
Sets the system faction based on the spobs it has.
Definition space.c:3377
Spob * spob_get(const char *spobname)
Gets a spob based on its name.
Definition space.c:1107
void space_factionChange(void)
Mark when a faction changes.
Definition space.c:1460
static char ** spobname_stack
Definition space.c:86
int system_addVirtualSpob(StarSystem *sys, const char *spobname)
Adds a virtual spob to a system.
Definition space.c:2765
const char * space_getRndSpob(int landable, unsigned int services, int(*filter)(Spob *p))
Gets the name of a random spob.
Definition space.c:666
static spob_lua_file * spob_lua_stack
Definition space.c:81
void space_update(double dt, double real_dt)
Controls fleet spawning.
Definition space.c:1482
static int systems_load(void)
Loads the entire systems, needs to be called after spobs_load.
Definition space.c:3587
StarSystem * system_getIndex(int id)
Get the system by its index.
Definition space.c:1038
static void spob_initDefaults(Spob *spob)
Initializes a new spob to safe defaults.
Definition space.c:2334
int spob_index(const Spob *p)
Gets the ID of a spob.
Definition space.c:1158
double system_getReputationOrGlobal(const StarSystem *sys, int faction)
Gets the local reputation of the player in a system or returns the global standing.
Definition space.c:4518
int spob_hasSystem(const Spob *spb)
Get whether or not a spob has a system (i.e. is on the map).
Definition space.c:1060
static int space_simulating
Definition space.c:115
#define FLAG_INTERFERENCESET
Definition space.c:67
static VirtualSpob * vspob_stack
Definition space.c:96
const char * jump_getSymbol(const JumpPoint *jp)
Gets the jump point symbol.
Definition space.c:1317
static int systemstack_changed
Definition space.c:100
const char * spob_getSymbol(const Spob *p)
Gets the spob symbol.
Definition space.c:1986
void space_renderOverlay(const double dt)
Renders the system overlay.
Definition space.c:3686
int spob_setFaction(Spob *p, int faction)
Changes the spobs faction.
Definition space.c:372
char spob_getColourChar(const Spob *p)
Gets the spob colour char.
Definition space.c:1966
StarSystem * system_new(void)
Creates a new star system.
Definition space.c:2908
static glTexture * jumpbuoy_gfx
Definition space.c:112
int space_sysReachable(const StarSystem *sys)
Sees if a system is reachable.
Definition space.c:869
JumpPoint * jump_getTarget(const StarSystem *target, const StarSystem *sys)
Less safe version of jump_get that works with pointers.
Definition space.c:1303
const char * system_existsCase(const char *sysname)
Checks to see if a system exists case insensitively.
Definition space.c:936
StarSystem * system_getAll(void)
Gets an array (array.h) of all star systems.
Definition space.c:925
char ** spob_searchFuzzyCase(const char *spobname, int *n)
Does a fuzzy case matching. Searches spob_name() but returns internal names.
Definition space.c:1211
int space_sysReachableFromSys(const StarSystem *target, const StarSystem *sys)
Sees if a system is reachable from another system.
Definition space.c:911
char ** system_searchFuzzyCase(const char *sysname, int *n)
Does a fuzzy case matching. Searches translated names but returns internal names.
Definition space.c:948
int space_playerLoad(xmlNodePtr parent, const char *version)
Loads player's space properties from an XML node.
Definition space.c:4221
StarSystem * system_get(const char *sysname)
Get the system from its name.
Definition space.c:1007
int space_calcJumpInPos(const StarSystem *in, const StarSystem *out, vec2 *pos, vec2 *vel, double *dir, const Pilot *p)
Calculates the jump in pos for a pilot.
Definition space.c:556
int spob_rmService(Spob *p, int service)
Removes a service from a spob.
Definition space.c:442
static MapShader ** mapshaders
Definition space.c:104
static Spob * spob_stack
Definition space.c:95
void system_updateAsteroids(StarSystem *sys)
Updates some internal calculations about asteroids in a system.
Definition space.c:347
void spob_setKnown(Spob *p)
Sets a spob's known status, if it's real.
Definition space.c:1174
static void system_scheduler(double dt, int init)
Controls fleet spawning.
Definition space.c:1330
void spobs_render(void)
Renders the current systems' spobs.
Definition space.c:3709
StarSystem * cur_system
Definition space.c:110
const char * spob_existsCase(const char *spobname)
Check to see if a spob exists (case insensitive).
Definition space.c:1199
int system_addSpob(StarSystem *sys, const char *spobname)
Adds a spob to a star system.
Definition space.c:2668
void space_exit(void)
Cleans up the system.
Definition space.c:3824
int space_addMarker(int objid, MissionMarkerType type)
Adds a marker to a system.
Definition space.c:4055
static int spob_parsePresence(xmlNodePtr node, SpobPresence *ap)
Parsess an spob presence from xml.
Definition space.c:2313
double system_getPresence(const StarSystem *sys, int faction)
Get the presence of a faction in a system.
Definition space.c:4537
void spob_updateLand(Spob *p)
Updates the land possibilities of a spob.
Definition space.c:2031
int space_sysReallyReachable(const char *sysname)
Sees if a system can be reached via jumping.
Definition space.c:889
static int space_simulating_effects
Definition space.c:116
static void space_renderJumpPoint(const JumpPoint *jp, int i)
Renders a jump point.
Definition space.c:3737
void spob_gfxLoad(Spob *spob)
Loads a spob's graphics (and radius).
Definition space.c:2206
int system_rmVirtualSpob(StarSystem *sys, const char *spobname)
Removes a virtual spob from a system.
Definition space.c:2789
void space_clearMarkers(void)
Clears all system markers.
Definition space.c:3964
int space_playerSave(xmlTextWriterPtr writer)
Saves what is needed to be saved for space.
Definition space.c:4166
static int virtualspobs_load(void)
Loads all the virtual spobs.
Definition space.c:1892
VirtualSpob * virtualspob_getAll(void)
Gets all the virtual spobs.
Definition space.c:1243
Spob * spob_getIndex(int ind)
Gets spob by index.
Definition space.c:1140
void system_presenceAddSpob(StarSystem *sys, const SpobPresence *ap)
Adds (or removes) some presence to a system.
Definition space.c:4339
VirtualSpob * virtualspob_get(const char *name)
Gets a virtual spob by matching name.
Definition space.c:1261
void space_clearKnown(void)
Clears all system knowledge.
Definition space.c:3945
static int space_fchg
Definition space.c:113
void system_addAllSpobsPresence(StarSystem *sys)
Go through all the spobs and call system_addPresence().
Definition space.c:4597
int spob_addCommodity(Spob *p, Commodity *c)
Adds a commodity to a spob.
Definition space.c:385
static int system_parseJumps(StarSystem *sys)
Loads the jumps into a system.
Definition space.c:3495
const char * spob_getSystemName(const char *spobname)
Get the name of a system from a spobname.
Definition space.c:1082
static const MapShader * mapshader_get(const char *name)
Gets the map shader by name.
Definition space.c:4807
int system_hasSpob(const StarSystem *sys)
See if the system has a spob.
Definition space.c:4660
int spob_addService(Spob *p, int service)
Removes a service from a spob.
Definition space.c:399
static int systems_loading
Definition space.c:109
const char * spob_name(const Spob *p)
Gets the translated name of a spob.
Definition space.c:1834
static int spobstack_changed
Definition space.c:102
static int virtualspob_cmp(const void *p1, const void *p2)
Comparison function for qsort'ing VirtuaSpob by name.
Definition space.c:1251
const char * spob_getClassName(const char *class)
Gets the long class name for a spob.
Definition space.c:226
static int system_parseAsteroidExclusion(const xmlNodePtr node, StarSystem *sys)
Parses a single asteroid exclusion zone for a system.
Definition space.c:3104
const char * space_populationStr(const Spob *spb)
Gets the population in an approximated string. Note this function changes the string value each call,...
Definition space.c:4743
void space_clearComputerMarkers(void)
Clears all the system computer markers.
Definition space.c:3984
void space_queueLand(Spob *pnt)
Cues a spob to be landed on. This is not done immediately, but when the engine thinks it is ok to do.
Definition space.c:4731
static void space_updateSpob(const Spob *p, double dt, double real_dt)
Renders a spob.
Definition space.c:3805
int system_rmJump(StarSystem *sys, StarSystem *target)
Removes a jump point from a star system.
Definition space.c:2868
int space_spawn
Definition space.c:122
static int system_parseAsteroidField(const xmlNodePtr node, StarSystem *sys)
Parses a single asteroid field for a system.
Definition space.c:3029
credits_t spob_commodityPrice(const Spob *p, const Commodity *c)
Gets the price of a commodity at a spob.
Definition space.c:293
const char * spob_getServiceName(int service)
Gets the (English) name for a service code.
Definition space.c:169
void systems_reconstructSpobs(void)
Updates the system spob pointers.
Definition space.c:3009
Spob * spob_new(void)
Creates a new spob.
Definition space.c:1804
StarSystem * spob_getSystem(const Spob *spob)
Gets the system a spob is in.
Definition space.c:1071
int space_needsEffects(void)
returns whether or not we're simulating with effects.
Definition space.c:1609
char ** space_getFactionSpob(const int *factions, int landable)
Gets the name of all the spobs that belong to factions.
Definition space.c:623
#define FLAG_FACTIONSET
Definition space.c:69
void spob_luaInitMem(const Spob *spob)
Initializes the memory fo a spob.
Definition space.c:2109
JumpPoint * jump_get(const char *jumpname, const StarSystem *sys)
Gets a jump point based on its target and system.
Definition space.c:1279
void space_checkLand(void)
Handles landing if necessary.
Definition space.c:1468
glTexture * jumppoint_gfx
Definition space.c:111
void space_gfxLoad(StarSystem *sys)
Loads all the graphics for a star system.
Definition space.c:2266
int space_hyperspace(Pilot *p)
Tries to get the pilot into hyperspace.
Definition space.c:529
int space_load(void)
Loads the entire universe into ram - pretty big feat eh?
Definition space.c:3532
int space_isSimulation(void)
returns whether we're just simulating.
Definition space.c:1601
credits_t spob_commodityPriceAtTime(const Spob *p, const Commodity *c, ntime_t t)
Gets the price of a commodity at a spob at given time.
Definition space.c:307
int system_index(const StarSystem *sys)
Gets the index of a star system.
Definition space.c:1049
int system_rmSpob(StarSystem *sys, const char *spobname)
Removes a spob from a star system.
Definition space.c:2709
int space_loadLua(void)
initializes the Lua for all the spobs.
Definition space.c:3569
static int system_parseJumpPoint(const xmlNodePtr node, StarSystem *sys)
Parses a single jump point for a system.
Definition space.c:3406
static int system_parse(StarSystem *system, const char *filename)
Creates a system from an XML node.
Definition space.c:3163
void system_reconstructJumps(StarSystem *sys)
Reconstructs the jumps for a single system.
Definition space.c:2958
#define FLAG_SERVICESSET
Definition space.c:68
void spfx_clear(void)
Clears all the currently running effects.
Definition spfx.c:549
const char * start_spob_lua_default(void)
Gets the default spob Lua file.
Definition start.c:292
Represents an asteroid field anchor.
Definition asteroid.h:111
double radius
Definition asteroid.h:118
double density
Definition asteroid.h:115
Asteroid * asteroids
Definition asteroid.h:116
Represents an asteroid exclusion zone.
Definition asteroid.h:136
Represents a single asteroid.
Definition asteroid.h:88
int state
Definition asteroid.h:92
Solid sol
Definition asteroid.h:98
Represents a commodity.
Definition commodity.h:57
Core damage that an outfit does.
Definition outfit.h:168
int type
Definition outfit.h:169
double disable
Definition outfit.h:174
double penetration
Definition outfit.h:171
double damage
Definition outfit.h:173
The actual hook parameter.
Definition hook.h:40
const char * str
Definition hook.h:44
union HookParam::@114305201244257020071001257133270030317263020235 u
HookParamType type
Definition hook.h:41
LuaSpob la
Definition hook.h:52
LuaJump lj
Definition hook.h:53
int destid
Definition nlua_jump.h:16
int srcid
Definition nlua_jump.h:15
Map shader.
Definition space.h:284
char * name
Definition space.h:285
GLuint time
Definition space.h:290
GLuint globalpos
Definition space.h:291
GLuint projection
Definition space.h:288
GLuint alpha
Definition space.h:289
GLuint program
Definition space.h:286
GLuint vertex
Definition space.h:287
The representation of an in-game pilot.
Definition pilot.h:263
int presence
Definition pilot.h:271
unsigned int id
Definition pilot.h:264
int faction
Definition pilot.h:269
int faction_spawn
Definition pilot.h:270
Represents relative ship statistics as a linked list.
Definition shipstats.h:198
struct ShipStatList_ * next
Definition shipstats.h:199
vec2 pos
Definition physics.h:49
Represents the presence of a spob.
Definition space.h:78
double bonus
Definition space.h:81
int range
Definition space.h:82
double base
Definition space.h:80
int faction
Definition space.h:79
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
Definition space.h:102
int can_land
Definition space.h:125
char * gfx_commPath
Definition space.h:152
Commodity ** commodities
Definition space.h:134
glTexture * gfx_space
Definition space.h:146
char * land_msg
Definition space.h:128
unsigned int services
Definition space.h:133
int lua_update
Definition space.h:172
int land_override
Definition space.h:126
char * lua_file_raw
Definition space.h:162
char * gfx_spacePath
Definition space.h:148
char * gfx_spaceName
Definition space.h:147
int lua_land
Definition space.h:170
int lua_distress
Definition space.h:177
double radius
Definition space.h:110
int markers
Definition space.h:159
double marker_scale
Definition space.h:113
char * feature
Definition space.h:108
char * bar_description
Definition space.h:132
int lua_comm
Definition space.h:173
int lua_render
Definition space.h:171
const SimpleShader * marker
Definition space.h:112
char * description
Definition space.h:131
char * class
Definition space.h:116
char ** tags
Definition space.h:155
int lua_mem
Definition space.h:165
int lua_barbg
Definition space.h:176
int lua_init
Definition space.h:166
int lua_load
Definition space.h:167
tech_group_t * tech
Definition space.h:137
double map_alpha
Definition space.h:158
char * gfx_exterior
Definition space.h:149
nlua_env lua_env
Definition space.h:164
char * name
Definition space.h:105
char * gfx_exteriorPath
Definition space.h:150
vec2 pos
Definition space.h:109
int id
Definition space.h:104
char * gfx_comm
Definition space.h:151
CommodityPrice * commodityPrice
Definition space.h:136
char * filename
Definition space.h:103
double hide
Definition space.h:122
double population
Definition space.h:118
int lua_can_land
Definition space.h:169
int lua_population
Definition space.h:174
int lua_unload
Definition space.h:168
char * lua_file
Definition space.h:163
SpobPresence presence
Definition space.h:121
char * display
Definition space.h:106
unsigned int flags
Definition space.h:156
Represents presence in a system.
Definition space.h:228
double curUsed
Definition space.h:233
double value
Definition space.h:232
double timer
Definition space.h:234
double base
Definition space.h:230
double local
Definition space.h:236
double bonus
Definition space.h:231
Basically modifies system parameters without creating any real objects.
Definition space.h:91
SpobPresence * presences
Definition space.h:93
char * name
Definition space.h:92
Abstraction for rendering sprite sheets.
Definition opengl_tex.h:43
double w
Definition opengl_tex.h:47
double h
Definition opengl_tex.h:48
nlua_env env
Definition space.c:77
int lua_mem
Definition space.c:78
const char * filename
Definition space.c:76
Represents a 2d vector.
Definition vec2.h:45
double y
Definition vec2.h:47
double x
Definition vec2.h:46
tech_group_t * tech_groupCreateXML(xmlNodePtr node)
Creates a tech group from an XML node.
Definition tech.c:185
void tech_groupDestroy(tech_group_t *grp)
Frees a tech group.
Definition tech.c:205
void weapon_clear(void)
Clears all the weapons, does NOT free the layers.
Definition weapon.c:2849
void weapon_newSystem(void)
Sets up collision stuff for a new system.
Definition weapon.c:203