naev 0.12.5
outfit.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
13#include "physfs.h"
14#include <math.h>
15#include <stdlib.h>
16
17#include "SDL_timer.h"
18
19#include "naev.h"
21
22#include "outfit.h"
23
24#include "array.h"
25#include "conf.h"
26#include "damagetype.h"
27#include "log.h"
28#include "mapData.h" // IWYU pragma: keep
29#include "ndata.h"
30#include "nlua.h"
31#include "nlua_camera.h"
32#include "nlua_gfx.h"
33#include "nlua_munition.h"
34#include "nlua_outfit.h"
35#include "nlua_pilotoutfit.h"
36#include "nstring.h"
37#include "nxml.h"
38#include "pilot.h"
39#include "pilot_heat.h"
40#include "pilot_outfit.h"
41#include "ship.h"
42#include "slots.h"
43#include "sound.h"
44#include "space.h"
45#include "spfx.h"
46#include "start.h"
47#include "threadpool.h"
48
49#define XML_OUTFIT_TAG "outfit"
50
51#define OUTFIT_SHORTDESC_MAX \
52 STRMAX_SHORT
53
57typedef struct OutfitThreadData_ {
58 char *filename;
59 Outfit outfit;
60 int ret;
62
63/*
64 * the stack
65 */
66static Outfit *outfit_stack = NULL;
67static char **license_stack = NULL;
68
69/*
70 * Helper stuff for setting up short descriptions for outfits.
71 */
72#define SDESC_ADD( l, temp, txt, ... ) \
73 ( l ) += scnprintf( &( temp )->summary_raw[l], \
74 OUTFIT_SHORTDESC_MAX - ( l ), ( txt ), ##__VA_ARGS__ )
75
76/*
77 * Prototypes
78 */
79/* misc */
80static OutfitType outfit_strToOutfitType( char *buf );
81/* parsing */
82static int outfit_loadDir( const char *dir );
83static int outfit_parseDamage( Damage *dmg, xmlNodePtr node );
84static int outfit_parseThread( void *ptr );
85static int outfit_parse( Outfit *temp, const char *file );
86static void outfit_parseSBolt( Outfit *temp, const xmlNodePtr parent );
87static void outfit_parseSBeam( Outfit *temp, const xmlNodePtr parent );
88static void outfit_parseSLauncher( Outfit *temp, const xmlNodePtr parent );
89static void outfit_parseSMod( Outfit *temp, const xmlNodePtr parent );
90static void outfit_parseSAfterburner( Outfit *temp, const xmlNodePtr parent );
91static void outfit_parseSFighterBay( Outfit *temp, const xmlNodePtr parent );
92static void outfit_parseSMap( Outfit *temp, const xmlNodePtr parent );
93static void outfit_parseSLocalMap( Outfit *temp, const xmlNodePtr parent );
94static void outfit_parseSGUI( Outfit *temp, const xmlNodePtr parent );
95static void outfit_parseSLicense( Outfit *temp, const xmlNodePtr parent );
96static int outfit_loadPLG( Outfit *temp, const char *buf );
97static int outfit_loadGFX( Outfit *temp, const xmlNodePtr node );
98static void sdesc_miningRarity( int *l, Outfit *temp, int rarity );
99/* Display */
100
101typedef struct s_Outfitstat {
102 const char *name;
103 const char *unit;
104 int colour;
105 int colour_threshold;
106 int hide_zero;
107 int precision;
108} t_os_stat;
109typedef const t_os_stat os_opts;
110
111/* Printing functions. */
112static int os_printD( char *buf, int len, double value, const os_opts *opts );
113static int os_printD_range( char *buffer, int i, double minValue,
114 double maxValue, const t_os_stat *opts );
115static int os_printD_rate( char *buffer, int i, double val,
116 const t_os_stat *val_opts, int multiplier,
117 double rate, const t_os_stat *rate_opts );
118
119/* Helpers for different attributes. */
120static os_opts darmour_opts = {
121 N_( "Armour Damage" ), _UNIT_PERCENT, 1, 100, 0, 0 };
122static os_opts dshield_opts = {
123 N_( "Shield Damage" ), _UNIT_PERCENT, 1, 100, 0, 0 };
124static os_opts dknockback_opts = {
125 N_( "Knockback" ), _UNIT_PERCENT, 0, 0, 1, 0 };
126static os_opts cpu_opts = { N_( "CPU" ), _UNIT_CPU, 1, 0, 1, 0 };
127static os_opts mass_opts = { N_( "Mass" ), _UNIT_MASS, 0, 0, 1, 0 };
128static os_opts penetration_opts = {
129 N_( "Penetration" ), _UNIT_PERCENT, 0, 0, 1, 0 };
130static os_opts damage_opts = { N_( "Damage" ), _UNIT_ENERGY, 0, 1, 1, 1 };
131static os_opts dps_opts = { N_( "Damage Rate" ), _UNIT_POWER, 0, 0, 1, 1 };
132static os_opts disable_opts = { N_( "Disable" ), _UNIT_ENERGY, 0, 1, 1, 1 };
133static os_opts disable_rate_opts = {
134 N_( "Disable Rate" ), _UNIT_POWER, 0, 0, 1, 1 };
135static os_opts fire_rate_opts = {
136 N_( "Fire Rate" ), _UNIT_PER_TIME, 0, 0, 0, 1 };
137static os_opts energy_opts = { N_( "Energy" ), _UNIT_ENERGY, 0, 1, 1, 1 };
138static os_opts power_opts = { N_( "Power" ), _UNIT_POWER, 0, 0, 1, 1 };
139static os_opts range_opts = { N_( "Range" ), _UNIT_DISTANCE, 0, 0, 1, 0 };
140static os_opts speed_opts = { N_( "Speed" ), _UNIT_SPEED, 0, 0, 1, 0 };
141static os_opts heatup_opts = { N_( "Overheat" ), _UNIT_TIME, 0, 0, 1, 1 };
142static os_opts dispersion_opts = {
143 N_( "Dispersion" ), _UNIT_ANGLE, 0, 0, 1, 0 };
144static os_opts swivel_opts = { N_( "Swivel" ), _UNIT_ANGLE, 0, 0, 1, 0 };
145static os_opts tracking_opts = { N_( "Tracking" ), _UNIT_DISTANCE, 0, 0, 1, 0 };
146static os_opts duration_opts = { N_( "Duration" ), _UNIT_TIME, 0, 0, 1, 1 };
147static os_opts cooldown_opts = { N_( "Cooldown" ), _UNIT_TIME, 0, 0, 1, 1 };
148static os_opts lockon_opts = { N_( "Lock-On" ), _UNIT_TIME, 0, 0, 1, 0 };
149static os_opts inflight_calib_opts = {
150 N_( "In-flight Calibration" ), _UNIT_TIME, 0, 0, 1, 1 };
151static os_opts initial_speed_opts = {
152 N_( "Launch Speed" ), _UNIT_SPEED, 0, 0, 1, 0 };
153static os_opts accel_opts = { N_( "Accel" ), _UNIT_ACCEL, 0, 0, 1, 0 };
154static os_opts max_speed_opts = { N_( "Max Speed" ), _UNIT_SPEED, 0, 0, 1, 0 };
155static os_opts reload_opts = { N_( "Reload Time" ), _UNIT_TIME, 0, 0, 1, 1 };
156static os_opts armour_opts = { N_( "Armour" ), _UNIT_ENERGY, 0, 0, 1, 1 };
157static os_opts absorp_opts = { N_( "Absorption" ), _UNIT_PERCENT, 0, 0, 1, 1 };
158static os_opts jam_res_opts = {
159 N_( "Jam Resistance" ), _UNIT_PERCENT, 0, 0, 1, 0 };
160static os_opts max_mass_opts = {
161 N_( "Max Effective Mass" ), _UNIT_MASS, 0, 0, 1, 0 };
162static os_opts rumble_opts = { N_( "Rumble" ), NULL, 0, 0, 1, 1 };
163static os_opts shots_delay_opts = {
164 N_( "Shots Delay" ), _UNIT_TIME, 0, 0, 1, 1 };
165
166static int outfit_cmp( const void *p1, const void *p2 )
167{
168 const Outfit *o1 = p1;
169 const Outfit *o2 = p2;
170 return strcmp( o1->name, o2->name );
171}
172
173int outfit_gfxStoreLoaded( const Outfit *o )
174{
175 return ( o->gfx_store != NULL );
176}
177
178int outfit_gfxStoreLoadNeeded( void )
179{
180 ThreadQueue *tq = vpool_create();
181 SDL_GL_MakeCurrent( gl_screen.window, NULL );
182 for ( int i = 0; i < array_size( outfit_stack ); i++ ) {
183 Outfit *o = &outfit_stack[i];
184 if ( !outfit_isProp( o, OUTFIT_PROP_NEEDSGFX ) )
185 continue;
186 vpool_enqueue( tq, (int ( * )( void * ))outfit_gfxStoreLoad, o );
187 outfit_rmProp( o, OUTFIT_PROP_NEEDSGFX );
188 }
189 vpool_wait( tq );
190 vpool_cleanup( tq );
191 SDL_GL_MakeCurrent( gl_screen.window, gl_screen.context );
192 return 0;
193}
194
199{
200 char filename[PATH_MAX];
201
202 if ( outfit_gfxStoreLoaded( o ) || ( o->gfx_store_path == NULL ) )
203 return 0;
204
205 /* Check for absolute pathe. */
206 if ( o->gfx_store_path[0] == '/' )
207 snprintf( filename, sizeof( filename ), "%s", o->gfx_store_path );
208 else
209 snprintf( filename, sizeof( filename ), OUTFIT_GFX_PATH "store/%s",
210 o->gfx_store_path );
211
212 /* Load the graphic. */
213 o->gfx_store = gl_newImage( filename, OPENGL_TEX_MIPMAPS );
214 return 0;
215}
216
223const Outfit *outfit_get( const char *name )
224{
225 const Outfit *o = outfit_getW( name );
226 if ( o == NULL )
227 WARN( _( "Outfit '%s' not found in stack." ), name );
228 return o;
229}
230
237const Outfit *outfit_getW( const char *name )
238{
239 const Outfit s = { .name = (char *)name };
240 return bsearch( &s, outfit_stack, array_size( outfit_stack ),
241 sizeof( Outfit ), outfit_cmp );
242}
243
247const Outfit *outfit_getAll( void )
248{
249 return outfit_stack;
250}
251
255const char *outfit_existsCase( const char *name )
256{
257 for ( int i = 0; i < array_size( outfit_stack ); i++ )
258 if ( strcasecmp( name, outfit_stack[i].name ) == 0 )
259 return outfit_stack[i].name;
260 return NULL;
261}
262
267char **outfit_searchFuzzyCase( const char *name, int *n )
268{
269 int len, nstack;
270 char **names;
271
272 /* Overallocate to maximum. */
273 nstack = array_size( outfit_stack );
274 names = malloc( sizeof( char *) * nstack );
275
276 /* Do fuzzy search. */
277 len = 0;
278 for ( int i = 0; i < nstack; i++ ) {
279 if ( SDL_strcasestr( _( outfit_stack[i].name ), name ) != NULL ) {
280 names[len] = outfit_stack[i].name;
281 len++;
282 }
283 }
284
285 /* Free if empty. */
286 if ( len == 0 ) {
287 free( names );
288 names = NULL;
289 }
290
291 *n = len;
292 return names;
293}
294
302int outfit_compareTech( const void *outfit1, const void *outfit2 )
303{
304 int ret;
305 const Outfit *o1, *o2;
306
307 /* Get outfits. */
308 o1 = *(const Outfit **)outfit1;
309 o2 = *(const Outfit **)outfit2;
310
311 /* Compare slot type. */
312 if ( o1->slot.type < o2->slot.type )
313 return +1;
314 else if ( o1->slot.type > o2->slot.type )
315 return -1;
316
317 /* Compare slot properties. */
318 if ( o1->slot.spid && !o2->slot.spid )
319 return -1;
320 else if ( !o1->slot.spid && o2->slot.spid )
321 return +1;
322
323 /* Compare intrinsic types. */
324 if ( o1->type < o2->type )
325 return -1;
326 else if ( o1->type > o2->type )
327 return +1;
328
329 /* Compare named types. */
330 if ( ( o1->typename == NULL ) && ( o2->typename != NULL ) )
331 return -1;
332 else if ( ( o1->typename != NULL ) && ( o2->typename == NULL ) )
333 return +1;
334 else if ( ( o1->typename != NULL ) && ( o2->typename != NULL ) ) {
335 ret = strcmp( o1->typename, o2->typename );
336 if ( ret != 0 )
337 return ret;
338 }
339
340 /* Compare slot sizes. */
341 if ( o1->slot.size < o2->slot.size )
342 return +1;
343 else if ( o1->slot.size > o2->slot.size )
344 return -1;
345
346 /* Compare sort priority. */
347 if ( o1->priority < o2->priority )
348 return +1;
349 else if ( o1->priority > o2->priority )
350 return -1;
351
352 /* Special prices are listed first. */
353 if ( ( o1->lua_price != LUA_NOREF ) && ( o2->lua_price == LUA_NOREF ) )
354 return -1;
355 else if ( ( o1->lua_price == LUA_NOREF ) && ( o2->lua_price != LUA_NOREF ) )
356 return +1;
357
358 /* Compare price. */
359 if ( o1->price < o2->price )
360 return +1;
361 else if ( o1->price > o2->price )
362 return -1;
363
364 /* It turns out they're the same. */
365 return strcmp( o1->name, o2->name );
366}
367
368int outfit_filterWeapon( const Outfit *o )
369{
370 return ( ( o->slot.type == OUTFIT_SLOT_WEAPON ) &&
371 !sp_required( o->slot.spid ) );
372}
373
374int outfit_filterUtility( const Outfit *o )
375{
376 return ( ( o->slot.type == OUTFIT_SLOT_UTILITY ) &&
377 !sp_required( o->slot.spid ) );
378}
379
380int outfit_filterStructure( const Outfit *o )
381{
382 return ( ( o->slot.type == OUTFIT_SLOT_STRUCTURE ) &&
383 !sp_required( o->slot.spid ) );
384}
385
386int outfit_filterCore( const Outfit *o )
387{
388 return sp_required( o->slot.spid );
389}
390
391int outfit_filterOther( const Outfit *o )
392{
393 return ( !sp_required( o->slot.spid ) &&
394 ( ( o->slot.type == OUTFIT_SLOT_NULL ) ||
395 ( o->slot.type == OUTFIT_SLOT_NA ) ||
396 ( o->slot.type == OUTFIT_SLOT_INTRINSIC ) ) );
397}
398
405const char *outfit_slotName( const Outfit *o )
406{
407 return slotName( o->slot.type );
408}
409
413const char *slotName( const OutfitSlotType type )
414{
415 switch ( type ) {
416 case OUTFIT_SLOT_NULL:
417 return "NULL";
418 case OUTFIT_SLOT_NA:
419 return N_( "N/A" );
420 case OUTFIT_SLOT_INTRINSIC:
421 return N_( "Intrinsic" );
422 case OUTFIT_SLOT_STRUCTURE:
423 return N_( "Structure" );
424 case OUTFIT_SLOT_UTILITY:
425 return N_( "Utility" );
426 case OUTFIT_SLOT_WEAPON:
427 return N_( "Weapon" );
428 default:
429 return N_( "Unknown" );
430 }
431}
432
436const char *slotSize( const OutfitSlotSize o )
437{
438 switch ( o ) {
439 case OUTFIT_SLOT_SIZE_NA:
440 return N_( "N/A" );
441 case OUTFIT_SLOT_SIZE_LIGHT:
442 return N_( "Small" );
443 case OUTFIT_SLOT_SIZE_MEDIUM:
444 return N_( "Medium" );
445 case OUTFIT_SLOT_SIZE_HEAVY:
446 return N_( "Large" );
447 default:
448 return N_( "Unknown" );
449 }
450}
451
458const char *outfit_slotSize( const Outfit *o )
459{
460 return slotSize( o->slot.size );
461}
462
469const glColour *outfit_slotSizeColour( const OutfitSlot *os )
470{
471 if ( os->size == OUTFIT_SLOT_SIZE_HEAVY )
472 return &cOutfitHeavy;
473 else if ( os->size == OUTFIT_SLOT_SIZE_MEDIUM )
474 return &cOutfitMedium;
475 else if ( os->size == OUTFIT_SLOT_SIZE_LIGHT )
476 return &cOutfitLight;
477 return NULL;
478}
479
488{
489 if ( os->size == OUTFIT_SLOT_SIZE_HEAVY )
490 return 'r';
491 else if ( os->size == OUTFIT_SLOT_SIZE_MEDIUM )
492 return 'b';
493 else if ( os->size == OUTFIT_SLOT_SIZE_LIGHT )
494 return 'y';
495 return '0';
496}
497
506{
507 if ( os->type == OUTFIT_SLOT_WEAPON )
508 return 'p';
509 else if ( os->type == OUTFIT_SLOT_UTILITY )
510 return 'g';
511 else if ( os->type == OUTFIT_SLOT_STRUCTURE )
512 return 'n';
513 return '0';
514}
515
525size_t outfit_getNameWithClass( const Outfit *outfit, char *buf, size_t size )
526{
527 size_t p = scnprintf( &buf[0], size, "%s", _( outfit->name ) );
528 if ( outfit->slot.type != OUTFIT_SLOT_NA ) {
529 p += scnprintf( &buf[p], size - p, _( "\n#%c%s #%c%s #0slot" ),
531 _( outfit_slotSize( outfit ) ),
533 _( outfit_slotName( outfit ) ) );
534 }
535 return p;
536}
537
544OutfitSlotSize outfit_toSlotSize( const char *s )
545{
546 if ( s == NULL ) {
547 /*WARN( "(NULL) outfit slot size" );*/
548 return OUTFIT_SLOT_SIZE_NA;
549 }
550
551 if ( strcasecmp( s, "Large" ) == 0 )
552 return OUTFIT_SLOT_SIZE_HEAVY;
553 else if ( strcasecmp( s, "Medium" ) == 0 )
554 return OUTFIT_SLOT_SIZE_MEDIUM;
555 else if ( strcasecmp( s, "Small" ) == 0 )
556 return OUTFIT_SLOT_SIZE_LIGHT;
557
558 WARN( _( "'%s' does not match any outfit slot sizes." ), s );
559 return OUTFIT_SLOT_SIZE_NA;
560}
561
567int outfit_isActive( const Outfit *o )
568{
569 if ( outfit_isForward( o ) || outfit_isTurret( o ) ||
571 return 1;
572 if ( outfit_isMod( o ) && o->u.mod.active )
573 return 1;
574 if ( outfit_isAfterburner( o ) )
575 return 1;
576 return 0;
577}
578
585{
586 /* Must be active. */
587 if ( !outfit_isActive( o ) )
588 return 0;
589
590 /* Special case it is lua-based and not toggleable. */
591 if ( outfit_isMod( o ) && ( o->lua_env != LUA_NOREF ) &&
592 ( o->lua_ontoggle == LUA_NOREF ) )
593 return 0;
594
595 return 1;
596}
597
603int outfit_isWeapon( const Outfit *o )
604{
605 return ( ( o->type == OUTFIT_TYPE_BOLT ) ||
606 ( o->type == OUTFIT_TYPE_BEAM ) ||
607 ( o->type == OUTFIT_TYPE_TURRET_BOLT ) ||
608 ( o->type == OUTFIT_TYPE_TURRET_BEAM ) ||
609 ( o->type == OUTFIT_TYPE_LAUNCHER ) ||
610 ( o->type == OUTFIT_TYPE_TURRET_LAUNCHER ) ||
611 ( o->type == OUTFIT_TYPE_FIGHTER_BAY ) );
612}
613
620{
621 return ( ( o->type == OUTFIT_TYPE_BOLT ) ||
622 ( o->type == OUTFIT_TYPE_BEAM ) );
623}
624
629int outfit_isBolt( const Outfit *o )
630{
631 return ( ( o->type == OUTFIT_TYPE_BOLT ) ||
632 ( o->type == OUTFIT_TYPE_TURRET_BOLT ) );
633}
634
639int outfit_isBeam( const Outfit *o )
640{
641 return ( ( o->type == OUTFIT_TYPE_BEAM ) ||
642 ( o->type == OUTFIT_TYPE_TURRET_BEAM ) );
643}
644
650{
651 return ( ( o->type == OUTFIT_TYPE_LAUNCHER ) ||
652 ( o->type == OUTFIT_TYPE_TURRET_LAUNCHER ) );
653}
654
659int outfit_isSeeker( const Outfit *o )
660{
661 if ( ( ( o->type == OUTFIT_TYPE_TURRET_LAUNCHER ) ||
662 ( o->type == OUTFIT_TYPE_LAUNCHER ) ) &&
663 ( o->u.lau.ai > 0 ) )
664 return 1;
665 return 0;
666}
667
672int outfit_isTurret( const Outfit *o )
673{
674 return ( ( o->type == OUTFIT_TYPE_TURRET_BOLT ) ||
675 ( o->type == OUTFIT_TYPE_TURRET_BEAM ) ||
676 ( o->type == OUTFIT_TYPE_TURRET_LAUNCHER ) );
677}
678
683int outfit_isMod( const Outfit *o )
684{
685 return ( o->type == OUTFIT_TYPE_MODIFICATION );
686}
687
693{
694 return ( o->type == OUTFIT_TYPE_AFTERBURNER );
695}
696
702{
703 return ( o->type == OUTFIT_TYPE_FIGHTER_BAY );
704}
705
710int outfit_isMap( const Outfit *o )
711{
712 return ( o->type == OUTFIT_TYPE_MAP );
713}
714
720{
721 return ( o->type == OUTFIT_TYPE_LOCALMAP );
722}
723
729{
730 return ( o->type == OUTFIT_TYPE_LICENSE );
731}
732
737int outfit_isGUI( const Outfit *o )
738{
739 return ( o->type == OUTFIT_TYPE_GUI );
740}
741
748{
749 return ( o->properties & OUTFIT_PROP_WEAP_SECONDARY ) != 0;
750}
751
756const OutfitGFX *outfit_gfx( const Outfit *o )
757{
758 if ( outfit_isBolt( o ) )
759 return &o->u.blt.gfx;
760 else if ( outfit_isLauncher( o ) )
761 return &o->u.lau.gfx;
762 return NULL;
763}
764
768const CollPoly *outfit_plg( const Outfit *o )
769{
770 if ( outfit_isBolt( o ) )
771 return &o->u.blt.gfx.polygon;
772 else if ( outfit_isLauncher( o ) )
773 return &o->u.lau.gfx.polygon;
774 return NULL;
775}
776
781{
782 if ( outfit_isBolt( o ) )
783 return o->u.blt.spfx_armour;
784 else if ( outfit_isBeam( o ) )
785 return o->u.bem.spfx_armour;
786 else if ( outfit_isLauncher( o ) )
787 return o->u.lau.spfx_armour;
788 return -1;
789}
790
795{
796 if ( outfit_isBolt( o ) )
797 return o->u.blt.spfx_shield;
798 else if ( outfit_isBeam( o ) )
799 return o->u.bem.spfx_shield;
800 else if ( outfit_isLauncher( o ) )
801 return o->u.lau.spfx_shield;
802 return -1;
803}
804
808const Damage *outfit_damage( const Outfit *o )
809{
810 if ( outfit_isBolt( o ) )
811 return &o->u.blt.dmg;
812 else if ( outfit_isBeam( o ) )
813 return &o->u.bem.dmg;
814 else if ( outfit_isLauncher( o ) )
815 return &o->u.lau.dmg;
816 return NULL;
817}
818
822double outfit_radius( const Outfit *o )
823{
824 if ( outfit_isBolt( o ) )
825 return o->u.blt.radius;
826 else if ( outfit_isLauncher( o ) )
827 return o->u.lau.radius;
828 return 0.;
829}
830
834double outfit_delay( const Outfit *o )
835{
836 if ( outfit_isBolt( o ) )
837 return o->u.blt.delay;
838 else if ( outfit_isBeam( o ) )
839 return o->u.bem.delay;
840 else if ( outfit_isLauncher( o ) )
841 return o->u.lau.delay;
842 else if ( outfit_isFighterBay( o ) )
843 return o->u.bay.delay;
844 return -1;
845}
846
850int outfit_amount( const Outfit *o )
851{
852 if ( outfit_isLauncher( o ) )
853 return o->u.lau.amount;
854 else if ( outfit_isFighterBay( o ) )
855 return o->u.bay.amount;
856 return -1;
857}
858
863double outfit_energy( const Outfit *o )
864{
865 if ( outfit_isBolt( o ) )
866 return o->u.blt.energy;
867 else if ( outfit_isBeam( o ) )
868 return o->u.bem.energy;
869 else if ( outfit_isLauncher( o ) )
870 return o->u.lau.energy;
871 return -1.;
872}
873
877double outfit_heat( const Outfit *o )
878{
879 if ( outfit_isBolt( o ) )
880 return o->u.blt.heat;
881 else if ( outfit_isAfterburner( o ) )
882 return o->u.afb.heat;
883 else if ( outfit_isBeam( o ) )
884 return o->u.bem.heat;
885 return -1;
886}
887
891double outfit_cpu( const Outfit *o )
892{
893 return o->cpu;
894}
895
899double outfit_range( const Outfit *o )
900{
901 return pilot_outfitRange( NULL, o );
902}
903
908double outfit_speed( const Outfit *o )
909{
910 return pilot_outfitSpeed( NULL, o );
911}
912
918double outfit_swivel( const Outfit *o )
919{
920 if ( outfit_isBolt( o ) )
921 return o->u.blt.swivel;
922 else if ( outfit_isLauncher( o ) )
923 return o->u.lau.swivel;
924 else if ( outfit_isBeam( o ) )
925 return o->u.bem.swivel;
926 return -1.;
927}
928
933double outfit_spin( const Outfit *o )
934{
935 if ( outfit_isBolt( o ) )
936 return o->u.blt.gfx.spin;
937 else if ( outfit_isLauncher( o ) )
938 return o->u.lau.gfx.spin;
939 return -1.;
940}
941
946double outfit_trackmin( const Outfit *o )
947{
948 if ( outfit_isBolt( o ) )
949 return o->u.blt.trackmin;
950 else if ( outfit_isLauncher( o ) )
951 return o->u.lau.trackmin;
952 else if ( outfit_isBeam( o ) )
953 return 0.;
954 return -1.;
955}
956
961double outfit_trackmax( const Outfit *o )
962{
963 if ( outfit_isBolt( o ) )
964 return o->u.blt.trackmax;
965 else if ( outfit_isLauncher( o ) )
966 return o->u.lau.trackmax;
967 else if ( outfit_isBeam( o ) )
968 return 1.;
969 return -1.;
970}
971
977{
978 if ( outfit_isBolt( o ) )
979 return o->u.blt.mining_rarity;
980 else if ( outfit_isLauncher( o ) )
981 return o->u.lau.mining_rarity;
982 else if ( outfit_isBeam( o ) )
983 return o->u.bem.mining_rarity;
984 return -1;
985}
986
991int outfit_sound( const Outfit *o )
992{
993 if ( outfit_isBolt( o ) )
994 return o->u.blt.sound;
995 else if ( outfit_isLauncher( o ) )
996 return o->u.lau.sound;
997 return -1.;
998}
999
1005{
1006 if ( outfit_isBolt( o ) )
1007 return o->u.blt.sound_hit;
1008 else if ( outfit_isLauncher( o ) )
1009 return o->u.lau.sound_hit;
1010 return -1.;
1011}
1012
1017double outfit_ammoMass( const Outfit *o )
1018{
1019 if ( outfit_isLauncher( o ) )
1020 return o->u.lau.ammo_mass;
1021 else if ( outfit_isFighterBay( o ) )
1022 return o->u.bay.ship_mass;
1023 return -1.;
1024}
1025
1030double outfit_duration( const Outfit *o )
1031{
1032 if ( outfit_isMod( o ) ) {
1033 if ( o->u.mod.active )
1034 return o->u.mod.duration;
1035 } else if ( outfit_isAfterburner( o ) )
1036 return INFINITY;
1037 return -1.;
1038}
1039
1044double outfit_cooldown( const Outfit *o )
1045{
1046 if ( outfit_isMod( o ) ) {
1047 if ( o->u.mod.active )
1048 return o->u.mod.cooldown;
1049 } else if ( outfit_isAfterburner( o ) )
1050 return 0.;
1051 return -1.;
1052}
1053
1060const char *outfit_getType( const Outfit *o )
1061{
1062 const char *outfit_typename[] = {
1063 N_( "NULL" ),
1064 N_( "Bolt Cannon" ),
1065 N_( "Beam Cannon" ),
1066 N_( "Bolt Turret" ),
1067 N_( "Beam Turret" ),
1068 N_( "Launcher" ),
1069 N_( "Turret Launcher" ),
1070 N_( "Ship Modification" ),
1071 N_( "Afterburner" ),
1072 N_( "Fighter Bay" ),
1073 N_( "Star Map" ),
1074 N_( "Local Map" ),
1075 N_( "GUI" ),
1076 N_( "License" ),
1077 };
1078
1079 /* Name override. */
1080 if ( o->typename != NULL )
1081 return o->typename;
1082 return outfit_typename[o->type];
1083}
1084
1091const char *outfit_getTypeBroad( const Outfit *o )
1092{
1093 if ( outfit_isBolt( o ) )
1094 return N_( "Bolt Weapon" );
1095 else if ( outfit_isBeam( o ) )
1096 return N_( "Beam Weapon" );
1097 else if ( outfit_isLauncher( o ) )
1098 return N_( "Launcher" );
1099 // else if (outfit_isTurret(o)) return N_("Turret");
1100 else if ( outfit_isMod( o ) )
1101 return N_( "Modification" );
1102 else if ( outfit_isAfterburner( o ) )
1103 return N_( "Afterburner" );
1104 else if ( outfit_isFighterBay( o ) )
1105 return N_( "Fighter Bay" );
1106 else if ( outfit_isMap( o ) )
1107 return N_( "Map" );
1108 else if ( outfit_isLocalMap( o ) )
1109 return N_( "Local Map" );
1110 else if ( outfit_isGUI( o ) )
1111 return N_( "GUI" );
1112 else if ( outfit_isLicense( o ) )
1113 return N_( "License" );
1114 else
1115 return N_( "Unknown" );
1116}
1117
1123const char *outfit_getAmmoAI( const Outfit *o )
1124{
1125 const char *ai_type[] = { N_( "Unguided" ), N_( "Seek" ), N_( "Smart" ) };
1126
1127 if ( !outfit_isLauncher( o ) ) {
1128 WARN( _( "Outfit '%s' is not a launcher outfit" ), o->name );
1129 return NULL;
1130 }
1131
1132 return ai_type[o->u.lau.ai];
1133}
1134
1143const char *outfit_description( const Outfit *o )
1144{
1145 return pilot_outfitDescription( NULL, o );
1146}
1147
1157const char *outfit_summary( const Outfit *o, int withname )
1158{
1159 return pilot_outfitSummary( NULL, o, withname );
1160}
1161
1168const char *outfit_shortname( const Outfit *o )
1169{
1170 return ( o->shortname != NULL ) ? _( o->shortname ) : _( o->name );
1171}
1172
1180int outfit_fitsSlot( const Outfit *o, const OutfitSlot *s )
1181{
1182 const OutfitSlot *os = &o->slot;
1183
1184 /* Outfit must have valid slot type. */
1185 if ( ( os->type == OUTFIT_SLOT_NULL ) || ( os->type == OUTFIT_SLOT_NA ) ||
1186 ( os->type == OUTFIT_SLOT_INTRINSIC ) )
1187 return 0;
1188
1189 /* Outfit type must match outfit slot. */
1190 if ( os->type != s->type )
1191 return 0;
1192
1193 /* It doesn't fit. */
1194 if ( os->size > s->size )
1195 return 0;
1196
1197 /* Must match slot property. */
1198 if ( os->spid != 0 )
1199 if ( s->spid != os->spid )
1200 return 0;
1201
1202 /* Exclusive only match property. */
1203 if ( s->exclusive )
1204 if ( s->spid != os->spid )
1205 return 0;
1206
1207 /* Must have valid slot size. */
1208 /*
1209 if (os->size == OUTFIT_SLOT_SIZE_NA)
1210 return 0;
1211 */
1212
1213 /* It meets all criteria. */
1214 return 1;
1215}
1216
1224int outfit_fitsSlotType( const Outfit *o, const OutfitSlot *s )
1225{
1226 const OutfitSlot *os = &o->slot;
1227
1228 /* Outfit must have valid slot type. */
1229 if ( ( os->type == OUTFIT_SLOT_NULL ) || ( os->type == OUTFIT_SLOT_NA ) ||
1230 ( os->type == OUTFIT_SLOT_INTRINSIC ) )
1231 return 0;
1232
1233 /* Outfit type must match outfit slot. */
1234 if ( os->type != s->type )
1235 return 0;
1236
1237 /* It meets all criteria. */
1238 return 1;
1239}
1240
1247{
1248 (void)s;
1249}
1250
1251#define O_CMP( s, t ) \
1252 if ( strcasecmp( buf, ( s ) ) == 0 ) \
1253 return t
1254
1260static OutfitType outfit_strToOutfitType( char *buf )
1261{
1262 O_CMP( "bolt", OUTFIT_TYPE_BOLT );
1263 O_CMP( "beam", OUTFIT_TYPE_BEAM );
1264 O_CMP( "turret bolt", OUTFIT_TYPE_TURRET_BOLT );
1265 O_CMP( "turret beam", OUTFIT_TYPE_TURRET_BEAM );
1266 O_CMP( "launcher", OUTFIT_TYPE_LAUNCHER );
1267 O_CMP( "turret launcher", OUTFIT_TYPE_TURRET_LAUNCHER );
1268 O_CMP( "modification", OUTFIT_TYPE_MODIFICATION );
1269 O_CMP( "afterburner", OUTFIT_TYPE_AFTERBURNER );
1270 O_CMP( "fighter bay", OUTFIT_TYPE_FIGHTER_BAY );
1271 O_CMP( "map", OUTFIT_TYPE_MAP );
1272 O_CMP( "localmap", OUTFIT_TYPE_LOCALMAP );
1273 O_CMP( "license", OUTFIT_TYPE_LICENSE );
1274 O_CMP( "gui", OUTFIT_TYPE_GUI );
1275
1276 WARN( _( "Invalid outfit type: '%s'" ), buf );
1277 return OUTFIT_TYPE_NULL;
1278}
1279#undef O_CMP
1280
1284static void sdesc_miningRarity( int *l, Outfit *temp, int rarity )
1285{
1286 if ( rarity == 0 )
1287 return;
1288 if ( rarity == 2 )
1289 SDESC_ADD( *l, temp, "\n#g%s#0",
1290 _( "Can mine uncommon and rare minerals" ) );
1291 else
1292 SDESC_ADD( *l, temp, "\n#g%s#0", _( "Can mine uncommon minerals" ) );
1293}
1294
1307static int outfit_parseDamage( Damage *dmg, xmlNodePtr node )
1308{
1309 xmlNodePtr cur;
1310
1311 /* Defaults. */
1313 dmg->damage = 0.;
1314 dmg->penetration = 0.;
1315 dmg->disable = 0.;
1316
1317 cur = node->xmlChildrenNode;
1318 do {
1319 xml_onlyNodes( cur );
1320
1321 /* Core properties. */
1322 xmlr_float( cur, "penetrate", dmg->penetration );
1323 xmlr_float( cur, "physical", dmg->damage );
1324 xmlr_float( cur, "disable", dmg->disable );
1325
1326 /* Get type */
1327 if ( xml_isNode( cur, "type" ) ) {
1328 char *buf = xml_get( cur );
1329 dmg->type = dtype_get( buf );
1330 if ( dmg->type < 0 ) { /* Case damage type in outfit.xml that isn't in
1331 damagetype.xml */
1332 dmg->type = 0;
1333 WARN( _( "Unknown damage type '%s'" ), buf );
1334 }
1335 continue;
1336 }
1337 // cppcheck-suppress nullPointerRedundantCheck
1338 WARN( _( "Damage has unknown node '%s'" ), cur->name );
1339
1340 } while ( xml_nextNode( cur ) );
1341
1342 /* Normalize. */
1343 dmg->penetration /= 100.;
1344
1345 return 0;
1346}
1347
1354static int outfit_loadPLG( Outfit *temp, const char *buf )
1355{
1356 char file[PATH_MAX];
1357 OutfitGFX *gfx;
1358 xmlDocPtr doc;
1359 xmlNodePtr node;
1360
1361 if ( outfit_isLauncher( temp ) )
1362 gfx = &temp->u.lau.gfx;
1363 else if ( outfit_isBolt( temp ) )
1364 gfx = &temp->u.blt.gfx;
1365 else {
1366 WARN( _( "Trying to load polygon for non-compatible outfit '%s'!" ),
1367 temp->name );
1368 return -1;
1369 }
1370
1371 snprintf( file, sizeof( file ), "%s%s.xml", OUTFIT_POLYGON_PATH, buf );
1372
1373 /* See if the file does exist. */
1374 if ( !PHYSFS_exists( file ) ) {
1375 WARN( _( "%s xml collision polygon does not exist!\n \
1376 Please use the script 'polygon_from_sprite.py' \
1377that can be found in Naev's artwork repo." ),
1378 file );
1379 return 0;
1380 }
1381
1382 /* Load the XML. */
1383 doc = xml_parsePhysFS( file );
1384
1385 if ( doc == NULL )
1386 return 0;
1387
1388 node = doc->xmlChildrenNode; /* First polygon node */
1389 if ( node == NULL ) {
1390 xmlFreeDoc( doc );
1391 WARN( _( "Malformed %s file: does not contain elements" ), file );
1392 return 0;
1393 }
1394
1395 do { /* load the polygon data */
1396 if ( xml_isNode( node, "polygons" ) )
1397 poly_load( &gfx->polygon, node, file );
1398 } while ( xml_nextNode( node ) );
1399
1400 xmlFreeDoc( doc );
1401 return 0;
1402}
1403
1407static int outfit_loadGFX( Outfit *temp, const xmlNodePtr node )
1408{
1409 char *type;
1410 OutfitGFX *gfx;
1411 int flags;
1412
1413 if ( outfit_isLauncher( temp ) )
1414 gfx = &temp->u.lau.gfx;
1415 else if ( outfit_isBolt( temp ) )
1416 gfx = &temp->u.blt.gfx;
1417 else {
1418 WARN( _( "Trying to load graphics for non-compatible outfit '%s'!" ),
1419 temp->name );
1420 return -1;
1421 }
1422
1423 /* Comomn properties. */
1424 xmlr_attr_float( node, "spin", gfx->spin );
1425 if ( gfx->spin != 0. )
1426 outfit_setProp( temp, OUTFIT_PROP_WEAP_SPIN );
1427
1428 /* Split by type. */
1429 xmlr_attr_strd( node, "type", type );
1430 if ( ( type != NULL ) && ( strcmp( type, "shader" ) == 0 ) ) {
1431 char *vertex;
1432
1433 xmlr_attr_strd( node, "vertex", vertex );
1434 if ( vertex == NULL )
1435 vertex = strdup( "project_pos.vert" );
1436 gl_contextSet();
1437 gfx->program = gl_program_vert_frag( vertex, xml_get( node ) );
1438 free( vertex );
1439 gfx->vertex = glGetAttribLocation( gfx->program, "vertex" );
1440 gfx->projection = glGetUniformLocation( gfx->program, "projection" );
1441 gfx->dimensions = glGetUniformLocation( gfx->program, "dimensions" );
1442 gfx->u_r = glGetUniformLocation( gfx->program, "u_r" );
1443 gfx->u_time = glGetUniformLocation( gfx->program, "u_time" );
1444 gfx->u_fade = glGetUniformLocation( gfx->program, "u_fade" );
1445 gl_contextUnset();
1446
1447 xmlr_attr_float_def( node, "size", gfx->size, -1. );
1448 if ( gfx->size < 0. )
1449 WARN( _( "Outfit '%s' has GFX shader but no 'size' set!" ),
1450 temp->name );
1451
1452 xmlr_attr_float_def( node, "col_size", gfx->col_size, gfx->size * 0.8 );
1453
1454 free( type );
1455 return 0;
1456 } else if ( type != NULL ) {
1457 WARN( _( "Outfit '%s' has unknown gfx type '%s'!" ), temp->name, type );
1458 free( type );
1459 return -1;
1460 }
1461
1462 /* Load the collision polygon. */
1463 const char *buf = xml_get( node );
1464 outfit_loadPLG( temp, buf );
1465
1466 /* Load normal graphics. */
1467 flags = OPENGL_TEX_MIPMAPS;
1468 if ( array_size( gfx->polygon.views ) == 0 )
1469 flags |= OPENGL_TEX_MAPTRANS;
1470 gfx->tex = xml_parseTexture( node, OUTFIT_GFX_PATH "space/%s", 6, 6, flags );
1471 gfx->size = ( gfx->tex->sw + gfx->tex->sh ) * 0.5;
1472
1473 /* See if there is a collision size, or an override. */
1474 char *col;
1475 xmlr_attr_strd( node, "col_size", col );
1476 if ( col != NULL ) {
1477 outfit_setProp( temp, OUTFIT_PROP_WEAP_COLLISION_OVERRIDE );
1478 gfx->col_size = strtod( col, NULL );
1479 free( col );
1480 }
1481
1482 /* Validity check: there must be 1 polygon per sprite. */
1483 if ( array_size( gfx->polygon.views ) <= 0 )
1484 WARN( _( "Outfit '%s' is missing collision polygon!" ), temp->name );
1485
1486 return 0;
1487}
1488
1495static void outfit_parseSBolt( Outfit *temp, const xmlNodePtr parent )
1496{
1497 ShipStatList *ll;
1498 xmlNodePtr node;
1499 double C, area;
1500 double dshield, darmour, dknockback;
1501 int l;
1502
1503 /* Defaults */
1504 temp->u.blt.gfx.size = -1.;
1505 temp->u.blt.spfx_armour = -1;
1506 temp->u.blt.spfx_shield = -1;
1507 temp->u.blt.sound = -1;
1508 temp->u.blt.sound_hit = -1;
1509 temp->u.blt.falloff = -1.;
1510 temp->u.blt.trackmin = -1.;
1511 temp->u.blt.trackmax = -1.;
1512 temp->u.blt.shots = 1;
1513
1514 node = parent->xmlChildrenNode;
1515 do { /* load all the data */
1516 xml_onlyNodes( node );
1517 xmlr_float( node, "speed", temp->u.blt.speed );
1518 xmlr_float( node, "delay", temp->u.blt.delay );
1519 xmlr_float( node, "energy", temp->u.blt.energy );
1520 xmlr_float( node, "heatup", temp->u.blt.heatup );
1521 xmlr_float( node, "trackmin", temp->u.blt.trackmin );
1522 xmlr_float( node, "trackmax", temp->u.blt.trackmax );
1523 xmlr_float( node, "swivel", temp->u.blt.swivel );
1524 xmlr_float( node, "dispersion", temp->u.blt.dispersion );
1525 xmlr_float( node, "speed_dispersion", temp->u.blt.speed_dispersion );
1526 xmlr_int( node, "shots", temp->u.blt.shots );
1527 xmlr_int( node, "mining_rarity", temp->u.blt.mining_rarity );
1528 xmlr_strd( node, "lua", temp->lua_file );
1529 if ( xml_isNode( node, "radius" ) ) {
1530 char *buf;
1531 temp->u.blt.radius = xml_getFloat( node );
1532 xmlr_attr_strd( node, "friendlyfire", buf );
1533 if ( buf != NULL ) {
1534 outfit_setProp( temp, OUTFIT_PROP_WEAP_FRIENDLYFIRE );
1535 free( buf );
1536 }
1537 continue;
1538 }
1539 if ( xml_isNode( node, "pointdefense" ) ) {
1540 outfit_setProp( temp, OUTFIT_PROP_WEAP_POINTDEFENSE );
1541 continue;
1542 }
1543 if ( xml_isNode( node, "miss_ships" ) ) {
1544 outfit_setProp( temp, OUTFIT_PROP_WEAP_MISS_SHIPS );
1545 continue;
1546 }
1547 if ( xml_isNode( node, "miss_asteroids" ) ) {
1548 outfit_setProp( temp, OUTFIT_PROP_WEAP_MISS_ASTEROIDS );
1549 continue;
1550 }
1551 if ( xml_isNode( node, "miss_explode" ) ) {
1552 outfit_setProp( temp, OUTFIT_PROP_WEAP_MISS_EXPLODE );
1553 continue;
1554 }
1555 if ( xml_isNode( node, "onlyhittarget" ) ) {
1556 outfit_setProp( temp, OUTFIT_PROP_WEAP_ONLYHITTARGET );
1557 continue;
1558 }
1559 if ( xml_isNode( node, "range" ) ) {
1560 char *buf;
1561 xmlr_attr_strd( node, "blowup", buf );
1562 if ( buf != NULL ) {
1563 if ( strcmp( buf, "armour" ) == 0 )
1564 outfit_setProp( temp, OUTFIT_PROP_WEAP_BLOWUP_SHIELD );
1565 else if ( strcmp( buf, "shield" ) == 0 )
1566 outfit_setProp( temp, OUTFIT_PROP_WEAP_BLOWUP_ARMOUR );
1567 else
1568 WARN( _( "Outfit '%s' has invalid blowup property: '%s'" ),
1569 temp->name, buf );
1570 free( buf );
1571 }
1572 temp->u.blt.range = xml_getFloat( node );
1573 continue;
1574 }
1575 xmlr_float( node, "falloff", temp->u.blt.falloff );
1576
1577 /* Graphics. */
1578 if ( xml_isNode( node, "gfx" ) ) {
1579 outfit_loadGFX( temp, node );
1580 continue;
1581 }
1582 if ( xml_isNode( node, "gfx_end" ) ) {
1584 node, OUTFIT_GFX_PATH "space/%s", 6, 6, OPENGL_TEX_MIPMAPS );
1585 continue;
1586 }
1587
1588 /* Special effects. */
1589 if ( xml_isNode( node, "spfx_shield" ) ) {
1590 temp->u.blt.spfx_shield = spfx_get( xml_get( node ) );
1591 if ( temp->u.blt.spfx_shield < 0 )
1592 WARN( _( "Outfit '%s' has unknown spfx_shield '%s'!" ), temp->name,
1593 xml_get( node ) );
1594 continue;
1595 }
1596 if ( xml_isNode( node, "spfx_armour" ) ) {
1597 temp->u.blt.spfx_armour = spfx_get( xml_get( node ) );
1598 if ( temp->u.blt.spfx_armour < 0 )
1599 WARN( _( "Outfit '%s' has unknown spfx_armour '%s'!" ), temp->name,
1600 xml_get( node ) );
1601 continue;
1602 }
1603
1604 /* Misc. */
1605 if ( xml_isNode( node, "sound" ) ) {
1606 temp->u.blt.sound = sound_get( xml_get( node ) );
1607 continue;
1608 }
1609 if ( xml_isNode( node, "sound_hit" ) ) {
1610 temp->u.blt.sound_hit = sound_get( xml_get( node ) );
1611 continue;
1612 }
1613 if ( xml_isNode( node, "damage" ) ) {
1614 outfit_parseDamage( &temp->u.blt.dmg, node );
1615 continue;
1616 }
1617
1618 /* Stats. */
1619 ll = ss_listFromXML( node );
1620 if ( ll != NULL ) {
1621 ll->next = temp->stats;
1622 temp->stats = ll;
1623 continue;
1624 }
1625 WARN( _( "Outfit '%s' has unknown node '%s'" ), temp->name, node->name );
1626 } while ( xml_nextNode( node ) );
1627
1628 /* If not defined assume maximum. */
1629 if ( temp->u.blt.falloff < 0. )
1630 temp->u.blt.falloff = temp->u.blt.range;
1631
1632 /* Post processing. */
1633 temp->u.blt.swivel *= M_PI / 180.;
1634 temp->u.blt.dispersion *= M_PI / 180.;
1635 if ( outfit_isTurret( temp ) )
1636 temp->u.blt.swivel = M_PI;
1637 /*
1638 * dT Mthermal - Qweap
1639 * Hweap = ----------------------
1640 * tweap
1641 */
1642 C = pilot_heatCalcOutfitC( temp );
1643 area = pilot_heatCalcOutfitArea( temp );
1644 temp->u.blt.heat = ( ( 800. - CONST_SPACE_STAR_TEMP ) * C +
1645 STEEL_HEAT_CONDUCTIVITY *
1646 ( ( 800. - CONST_SPACE_STAR_TEMP ) * area ) ) /
1647 temp->u.blt.heatup * temp->u.blt.delay;
1648
1649 /* Set short description. */
1650 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
1651 l = 0;
1652 SDESC_ADD( l, temp, p_( "outfitstats", "%s [%s]" ),
1653 _( outfit_getType( temp ) ),
1654 _( dtype_damageTypeToStr( temp->u.blt.dmg.type ) ) );
1655 dtype_raw( temp->u.blt.dmg.type, &dshield, &darmour, &dknockback );
1656 // new_opts(name, unit, colour, threshold, hidezero, precision)
1657 l = os_printD( temp->summary_raw, l, darmour * 100., &darmour_opts );
1658 l = os_printD( temp->summary_raw, l, dshield * 100., &dshield_opts );
1659 l = os_printD( temp->summary_raw, l, dknockback * 100., &dknockback_opts );
1660 l = os_printD( temp->summary_raw, l, temp->cpu, &cpu_opts );
1661 l = os_printD( temp->summary_raw, l, temp->mass, &mass_opts );
1662 /* Higher level stats. */
1663 l = os_printD_rate( temp->summary_raw, l, temp->u.blt.dmg.damage,
1664 &damage_opts, temp->u.blt.shots,
1665 (double)temp->u.blt.shots * temp->u.blt.dmg.damage /
1666 temp->u.blt.delay,
1667 &dps_opts );
1668 l = os_printD_rate( temp->summary_raw, l, temp->u.blt.dmg.disable,
1669 &disable_opts, temp->u.blt.shots,
1670 (double)temp->u.blt.shots * temp->u.blt.dmg.disable /
1671 temp->u.blt.delay,
1672 &disable_rate_opts );
1673 l = os_printD_rate( temp->summary_raw, l, temp->u.blt.energy, &energy_opts,
1674 1, (double)temp->u.blt.energy / temp->u.blt.delay,
1675 &power_opts );
1676 /* Standard stats. */
1677 l = os_printD( temp->summary_raw, l, temp->u.blt.dmg.penetration * 100.,
1678 &penetration_opts );
1679 l = os_printD( temp->summary_raw, l, 1. / temp->u.blt.delay,
1680 &fire_rate_opts );
1681 if ( temp->u.blt.radius > 0. ) {
1682 char radius[STRMAX_SHORT];
1683 snprintf( radius, sizeof( radius ),
1684 outfit_isProp( temp, OUTFIT_PROP_WEAP_FRIENDLYFIRE )
1685 ? p_( "friendlyfire", "#r!! %s !!#0" )
1686 : "%s",
1687 _( "Hit radius" ) );
1688 const t_os_stat radius_opts = {
1689 .name = radius,
1690 .unit = _UNIT_DISTANCE,
1691 .colour = 0,
1692 .colour_threshold = 0,
1693 .hide_zero = 1,
1694 .precision = 0,
1695 };
1696 l = os_printD( temp->summary_raw, l, temp->u.blt.radius, &radius_opts );
1697 }
1698 l = os_printD( temp->summary_raw, l, temp->u.blt.range, &range_opts );
1699 l = os_printD( temp->summary_raw, l, temp->u.blt.speed, &speed_opts );
1700 l = os_printD( temp->summary_raw, l, temp->u.blt.heatup, &heatup_opts );
1701 l = os_printD( temp->summary_raw, l, temp->u.blt.dispersion * 180. / M_PI,
1702 &dispersion_opts );
1703 if ( !outfit_isTurret( temp ) )
1704 l = os_printD( temp->summary_raw, l, temp->u.blt.swivel * 180. / M_PI,
1705 &swivel_opts );
1706 l = os_printD_range( temp->summary_raw, l, temp->u.blt.trackmin,
1707 temp->u.blt.trackmax, &tracking_opts );
1708 sdesc_miningRarity( &l, temp, temp->u.blt.mining_rarity );
1709
1710#define MELEMENT( o, s ) \
1711 if ( o ) \
1712 WARN( _( "Outfit '%s' missing/invalid '%s' element" ), temp->name, \
1713 s )
1714 MELEMENT( temp->u.blt.gfx.size < 0., "gfx" );
1715 MELEMENT( temp->u.blt.spfx_shield == -1, "spfx_shield" );
1716 MELEMENT( temp->u.blt.spfx_armour == -1, "spfx_armour" );
1717 MELEMENT( ( sound_disabled != 0 ) && ( temp->u.blt.sound < 0 ), "sound" );
1718 MELEMENT( temp->mass == 0., "mass" );
1719 MELEMENT( temp->u.blt.delay == 0, "delay" );
1720 MELEMENT( temp->u.blt.speed == 0, "speed" );
1721 MELEMENT( temp->u.blt.range == 0, "range" );
1722 MELEMENT( temp->u.blt.dmg.damage == 0, "damage" );
1723 MELEMENT( temp->u.blt.energy == 0., "energy" );
1724 // MELEMENT(temp->cpu==0.,"cpu");
1725 MELEMENT( temp->u.blt.falloff > temp->u.blt.range, "falloff" );
1726 MELEMENT( temp->u.blt.heatup == 0., "heatup" );
1727 MELEMENT( ( ( temp->u.blt.swivel > 0. ) || outfit_isTurret( temp ) ) &&
1728 ( temp->u.blt.trackmin < 0. ),
1729 "trackmin" );
1730 MELEMENT( ( ( temp->u.blt.swivel > 0. ) || outfit_isTurret( temp ) ) &&
1731 ( temp->u.blt.trackmax < 0. ),
1732 "trackmax" );
1733#undef MELEMENT
1734}
1735
1742static void outfit_parseSBeam( Outfit *temp, const xmlNodePtr parent )
1743{
1744 ShipStatList *ll;
1745 int l;
1746 xmlNodePtr node;
1747 double C, area;
1748 double dshield, darmour, dknockback;
1749 char *shader;
1750
1751 /* Defaults. */
1752 temp->u.bem.spfx_armour = -1;
1753 temp->u.bem.spfx_shield = -1;
1754 temp->u.bem.sound_warmup = -1;
1755 temp->u.bem.sound = -1;
1756 temp->u.bem.sound_off = -1;
1757
1758 node = parent->xmlChildrenNode;
1759 do { /* load all the data */
1760 xml_onlyNodes( node );
1761 xmlr_float( node, "range", temp->u.bem.range );
1762 xmlr_float( node, "turn", temp->u.bem.turn );
1763 xmlr_float( node, "energy", temp->u.bem.energy );
1764 xmlr_float( node, "duration", temp->u.bem.duration );
1765 xmlr_float( node, "warmup", temp->u.bem.warmup );
1766 xmlr_float( node, "heatup", temp->u.bem.heatup );
1767 xmlr_float( node, "swivel", temp->u.bem.swivel );
1768 xmlr_int( node, "mining_rarity", temp->u.bem.mining_rarity );
1769 xmlr_strd( node, "lua", temp->lua_file );
1770 if ( xml_isNode( node, "pointdefense" ) ) {
1771 outfit_setProp( temp, OUTFIT_PROP_WEAP_POINTDEFENSE );
1772 continue;
1773 }
1774 if ( xml_isNode( node, "miss_ships" ) ) {
1775 outfit_setProp( temp, OUTFIT_PROP_WEAP_MISS_SHIPS );
1776 continue;
1777 }
1778 if ( xml_isNode( node, "miss_asteroids" ) ) {
1779 outfit_setProp( temp, OUTFIT_PROP_WEAP_MISS_ASTEROIDS );
1780 continue;
1781 }
1782
1783 if ( xml_isNode( node, "delay" ) ) {
1784 xmlr_attr_float( node, "min", temp->u.bem.min_delay );
1785 temp->u.bem.delay = xml_getFloat( node );
1786 continue;
1787 }
1788
1789 if ( xml_isNode( node, "damage" ) ) {
1790 outfit_parseDamage( &temp->u.bem.dmg, node );
1791 continue;
1792 }
1793
1794 /* Graphic stuff. */
1795 if ( xml_isNode( node, "shader" ) ) {
1796 xmlr_attr_float( node, "r", temp->u.bem.colour.r );
1797 xmlr_attr_float( node, "g", temp->u.bem.colour.g );
1798 xmlr_attr_float( node, "b", temp->u.bem.colour.b );
1799 xmlr_attr_float( node, "a", temp->u.bem.colour.a );
1800 xmlr_attr_float( node, "width", temp->u.bem.width );
1801 col_gammaToLinear( &temp->u.bem.colour );
1802 shader = xml_get( node );
1803 if ( gl_has( OPENGL_SUBROUTINES ) ) {
1804 gl_contextSet();
1805 temp->u.bem.shader = glGetSubroutineIndex(
1806 shaders.beam.program, GL_FRAGMENT_SHADER, shader );
1807 if ( temp->u.bem.shader == GL_INVALID_INDEX )
1808 WARN( "Beam outfit '%s' has unknown shader function '%s'",
1809 temp->name, shader );
1810 gl_contextUnset();
1811 }
1812 continue;
1813 }
1814 if ( xml_isNode( node, "spfx_armour" ) ) {
1815 temp->u.bem.spfx_armour = spfx_get( xml_get( node ) );
1816 continue;
1817 }
1818 if ( xml_isNode( node, "spfx_shield" ) ) {
1819 temp->u.bem.spfx_shield = spfx_get( xml_get( node ) );
1820 continue;
1821 }
1822
1823 /* Sound stuff. */
1824 if ( xml_isNode( node, "sound_warmup" ) ) {
1825 temp->u.bem.sound_warmup = sound_get( xml_get( node ) );
1826 continue;
1827 }
1828 if ( xml_isNode( node, "sound" ) ) {
1829 temp->u.bem.sound = sound_get( xml_get( node ) );
1830 continue;
1831 }
1832 if ( xml_isNode( node, "sound_off" ) ) {
1833 temp->u.bem.sound_off = sound_get( xml_get( node ) );
1834 continue;
1835 }
1836
1837 /* Stats. */
1838 ll = ss_listFromXML( node );
1839 if ( ll != NULL ) {
1840 ll->next = temp->stats;
1841 temp->stats = ll;
1842 continue;
1843 }
1844 WARN( _( "Outfit '%s' has unknown node '%s'" ), temp->name, node->name );
1845 } while ( xml_nextNode( node ) );
1846
1847 /* Post processing. */
1848 temp->u.bem.swivel *= M_PI / 180.;
1849 temp->u.bem.turn *= M_PI / 180.; /* Convert to rad/s. */
1850 C = pilot_heatCalcOutfitC( temp );
1851 area = pilot_heatCalcOutfitArea( temp );
1852 temp->u.bem.heat =
1853 ( ( 800. - CONST_SPACE_STAR_TEMP ) * C +
1854 STEEL_HEAT_CONDUCTIVITY *
1855 ( ( 800. - CONST_SPACE_STAR_TEMP ) * area ) ) /
1856 temp->u.bem.heatup *
1857 ( temp->u.bem.delay + temp->u.bem.warmup + temp->u.bem.duration ) /
1858 temp->u.bem.delay;
1859
1860 /* Set short description. */
1861 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
1862 l = 0;
1863 SDESC_ADD( l, temp, "%s [%s]", _( outfit_getType( temp ) ),
1864 _( dtype_damageTypeToStr( temp->u.bem.dmg.type ) ) );
1865 dtype_raw( temp->u.bem.dmg.type, &dshield, &darmour, &dknockback );
1866 l = os_printD( temp->summary_raw, l, darmour * 100., &darmour_opts );
1867 l = os_printD( temp->summary_raw, l, dshield * 100., &dshield_opts );
1868 l = os_printD( temp->summary_raw, l, dknockback * 100., &dknockback_opts );
1869 l = os_printD( temp->summary_raw, l, temp->cpu, &cpu_opts );
1870 l = os_printD( temp->summary_raw, l, temp->mass, &mass_opts );
1871 /* Higher level stats. */
1872 l = os_printD( temp->summary_raw, l, temp->u.bem.dmg.damage,
1873 &dps_opts ); /* TODO display DPS also? */
1874 l = os_printD( temp->summary_raw, l, temp->u.bem.dmg.disable,
1875 &disable_rate_opts );
1876 l = os_printD( temp->summary_raw, l, temp->u.bem.energy, &power_opts );
1877 /* Standard stats. */
1878 l = os_printD( temp->summary_raw, l, temp->u.bem.dmg.penetration * 100,
1879 &penetration_opts );
1880 l = os_printD( temp->summary_raw, l, temp->u.bem.duration, &duration_opts );
1881 l = os_printD_range( temp->summary_raw, l, temp->u.bem.min_delay,
1882 temp->u.bem.delay, &cooldown_opts );
1883 l = os_printD( temp->summary_raw, l, temp->u.bem.range, &range_opts );
1884 l = os_printD( temp->summary_raw, l, temp->u.bem.heatup, &heatup_opts );
1885 if ( !outfit_isTurret( temp ) )
1886 l = os_printD( temp->summary_raw, l, temp->u.bem.swivel * 180. / M_PI,
1887 &swivel_opts );
1888 sdesc_miningRarity( &l, temp, temp->u.bem.mining_rarity );
1889
1890#define MELEMENT( o, s ) \
1891 if ( o ) \
1892 WARN( _( "Outfit '%s' missing/invalid '%s' element" ), temp->name, \
1893 s )
1894 MELEMENT( temp->u.bem.width == 0., "shader width" );
1895 MELEMENT( temp->u.bem.spfx_shield == -1, "spfx_shield" );
1896 MELEMENT( temp->u.bem.spfx_armour == -1, "spfx_armour" );
1897 MELEMENT( ( sound_disabled != 0 ) && ( temp->u.bem.warmup > 0. ) &&
1898 ( temp->u.bem.sound < 0 ),
1899 "sound_warmup" );
1900 MELEMENT( ( sound_disabled != 0 ) && ( temp->u.bem.sound < 0 ), "sound" );
1901 MELEMENT( ( sound_disabled != 0 ) && ( temp->u.bem.sound_off < 0 ),
1902 "sound_off" );
1903 MELEMENT( temp->u.bem.delay == 0, "delay" );
1904 MELEMENT( temp->u.bem.duration == 0, "duration" );
1905 MELEMENT( temp->u.bem.min_delay < 0, "delay" );
1906 MELEMENT( temp->u.bem.min_delay > temp->u.bem.delay, "delay" );
1907 MELEMENT( temp->u.bem.range == 0, "range" );
1908 MELEMENT( ( temp->type != OUTFIT_TYPE_BEAM ) && ( temp->u.bem.turn == 0 ),
1909 "turn" );
1910 MELEMENT( temp->u.bem.energy == 0., "energy" );
1911 MELEMENT( temp->cpu == 0., "cpu" );
1912 MELEMENT( temp->u.bem.dmg.damage == 0, "damage" );
1913 MELEMENT( temp->u.bem.heatup == 0., "heatup" );
1914#undef MELEMENT
1915}
1916
1923static void outfit_parseSLauncher( Outfit *temp, const xmlNodePtr parent )
1924{
1925 ShipStatList *ll;
1926 xmlNodePtr node;
1927 double dshield, darmour, dknockback;
1928 int l;
1929
1930 temp->u.lau.trackmin = -1.;
1931 temp->u.lau.trackmax = -1.;
1932 temp->u.lau.spfx_armour = -1;
1933 temp->u.lau.spfx_shield = -1;
1934 temp->u.lau.sound = -1;
1935 temp->u.lau.sound_hit = -1;
1936 temp->u.lau.trail_spec = NULL;
1937 temp->u.lau.ai = -1;
1938 temp->u.lau.speed_max = -1.;
1939 temp->u.lau.shots = 1;
1940
1941 node = parent->xmlChildrenNode;
1942 do { /* load all the data */
1943 xml_onlyNodes( node );
1944 xmlr_float( node, "delay", temp->u.lau.delay );
1945 xmlr_int( node, "amount", temp->u.lau.amount );
1946 xmlr_float( node, "reload_time", temp->u.lau.reload_time );
1947 xmlr_float( node, "trackmin", temp->u.lau.trackmin );
1948 xmlr_float( node, "trackmax", temp->u.lau.trackmax );
1949 xmlr_float( node, "lockon", temp->u.lau.lockon );
1950 xmlr_float( node, "iflockon", temp->u.lau.iflockon );
1951 xmlr_float( node, "swivel", temp->u.lau.swivel );
1952 xmlr_float( node, "dispersion", temp->u.lau.dispersion );
1953 xmlr_float( node, "speed_dispersion", temp->u.lau.speed_dispersion );
1954 xmlr_float( node, "armour", temp->u.lau.armour );
1955 xmlr_float( node, "absorb", temp->u.lau.dmg_absorb );
1956 xmlr_int( node, "shots", temp->u.lau.shots );
1957 xmlr_int( node, "mining_rarity", temp->u.lau.mining_rarity );
1958 xmlr_strd( node, "lua", temp->lua_file );
1959 if ( xml_isNode( node, "radius" ) ) {
1960 char *buf;
1961 temp->u.lau.radius = xml_getFloat( node );
1962 xmlr_attr_strd( node, "friendlyfire", buf );
1963 if ( buf != NULL ) {
1964 outfit_setProp( temp, OUTFIT_PROP_WEAP_FRIENDLYFIRE );
1965 free( buf );
1966 }
1967 continue;
1968 }
1969 if ( xml_isNode( node, "pointdefense" ) ) {
1970 outfit_setProp( temp, OUTFIT_PROP_WEAP_POINTDEFENSE );
1971 continue;
1972 }
1973 if ( xml_isNode( node, "miss_ships" ) ) {
1974 outfit_setProp( temp, OUTFIT_PROP_WEAP_MISS_SHIPS );
1975 continue;
1976 }
1977 if ( xml_isNode( node, "miss_asteroids" ) ) {
1978 outfit_setProp( temp, OUTFIT_PROP_WEAP_MISS_ASTEROIDS );
1979 continue;
1980 }
1981 if ( xml_isNode( node, "miss_explode" ) ) {
1982 outfit_setProp( temp, OUTFIT_PROP_WEAP_MISS_EXPLODE );
1983 continue;
1984 }
1985 if ( xml_isNode( node, "onlyhittarget" ) ) {
1986 outfit_setProp( temp, OUTFIT_PROP_WEAP_ONLYHITTARGET );
1987 continue;
1988 }
1989
1990 if ( !outfit_isTurret( temp ) )
1991 xmlr_float( node, "arc",
1992 temp->u.lau.arc ); /* This is in semi-arc like swivel. */
1993
1994 /* Ammo stuff. */
1995 /* Basic */
1996 if ( xml_isNode( node, "duration" ) ) {
1997 char *buf;
1998 xmlr_attr_strd( node, "blowup", buf );
1999 if ( buf != NULL ) {
2000 if ( strcmp( buf, "armour" ) == 0 )
2001 outfit_setProp( temp, OUTFIT_PROP_WEAP_BLOWUP_SHIELD );
2002 else if ( strcmp( buf, "shield" ) == 0 )
2003 outfit_setProp( temp, OUTFIT_PROP_WEAP_BLOWUP_ARMOUR );
2004 else
2005 WARN( _( "Outfit '%s' has invalid blowup property: '%s'" ),
2006 temp->name, buf );
2007 free( buf );
2008 }
2009 temp->u.lau.duration = xml_getFloat( node );
2010 continue;
2011 }
2012 xmlr_float( node, "resist", temp->u.lau.resist );
2013 /* Movement */
2014 xmlr_float( node, "accel", temp->u.lau.accel );
2015 xmlr_float( node, "turn", temp->u.lau.turn );
2016 xmlr_float( node, "speed", temp->u.lau.speed );
2017 xmlr_float( node, "speed_max", temp->u.lau.speed_max );
2018 xmlr_float( node, "energy", temp->u.lau.energy );
2019 xmlr_float( node, "ammo_mass", temp->u.lau.ammo_mass );
2020 if ( xml_isNode( node, "gfx" ) ) {
2021 outfit_loadGFX( temp, node );
2022 continue;
2023 }
2024 if ( xml_isNode( node, "spfx_armour" ) ) {
2025 temp->u.lau.spfx_armour = spfx_get( xml_get( node ) );
2026 continue;
2027 }
2028 if ( xml_isNode( node, "spfx_shield" ) ) {
2029 temp->u.lau.spfx_shield = spfx_get( xml_get( node ) );
2030 continue;
2031 }
2032 if ( xml_isNode( node, "sound" ) ) {
2033 temp->u.lau.sound = sound_get( xml_get( node ) );
2034 continue;
2035 }
2036 if ( xml_isNode( node, "sound_hit" ) ) {
2037 temp->u.lau.sound_hit = sound_get( xml_get( node ) );
2038 continue;
2039 }
2040 if ( xml_isNode( node, "damage" ) ) {
2041 outfit_parseDamage( &temp->u.lau.dmg, node );
2042 continue;
2043 }
2044 if ( xml_isNode( node, "trail_generator" ) ) {
2045 xmlr_attr_float( node, "x", temp->u.lau.trail_x_offset );
2046 char *buf = xml_get( node );
2047 if ( buf == NULL )
2048 buf = "default";
2049 temp->u.lau.trail_spec = trailSpec_get( buf );
2050 continue;
2051 }
2052 if ( xml_isNode( node, "ai" ) ) {
2053 char *buf = xml_get( node );
2054 if ( buf != NULL ) {
2055 if ( strcmp( buf, "unguided" ) == 0 )
2056 temp->u.lau.ai = AMMO_AI_UNGUIDED;
2057 else if ( strcmp( buf, "seek" ) == 0 )
2058 temp->u.lau.ai = AMMO_AI_SEEK;
2059 else if ( strcmp( buf, "smart" ) == 0 )
2060 temp->u.lau.ai = AMMO_AI_SMART;
2061 else
2062 WARN( _( "Ammo '%s' has unknown ai type '%s'." ), temp->name,
2063 buf );
2064 }
2065 continue;
2066 }
2067
2068 /* Stats. */
2069 ll = ss_listFromXML( node );
2070 if ( ll != NULL ) {
2071 ll->next = temp->stats;
2072 temp->stats = ll;
2073 continue;
2074 }
2075 WARN( _( "Outfit '%s' has unknown node '%s'" ), temp->name, node->name );
2076 } while ( xml_nextNode( node ) );
2077
2078 /* Post processing. */
2079 if ( !outfit_isProp( temp, OUTFIT_PROP_TEMPLATE ) ) {
2080 temp->mass -= temp->u.lau.ammo_mass * temp->u.lau.amount;
2081 if ( temp->mass < 0. )
2082 WARN( _( "Launcher outfit '%s' has negative mass when subtracting "
2083 "ammo mass!" ),
2084 temp->name );
2085 }
2086 temp->u.lau.dmg_absorb /= 100.;
2087 temp->u.lau.swivel *= M_PI / 180.;
2088 temp->u.lau.arc *= M_PI / 180.;
2089 /* Note that arc will be 0. for turrets. */
2090 if ( outfit_isTurret( temp ) )
2091 temp->u.lau.swivel = M_PI;
2092 temp->u.lau.dispersion *= M_PI / 180.;
2093 temp->u.lau.turn *= M_PI / 180.; /* Convert to rad/s. */
2094 if ( temp->u.lau.speed_max < 0. )
2095 temp->u.lau.speed_max = temp->u.lau.speed;
2096 else if ( temp->u.lau.speed > 0. &&
2097 temp->u.lau.accel >
2098 0. ) /* Condition for not taking max_speed into account. */
2099 WARN( _( "Max speed of ammo '%s' will be ignored." ), temp->name );
2100 temp->u.lau.resist /= 100.;
2101
2102 /* Short description. */
2103 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
2104 l = 0;
2105 SDESC_ADD( l, temp, "%s [%s]", _( outfit_getType( temp ) ),
2106 _( dtype_damageTypeToStr( temp->u.lau.dmg.type ) ) );
2107 dtype_raw( temp->u.lau.dmg.type, &dshield, &darmour, &dknockback );
2108 l = os_printD( temp->summary_raw, l, darmour * 100., &darmour_opts );
2109 l = os_printD( temp->summary_raw, l, dshield * 100., &dshield_opts );
2110 l = os_printD( temp->summary_raw, l, dknockback * 100., &dknockback_opts );
2111 l = os_printD( temp->summary_raw, l, temp->cpu, &cpu_opts );
2112 l = os_printD( temp->summary_raw, l,
2113 temp->mass + temp->u.lau.ammo_mass * temp->u.lau.amount,
2114 &mass_opts ); /* Include ammo. */
2115 /* Higher level stats. */
2116 l = os_printD_rate( temp->summary_raw, l, temp->u.lau.dmg.damage,
2117 &damage_opts, temp->u.lau.shots,
2118 temp->u.lau.dmg.damage * (double)temp->u.lau.shots /
2119 temp->u.lau.delay,
2120 &dps_opts );
2121 l = os_printD_rate( temp->summary_raw, l, temp->u.lau.dmg.disable,
2122 &disable_opts, temp->u.lau.shots,
2123 temp->u.lau.dmg.disable * (double)temp->u.lau.shots /
2124 temp->u.lau.delay,
2125 &disable_rate_opts );
2126 l = os_printD_rate( temp->summary_raw, l, temp->u.lau.energy, &energy_opts,
2127 1, temp->u.lau.delay * temp->u.lau.energy, &power_opts );
2128 /* Standard stats. */
2129 l = os_printD( temp->summary_raw, l, temp->u.lau.dmg.penetration * 100.,
2130 &penetration_opts );
2131 if ( outfit_isSeeker( temp ) ) {
2132 l = os_printD( temp->summary_raw, l, temp->u.lau.lockon, &lockon_opts );
2133 l = os_printD( temp->summary_raw, l, temp->u.lau.iflockon,
2134 &inflight_calib_opts );
2135 l = os_printD_range( temp->summary_raw, l, temp->u.lau.trackmin,
2136 temp->u.lau.trackmax, &tracking_opts );
2137 } else {
2138 SDESC_ADD( l, temp, "\n%s", _( "No Seeking" ) );
2139 if ( outfit_isTurret( temp ) || temp->u.lau.swivel > 0. ) {
2140 l = os_printD_range( temp->summary_raw, l, temp->u.lau.trackmin,
2141 temp->u.lau.trackmax, &tracking_opts );
2142 l = os_printD( temp->summary_raw, l, temp->u.lau.swivel * 180. / M_PI,
2143 &swivel_opts );
2144 }
2145 }
2146
2147 SDESC_ADD( l, temp, _( "\n Holds %d ammo" ), temp->u.lau.amount );
2148 if ( temp->u.lau.radius > 0. ) {
2149 char radius[STRMAX_SHORT];
2150 snprintf( radius, sizeof( radius ),
2151 outfit_isProp( temp, OUTFIT_PROP_WEAP_FRIENDLYFIRE )
2152 ? p_( "friendlyfire", "#r!! %s !!#0" )
2153 : "%s",
2154 _( "Hit radius" ) );
2155 t_os_stat radius_opts = {
2156 .name = radius,
2157 .unit = _UNIT_DISTANCE,
2158 .colour = 0,
2159 .colour_threshold = 0,
2160 .hide_zero = 1,
2161 .precision = 0,
2162 };
2163 l = os_printD( temp->summary_raw, l, temp->u.lau.radius, &radius_opts );
2164 }
2165 l = os_printD( temp->summary_raw, l, 1. / temp->u.lau.delay,
2166 &fire_rate_opts );
2167 l = os_printD( temp->summary_raw, l, outfit_range( temp ), &range_opts );
2168 // l = os_printD( temp->summary_raw, l, temp->u.lau.duration, &duration_opts
2169 // );
2170
2171 if ( temp->u.lau.accel > 0. ) {
2172 if ( temp->u.lau.speed > 0. )
2173 l = os_printD( temp->summary_raw, l, temp->u.lau.speed,
2174 &initial_speed_opts );
2175 l = os_printD( temp->summary_raw, l, temp->u.lau.accel, &accel_opts );
2176 } else
2177 l = os_printD( temp->summary_raw, l, temp->u.lau.speed,
2178 &initial_speed_opts );
2179 if ( !( temp->u.lau.accel > 0. && temp->u.lau.speed > 0. ) )
2180 l = os_printD( temp->summary_raw, l, temp->u.lau.speed_max,
2181 &max_speed_opts );
2182 l = os_printD( temp->summary_raw, l, temp->u.lau.reload_time, &reload_opts );
2183 l = os_printD( temp->summary_raw, l, temp->u.lau.armour, &armour_opts );
2184 l = os_printD( temp->summary_raw, l, temp->u.lau.dmg_absorb * 100.,
2185 &absorp_opts );
2186 l = os_printD( temp->summary_raw, l, temp->u.lau.resist * 100.,
2187 &jam_res_opts );
2188 sdesc_miningRarity( &l, temp, temp->u.lau.mining_rarity );
2189
2190#define MELEMENT( o, s ) \
2191 if ( o ) \
2192 WARN( _( "Outfit '%s' missing '%s' element" ), temp->name, \
2193 s )
2194 MELEMENT( temp->u.lau.delay == 0., "delay" );
2195 // MELEMENT(temp->cpu==0.,"cpu");
2196 MELEMENT( temp->u.lau.amount == 0., "amount" );
2197 MELEMENT( temp->u.lau.reload_time == 0., "reload_time" );
2198 MELEMENT( temp->u.lau.ammo_mass == 0., "mass" );
2199 // MELEMENT(!outfit_isProp(temp,OUTFIT_PROP_SHOOT_DRY)&&temp->u.lau.gfx_space==NULL,"gfx");
2200 MELEMENT( !outfit_isProp( temp, OUTFIT_PROP_SHOOT_DRY ) &&
2201 temp->u.lau.spfx_shield == -1,
2202 "spfx_shield" );
2203 MELEMENT( !outfit_isProp( temp, OUTFIT_PROP_SHOOT_DRY ) &&
2204 temp->u.lau.spfx_armour == -1,
2205 "spfx_armour" );
2206 MELEMENT( ( sound_disabled != 0 ) && ( temp->u.lau.sound < 0 ), "sound" );
2207 /* MELEMENT(temp->u.lau.accel==0,"accel"); */
2208 /* Unguided missiles don't need everything */
2209 if ( outfit_isSeeker( temp ) ) {
2210 MELEMENT( temp->u.lau.turn == 0, "turn" );
2211 MELEMENT( temp->u.lau.trackmin < 0, "trackmin" );
2212 MELEMENT( temp->u.lau.trackmax < 0, "trackmax" );
2213 MELEMENT( temp->u.lau.lockon < 0, "lockon" );
2214 MELEMENT( !outfit_isTurret( temp ) && ( temp->u.lau.arc == 0. ), "arc" );
2215 }
2216 MELEMENT( !outfit_isProp( temp, OUTFIT_PROP_SHOOT_DRY ) &&
2217 temp->u.lau.speed_max == 0,
2218 "speed_max" );
2219 MELEMENT( !outfit_isProp( temp, OUTFIT_PROP_SHOOT_DRY ) &&
2220 temp->u.lau.duration == 0,
2221 "duration" );
2222 MELEMENT( temp->u.lau.dmg.damage == 0, "damage" );
2223 /*MELEMENT(temp->u.lau.energy==0.,"energy");*/
2224#undef MELEMENT
2225 if ( !outfit_isProp( temp, OUTFIT_PROP_SHOOT_DRY ) &&
2226 temp->u.lau.speed == 0. && temp->u.lau.accel == 0. )
2227 WARN( _( "Outfit '%s' has no speed nor accel set!" ), temp->name );
2228 if ( !outfit_isProp( temp, OUTFIT_PROP_SHOOT_DRY ) &&
2229 temp->u.lau.iflockon >= temp->u.lau.duration )
2230 WARN( _( "Outfit '%s' has longer 'iflockon' than ammo 'duration'" ),
2231 temp->name );
2232}
2233
2240static void outfit_parseSMod( Outfit *temp, const xmlNodePtr parent )
2241{
2242 xmlNodePtr node = parent->children;
2243
2244 do { /* load all the data */
2245 ShipStatList *ll;
2246
2247 xml_onlyNodes( node );
2248 xmlr_strd( node, "lua", temp->lua_file );
2249
2250 if ( xml_isNode( node, "active" ) ) {
2251 xmlr_attr_float( node, "cooldown", temp->u.mod.cooldown );
2252 temp->u.mod.active = 1;
2253 temp->u.mod.duration = xml_getFloat( node );
2254
2255 /* Infinity if no duration specified. */
2256 if ( temp->u.mod.duration == 0 )
2257 temp->u.mod.duration = INFINITY;
2258
2259 continue;
2260 }
2261
2262 /* Stats. */
2263 ll = ss_listFromXML( node );
2264 if ( ll != NULL ) {
2265 ll->next = temp->stats;
2266 temp->stats = ll;
2267 continue;
2268 }
2269
2270 WARN( _( "Outfit '%s' has unknown node '%s'" ), temp->name, node->name );
2271 } while ( xml_nextNode( node ) );
2272
2273 /* Set short description. */
2274 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
2275 /* Modifier outfits are actually done in the third pass... */
2276}
2277
2284static void outfit_parseSAfterburner( Outfit *temp, const xmlNodePtr parent )
2285{
2286 double C, area;
2287 size_t l;
2288 xmlNodePtr node = parent->children;
2289
2290 /* Defaults. */
2291 temp->u.afb.sound = -1;
2292 temp->u.afb.sound_on = -1;
2293 temp->u.afb.sound_off = -1;
2294
2295 /* must be >= 1. */
2296 temp->u.afb.accel = 1.;
2297 temp->u.afb.speed = 1.;
2298
2299 do { /* parse the data */
2300 ShipStatList *ll;
2301
2302 xml_onlyNodes( node );
2303 xmlr_float( node, "rumble", temp->u.afb.rumble );
2304 xmlr_strd( node, "lua", temp->lua_file );
2305 if ( xml_isNode( node, "sound_on" ) ) {
2306 temp->u.afb.sound_on = sound_get( xml_get( node ) );
2307 continue;
2308 }
2309 if ( xml_isNode( node, "sound" ) ) {
2310 temp->u.afb.sound = sound_get( xml_get( node ) );
2311 continue;
2312 }
2313 if ( xml_isNode( node, "sound_off" ) ) {
2314 temp->u.afb.sound_off = sound_get( xml_get( node ) );
2315 continue;
2316 }
2317 xmlr_float( node, "accel", temp->u.afb.accel );
2318 xmlr_float( node, "speed", temp->u.afb.speed );
2319 xmlr_float( node, "energy", temp->u.afb.energy );
2320 xmlr_float( node, "mass_limit", temp->u.afb.mass_limit );
2321 xmlr_float( node, "heatup", temp->u.afb.heatup );
2322 xmlr_float( node, "heat_cap", temp->overheat_max );
2323 xmlr_float( node, "heat_base", temp->overheat_min );
2324
2325 /* Stats. */
2326 ll = ss_listFromXML( node );
2327 if ( ll != NULL ) {
2328 ll->next = temp->stats;
2329 temp->stats = ll;
2330 continue;
2331 }
2332 WARN( _( "Outfit '%s' has unknown node '%s'" ), temp->name, node->name );
2333 } while ( xml_nextNode( node ) );
2334
2335 /* Set short description. */
2336 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
2337 l = 0;
2338 SDESC_ADD( l, temp, "%s", _( outfit_getType( temp ) ) );
2339 SDESC_ADD( l, temp, "\n#o%s#0", _( "Activated Outfit" ) );
2340
2341 l = os_printD( temp->summary_raw, l, temp->cpu, &cpu_opts );
2342 l = os_printD( temp->summary_raw, l, temp->mass, &mass_opts );
2343 l =
2344 os_printD( temp->summary_raw, l, temp->u.afb.mass_limit, &max_mass_opts );
2345 SDESC_ADD( l, temp, "\n%s", _( "Only one can be equipped" ) );
2346 l = os_printD( temp->summary_raw, l, temp->u.afb.accel + 100., &accel_opts );
2347 l = os_printD( temp->summary_raw, l, temp->u.afb.speed + 100.,
2348 &max_speed_opts );
2349 l = os_printD( temp->summary_raw, l, temp->u.afb.energy, &power_opts );
2350 /*l =*/os_printD( temp->summary_raw, l, temp->u.afb.rumble, &rumble_opts );
2351
2352 /* Post processing. */
2353 temp->u.afb.accel /= 100.;
2354 temp->u.afb.speed /= 100.;
2355 C = pilot_heatCalcOutfitC( temp );
2356 area = pilot_heatCalcOutfitArea( temp );
2357 temp->u.afb.heat = ( ( 800. - CONST_SPACE_STAR_TEMP ) * C +
2358 STEEL_HEAT_CONDUCTIVITY *
2359 ( ( 800. - CONST_SPACE_STAR_TEMP ) * area ) ) /
2360 temp->u.afb.heatup;
2361
2362#define MELEMENT( o, s ) \
2363 if ( o ) \
2364 WARN( _( "Outfit '%s' missing/invalid '%s' element" ), temp->name, \
2365 s )
2366 MELEMENT( temp->u.afb.accel == 0., "accel" );
2367 MELEMENT( temp->u.afb.speed == 0., "speed" );
2368 MELEMENT( temp->u.afb.energy == 0., "energy" );
2369 // MELEMENT(temp->cpu==0.,"cpu");
2370 MELEMENT( temp->u.afb.mass_limit == 0., "mass_limit" );
2371 MELEMENT( temp->u.afb.heatup == 0., "heatup" );
2372#undef MELEMENT
2373}
2374
2381static void outfit_parseSFighterBay( Outfit *temp, const xmlNodePtr parent )
2382{
2383 size_t l;
2384 xmlNodePtr node = parent->children;
2385
2386 do {
2387 ShipStatList *ll;
2388
2389 xml_onlyNodes( node );
2390 xmlr_float( node, "delay", temp->u.bay.delay );
2391 xmlr_float( node, "reload_time", temp->u.bay.reload_time );
2392 xmlr_strd( node, "ship", temp->u.bay.shipname );
2393 xmlr_float( node, "ship_mass", temp->u.bay.ship_mass );
2394 xmlr_int( node, "amount", temp->u.bay.amount );
2395 xmlr_strd( node, "lua", temp->lua_file );
2396
2397 /* Stats. */
2398 ll = ss_listFromXML( node );
2399 if ( ll != NULL ) {
2400 ll->next = temp->stats;
2401 temp->stats = ll;
2402 continue;
2403 }
2404 WARN( _( "Outfit '%s' has unknown node '%s'" ), temp->name, node->name );
2405 } while ( xml_nextNode( node ) );
2406
2407 /* Post-processing. */
2408 temp->mass -= temp->u.bay.ship_mass * temp->u.bay.amount;
2409 if ( temp->mass < 0. )
2410 WARN( _( "Fighter bay outfit '%s' has negative mass when subtracting "
2411 "ship mass!" ),
2412 temp->name );
2413
2414 /* Set short description. */
2415 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
2416 l = 0;
2417 SDESC_ADD( l, temp, "%s", _( outfit_getType( temp ) ) );
2418 l = os_printD( temp->summary_raw, l, temp->cpu, &cpu_opts );
2419 l = os_printD( temp->summary_raw, l,
2420 temp->mass + temp->u.bay.ship_mass * temp->u.bay.amount,
2421 &mass_opts );
2422 SDESC_ADD( l, temp, _( "\n Holds %d ships" ), temp->u.bay.amount );
2423 l = os_printD( temp->summary_raw, l, temp->u.bay.delay, &shots_delay_opts );
2424 /*l =*/os_printD( temp->summary_raw, l, temp->u.bay.reload_time,
2425 &reload_opts );
2426
2427#define MELEMENT( o, s ) \
2428 if ( o ) \
2429 WARN( _( "Outfit '%s' missing/invalid '%s' element" ), temp->name, \
2430 s )
2431 MELEMENT( temp->u.bay.shipname == NULL, "ship" );
2432 MELEMENT( temp->u.bay.ship_mass <= 0., "ship_mass" );
2433 MELEMENT( temp->u.bay.delay == 0, "delay" );
2434 MELEMENT( temp->u.bay.reload_time == 0., "reload_time" );
2435 MELEMENT( temp->cpu == 0., "cpu" );
2436 MELEMENT( temp->u.bay.amount == 0, "amount" );
2437#undef MELEMENT
2438}
2439
2446static void outfit_parseSMap( Outfit *temp, const xmlNodePtr parent )
2447{
2448 xmlNodePtr node;
2449 char *buf;
2450
2451 node = parent->children;
2452
2453 temp->slot.type = OUTFIT_SLOT_NA;
2454 temp->slot.size = OUTFIT_SLOT_SIZE_NA;
2455
2456 temp->u.map->systems = array_create( StarSystem * );
2457 temp->u.map->spobs = array_create( Spob *);
2458 temp->u.map->jumps = array_create( JumpPoint *);
2459
2460 do {
2461 xml_onlyNodes( node );
2462
2463 if ( xml_isNode( node, "sys" ) ) {
2464 xmlr_attr_strd( node, "name", buf );
2465 StarSystem *sys = system_get( buf );
2466 if ( sys == NULL ) {
2467 WARN( _( "Map '%s' has invalid system '%s'" ), temp->name, buf );
2468 free( buf );
2469 continue;
2470 }
2471
2472 free( buf );
2473 array_push_back( &temp->u.map->systems, sys );
2474
2475 xmlNodePtr cur = node->children;
2476 do {
2477 xml_onlyNodes( cur );
2478
2479 if ( xml_isNode( cur, "spob" ) ) {
2480 buf = xml_get( cur );
2481 Spob *spob = ( buf != NULL ) ? spob_get( buf ) : NULL;
2482 if ( ( buf != NULL ) && ( spob != NULL ) )
2483 array_push_back( &temp->u.map->spobs, spob );
2484 else
2485 WARN( _( "Map '%s' has invalid spob '%s'" ), temp->name,
2486 buf );
2487 } else if ( xml_isNode( cur, "jump" ) ) {
2488 JumpPoint *jump;
2489 buf = xml_get( cur );
2490 if ( ( buf != NULL ) &&
2491 ( ( jump = jump_get(
2492 xml_get( cur ),
2493 temp->u.map
2494 ->systems[array_size( temp->u.map->systems ) -
2495 1] ) ) != NULL ) )
2496 array_push_back( &temp->u.map->jumps, jump );
2497 else
2498 WARN( _( "Map '%s' has invalid jump point '%s'" ), temp->name,
2499 buf );
2500 } else
2501 WARN( _( "Outfit '%s' has unknown node '%s'" ), temp->name,
2502 cur->name );
2503 } while ( xml_nextNode( cur ) );
2504 } else if ( xml_isNode( node, "short_desc" ) ) {
2505 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
2506 snprintf( temp->summary_raw, OUTFIT_SHORTDESC_MAX, "%s",
2507 xml_get( node ) );
2508 } else if ( xml_isNode( node, "all" ) ) { /* Add everything to the map */
2509 StarSystem *system_stack = system_getAll();
2510 for ( int i = 0; i < array_size( system_stack ); i++ ) {
2511 StarSystem *ss = &system_stack[i];
2512 array_push_back( &temp->u.map->systems, ss );
2513 for ( int j = 0; j < array_size( system_stack[i].spobs ); j++ )
2514 array_push_back( &temp->u.map->spobs, ss->spobs[j] );
2515 for ( int j = 0; j < array_size( system_stack[i].jumps ); j++ )
2516 array_push_back( &temp->u.map->jumps, &ss->jumps[j] );
2517 }
2518 } else
2519 WARN( _( "Outfit '%s' has unknown node '%s'" ), temp->name,
2520 node->name );
2521 } while ( xml_nextNode( node ) );
2522
2523 array_shrink( &temp->u.map->systems );
2524 array_shrink( &temp->u.map->spobs );
2525 array_shrink( &temp->u.map->jumps );
2526
2527 if ( temp->summary_raw == NULL ) {
2528 /* Set short description based on type. */
2529 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
2530 snprintf( temp->summary_raw, OUTFIT_SHORTDESC_MAX, "%s",
2531 _( outfit_getType( temp ) ) );
2532 }
2533
2534#define MELEMENT( o, s ) \
2535 if ( o ) \
2536 WARN( _( "Outfit '%s' missing/invalid '%s' element" ), temp->name, s )
2538 MELEMENT( temp->mass != 0., "cpu" );
2539 MELEMENT( temp->cpu != 0., "cpu" );
2540#undef MELEMENT
2541}
2542
2549static void outfit_parseSLocalMap( Outfit *temp, const xmlNodePtr parent )
2550{
2551 xmlNodePtr node = parent->children;
2552
2553 temp->slot.type = OUTFIT_SLOT_NA;
2554 temp->slot.size = OUTFIT_SLOT_SIZE_NA;
2555
2556 do {
2557 xml_onlyNodes( node );
2558 xmlr_float( node, "spob_detect", temp->u.lmap.spob_detect );
2559 xmlr_float( node, "jump_detect", temp->u.lmap.jump_detect );
2560 // cppcheck-suppress nullPointerRedundantCheck
2561 WARN( _( "Outfit '%s' has unknown node '%s'" ), temp->name, node->name );
2562 } while ( xml_nextNode( node ) );
2563
2564 temp->u.lmap.spob_detect = pow2( temp->u.lmap.spob_detect );
2565 temp->u.lmap.jump_detect = pow2( temp->u.lmap.jump_detect );
2566
2567 /* Set short description. */
2568 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
2569 snprintf( temp->summary_raw, OUTFIT_SHORTDESC_MAX, "%s",
2570 _( outfit_getType( temp ) ) );
2571
2572#define MELEMENT( o, s ) \
2573 if ( o ) \
2574 WARN( _( "Outfit '%s' missing/invalid '%s' element" ), temp->name, s )
2576 MELEMENT( temp->mass != 0., "cpu" );
2577 MELEMENT( temp->cpu != 0., "cpu" );
2578#undef MELEMENT
2579}
2580
2587static void outfit_parseSGUI( Outfit *temp, const xmlNodePtr parent )
2588{
2589 xmlNodePtr node = parent->children;
2590
2591 temp->slot.type = OUTFIT_SLOT_NA;
2592 temp->slot.size = OUTFIT_SLOT_SIZE_NA;
2593
2594 do {
2595 xml_onlyNodes( node );
2596 xmlr_strd( node, "gui", temp->u.gui.gui );
2597 WARN( _( "Outfit '%s' has unknown node '%s'" ), temp->name, node->name );
2598 } while ( xml_nextNode( node ) );
2599
2600 /* Set short description. */
2601 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
2602 snprintf( temp->summary_raw, OUTFIT_SHORTDESC_MAX,
2603 _( "GUI (Graphical User Interface)" ) );
2604
2605#define MELEMENT( o, s ) \
2606 if ( o ) \
2607 WARN( _( "Outfit '%s' missing/invalid '%s' element" ), temp->name, s )
2609 MELEMENT( temp->u.gui.gui == NULL, "gui" );
2610 MELEMENT( temp->mass != 0., "cpu" );
2611 MELEMENT( temp->cpu != 0., "cpu" );
2612#undef MELEMENT
2613}
2614
2621static void outfit_parseSLicense( Outfit *temp, const xmlNodePtr parent )
2622{
2623 xmlNodePtr node = parent->children;
2624
2625 temp->slot.type = OUTFIT_SLOT_NA;
2626 temp->slot.size = OUTFIT_SLOT_SIZE_NA;
2627
2628 do {
2629 xml_onlyNodes( node );
2630 xmlr_strd( node, "provides", temp->u.lic.provides );
2631 WARN( _( "Outfit '%s' has unknown node '%s'" ), temp->name, node->name );
2632 } while ( xml_nextNode( node ) );
2633
2634 if ( temp->u.lic.provides == NULL )
2635 temp->u.lic.provides = strdup( temp->name );
2636
2637 /* Set short description. */
2638 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
2639 snprintf( temp->summary_raw, OUTFIT_SHORTDESC_MAX, "%s",
2640 _( outfit_getType( temp ) ) );
2641
2642#define MELEMENT( o, s ) \
2643 if ( o ) \
2644 WARN( _( "Outfit '%s' missing/invalid '%s' element" ), temp->name, s )
2646 MELEMENT( temp->mass != 0., "cpu" );
2647 MELEMENT( temp->cpu != 0., "cpu" );
2648#undef MELEMENT
2649}
2650
2658static int outfit_parse( Outfit *temp, const char *file )
2659{
2660 xmlNodePtr node, parent;
2661 char *prop;
2662 const char *cprop;
2663 int group;
2664
2665 xmlDocPtr doc = xml_parsePhysFS( file );
2666 if ( doc == NULL )
2667 return -1;
2668
2669 parent = doc->xmlChildrenNode; /* first outfit node */
2670 if ( parent == NULL ) {
2671 WARN( _( "Malformed '%s' file: does not contain elements" ), file );
2672 return -1;
2673 }
2674
2675 /* Clear data. */
2676 memset( temp, 0, sizeof( Outfit ) );
2677 temp->filename = strdup( file );
2678
2679 /* Defaults. */
2680 temp->overheat_min = 350.;
2681 temp->overheat_max = 500.;
2682 temp->lua_env = LUA_NOREF;
2683 temp->lua_descextra = LUA_NOREF;
2684 temp->lua_onadd = LUA_NOREF;
2685 temp->lua_onremove = LUA_NOREF;
2686 temp->lua_init = LUA_NOREF;
2687 temp->lua_cleanup = LUA_NOREF;
2688 temp->lua_update = LUA_NOREF;
2689 temp->lua_ontoggle = LUA_NOREF;
2690 temp->lua_onshoot = LUA_NOREF;
2691 temp->lua_onhit = LUA_NOREF;
2692 temp->lua_outofenergy = LUA_NOREF;
2693 temp->lua_onshootany = LUA_NOREF;
2694 temp->lua_onstealth = LUA_NOREF;
2695 temp->lua_onscanned = LUA_NOREF;
2696 temp->lua_onscan = LUA_NOREF;
2697 temp->lua_cooldown = LUA_NOREF;
2698 temp->lua_land = LUA_NOREF;
2699 temp->lua_takeoff = LUA_NOREF;
2700 temp->lua_jumpin = LUA_NOREF;
2701 temp->lua_board = LUA_NOREF;
2702 temp->lua_keydoubletap = LUA_NOREF;
2703 temp->lua_keyrelease = LUA_NOREF;
2704 temp->lua_onimpact = LUA_NOREF;
2705 temp->lua_onmiss = LUA_NOREF;
2706 temp->lua_price = LUA_NOREF;
2707 temp->lua_buy = LUA_NOREF;
2708 temp->lua_sell = LUA_NOREF;
2709
2710 xmlr_attr_strd( parent, "name", temp->name );
2711 if ( temp->name == NULL )
2712 WARN( _( "Outfit '%s' has invalid or no name" ), file );
2713
2714 node = parent->xmlChildrenNode;
2715
2716 do { /* load all the data */
2717
2718 /* Only handle nodes. */
2719 xml_onlyNodes( node );
2720
2721 if ( xml_isNode( node, "general" ) ) {
2722 xmlNodePtr cur = node->children;
2723 do {
2724 xml_onlyNodes( cur );
2725 xmlr_int( cur, "rarity", temp->rarity );
2726 xmlr_strd( cur, "shortname", temp->shortname );
2727 xmlr_strd( cur, "license", temp->license );
2728 xmlr_strd( cur, "cond", temp->cond );
2729 xmlr_strd( cur, "condstr", temp->condstr );
2730 xmlr_float( cur, "mass", temp->mass );
2731 xmlr_float( cur, "cpu", temp->cpu );
2732 xmlr_long( cur, "price", temp->price );
2733 xmlr_strd( cur, "limit", temp->limit );
2734 xmlr_strd( cur, "description", temp->desc_raw );
2735 xmlr_strd( cur, "desc_extra", temp->desc_extra );
2736 xmlr_strd( cur, "typename", temp->typename );
2737 xmlr_int( cur, "priority", temp->priority );
2738 xmlr_float( cur, "overheat_min", temp->overheat_min );
2739 xmlr_float( cur, "overheat_max", temp->overheat_max );
2740 if ( xml_isNode( cur, "unique" ) ) {
2741 outfit_setProp( temp, OUTFIT_PROP_UNIQUE );
2742 continue;
2743 } else if ( xml_isNode( cur, "stealth_on" ) ) {
2744 outfit_setProp( temp, OUTFIT_PROP_STEALTH_ON );
2745 continue;
2746 } else if ( xml_isNode( cur, "shoot_dry" ) ) {
2747 outfit_setProp( temp, OUTFIT_PROP_SHOOT_DRY );
2748 continue;
2749 } else if ( xml_isNode( cur, "template" ) ) {
2750 outfit_setProp( temp, OUTFIT_PROP_TEMPLATE );
2751 continue;
2752 } else if ( xml_isNode( cur, "gfx_store" ) ) {
2753 const char *str = xml_get( cur );
2754 if ( str != NULL )
2755 temp->gfx_store_path = strdup( str );
2756 else
2757 WARN( _( "Outfit '%s' has NULL tag '%s'!" ), temp->name,
2758 "gfx_store" );
2759 continue;
2760 } else if ( xml_isNode( cur, "gfx_overlays" ) ) {
2761 xmlNodePtr ccur = cur->children;
2763 do {
2764 xml_onlyNodes( ccur );
2765 if ( xml_isNode( ccur, "gfx_overlay" ) )
2767 &temp->gfx_overlays,
2768 xml_parseTexture( ccur, OVERLAY_GFX_PATH "%s", 1, 1,
2769 OPENGL_TEX_MIPMAPS ) );
2770 } while ( xml_nextNode( ccur ) );
2771 continue;
2772 } else if ( xml_isNode( cur, "slot" ) ) {
2773 cprop = xml_get( cur );
2774 if ( cprop == NULL )
2775 WARN( _( "Outfit '%s' has an slot type invalid." ),
2776 temp->name );
2777 else if ( strcmp( cprop, "structure" ) == 0 )
2778 temp->slot.type = OUTFIT_SLOT_STRUCTURE;
2779 else if ( strcmp( cprop, "utility" ) == 0 )
2780 temp->slot.type = OUTFIT_SLOT_UTILITY;
2781 else if ( strcmp( cprop, "weapon" ) == 0 )
2782 temp->slot.type = OUTFIT_SLOT_WEAPON;
2783 else if ( strcmp( cprop, "intrinsic" ) == 0 )
2784 temp->slot.type = OUTFIT_SLOT_INTRINSIC;
2785 else if ( strcmp( cprop, "none" ) == 0 )
2786 temp->slot.type = OUTFIT_SLOT_NA;
2787 else
2788 WARN( _( "Outfit '%s' has unknown slot type '%s'." ),
2789 temp->name, cprop );
2790
2791 /* Property. */
2792 xmlr_attr_strd( cur, "prop", prop );
2793 if ( prop != NULL )
2794 temp->slot.spid = sp_get( prop );
2795 free( prop );
2796 continue;
2797 } else if ( xml_isNode( cur, "size" ) ) {
2798 temp->slot.size = outfit_toSlotSize( xml_get( cur ) );
2799 continue;
2800 } else if ( xml_isNode( cur, "illegalto" ) ) {
2801 xmlNodePtr ccur = cur->xmlChildrenNode;
2802 temp->illegaltoS = array_create( char * );
2803 do {
2804 xml_onlyNodes( ccur );
2805 if ( xml_isNode( ccur, "faction" ) ) {
2806 const char *s = xml_get( ccur );
2807 if ( s == NULL )
2808 WARN( _( "Empty faction string for outfit '%s' "
2809 "legality!" ),
2810 temp->name );
2811 else
2812 array_push_back( &temp->illegaltoS, strdup( s ) );
2813 }
2814 } while ( xml_nextNode( ccur ) );
2815 if ( array_size( temp->illegaltoS ) <= 0 )
2816 WARN( _( "Outfit '%s' has no factions defined in <illegalto> "
2817 "block!" ),
2818 temp->name );
2819 continue;
2820 }
2821 // cppcheck-suppress nullPointerRedundantCheck
2822 WARN( _( "Outfit '%s' has unknown general node '%s'" ), temp->name,
2823 cur->name );
2824 } while ( xml_nextNode( cur ) );
2825 continue;
2826 }
2827
2828 if ( xml_isNode( node, "stats" ) ) {
2829 xmlNodePtr cur = node->children;
2830 do {
2831 ShipStatList *ll;
2832 xml_onlyNodes( cur );
2833 /* Stats. */
2834 ll = ss_listFromXML( cur );
2835 if ( ll != NULL ) {
2836 ll->next = temp->stats;
2837 temp->stats = ll;
2838 continue;
2839 }
2840 WARN( _( "Outfit '%s' has unknown node '%s'" ), temp->name,
2841 cur->name );
2842 } while ( xml_nextNode( cur ) );
2843 continue;
2844 }
2845
2846 /* Parse tags. */
2847 if ( xml_isNode( node, "tags" ) ) {
2848 xmlNodePtr cur = node->children;
2849 temp->tags = array_create( char *);
2850 do {
2851 xml_onlyNodes( cur );
2852 if ( xml_isNode( cur, "tag" ) ) {
2853 const char *tmp = xml_get( cur );
2854 if ( tmp != NULL )
2855 array_push_back( &temp->tags, strdup( tmp ) );
2856 continue;
2857 }
2858 WARN( _( "Outfit '%s' has unknown node in tags '%s'." ), temp->name,
2859 cur->name );
2860 } while ( xml_nextNode( cur ) );
2861 continue;
2862 }
2863
2864 if ( xml_isNode( node,
2865 "specific" ) ) { /* has to be processed separately */
2866 /* get the type */
2867 xmlr_attr_strd( node, "type", prop );
2868 if ( prop == NULL )
2869 WARN( _( "Outfit '%s' element 'specific' missing property 'type'" ),
2870 temp->name );
2871 else
2872 temp->type = outfit_strToOutfitType( prop );
2873 free( prop );
2874
2875 /* is secondary weapon? */
2876 xmlr_attr_strd( node, "secondary", prop );
2877 if ( prop != NULL ) {
2878 if ( (int)atoi( prop ) )
2879 outfit_setProp( temp, OUTFIT_PROP_WEAP_SECONDARY );
2880 free( prop );
2881 }
2882
2883 /* Check for manually-defined group. */
2884 xmlr_attr_int_def( node, "group", group, -1 );
2885 if ( group != -1 ) {
2886 if ( group > PILOT_WEAPON_SETS || group < 1 ) {
2887 WARN( _( "Outfit '%s' has group '%d', should be in the 1-%d "
2888 "range" ),
2889 temp->name, group, PILOT_WEAPON_SETS );
2890 }
2891
2892 temp->group = CLAMP( 0, 9, group );
2893 }
2894
2895 /*
2896 * Parse type.
2897 */
2898 if ( temp->type == OUTFIT_TYPE_NULL )
2899 WARN( _( "Outfit '%s' is of type NONE" ), temp->name );
2900 else if ( outfit_isBolt( temp ) )
2901 outfit_parseSBolt( temp, node );
2902 else if ( outfit_isBeam( temp ) )
2903 outfit_parseSBeam( temp, node );
2904 else if ( outfit_isLauncher( temp ) )
2905 outfit_parseSLauncher( temp, node );
2906 else if ( outfit_isMod( temp ) )
2907 outfit_parseSMod( temp, node );
2908 else if ( outfit_isAfterburner( temp ) )
2909 outfit_parseSAfterburner( temp, node );
2910 else if ( outfit_isFighterBay( temp ) )
2911 outfit_parseSFighterBay( temp, node );
2912 else if ( outfit_isMap( temp ) ) {
2913 temp->u.map =
2914 malloc( sizeof( OutfitMapData_t ) );
2916 temp->slot.type = OUTFIT_SLOT_NA;
2917 temp->slot.size = OUTFIT_SLOT_SIZE_NA;
2918 } else if ( outfit_isLocalMap( temp ) )
2919 outfit_parseSLocalMap( temp, node );
2920 else if ( outfit_isGUI( temp ) )
2921 outfit_parseSGUI( temp, node );
2922 else if ( outfit_isLicense( temp ) )
2923 outfit_parseSLicense( temp, node );
2924
2925 /* Sort stats. */
2926 ss_sort( &temp->stats );
2927
2928 continue;
2929 }
2930 // cppcheck-suppress nullPointerRedundantCheck
2931 WARN( _( "Outfit '%s' has unknown node '%s'" ), temp->name, node->name );
2932 } while ( xml_nextNode( node ) );
2933
2934#define MELEMENT( o, s ) \
2935 if ( o ) \
2936 WARN( _( "Outfit '%s' missing/invalid '%s' element" ), temp->name, \
2937 s )
2938 MELEMENT( temp->name == NULL, "name" );
2939 if ( !outfit_isProp( temp, OUTFIT_PROP_TEMPLATE ) ) {
2940 MELEMENT( temp->slot.type == OUTFIT_SLOT_NULL, "slot" );
2941 MELEMENT( ( temp->slot.type != OUTFIT_SLOT_NA ) &&
2942 ( temp->slot.type != OUTFIT_SLOT_INTRINSIC ) &&
2943 ( temp->slot.size == OUTFIT_SLOT_SIZE_NA ),
2944 "size" );
2945 MELEMENT( temp->gfx_store_path == NULL, "gfx_store" );
2946 MELEMENT( temp->desc_raw == NULL, "description" );
2947 }
2948 /*MELEMENT(temp->mass==0,"mass"); Not really needed */
2949 MELEMENT( temp->type == 0, "type" );
2950 /*MELEMENT(temp->price==0,"price");*/
2951 MELEMENT( ( temp->cond != NULL ) && ( temp->condstr == NULL ), "condstr" );
2952 MELEMENT( ( temp->cond == NULL ) && ( temp->condstr != NULL ), "cond" );
2953#undef MELEMENT
2954
2955 xmlFreeDoc( doc );
2956
2957 return 0;
2958}
2959
2960static int outfit_parseThread( void *ptr )
2961{
2962 OutfitThreadData *data = ptr;
2963 data->ret = outfit_parse( &data->outfit, data->filename );
2964 /* Render if necessary. */
2966 gl_contextSet();
2968 gl_contextUnset();
2969 }
2970 return data->ret;
2971}
2972
2979static int outfit_loadDir( const char *dir )
2980{
2981 char **outfit_files = ndata_listRecursive( dir );
2982 ThreadQueue *tq = vpool_create();
2984
2985 for ( int i = 0; i < array_size( outfit_files ); i++ ) {
2986 if ( ndata_matchExt( outfit_files[i], "xml" ) ) {
2987 OutfitThreadData *od = &array_grow( &odata );
2988 od->filename = outfit_files[i];
2989 } else
2990 free( outfit_files[i] );
2991 }
2992 array_free( outfit_files );
2993
2994 /* Enqueue the jobs after the data array is done. */
2995 SDL_GL_MakeCurrent( gl_screen.window, NULL );
2996 for ( int i = 0; i < array_size( odata ); i++ )
2997 vpool_enqueue( tq, outfit_parseThread, &odata[i] );
2998 /* Wait until done processing. */
2999 vpool_wait( tq );
3000 vpool_cleanup( tq );
3001 SDL_GL_MakeCurrent( gl_screen.window, gl_screen.context );
3002
3003 /* Properly load the data. */
3004 license_stack = array_create( char * );
3005 for ( int i = 0; i < array_size( odata ); i++ ) {
3006 OutfitThreadData *od = &odata[i];
3007 if ( !od->ret )
3008 array_push_back( &outfit_stack, od->outfit );
3009 if ( outfit_isLicense( &od->outfit ) )
3010 array_push_back( &license_stack, od->outfit.u.lic.provides );
3011 free( od->filename );
3012 }
3013 array_free( odata );
3014
3015 return 0;
3016}
3017
3023int outfit_load( void )
3024{
3025#if DEBUGGING
3026 Uint32 time = SDL_GetTicks();
3027#endif /* DEBUGGING */
3028 int noutfits;
3029
3030 /* First pass, Loads up all outfits, without filling ammunition and the
3031 * likes. */
3033 outfit_loadDir( OUTFIT_DATA_PATH );
3035 noutfits = array_size( outfit_stack );
3036 /* Sort up licenses. */
3037 qsort( outfit_stack, noutfits, sizeof( Outfit ), outfit_cmp );
3038 if ( license_stack != NULL )
3039 qsort( license_stack, array_size( license_stack ), sizeof( char * ),
3040 strsort );
3041
3042#if DEBUGGING
3043 for ( int i = 1; i < noutfits; i++ )
3044 if ( strcmp( outfit_stack[i - 1].name, outfit_stack[i].name ) == 0 )
3045 WARN( _( "Duplicated outfit name '%s' detected!" ),
3046 outfit_stack[i].name );
3047#endif /* DEBUGGING */
3048
3049 /* Second pass. */
3050 for ( int i = 0; i < noutfits; i++ ) {
3051 Outfit *o = &outfit_stack[i];
3052 if ( o->lua_file == NULL )
3053 continue;
3054
3055 nlua_env env;
3056 size_t sz;
3057 char *dat = ndata_read( o->lua_file, &sz );
3058 if ( dat == NULL ) {
3059 WARN( _( "Outfit '%s' failed to read Lua '%s'!" ), o->name,
3060 o->lua_file );
3061 continue;
3062 }
3063
3064 env = nlua_newEnv( o->lua_file );
3065 o->lua_env = env;
3066 /* TODO limit libraries here. */
3067 nlua_loadStandard( env );
3068 nlua_loadGFX( env );
3069 nlua_loadPilotOutfit( env );
3070 nlua_loadCamera( env );
3071 nlua_loadMunition( env );
3072
3073 /* Run code. */
3074 if ( nlua_dobufenv( env, dat, sz, o->lua_file ) != 0 ) {
3075 WARN( _( "Outfit '%s' Lua error:\n%s" ), o->name,
3076 lua_tostring( naevL, -1 ) );
3077 lua_pop( naevL, 1 );
3078 nlua_freeEnv( o->lua_env );
3079 free( dat );
3080 o->lua_env = LUA_NOREF;
3081 continue;
3082 }
3083 free( dat );
3084
3085 /* Check functions as necessary. */
3086 o->lua_descextra = nlua_refenvtype( env, "descextra", LUA_TFUNCTION );
3087 o->lua_onadd = nlua_refenvtype( env, "onadd", LUA_TFUNCTION );
3088 o->lua_onremove = nlua_refenvtype( env, "onremove", LUA_TFUNCTION );
3089 o->lua_init = nlua_refenvtype( env, "init", LUA_TFUNCTION );
3090 o->lua_cleanup = nlua_refenvtype( env, "cleanup", LUA_TFUNCTION );
3091 o->lua_update = nlua_refenvtype( env, "update", LUA_TFUNCTION );
3092 o->lua_ontoggle = nlua_refenvtype( env, "ontoggle", LUA_TFUNCTION );
3093 o->lua_onshoot = nlua_refenvtype( env, "onshoot", LUA_TFUNCTION );
3094 o->lua_onhit = nlua_refenvtype( env, "onhit", LUA_TFUNCTION );
3095 o->lua_outofenergy = nlua_refenvtype( env, "outofenergy", LUA_TFUNCTION );
3096 o->lua_onshootany = nlua_refenvtype( env, "onshootany", LUA_TFUNCTION );
3097 o->lua_onstealth = nlua_refenvtype( env, "onstealth", LUA_TFUNCTION );
3098 o->lua_onscanned = nlua_refenvtype( env, "onscanned", LUA_TFUNCTION );
3099 o->lua_onscan = nlua_refenvtype( env, "onscan", LUA_TFUNCTION );
3100 o->lua_cooldown = nlua_refenvtype( env, "cooldown", LUA_TFUNCTION );
3101 o->lua_land = nlua_refenvtype( env, "land", LUA_TFUNCTION );
3102 o->lua_takeoff = nlua_refenvtype( env, "takeoff", LUA_TFUNCTION );
3103 o->lua_jumpin = nlua_refenvtype( env, "jumpin", LUA_TFUNCTION );
3104 o->lua_board = nlua_refenvtype( env, "board", LUA_TFUNCTION );
3105 o->lua_keydoubletap =
3106 nlua_refenvtype( env, "keydoubletap", LUA_TFUNCTION );
3107 o->lua_keyrelease = nlua_refenvtype( env, "keyrelease", LUA_TFUNCTION );
3108 o->lua_onimpact = nlua_refenvtype( env, "onimpact", LUA_TFUNCTION );
3109 o->lua_onmiss = nlua_refenvtype( env, "onmiss", LUA_TFUNCTION );
3110 o->lua_price = nlua_refenvtype( env, "price", LUA_TFUNCTION );
3111 o->lua_buy = nlua_refenvtype( env, "buy", LUA_TFUNCTION );
3112 o->lua_sell = nlua_refenvtype( env, "sell", LUA_TFUNCTION );
3113
3114 if ( outfit_isMod( o ) ) {
3115 nlua_getenv( naevL, env, "notactive" );
3116 o->u.mod.active = 1;
3117 if ( lua_toboolean( naevL, -1 ) ) {
3118 o->u.mod.active = 0;
3119 if ( o->lua_ontoggle != LUA_NOREF )
3120 WARN( _( "Outfit '%s' has '%s' Lua function defined, but "
3121 "is set as 'notactive'!" ),
3122 o->name, "ontoggle" );
3123 }
3124 lua_pop( naevL, 1 );
3125
3126 /* Regenerate the description... TODO not duplicate code... */
3127 int l = 0;
3128 SDESC_ADD( l, o, "%s", _( outfit_getType( o ) ) );
3129 if ( o->u.mod.active )
3130 SDESC_ADD( l, o, "\n#o%s#0", _( "Activated Outfit" ) );
3131 if ( o->u.mod.active && o->u.mod.cooldown > 0. )
3132 l = os_printD( o->summary_raw, l, o->u.mod.cooldown,
3133 &cooldown_opts );
3134 l = os_printD( o->summary_raw, l, o->cpu, &cpu_opts );
3135 /*l =*/os_printD( o->summary_raw, l, o->mass, &mass_opts );
3136 }
3137 }
3138
3139 /* Third pass for descriptions. */
3140 for ( int i = 0; i < noutfits; i++ ) {
3141 Outfit *o = &outfit_stack[i];
3142 if ( outfit_isMod( o ) ) {
3143 int l = 0;
3144 Outfit *temp = o; /* Needed for SDESC_ADD macro. */
3145 SDESC_ADD( l, temp, "%s", _( outfit_getType( temp ) ) );
3146 if ( temp->u.mod.active ) /* Ignore Lua since it's handled later. */
3147 SDESC_ADD( l, temp, "\n#o%s#0", _( "Activated Outfit" ) );
3148 if ( temp->u.mod.active && temp->u.mod.cooldown > 0. )
3149 l = os_printD( temp->summary_raw, l, temp->u.mod.cooldown,
3150 &cooldown_opts );
3151 l = os_printD( temp->summary_raw, l, temp->cpu, &cpu_opts );
3152 /*l =*/os_printD( temp->summary_raw, l, temp->mass, &mass_opts );
3153 }
3154 /* We add the ship stats to the description here. */
3155 if ( o->summary_raw != NULL ) {
3156 int l = strlen( o->summary_raw );
3157 /*l +=*/ss_statsListDesc( o->stats, &o->summary_raw[l],
3158 OUTFIT_SHORTDESC_MAX - l, 1 );
3159 }
3160 }
3161
3162#ifdef DEBUGGING
3163 char **outfit_names = malloc( noutfits * sizeof( char * ) );
3164 int start;
3165
3166 for ( int i = 0; i < noutfits; i++ )
3167 outfit_names[i] = outfit_stack[i].name;
3168
3169 qsort( outfit_names, noutfits, sizeof( char * ), strsort );
3170 for ( int i = 0; i < ( noutfits - 1 ); i++ ) {
3171 start = i;
3172 while ( strcmp( outfit_names[i], outfit_names[i + 1] ) == 0 )
3173 i++;
3174
3175 if ( i == start )
3176 continue;
3177
3178 WARN( n_( "Name collision! %d outfit is named '%s'",
3179 "Name collision! %d outfits are named '%s'", i + 1 - start ),
3180 i + 1 - start, outfit_names[start] );
3181 }
3182 free( outfit_names );
3183#endif /* DEBUGGING */
3184
3185#if DEBUGGING
3186 if ( conf.devmode ) {
3187 time = SDL_GetTicks() - time;
3188 DEBUG( n_( "Loaded %d Outfit in %.3f s", "Loaded %d Outfits in %.3f s",
3189 noutfits ),
3190 noutfits, time / 1000. );
3191 } else
3192 DEBUG( n_( "Loaded %d Outfit", "Loaded %d Outfits", noutfits ),
3193 noutfits );
3194#endif /* DEBUGGING */
3195 return 0;
3196}
3197
3204{
3205 for ( int i = 0; i < array_size( outfit_stack ); i++ ) {
3206 Outfit *o = &outfit_stack[i];
3207
3208 if ( outfit_isFighterBay( o ) && ( o->u.bay.shipname != NULL ) )
3209 o->u.bay.ship = ship_get( o->u.bay.shipname );
3210
3211 /* Add illegality as necessary. */
3212 if ( array_size( o->illegaltoS ) > 0 ) {
3214 for ( int j = 0; j < array_size( o->illegaltoS ); j++ ) {
3215 int f = faction_get( o->illegaltoS[j] );
3216 array_push_back( &o->illegalto, f );
3217 free( o->illegaltoS[j] );
3218 }
3219 array_free( o->illegaltoS );
3220 o->illegaltoS = NULL;
3221
3222 int l = strlen( o->summary_raw );
3223 SDESC_ADD( l, o, "\n#r%s#0", _( "Illegal to:" ) );
3224 for ( int j = 0; j < array_size( o->illegalto ); j++ )
3225 SDESC_ADD( l, o, _( "\n#r- %s#0" ),
3226 _( faction_name( o->illegalto[j] ) ) );
3227 }
3228
3229 /* Handle initializing module stuff. */
3230 if ( o->lua_env != LUA_NOREF ) {
3231 nlua_getenv( naevL, o->lua_env, "onload" );
3232 if ( lua_isnil( naevL, -1 ) )
3233 lua_pop( naevL, 1 );
3234 else {
3235 lua_pushoutfit( naevL, o );
3236 if ( nlua_pcall( o->lua_env, 1, 0 ) ) {
3237 WARN( _( "Outfit '%s' lua load error -> 'load':\n%s" ), o->name,
3238 lua_tostring( naevL, -1 ) );
3239 lua_pop( naevL, 1 );
3240 }
3241 }
3242 }
3243
3244 /* Make sure licenses are valid. */
3245 if ( ( o->license != NULL ) && !outfit_licenseExists( o->license ) )
3246 WARN( _( "Outfit '%s' has inexistent license requirement '%s'!" ),
3247 o->name, o->license );
3248 }
3249
3250 return 0;
3251}
3252
3258{
3259 for ( int i = 0; i < array_size( outfit_stack ); i++ ) {
3260 xmlDocPtr doc;
3261 xmlNodePtr node, cur;
3262 Outfit *o = &outfit_stack[i];
3263
3264 if ( !outfit_isMap( o ) )
3265 continue;
3266
3267 doc = xml_parsePhysFS( o->filename );
3268 if ( doc == NULL )
3269 continue;
3270
3271 node = doc->xmlChildrenNode; /* first system node */
3272 if ( node == NULL ) {
3273 WARN( _( "Malformed '%s' file: does not contain elements" ),
3274 o->filename );
3275 xmlFreeDoc( doc );
3276 continue;
3277 }
3278
3279 cur = node->xmlChildrenNode;
3280 do { /* load all the data */
3281 /* Only handle nodes. */
3282 xml_onlyNodes( cur );
3283
3284 if ( xml_isNode( cur, "specific" ) )
3285 outfit_parseSMap( o, cur );
3286
3287 } while ( xml_nextNode( cur ) );
3288
3289 /* Clean up. */
3290 xmlFreeDoc( doc );
3291 }
3292
3293 return 0;
3294}
3295
3300{
3301 char s[PATH_MAX];
3302 snprintf( s, sizeof( s ), OVERLAY_GFX_PATH "rarity_%d.webp", rarity );
3303 return gl_newImage( s, OPENGL_TEX_MIPMAPS );
3304}
3305
3309int outfit_checkIllegal( const Outfit *o, int fct )
3310{
3311 for ( int i = 0; i < array_size( o->illegalto ); i++ ) {
3312 if ( o->illegalto[i] == fct )
3313 return 1;
3314 }
3315 return 0;
3316}
3317
3321int outfit_licenseExists( const char *name )
3322{
3323 if ( license_stack == NULL )
3324 return 0;
3325 const char *lic = bsearch( &name, license_stack, array_size( license_stack ),
3326 sizeof( char * ), strsort );
3327 return ( lic != NULL );
3328}
3329
3333void outfit_free( void )
3334{
3335 for ( int i = 0; i < array_size( outfit_stack ); i++ ) {
3336 Outfit *o = &outfit_stack[i];
3337
3338 free( o->filename );
3339
3340 /* Free graphics */
3341 OutfitGFX *gfx = NULL;
3342 if ( outfit_isBolt( o ) )
3343 gfx = &o->u.blt.gfx;
3344 else if ( outfit_isLauncher( o ) )
3345 gfx = &o->u.lau.gfx;
3346 if ( gfx != NULL ) {
3347 gl_freeTexture( gfx->tex );
3348 gl_freeTexture( gfx->tex_end );
3349 poly_free( &gfx->polygon );
3350 glDeleteProgram( gfx->program );
3351 }
3352
3353 /* Free slot. */
3354 outfit_freeSlot( &o->slot );
3355
3356 /* Free stats. */
3357 ss_free( o->stats );
3358
3359 /* Free illegality. */
3360 array_free( o->illegalto );
3361
3362 if ( outfit_isFighterBay( o ) )
3363 free( o->u.bay.shipname );
3364 else if ( outfit_isGUI( o ) )
3365 free( o->u.gui.gui );
3366 else if ( outfit_isLicense( o ) )
3367 free( o->u.lic.provides );
3368 else if ( outfit_isMap( o ) ) {
3369 array_free( o->u.map->systems );
3370 array_free( o->u.map->spobs );
3371 array_free( o->u.map->jumps );
3372 free( o->u.map );
3373 }
3374
3375 /* Lua. */
3376 nlua_freeEnv( o->lua_env );
3377 o->lua_env = LUA_NOREF;
3378 free( o->lua_file );
3379
3380 /* strings */
3381 free( o->typename );
3382 free( o->desc_raw );
3383 free( o->desc_extra );
3384 free( o->limit );
3385 free( o->summary_raw );
3386 free( o->license );
3387 free( o->cond );
3388 free( o->condstr );
3389 free( o->name );
3390 free( o->shortname );
3392 free( o->gfx_store_path );
3393 for ( int j = 0; j < array_size( o->gfx_overlays ); j++ )
3396
3397 /* Free tags. */
3398 for ( int j = 0; j < array_size( o->tags ); j++ )
3399 free( o->tags[j] );
3400 array_free( o->tags );
3401 }
3402
3405}
3406
3416static int os_printD( char *buffer, int i, double value, const t_os_stat *opts )
3417{
3418 const int MAXLEN = OUTFIT_SHORTDESC_MAX - i;
3419 int precision;
3420 char buf[NUM2STRLEN];
3421
3422 if ( opts->hide_zero && fabs( value ) < 1e-2 )
3423 return i;
3424
3425 if ( value > 1. )
3426 precision = opts->precision;
3427 else
3428 precision = MAX( opts->precision, 2 );
3429
3430 i += scnprintf( buffer + i, MAXLEN, "\n" );
3431 if ( opts->colour )
3432 i += scnprintf( buffer + i, MAXLEN,
3433 value > opts->colour_threshold ? "#g"
3434 : value < opts->colour_threshold ? "#r"
3435 : "" );
3436 /* The brochure of the International System of Units declares in chapter 5:
3437 * "a space separates the number and the symbol %". The ISO 31-0 standard
3438 * also specifies a space, and the TeX typesetting system encourages using
3439 * one. */
3440 num2str( buf, value, precision );
3441 i += scnprintf( buffer + i, MAXLEN, p_( "outfitstats", "%s: %s %s" ),
3442 _( opts->name ), buf, opts->unit ? _( opts->unit ) : "" );
3443 if ( opts->colour )
3444 i += scnprintf( buffer + i, MAXLEN, "#0" );
3445 return i;
3446}
3447
3459static int os_printD_range( char *buffer, int i, double minValue,
3460 double maxValue, const t_os_stat *opts )
3461{
3462 const int MAXLEN = OUTFIT_SHORTDESC_MAX - i;
3463 char buf1[NUM2STRLEN], buf2[NUM2STRLEN];
3464
3465 if ( opts->hide_zero && fabs( maxValue ) < 1e-2 )
3466 return i;
3467
3468 num2str( buf1, minValue, opts->precision );
3469 num2str( buf2, maxValue, opts->precision );
3470
3471 i += scnprintf( buffer + i, MAXLEN, "\n" );
3472 if ( opts->colour )
3473 i += scnprintf( buffer + i, MAXLEN,
3474 maxValue > opts->colour_threshold ? "#g"
3475 : maxValue < opts->colour_threshold ? "#r"
3476 : "" );
3477 i += scnprintf( buffer + i, MAXLEN, p_( "outfitstats", "%s: %s %s - %s %s" ),
3478 _( opts->name ), buf1, _( opts->unit ), buf2,
3479 _( opts->unit ) );
3480 if ( opts->colour )
3481 i += scnprintf( buffer + i, MAXLEN, "#0" );
3482 return i;
3483}
3484
3498static int os_printD_rate( char *buffer, int i, double val,
3499 const t_os_stat *val_opts, int multiplier,
3500 double rate, const t_os_stat *rate_opts )
3501{
3502 const int MAXLEN = OUTFIT_SHORTDESC_MAX - i;
3503 char mult[128];
3504 char buf1[NUM2STRLEN], buf2[NUM2STRLEN];
3505
3506 if ( val_opts->hide_zero && fabs( val ) < 1e-2 )
3507 return i;
3508
3509 i += scnprintf( buffer + i, MAXLEN, "\n" );
3510 if ( val_opts->colour )
3511 i += scnprintf( buffer + i, MAXLEN,
3512 val > val_opts->colour_threshold ? "#g"
3513 : val < val_opts->colour_threshold ? "#r"
3514 : "" );
3515
3516 if ( multiplier > 1 )
3517 snprintf( mult, sizeof( mult ), p_( "multiplier", " x %d" ), multiplier );
3518 else
3519 mult[0] = '\0';
3520
3521 num2str( buf1, val, val_opts->precision );
3522 num2str( buf2, rate, rate_opts->precision );
3523
3524 i +=
3525 scnprintf( buffer + i, MAXLEN, p_( "outfitstats", "%s: %s%s %s [%s %s]" ),
3526 _( val_opts->name ), buf1, mult, _( val_opts->unit ), buf2,
3527 _( rate_opts->unit ) );
3528 if ( val_opts->colour )
3529 i += scnprintf( buffer + i, MAXLEN, "#0" );
3530 return i;
3531}
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
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 poly_load(CollPoly *polygon, xmlNodePtr base, const char *name)
Loads a polygon from an xml node.
Definition collision.c:37
int dtype_raw(int type, double *shield, double *armour, double *knockback)
Gets the raw modulation stats of a damage type.
Definition damagetype.c:251
int dtype_get(const char *name)
Gets the id of a dtype based on name.
Definition damagetype.c:157
const char * dtype_damageTypeToStr(int type)
Gets the human readable string from damage type.
Definition damagetype.c:184
const char * faction_name(int f)
Gets a factions "real" (internal) name.
Definition faction.c:331
int faction_get(const char *name)
Gets a faction ID by name.
Definition faction.c:209
void naev_renderLoadscreen(void)
Renders the loadscreen if necessary.
Definition naev.c:640
int naev_shouldRenderLoadscreen(void)
Whether or not we want to render the loadscreen.
Definition naev.c:596
Header file with generic functions and naev-specifics.
#define CLAMP(a, b, x)
Definition naev.h:41
#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
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
int nlua_loadMunition(nlua_env env)
Loads the munition library.
const Outfit ** lua_pushoutfit(lua_State *L, const Outfit *outfit)
Pushes a outfit on the stack.
int nlua_loadPilotOutfit(nlua_env env)
Loads the pilot outfit library.
int num2str(char dest[NUM2STRLEN], double n, int decimals)
Converts a numeric value to a string.
Definition nstring.c:123
int strsort(const void *p1, const void *p2)
Sort function for sorting strings with qsort().
Definition nstring.c:83
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
glTexture * xml_parseTexture(xmlNodePtr node, const char *path, int defsx, int defsy, const unsigned int flags)
Parses a texture handling the sx and sy elements.
Definition nxml.c:25
xmlDocPtr xml_parsePhysFS(const char *filename)
Analogous to xmlParseMemory/xmlParseFile.
Definition nxml.c:70
glInfo gl_screen
Definition opengl.c:47
glTexture * gl_newImage(const char *path, const unsigned int flags)
Loads an image as a texture.
Definition opengl_tex.c:587
void gl_freeTexture(glTexture *texture)
Frees a texture.
Definition opengl_tex.c:835
int outfit_isSecondary(const Outfit *o)
Checks if outfit has the secondary flag set.
Definition outfit.c:747
const char * outfit_description(const Outfit *o)
Gets the description of an outfit.
Definition outfit.c:1143
static int outfit_loadGFX(Outfit *temp, const xmlNodePtr node)
Loads the graphics for an outfit.
Definition outfit.c:1407
static void outfit_parseSMap(Outfit *temp, const xmlNodePtr parent)
Parses the map tidbits of the outfit.
Definition outfit.c:2446
const char * outfit_shortname(const Outfit *o)
Gets the short name (translated) of an outfit.
Definition outfit.c:1168
double outfit_speed(const Outfit *o)
Gets the outfit's speed.
Definition outfit.c:908
static Outfit * outfit_stack
Definition outfit.c:66
double outfit_trackmin(const Outfit *o)
Gets the outfit's minimal tracking.
Definition outfit.c:946
static void outfit_parseSLauncher(Outfit *temp, const xmlNodePtr parent)
Parses the specific area for a launcher and loads it into Outfit.
Definition outfit.c:1923
static int outfit_parseDamage(Damage *dmg, xmlNodePtr node)
Parses a damage node.
Definition outfit.c:1307
int outfit_isBeam(const Outfit *o)
Checks if outfit is a beam type weapon.
Definition outfit.c:639
const char * outfit_summary(const Outfit *o, int withname)
Gets the summary of an outfit.
Definition outfit.c:1157
const Outfit * outfit_getAll(void)
Gets the array (array.h) of all outfits.
Definition outfit.c:247
double outfit_cpu(const Outfit *o)
Gets the outfit's cpu usage.
Definition outfit.c:891
static void outfit_parseSAfterburner(Outfit *temp, const xmlNodePtr parent)
Parses the afterburner tidbits of the outfit.
Definition outfit.c:2284
int outfit_isActive(const Outfit *o)
Checks if outfit is an active outfit.
Definition outfit.c:567
int outfit_soundHit(const Outfit *o)
Gets the outfit's hit sound.
Definition outfit.c:1004
static int outfit_parse(Outfit *temp, const char *file)
Parses and returns Outfit from parent node.
Definition outfit.c:2658
const Outfit * outfit_get(const char *name)
Gets an outfit by name.
Definition outfit.c:223
int outfit_isLauncher(const Outfit *o)
Checks if outfit is a weapon launcher.
Definition outfit.c:649
int outfit_isSeeker(const Outfit *o)
Checks if outfit is a seeking weapon.
Definition outfit.c:659
static OutfitType outfit_strToOutfitType(char *buf)
Gets the outfit type from a human readable string.
Definition outfit.c:1260
OutfitSlotSize outfit_toSlotSize(const char *s)
Gets the outfit slot size from a human readable string.
Definition outfit.c:544
int outfit_miningRarity(const Outfit *o)
Gets the maximum rarity the outfit can mine up to.
Definition outfit.c:976
static int outfit_loadDir(const char *dir)
Loads all the files in a directory.
Definition outfit.c:2979
static void outfit_parseSLicense(Outfit *temp, const xmlNodePtr parent)
Parses the license tidbits of the outfit.
Definition outfit.c:2621
int outfit_isLocalMap(const Outfit *o)
Checks if outfit is a local space map.
Definition outfit.c:719
int outfit_isWeapon(const Outfit *o)
Checks to see if an outfit is a weapon.
Definition outfit.c:603
static void outfit_parseSFighterBay(Outfit *temp, const xmlNodePtr parent)
Parses the fighter bay tidbits of the outfit.
Definition outfit.c:2381
char outfit_slotTypeColourFont(const OutfitSlot *os)
Gets a font colour character that roughly matches an outfit slot type colour.
Definition outfit.c:505
int outfit_isToggleable(const Outfit *o)
Checks if outfit can be toggled.
Definition outfit.c:584
static void outfit_parseSBolt(Outfit *temp, const xmlNodePtr parent)
Parses the specific area for a bolt weapon and loads it into Outfit.
Definition outfit.c:1495
int outfit_compareTech(const void *outfit1, const void *outfit2)
Function meant for use with C89, C99 algorithm qsort().
Definition outfit.c:302
size_t outfit_getNameWithClass(const Outfit *outfit, char *buf, size_t size)
Gets a brief name/class description suitable for the title section of an outfitter screen.
Definition outfit.c:525
const Outfit * outfit_getW(const char *name)
Gets an outfit by name without warning on no-find.
Definition outfit.c:237
#define O_CMP(s, t)
Definition outfit.c:1251
const char * outfit_getTypeBroad(const Outfit *o)
Gets the outfit's broad type.
Definition outfit.c:1091
static void outfit_parseSMod(Outfit *temp, const xmlNodePtr parent)
Parses the modification tidbits of the outfit.
Definition outfit.c:2240
int outfit_fitsSlot(const Outfit *o, const OutfitSlot *s)
Checks to see if an outfit fits a slot.
Definition outfit.c:1180
int outfit_checkIllegal(const Outfit *o, int fct)
Checks illegality of an outfit to a faction.
Definition outfit.c:3309
int outfit_isFighterBay(const Outfit *o)
Checks if outfit is a fighter bay.
Definition outfit.c:701
int outfit_isAfterburner(const Outfit *o)
Checks if outfit is an afterburner.
Definition outfit.c:692
double outfit_range(const Outfit *o)
Gets the outfit's range.
Definition outfit.c:899
int outfit_isMap(const Outfit *o)
Checks if outfit is a space map.
Definition outfit.c:710
double outfit_radius(const Outfit *o)
Gets the outfit's explosion radius.
Definition outfit.c:822
int outfit_spfxShield(const Outfit *o)
Gets the outfit's sound effect.
Definition outfit.c:794
int outfit_isTurret(const Outfit *o)
Checks if outfit is a turret class weapon.
Definition outfit.c:672
double outfit_ammoMass(const Outfit *o)
Gets the outfit's ammunition mass.
Definition outfit.c:1017
int outfit_isForward(const Outfit *o)
Checks if outfit is a fixed mounted weapon.
Definition outfit.c:619
const CollPoly * outfit_plg(const Outfit *o)
Gets the outfit's collision polygon.
Definition outfit.c:768
static void sdesc_miningRarity(int *l, Outfit *temp, int rarity)
Adds a small blurb about rarity mining.
Definition outfit.c:1284
int outfit_isLicense(const Outfit *o)
Checks if outfit is a license.
Definition outfit.c:728
static void outfit_parseSGUI(Outfit *temp, const xmlNodePtr parent)
Parses the GUI tidbits of the outfit.
Definition outfit.c:2587
char outfit_slotSizeColourFont(const OutfitSlot *os)
Gets a font colour character that roughly matches an outfit slot size colour.
Definition outfit.c:487
const OutfitGFX * outfit_gfx(const Outfit *o)
Gets the outfit's graphic effect.
Definition outfit.c:756
int outfit_amount(const Outfit *o)
Gets the amount an outfit can hold.
Definition outfit.c:850
int outfit_load(void)
Loads all the outfits.
Definition outfit.c:3023
const char * slotName(const OutfitSlotType type)
Definition outfit.c:413
double outfit_spin(const Outfit *o)
Gets the outfit's animation spin.
Definition outfit.c:933
int outfit_mapParse(void)
Parses all the maps.
Definition outfit.c:3257
const char * outfit_getType(const Outfit *o)
Gets the outfit's specific type.
Definition outfit.c:1060
double outfit_heat(const Outfit *o)
Gets the outfit's heat generation.
Definition outfit.c:877
double outfit_trackmax(const Outfit *o)
Gets the outfit's minimal tracking.
Definition outfit.c:961
static void outfit_parseSBeam(Outfit *temp, const xmlNodePtr parent)
Parses the beam weapon specifics of an outfit.
Definition outfit.c:1742
static char ** license_stack
Definition outfit.c:67
int outfit_isMod(const Outfit *o)
Checks if outfit is a ship modification.
Definition outfit.c:683
static int os_printD_range(char *buffer, int i, double minValue, double maxValue, const t_os_stat *opts)
Writes an outfit statistic representing a range between two values to a buffer.
Definition outfit.c:3459
int outfit_isGUI(const Outfit *o)
Checks if outfit is a GUI.
Definition outfit.c:737
int outfit_gfxStoreLoad(Outfit *o)
Loads the store graphics for the outfit.
Definition outfit.c:198
int outfit_sound(const Outfit *o)
Gets the outfit's sound.
Definition outfit.c:991
int outfit_spfxArmour(const Outfit *o)
Gets the outfit's sound effect.
Definition outfit.c:780
#define OUTFIT_SHORTDESC_MAX
Definition outfit.c:51
static int outfit_loadPLG(Outfit *temp, const char *buf)
Loads the collision polygon for a bolt outfit.
Definition outfit.c:1354
const char * slotSize(const OutfitSlotSize o)
Gets the slot size as a string.
Definition outfit.c:436
const Damage * outfit_damage(const Outfit *o)
Gets the outfit's damage.
Definition outfit.c:808
char ** outfit_searchFuzzyCase(const char *name, int *n)
Does a fuzzy search of all the outfits. Searches translated names but returns internal names.
Definition outfit.c:267
double outfit_swivel(const Outfit *o)
Gets the swivel of an outfit.
Definition outfit.c:918
static void outfit_parseSLocalMap(Outfit *temp, const xmlNodePtr parent)
Parses the map tidbits of the outfit.
Definition outfit.c:2549
double outfit_duration(const Outfit *o)
Gets the outfit's duration.
Definition outfit.c:1030
double outfit_cooldown(const Outfit *o)
Gets the outfit's cooldown.
Definition outfit.c:1044
const char * outfit_slotName(const Outfit *o)
Gets the name of the slot type of an outfit.
Definition outfit.c:405
int outfit_isBolt(const Outfit *o)
Checks if outfit is bolt type weapon.
Definition outfit.c:629
const char * outfit_slotSize(const Outfit *o)
Gets the name of the slot size of an outfit.
Definition outfit.c:458
void outfit_free(void)
Frees the outfit stack.
Definition outfit.c:3333
const char * outfit_existsCase(const char *name)
Checks to see if an outfit exists matching name (case insensitive).
Definition outfit.c:255
double outfit_energy(const Outfit *o)
Gets the outfit's energy usage.
Definition outfit.c:863
int outfit_licenseExists(const char *name)
Checks to see if a license exists.
Definition outfit.c:3321
void outfit_freeSlot(OutfitSlot *s)
Frees an outfit slot.
Definition outfit.c:1246
static int os_printD(char *buf, int len, double value, const os_opts *opts)
Writes an outfit statistic to a buffer.
Definition outfit.c:3416
const char * outfit_getAmmoAI(const Outfit *o)
Gets a human-readable string describing an ammo outfit's AI.
Definition outfit.c:1123
glTexture * rarity_texture(int rarity)
Definition outfit.c:3299
const glColour * outfit_slotSizeColour(const OutfitSlot *os)
Gets the slot size colour for an outfit slot.
Definition outfit.c:469
static int os_printD_rate(char *buffer, int i, double val, const t_os_stat *val_opts, int multiplier, double rate, const t_os_stat *rate_opts)
Writes an outfit statistic representing a "per unit" value and rate of change value.
Definition outfit.c:3498
double outfit_delay(const Outfit *o)
Gets the outfit's delay.
Definition outfit.c:834
int outfit_fitsSlotType(const Outfit *o, const OutfitSlot *s)
Checks to see if an outfit fits a slot type (ignoring size).
Definition outfit.c:1224
int outfit_loadPost(void)
Loads all the outfits legality.
Definition outfit.c:3203
double pilot_heatCalcOutfitC(const Outfit *o)
Calculates the thermal mass of an outfit.
Definition pilot_heat.c:63
double pilot_heatCalcOutfitArea(const Outfit *o)
Calculates the effective transfer area of an outfit.
Definition pilot_heat.c:75
const char * pilot_outfitDescription(const Pilot *p, const Outfit *o)
Gets the description of an outfit for a given pilot.
const char * pilot_outfitSummary(const Pilot *p, const Outfit *o, int withname)
Gets the summary of an outfit for a give pilot.
double pilot_outfitSpeed(const Pilot *p, const Outfit *o)
Gets the speed of an outfit given a pilot.
static cholmod_common C
Definition safelanes.c:106
const Ship * ship_get(const char *name)
Gets a ship based on its name.
Definition ship.c:99
int ss_statsListDesc(const ShipStatList *ll, char *buf, int len, int newline)
Writes the ship statistics description.
Definition shipstats.c:820
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
unsigned int sp_get(const char *name)
Gets the id of a slot property.
Definition slots.c:139
int sp_required(unsigned int spid)
Gets whether or not a slot property is required.
Definition slots.c:185
int sound_disabled
Definition sound.c:130
int sound_get(const char *name)
Gets the buffer to sound of name.
Definition sound.c:761
Spob * spob_get(const char *spobname)
Gets a spob based on its name.
Definition space.c:1107
StarSystem * system_getAll(void)
Gets an array (array.h) of all star systems.
Definition space.c:925
StarSystem * system_get(const char *sysname)
Get the system from its name.
Definition space.c:1007
JumpPoint * jump_get(const char *jumpname, const StarSystem *sys)
Gets a jump point based on its target and system.
Definition space.c:1279
const TrailSpec * trailSpec_get(const char *name)
Gets a trail spec by name.
Definition spfx.c:1396
int spfx_get(const char *name)
Gets the id of an spfx based on name.
Definition spfx.c:355
const char * start_dtype_default(void)
Gets the default damage type.
Definition start.c:301
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
double energy
Definition outfit.h:224
double heatup
Definition outfit.h:226
int sound_warmup
Definition outfit.h:239
double duration
Definition outfit.h:218
int spfx_armour
Definition outfit.h:237
double warmup
Definition outfit.h:217
int mining_rarity
Definition outfit.h:231
Damage dmg
Definition outfit.h:225
GLuint shader
Definition outfit.h:236
double turn
Definition outfit.h:223
double heat
Definition outfit.h:228
double min_delay
Definition outfit.h:219
double range
Definition outfit.h:222
double swivel
Definition outfit.h:229
glColour colour
Definition outfit.h:234
GLfloat width
Definition outfit.h:235
int spfx_shield
Definition outfit.h:238
double delay
Definition outfit.h:216
int spfx_shield
Definition outfit.h:208
double dispersion
Definition outfit.h:197
double heat
Definition outfit.h:192
double trackmin
Definition outfit.h:193
double radius
Definition outfit.h:188
double falloff
Definition outfit.h:185
double range
Definition outfit.h:184
double delay
Definition outfit.h:182
double swivel
Definition outfit.h:195
double heatup
Definition outfit.h:190
Damage dmg
Definition outfit.h:187
double trackmax
Definition outfit.h:194
double speed
Definition outfit.h:183
int mining_rarity
Definition outfit.h:201
double energy
Definition outfit.h:186
OutfitGFX gfx
Definition outfit.h:204
int spfx_armour
Definition outfit.h:207
double speed_dispersion
Definition outfit.h:199
const struct Ship_ * ship
Definition outfit.h:334
double col_size
Definition outfit.h:162
CollPoly polygon
Definition outfit.h:152
glTexture * tex_end
Definition outfit.h:151
GLuint u_r
Definition outfit.h:158
GLuint program
Definition outfit.h:154
GLuint dimensions
Definition outfit.h:157
double spin
Definition outfit.h:153
GLuint projection
Definition outfit.h:156
GLuint u_time
Definition outfit.h:159
glTexture * tex
Definition outfit.h:150
GLuint u_fade
Definition outfit.h:160
GLuint vertex
Definition outfit.h:155
double size
Definition outfit.h:161
char * gui
Definition outfit.h:358
double dmg_absorb
Definition outfit.h:283
double dispersion
Definition outfit.h:262
double speed_dispersion
Definition outfit.h:264
double trail_x_offset
Definition outfit.h:291
const TrailSpec * trail_spec
Definition outfit.h:290
OutfitGFX gfx
Definition outfit.h:285
double reload_time
Definition outfit.h:252
OutfitAmmoAI ai
Definition outfit.h:271
char * provides
Definition outfit.h:365
double jump_detect
Definition outfit.h:350
double spob_detect
Definition outfit.h:351
Pilot slot that can contain outfits.
Definition outfit.h:139
OutfitSlotSize size
Definition outfit.h:143
int exclusive
Definition outfit.h:141
unsigned int spid
Definition outfit.h:140
OutfitSlotType type
Definition outfit.h:142
For threaded loading of outfits.
Definition outfit.c:57
A ship outfit, depends radically on the type.
Definition outfit.h:372
char ** tags
Definition outfit.h:413
unsigned int properties
Definition outfit.h:406
int lua_ontoggle
Definition outfit.h:427
char * lua_file
Definition outfit.h:416
credits_t price
Definition outfit.h:391
int lua_onscan
Definition outfit.h:434
char * limit
Definition outfit.h:386
int lua_jumpin
Definition outfit.h:438
char * cond
Definition outfit.h:382
union Outfit::@264277167364127137334024361374356236341374052147 u
double cpu
Definition outfit.h:385
OutfitLauncherData lau
Definition outfit.h:456
int lua_cooldown
Definition outfit.h:435
char * desc_raw
Definition outfit.h:392
OutfitBeamData bem
Definition outfit.h:455
int lua_buy
Definition outfit.h:448
OutfitBoltData blt
Definition outfit.h:454
int lua_keyrelease
Definition outfit.h:441
int lua_takeoff
Definition outfit.h:437
double overheat_max
Definition outfit.h:404
OutfitType type
Definition outfit.h:452
int lua_onimpact
Definition outfit.h:443
int lua_outofenergy
Definition outfit.h:430
int lua_land
Definition outfit.h:436
int lua_onscanned
Definition outfit.h:433
int lua_onshootany
Definition outfit.h:431
int lua_update
Definition outfit.h:426
int lua_onmiss
Definition outfit.h:444
glTexture * gfx_store
Definition outfit.h:398
int rarity
Definition outfit.h:376
int lua_descextra
Definition outfit.h:419
int lua_price
Definition outfit.h:446
OutfitGUIData gui
Definition outfit.h:462
unsigned int group
Definition outfit.h:407
char * condstr
Definition outfit.h:383
char ** illegaltoS
Definition outfit.h:388
char * shortname
Definition outfit.h:375
char * filename
Definition outfit.h:377
OutfitModificationData mod
Definition outfit.h:457
OutfitSlot slot
Definition outfit.h:380
int lua_onhit
Definition outfit.h:429
char * summary_raw
Definition outfit.h:393
OutfitAfterburnerData afb
Definition outfit.h:458
int * illegalto
Definition outfit.h:387
char * typename
Definition outfit.h:374
int lua_onshoot
Definition outfit.h:428
OutfitFighterBayData bay
Definition outfit.h:459
int priority
Definition outfit.h:395
int lua_cleanup
Definition outfit.h:425
int lua_sell
Definition outfit.h:449
ShipStatList * stats
Definition outfit.h:410
nlua_env lua_env
Definition outfit.h:418
int lua_onstealth
Definition outfit.h:432
char * license
Definition outfit.h:381
OutfitLocalMapData lmap
Definition outfit.h:461
int lua_onadd
Definition outfit.h:421
glTexture ** gfx_overlays
Definition outfit.h:399
int lua_board
Definition outfit.h:439
int lua_keydoubletap
Definition outfit.h:440
int lua_onremove
Definition outfit.h:422
double overheat_min
Definition outfit.h:402
char * gfx_store_path
Definition outfit.h:397
double mass
Definition outfit.h:384
char * desc_extra
Definition outfit.h:394
OutfitMapData_t * map
Definition outfit.h:460
char * name
Definition outfit.h:373
int lua_init
Definition outfit.h:424
OutfitLicenseData lic
Definition outfit.h:463
Represents relative ship statistics as a linked list.
Definition shipstats.h:198
struct ShipStatList_ * next
Definition shipstats.h:199
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
Definition space.h:102
Abstraction for rendering sprite sheets.
Definition opengl_tex.h:43
double sw
Definition opengl_tex.h:53
double sh
Definition opengl_tex.h:54