naev 0.10.4
outfit.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
13#include <math.h>
14#include <stdlib.h>
15#include "SDL_thread.h"
16#include "physfs.h"
17
18#include "naev.h"
21#include "outfit.h"
22
23#include "array.h"
24#include "conf.h"
25#include "damagetype.h"
26#include "log.h"
27#include "mapData.h"
28#include "ndata.h"
29#include "nfile.h"
30#include "nlua.h"
31#include "nlua_gfx.h"
32#include "nlua_pilotoutfit.h"
33#include "nlua_outfit.h"
34#include "nlua_camera.h"
35#include "nlua_hook.h"
36#include "nstring.h"
37#include "nstring.h"
38#include "nxml.h"
39#include "pilot.h"
40#include "pilot_heat.h"
41#include "ship.h"
42#include "slots.h"
43#include "spfx.h"
44#include "unistd.h"
45
46#define outfit_setProp(o,p) ((o)->properties |= p)
48#define XML_OUTFIT_TAG "outfit"
50#define OUTFIT_SHORTDESC_MAX STRMAX_SHORT
52/*
53 * the stack
54 */
55static Outfit* outfit_stack = NULL;
56static char **license_stack = NULL;
58/*
59 * Helper stuff for setting up short descriptions for outfits.
60 */
61#define SDESC_ADD( l, temp, txt, args... ) \
62(l) += scnprintf( &(temp)->summary_raw[l], OUTFIT_SHORTDESC_MAX-(l), (txt), ## args )
63#define SDESC_COLOUR( l, temp, txt, val, args... ) \
64SDESC_ADD( l, temp, "#%c", ((val)>0)?'g':(((val)<0)?'r':'0') ); \
65SDESC_ADD( l, temp, txt, (val), ## args ); \
66SDESC_ADD( l, temp, "#0" )
67
68#define SDESC_COLOURT( l, temp, txt, threshold, val, args... ) \
69SDESC_ADD( l, temp, "#%c", ((val)>threshold)?'g':(((val)<threshold)?'r':'0') ); \
70SDESC_ADD( l, temp, txt, (val), ## args ); \
71SDESC_ADD( l, temp, "#0" )
72
73#define SDESC_COND( l, temp, txt, val, args... ) \
74if (fabs(val) > 1e-5) { \
75 SDESC_ADD( l, temp, txt, (val), ## args ); \
76}
77#define SDESC_COND_COLOUR( l, temp, txt, val, args... ) \
78if (fabs(val) > 1e-5) { \
79 SDESC_COLOUR( l, temp, txt, (val), ## args ); \
80}
81
82/*
83 * Prototypes
84 */
85/* misc */
86static OutfitType outfit_strToOutfitType( char *buf );
87/* parsing */
88static int outfit_loadDir( char *dir );
89static int outfit_parseDamage( Damage *dmg, xmlNodePtr node );
90static int outfit_parse( Outfit* temp, const char* file );
91static void outfit_parseSBolt( Outfit* temp, const xmlNodePtr parent );
92static void outfit_parseSBeam( Outfit* temp, const xmlNodePtr parent );
93static void outfit_parseSLauncher( Outfit* temp, const xmlNodePtr parent );
94static void outfit_parseSMod( Outfit* temp, const xmlNodePtr parent );
95static void outfit_parseSAfterburner( Outfit* temp, const xmlNodePtr parent );
96static void outfit_parseSFighterBay( Outfit *temp, const xmlNodePtr parent );
97static void outfit_parseSMap( Outfit *temp, const xmlNodePtr parent );
98static void outfit_parseSLocalMap( Outfit *temp, const xmlNodePtr parent );
99static void outfit_parseSGUI( Outfit *temp, const xmlNodePtr parent );
100static void outfit_parseSLicense( Outfit *temp, const xmlNodePtr parent );
101static int outfit_loadPLG( Outfit *temp, const char *buf, unsigned int bolt );
102static void sdesc_miningRarity( int *l, Outfit *temp, int rarity );
103
104static int outfit_cmp( const void *p1, const void *p2 )
105{
106 const Outfit *o1, *o2;
107 o1 = (const Outfit*) p1;
108 o2 = (const Outfit*) p2;
109 return strcmp( o1->name, o2->name );
110}
111
118const Outfit* outfit_get( const char* name )
119{
120 const Outfit *o = outfit_getW( name );
121 if (o==NULL)
122 WARN(_("Outfit '%s' not found in stack."), name);
123 return o;
124}
125
132const Outfit* outfit_getW( const char* name )
133{
134 const Outfit s = {.name = (char*)name };
135 return bsearch( &s, outfit_stack, array_size(outfit_stack), sizeof(Outfit), outfit_cmp );
136}
137
142{
143 return outfit_stack;
144}
145
149const char *outfit_existsCase( const char* name )
150{
151 for (int i=0; i<array_size(outfit_stack); i++)
152 if (strcasecmp(name,outfit_stack[i].name)==0)
153 return outfit_stack[i].name;
154 return NULL;
155}
156
160char **outfit_searchFuzzyCase( const char* name, int *n )
161{
162 int len, nstack;
163 char **names;
164
165 /* Overallocate to maximum. */
166 nstack = array_size(outfit_stack);
167 names = malloc( sizeof(char*) * nstack );
168
169 /* Do fuzzy search. */
170 len = 0;
171 for (int i=0; i<nstack; i++) {
172 if (strcasestr( _(outfit_stack[i].name), name ) != NULL) {
173 names[len] = outfit_stack[i].name;
174 len++;
175 }
176 }
177
178 /* Free if empty. */
179 if (len == 0) {
180 free(names);
181 names = NULL;
182 }
183
184 *n = len;
185 return names;
186}
187
195int outfit_compareTech( const void *outfit1, const void *outfit2 )
196{
197 int ret;
198 const Outfit *o1, *o2;
199
200 /* Get outfits. */
201 o1 = * (const Outfit**) outfit1;
202 o2 = * (const Outfit**) outfit2;
203
204 /* Compare slot type. */
205 if (o1->slot.type < o2->slot.type)
206 return +1;
207 else if (o1->slot.type > o2->slot.type)
208 return -1;
209
210 /* Compare intrinsic types. */
211 if (o1->type < o2->type)
212 return -1;
213 else if (o1->type > o2->type)
214 return +1;
215
216 /* Compare named types. */
217 if ((o1->typename == NULL) && (o2->typename != NULL))
218 return -1;
219 else if ((o1->typename != NULL) && (o2->typename == NULL))
220 return +1;
221 else if ((o1->typename != NULL) && (o2->typename != NULL)) {
222 ret = strcmp( o1->typename, o2->typename );
223 if (ret != 0)
224 return ret;
225 }
226
227 /* Compare slot sizes. */
228 if (o1->slot.size < o2->slot.size)
229 return +1;
230 else if (o1->slot.size > o2->slot.size)
231 return -1;
232
233 /* Compare sort priority. */
234 if (o1->priority < o2->priority)
235 return +1;
236 else if (o1->priority > o2->priority)
237 return -1;
238
239 /* Special prices are listed first. */
240 if ((o1->lua_price != LUA_NOREF) && (o2->lua_price == LUA_NOREF))
241 return -1;
242 else if ((o1->lua_price == LUA_NOREF) && (o2->lua_price != LUA_NOREF))
243 return +1;
244
245 /* Compare price. */
246 if (o1->price < o2->price)
247 return +1;
248 else if (o1->price > o2->price)
249 return -1;
250
251 /* It turns out they're the same. */
252 return strcmp( o1->name, o2->name );
253}
254
255int outfit_filterWeapon( const Outfit *o )
256{ return ((o->slot.type == OUTFIT_SLOT_WEAPON) && !sp_required( o->slot.spid )); }
257
258int outfit_filterUtility( const Outfit *o )
259{ return ((o->slot.type == OUTFIT_SLOT_UTILITY) && !sp_required( o->slot.spid )); }
260
261int outfit_filterStructure( const Outfit *o )
262{ return ((o->slot.type == OUTFIT_SLOT_STRUCTURE) && !sp_required( o->slot.spid )); }
263
264int outfit_filterCore( const Outfit *o )
265{ return sp_required( o->slot.spid ); }
266
267int outfit_filterOther( const Outfit *o )
268{
269 return (!sp_required( o->slot.spid ) && ((o->slot.type == OUTFIT_SLOT_NULL)
270 || (o->slot.type == OUTFIT_SLOT_NA)));
271}
272
279const char *outfit_slotName( const Outfit* o )
280{
281 return slotName( o->slot.type );
282}
283
287const char *slotName( const OutfitSlotType type )
288{
289 switch (type) {
290 case OUTFIT_SLOT_NULL:
291 return "NULL";
292 case OUTFIT_SLOT_NA:
293 return gettext_noop("N/A");
294 case OUTFIT_SLOT_STRUCTURE:
295 return gettext_noop("Structure");
296 case OUTFIT_SLOT_UTILITY:
297 return gettext_noop("Utility");
298 case OUTFIT_SLOT_WEAPON:
299 return gettext_noop("Weapon");
300 default:
301 return gettext_noop("Unknown");
302 }
303}
304
308const char *slotSize( const OutfitSlotSize o )
309{
310 switch (o) {
311 case OUTFIT_SLOT_SIZE_NA:
312 return gettext_noop("N/A");
313 case OUTFIT_SLOT_SIZE_LIGHT:
314 return gettext_noop("Small");
315 case OUTFIT_SLOT_SIZE_MEDIUM:
316 return gettext_noop("Medium");
317 case OUTFIT_SLOT_SIZE_HEAVY:
318 return gettext_noop("Large");
319 default:
320 return gettext_noop("Unknown");
321 }
322}
323
330const char *outfit_slotSize( const Outfit* o )
331{
332 return slotSize( o->slot.size );
333}
334
341const glColour *outfit_slotSizeColour( const OutfitSlot* os )
342{
343 if (os->size == OUTFIT_SLOT_SIZE_HEAVY)
344 return &cOutfitHeavy;
345 else if (os->size == OUTFIT_SLOT_SIZE_MEDIUM)
346 return &cOutfitMedium;
347 else if (os->size == OUTFIT_SLOT_SIZE_LIGHT)
348 return &cOutfitLight;
349 return NULL;
350}
351
359{
360 if (os->size == OUTFIT_SLOT_SIZE_HEAVY)
361 return 'r';
362 else if (os->size == OUTFIT_SLOT_SIZE_MEDIUM)
363 return 'b';
364 else if (os->size == OUTFIT_SLOT_SIZE_LIGHT)
365 return 'y';
366 return '0';
367}
368
376{
377 if (os->type == OUTFIT_SLOT_WEAPON)
378 return 'p';
379 else if (os->type == OUTFIT_SLOT_UTILITY)
380 return 'g';
381 else if (os->type == OUTFIT_SLOT_STRUCTURE)
382 return 'n';
383 return '0';
384}
385
394size_t outfit_getNameWithClass( const Outfit* outfit, char* buf, size_t size )
395{
396 size_t p = scnprintf( &buf[0], size, "%s", _(outfit->name) );
397 if (outfit->slot.type != OUTFIT_SLOT_NA) {
398 p += scnprintf( &buf[p], size-p, _("\n#%c%s #%c%s #0slot"),
399 outfit_slotSizeColourFont( &outfit->slot ), _(outfit_slotSize( outfit )),
400 outfit_slotTypeColourFont( &outfit->slot ), _(outfit_slotName( outfit )) );
401 }
402 return p;
403}
404
411OutfitSlotSize outfit_toSlotSize( const char *s )
412{
413 if (s == NULL) {
414 /*WARN( "(NULL) outfit slot size" );*/
415 return OUTFIT_SLOT_SIZE_NA;
416 }
417
418 if (strcasecmp(s,"Large")==0)
419 return OUTFIT_SLOT_SIZE_HEAVY;
420 else if (strcasecmp(s,"Medium")==0)
421 return OUTFIT_SLOT_SIZE_MEDIUM;
422 else if (strcasecmp(s,"Small")==0)
423 return OUTFIT_SLOT_SIZE_LIGHT;
424
425 WARN(_("'%s' does not match any outfit slot sizes."), s);
426 return OUTFIT_SLOT_SIZE_NA;
427}
428
434int outfit_isActive( const Outfit* o )
435{
437 return 1;
438 if (outfit_isMod(o) && o->u.mod.active)
439 return 1;
441 return 1;
442 return 0;
443}
444
451{
452 /* Must be active. */
453 if (!outfit_isActive(o))
454 return 0;
455
456 /* Special case it is lua-based and not toggleable. */
457 if (outfit_isMod(o) && (o->lua_env != LUA_NOREF) && (o->lua_ontoggle == LUA_NOREF))
458 return 0;
459
460 return 1;
461}
462
469{
470 return ( (o->type==OUTFIT_TYPE_BOLT) ||
471 (o->type==OUTFIT_TYPE_BEAM) );
472}
478int outfit_isBolt( const Outfit* o )
479{
480 return ( (o->type==OUTFIT_TYPE_BOLT) ||
481 (o->type==OUTFIT_TYPE_TURRET_BOLT) );
482}
488int outfit_isBeam( const Outfit* o )
489{
490 return ( (o->type==OUTFIT_TYPE_BEAM) ||
491 (o->type==OUTFIT_TYPE_TURRET_BEAM) );
492}
499{
500 return ( (o->type==OUTFIT_TYPE_LAUNCHER) ||
501 (o->type==OUTFIT_TYPE_TURRET_LAUNCHER) );
502}
508int outfit_isSeeker( const Outfit* o )
509{
510 if (((o->type==OUTFIT_TYPE_TURRET_LAUNCHER) ||
511 (o->type==OUTFIT_TYPE_LAUNCHER)) &&
512 (o->u.lau.ai > 0))
513 return 1;
514 return 0;
515}
521int outfit_isTurret( const Outfit* o )
522{
523 return ((o->type==OUTFIT_TYPE_TURRET_BOLT) ||
524 (o->type==OUTFIT_TYPE_TURRET_BEAM) ||
525 (o->type==OUTFIT_TYPE_TURRET_LAUNCHER));
526}
532int outfit_isMod( const Outfit* o )
533{
534 return (o->type==OUTFIT_TYPE_MODIFICATION);
535}
542{
543 return (o->type==OUTFIT_TYPE_AFTERBURNER);
544}
551{
552 return (o->type==OUTFIT_TYPE_FIGHTER_BAY);
553}
559int outfit_isMap( const Outfit* o )
560{
561 return (o->type==OUTFIT_TYPE_MAP);
562}
569{
570 return (o->type==OUTFIT_TYPE_LOCALMAP);
571}
578{
579 return (o->type==OUTFIT_TYPE_LICENSE);
580}
586int outfit_isGUI( const Outfit* o )
587{
588 return (o->type==OUTFIT_TYPE_GUI);
589}
590
597{
598 return (o->properties & OUTFIT_PROP_WEAP_SECONDARY) != 0;
599}
600
605const glTexture* outfit_gfx( const Outfit* o )
606{
607 if (outfit_isBolt(o)) return o->u.blt.gfx_space;
608 else if (outfit_isLauncher(o)) return o->u.lau.gfx_space;
609 return NULL;
610}
615const CollPoly* outfit_plg( const Outfit* o )
616{
617 if (outfit_isBolt(o)) return o->u.blt.polygon;
618 else if (outfit_isLauncher(o)) return o->u.lau.polygon;
619 return NULL;
620}
626{
627 if (outfit_isBolt(o)) return o->u.blt.spfx_armour;
628 else if (outfit_isBeam(o)) return o->u.bem.spfx_armour;
629 else if (outfit_isLauncher(o)) return o->u.lau.spfx_armour;
630 return -1;
631}
637{
638 if (outfit_isBolt(o)) return o->u.blt.spfx_shield;
639 else if (outfit_isBeam(o)) return o->u.bem.spfx_shield;
640 else if (outfit_isLauncher(o)) return o->u.lau.spfx_shield;
641 return -1;
642}
647const Damage *outfit_damage( const Outfit* o )
648{
649 if (outfit_isBolt(o)) return &o->u.blt.dmg;
650 else if (outfit_isBeam(o)) return &o->u.bem.dmg;
651 else if (outfit_isLauncher(o)) return &o->u.lau.dmg;
652 return NULL;
653}
658double outfit_delay( const Outfit* o )
659{
660 if (outfit_isBolt(o)) return o->u.blt.delay;
661 else if (outfit_isBeam(o)) return o->u.bem.delay;
662 else if (outfit_isLauncher(o)) return o->u.lau.delay;
663 else if (outfit_isFighterBay(o)) return o->u.bay.delay;
664 return -1;
665}
670int outfit_amount( const Outfit* o )
671{
672 if (outfit_isLauncher(o)) return o->u.lau.amount;
673 else if (outfit_isFighterBay(o)) return o->u.bay.amount;
674 return -1;
675}
676
681double outfit_energy( const Outfit* o )
682{
683 if (outfit_isBolt(o)) return o->u.blt.energy;
684 else if (outfit_isBeam(o)) return o->u.bem.energy;
685 else if (outfit_isLauncher(o)) return o->u.lau.energy;
686 return -1.;
687}
692double outfit_heat( const Outfit* o )
693{
694 if (outfit_isBolt(o)) return o->u.blt.heat;
695 else if (outfit_isAfterburner(o)) return o->u.afb.heat;
696 else if (outfit_isBeam(o)) return o->u.bem.heat;
697 return -1;
698}
703double outfit_cpu( const Outfit* o )
704{
705 return o->cpu;
706}
711double outfit_range( const Outfit* o )
712{
713 if (outfit_isBolt(o))
714 return o->u.blt.falloff + (o->u.blt.range - o->u.blt.falloff)/2.;
715 else if (outfit_isBeam(o))
716 return o->u.bem.range;
717 else if (outfit_isLauncher(o)) {
718 if (o->u.lau.thrust) {
719 double speedinc;
720 if (o->u.lau.speed > 0.) /* Ammo that don't start stopped don't have max speed. */
721 speedinc = INFINITY;
722 else
723 speedinc = o->u.lau.speed_max - o->u.lau.speed;
724 double at = speedinc / o->u.lau.thrust;
725 if (at < o->u.lau.duration)
726 return speedinc * (o->u.lau.duration - at / 2.) + o->u.lau.speed * o->u.lau.duration;
727
728 /* Maximum speed will never be reached. */
729 return pow2(o->u.lau.duration) * o->u.lau.thrust / 2. + o->u.lau.duration * o->u.lau.speed;
730 }
731 return o->u.lau.speed * o->u.lau.duration;
732 }
733 else if (outfit_isFighterBay(o))
734 return INFINITY;
735 return -1.;
736}
742double outfit_speed( const Outfit* o )
743{
744 if (outfit_isBolt(o))
745 return o->u.blt.speed;
746 else if (outfit_isLauncher(o)) {
747 if (o->u.lau.thrust == 0.)
748 return o->u.lau.speed;
749 else { /* Gets the average speed. */
750 double t;
751 if (o->u.lau.speed > 0.) /* Ammo that don't start stopped don't have max speed. */
752 t = INFINITY;
753 else
754 t = (o->u.lau.speed_max - o->u.lau.speed) / o->u.lau.thrust; /* Time to reach max speed */
755
756 /* Reaches max speed. */
757 if (t < o->u.lau.duration)
758 return (o->u.lau.thrust * t * t / 2. + (o->u.lau.speed_max - o->u.lau.speed) * (o->u.lau.duration - t)) / o->u.lau.duration + o->u.lau.speed;
759 /* Doesn't reach max speed. */
760 else
761 return o->u.lau.thrust * o->u.lau.duration / 2. + o->u.lau.speed;
762 }
763 }
764 return -1.;
765}
771double outfit_spin( const Outfit* o )
772{
773 if (outfit_isBolt(o)) return o->u.blt.spin;
774 else if (outfit_isLauncher(o)) return o->u.lau.spin;
775 return -1.;
776}
782double outfit_trackmin( const Outfit* o )
783{
784 if (outfit_isBolt(o)) return o->u.blt.trackmin;
785 else if (outfit_isLauncher(o)) return o->u.lau.trackmin;
786 else if (outfit_isBeam(o)) return 0.;
787 return -1.;
788}
794double outfit_trackmax( const Outfit* o )
795{
796 if (outfit_isBolt(o)) return o->u.blt.trackmax;
797 else if (outfit_isLauncher(o)) return o->u.lau.trackmax;
798 else if (outfit_isBeam(o)) return 1.;
799 return -1.;
800}
807{
808 if (outfit_isBolt(o)) return o->u.blt.mining_rarity;
809 else if (outfit_isLauncher(o)) return o->u.lau.mining_rarity;
810 else if (outfit_isBeam(o)) return o->u.bem.mining_rarity;
811 return -1;
812}
818int outfit_sound( const Outfit* o )
819{
820 if (outfit_isBolt(o)) return o->u.blt.sound;
821 else if (outfit_isLauncher(o)) return o->u.lau.sound;
822 return -1.;
823}
829int outfit_soundHit( const Outfit* o )
830{
831 if (outfit_isBolt(o)) return o->u.blt.sound_hit;
832 else if (outfit_isLauncher(o)) return o->u.lau.sound_hit;
833 return -1.;
834}
840double outfit_ammoMass( const Outfit *o )
841{
842 if (outfit_isLauncher(o)) return o->u.lau.ammo_mass;
843 else if (outfit_isFighterBay(o)) return o->u.bay.ship_mass;
844 return -1.;
845}
851double outfit_duration( const Outfit* o )
852{
853 if (outfit_isMod(o)) { if (o->u.mod.active) return o->u.mod.duration; }
854 else if (outfit_isAfterburner(o)) return INFINITY;
855 else if (outfit_isBolt(o)) return (o->u.blt.range / o->u.blt.speed);
856 else if (outfit_isBeam(o)) return o->u.bem.duration;
857 else if (outfit_isLauncher(o)) return o->u.lau.duration;
858 else if (outfit_isFighterBay(o)) return INFINITY;
859 return -1.;
860}
866double outfit_cooldown( const Outfit* o )
867{
868 if (outfit_isMod(o)) { if (o->u.mod.active) return o->u.mod.cooldown; }
869 else if (outfit_isAfterburner(o)) return 0.;
870 return -1.;
871}
872
879const char* outfit_getType( const Outfit* o )
880{
881 const char* outfit_typename[] = {
882 N_("NULL"),
883 N_("Bolt Cannon"),
884 N_("Beam Cannon"),
885 N_("Bolt Turret"),
886 N_("Beam Turret"),
887 N_("Launcher"),
888 N_("Turret Launcher"),
889 N_("Ship Modification"),
890 N_("Afterburner"),
891 N_("Fighter Bay"),
892 N_("Star Map"),
893 N_("Local Map"),
894 N_("GUI"),
895 N_("License"),
896 };
897
898 /* Name override. */
899 if (o->typename != NULL)
900 return o->typename;
901 return outfit_typename[o->type];
902}
903
910const char* outfit_getTypeBroad( const Outfit* o )
911{
912 if (outfit_isBolt(o)) return N_("Bolt Weapon");
913 else if (outfit_isBeam(o)) return N_("Beam Weapon");
914 else if (outfit_isLauncher(o)) return N_("Launcher");
915 //else if (outfit_isTurret(o)) return N_("Turret");
916 else if (outfit_isMod(o)) return N_("Modification");
917 else if (outfit_isAfterburner(o)) return N_("Afterburner");
918 else if (outfit_isFighterBay(o)) return N_("Fighter Bay");
919 else if (outfit_isMap(o)) return N_("Map");
920 else if (outfit_isLocalMap(o)) return N_("Local Map");
921 else if (outfit_isGUI(o)) return N_("GUI");
922 else if (outfit_isLicense(o)) return N_("License");
923 else return N_("Unknown");
924}
925
931const char* outfit_getAmmoAI( const Outfit *o )
932{
933 const char *ai_type[] = {
934 N_("Unguided"),
935 N_("Seek"),
936 N_("Smart")
937 };
938
939 if (!outfit_isLauncher(o)) {
940 WARN(_("Outfit '%s' is not a launcher outfit"), o->name);
941 return NULL;
942 }
943
944 return ai_type[o->u.lau.ai];
945}
946
955const char* outfit_description( const Outfit *o )
956{
957 return pilot_outfitDescription( NULL, o );
958}
959
969const char* outfit_summary( const Outfit *o, int withname )
970{
971 return pilot_outfitSummary( NULL, o, withname );
972}
973
981int outfit_fitsSlot( const Outfit* o, const OutfitSlot* s )
982{
983 const OutfitSlot *os = &o->slot;
984
985 /* Outfit must have valid slot type. */
986 if ((os->type == OUTFIT_SLOT_NULL) ||
987 (os->type == OUTFIT_SLOT_NA))
988 return 0;
989
990 /* Outfit type must match outfit slot. */
991 if (os->type != s->type)
992 return 0;
993
994 /* It doesn't fit. */
995 if (os->size > s->size)
996 return 0;
997
998 /* Must match slot property. */
999 if (os->spid != 0)
1000 if (s->spid != os->spid)
1001 return 0;
1002
1003 /* Exclusive only match property. */
1004 if (s->exclusive)
1005 if (s->spid != os->spid)
1006 return 0;
1007
1008 /* Must have valid slot size. */
1009 /*
1010 if (os->size == OUTFIT_SLOT_SIZE_NA)
1011 return 0;
1012 */
1013
1014 /* It meets all criteria. */
1015 return 1;
1016}
1017
1025int outfit_fitsSlotType( const Outfit* o, const OutfitSlot* s )
1026{
1027 const OutfitSlot *os = &o->slot;
1028
1029 /* Outfit must have valid slot type. */
1030 if ((os->type == OUTFIT_SLOT_NULL) ||
1031 (os->type == OUTFIT_SLOT_NA))
1032 return 0;
1033
1034 /* Outfit type must match outfit slot. */
1035 if (os->type != s->type)
1036 return 0;
1037
1038 /* It meets all criteria. */
1039 return 1;
1040}
1041
1048{
1049 (void) s;
1050}
1051
1052#define O_CMP(s,t) \
1053if (strcasecmp(buf,(s))==0) return t
1060static OutfitType outfit_strToOutfitType( char *buf )
1062 O_CMP("bolt", OUTFIT_TYPE_BOLT);
1063 O_CMP("beam", OUTFIT_TYPE_BEAM);
1064 O_CMP("turret bolt", OUTFIT_TYPE_TURRET_BOLT);
1065 O_CMP("turret beam", OUTFIT_TYPE_TURRET_BEAM);
1066 O_CMP("launcher", OUTFIT_TYPE_LAUNCHER);
1067 O_CMP("turret launcher",OUTFIT_TYPE_TURRET_LAUNCHER);
1068 O_CMP("modification", OUTFIT_TYPE_MODIFICATION);
1069 O_CMP("afterburner", OUTFIT_TYPE_AFTERBURNER);
1070 O_CMP("fighter bay", OUTFIT_TYPE_FIGHTER_BAY);
1071 O_CMP("map", OUTFIT_TYPE_MAP);
1072 O_CMP("localmap", OUTFIT_TYPE_LOCALMAP);
1073 O_CMP("license", OUTFIT_TYPE_LICENSE);
1074 O_CMP("gui", OUTFIT_TYPE_GUI);
1075
1076 WARN(_("Invalid outfit type: '%s'"),buf);
1077 return OUTFIT_TYPE_NULL;
1078}
1079#undef O_CMP
1080
1084static void sdesc_miningRarity( int *l, Outfit *temp, int rarity )
1086 if (rarity == 0)
1087 return;
1088 if (rarity == 2)
1089 SDESC_ADD( *l, temp, "\n#g%s#0", _("Can mine uncommon and rare minerals") );
1090 else
1091 SDESC_ADD( *l, temp, "\n#g%s#0", _("Can mine uncommon minerals") );
1092}
1093
1106static int outfit_parseDamage( Damage *dmg, xmlNodePtr node )
1108 xmlNodePtr cur;
1109
1110 /* Defaults. */
1111 dmg->type = dtype_get("normal");
1112 dmg->damage = 0.;
1113 dmg->penetration = 0.;
1114 dmg->disable = 0.;
1115
1116 cur = node->xmlChildrenNode;
1117 do {
1118 xml_onlyNodes( cur );
1119
1120 /* Core properties. */
1121 xmlr_float( cur, "penetrate", dmg->penetration );
1122 xmlr_float( cur, "physical", dmg->damage );
1123 xmlr_float( cur, "disable", dmg->disable );
1124
1125 /* Get type */
1126 if (xml_isNode(cur,"type")) {
1127 char *buf = xml_get( cur );
1128 dmg->type = dtype_get(buf);
1129 if (dmg->type < 0) { /* Case damage type in outfit.xml that isn't in damagetype.xml */
1130 dmg->type = 0;
1131 WARN(_("Unknown damage type '%s'"), buf);
1132 }
1133 }
1134 else WARN(_("Damage has unknown node '%s'"), cur->name);
1135
1136 } while (xml_nextNode(cur));
1137
1138 /* Normalize. */
1139 dmg->penetration /= 100.;
1140
1141 return 0;
1142}
1143
1151static int outfit_loadPLG( Outfit *temp, const char *buf, unsigned int bolt )
1153 char *file;
1154 CollPoly *polygon;
1155 xmlDocPtr doc;
1156 xmlNodePtr node, cur;
1157
1158 asprintf( &file, "%s%s.xml", OUTFIT_POLYGON_PATH, buf );
1159
1160 /* See if the file does exist. */
1161 if (!PHYSFS_exists(file)) {
1162 WARN(_("%s xml collision polygon does not exist!\n \
1163 Please use the script 'polygon_from_sprite.py' \
1164that can be found in Naev's artwork repo."), file);
1165 free(file);
1166 return 0;
1167 }
1168
1169 /* Load the XML. */
1170 doc = xml_parsePhysFS( file );
1171
1172 if (doc == NULL) {
1173 free(file);
1174 return 0;
1175 }
1176
1177 node = doc->xmlChildrenNode; /* First polygon node */
1178 if (node == NULL) {
1179 xmlFreeDoc(doc);
1180 WARN(_("Malformed %s file: does not contain elements"), file);
1181 free(file);
1182 return 0;
1183 }
1184
1185 free(file);
1186
1187 if (bolt) {
1188 do { /* load the polygon data */
1189 if (xml_isNode(node,"polygons")) {
1190 cur = node->children;
1191 temp->u.blt.polygon = array_create_size( CollPoly, 36 );
1192 do {
1193 if (xml_isNode(cur,"polygon")) {
1194 polygon = &array_grow( &temp->u.blt.polygon );
1195 LoadPolygon( polygon, cur );
1196 }
1197 } while (xml_nextNode(cur));
1198 }
1199 } while (xml_nextNode(node));
1200 }
1201 else {
1202 do { /* Second case: outfit is an ammo */
1203 if (xml_isNode(node,"polygons")) {
1204 cur = node->children;
1205 temp->u.lau.polygon = array_create_size( CollPoly, 36 );
1206 do {
1207 if (xml_isNode(cur,"polygon")) {
1208 polygon = &array_grow( &temp->u.lau.polygon );
1209 LoadPolygon( polygon, cur );
1210 }
1211 } while (xml_nextNode(cur));
1212 }
1213 } while (xml_nextNode(node));
1214 }
1215
1216 xmlFreeDoc(doc);
1217 return 0;
1218}
1219
1226static void outfit_parseSBolt( Outfit* temp, const xmlNodePtr parent )
1228 ShipStatList *ll;
1229 xmlNodePtr node;
1230 char *buf;
1231 double C, area;
1232 double dshield, darmour, dknockback;
1233 int l;
1234
1235 /* Defaults */
1236 temp->u.blt.spfx_armour = -1;
1237 temp->u.blt.spfx_shield = -1;
1238 temp->u.blt.sound = -1;
1239 temp->u.blt.sound_hit = -1;
1240 temp->u.blt.falloff = -1.;
1241 temp->u.blt.trackmin = -1.;
1242 temp->u.blt.trackmax = -1.;
1243 temp->u.blt.shots = 1;
1244
1245 node = parent->xmlChildrenNode;
1246 do { /* load all the data */
1247 xml_onlyNodes(node);
1248 xmlr_float(node,"speed",temp->u.blt.speed);
1249 xmlr_float(node,"delay",temp->u.blt.delay);
1250 xmlr_float(node,"energy",temp->u.blt.energy);
1251 xmlr_float(node,"heatup",temp->u.blt.heatup);
1252 xmlr_float(node,"trackmin",temp->u.blt.trackmin);
1253 xmlr_float(node,"trackmax",temp->u.blt.trackmax);
1254 xmlr_float(node,"swivel",temp->u.blt.swivel);
1255 xmlr_float(node,"dispersion",temp->u.blt.dispersion);
1256 xmlr_float(node,"speed_dispersion",temp->u.blt.speed_dispersion);
1257 xmlr_int(node,"shots",temp->u.blt.shots);
1258 xmlr_int(node,"mining_rarity",temp->u.blt.mining_rarity);
1259 xmlr_strd(node,"lua",temp->lua_file);
1260 if (xml_isNode(node,"range")) {
1261 xmlr_attr_strd(node,"blowup",buf);
1262 if (buf != NULL) {
1263 if (strcmp(buf,"armour")==0)
1264 outfit_setProp(temp, OUTFIT_PROP_WEAP_BLOWUP_SHIELD);
1265 else if (strcmp(buf,"shield")==0)
1266 outfit_setProp(temp, OUTFIT_PROP_WEAP_BLOWUP_ARMOUR);
1267 else
1268 WARN(_("Outfit '%s' has invalid blowup property: '%s'"),
1269 temp->name, buf );
1270 free(buf);
1271 }
1272 temp->u.blt.range = xml_getFloat(node);
1273 continue;
1274 }
1275 xmlr_float(node,"falloff",temp->u.blt.falloff);
1276
1277 /* Graphics. */
1278 if (xml_isNode(node,"gfx")) {
1279 temp->u.blt.gfx_space = xml_parseTexture( node,
1280 OUTFIT_GFX_PATH"space/%s", 6, 6,
1281 OPENGL_TEX_MAPTRANS | OPENGL_TEX_MIPMAPS );
1282 xmlr_attr_strd(node, "spin", buf);
1283 if (buf != NULL) {
1284 outfit_setProp( temp, OUTFIT_PROP_WEAP_SPIN );
1285 temp->u.blt.spin = atof( buf );
1286 free(buf);
1287 }
1288 /* Load the collision polygon. */
1289 buf = xml_get(node);
1290 outfit_loadPLG( temp, buf, 1 );
1291
1292 /* Validity check: there must be 1 polygon per sprite. */
1293 if (array_size(temp->u.blt.polygon) != 36) {
1294 WARN(_("Outfit '%s': the number of collision polygons is wrong.\n \
1295 npolygon = %i and sx*sy = %i"),
1296 temp->name, array_size(temp->u.blt.polygon), 36);
1297 }
1298 continue;
1299 }
1300 if (xml_isNode(node,"gfx_end")) {
1301 temp->u.blt.gfx_end = xml_parseTexture( node,
1302 OUTFIT_GFX_PATH"space/%s", 6, 6,
1303 OPENGL_TEX_MAPTRANS | OPENGL_TEX_MIPMAPS );
1304 continue;
1305 }
1306
1307 /* Special effects. */
1308 if (xml_isNode(node,"spfx_shield")) {
1309 temp->u.blt.spfx_shield = spfx_get(xml_get(node));
1310 continue;
1311 }
1312 if (xml_isNode(node,"spfx_armour")) {
1313 temp->u.blt.spfx_armour = spfx_get(xml_get(node));
1314 continue;
1315 }
1316
1317 /* Misc. */
1318 if (xml_isNode(node,"sound")) {
1319 temp->u.blt.sound = sound_get( xml_get(node) );
1320 continue;
1321 }
1322 if (xml_isNode(node,"sound_hit")) {
1323 temp->u.blt.sound_hit = sound_get( xml_get(node) );
1324 continue;
1325 }
1326 if (xml_isNode(node,"damage")) {
1327 outfit_parseDamage( &temp->u.blt.dmg, node );
1328 continue;
1329 }
1330
1331 /* Stats. */
1332 ll = ss_listFromXML( node );
1333 if (ll != NULL) {
1334 ll->next = temp->stats;
1335 temp->stats = ll;
1336 continue;
1337 }
1338 WARN(_("Outfit '%s' has unknown node '%s'"),temp->name, node->name);
1339 } while (xml_nextNode(node));
1340
1341 /* If not defined assume maximum. */
1342 if (temp->u.blt.falloff < 0.)
1343 temp->u.blt.falloff = temp->u.blt.range;
1344
1345 /* Post processing. */
1346 temp->u.blt.swivel *= M_PI/180.;
1347 temp->u.blt.dispersion *= M_PI/100.;
1348 if (outfit_isTurret(temp))
1349 temp->u.blt.swivel = M_PI;
1350 /*
1351 * dT Mthermal - Qweap
1352 * Hweap = ----------------------
1353 * tweap
1354 */
1355 C = pilot_heatCalcOutfitC(temp);
1356 area = pilot_heatCalcOutfitArea(temp);
1357 temp->u.blt.heat = ((800.-CONST_SPACE_STAR_TEMP)*C +
1358 STEEL_HEAT_CONDUCTIVITY * ((800-CONST_SPACE_STAR_TEMP) * area)) /
1359 temp->u.blt.heatup * temp->u.blt.delay;
1360
1361 /* Set short description. */
1362 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
1363 l = 0;
1364 SDESC_ADD( l, temp, _("%s [%s]"), _(outfit_getType(temp)),
1365 _(dtype_damageTypeToStr(temp->u.blt.dmg.type)) );
1366 dtype_raw( temp->u.blt.dmg.type, &dshield, &darmour, &dknockback );
1367 SDESC_COLOURT( l, temp, _("\n %.0f%% vs armour"), 100., darmour*100. );
1368 SDESC_COLOURT( l, temp, _("\n %.0f%% vs shield"), 100., dshield*100. );
1369 SDESC_COND( l, temp, _("\n %.0f%% knockback"), dknockback*100. );
1370 SDESC_COND_COLOUR( l, temp, _("\n%.0f CPU"), temp->cpu );
1371 SDESC_COND( l, temp, _("\n%.0f Mass"), temp->mass );
1372 SDESC_ADD( l, temp, _("\n%.0f%% Penetration"), temp->u.blt.dmg.penetration*100. );
1373 SDESC_COND( l, temp, _("\n%.2f DPS [%.0f Damage]"),
1374 1./temp->u.blt.delay * temp->u.blt.dmg.damage * (double)temp->u.blt.shots, temp->u.blt.dmg.damage * (double)temp->u.blt.shots );
1375 SDESC_COND( l, temp, _("\n%.2f Disable/s [%.0f Disable]"),
1376 1./temp->u.blt.delay * temp->u.blt.dmg.disable * (double)temp->u.blt.shots, temp->u.blt.dmg.disable * (double)temp->u.blt.shots );
1377 SDESC_ADD( l, temp, _("\n%.1f Shots Per Second"), 1./temp->u.blt.delay );
1378 SDESC_COND( l, temp, _("\n%.1f EPS [%.0f Energy]"),
1379 1./temp->u.blt.delay * temp->u.blt.energy, temp->u.blt.energy );
1380 SDESC_ADD( l, temp, _("\n%s Range"), num2strU( temp->u.blt.range, 0 ) );
1381 SDESC_ADD( l, temp, _("\n%.0f Speed"), temp->u.blt.speed );
1382 SDESC_COND( l, temp, _("\n%.1f second heat up"), temp->u.blt.heatup);
1383 SDESC_COND( l, temp, _("\n%.1f Degree Dispersion"), temp->u.blt.dispersion*180./M_PI );
1384 if (!outfit_isTurret(temp))
1385 SDESC_ADD( l, temp, _("\n%.1f Degree Swivel"), temp->u.blt.swivel*180./M_PI );
1386 SDESC_ADD( l, temp, _("\n%s Optimal Tracking"), num2strU( temp->u.blt.trackmax, 0 ) );
1387 SDESC_ADD( l, temp, _("\n%s Minimal Tracking"), num2strU( temp->u.blt.trackmin, 0 ) );
1388 sdesc_miningRarity( &l, temp, temp->u.blt.mining_rarity );
1389
1390#define MELEMENT(o,s) \
1391if (o) WARN(_("Outfit '%s' missing/invalid '%s' element"), temp->name, s)
1392 MELEMENT(temp->u.blt.gfx_space==NULL,"gfx");
1393 MELEMENT(temp->u.blt.spfx_shield==-1,"spfx_shield");
1394 MELEMENT(temp->u.blt.spfx_armour==-1,"spfx_armour");
1395 MELEMENT((sound_disabled!=0) && (temp->u.blt.sound<0),"sound");
1396 MELEMENT(temp->mass==0.,"mass");
1397 MELEMENT(temp->u.blt.delay==0,"delay");
1398 MELEMENT(temp->u.blt.speed==0,"speed");
1399 MELEMENT(temp->u.blt.range==0,"range");
1400 MELEMENT(temp->u.blt.dmg.damage==0,"damage");
1401 MELEMENT(temp->u.blt.energy==0.,"energy");
1402 //MELEMENT(temp->cpu==0.,"cpu");
1403 MELEMENT(temp->u.blt.falloff > temp->u.blt.range,"falloff");
1404 MELEMENT(temp->u.blt.heatup==0.,"heatup");
1405 MELEMENT(((temp->u.blt.swivel > 0.) || outfit_isTurret(temp)) && (temp->u.blt.trackmin<0.),"trackmin");
1406 MELEMENT(((temp->u.blt.swivel > 0.) || outfit_isTurret(temp)) && (temp->u.blt.trackmax<0.),"trackmax");
1407#undef MELEMENT
1408}
1409
1416static void outfit_parseSBeam( Outfit* temp, const xmlNodePtr parent )
1418 ShipStatList *ll;
1419 int l;
1420 xmlNodePtr node;
1421 double C, area;
1422 double dshield, darmour, dknockback;
1423 char *shader;
1424
1425 /* Defaults. */
1426 temp->u.bem.spfx_armour = -1;
1427 temp->u.bem.spfx_shield = -1;
1428 temp->u.bem.sound_warmup = -1;
1429 temp->u.bem.sound = -1;
1430 temp->u.bem.sound_off = -1;
1431
1432 node = parent->xmlChildrenNode;
1433 do { /* load all the data */
1434 xml_onlyNodes(node);
1435 xmlr_float(node,"range",temp->u.bem.range);
1436 xmlr_float(node,"turn",temp->u.bem.turn);
1437 xmlr_float(node,"energy",temp->u.bem.energy);
1438 xmlr_float(node,"delay",temp->u.bem.delay);
1439 xmlr_float(node,"warmup",temp->u.bem.warmup);
1440 xmlr_float(node,"heatup",temp->u.bem.heatup);
1441 xmlr_float(node,"swivel",temp->u.bem.swivel);
1442 xmlr_int(node,"mining_rarity",temp->u.bem.mining_rarity);
1443 xmlr_strd(node,"lua",temp->lua_file);
1444
1445 if (xml_isNode(node, "duration")) {
1446 xmlr_attr_float(node, "min", temp->u.bem.min_duration);
1447 temp->u.bem.duration = xml_getFloat(node);
1448 continue;
1449 }
1450
1451 if (xml_isNode(node,"damage")) {
1452 outfit_parseDamage( &temp->u.bem.dmg, node );
1453 continue;
1454 }
1455
1456 /* Graphic stuff. */
1457 if (xml_isNode(node,"shader")) {
1458 xmlr_attr_float(node, "r", temp->u.bem.colour.r);
1459 xmlr_attr_float(node, "g", temp->u.bem.colour.g);
1460 xmlr_attr_float(node, "b", temp->u.bem.colour.b);
1461 xmlr_attr_float(node, "a", temp->u.bem.colour.a);
1462 xmlr_attr_float(node, "width", temp->u.bem.width);
1463 col_gammaToLinear( &temp->u.bem.colour );
1464 shader = xml_get(node);
1465 if (gl_has( OPENGL_SUBROUTINES )) {
1466 temp->u.bem.shader = glGetSubroutineIndex( shaders.beam.program, GL_FRAGMENT_SHADER, shader );
1467 if (temp->u.bem.shader == GL_INVALID_INDEX)
1468 WARN("Beam outfit '%s' has unknown shader function '%s'", temp->name, shader);
1469 }
1470 continue;
1471 }
1472 if (xml_isNode(node,"spfx_armour")) {
1473 temp->u.bem.spfx_armour = spfx_get(xml_get(node));
1474 continue;
1475 }
1476 if (xml_isNode(node,"spfx_shield")) {
1477 temp->u.bem.spfx_shield = spfx_get(xml_get(node));
1478 continue;
1479 }
1480
1481 /* Sound stuff. */
1482 if (xml_isNode(node,"sound_warmup")) {
1483 temp->u.bem.sound_warmup = sound_get( xml_get(node) );
1484 continue;
1485 }
1486 if (xml_isNode(node,"sound")) {
1487 temp->u.bem.sound = sound_get( xml_get(node) );
1488 continue;
1489 }
1490 if (xml_isNode(node,"sound_off")) {
1491 temp->u.bem.sound_off = sound_get( xml_get(node) );
1492 continue;
1493 }
1494
1495 /* Stats. */
1496 ll = ss_listFromXML( node );
1497 if (ll != NULL) {
1498 ll->next = temp->stats;
1499 temp->stats = ll;
1500 continue;
1501 }
1502 WARN(_("Outfit '%s' has unknown node '%s'"),temp->name, node->name);
1503 } while (xml_nextNode(node));
1504
1505 /* Post processing. */
1506 temp->u.bem.swivel *= M_PI/180.;
1507 temp->u.bem.turn *= M_PI/180.; /* Convert to rad/s. */
1508 C = pilot_heatCalcOutfitC(temp);
1509 area = pilot_heatCalcOutfitArea(temp);
1510 temp->u.bem.heat = ((800.-CONST_SPACE_STAR_TEMP)*C +
1511 STEEL_HEAT_CONDUCTIVITY * ((800-CONST_SPACE_STAR_TEMP) * area)) /
1512 temp->u.bem.heatup * (temp->u.bem.delay+temp->u.bem.warmup+temp->u.bem.duration) / temp->u.bem.delay;
1513
1514 /* Set short description. */
1515 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
1516 l = 0;
1517 SDESC_ADD( l, temp, _("%s [%s]"), _(outfit_getType(temp)),
1518 _(dtype_damageTypeToStr(temp->u.bem.dmg.type)) );
1519 dtype_raw( temp->u.bem.dmg.type, &dshield, &darmour, &dknockback );
1520 SDESC_COLOURT( l, temp, _("\n %.0f%% vs armour"), 100., darmour*100. );
1521 SDESC_COLOURT( l, temp, _("\n %.0f%% vs shield"), 100., dshield*100. );
1522 SDESC_COND( l, temp, _("\n %.0f%% knockback"), dknockback*100. );
1523 SDESC_COND_COLOUR( l, temp, _("\n%.0f CPU"), temp->cpu );
1524 SDESC_COND( l, temp, _("\n%.0f Mass"), temp->mass );
1525 SDESC_ADD( l, temp, _("\n%.0f%% Penetration"), temp->u.bem.dmg.penetration*100 );
1526 SDESC_ADD( l, temp, _("\n%.2f DPS [%s]"),
1527 temp->u.bem.dmg.damage * temp->u.bem.duration / (temp->u.bem.duration + temp->u.bem.delay),
1528 _(dtype_damageTypeToStr(temp->u.bem.dmg.type) ) );
1529 SDESC_COND( l, temp, _("\n%.0f Disable/s"), temp->u.bem.dmg.disable );
1530 SDESC_COND( l, temp, _("\n%.1f EPS"), temp->u.bem.energy );
1531 SDESC_ADD( l, temp, _("\n%.1f Duration"), temp->u.bem.duration );
1532 SDESC_ADD( l, temp, _(" (%.1f minimum)"), temp->u.bem.min_duration );
1533 SDESC_ADD( l, temp, _("\n%.1f Cooldown"), temp->u.bem.duration );
1534 SDESC_ADD( l, temp, _("\n%s Range"), num2strU(temp->u.bem.range,0) );
1535 SDESC_COND( l, temp, _("\n%.1f second heat up"),temp->u.bem.heatup );
1536 if (!outfit_isTurret(temp))
1537 SDESC_ADD( l, temp, _("\n%.1f Degree Swivel"), temp->u.bem.swivel*180./M_PI );
1538 sdesc_miningRarity( &l, temp, temp->u.bem.mining_rarity );
1539
1540#define MELEMENT(o,s) \
1541if (o) WARN( _("Outfit '%s' missing/invalid '%s' element"), temp->name, s)
1542 MELEMENT(temp->u.bem.width==0.,"shader width");
1543 MELEMENT(temp->u.bem.spfx_shield==-1,"spfx_shield");
1544 MELEMENT(temp->u.bem.spfx_armour==-1,"spfx_armour");
1545 MELEMENT((sound_disabled!=0) && (temp->u.bem.warmup > 0.) && (temp->u.bem.sound<0),"sound_warmup");
1546 MELEMENT((sound_disabled!=0) && (temp->u.bem.sound<0),"sound");
1547 MELEMENT((sound_disabled!=0) && (temp->u.bem.sound_off<0),"sound_off");
1548 MELEMENT(temp->u.bem.delay==0,"delay");
1549 MELEMENT(temp->u.bem.duration==0,"duration");
1550 MELEMENT(temp->u.bem.min_duration < 0,"duration");
1551 MELEMENT(temp->u.bem.range==0,"range");
1552 MELEMENT((temp->type!=OUTFIT_TYPE_BEAM) && (temp->u.bem.turn==0),"turn");
1553 MELEMENT(temp->u.bem.energy==0.,"energy");
1554 MELEMENT(temp->cpu==0.,"cpu");
1555 MELEMENT(temp->u.bem.dmg.damage==0,"damage");
1556 MELEMENT(temp->u.bem.heatup==0.,"heatup");
1557#undef MELEMENT
1558}
1559
1566static void outfit_parseSLauncher( Outfit* temp, const xmlNodePtr parent )
1568 ShipStatList *ll;
1569 xmlNodePtr node;
1570 double dshield, darmour, dknockback;
1571 int l;
1572
1573 temp->u.lau.trackmin = -1.;
1574 temp->u.lau.trackmax = -1.;
1575 temp->u.lau.spfx_armour = -1;
1576 temp->u.lau.spfx_shield = -1;
1577 temp->u.lau.sound = -1;
1578 temp->u.lau.sound_hit = -1;
1579 temp->u.lau.trail_spec = NULL;
1580 temp->u.lau.ai = -1;
1581 temp->u.lau.speed_max = -1.;
1582 temp->u.lau.shots = 1;
1583
1584 node = parent->xmlChildrenNode;
1585 do { /* load all the data */
1586 xml_onlyNodes(node);
1587 xmlr_float(node,"delay",temp->u.lau.delay);
1588 xmlr_int(node,"amount",temp->u.lau.amount);
1589 xmlr_float(node,"reload_time",temp->u.lau.reload_time);
1590 xmlr_float(node,"trackmin",temp->u.lau.trackmin);
1591 xmlr_float(node,"trackmax",temp->u.lau.trackmax);
1592 xmlr_float(node,"lockon",temp->u.lau.lockon);
1593 xmlr_float(node,"iflockon",temp->u.lau.iflockon);
1594 xmlr_float(node,"swivel",temp->u.lau.swivel);
1595 xmlr_float(node,"dispersion",temp->u.blt.dispersion);
1596 xmlr_float(node,"speed_dispersion",temp->u.blt.speed_dispersion);
1597 xmlr_int(node,"shots",temp->u.blt.shots);
1598 xmlr_int(node,"mining_rarity",temp->u.lau.mining_rarity);
1599 xmlr_strd(node,"lua",temp->lua_file);
1600
1601 if (!outfit_isTurret(temp))
1602 xmlr_float(node,"arc",temp->u.lau.arc); /* This is in semi-arc like swivel. */
1603
1604 /* Ammo stuff. */
1605 /* Basic */
1606 if (xml_isNode(node,"duration")) {
1607 char *buf;
1608 xmlr_attr_strd(node,"blowup",buf);
1609 if (buf != NULL) {
1610 if (strcmp(buf,"armour")==0)
1611 outfit_setProp(temp, OUTFIT_PROP_WEAP_BLOWUP_SHIELD);
1612 else if (strcmp(buf,"shield")==0)
1613 outfit_setProp(temp, OUTFIT_PROP_WEAP_BLOWUP_ARMOUR);
1614 else
1615 WARN(_("Outfit '%s' has invalid blowup property: '%s'"),
1616 temp->name, buf );
1617 free(buf);
1618 }
1619 temp->u.lau.duration = xml_getFloat(node);
1620 continue;
1621 }
1622 xmlr_float(node,"resist",temp->u.lau.resist);
1623 /* Movement */
1624 xmlr_float(node,"thrust",temp->u.lau.thrust);
1625 xmlr_float(node,"turn",temp->u.lau.turn);
1626 xmlr_float(node,"speed",temp->u.lau.speed);
1627 xmlr_float(node,"speed_max",temp->u.lau.speed_max);
1628 xmlr_float(node,"energy",temp->u.lau.energy);
1629 xmlr_float(node,"ammo_mass",temp->u.lau.ammo_mass);
1630 if (xml_isNode(node,"gfx")) {
1631 temp->u.lau.gfx_space = xml_parseTexture( node,
1632 OUTFIT_GFX_PATH"space/%s", 6, 6,
1633 OPENGL_TEX_MAPTRANS | OPENGL_TEX_MIPMAPS );
1634 xmlr_attr_float(node, "spin", temp->u.lau.spin);
1635 if (temp->u.lau.spin != 0)
1636 outfit_setProp( temp, OUTFIT_PROP_WEAP_SPIN );
1637 /* Load the collision polygon. */
1638 char *buf = xml_get(node);
1639 outfit_loadPLG( temp, buf, 0 );
1640
1641 /* Validity check: there must be 1 polygon per sprite. */
1642 if (array_size(temp->u.lau.polygon) != 36) {
1643 WARN(_("Outfit '%s': the number of collision polygons is wrong.\n \
1644 npolygon = %i and sx*sy = %i"),
1645 temp->name, array_size(temp->u.lau.polygon), 36);
1646 }
1647 continue;
1648 }
1649 if (xml_isNode(node,"spfx_armour")) {
1650 temp->u.lau.spfx_armour = spfx_get(xml_get(node));
1651 continue;
1652 }
1653 if (xml_isNode(node,"spfx_shield")) {
1654 temp->u.lau.spfx_shield = spfx_get(xml_get(node));
1655 continue;
1656 }
1657 if (xml_isNode(node,"sound")) {
1658 temp->u.lau.sound = sound_get( xml_get(node) );
1659 continue;
1660 }
1661 if (xml_isNode(node,"sound_hit")) {
1662 temp->u.lau.sound_hit = sound_get( xml_get(node) );
1663 continue;
1664 }
1665 if (xml_isNode(node,"damage")) {
1666 outfit_parseDamage( &temp->u.lau.dmg, node );
1667 continue;
1668 }
1669 if (xml_isNode(node,"trail_generator")) {
1670 xmlr_attr_float( node, "x", temp->u.lau.trail_x_offset );
1671 char *buf = xml_get(node);
1672 if (buf == NULL)
1673 buf = "default";
1674 temp->u.lau.trail_spec = trailSpec_get( buf );
1675 continue;
1676 }
1677 if (xml_isNode(node,"ai")) {
1678 char *buf = xml_get(node);
1679 if (buf != NULL) {
1680 if (strcmp(buf,"unguided")==0)
1681 temp->u.lau.ai = AMMO_AI_UNGUIDED;
1682 else if (strcmp(buf,"seek")==0)
1683 temp->u.lau.ai = AMMO_AI_SEEK;
1684 else if (strcmp(buf,"smart")==0)
1685 temp->u.lau.ai = AMMO_AI_SMART;
1686 else
1687 WARN(_("Ammo '%s' has unknown ai type '%s'."), temp->name, buf);
1688 }
1689 continue;
1690 }
1691
1692 /* Stats. */
1693 ll = ss_listFromXML( node );
1694 if (ll != NULL) {
1695 ll->next = temp->stats;
1696 temp->stats = ll;
1697 continue;
1698 }
1699 WARN(_("Outfit '%s' has unknown node '%s'"),temp->name, node->name);
1700 } while (xml_nextNode(node));
1701
1702 /* Post processing. */
1703 temp->u.lau.swivel *= M_PI/180.;
1704 temp->u.lau.arc *= M_PI/180.;
1705 /* Note that arc will be 0. for turrets. */
1706 temp->u.lau.dispersion *= M_PI/180.;
1707 temp->u.lau.turn *= M_PI/180.; /* Convert to rad/s. */
1708 if (temp->u.lau.speed_max < 0.)
1709 temp->u.lau.speed_max = temp->u.lau.speed;
1710 else if (temp->u.lau.speed > 0. && temp->u.lau.thrust > 0.) /* Condition for not taking max_speed into account. */
1711 WARN(_("Max speed of ammo '%s' will be ignored."), temp->name);
1712 temp->u.lau.resist /= 100.;
1713
1714 /* Short description. */
1715 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
1716 l = 0;
1717 SDESC_ADD( l, temp, _("%s [%s]"), _(outfit_getType(temp)),
1718 _(dtype_damageTypeToStr(temp->u.lau.dmg.type)) );
1719 dtype_raw( temp->u.lau.dmg.type, &dshield, &darmour, &dknockback );
1720 SDESC_COLOURT( l, temp, _("\n %.0f%% vs armour"), 100., darmour*100. );
1721 SDESC_COLOURT( l, temp, _("\n %.0f%% vs shield"), 100., dshield*100. );
1722 SDESC_COND( l, temp, _("\n %.0f%% knockback"), dknockback*100. );
1723 SDESC_COND_COLOUR( l, temp, _("\n%.0f CPU"), temp->cpu );
1724 SDESC_COND( l, temp, _("\n%.0f Mass"), temp->mass );
1725
1726 if (outfit_isSeeker(temp)) {
1727 SDESC_ADD( l, temp, _("\n%.1f Second Lock-on"), temp->u.lau.lockon );
1728 SDESC_ADD( l, temp, _("\n%.1f Second In-Flight Calibration"), temp->u.lau.iflockon );
1729 SDESC_ADD( l, temp, _("\n%s Optimal Tracking"), num2strU( temp->u.lau.trackmax, 0 ) );
1730 SDESC_ADD( l, temp, _("\n%s Minimal Tracking"), num2strU( temp->u.lau.trackmin, 0 ) );
1731 }
1732 else {
1733 SDESC_ADD( l, temp, _("\nNo Seeking") );
1734 if (outfit_isTurret(temp) || temp->u.lau.swivel > 0.) {
1735 SDESC_ADD( l, temp, _("\n%s Optimal Tracking"), num2strU( temp->u.lau.trackmax, 0 ) );
1736 SDESC_ADD( l, temp, _("\n%s Minimal Tracking"), num2strU( temp->u.lau.trackmin, 0 ) );
1737 SDESC_COND( l, temp, _("\n%.1f Degree Swivel"), temp->u.lau.swivel*180./M_PI );
1738 }
1739 }
1740
1741 SDESC_ADD( l, temp, _("\nHolds %d ammo"), temp->u.lau.amount );
1742 SDESC_ADD( l, temp, _("\n%.0f%% Penetration"), temp->u.lau.dmg.penetration * 100. );
1743 SDESC_COND( l, temp, _("\n%.2f DPS [%.0f Damage]"),
1744 1. / temp->u.lau.delay * temp->u.lau.dmg.damage * (double)temp->u.lau.shots, temp->u.lau.dmg.damage * (double)temp->u.lau.shots );
1745 SDESC_COND( l, temp, _("\n%.1f Disable/s [%.0f Disable]"),
1746 1. / temp->u.lau.delay * temp->u.lau.dmg.disable * (double)temp->u.lau.shots, temp->u.lau.dmg.disable * (double)temp->u.lau.shots );
1747 SDESC_ADD( l, temp, _("\n%.1f Shots Per Second"), 1. / temp->u.lau.delay );
1748 SDESC_ADD( l, temp, _("\n%s Range [%.1f duration]"), num2strU( outfit_range(temp), 0 ), temp->u.lau.duration );
1749 if (temp->u.lau.thrust > 0.) {
1750 if (temp->u.lau.speed > 0.)
1751 SDESC_ADD( l, temp, _("\n%.0f Initial Speed (%.0f Thrust)"), temp->u.lau.speed, temp->u.lau.thrust );
1752 else
1753 SDESC_ADD( l, temp, _("\n%.0f Thrust"), temp->u.lau.thrust );
1754 }
1755 else
1756 SDESC_COND( l, temp, _("\n%.0f Speed"), temp->u.lau.speed );
1757 if (!(temp->u.lau.thrust > 0. && temp->u.lau.speed > 0.))
1758 SDESC_ADD( l, temp, _("\n%.0f Maximum Speed"), temp->u.lau.speed_max );
1759 SDESC_ADD( l, temp, _("\n%.1f Seconds to Reload"), temp->u.lau.reload_time );
1760 SDESC_COND( l, temp, _("\n%.1f EPS [%.0f Energy]"), temp->u.lau.delay * temp->u.lau.energy, temp->u.lau.energy );
1761 SDESC_COND( l, temp, _("\n%.0f%% Jam Resistance"), temp->u.lau.resist * 100. );
1762 sdesc_miningRarity( &l, temp, temp->u.lau.mining_rarity );
1763
1764#define MELEMENT(o,s) \
1765if (o) WARN(_("Outfit '%s' missing '%s' element"), temp->name, s)
1766 MELEMENT(temp->u.lau.delay==0.,"delay");
1767 MELEMENT(temp->cpu==0.,"cpu");
1768 MELEMENT(temp->u.lau.amount==0.,"amount");
1769 MELEMENT(temp->u.lau.reload_time==0.,"reload_time");
1770 MELEMENT(temp->u.lau.ammo_mass==0.,"mass");
1771 MELEMENT(!outfit_isProp(temp,OUTFIT_PROP_SHOOT_DRY)&&temp->u.lau.gfx_space==NULL,"gfx");
1772 MELEMENT(!outfit_isProp(temp,OUTFIT_PROP_SHOOT_DRY)&&temp->u.lau.spfx_shield==-1,"spfx_shield");
1773 MELEMENT(!outfit_isProp(temp,OUTFIT_PROP_SHOOT_DRY)&&temp->u.lau.spfx_armour==-1,"spfx_armour");
1774 MELEMENT((sound_disabled!=0) && (temp->u.lau.sound<0),"sound");
1775 /* MELEMENT(temp->u.lau.thrust==0,"thrust"); */
1776 /* Unguided missiles don't need everything */
1777 if (outfit_isSeeker(temp)) {
1778 MELEMENT(temp->u.lau.turn==0,"turn");
1779 MELEMENT(temp->u.lau.trackmin<0,"trackmin");
1780 MELEMENT(temp->u.lau.trackmax<0,"trackmax");
1781 MELEMENT(temp->u.lau.lockon<0,"lockon");
1782 MELEMENT(!outfit_isTurret(temp) && (temp->u.lau.arc==0.),"arc");
1783 }
1784 MELEMENT(!outfit_isProp(temp,OUTFIT_PROP_SHOOT_DRY)&&temp->u.lau.speed_max==0,"speed_max");
1785 MELEMENT(!outfit_isProp(temp,OUTFIT_PROP_SHOOT_DRY)&&temp->u.lau.duration==0,"duration");
1786 MELEMENT(temp->u.lau.dmg.damage==0,"damage");
1787 /*MELEMENT(temp->u.lau.energy==0.,"energy");*/
1788#undef MELEMENT
1789 if (!outfit_isProp(temp,OUTFIT_PROP_SHOOT_DRY)&&temp->u.lau.speed==0. && temp->u.lau.thrust==0.)
1790 WARN(_("Outfit '%s' has no speed nor thrust set!"), temp->name);
1791 if (!outfit_isProp(temp,OUTFIT_PROP_SHOOT_DRY)&&temp->u.lau.iflockon >= temp->u.lau.duration)
1792 WARN(_("Outfit '%s' has longer 'iflockon' than ammo 'duration'"), temp->name);
1793}
1794
1801static void outfit_parseSMod( Outfit* temp, const xmlNodePtr parent )
1803 int l;
1804 xmlNodePtr node = parent->children;
1805
1806 do { /* load all the data */
1807 ShipStatList *ll;
1808
1809 xml_onlyNodes(node);
1810 xmlr_strd(node,"lua",temp->lua_file);
1811
1812 if (xml_isNode(node,"active")) {
1813 xmlr_attr_float(node, "cooldown", temp->u.mod.cooldown);
1814 temp->u.mod.active = 1;
1815 temp->u.mod.duration = xml_getFloat(node);
1816
1817 /* Infinity if no duration specified. */
1818 if (temp->u.mod.duration == 0)
1819 temp->u.mod.duration = INFINITY;
1820
1821 continue;
1822 }
1823
1824 /* Stats. */
1825 ll = ss_listFromXML( node );
1826 if (ll != NULL) {
1827 ll->next = temp->stats;
1828 temp->stats = ll;
1829 continue;
1830 }
1831
1832 WARN(_("Outfit '%s' has unknown node '%s'"),temp->name, node->name);
1833 } while (xml_nextNode(node));
1834
1835 /* Set short description. */
1836 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
1837 l = 0;
1838 SDESC_ADD( l, temp, "%s", _(outfit_getType(temp)) );
1839 if (temp->u.mod.active || temp->lua_ontoggle != LUA_NOREF)
1840 SDESC_ADD( l, temp, "%s", _("\n#rActivated Outfit#0") );
1841 if (temp->u.mod.active && temp->u.mod.cooldown > 0.)
1842 SDESC_ADD( l, temp, _(" #r(%.1f s Cooldown)#0"), temp->u.mod.cooldown );
1843 SDESC_COND_COLOUR( l, temp, _("\n%+.0f CPU"), temp->cpu );
1844 SDESC_COND( l, temp, _("\n%.0f Mass"), temp->mass );
1845}
1846
1853static void outfit_parseSAfterburner( Outfit* temp, const xmlNodePtr parent )
1855 double C, area;
1856 size_t l;
1857 xmlNodePtr node = parent->children;
1858
1859 /* Defaults. */
1860 temp->u.afb.sound = -1;
1861 temp->u.afb.sound_on = -1;
1862 temp->u.afb.sound_off = -1;
1863
1864 /* must be >= 1. */
1865 temp->u.afb.thrust = 1.;
1866 temp->u.afb.speed = 1.;
1867
1868 do { /* parse the data */
1869 ShipStatList *ll;
1870
1871 xml_onlyNodes(node);
1872 xmlr_float(node,"rumble",temp->u.afb.rumble);
1873 xmlr_strd(node,"lua",temp->lua_file);
1874 if (xml_isNode(node,"sound_on")) {
1875 temp->u.afb.sound_on = sound_get( xml_get(node) );
1876 continue;
1877 }
1878 if (xml_isNode(node,"sound")) {
1879 temp->u.afb.sound = sound_get( xml_get(node) );
1880 continue;
1881 }
1882 if (xml_isNode(node,"sound_off")) {
1883 temp->u.afb.sound_off = sound_get( xml_get(node) );
1884 continue;
1885 }
1886 xmlr_float(node,"thrust",temp->u.afb.thrust);
1887 xmlr_float(node,"speed",temp->u.afb.speed);
1888 xmlr_float(node,"energy",temp->u.afb.energy);
1889 xmlr_float(node,"mass_limit",temp->u.afb.mass_limit);
1890 xmlr_float(node,"heatup",temp->u.afb.heatup);
1891 xmlr_float(node,"heat_cap",temp->u.afb.heat_cap);
1892 xmlr_float(node,"heat_base",temp->u.afb.heat_base);
1893
1894 /* Stats. */
1895 ll = ss_listFromXML( node );
1896 if (ll != NULL) {
1897 ll->next = temp->stats;
1898 temp->stats = ll;
1899 continue;
1900 }
1901 WARN(_("Outfit '%s' has unknown node '%s'"),temp->name, node->name);
1902 } while (xml_nextNode(node));
1903
1904 /* Set short description. */
1905 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
1906 l = 0;
1907 SDESC_ADD( l, temp, "%s", _(outfit_getType(temp)) );
1908 SDESC_ADD( l, temp, _("\n#rActivated Outfit#0") );
1909 SDESC_COND_COLOUR( l, temp, _("\n%.0f CPU"), temp->cpu );
1910 SDESC_COND( l, temp, _("\n%.0f Mass"), temp->mass );
1911 SDESC_ADD( l, temp, _("\nOnly one can be equipped") );
1912 SDESC_ADD( l, temp, _("\n%.0f Maximum Effective Mass"), temp->u.afb.mass_limit );
1913 SDESC_ADD( l, temp, _("\n%.0f%% Thrust"), temp->u.afb.thrust + 100. );
1914 SDESC_ADD( l, temp, _("\n%.0f%% Maximum Speed"), temp->u.afb.speed + 100. );
1915 SDESC_COND( l, temp, _("\n%.1f EPS"), temp->u.afb.energy );
1916 SDESC_COND( l, temp, _("\n%.1f Rumble"), temp->u.afb.rumble );
1917
1918 /* Post processing. */
1919 temp->u.afb.thrust /= 100.;
1920 temp->u.afb.speed /= 100.;
1921 C = pilot_heatCalcOutfitC(temp);
1922 area = pilot_heatCalcOutfitArea(temp);
1923 temp->u.afb.heat = ((800.-CONST_SPACE_STAR_TEMP)*C +
1924 STEEL_HEAT_CONDUCTIVITY * ((800-CONST_SPACE_STAR_TEMP) * area)) /
1925 temp->u.afb.heatup;
1926
1927#define MELEMENT(o,s) \
1928if (o) WARN(_("Outfit '%s' missing/invalid '%s' element"), temp->name, s)
1929 MELEMENT(temp->u.afb.thrust==0.,"thrust");
1930 MELEMENT(temp->u.afb.speed==0.,"speed");
1931 MELEMENT(temp->u.afb.energy==0.,"energy");
1932 //MELEMENT(temp->cpu==0.,"cpu");
1933 MELEMENT(temp->u.afb.mass_limit==0.,"mass_limit");
1934 MELEMENT(temp->u.afb.heatup==0.,"heatup");
1935#undef MELEMENT
1936}
1937
1944static void outfit_parseSFighterBay( Outfit *temp, const xmlNodePtr parent )
1946 size_t l;
1947 xmlNodePtr node = parent->children;
1948
1949 do {
1950 ShipStatList *ll;
1951
1952 xml_onlyNodes(node);
1953 xmlr_float(node,"delay",temp->u.bay.delay);
1954 xmlr_float(node,"reload_time",temp->u.bay.reload_time);
1955 xmlr_strd(node,"ship",temp->u.bay.ship);
1956 xmlr_float(node,"ship_mass",temp->u.bay.ship_mass);
1957 xmlr_int(node,"amount",temp->u.bay.amount);
1958 xmlr_strd(node,"lua",temp->lua_file);
1959
1960 /* Stats. */
1961 ll = ss_listFromXML( node );
1962 if (ll != NULL) {
1963 ll->next = temp->stats;
1964 temp->stats = ll;
1965 continue;
1966 }
1967 WARN(_("Outfit '%s' has unknown node '%s'"),temp->name, node->name);
1968 } while (xml_nextNode(node));
1969
1970 /* Set short description. */
1971 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
1972 l = 0;
1973 SDESC_ADD( l, temp, "%s", _(outfit_getType(temp)) );
1974 SDESC_COND_COLOUR( l, temp, _("\n%.0f CPU"), temp->cpu );
1975 SDESC_COND( l, temp, _("\n%.0f Mass"), temp->mass );
1976 SDESC_COND( l, temp, _("\n%.1f Seconds Per Launch"), temp->u.bay.delay );
1977 SDESC_ADD( l, temp, _("\nHolds %d ships"), temp->u.bay.amount );
1978 SDESC_COND( l, temp, _("\n%.1f Seconds to Reload"), temp->u.bay.reload_time );
1979
1980#define MELEMENT(o,s) \
1981if (o) WARN(_("Outfit '%s' missing/invalid '%s' element"), temp->name, s)
1982 MELEMENT(temp->u.bay.ship==NULL,"ship");
1983 MELEMENT(temp->u.bay.ship_mass<=0.,"ship_mass");
1984 MELEMENT(temp->u.bay.delay==0,"delay");
1985 MELEMENT(temp->u.bay.reload_time==0.,"reload_time");
1986 MELEMENT(temp->cpu==0.,"cpu");
1987 MELEMENT(temp->u.bay.amount==0,"amount");
1988#undef MELEMENT
1989}
1990
1997static void outfit_parseSMap( Outfit *temp, const xmlNodePtr parent )
1999 xmlNodePtr node;
2000 char *buf;
2001 StarSystem *system_stack;
2002 Spob *spob;
2003 JumpPoint *jump;
2004
2005 node = parent->children;
2006
2007 temp->slot.type = OUTFIT_SLOT_NA;
2008 temp->slot.size = OUTFIT_SLOT_SIZE_NA;
2009
2010 temp->u.map->systems = array_create(StarSystem*);
2011 temp->u.map->spobs = array_create(Spob*);
2012 temp->u.map->jumps = array_create(JumpPoint*);
2013
2014 do {
2015 xml_onlyNodes(node);
2016
2017 if (xml_isNode(node,"sys")) {
2018 xmlr_attr_strd(node,"name",buf);
2019 StarSystem *sys = system_get(buf);
2020 if (sys == NULL) {
2021 WARN(_("Map '%s' has invalid system '%s'"), temp->name, buf);
2022 free(buf);
2023 continue;
2024 }
2025
2026 free(buf);
2027 array_grow( &temp->u.map->systems ) = sys;
2028
2029 xmlNodePtr cur = node->children;
2030 do {
2031 xml_onlyNodes(cur);
2032
2033 if (xml_isNode(cur,"spob")) {
2034 buf = xml_get(cur);
2035 if ((buf != NULL) && ((spob = spob_get(buf)) != NULL))
2036 array_grow( &temp->u.map->spobs ) = spob;
2037 else
2038 WARN(_("Map '%s' has invalid spob '%s'"), temp->name, buf);
2039 }
2040 else if (xml_isNode(cur,"jump")) {
2041 buf = xml_get(cur);
2042 if ((buf != NULL) && ((jump = jump_get(xml_get(cur),
2043 temp->u.map->systems[array_size(temp->u.map->systems)-1] )) != NULL))
2044 array_grow( &temp->u.map->jumps ) = jump;
2045 else
2046 WARN(_("Map '%s' has invalid jump point '%s'"), temp->name, buf);
2047 }
2048 else
2049 WARN(_("Outfit '%s' has unknown node '%s'"),temp->name, cur->name);
2050 } while (xml_nextNode(cur));
2051 }
2052 else if (xml_isNode(node,"short_desc")) {
2053 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
2054 snprintf( temp->summary_raw, OUTFIT_SHORTDESC_MAX, "%s", xml_get(node) );
2055 }
2056 else if (xml_isNode(node,"all")) { /* Add everything to the map */
2057 system_stack = system_getAll();
2058 for (int i=0;i<array_size(system_stack);i++) {
2059 array_grow( &temp->u.map->systems ) = &system_stack[i];
2060 for (int j=0;j<array_size(system_stack[i].spobs);j++)
2061 array_grow( &temp->u.map->spobs ) = system_stack[i].spobs[j];
2062 for (int j=0;j<array_size(system_stack[i].jumps);j++)
2063 array_grow( &temp->u.map->jumps ) = &system_stack[i].jumps[j];
2064 }
2065 }
2066 else
2067 WARN(_("Outfit '%s' has unknown node '%s'"),temp->name, node->name);
2068 } while (xml_nextNode(node));
2069
2070 array_shrink( &temp->u.map->systems );
2071 array_shrink( &temp->u.map->spobs );
2072 array_shrink( &temp->u.map->jumps );
2073
2074 if (temp->summary_raw == NULL) {
2075 /* Set short description based on type. */
2076 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
2077 snprintf( temp->summary_raw, OUTFIT_SHORTDESC_MAX,
2078 "%s", _(outfit_getType(temp)) );
2079 }
2080
2081#define MELEMENT(o,s) \
2082if (o) WARN(_("Outfit '%s' missing/invalid '%s' element"), temp->name, s)
2084 MELEMENT(temp->mass!=0.,"cpu");
2085 MELEMENT(temp->cpu!=0.,"cpu");
2086#undef MELEMENT
2087}
2088
2095static void outfit_parseSLocalMap( Outfit *temp, const xmlNodePtr parent )
2097 xmlNodePtr node = parent->children;
2098
2099 temp->slot.type = OUTFIT_SLOT_NA;
2100 temp->slot.size = OUTFIT_SLOT_SIZE_NA;
2101
2102 do {
2103 xml_onlyNodes(node);
2104 xmlr_float(node,"spob_detect",temp->u.lmap.spob_detect);
2105 xmlr_float(node,"jump_detect",temp->u.lmap.jump_detect);
2106 WARN(_("Outfit '%s' has unknown node '%s'"),temp->name, node->name);
2107 } while (xml_nextNode(node));
2108
2109 temp->u.lmap.spob_detect = pow2( temp->u.lmap.spob_detect );
2110 temp->u.lmap.jump_detect = pow2( temp->u.lmap.jump_detect );
2111
2112 /* Set short description. */
2113 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
2114 snprintf( temp->summary_raw, OUTFIT_SHORTDESC_MAX,
2115 "%s",
2116 _(outfit_getType(temp)) );
2117
2118#define MELEMENT(o,s) \
2119if (o) WARN(_("Outfit '%s' missing/invalid '%s' element"), temp->name, s)
2121 MELEMENT(temp->mass!=0.,"cpu");
2122 MELEMENT(temp->cpu!=0.,"cpu");
2123#undef MELEMENT
2124}
2125
2132static void outfit_parseSGUI( Outfit *temp, const xmlNodePtr parent )
2134 xmlNodePtr node = parent->children;
2135
2136 temp->slot.type = OUTFIT_SLOT_NA;
2137 temp->slot.size = OUTFIT_SLOT_SIZE_NA;
2138
2139 do {
2140 xml_onlyNodes(node);
2141 xmlr_strd(node,"gui",temp->u.gui.gui);
2142 WARN(_("Outfit '%s' has unknown node '%s'"),temp->name, node->name);
2143 } while (xml_nextNode(node));
2144
2145 /* Set short description. */
2146 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
2147 snprintf( temp->summary_raw, OUTFIT_SHORTDESC_MAX,
2148 _("GUI (Graphical User Interface)") );
2149
2150#define MELEMENT(o,s) \
2151if (o) WARN(_("Outfit '%s' missing/invalid '%s' element"), temp->name, s)
2153 MELEMENT(temp->u.gui.gui==NULL,"gui");
2154 MELEMENT(temp->mass!=0.,"cpu");
2155 MELEMENT(temp->cpu!=0.,"cpu");
2156#undef MELEMENT
2157}
2158
2165static void outfit_parseSLicense( Outfit *temp, const xmlNodePtr parent )
2167 xmlNodePtr node = parent->children;
2168
2169 temp->slot.type = OUTFIT_SLOT_NA;
2170 temp->slot.size = OUTFIT_SLOT_SIZE_NA;
2171
2172 do {
2173 xml_onlyNodes(node);
2174 xmlr_strd(node,"provides",temp->u.lic.provides);
2175 WARN(_("Outfit '%s' has unknown node '%s'"),temp->name, node->name);
2176 } while (xml_nextNode(node));
2177
2178 if (temp->u.lic.provides==NULL)
2179 temp->u.lic.provides = strdup( temp->name );
2180
2181 if (license_stack == NULL)
2182 license_stack = array_create( char* );
2184
2185 /* Set short description. */
2186 temp->summary_raw = malloc( OUTFIT_SHORTDESC_MAX );
2187 snprintf( temp->summary_raw, OUTFIT_SHORTDESC_MAX,
2188 "%s",
2189 _(outfit_getType(temp)) );
2190
2191#define MELEMENT(o,s) \
2192if (o) WARN(_("Outfit '%s' missing/invalid '%s' element"), temp->name, s)
2194 MELEMENT(temp->mass!=0.,"cpu");
2195 MELEMENT(temp->cpu!=0.,"cpu");
2196#undef MELEMENT
2197}
2198
2206static int outfit_parse( Outfit* temp, const char* file )
2208 xmlNodePtr node, parent;
2209 char *prop, *desc_extra;
2210 const char *cprop;
2211 int group, l;
2212
2213 xmlDocPtr doc = xml_parsePhysFS( file );
2214 if (doc == NULL)
2215 return -1;
2216
2217 parent = doc->xmlChildrenNode; /* first outfit node */
2218 if (parent == NULL) {
2219 ERR( _("Malformed '%s' file: does not contain elements"), file);
2220 return -1;
2221 }
2222
2223 /* Clear data. */
2224 memset( temp, 0, sizeof(Outfit) );
2225 temp->filename = strdup( file );
2226 desc_extra = NULL;
2227
2228 /* Defaults. */
2229 temp->lua_env = LUA_NOREF;
2230 temp->lua_descextra = LUA_NOREF;
2231 temp->lua_onadd = LUA_NOREF;
2232 temp->lua_onremove = LUA_NOREF;
2233 temp->lua_init = LUA_NOREF;
2234 temp->lua_cleanup = LUA_NOREF;
2235 temp->lua_update = LUA_NOREF;
2236 temp->lua_ontoggle = LUA_NOREF;
2237 temp->lua_onhit = LUA_NOREF;
2238 temp->lua_outofenergy = LUA_NOREF;
2239 temp->lua_onshoot = LUA_NOREF;
2240 temp->lua_onstealth = LUA_NOREF;
2241 temp->lua_onscanned = LUA_NOREF;
2242 temp->lua_onscan = LUA_NOREF;
2243 temp->lua_cooldown = LUA_NOREF;
2244 temp->lua_land = LUA_NOREF;
2245 temp->lua_takeoff = LUA_NOREF;
2246 temp->lua_jumpin = LUA_NOREF;
2247 temp->lua_onimpact = LUA_NOREF;
2248 temp->lua_onmiss = LUA_NOREF;
2249 temp->lua_price = LUA_NOREF;
2250 temp->lua_buy = LUA_NOREF;
2251 temp->lua_sell = LUA_NOREF;
2252
2253 xmlr_attr_strd(parent,"name",temp->name);
2254 if (temp->name == NULL)
2255 WARN(_("Outfit '%s' has invalid or no name"), file);
2256
2257 node = parent->xmlChildrenNode;
2258
2259 do { /* load all the data */
2260
2261 /* Only handle nodes. */
2262 xml_onlyNodes(node);
2263
2264 if (xml_isNode(node,"general")) {
2265 xmlNodePtr cur = node->children;
2266 do {
2267 xml_onlyNodes(cur);
2268 xmlr_int(cur,"rarity",temp->rarity);
2269 xmlr_strd(cur,"license",temp->license);
2270 xmlr_strd(cur,"cond",temp->cond);
2271 xmlr_strd(cur,"condstr",temp->condstr);
2272 xmlr_float(cur,"mass",temp->mass);
2273 xmlr_float(cur,"cpu",temp->cpu);
2274 xmlr_long(cur,"price",temp->price);
2275 xmlr_strd(cur,"limit",temp->limit);
2276 xmlr_strd(cur,"description",temp->desc_raw);
2277 xmlr_strd(cur,"desc_extra",desc_extra);
2278 xmlr_strd(cur,"typename",temp->typename);
2279 xmlr_int(cur,"priority",temp->priority);
2280 if (xml_isNode(cur,"unique")) {
2281 outfit_setProp(temp, OUTFIT_PROP_UNIQUE);
2282 continue;
2283 }
2284 if (xml_isNode(cur,"shoot_dry")) {
2285 outfit_setProp(temp, OUTFIT_PROP_SHOOT_DRY);
2286 continue;
2287 }
2288 else if (xml_isNode(cur,"gfx_store")) {
2289 temp->gfx_store = xml_parseTexture( cur,
2290 OUTFIT_GFX_PATH"store/%s", 1, 1, OPENGL_TEX_MIPMAPS );
2291 continue;
2292 }
2293 else if (xml_isNode(cur,"gfx_overlays")) {
2294 xmlNodePtr ccur = cur->children;
2296 do {
2297 xml_onlyNodes(ccur);
2298 if (xml_isNode(ccur,"gfx_overlay"))
2300 xml_parseTexture( ccur, OVERLAY_GFX_PATH"%s", 1, 1, OPENGL_TEX_MIPMAPS ) );
2301 } while (xml_nextNode(ccur));
2302 continue;
2303 }
2304 else if (xml_isNode(cur,"slot")) {
2305 cprop = xml_get(cur);
2306 if (cprop == NULL)
2307 WARN(_("Outfit '%s' has an slot type invalid."), temp->name);
2308 else if (strcmp(cprop,"structure") == 0)
2309 temp->slot.type = OUTFIT_SLOT_STRUCTURE;
2310 else if (strcmp(cprop,"utility") == 0)
2311 temp->slot.type = OUTFIT_SLOT_UTILITY;
2312 else if (strcmp(cprop,"weapon") == 0)
2313 temp->slot.type = OUTFIT_SLOT_WEAPON;
2314 else if (strcmp(cprop,"intrinsic") == 0)
2315 temp->slot.type = OUTFIT_SLOT_NA;
2316 else
2317 WARN(_("Outfit '%s' has unknown slot type '%s'."), temp->name, cprop);
2318
2319 /* Property. */
2320 xmlr_attr_strd( cur, "prop", prop );
2321 if (prop != NULL)
2322 temp->slot.spid = sp_get( prop );
2323 free( prop );
2324 continue;
2325 }
2326 else if (xml_isNode(cur,"size")) {
2327 temp->slot.size = outfit_toSlotSize( xml_get(cur) );
2328 continue;
2329 }
2330 else if (xml_isNode(cur, "illegalto")) {
2331 xmlNodePtr ccur = cur->xmlChildrenNode;
2332 temp->illegaltoS = array_create( char* );
2333 do {
2334 xml_onlyNodes(ccur);
2335 if (xml_isNode(ccur, "faction")) {
2336 const char *s = xml_get(ccur);
2337 if (s==NULL)
2338 WARN(_("Empty faction string for outfit '%s' legality!"), temp->name);
2339 else
2340 array_push_back( &temp->illegaltoS, strdup(s) );
2341 }
2342 } while (xml_nextNode(ccur));
2343 if (array_size(temp->illegaltoS) <= 0 )
2344 WARN(_("Outfit '%s' has no factions defined in <illegalto> block!"), temp->name);
2345 continue;
2346 }
2347 WARN(_("Outfit '%s' has unknown general node '%s'"),temp->name, cur->name);
2348 } while (xml_nextNode(cur));
2349 continue;
2350 }
2351
2352 if (xml_isNode(node,"stats")) {
2353 xmlNodePtr cur = node->children;
2354 do {
2355 ShipStatList *ll;
2356 xml_onlyNodes(cur);
2357 /* Stats. */
2358 ll = ss_listFromXML( cur );
2359 if (ll != NULL) {
2360 ll->next = temp->stats;
2361 temp->stats = ll;
2362 continue;
2363 }
2364 WARN(_("Outfit '%s' has unknown node '%s'"), temp->name, cur->name);
2365 } while (xml_nextNode(cur));
2366 continue;
2367 }
2368
2369 /* Parse tags. */
2370 if (xml_isNode(node, "tags")) {
2371 xmlNodePtr cur = node->children;
2372 temp->tags = array_create( char* );
2373 do {
2374 xml_onlyNodes(cur);
2375 if (xml_isNode(cur, "tag")) {
2376 char *tmp = xml_get(cur);
2377 if (tmp != NULL)
2378 array_push_back( &temp->tags, strdup(tmp) );
2379 continue;
2380 }
2381 WARN(_("Outfit '%s' has unknown node in tags '%s'."), temp->name, cur->name );
2382 } while (xml_nextNode(cur));
2383 continue;
2384 }
2385
2386 if (xml_isNode(node,"specific")) { /* has to be processed separately */
2387 /* get the type */
2388 xmlr_attr_strd(node, "type", prop);
2389 if (prop == NULL)
2390 ERR(_("Outfit '%s' element 'specific' missing property 'type'"),temp->name);
2391 temp->type = outfit_strToOutfitType(prop);
2392 free(prop);
2393
2394 /* is secondary weapon? */
2395 xmlr_attr_strd(node, "secondary", prop);
2396 if (prop != NULL) {
2397 if ((int)atoi(prop))
2398 outfit_setProp(temp, OUTFIT_PROP_WEAP_SECONDARY);
2399 free(prop);
2400 }
2401
2402 /* Check for manually-defined group. */
2403 xmlr_attr_int_def(node, "group", group, -1);
2404 if (group != -1) {
2405 if (group > PILOT_WEAPON_SETS || group < 1) {
2406 WARN(_("Outfit '%s' has group '%d', should be in the 1-%d range"),
2407 temp->name, group, PILOT_WEAPON_SETS);
2408 }
2409
2410 temp->group = CLAMP(0, 9, group);
2411 }
2412
2413 /*
2414 * Parse type.
2415 */
2416 if (temp->type==OUTFIT_TYPE_NULL)
2417 WARN(_("Outfit '%s' is of type NONE"), temp->name);
2418 else if (outfit_isBolt(temp))
2419 outfit_parseSBolt( temp, node );
2420 else if (outfit_isBeam(temp))
2421 outfit_parseSBeam( temp, node );
2422 else if (outfit_isLauncher(temp))
2423 outfit_parseSLauncher( temp, node );
2424 else if (outfit_isMod(temp))
2425 outfit_parseSMod( temp, node );
2426 else if (outfit_isAfterburner(temp))
2427 outfit_parseSAfterburner( temp, node );
2428 else if (outfit_isFighterBay(temp))
2429 outfit_parseSFighterBay( temp, node );
2430 else if (outfit_isMap(temp)) {
2431 temp->u.map = malloc( sizeof(OutfitMapData_t) );
2432 temp->slot.type = OUTFIT_SLOT_NA;
2433 temp->slot.size = OUTFIT_SLOT_SIZE_NA;
2434 }
2435 else if (outfit_isLocalMap(temp))
2436 outfit_parseSLocalMap( temp, node );
2437 else if (outfit_isGUI(temp))
2438 outfit_parseSGUI( temp, node );
2439 else if (outfit_isLicense(temp))
2440 outfit_parseSLicense( temp, node );
2441
2442 /* We add the ship stats to the description here. */
2443 if (temp->summary_raw != NULL) {
2444 l = strlen(temp->summary_raw);
2445 ss_statsListDesc( temp->stats, &temp->summary_raw[l], OUTFIT_SHORTDESC_MAX-l, 1 );
2446 /* Add extra description task if available. */
2447 if (desc_extra != NULL) {
2448 l = strlen(temp->summary_raw);
2449 snprintf( &temp->summary_raw[l], OUTFIT_SHORTDESC_MAX-l, "\n%s", _(desc_extra) );
2450 free( desc_extra );
2451 desc_extra = NULL;
2452 }
2453 }
2454
2455 continue;
2456 }
2457 WARN(_("Outfit '%s' has unknown node '%s'"),temp->name, node->name);
2458 } while (xml_nextNode(node));
2459
2460#define MELEMENT(o,s) \
2461if (o) WARN( _("Outfit '%s' missing/invalid '%s' element"), temp->name, s)
2462 MELEMENT(temp->name==NULL,"name");
2463 MELEMENT(temp->slot.type==OUTFIT_SLOT_NULL,"slot");
2464 MELEMENT((temp->slot.type!=OUTFIT_SLOT_NA) && (temp->slot.size==OUTFIT_SLOT_SIZE_NA),"size");
2465 MELEMENT(temp->gfx_store==NULL,"gfx_store");
2466 /*MELEMENT(temp->mass==0,"mass"); Not really needed */
2467 MELEMENT(temp->type==0,"type");
2468 /*MELEMENT(temp->price==0,"price");*/
2469 MELEMENT(temp->desc_raw==NULL,"description");
2470 MELEMENT((temp->cond!=NULL) && (temp->condstr==NULL), "condstr");
2471 MELEMENT((temp->cond==NULL) && (temp->condstr!=NULL), "cond");
2472#undef MELEMENT
2473
2474 xmlFreeDoc(doc);
2475
2476 return 0;
2477}
2478
2485static int outfit_loadDir( char *dir )
2487 char **outfit_files = ndata_listRecursive( dir );
2488 for (int i=0; i < array_size( outfit_files ); i++) {
2489 if (ndata_matchExt( outfit_files[i], "xml" )) {
2490 Outfit o;
2491 int ret = outfit_parse( &o, outfit_files[i] );
2492 if (ret == 0)
2494
2495 /* Render if necessary. */
2497 }
2498 free( outfit_files[i] );
2499 }
2500 array_free( outfit_files );
2501
2502 return 0;
2503}
2504
2510int outfit_load (void)
2512 int noutfits;
2513 Uint32 time = SDL_GetTicks();
2514
2515 /* First pass, Loads up all outfits, without filling ammunition and the likes. */
2517 outfit_loadDir( OUTFIT_DATA_PATH );
2519 noutfits = array_size(outfit_stack);
2520 /* Sort up licenses. */
2521 qsort( outfit_stack, noutfits, sizeof(Outfit), outfit_cmp );
2522 if (license_stack != NULL)
2523 qsort( license_stack, array_size(license_stack), sizeof(char*), strsort );
2524
2525 /* Second pass. */
2526 for (int i=0; i<noutfits; i++) {
2527 Outfit *o = &outfit_stack[i];
2528
2529 if (o->lua_file==NULL)
2530 continue;
2531 nlua_env env;
2532 size_t sz;
2533 char *dat = ndata_read( o->lua_file, &sz );
2534 if (dat==NULL) {
2535 WARN(_("Outfit '%s' failed to read Lua '%s'!"), o->name, o->lua_file );
2536 continue;
2537 }
2538
2539 env = nlua_newEnv();
2540 o->lua_env = env;
2541 /* TODO limit libraries here. */
2542 nlua_loadStandard( env );
2543 nlua_loadGFX( env );
2544 nlua_loadPilotOutfit( env );
2545 nlua_loadCamera( env );
2546 nlua_loadHook( env );
2547
2548 /* Run code. */
2549 if (nlua_dobufenv( env, dat, sz, o->lua_file ) != 0) {
2550 WARN(_("Outfit '%s' Lua error:\n%s"), o->name, lua_tostring(naevL,-1));
2551 lua_pop(naevL,1);
2552 nlua_freeEnv( o->lua_env );
2553 free( dat );
2554 o->lua_env = LUA_NOREF;
2555 continue;
2556 }
2557 free( dat );
2558
2559 /* Check functions as necessary. */
2560 o->lua_descextra = nlua_refenvtype( env, "descextra",LUA_TFUNCTION );
2561 o->lua_onadd = nlua_refenvtype( env, "onadd", LUA_TFUNCTION );
2562 o->lua_onremove = nlua_refenvtype( env, "onremove", LUA_TFUNCTION );
2563 o->lua_init = nlua_refenvtype( env, "init", LUA_TFUNCTION );
2564 o->lua_cleanup = nlua_refenvtype( env, "cleanup", LUA_TFUNCTION );
2565 o->lua_update = nlua_refenvtype( env, "update", LUA_TFUNCTION );
2566 o->lua_ontoggle = nlua_refenvtype( env, "ontoggle", LUA_TFUNCTION );
2567 o->lua_onhit = nlua_refenvtype( env, "onhit", LUA_TFUNCTION );
2568 o->lua_outofenergy= nlua_refenvtype( env, "outofenergy",LUA_TFUNCTION );
2569 o->lua_onshoot = nlua_refenvtype( env, "onshoot", LUA_TFUNCTION );
2570 o->lua_onstealth = nlua_refenvtype( env, "onstealth",LUA_TFUNCTION );
2571 o->lua_onscanned = nlua_refenvtype( env, "onscanned",LUA_TFUNCTION );
2572 o->lua_onscan = nlua_refenvtype( env, "onscan", LUA_TFUNCTION );
2573 o->lua_cooldown = nlua_refenvtype( env, "cooldown", LUA_TFUNCTION );
2574 o->lua_land = nlua_refenvtype( env, "land", LUA_TFUNCTION );
2575 o->lua_takeoff = nlua_refenvtype( env, "takeoff", LUA_TFUNCTION );
2576 o->lua_jumpin = nlua_refenvtype( env, "jumpin", LUA_TFUNCTION );
2577 o->lua_onimpact = nlua_refenvtype( env, "onimpact", LUA_TFUNCTION );
2578 o->lua_onmiss = nlua_refenvtype( env, "onmiss", LUA_TFUNCTION );
2579 o->lua_price = nlua_refenvtype( env, "price", LUA_TFUNCTION );
2580 o->lua_buy = nlua_refenvtype( env, "buy", LUA_TFUNCTION );
2581 o->lua_sell = nlua_refenvtype( env, "sell", LUA_TFUNCTION );
2582
2583 if (outfit_isMod(o)) {
2584 nlua_getenv( naevL, env, "notactive" );
2585 o->u.mod.active = 1;
2586 if (lua_toboolean(naevL,-1)) {
2587 o->u.mod.active = 0;
2588 if (o->lua_ontoggle != LUA_NOREF)
2589 WARN(_("Outfit '%s' has 'ontoggle' Lua function defined, but is set as 'notactive'!"),o->name);
2590 }
2591 lua_pop(naevL,1);
2592 }
2593 }
2594
2595#ifdef DEBUGGING
2596 char **outfit_names = malloc( noutfits * sizeof(char*) );
2597 int start;
2598
2599 for (int i=0; i<noutfits; i++)
2600 outfit_names[i] = outfit_stack[i].name;
2601
2602 qsort( outfit_names, noutfits, sizeof(char*), strsort );
2603 for (int i=0; i<(noutfits - 1); i++) {
2604 start = i;
2605 while (strcmp(outfit_names[i], outfit_names[i+1]) == 0)
2606 i++;
2607
2608 if (i == start)
2609 continue;
2610
2611 WARN( n_( "Name collision! %d outfit is named '%s'", "Name collision! %d outfits are named '%s'",
2612 i + 1 - start ),
2613 i + 1 - start, outfit_names[ start ] );
2614 }
2615 free(outfit_names);
2616#endif /* DEBUGGING */
2617
2618 if (conf.devmode) {
2619 time = SDL_GetTicks() - time;
2620 DEBUG( n_( "Loaded %d Outfit in %.3f s", "Loaded %d Outfits in %.3f s", noutfits ), noutfits, time/1000. );
2621 }
2622 else
2623 DEBUG( n_( "Loaded %d Outfit", "Loaded %d Outfits", noutfits ), noutfits );
2624
2625 return 0;
2626}
2627
2633int outfit_loadPost (void)
2635 for (int i=0; i<array_size(outfit_stack); i++) {
2636 Outfit *o = &outfit_stack[i];
2637
2638 if (outfit_isFighterBay(o) && (o->u.bay.ship!=NULL) && ship_get(o->u.bay.ship)==NULL)
2639 WARN(_("Fighter Bay Outfit '%s' has not found ship '%s'!"),o->name,o->u.bay.ship);
2640
2641 /* Add illegality as necessary. */
2642 if (array_size(o->illegaltoS) > 0) {
2644 for (int j=0; j<array_size(o->illegaltoS); j++) {
2645 int f = faction_get( o->illegaltoS[j] );
2646 array_push_back( &o->illegalto, f );
2647 free( o->illegaltoS[j] );
2648 }
2649 array_free( o->illegaltoS );
2650 o->illegaltoS = NULL;
2651
2652 int l = strlen( o->summary_raw );
2653 SDESC_ADD( l, o, _("\n#rIllegal to:#0") );
2654 for (int j=0; j<array_size(o->illegalto); j++)
2655 SDESC_ADD( l, o, _("\n#r- %s#0"), _(faction_name(o->illegalto[j])) );
2656 }
2657
2658 /* Handle initializing module stuff. */
2659 if (o->lua_env != LUA_NOREF) {
2660 nlua_getenv( naevL, o->lua_env, "onload" );
2661 if (lua_isnil(naevL,-1))
2662 lua_pop(naevL,1);
2663 else {
2664 lua_pushoutfit( naevL, o );
2665 if (nlua_pcall( o->lua_env, 1, 0 )) {
2666 WARN(_("Outfit '%s' lua load error -> 'load':\n%s"), o->name, lua_tostring(naevL,-1));
2667 lua_pop(naevL,1);
2668 }
2669 }
2670 }
2671
2672 /* Make sure licenses are valid. */
2673 if ((o->license!=NULL) && !outfit_licenseExists( o->license ))
2674 WARN(_("Outfit '%s' has inexistent license requirement '%s'!"), o->name, o->license);
2675 }
2676
2677 return 0;
2678}
2679
2684int outfit_mapParse (void)
2686 for (int i=0; i<array_size(outfit_stack); i++) {
2687 xmlDocPtr doc;
2688 xmlNodePtr node, cur;
2689 Outfit *o = &outfit_stack[i];
2690
2691 if (!outfit_isMap(o))
2692 continue;
2693
2694 doc = xml_parsePhysFS( o->filename );
2695 if (doc == NULL)
2696 continue;
2697
2698 node = doc->xmlChildrenNode; /* first system node */
2699 if (node == NULL) {
2700 WARN( _("Malformed '%s' file: does not contain elements"), o->filename );
2701 xmlFreeDoc(doc);
2702 continue;
2703 }
2704
2705 cur = node->xmlChildrenNode;
2706 do { /* load all the data */
2707 /* Only handle nodes. */
2708 xml_onlyNodes(cur);
2709
2710 if (xml_isNode(cur,"specific"))
2711 outfit_parseSMap(o, cur);
2712
2713 } while (xml_nextNode(cur));
2714
2715 /* Clean up. */
2716 xmlFreeDoc(doc);
2717 }
2718
2719 return 0;
2720}
2721
2725glTexture* rarity_texture( int rarity )
2727 char s[PATH_MAX];
2728 snprintf( s, sizeof(s), OVERLAY_GFX_PATH"rarity_%d.webp", rarity );
2729 return gl_newImage( s, OPENGL_TEX_MIPMAPS );
2730}
2731
2735int outfit_checkIllegal( const Outfit* o, int fct )
2737 for (int i=0; i<array_size(o->illegalto); i++) {
2738 if (o->illegalto[i] == fct)
2739 return 1;
2740 }
2741 return 0;
2742}
2743
2747int outfit_licenseExists( const char *name )
2749 if (license_stack==NULL)
2750 return 0;
2751 char *lic = bsearch( &name, license_stack, array_size(license_stack), sizeof(char*), strsort );
2752 return (lic!=NULL);
2753}
2754
2758void outfit_free (void)
2760 for (int i=0; i < array_size(outfit_stack); i++) {
2761 Outfit *o = &outfit_stack[i];
2762
2763 free( o->filename );
2764
2765 /* Free graphics */
2766 gl_freeTexture( (glTexture*) outfit_gfx(o)); /*< This is horrible and I should be ashamed. */
2767
2768 /* Free slot. */
2769 outfit_freeSlot( &o->slot );
2770
2771 /* Free stats. */
2772 ss_free( o->stats );
2773
2774 /* Free illegality. */
2775 array_free( o->illegalto );
2776
2777 if (outfit_isLauncher(o)) {
2778 /* Free collision polygons. */
2779 for (int j=0; j<array_size(o->u.lau.polygon); j++) {
2780 free(o->u.lau.polygon[j].x);
2781 free(o->u.lau.polygon[j].y);
2782 }
2783 array_free(o->u.lau.polygon);
2784 }
2785 /* Type specific. */
2786 else if (outfit_isBolt(o)) {
2788 /* Free collision polygons. */
2789 for (int j=0; j<array_size(o->u.blt.polygon); j++) {
2790 free(o->u.blt.polygon[j].x);
2791 free(o->u.blt.polygon[j].y);
2792 }
2793 array_free(o->u.blt.polygon);
2794 }
2795 else if (outfit_isFighterBay(o))
2796 free(o->u.bay.ship);
2797 else if (outfit_isGUI(o))
2798 free(o->u.gui.gui);
2799 else if (outfit_isLicense(o))
2800 free(o->u.lic.provides);
2801 else if (outfit_isMap(o)) {
2802 array_free( o->u.map->systems );
2803 array_free( o->u.map->spobs );
2804 array_free( o->u.map->jumps );
2805 free( o->u.map );
2806 }
2807
2808 /* Lua. */
2809 nlua_freeEnv( o->lua_env );
2810 o->lua_env = LUA_NOREF;
2811 free(o->lua_file);
2812
2813 /* strings */
2814 free(o->typename);
2815 free(o->desc_raw);
2816 free(o->limit);
2817 free(o->summary_raw);
2818 free(o->license);
2819 free(o->cond);
2820 free(o->condstr);
2821 free(o->name);
2823 for (int j=0; j<array_size(o->gfx_overlays); j++)
2826
2827 /* Free tags. */
2828 for (int j=0; j<array_size(o->tags); j++)
2829 free(o->tags[j]);
2830 array_free(o->tags);
2831 }
2832
2835}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition: array.h:158
#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:168
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition: array.h:119
#define array_shrink(ptr_array)
Shrinks memory to fit only ‘size’ elements.
Definition: array.h:149
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition: array.h:129
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition: array.h:93
void LoadPolygon(CollPoly *polygon, xmlNodePtr node)
Loads a polygon from an xml node.
Definition: collision.c:32
int dtype_raw(int type, double *shield, double *armour, double *knockback)
Gets the raw modulation stats of a damage type.
Definition: damagetype.c:233
int dtype_get(const char *name)
Gets the id of a dtype based on name.
Definition: damagetype.c:144
const char * dtype_damageTypeToStr(int type)
Gets the human readable string from damage type.
Definition: damagetype.c:168
const char * faction_name(int f)
Gets a factions "real" (internal) name.
Definition: faction.c:304
int faction_get(const char *name)
Gets a faction ID by name.
Definition: faction.c:182
void naev_renderLoadscreen(void)
Renders the loadscreen if necessary.
Definition: naev.c:549
Header file with generic functions and naev-specifics.
#define CLAMP(a, b, x)
Definition: naev.h:41
#define pow2(x)
Definition: naev.h:46
#define PATH_MAX
Definition: naev.h:50
void * ndata_read(const char *path, size_t *filesize)
Reads a file from the ndata (will be NUL terminated).
Definition: ndata.c:154
int ndata_matchExt(const char *path, const char *ext)
Sees if a file matches an extension.
Definition: ndata.c:366
char ** ndata_listRecursive(const char *path)
Lists all the visible files in a directory, at any depth.
Definition: ndata.c:232
static char buf[NEWS_MAX_LENGTH]
Definition: news.c:45
int nlua_loadStandard(nlua_env env)
Loads the standard Naev Lua API.
Definition: nlua.c:760
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:873
int nlua_loadCamera(nlua_env env)
Loads the camera library.
Definition: nlua_camera.c:47
int nlua_loadGFX(nlua_env env)
Loads the graphics library.
Definition: nlua_gfx.c:97
int nlua_loadHook(nlua_env env)
Loads the hook Lua library.
Definition: nlua_hook.c:121
const Outfit ** lua_pushoutfit(lua_State *L, const Outfit *outfit)
Pushes a outfit on the stack.
Definition: nlua_outfit.c:160
int nlua_loadPilotOutfit(nlua_env env)
Loads the pilot outfit library.
int asprintf(char **strp, const char *fmt,...)
Like sprintf(), but it allocates a large-enough string and returns the pointer in the first argument....
Definition: nstring.c:161
int strsort(const void *p1, const void *p2)
Sort function for sorting strings with qsort().
Definition: nstring.c:108
char * strcasestr(const char *haystack, const char *needle)
Finds a string inside another string case insensitively.
Definition: nstring.c:68
int scnprintf(char *text, size_t maxlen, const char *fmt,...)
Like snprintf(), but returns the number of characters ACTUALLY "printed" into the buffer....
Definition: nstring.c:178
const char * num2strU(double n, int decimals)
Unsafe version of num2str that uses an internal buffer. Every call overwrites the return value.
Definition: nstring.c:230
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:29
xmlDocPtr xml_parsePhysFS(const char *filename)
Analogous to xmlParseMemory/xmlParseFile.
Definition: nxml.c:75
glTexture * gl_newImage(const char *path, const unsigned int flags)
Loads an image as a texture.
Definition: opengl_tex.c:570
void gl_freeTexture(glTexture *texture)
Frees a texture.
Definition: opengl_tex.c:755
int outfit_isSecondary(const Outfit *o)
Checks if outfit has the secondary flag set.
Definition: outfit.c:596
const char * outfit_description(const Outfit *o)
Gets the description of an outfit.
Definition: outfit.c:955
static void outfit_parseSMap(Outfit *temp, const xmlNodePtr parent)
Parses the map tidbits of the outfit.
Definition: outfit.c:1998
static int outfit_loadPLG(Outfit *temp, const char *buf, unsigned int bolt)
Loads the collision polygon for a bolt outfit.
Definition: outfit.c:1152
double outfit_speed(const Outfit *o)
Gets the outfit's speed.
Definition: outfit.c:742
static Outfit * outfit_stack
Definition: outfit.c:55
double outfit_trackmin(const Outfit *o)
Gets the outfit's minimal tracking.
Definition: outfit.c:782
static void outfit_parseSLauncher(Outfit *temp, const xmlNodePtr parent)
Parses the specific area for a launcher and loads it into Outfit.
Definition: outfit.c:1567
static int outfit_parseDamage(Damage *dmg, xmlNodePtr node)
Parses a damage node.
Definition: outfit.c:1107
int outfit_isBeam(const Outfit *o)
Checks if outfit is a beam type weapon.
Definition: outfit.c:488
const char * outfit_summary(const Outfit *o, int withname)
Gets the summary of an outfit.
Definition: outfit.c:969
const Outfit * outfit_getAll(void)
Gets the array (array.h) of all outfits.
Definition: outfit.c:141
double outfit_cpu(const Outfit *o)
Gets the outfit's cpu usage.
Definition: outfit.c:703
static void outfit_parseSAfterburner(Outfit *temp, const xmlNodePtr parent)
Parses the afterburner tidbits of the outfit.
Definition: outfit.c:1854
int outfit_isActive(const Outfit *o)
Checks if outfit is an active outfit.
Definition: outfit.c:434
int outfit_soundHit(const Outfit *o)
Gets the outfit's hit sound.
Definition: outfit.c:829
static int outfit_parse(Outfit *temp, const char *file)
Parses and returns Outfit from parent node.
Definition: outfit.c:2207
const Outfit * outfit_get(const char *name)
Gets an outfit by name.
Definition: outfit.c:118
int outfit_isLauncher(const Outfit *o)
Checks if outfit is a weapon launcher.
Definition: outfit.c:498
int outfit_isSeeker(const Outfit *o)
Checks if outfit is a seeking weapon.
Definition: outfit.c:508
static OutfitType outfit_strToOutfitType(char *buf)
Gets the outfit type from a human readable string.
Definition: outfit.c:1061
OutfitSlotSize outfit_toSlotSize(const char *s)
Gets the outfit slot size from a human readable string.
Definition: outfit.c:411
int outfit_miningRarity(const Outfit *o)
Gets the maximum rarity the outfit can mine up to.
Definition: outfit.c:806
static void outfit_parseSLicense(Outfit *temp, const xmlNodePtr parent)
Parses the license tidbits of the outfit.
Definition: outfit.c:2166
int outfit_isLocalMap(const Outfit *o)
Checks if outfit is a local space map.
Definition: outfit.c:568
static void outfit_parseSFighterBay(Outfit *temp, const xmlNodePtr parent)
Parses the fighter bay tidbits of the outfit.
Definition: outfit.c:1945
char outfit_slotTypeColourFont(const OutfitSlot *os)
Gets a font colour character that roughly matches an outfit slot type colour.
Definition: outfit.c:375
int outfit_isToggleable(const Outfit *o)
Checks if outfit can be toggled.
Definition: outfit.c:450
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:1227
int outfit_compareTech(const void *outfit1, const void *outfit2)
Function meant for use with C89, C99 algorithm qsort().
Definition: outfit.c:195
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:394
const Outfit * outfit_getW(const char *name)
Gets an outfit by name without warning on no-find.
Definition: outfit.c:132
#define O_CMP(s, t)
Definition: outfit.c:1052
const char * outfit_getTypeBroad(const Outfit *o)
Gets the outfit's broad type.
Definition: outfit.c:910
static void outfit_parseSMod(Outfit *temp, const xmlNodePtr parent)
Parses the modification tidbits of the outfit.
Definition: outfit.c:1802
int outfit_fitsSlot(const Outfit *o, const OutfitSlot *s)
Checks to see if an outfit fits a slot.
Definition: outfit.c:981
int outfit_checkIllegal(const Outfit *o, int fct)
Checks illegality of an outfit to a faction.
Definition: outfit.c:2736
int outfit_isFighterBay(const Outfit *o)
Checks if outfit is a fighter bay.
Definition: outfit.c:550
int outfit_isAfterburner(const Outfit *o)
Checks if outfit is an afterburner.
Definition: outfit.c:541
double outfit_range(const Outfit *o)
Gets the outfit's range.
Definition: outfit.c:711
int outfit_isMap(const Outfit *o)
Checks if outfit is a space map.
Definition: outfit.c:559
int outfit_spfxShield(const Outfit *o)
Gets the outfit's sound effect.
Definition: outfit.c:636
int outfit_isTurret(const Outfit *o)
Checks if outfit is a turret class weapon.
Definition: outfit.c:521
double outfit_ammoMass(const Outfit *o)
Gets the outfit's ammunition mass.
Definition: outfit.c:840
int outfit_isForward(const Outfit *o)
Checks if outfit is a fixed mounted weapon.
Definition: outfit.c:468
const CollPoly * outfit_plg(const Outfit *o)
Gets the outfit's collision polygon.
Definition: outfit.c:615
static void sdesc_miningRarity(int *l, Outfit *temp, int rarity)
Adds a small blurb about rarity mining.
Definition: outfit.c:1085
int outfit_isLicense(const Outfit *o)
Checks if outfit is a license.
Definition: outfit.c:577
static void outfit_parseSGUI(Outfit *temp, const xmlNodePtr parent)
Parses the GUI tidbits of the outfit.
Definition: outfit.c:2133
char outfit_slotSizeColourFont(const OutfitSlot *os)
Gets a font colour character that roughly matches an outfit slot size colour.
Definition: outfit.c:358
int outfit_amount(const Outfit *o)
Gets the amount an outfit can hold.
Definition: outfit.c:670
const glTexture * outfit_gfx(const Outfit *o)
Gets the outfit's graphic effect.
Definition: outfit.c:605
int outfit_load(void)
Loads all the outfits.
Definition: outfit.c:2511
const char * slotName(const OutfitSlotType type)
Definition: outfit.c:287
double outfit_spin(const Outfit *o)
Gets the outfit's animation spin.
Definition: outfit.c:771
int outfit_mapParse(void)
Parses all the maps.
Definition: outfit.c:2685
const char * outfit_getType(const Outfit *o)
Gets the outfit's specific type.
Definition: outfit.c:879
double outfit_heat(const Outfit *o)
Gets the outfit's heat generation.
Definition: outfit.c:692
double outfit_trackmax(const Outfit *o)
Gets the outfit's minimal tracking.
Definition: outfit.c:794
static void outfit_parseSBeam(Outfit *temp, const xmlNodePtr parent)
Parses the beam weapon specifics of an outfit.
Definition: outfit.c:1417
static char ** license_stack
Definition: outfit.c:56
int outfit_isMod(const Outfit *o)
Checks if outfit is a ship modification.
Definition: outfit.c:532
int outfit_isGUI(const Outfit *o)
Checks if outfit is a GUI.
Definition: outfit.c:586
int outfit_sound(const Outfit *o)
Gets the outfit's sound.
Definition: outfit.c:818
int outfit_spfxArmour(const Outfit *o)
Gets the outfit's sound effect.
Definition: outfit.c:625
#define OUTFIT_SHORTDESC_MAX
Definition: outfit.c:50
const char * slotSize(const OutfitSlotSize o)
Gets the slot size as a string.
Definition: outfit.c:308
const Damage * outfit_damage(const Outfit *o)
Gets the outfit's damage.
Definition: outfit.c:647
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:160
static void outfit_parseSLocalMap(Outfit *temp, const xmlNodePtr parent)
Parses the map tidbits of the outfit.
Definition: outfit.c:2096
double outfit_duration(const Outfit *o)
Gets the outfit's duration.
Definition: outfit.c:851
double outfit_cooldown(const Outfit *o)
Gets the outfit's cooldown.
Definition: outfit.c:866
const char * outfit_slotName(const Outfit *o)
Gets the name of the slot type of an outfit.
Definition: outfit.c:279
int outfit_isBolt(const Outfit *o)
Checks if outfit is bolt type weapon.
Definition: outfit.c:478
const char * outfit_slotSize(const Outfit *o)
Gets the name of the slot size of an outfit.
Definition: outfit.c:330
void outfit_free(void)
Frees the outfit stack.
Definition: outfit.c:2759
#define outfit_setProp(o, p)
Definition: outfit.c:46
const char * outfit_existsCase(const char *name)
Checks to see if an outfit exists matching name (case insensitive).
Definition: outfit.c:149
static int outfit_loadDir(char *dir)
Loads all the files in a directory.
Definition: outfit.c:2486
double outfit_energy(const Outfit *o)
Gets the outfit's energy usage.
Definition: outfit.c:681
int outfit_licenseExists(const char *name)
Checks to see if a license exists.
Definition: outfit.c:2748
void outfit_freeSlot(OutfitSlot *s)
Frees an outfit slot.
Definition: outfit.c:1047
const char * outfit_getAmmoAI(const Outfit *o)
Gets a human-readable string describing an ammo outfit's AI.
Definition: outfit.c:931
glTexture * rarity_texture(int rarity)
Definition: outfit.c:2726
const glColour * outfit_slotSizeColour(const OutfitSlot *os)
Gets the slot size colour for an outfit slot.
Definition: outfit.c:341
double outfit_delay(const Outfit *o)
Gets the outfit's delay.
Definition: outfit.c:658
int outfit_fitsSlotType(const Outfit *o, const OutfitSlot *s)
Checks to see if an outfit fits a slot type (ignoring size).
Definition: outfit.c:1025
int outfit_loadPost(void)
Loads all the outfits legality.
Definition: outfit.c:2634
double pilot_heatCalcOutfitC(const Outfit *o)
Calculates the thermal mass of an outfit.
Definition: pilot_heat.c:68
double pilot_heatCalcOutfitArea(const Outfit *o)
Calculates the effective transfer area of an outfit.
Definition: pilot_heat.c:80
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.
static cholmod_common C
Definition: safelanes.c:92
const Ship * ship_get(const char *name)
Gets a ship based on its name.
Definition: ship.c:73
int ss_statsListDesc(const ShipStatList *ll, char *buf, int len, int newline)
Writes the ship statistics description.
Definition: shipstats.c:699
void ss_free(ShipStatList *ll)
Frees a list of ship stats.
Definition: shipstats.c:807
ShipStatList * ss_listFromXML(xmlNodePtr node)
Creates a shipstat list element from an xml node.
Definition: shipstats.c:209
unsigned int sp_get(const char *name)
Gets the id of a slot property.
Definition: slots.c:124
int sp_required(unsigned int spid)
Gets whether or not a slot property is required.
Definition: slots.c:170
int sound_disabled
Definition: sound.c:133
int sound_get(const char *name)
Gets the buffer to sound of name.
Definition: sound.c:764
Spob * spob_get(const char *spobname)
Gets a spob based on its name.
Definition: space.c:1006
StarSystem * system_getAll(void)
Gets an array (array.h) of all star systems.
Definition: space.c:847
StarSystem * system_get(const char *sysname)
Get the system from its name.
Definition: space.c:914
JumpPoint * jump_get(const char *jumpname, const StarSystem *sys)
Gets a jump point based on its target and system.
Definition: space.c:1174
const TrailSpec * trailSpec_get(const char *name)
Gets a trail spec by name.
Definition: spfx.c:1268
int spfx_get(char *name)
Gets the id of an spfx based on name.
Definition: spfx.c:333
Represents a polygon used for collision detection.
Definition: collision.h:13
float * x
Definition: collision.h:14
float * y
Definition: collision.h:15
Core damage that an outfit does.
Definition: outfit.h:111
int type
Definition: outfit.h:112
double disable
Definition: outfit.h:115
double penetration
Definition: outfit.h:113
double damage
Definition: outfit.h:114
double energy
Definition: outfit.h:164
double heatup
Definition: outfit.h:166
int sound_warmup
Definition: outfit.h:177
double duration
Definition: outfit.h:158
int spfx_armour
Definition: outfit.h:175
double warmup
Definition: outfit.h:157
int mining_rarity
Definition: outfit.h:169
Damage dmg
Definition: outfit.h:165
GLuint shader
Definition: outfit.h:174
double min_duration
Definition: outfit.h:159
double turn
Definition: outfit.h:163
double heat
Definition: outfit.h:167
double range
Definition: outfit.h:162
int sound_off
Definition: outfit.h:179
double swivel
Definition: outfit.h:168
glColour colour
Definition: outfit.h:172
GLfloat width
Definition: outfit.h:173
int spfx_shield
Definition: outfit.h:176
double delay
Definition: outfit.h:156
glTexture * gfx_end
Definition: outfit.h:140
int spfx_shield
Definition: outfit.h:145
double dispersion
Definition: outfit.h:133
double heat
Definition: outfit.h:129
double trackmin
Definition: outfit.h:130
double falloff
Definition: outfit.h:125
double range
Definition: outfit.h:124
double delay
Definition: outfit.h:122
double swivel
Definition: outfit.h:132
CollPoly * polygon
Definition: outfit.h:148
int sound_hit
Definition: outfit.h:143
double heatup
Definition: outfit.h:128
double spin
Definition: outfit.h:141
Damage dmg
Definition: outfit.h:127
double trackmax
Definition: outfit.h:131
double speed
Definition: outfit.h:123
int mining_rarity
Definition: outfit.h:136
double energy
Definition: outfit.h:126
glTexture * gfx_space
Definition: outfit.h:139
int spfx_armour
Definition: outfit.h:144
double speed_dispersion
Definition: outfit.h:134
double reload_time
Definition: outfit.h:271
char * gui
Definition: outfit.h:291
double trackmax
Definition: outfit.h:196
CollPoly * polygon
Definition: outfit.h:226
double iflockon
Definition: outfit.h:194
double speed_max
Definition: outfit.h:210
double dispersion
Definition: outfit.h:199
double trail_x_offset
Definition: outfit.h:223
const TrailSpec * trail_spec
Definition: outfit.h:222
double reload_time
Definition: outfit.h:190
double trackmin
Definition: outfit.h:195
glTexture * gfx_space
Definition: outfit.h:216
double duration
Definition: outfit.h:205
OutfitAmmoAI ai
Definition: outfit.h:207
double ammo_mass
Definition: outfit.h:204
char * provides
Definition: outfit.h:298
double jump_detect
Definition: outfit.h:283
double spob_detect
Definition: outfit.h:284
Pilot slot that can contain outfits.
Definition: outfit.h:101
OutfitSlotSize size
Definition: outfit.h:105
int exclusive
Definition: outfit.h:103
unsigned int spid
Definition: outfit.h:102
OutfitSlotType type
Definition: outfit.h:104
A ship outfit, depends radically on the type.
Definition: outfit.h:304
char ** tags
Definition: outfit.h:338
unsigned int properties
Definition: outfit.h:331
int lua_ontoggle
Definition: outfit.h:349
char * lua_file
Definition: outfit.h:341
credits_t price
Definition: outfit.h:322
int lua_onscan
Definition: outfit.h:355
char * limit
Definition: outfit.h:317
int lua_jumpin
Definition: outfit.h:359
char * cond
Definition: outfit.h:313
double cpu
Definition: outfit.h:316
OutfitLauncherData lau
Definition: outfit.h:373
int lua_cooldown
Definition: outfit.h:356
char * desc_raw
Definition: outfit.h:323
OutfitBeamData bem
Definition: outfit.h:372
int lua_buy
Definition: outfit.h:365
OutfitBoltData blt
Definition: outfit.h:371
int lua_takeoff
Definition: outfit.h:358
OutfitType type
Definition: outfit.h:369
int lua_onimpact
Definition: outfit.h:361
int lua_outofenergy
Definition: outfit.h:351
int lua_land
Definition: outfit.h:357
int lua_onscanned
Definition: outfit.h:354
int lua_update
Definition: outfit.h:348
int lua_onmiss
Definition: outfit.h:362
glTexture * gfx_store
Definition: outfit.h:328
int rarity
Definition: outfit.h:307
int lua_descextra
Definition: outfit.h:343
int lua_price
Definition: outfit.h:364
OutfitGUIData gui
Definition: outfit.h:379
unsigned int group
Definition: outfit.h:332
char * condstr
Definition: outfit.h:314
char ** illegaltoS
Definition: outfit.h:319
char * filename
Definition: outfit.h:308
OutfitModificationData mod
Definition: outfit.h:374
OutfitSlot slot
Definition: outfit.h:311
int lua_onhit
Definition: outfit.h:350
char * summary_raw
Definition: outfit.h:324
OutfitAfterburnerData afb
Definition: outfit.h:375
int * illegalto
Definition: outfit.h:318
char * typename
Definition: outfit.h:306
int lua_onshoot
Definition: outfit.h:352
OutfitFighterBayData bay
Definition: outfit.h:376
int priority
Definition: outfit.h:326
int lua_cleanup
Definition: outfit.h:347
int lua_sell
Definition: outfit.h:366
ShipStatList * stats
Definition: outfit.h:335
nlua_env lua_env
Definition: outfit.h:342
int lua_onstealth
Definition: outfit.h:353
char * license
Definition: outfit.h:312
OutfitLocalMapData lmap
Definition: outfit.h:378
int lua_onadd
Definition: outfit.h:344
glTexture ** gfx_overlays
Definition: outfit.h:329
int lua_onremove
Definition: outfit.h:345
double mass
Definition: outfit.h:315
union Outfit::@22 u
OutfitMapData_t * map
Definition: outfit.h:377
char * name
Definition: outfit.h:305
int lua_init
Definition: outfit.h:346
OutfitLicenseData lic
Definition: outfit.h:380
int devmode
Definition: conf.h:158
Represents relative ship statistics as a linked list.
Definition: shipstats.h:167
struct ShipStatList_ * next
Definition: shipstats.h:168
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
Definition: space.h:88
Abstraction for rendering sprite sheets.
Definition: opengl_tex.h:34