26#include "damagetype.h"
31#include "nlua_camera.h"
33#include "nlua_munition.h"
34#include "nlua_outfit.h"
35#include "nlua_pilotoutfit.h"
39#include "pilot_heat.h"
40#include "pilot_outfit.h"
47#include "threadpool.h"
49#define XML_OUTFIT_TAG "outfit"
51#define OUTFIT_SHORTDESC_MAX \
57typedef struct OutfitThreadData_ {
72#define SDESC_ADD( l, temp, txt, ... ) \
73 ( l ) += scnprintf( &( temp )->summary_raw[l], \
74 OUTFIT_SHORTDESC_MAX - ( l ), ( txt ), ##__VA_ARGS__ )
84static int outfit_parseThread(
void *ptr );
101typedef struct s_Outfitstat {
105 int colour_threshold;
112static int os_printD(
char *buf,
int len,
double value,
const os_opts *opts );
114 double maxValue,
const t_os_stat *opts );
116 const t_os_stat *val_opts,
int multiplier,
117 double rate,
const t_os_stat *rate_opts );
120static os_opts darmour_opts = {
121 N_(
"Armour Damage" ), _UNIT_PERCENT, 1, 100, 0, 0 };
122static os_opts dshield_opts = {
123 N_(
"Shield Damage" ), _UNIT_PERCENT, 1, 100, 0, 0 };
124static os_opts dknockback_opts = {
125 N_(
"Knockback" ), _UNIT_PERCENT, 0, 0, 1, 0 };
126static os_opts cpu_opts = { N_(
"CPU" ), _UNIT_CPU, 1, 0, 1, 0 };
127static os_opts mass_opts = { N_(
"Mass" ), _UNIT_MASS, 0, 0, 1, 0 };
128static os_opts penetration_opts = {
129 N_(
"Penetration" ), _UNIT_PERCENT, 0, 0, 1, 0 };
130static os_opts damage_opts = { N_(
"Damage" ), _UNIT_ENERGY, 0, 1, 1, 1 };
131static os_opts dps_opts = { N_(
"Damage Rate" ), _UNIT_POWER, 0, 0, 1, 1 };
132static os_opts disable_opts = { N_(
"Disable" ), _UNIT_ENERGY, 0, 1, 1, 1 };
133static os_opts disable_rate_opts = {
134 N_(
"Disable Rate" ), _UNIT_POWER, 0, 0, 1, 1 };
135static os_opts fire_rate_opts = {
136 N_(
"Fire Rate" ), _UNIT_PER_TIME, 0, 0, 0, 1 };
137static os_opts energy_opts = { N_(
"Energy" ), _UNIT_ENERGY, 0, 1, 1, 1 };
138static os_opts power_opts = { N_(
"Power" ), _UNIT_POWER, 0, 0, 1, 1 };
139static os_opts range_opts = { N_(
"Range" ), _UNIT_DISTANCE, 0, 0, 1, 0 };
140static os_opts speed_opts = { N_(
"Speed" ), _UNIT_SPEED, 0, 0, 1, 0 };
141static os_opts heatup_opts = { N_(
"Overheat" ), _UNIT_TIME, 0, 0, 1, 1 };
142static os_opts dispersion_opts = {
143 N_(
"Dispersion" ), _UNIT_ANGLE, 0, 0, 1, 0 };
144static os_opts swivel_opts = { N_(
"Swivel" ), _UNIT_ANGLE, 0, 0, 1, 0 };
145static os_opts tracking_opts = { N_(
"Tracking" ), _UNIT_DISTANCE, 0, 0, 1, 0 };
146static os_opts duration_opts = { N_(
"Duration" ), _UNIT_TIME, 0, 0, 1, 1 };
147static os_opts cooldown_opts = { N_(
"Cooldown" ), _UNIT_TIME, 0, 0, 1, 1 };
148static os_opts lockon_opts = { N_(
"Lock-On" ), _UNIT_TIME, 0, 0, 1, 0 };
149static os_opts inflight_calib_opts = {
150 N_(
"In-flight Calibration" ), _UNIT_TIME, 0, 0, 1, 1 };
151static os_opts initial_speed_opts = {
152 N_(
"Launch Speed" ), _UNIT_SPEED, 0, 0, 1, 0 };
153static os_opts accel_opts = { N_(
"Accel" ), _UNIT_ACCEL, 0, 0, 1, 0 };
154static os_opts max_speed_opts = { N_(
"Max Speed" ), _UNIT_SPEED, 0, 0, 1, 0 };
155static os_opts reload_opts = { N_(
"Reload Time" ), _UNIT_TIME, 0, 0, 1, 1 };
156static os_opts armour_opts = { N_(
"Armour" ), _UNIT_ENERGY, 0, 0, 1, 1 };
157static os_opts absorp_opts = { N_(
"Absorption" ), _UNIT_PERCENT, 0, 0, 1, 1 };
158static os_opts jam_res_opts = {
159 N_(
"Jam Resistance" ), _UNIT_PERCENT, 0, 0, 1, 0 };
160static os_opts max_mass_opts = {
161 N_(
"Max Effective Mass" ), _UNIT_MASS, 0, 0, 1, 0 };
162static os_opts rumble_opts = { N_(
"Rumble" ), NULL, 0, 0, 1, 1 };
163static os_opts shots_delay_opts = {
164 N_(
"Shots Delay" ), _UNIT_TIME, 0, 0, 1, 1 };
166static int outfit_cmp(
const void *p1,
const void *p2 )
170 return strcmp( o1->
name, o2->
name );
173int outfit_gfxStoreLoaded(
const Outfit *o )
178int outfit_gfxStoreLoadNeeded(
void )
180 ThreadQueue *tq = vpool_create();
181 SDL_GL_MakeCurrent(
gl_screen.window, NULL );
184 if ( !outfit_isProp( o, OUTFIT_PROP_NEEDSGFX ) )
187 outfit_rmProp( o, OUTFIT_PROP_NEEDSGFX );
202 if ( outfit_gfxStoreLoaded( o ) || ( o->
gfx_store_path == NULL ) )
207 snprintf( filename,
sizeof( filename ),
"%s", o->
gfx_store_path );
209 snprintf( filename,
sizeof( filename ), OUTFIT_GFX_PATH
"store/%s",
227 WARN( _(
"Outfit '%s' not found in stack." ), name );
239 const Outfit s = { .name = (
char *)name };
241 sizeof(
Outfit ), outfit_cmp );
274 names = malloc(
sizeof(
char *) * nstack );
278 for (
int i = 0; i < nstack; i++ ) {
279 if ( SDL_strcasestr( _(
outfit_stack[i].name ), name ) != NULL ) {
308 o1 = *(
const Outfit **)outfit1;
309 o2 = *(
const Outfit **)outfit2;
365 return strcmp( o1->
name, o2->
name );
368int outfit_filterWeapon(
const Outfit *o )
370 return ( ( o->
slot.
type == OUTFIT_SLOT_WEAPON ) &&
374int outfit_filterUtility(
const Outfit *o )
376 return ( ( o->
slot.
type == OUTFIT_SLOT_UTILITY ) &&
380int outfit_filterStructure(
const Outfit *o )
382 return ( ( o->
slot.
type == OUTFIT_SLOT_STRUCTURE ) &&
386int outfit_filterCore(
const Outfit *o )
391int outfit_filterOther(
const Outfit *o )
394 ( ( o->
slot.
type == OUTFIT_SLOT_NULL ) ||
395 ( o->
slot.
type == OUTFIT_SLOT_NA ) ||
396 ( o->
slot.
type == OUTFIT_SLOT_INTRINSIC ) ) );
416 case OUTFIT_SLOT_NULL:
420 case OUTFIT_SLOT_INTRINSIC:
421 return N_(
"Intrinsic" );
422 case OUTFIT_SLOT_STRUCTURE:
423 return N_(
"Structure" );
424 case OUTFIT_SLOT_UTILITY:
425 return N_(
"Utility" );
426 case OUTFIT_SLOT_WEAPON:
427 return N_(
"Weapon" );
429 return N_(
"Unknown" );
439 case OUTFIT_SLOT_SIZE_NA:
441 case OUTFIT_SLOT_SIZE_LIGHT:
442 return N_(
"Small" );
443 case OUTFIT_SLOT_SIZE_MEDIUM:
444 return N_(
"Medium" );
445 case OUTFIT_SLOT_SIZE_HEAVY:
446 return N_(
"Large" );
448 return N_(
"Unknown" );
471 if ( os->
size == OUTFIT_SLOT_SIZE_HEAVY )
472 return &cOutfitHeavy;
473 else if ( os->
size == OUTFIT_SLOT_SIZE_MEDIUM )
474 return &cOutfitMedium;
475 else if ( os->
size == OUTFIT_SLOT_SIZE_LIGHT )
476 return &cOutfitLight;
489 if ( os->
size == OUTFIT_SLOT_SIZE_HEAVY )
491 else if ( os->
size == OUTFIT_SLOT_SIZE_MEDIUM )
493 else if ( os->
size == OUTFIT_SLOT_SIZE_LIGHT )
507 if ( os->
type == OUTFIT_SLOT_WEAPON )
509 else if ( os->
type == OUTFIT_SLOT_UTILITY )
511 else if ( os->
type == OUTFIT_SLOT_STRUCTURE )
527 size_t p =
scnprintf( &buf[0], size,
"%s", _( outfit->
name ) );
528 if ( outfit->
slot.
type != OUTFIT_SLOT_NA ) {
529 p +=
scnprintf( &buf[p], size - p, _(
"\n#%c%s #%c%s #0slot" ),
548 return OUTFIT_SLOT_SIZE_NA;
551 if ( strcasecmp( s,
"Large" ) == 0 )
552 return OUTFIT_SLOT_SIZE_HEAVY;
553 else if ( strcasecmp( s,
"Medium" ) == 0 )
554 return OUTFIT_SLOT_SIZE_MEDIUM;
555 else if ( strcasecmp( s,
"Small" ) == 0 )
556 return OUTFIT_SLOT_SIZE_LIGHT;
558 WARN( _(
"'%s' does not match any outfit slot sizes." ), s );
559 return OUTFIT_SLOT_SIZE_NA;
605 return ( ( o->
type == OUTFIT_TYPE_BOLT ) ||
606 ( o->
type == OUTFIT_TYPE_BEAM ) ||
607 ( o->
type == OUTFIT_TYPE_TURRET_BOLT ) ||
608 ( o->
type == OUTFIT_TYPE_TURRET_BEAM ) ||
609 ( o->
type == OUTFIT_TYPE_LAUNCHER ) ||
610 ( o->
type == OUTFIT_TYPE_TURRET_LAUNCHER ) ||
611 ( o->
type == OUTFIT_TYPE_FIGHTER_BAY ) );
621 return ( ( o->
type == OUTFIT_TYPE_BOLT ) ||
622 ( o->
type == OUTFIT_TYPE_BEAM ) );
631 return ( ( o->
type == OUTFIT_TYPE_BOLT ) ||
632 ( o->
type == OUTFIT_TYPE_TURRET_BOLT ) );
641 return ( ( o->
type == OUTFIT_TYPE_BEAM ) ||
642 ( o->
type == OUTFIT_TYPE_TURRET_BEAM ) );
651 return ( ( o->
type == OUTFIT_TYPE_LAUNCHER ) ||
652 ( o->
type == OUTFIT_TYPE_TURRET_LAUNCHER ) );
661 if ( ( ( o->
type == OUTFIT_TYPE_TURRET_LAUNCHER ) ||
662 ( o->
type == OUTFIT_TYPE_LAUNCHER ) ) &&
674 return ( ( o->
type == OUTFIT_TYPE_TURRET_BOLT ) ||
675 ( o->
type == OUTFIT_TYPE_TURRET_BEAM ) ||
676 ( o->
type == OUTFIT_TYPE_TURRET_LAUNCHER ) );
685 return ( o->
type == OUTFIT_TYPE_MODIFICATION );
694 return ( o->
type == OUTFIT_TYPE_AFTERBURNER );
703 return ( o->
type == OUTFIT_TYPE_FIGHTER_BAY );
712 return ( o->
type == OUTFIT_TYPE_MAP );
721 return ( o->
type == OUTFIT_TYPE_LOCALMAP );
730 return ( o->
type == OUTFIT_TYPE_LICENSE );
739 return ( o->
type == OUTFIT_TYPE_GUI );
749 return ( o->
properties & OUTFIT_PROP_WEAP_SECONDARY ) != 0;
901 return pilot_outfitRange( NULL, o );
1062 const char *outfit_typename[] = {
1064 N_(
"Bolt Cannon" ),
1065 N_(
"Beam Cannon" ),
1066 N_(
"Bolt Turret" ),
1067 N_(
"Beam Turret" ),
1069 N_(
"Turret Launcher" ),
1070 N_(
"Ship Modification" ),
1071 N_(
"Afterburner" ),
1072 N_(
"Fighter Bay" ),
1082 return outfit_typename[o->
type];
1094 return N_(
"Bolt Weapon" );
1096 return N_(
"Beam Weapon" );
1098 return N_(
"Launcher" );
1101 return N_(
"Modification" );
1103 return N_(
"Afterburner" );
1105 return N_(
"Fighter Bay" );
1109 return N_(
"Local Map" );
1113 return N_(
"License" );
1115 return N_(
"Unknown" );
1125 const char *ai_type[] = { N_(
"Unguided" ), N_(
"Seek" ), N_(
"Smart" ) };
1128 WARN( _(
"Outfit '%s' is not a launcher outfit" ), o->
name );
1132 return ai_type[o->
u.
lau.
ai];
1185 if ( ( os->
type == OUTFIT_SLOT_NULL ) || ( os->
type == OUTFIT_SLOT_NA ) ||
1186 ( os->
type == OUTFIT_SLOT_INTRINSIC ) )
1198 if ( os->
spid != 0 )
1229 if ( ( os->
type == OUTFIT_SLOT_NULL ) || ( os->
type == OUTFIT_SLOT_NA ) ||
1230 ( os->
type == OUTFIT_SLOT_INTRINSIC ) )
1251#define O_CMP( s, t ) \
1252 if ( strcasecmp( buf, ( s ) ) == 0 ) \
1262 O_CMP(
"bolt", OUTFIT_TYPE_BOLT );
1263 O_CMP(
"beam", OUTFIT_TYPE_BEAM );
1264 O_CMP(
"turret bolt", OUTFIT_TYPE_TURRET_BOLT );
1265 O_CMP(
"turret beam", OUTFIT_TYPE_TURRET_BEAM );
1266 O_CMP(
"launcher", OUTFIT_TYPE_LAUNCHER );
1267 O_CMP(
"turret launcher", OUTFIT_TYPE_TURRET_LAUNCHER );
1268 O_CMP(
"modification", OUTFIT_TYPE_MODIFICATION );
1269 O_CMP(
"afterburner", OUTFIT_TYPE_AFTERBURNER );
1270 O_CMP(
"fighter bay", OUTFIT_TYPE_FIGHTER_BAY );
1271 O_CMP(
"map", OUTFIT_TYPE_MAP );
1272 O_CMP(
"localmap", OUTFIT_TYPE_LOCALMAP );
1273 O_CMP(
"license", OUTFIT_TYPE_LICENSE );
1274 O_CMP(
"gui", OUTFIT_TYPE_GUI );
1276 WARN( _(
"Invalid outfit type: '%s'" ), buf );
1277 return OUTFIT_TYPE_NULL;
1289 SDESC_ADD( *l, temp,
"\n#g%s#0",
1290 _(
"Can mine uncommon and rare minerals" ) );
1292 SDESC_ADD( *l, temp,
"\n#g%s#0", _(
"Can mine uncommon minerals" ) );
1317 cur = node->xmlChildrenNode;
1319 xml_onlyNodes( cur );
1323 xmlr_float( cur,
"physical", dmg->
damage );
1324 xmlr_float( cur,
"disable", dmg->
disable );
1327 if ( xml_isNode( cur,
"type" ) ) {
1328 char *buf = xml_get( cur );
1330 if ( dmg->
type < 0 ) {
1333 WARN( _(
"Unknown damage type '%s'" ), buf );
1338 WARN( _(
"Damage has unknown node '%s'" ), cur->name );
1340 }
while ( xml_nextNode( cur ) );
1366 WARN( _(
"Trying to load polygon for non-compatible outfit '%s'!" ),
1371 snprintf( file,
sizeof( file ),
"%s%s.xml", OUTFIT_POLYGON_PATH, buf );
1374 if ( !PHYSFS_exists( file ) ) {
1375 WARN( _(
"%s xml collision polygon does not exist!\n \
1376 Please use the script 'polygon_from_sprite.py' \
1377that can be found in Naev's artwork repo." ),
1388 node = doc->xmlChildrenNode;
1389 if ( node == NULL ) {
1391 WARN( _(
"Malformed %s file: does not contain elements" ), file );
1396 if ( xml_isNode( node,
"polygons" ) )
1398 }
while ( xml_nextNode( node ) );
1418 WARN( _(
"Trying to load graphics for non-compatible outfit '%s'!" ),
1424 xmlr_attr_float( node,
"spin", gfx->
spin );
1425 if ( gfx->
spin != 0. )
1426 outfit_setProp( temp, OUTFIT_PROP_WEAP_SPIN );
1429 xmlr_attr_strd( node,
"type", type );
1430 if ( ( type != NULL ) && ( strcmp( type,
"shader" ) == 0 ) ) {
1433 xmlr_attr_strd( node,
"vertex", vertex );
1434 if ( vertex == NULL )
1435 vertex = strdup(
"project_pos.vert" );
1437 gfx->
program = gl_program_vert_frag( vertex, xml_get( node ) );
1439 gfx->
vertex = glGetAttribLocation( gfx->
program,
"vertex" );
1442 gfx->
u_r = glGetUniformLocation( gfx->
program,
"u_r" );
1443 gfx->
u_time = glGetUniformLocation( gfx->
program,
"u_time" );
1444 gfx->
u_fade = glGetUniformLocation( gfx->
program,
"u_fade" );
1447 xmlr_attr_float_def( node,
"size", gfx->
size, -1. );
1448 if ( gfx->
size < 0. )
1449 WARN( _(
"Outfit '%s' has GFX shader but no 'size' set!" ),
1452 xmlr_attr_float_def( node,
"col_size", gfx->
col_size, gfx->
size * 0.8 );
1456 }
else if ( type != NULL ) {
1457 WARN( _(
"Outfit '%s' has unknown gfx type '%s'!" ), temp->
name, type );
1463 const char *buf = xml_get( node );
1467 flags = OPENGL_TEX_MIPMAPS;
1469 flags |= OPENGL_TEX_MAPTRANS;
1475 xmlr_attr_strd( node,
"col_size", col );
1476 if ( col != NULL ) {
1477 outfit_setProp( temp, OUTFIT_PROP_WEAP_COLLISION_OVERRIDE );
1478 gfx->
col_size = strtod( col, NULL );
1484 WARN( _(
"Outfit '%s' is missing collision polygon!" ), temp->
name );
1500 double dshield, darmour, dknockback;
1514 node = parent->xmlChildrenNode;
1516 xml_onlyNodes( node );
1517 xmlr_float( node,
"speed", temp->
u.
blt.
speed );
1518 xmlr_float( node,
"delay", temp->
u.
blt.
delay );
1519 xmlr_float( node,
"energy", temp->
u.
blt.
energy );
1520 xmlr_float( node,
"heatup", temp->
u.
blt.
heatup );
1523 xmlr_float( node,
"swivel", temp->
u.
blt.
swivel );
1526 xmlr_int( node,
"shots", temp->
u.
blt.
shots );
1528 xmlr_strd( node,
"lua", temp->
lua_file );
1529 if ( xml_isNode( node,
"radius" ) ) {
1532 xmlr_attr_strd( node,
"friendlyfire", buf );
1533 if ( buf != NULL ) {
1534 outfit_setProp( temp, OUTFIT_PROP_WEAP_FRIENDLYFIRE );
1539 if ( xml_isNode( node,
"pointdefense" ) ) {
1540 outfit_setProp( temp, OUTFIT_PROP_WEAP_POINTDEFENSE );
1543 if ( xml_isNode( node,
"miss_ships" ) ) {
1544 outfit_setProp( temp, OUTFIT_PROP_WEAP_MISS_SHIPS );
1547 if ( xml_isNode( node,
"miss_asteroids" ) ) {
1548 outfit_setProp( temp, OUTFIT_PROP_WEAP_MISS_ASTEROIDS );
1551 if ( xml_isNode( node,
"miss_explode" ) ) {
1552 outfit_setProp( temp, OUTFIT_PROP_WEAP_MISS_EXPLODE );
1555 if ( xml_isNode( node,
"onlyhittarget" ) ) {
1556 outfit_setProp( temp, OUTFIT_PROP_WEAP_ONLYHITTARGET );
1559 if ( xml_isNode( node,
"range" ) ) {
1561 xmlr_attr_strd( node,
"blowup", buf );
1562 if ( buf != NULL ) {
1563 if ( strcmp( buf,
"armour" ) == 0 )
1564 outfit_setProp( temp, OUTFIT_PROP_WEAP_BLOWUP_SHIELD );
1565 else if ( strcmp( buf,
"shield" ) == 0 )
1566 outfit_setProp( temp, OUTFIT_PROP_WEAP_BLOWUP_ARMOUR );
1568 WARN( _(
"Outfit '%s' has invalid blowup property: '%s'" ),
1572 temp->
u.
blt.
range = xml_getFloat( node );
1575 xmlr_float( node,
"falloff", temp->
u.
blt.
falloff );
1578 if ( xml_isNode( node,
"gfx" ) ) {
1582 if ( xml_isNode( node,
"gfx_end" ) ) {
1584 node, OUTFIT_GFX_PATH
"space/%s", 6, 6, OPENGL_TEX_MIPMAPS );
1589 if ( xml_isNode( node,
"spfx_shield" ) ) {
1592 WARN( _(
"Outfit '%s' has unknown spfx_shield '%s'!" ), temp->
name,
1596 if ( xml_isNode( node,
"spfx_armour" ) ) {
1599 WARN( _(
"Outfit '%s' has unknown spfx_armour '%s'!" ), temp->
name,
1605 if ( xml_isNode( node,
"sound" ) ) {
1609 if ( xml_isNode( node,
"sound_hit" ) ) {
1613 if ( xml_isNode( node,
"damage" ) ) {
1625 WARN( _(
"Outfit '%s' has unknown node '%s'" ), temp->
name, node->name );
1626 }
while ( xml_nextNode( node ) );
1644 temp->
u.
blt.
heat = ( ( 800. - CONST_SPACE_STAR_TEMP ) *
C +
1645 STEEL_HEAT_CONDUCTIVITY *
1646 ( ( 800. - CONST_SPACE_STAR_TEMP ) * area ) ) /
1652 SDESC_ADD( l, temp, p_(
"outfitstats",
"%s [%s]" ),
1672 &disable_rate_opts );
1678 &penetration_opts );
1682 char radius[STRMAX_SHORT];
1683 snprintf( radius,
sizeof( radius ),
1684 outfit_isProp( temp, OUTFIT_PROP_WEAP_FRIENDLYFIRE )
1685 ? p_(
"friendlyfire",
"#r!! %s !!#0" )
1687 _(
"Hit radius" ) );
1690 .unit = _UNIT_DISTANCE,
1692 .colour_threshold = 0,
1710#define MELEMENT( o, s ) \
1712 WARN( _( "Outfit '%s' missing/invalid '%s' element" ), temp->name, \
1718 MELEMENT( temp->
mass == 0.,
"mass" );
1719 MELEMENT( temp->
u.
blt.
delay == 0,
"delay" );
1720 MELEMENT( temp->
u.
blt.
speed == 0,
"speed" );
1721 MELEMENT( temp->
u.
blt.
range == 0,
"range" );
1723 MELEMENT( temp->
u.
blt.
energy == 0.,
"energy" );
1726 MELEMENT( temp->
u.
blt.
heatup == 0.,
"heatup" );
1748 double dshield, darmour, dknockback;
1758 node = parent->xmlChildrenNode;
1760 xml_onlyNodes( node );
1761 xmlr_float( node,
"range", temp->
u.
bem.
range );
1762 xmlr_float( node,
"turn", temp->
u.
bem.
turn );
1763 xmlr_float( node,
"energy", temp->
u.
bem.
energy );
1765 xmlr_float( node,
"warmup", temp->
u.
bem.
warmup );
1766 xmlr_float( node,
"heatup", temp->
u.
bem.
heatup );
1767 xmlr_float( node,
"swivel", temp->
u.
bem.
swivel );
1769 xmlr_strd( node,
"lua", temp->
lua_file );
1770 if ( xml_isNode( node,
"pointdefense" ) ) {
1771 outfit_setProp( temp, OUTFIT_PROP_WEAP_POINTDEFENSE );
1774 if ( xml_isNode( node,
"miss_ships" ) ) {
1775 outfit_setProp( temp, OUTFIT_PROP_WEAP_MISS_SHIPS );
1778 if ( xml_isNode( node,
"miss_asteroids" ) ) {
1779 outfit_setProp( temp, OUTFIT_PROP_WEAP_MISS_ASTEROIDS );
1783 if ( xml_isNode( node,
"delay" ) ) {
1785 temp->
u.
bem.
delay = xml_getFloat( node );
1789 if ( xml_isNode( node,
"damage" ) ) {
1795 if ( xml_isNode( node,
"shader" ) ) {
1796 xmlr_attr_float( node,
"r", temp->
u.
bem.
colour.r );
1797 xmlr_attr_float( node,
"g", temp->
u.
bem.
colour.g );
1798 xmlr_attr_float( node,
"b", temp->
u.
bem.
colour.b );
1799 xmlr_attr_float( node,
"a", temp->
u.
bem.
colour.a );
1800 xmlr_attr_float( node,
"width", temp->
u.
bem.
width );
1802 shader = xml_get( node );
1803 if ( gl_has( OPENGL_SUBROUTINES ) ) {
1806 shaders.beam.program, GL_FRAGMENT_SHADER, shader );
1807 if ( temp->
u.
bem.
shader == GL_INVALID_INDEX )
1808 WARN(
"Beam outfit '%s' has unknown shader function '%s'",
1809 temp->
name, shader );
1814 if ( xml_isNode( node,
"spfx_armour" ) ) {
1818 if ( xml_isNode( node,
"spfx_shield" ) ) {
1824 if ( xml_isNode( node,
"sound_warmup" ) ) {
1828 if ( xml_isNode( node,
"sound" ) ) {
1832 if ( xml_isNode( node,
"sound_off" ) ) {
1844 WARN( _(
"Outfit '%s' has unknown node '%s'" ), temp->
name, node->name );
1845 }
while ( xml_nextNode( node ) );
1853 ( ( 800. - CONST_SPACE_STAR_TEMP ) *
C +
1854 STEEL_HEAT_CONDUCTIVITY *
1855 ( ( 800. - CONST_SPACE_STAR_TEMP ) * area ) ) /
1875 &disable_rate_opts );
1879 &penetration_opts );
1890#define MELEMENT( o, s ) \
1892 WARN( _( "Outfit '%s' missing/invalid '%s' element" ), temp->name, \
1894 MELEMENT( temp->
u.
bem.
width == 0.,
"shader width" );
1903 MELEMENT( temp->
u.
bem.
delay == 0,
"delay" );
1907 MELEMENT( temp->
u.
bem.
range == 0,
"range" );
1908 MELEMENT( ( temp->
type != OUTFIT_TYPE_BEAM ) && ( temp->
u.
bem.
turn == 0 ),
1910 MELEMENT( temp->
u.
bem.
energy == 0.,
"energy" );
1911 MELEMENT( temp->
cpu == 0.,
"cpu" );
1913 MELEMENT( temp->
u.
bem.
heatup == 0.,
"heatup" );
1927 double dshield, darmour, dknockback;
1941 node = parent->xmlChildrenNode;
1943 xml_onlyNodes( node );
1944 xmlr_float( node,
"delay", temp->
u.
lau.
delay );
1945 xmlr_int( node,
"amount", temp->
u.
lau.
amount );
1949 xmlr_float( node,
"lockon", temp->
u.
lau.
lockon );
1951 xmlr_float( node,
"swivel", temp->
u.
lau.
swivel );
1954 xmlr_float( node,
"armour", temp->
u.
lau.
armour );
1956 xmlr_int( node,
"shots", temp->
u.
lau.
shots );
1958 xmlr_strd( node,
"lua", temp->
lua_file );
1959 if ( xml_isNode( node,
"radius" ) ) {
1962 xmlr_attr_strd( node,
"friendlyfire", buf );
1963 if ( buf != NULL ) {
1964 outfit_setProp( temp, OUTFIT_PROP_WEAP_FRIENDLYFIRE );
1969 if ( xml_isNode( node,
"pointdefense" ) ) {
1970 outfit_setProp( temp, OUTFIT_PROP_WEAP_POINTDEFENSE );
1973 if ( xml_isNode( node,
"miss_ships" ) ) {
1974 outfit_setProp( temp, OUTFIT_PROP_WEAP_MISS_SHIPS );
1977 if ( xml_isNode( node,
"miss_asteroids" ) ) {
1978 outfit_setProp( temp, OUTFIT_PROP_WEAP_MISS_ASTEROIDS );
1981 if ( xml_isNode( node,
"miss_explode" ) ) {
1982 outfit_setProp( temp, OUTFIT_PROP_WEAP_MISS_EXPLODE );
1985 if ( xml_isNode( node,
"onlyhittarget" ) ) {
1986 outfit_setProp( temp, OUTFIT_PROP_WEAP_ONLYHITTARGET );
1991 xmlr_float( node,
"arc",
1996 if ( xml_isNode( node,
"duration" ) ) {
1998 xmlr_attr_strd( node,
"blowup", buf );
1999 if ( buf != NULL ) {
2000 if ( strcmp( buf,
"armour" ) == 0 )
2001 outfit_setProp( temp, OUTFIT_PROP_WEAP_BLOWUP_SHIELD );
2002 else if ( strcmp( buf,
"shield" ) == 0 )
2003 outfit_setProp( temp, OUTFIT_PROP_WEAP_BLOWUP_ARMOUR );
2005 WARN( _(
"Outfit '%s' has invalid blowup property: '%s'" ),
2012 xmlr_float( node,
"resist", temp->
u.
lau.
resist );
2014 xmlr_float( node,
"accel", temp->
u.
lau.
accel );
2015 xmlr_float( node,
"turn", temp->
u.
lau.
turn );
2016 xmlr_float( node,
"speed", temp->
u.
lau.
speed );
2018 xmlr_float( node,
"energy", temp->
u.
lau.
energy );
2020 if ( xml_isNode( node,
"gfx" ) ) {
2024 if ( xml_isNode( node,
"spfx_armour" ) ) {
2028 if ( xml_isNode( node,
"spfx_shield" ) ) {
2032 if ( xml_isNode( node,
"sound" ) ) {
2036 if ( xml_isNode( node,
"sound_hit" ) ) {
2040 if ( xml_isNode( node,
"damage" ) ) {
2044 if ( xml_isNode( node,
"trail_generator" ) ) {
2046 char *buf = xml_get( node );
2052 if ( xml_isNode( node,
"ai" ) ) {
2053 char *buf = xml_get( node );
2054 if ( buf != NULL ) {
2055 if ( strcmp( buf,
"unguided" ) == 0 )
2056 temp->
u.
lau.
ai = AMMO_AI_UNGUIDED;
2057 else if ( strcmp( buf,
"seek" ) == 0 )
2058 temp->
u.
lau.
ai = AMMO_AI_SEEK;
2059 else if ( strcmp( buf,
"smart" ) == 0 )
2060 temp->
u.
lau.
ai = AMMO_AI_SMART;
2062 WARN( _(
"Ammo '%s' has unknown ai type '%s'." ), temp->
name,
2075 WARN( _(
"Outfit '%s' has unknown node '%s'" ), temp->
name, node->name );
2076 }
while ( xml_nextNode( node ) );
2079 if ( !outfit_isProp( temp, OUTFIT_PROP_TEMPLATE ) ) {
2081 if ( temp->
mass < 0. )
2082 WARN( _(
"Launcher outfit '%s' has negative mass when subtracting "
2088 temp->
u.
lau.
arc *= M_PI / 180.;
2099 WARN( _(
"Max speed of ammo '%s' will be ignored." ), temp->
name );
2125 &disable_rate_opts );
2130 &penetration_opts );
2134 &inflight_calib_opts );
2138 SDESC_ADD( l, temp,
"\n%s", _(
"No Seeking" ) );
2147 SDESC_ADD( l, temp, _(
"\n Holds %d ammo" ), temp->
u.
lau.
amount );
2149 char radius[STRMAX_SHORT];
2150 snprintf( radius,
sizeof( radius ),
2151 outfit_isProp( temp, OUTFIT_PROP_WEAP_FRIENDLYFIRE )
2152 ? p_(
"friendlyfire",
"#r!! %s !!#0" )
2154 _(
"Hit radius" ) );
2157 .unit = _UNIT_DISTANCE,
2159 .colour_threshold = 0,
2174 &initial_speed_opts );
2178 &initial_speed_opts );
2190#define MELEMENT( o, s ) \
2192 WARN( _( "Outfit '%s' missing '%s' element" ), temp->name, \
2194 MELEMENT( temp->
u.
lau.
delay == 0.,
"delay" );
2196 MELEMENT( temp->
u.
lau.
amount == 0.,
"amount" );
2200 MELEMENT( !outfit_isProp( temp, OUTFIT_PROP_SHOOT_DRY ) &&
2203 MELEMENT( !outfit_isProp( temp, OUTFIT_PROP_SHOOT_DRY ) &&
2210 MELEMENT( temp->
u.
lau.
turn == 0,
"turn" );
2213 MELEMENT( temp->
u.
lau.
lockon < 0,
"lockon" );
2216 MELEMENT( !outfit_isProp( temp, OUTFIT_PROP_SHOOT_DRY ) &&
2219 MELEMENT( !outfit_isProp( temp, OUTFIT_PROP_SHOOT_DRY ) &&
2225 if ( !outfit_isProp( temp, OUTFIT_PROP_SHOOT_DRY ) &&
2227 WARN( _(
"Outfit '%s' has no speed nor accel set!" ), temp->
name );
2228 if ( !outfit_isProp( temp, OUTFIT_PROP_SHOOT_DRY ) &&
2230 WARN( _(
"Outfit '%s' has longer 'iflockon' than ammo 'duration'" ),
2242 xmlNodePtr node = parent->children;
2247 xml_onlyNodes( node );
2248 xmlr_strd( node,
"lua", temp->
lua_file );
2250 if ( xml_isNode( node,
"active" ) ) {
2251 xmlr_attr_float( node,
"cooldown", temp->
u.
mod.
cooldown );
2270 WARN( _(
"Outfit '%s' has unknown node '%s'" ), temp->
name, node->name );
2271 }
while ( xml_nextNode( node ) );
2288 xmlNodePtr node = parent->children;
2302 xml_onlyNodes( node );
2303 xmlr_float( node,
"rumble", temp->
u.
afb.
rumble );
2304 xmlr_strd( node,
"lua", temp->
lua_file );
2305 if ( xml_isNode( node,
"sound_on" ) ) {
2309 if ( xml_isNode( node,
"sound" ) ) {
2313 if ( xml_isNode( node,
"sound_off" ) ) {
2317 xmlr_float( node,
"accel", temp->
u.
afb.
accel );
2318 xmlr_float( node,
"speed", temp->
u.
afb.
speed );
2319 xmlr_float( node,
"energy", temp->
u.
afb.
energy );
2321 xmlr_float( node,
"heatup", temp->
u.
afb.
heatup );
2332 WARN( _(
"Outfit '%s' has unknown node '%s'" ), temp->
name, node->name );
2333 }
while ( xml_nextNode( node ) );
2339 SDESC_ADD( l, temp,
"\n#o%s#0", _(
"Activated Outfit" ) );
2345 SDESC_ADD( l, temp,
"\n%s", _(
"Only one can be equipped" ) );
2357 temp->
u.
afb.
heat = ( ( 800. - CONST_SPACE_STAR_TEMP ) *
C +
2358 STEEL_HEAT_CONDUCTIVITY *
2359 ( ( 800. - CONST_SPACE_STAR_TEMP ) * area ) ) /
2362#define MELEMENT( o, s ) \
2364 WARN( _( "Outfit '%s' missing/invalid '%s' element" ), temp->name, \
2366 MELEMENT( temp->
u.
afb.
accel == 0.,
"accel" );
2367 MELEMENT( temp->
u.
afb.
speed == 0.,
"speed" );
2368 MELEMENT( temp->
u.
afb.
energy == 0.,
"energy" );
2371 MELEMENT( temp->
u.
afb.
heatup == 0.,
"heatup" );
2384 xmlNodePtr node = parent->children;
2389 xml_onlyNodes( node );
2390 xmlr_float( node,
"delay", temp->
u.
bay.
delay );
2394 xmlr_int( node,
"amount", temp->
u.
bay.
amount );
2395 xmlr_strd( node,
"lua", temp->
lua_file );
2404 WARN( _(
"Outfit '%s' has unknown node '%s'" ), temp->
name, node->name );
2405 }
while ( xml_nextNode( node ) );
2409 if ( temp->
mass < 0. )
2410 WARN( _(
"Fighter bay outfit '%s' has negative mass when subtracting "
2422 SDESC_ADD( l, temp, _(
"\n Holds %d ships" ), temp->
u.
bay.
amount );
2427#define MELEMENT( o, s ) \
2429 WARN( _( "Outfit '%s' missing/invalid '%s' element" ), temp->name, \
2433 MELEMENT( temp->
u.
bay.
delay == 0,
"delay" );
2435 MELEMENT( temp->
cpu == 0.,
"cpu" );
2436 MELEMENT( temp->
u.
bay.
amount == 0,
"amount" );
2451 node = parent->children;
2454 temp->
slot.
size = OUTFIT_SLOT_SIZE_NA;
2461 xml_onlyNodes( node );
2463 if ( xml_isNode( node,
"sys" ) ) {
2464 xmlr_attr_strd( node,
"name", buf );
2466 if ( sys == NULL ) {
2467 WARN( _(
"Map '%s' has invalid system '%s'" ), temp->
name, buf );
2475 xmlNodePtr cur = node->children;
2477 xml_onlyNodes( cur );
2479 if ( xml_isNode( cur,
"spob" ) ) {
2480 buf = xml_get( cur );
2482 if ( ( buf != NULL ) && ( spob != NULL ) )
2485 WARN( _(
"Map '%s' has invalid spob '%s'" ), temp->
name,
2487 }
else if ( xml_isNode( cur,
"jump" ) ) {
2489 buf = xml_get( cur );
2490 if ( ( buf != NULL ) &&
2498 WARN( _(
"Map '%s' has invalid jump point '%s'" ), temp->
name,
2501 WARN( _(
"Outfit '%s' has unknown node '%s'" ), temp->
name,
2503 }
while ( xml_nextNode( cur ) );
2504 }
else if ( xml_isNode( node,
"short_desc" ) ) {
2508 }
else if ( xml_isNode( node,
"all" ) ) {
2510 for (
int i = 0; i <
array_size( system_stack ); i++ ) {
2511 StarSystem *ss = &system_stack[i];
2513 for (
int j = 0; j <
array_size( system_stack[i].spobs ); j++ )
2515 for (
int j = 0; j <
array_size( system_stack[i].jumps ); j++ )
2519 WARN( _(
"Outfit '%s' has unknown node '%s'" ), temp->
name,
2521 }
while ( xml_nextNode( node ) );
2534#define MELEMENT( o, s ) \
2536 WARN( _( "Outfit '%s' missing/invalid '%s' element" ), temp->name, s )
2538 MELEMENT( temp->
mass != 0.,
"cpu" );
2539 MELEMENT( temp->
cpu != 0.,
"cpu" );
2551 xmlNodePtr node = parent->children;
2554 temp->
slot.
size = OUTFIT_SLOT_SIZE_NA;
2557 xml_onlyNodes( node );
2561 WARN( _(
"Outfit '%s' has unknown node '%s'" ), temp->
name, node->name );
2562 }
while ( xml_nextNode( node ) );
2572#define MELEMENT( o, s ) \
2574 WARN( _( "Outfit '%s' missing/invalid '%s' element" ), temp->name, s )
2576 MELEMENT( temp->
mass != 0.,
"cpu" );
2577 MELEMENT( temp->
cpu != 0.,
"cpu" );
2589 xmlNodePtr node = parent->children;
2592 temp->
slot.
size = OUTFIT_SLOT_SIZE_NA;
2595 xml_onlyNodes( node );
2596 xmlr_strd( node,
"gui", temp->
u.
gui.
gui );
2597 WARN( _(
"Outfit '%s' has unknown node '%s'" ), temp->
name, node->name );
2598 }
while ( xml_nextNode( node ) );
2603 _(
"GUI (Graphical User Interface)" ) );
2605#define MELEMENT( o, s ) \
2607 WARN( _( "Outfit '%s' missing/invalid '%s' element" ), temp->name, s )
2609 MELEMENT( temp->
u.
gui.
gui == NULL,
"gui" );
2610 MELEMENT( temp->
mass != 0.,
"cpu" );
2611 MELEMENT( temp->
cpu != 0.,
"cpu" );
2623 xmlNodePtr node = parent->children;
2626 temp->
slot.
size = OUTFIT_SLOT_SIZE_NA;
2629 xml_onlyNodes( node );
2631 WARN( _(
"Outfit '%s' has unknown node '%s'" ), temp->
name, node->name );
2632 }
while ( xml_nextNode( node ) );
2642#define MELEMENT( o, s ) \
2644 WARN( _( "Outfit '%s' missing/invalid '%s' element" ), temp->name, s )
2646 MELEMENT( temp->
mass != 0.,
"cpu" );
2647 MELEMENT( temp->
cpu != 0.,
"cpu" );
2660 xmlNodePtr node, parent;
2669 parent = doc->xmlChildrenNode;
2670 if ( parent == NULL ) {
2671 WARN( _(
"Malformed '%s' file: does not contain elements" ), file );
2676 memset( temp, 0,
sizeof(
Outfit ) );
2710 xmlr_attr_strd( parent,
"name", temp->
name );
2711 if ( temp->
name == NULL )
2712 WARN( _(
"Outfit '%s' has invalid or no name" ), file );
2714 node = parent->xmlChildrenNode;
2719 xml_onlyNodes( node );
2721 if ( xml_isNode( node,
"general" ) ) {
2722 xmlNodePtr cur = node->children;
2724 xml_onlyNodes( cur );
2725 xmlr_int( cur,
"rarity", temp->
rarity );
2726 xmlr_strd( cur,
"shortname", temp->
shortname );
2727 xmlr_strd( cur,
"license", temp->
license );
2728 xmlr_strd( cur,
"cond", temp->
cond );
2729 xmlr_strd( cur,
"condstr", temp->
condstr );
2730 xmlr_float( cur,
"mass", temp->
mass );
2731 xmlr_float( cur,
"cpu", temp->
cpu );
2732 xmlr_long( cur,
"price", temp->
price );
2733 xmlr_strd( cur,
"limit", temp->
limit );
2734 xmlr_strd( cur,
"description", temp->
desc_raw );
2735 xmlr_strd( cur,
"desc_extra", temp->
desc_extra );
2736 xmlr_strd( cur,
"typename", temp->
typename );
2737 xmlr_int( cur,
"priority", temp->
priority );
2740 if ( xml_isNode( cur,
"unique" ) ) {
2741 outfit_setProp( temp, OUTFIT_PROP_UNIQUE );
2743 }
else if ( xml_isNode( cur,
"stealth_on" ) ) {
2744 outfit_setProp( temp, OUTFIT_PROP_STEALTH_ON );
2746 }
else if ( xml_isNode( cur,
"shoot_dry" ) ) {
2747 outfit_setProp( temp, OUTFIT_PROP_SHOOT_DRY );
2749 }
else if ( xml_isNode( cur,
"template" ) ) {
2750 outfit_setProp( temp, OUTFIT_PROP_TEMPLATE );
2752 }
else if ( xml_isNode( cur,
"gfx_store" ) ) {
2753 const char *str = xml_get( cur );
2757 WARN( _(
"Outfit '%s' has NULL tag '%s'!" ), temp->
name,
2760 }
else if ( xml_isNode( cur,
"gfx_overlays" ) ) {
2761 xmlNodePtr ccur = cur->children;
2764 xml_onlyNodes( ccur );
2765 if ( xml_isNode( ccur,
"gfx_overlay" ) )
2769 OPENGL_TEX_MIPMAPS ) );
2770 }
while ( xml_nextNode( ccur ) );
2772 }
else if ( xml_isNode( cur,
"slot" ) ) {
2773 cprop = xml_get( cur );
2774 if ( cprop == NULL )
2775 WARN( _(
"Outfit '%s' has an slot type invalid." ),
2777 else if ( strcmp( cprop,
"structure" ) == 0 )
2778 temp->
slot.
type = OUTFIT_SLOT_STRUCTURE;
2779 else if ( strcmp( cprop,
"utility" ) == 0 )
2780 temp->
slot.
type = OUTFIT_SLOT_UTILITY;
2781 else if ( strcmp( cprop,
"weapon" ) == 0 )
2782 temp->
slot.
type = OUTFIT_SLOT_WEAPON;
2783 else if ( strcmp( cprop,
"intrinsic" ) == 0 )
2784 temp->
slot.
type = OUTFIT_SLOT_INTRINSIC;
2785 else if ( strcmp( cprop,
"none" ) == 0 )
2788 WARN( _(
"Outfit '%s' has unknown slot type '%s'." ),
2789 temp->
name, cprop );
2792 xmlr_attr_strd( cur,
"prop", prop );
2797 }
else if ( xml_isNode( cur,
"size" ) ) {
2800 }
else if ( xml_isNode( cur,
"illegalto" ) ) {
2801 xmlNodePtr ccur = cur->xmlChildrenNode;
2804 xml_onlyNodes( ccur );
2805 if ( xml_isNode( ccur,
"faction" ) ) {
2806 const char *s = xml_get( ccur );
2808 WARN( _(
"Empty faction string for outfit '%s' "
2814 }
while ( xml_nextNode( ccur ) );
2816 WARN( _(
"Outfit '%s' has no factions defined in <illegalto> "
2822 WARN( _(
"Outfit '%s' has unknown general node '%s'" ), temp->
name,
2824 }
while ( xml_nextNode( cur ) );
2828 if ( xml_isNode( node,
"stats" ) ) {
2829 xmlNodePtr cur = node->children;
2832 xml_onlyNodes( cur );
2840 WARN( _(
"Outfit '%s' has unknown node '%s'" ), temp->
name,
2842 }
while ( xml_nextNode( cur ) );
2847 if ( xml_isNode( node,
"tags" ) ) {
2848 xmlNodePtr cur = node->children;
2851 xml_onlyNodes( cur );
2852 if ( xml_isNode( cur,
"tag" ) ) {
2853 const char *tmp = xml_get( cur );
2858 WARN( _(
"Outfit '%s' has unknown node in tags '%s'." ), temp->
name,
2860 }
while ( xml_nextNode( cur ) );
2864 if ( xml_isNode( node,
2867 xmlr_attr_strd( node,
"type", prop );
2869 WARN( _(
"Outfit '%s' element 'specific' missing property 'type'" ),
2876 xmlr_attr_strd( node,
"secondary", prop );
2877 if ( prop != NULL ) {
2878 if ( (
int)atoi( prop ) )
2879 outfit_setProp( temp, OUTFIT_PROP_WEAP_SECONDARY );
2884 xmlr_attr_int_def( node,
"group", group, -1 );
2885 if ( group != -1 ) {
2886 if ( group > PILOT_WEAPON_SETS || group < 1 ) {
2887 WARN( _(
"Outfit '%s' has group '%d', should be in the 1-%d "
2889 temp->
name, group, PILOT_WEAPON_SETS );
2898 if ( temp->
type == OUTFIT_TYPE_NULL )
2899 WARN( _(
"Outfit '%s' is of type NONE" ), temp->
name );
2914 malloc(
sizeof( OutfitMapData_t ) );
2917 temp->
slot.
size = OUTFIT_SLOT_SIZE_NA;
2931 WARN( _(
"Outfit '%s' has unknown node '%s'" ), temp->
name, node->name );
2932 }
while ( xml_nextNode( node ) );
2934#define MELEMENT( o, s ) \
2936 WARN( _( "Outfit '%s' missing/invalid '%s' element" ), temp->name, \
2938 MELEMENT( temp->
name == NULL,
"name" );
2939 if ( !outfit_isProp( temp, OUTFIT_PROP_TEMPLATE ) ) {
2940 MELEMENT( temp->
slot.
type == OUTFIT_SLOT_NULL,
"slot" );
2941 MELEMENT( ( temp->
slot.
type != OUTFIT_SLOT_NA ) &&
2942 ( temp->
slot.
type != OUTFIT_SLOT_INTRINSIC ) &&
2943 ( temp->
slot.
size == OUTFIT_SLOT_SIZE_NA ),
2946 MELEMENT( temp->
desc_raw == NULL,
"description" );
2949 MELEMENT( temp->
type == 0,
"type" );
2951 MELEMENT( ( temp->
cond != NULL ) && ( temp->
condstr == NULL ),
"condstr" );
2952 MELEMENT( ( temp->
cond == NULL ) && ( temp->
condstr != NULL ),
"cond" );
2960static int outfit_parseThread(
void *ptr )
2963 data->ret =
outfit_parse( &data->outfit, data->filename );
2982 ThreadQueue *tq = vpool_create();
2985 for (
int i = 0; i <
array_size( outfit_files ); i++ ) {
2988 od->filename = outfit_files[i];
2990 free( outfit_files[i] );
2995 SDL_GL_MakeCurrent(
gl_screen.window, NULL );
2996 for (
int i = 0; i <
array_size( odata ); i++ )
2997 vpool_enqueue( tq, outfit_parseThread, &odata[i] );
3000 vpool_cleanup( tq );
3005 for (
int i = 0; i <
array_size( odata ); i++ ) {
3011 free( od->filename );
3026 Uint32 time = SDL_GetTicks();
3043 for (
int i = 1; i < noutfits; i++ )
3045 WARN( _(
"Duplicated outfit name '%s' detected!" ),
3050 for (
int i = 0; i < noutfits; i++ ) {
3058 if ( dat == NULL ) {
3059 WARN( _(
"Outfit '%s' failed to read Lua '%s'!" ), o->
name,
3074 if ( nlua_dobufenv( env, dat, sz, o->
lua_file ) != 0 ) {
3075 WARN( _(
"Outfit '%s' Lua error:\n%s" ), o->
name,
3076 lua_tostring(
naevL, -1 ) );
3077 lua_pop(
naevL, 1 );
3115 nlua_getenv(
naevL, env,
"notactive" );
3117 if ( lua_toboolean(
naevL, -1 ) ) {
3120 WARN( _(
"Outfit '%s' has '%s' Lua function defined, but "
3121 "is set as 'notactive'!" ),
3122 o->
name,
"ontoggle" );
3124 lua_pop(
naevL, 1 );
3130 SDESC_ADD( l, o,
"\n#o%s#0", _(
"Activated Outfit" ) );
3140 for (
int i = 0; i < noutfits; i++ ) {
3147 SDESC_ADD( l, temp,
"\n#o%s#0", _(
"Activated Outfit" ) );
3163 char **outfit_names = malloc( noutfits *
sizeof(
char * ) );
3166 for (
int i = 0; i < noutfits; i++ )
3169 qsort( outfit_names, noutfits,
sizeof(
char * ),
strsort );
3170 for (
int i = 0; i < ( noutfits - 1 ); i++ ) {
3172 while ( strcmp( outfit_names[i], outfit_names[i + 1] ) == 0 )
3178 WARN( n_(
"Name collision! %d outfit is named '%s'",
3179 "Name collision! %d outfits are named '%s'", i + 1 - start ),
3180 i + 1 - start, outfit_names[start] );
3182 free( outfit_names );
3186 if ( conf.devmode ) {
3187 time = SDL_GetTicks() - time;
3188 DEBUG( n_(
"Loaded %d Outfit in %.3f s",
"Loaded %d Outfits in %.3f s",
3190 noutfits, time / 1000. );
3192 DEBUG( n_(
"Loaded %d Outfit",
"Loaded %d Outfits", noutfits ),
3223 SDESC_ADD( l, o,
"\n#r%s#0", _(
"Illegal to:" ) );
3225 SDESC_ADD( l, o, _(
"\n#r- %s#0" ),
3230 if ( o->
lua_env != LUA_NOREF ) {
3232 if ( lua_isnil(
naevL, -1 ) )
3233 lua_pop(
naevL, 1 );
3236 if ( nlua_pcall( o->
lua_env, 1, 0 ) ) {
3237 WARN( _(
"Outfit '%s' lua load error -> 'load':\n%s" ), o->
name,
3238 lua_tostring(
naevL, -1 ) );
3239 lua_pop(
naevL, 1 );
3246 WARN( _(
"Outfit '%s' has inexistent license requirement '%s'!" ),
3261 xmlNodePtr node, cur;
3271 node = doc->xmlChildrenNode;
3272 if ( node == NULL ) {
3273 WARN( _(
"Malformed '%s' file: does not contain elements" ),
3279 cur = node->xmlChildrenNode;
3282 xml_onlyNodes( cur );
3284 if ( xml_isNode( cur,
"specific" ) )
3287 }
while ( xml_nextNode( cur ) );
3302 snprintf( s,
sizeof( s ), OVERLAY_GFX_PATH
"rarity_%d.webp", rarity );
3327 return ( lic != NULL );
3346 if ( gfx != NULL ) {
3350 glDeleteProgram( gfx->
program );
3420 char buf[NUM2STRLEN];
3422 if ( opts->hide_zero && fabs( value ) < 1e-2 )
3426 precision = opts->precision;
3428 precision =
MAX( opts->precision, 2 );
3430 i +=
scnprintf( buffer + i, MAXLEN,
"\n" );
3433 value > opts->colour_threshold ?
"#g"
3434 : value < opts->colour_threshold ?
"#r"
3440 num2str( buf, value, precision );
3441 i +=
scnprintf( buffer + i, MAXLEN, p_(
"outfitstats",
"%s: %s %s" ),
3442 _( opts->name ), buf, opts->unit ? _( opts->unit ) :
"" );
3444 i +=
scnprintf( buffer + i, MAXLEN,
"#0" );
3460 double maxValue,
const t_os_stat *opts )
3463 char buf1[NUM2STRLEN], buf2[NUM2STRLEN];
3465 if ( opts->hide_zero && fabs( maxValue ) < 1e-2 )
3468 num2str( buf1, minValue, opts->precision );
3469 num2str( buf2, maxValue, opts->precision );
3471 i +=
scnprintf( buffer + i, MAXLEN,
"\n" );
3474 maxValue > opts->colour_threshold ?
"#g"
3475 : maxValue < opts->colour_threshold ?
"#r"
3477 i +=
scnprintf( buffer + i, MAXLEN, p_(
"outfitstats",
"%s: %s %s - %s %s" ),
3478 _( opts->name ), buf1, _( opts->unit ), buf2,
3481 i +=
scnprintf( buffer + i, MAXLEN,
"#0" );
3499 const t_os_stat *val_opts,
int multiplier,
3500 double rate,
const t_os_stat *rate_opts )
3504 char buf1[NUM2STRLEN], buf2[NUM2STRLEN];
3506 if ( val_opts->hide_zero && fabs( val ) < 1e-2 )
3509 i +=
scnprintf( buffer + i, MAXLEN,
"\n" );
3510 if ( val_opts->colour )
3512 val > val_opts->colour_threshold ?
"#g"
3513 : val < val_opts->colour_threshold ?
"#r"
3516 if ( multiplier > 1 )
3517 snprintf( mult,
sizeof( mult ), p_(
"multiplier",
" x %d" ), multiplier );
3521 num2str( buf1, val, val_opts->precision );
3522 num2str( buf2, rate, rate_opts->precision );
3525 scnprintf( buffer + i, MAXLEN, p_(
"outfitstats",
"%s: %s%s %s [%s %s]" ),
3526 _( val_opts->name ), buf1, mult, _( val_opts->unit ), buf2,
3527 _( rate_opts->unit ) );
3528 if ( val_opts->colour )
3529 i +=
scnprintf( buffer + i, MAXLEN,
"#0" );
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
#define array_create_size(basic_type, capacity)
Creates a new dynamic array of ‘basic_type’ with an initial capacity.
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
#define array_shrink(ptr_array)
Shrinks memory to fit only ‘size’ elements.
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
void poly_load(CollPoly *polygon, xmlNodePtr base, const char *name)
Loads a polygon from an xml node.
int dtype_raw(int type, double *shield, double *armour, double *knockback)
Gets the raw modulation stats of a damage type.
int dtype_get(const char *name)
Gets the id of a dtype based on name.
const char * dtype_damageTypeToStr(int type)
Gets the human readable string from damage type.
const char * faction_name(int f)
Gets a factions "real" (internal) name.
int faction_get(const char *name)
Gets a faction ID by name.
void naev_renderLoadscreen(void)
Renders the loadscreen if necessary.
int naev_shouldRenderLoadscreen(void)
Whether or not we want to render the loadscreen.
Header file with generic functions and naev-specifics.
void * ndata_read(const char *path, size_t *filesize)
Reads a file from the ndata (will be NUL terminated).
int ndata_matchExt(const char *path, const char *ext)
Sees if a file matches an extension.
char ** ndata_listRecursive(const char *path)
Lists all the visible files in a directory, at any depth.
int nlua_loadStandard(nlua_env env)
Loads the standard Naev Lua API.
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.
int nlua_loadCamera(nlua_env env)
Loads the camera library.
int nlua_loadGFX(nlua_env env)
Loads the graphics library.
int nlua_loadMunition(nlua_env env)
Loads the munition library.
const Outfit ** lua_pushoutfit(lua_State *L, const Outfit *outfit)
Pushes a outfit on the stack.
int nlua_loadPilotOutfit(nlua_env env)
Loads the pilot outfit library.
int num2str(char dest[NUM2STRLEN], double n, int decimals)
Converts a numeric value to a string.
int strsort(const void *p1, const void *p2)
Sort function for sorting strings with qsort().
int scnprintf(char *text, size_t maxlen, const char *fmt,...)
Like snprintf(), but returns the number of characters ACTUALLY "printed" into the buffer....
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.
xmlDocPtr xml_parsePhysFS(const char *filename)
Analogous to xmlParseMemory/xmlParseFile.
glTexture * gl_newImage(const char *path, const unsigned int flags)
Loads an image as a texture.
void gl_freeTexture(glTexture *texture)
Frees a texture.
int outfit_isSecondary(const Outfit *o)
Checks if outfit has the secondary flag set.
const char * outfit_description(const Outfit *o)
Gets the description of an outfit.
static int outfit_loadGFX(Outfit *temp, const xmlNodePtr node)
Loads the graphics for an outfit.
static void outfit_parseSMap(Outfit *temp, const xmlNodePtr parent)
Parses the map tidbits of the outfit.
const char * outfit_shortname(const Outfit *o)
Gets the short name (translated) of an outfit.
double outfit_speed(const Outfit *o)
Gets the outfit's speed.
static Outfit * outfit_stack
double outfit_trackmin(const Outfit *o)
Gets the outfit's minimal tracking.
static void outfit_parseSLauncher(Outfit *temp, const xmlNodePtr parent)
Parses the specific area for a launcher and loads it into Outfit.
static int outfit_parseDamage(Damage *dmg, xmlNodePtr node)
Parses a damage node.
int outfit_isBeam(const Outfit *o)
Checks if outfit is a beam type weapon.
const char * outfit_summary(const Outfit *o, int withname)
Gets the summary of an outfit.
const Outfit * outfit_getAll(void)
Gets the array (array.h) of all outfits.
double outfit_cpu(const Outfit *o)
Gets the outfit's cpu usage.
static void outfit_parseSAfterburner(Outfit *temp, const xmlNodePtr parent)
Parses the afterburner tidbits of the outfit.
int outfit_isActive(const Outfit *o)
Checks if outfit is an active outfit.
int outfit_soundHit(const Outfit *o)
Gets the outfit's hit sound.
static int outfit_parse(Outfit *temp, const char *file)
Parses and returns Outfit from parent node.
const Outfit * outfit_get(const char *name)
Gets an outfit by name.
int outfit_isLauncher(const Outfit *o)
Checks if outfit is a weapon launcher.
int outfit_isSeeker(const Outfit *o)
Checks if outfit is a seeking weapon.
static OutfitType outfit_strToOutfitType(char *buf)
Gets the outfit type from a human readable string.
OutfitSlotSize outfit_toSlotSize(const char *s)
Gets the outfit slot size from a human readable string.
int outfit_miningRarity(const Outfit *o)
Gets the maximum rarity the outfit can mine up to.
static int outfit_loadDir(const char *dir)
Loads all the files in a directory.
static void outfit_parseSLicense(Outfit *temp, const xmlNodePtr parent)
Parses the license tidbits of the outfit.
int outfit_isLocalMap(const Outfit *o)
Checks if outfit is a local space map.
int outfit_isWeapon(const Outfit *o)
Checks to see if an outfit is a weapon.
static void outfit_parseSFighterBay(Outfit *temp, const xmlNodePtr parent)
Parses the fighter bay tidbits of the outfit.
char outfit_slotTypeColourFont(const OutfitSlot *os)
Gets a font colour character that roughly matches an outfit slot type colour.
int outfit_isToggleable(const Outfit *o)
Checks if outfit can be toggled.
static void outfit_parseSBolt(Outfit *temp, const xmlNodePtr parent)
Parses the specific area for a bolt weapon and loads it into Outfit.
int outfit_compareTech(const void *outfit1, const void *outfit2)
Function meant for use with C89, C99 algorithm qsort().
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.
const Outfit * outfit_getW(const char *name)
Gets an outfit by name without warning on no-find.
const char * outfit_getTypeBroad(const Outfit *o)
Gets the outfit's broad type.
static void outfit_parseSMod(Outfit *temp, const xmlNodePtr parent)
Parses the modification tidbits of the outfit.
int outfit_fitsSlot(const Outfit *o, const OutfitSlot *s)
Checks to see if an outfit fits a slot.
int outfit_checkIllegal(const Outfit *o, int fct)
Checks illegality of an outfit to a faction.
int outfit_isFighterBay(const Outfit *o)
Checks if outfit is a fighter bay.
int outfit_isAfterburner(const Outfit *o)
Checks if outfit is an afterburner.
double outfit_range(const Outfit *o)
Gets the outfit's range.
int outfit_isMap(const Outfit *o)
Checks if outfit is a space map.
double outfit_radius(const Outfit *o)
Gets the outfit's explosion radius.
int outfit_spfxShield(const Outfit *o)
Gets the outfit's sound effect.
int outfit_isTurret(const Outfit *o)
Checks if outfit is a turret class weapon.
double outfit_ammoMass(const Outfit *o)
Gets the outfit's ammunition mass.
int outfit_isForward(const Outfit *o)
Checks if outfit is a fixed mounted weapon.
const CollPoly * outfit_plg(const Outfit *o)
Gets the outfit's collision polygon.
static void sdesc_miningRarity(int *l, Outfit *temp, int rarity)
Adds a small blurb about rarity mining.
int outfit_isLicense(const Outfit *o)
Checks if outfit is a license.
static void outfit_parseSGUI(Outfit *temp, const xmlNodePtr parent)
Parses the GUI tidbits of the outfit.
char outfit_slotSizeColourFont(const OutfitSlot *os)
Gets a font colour character that roughly matches an outfit slot size colour.
const OutfitGFX * outfit_gfx(const Outfit *o)
Gets the outfit's graphic effect.
int outfit_amount(const Outfit *o)
Gets the amount an outfit can hold.
int outfit_load(void)
Loads all the outfits.
const char * slotName(const OutfitSlotType type)
double outfit_spin(const Outfit *o)
Gets the outfit's animation spin.
int outfit_mapParse(void)
Parses all the maps.
const char * outfit_getType(const Outfit *o)
Gets the outfit's specific type.
double outfit_heat(const Outfit *o)
Gets the outfit's heat generation.
double outfit_trackmax(const Outfit *o)
Gets the outfit's minimal tracking.
static void outfit_parseSBeam(Outfit *temp, const xmlNodePtr parent)
Parses the beam weapon specifics of an outfit.
static char ** license_stack
int outfit_isMod(const Outfit *o)
Checks if outfit is a ship modification.
static int os_printD_range(char *buffer, int i, double minValue, double maxValue, const t_os_stat *opts)
Writes an outfit statistic representing a range between two values to a buffer.
int outfit_isGUI(const Outfit *o)
Checks if outfit is a GUI.
int outfit_gfxStoreLoad(Outfit *o)
Loads the store graphics for the outfit.
int outfit_sound(const Outfit *o)
Gets the outfit's sound.
int outfit_spfxArmour(const Outfit *o)
Gets the outfit's sound effect.
#define OUTFIT_SHORTDESC_MAX
static int outfit_loadPLG(Outfit *temp, const char *buf)
Loads the collision polygon for a bolt outfit.
const char * slotSize(const OutfitSlotSize o)
Gets the slot size as a string.
const Damage * outfit_damage(const Outfit *o)
Gets the outfit's damage.
char ** outfit_searchFuzzyCase(const char *name, int *n)
Does a fuzzy search of all the outfits. Searches translated names but returns internal names.
double outfit_swivel(const Outfit *o)
Gets the swivel of an outfit.
static void outfit_parseSLocalMap(Outfit *temp, const xmlNodePtr parent)
Parses the map tidbits of the outfit.
double outfit_duration(const Outfit *o)
Gets the outfit's duration.
double outfit_cooldown(const Outfit *o)
Gets the outfit's cooldown.
const char * outfit_slotName(const Outfit *o)
Gets the name of the slot type of an outfit.
int outfit_isBolt(const Outfit *o)
Checks if outfit is bolt type weapon.
const char * outfit_slotSize(const Outfit *o)
Gets the name of the slot size of an outfit.
void outfit_free(void)
Frees the outfit stack.
const char * outfit_existsCase(const char *name)
Checks to see if an outfit exists matching name (case insensitive).
double outfit_energy(const Outfit *o)
Gets the outfit's energy usage.
int outfit_licenseExists(const char *name)
Checks to see if a license exists.
void outfit_freeSlot(OutfitSlot *s)
Frees an outfit slot.
static int os_printD(char *buf, int len, double value, const os_opts *opts)
Writes an outfit statistic to a buffer.
const char * outfit_getAmmoAI(const Outfit *o)
Gets a human-readable string describing an ammo outfit's AI.
glTexture * rarity_texture(int rarity)
const glColour * outfit_slotSizeColour(const OutfitSlot *os)
Gets the slot size colour for an outfit slot.
static int os_printD_rate(char *buffer, int i, double val, const t_os_stat *val_opts, int multiplier, double rate, const t_os_stat *rate_opts)
Writes an outfit statistic representing a "per unit" value and rate of change value.
double outfit_delay(const Outfit *o)
Gets the outfit's delay.
int outfit_fitsSlotType(const Outfit *o, const OutfitSlot *s)
Checks to see if an outfit fits a slot type (ignoring size).
int outfit_loadPost(void)
Loads all the outfits legality.
double pilot_heatCalcOutfitC(const Outfit *o)
Calculates the thermal mass of an outfit.
double pilot_heatCalcOutfitArea(const Outfit *o)
Calculates the effective transfer area of an outfit.
const char * pilot_outfitDescription(const Pilot *p, const Outfit *o)
Gets the description of an outfit for a given pilot.
const char * pilot_outfitSummary(const Pilot *p, const Outfit *o, int withname)
Gets the summary of an outfit for a give pilot.
double pilot_outfitSpeed(const Pilot *p, const Outfit *o)
Gets the speed of an outfit given a pilot.
const Ship * ship_get(const char *name)
Gets a ship based on its name.
int ss_statsListDesc(const ShipStatList *ll, char *buf, int len, int newline)
Writes the ship statistics description.
void ss_free(ShipStatList *ll)
Frees a list of ship stats.
ShipStatList * ss_listFromXML(xmlNodePtr node)
Creates a shipstat list element from an xml node.
int ss_sort(ShipStatList **ll)
Sorts the ship stats, useful if doing saving stuff.
unsigned int sp_get(const char *name)
Gets the id of a slot property.
int sp_required(unsigned int spid)
Gets whether or not a slot property is required.
int sound_get(const char *name)
Gets the buffer to sound of name.
Spob * spob_get(const char *spobname)
Gets a spob based on its name.
StarSystem * system_getAll(void)
Gets an array (array.h) of all star systems.
StarSystem * system_get(const char *sysname)
Get the system from its name.
JumpPoint * jump_get(const char *jumpname, const StarSystem *sys)
Gets a jump point based on its target and system.
const TrailSpec * trailSpec_get(const char *name)
Gets a trail spec by name.
int spfx_get(const char *name)
Gets the id of an spfx based on name.
const char * start_dtype_default(void)
Gets the default damage type.
Core damage that an outfit does.
const struct Ship_ * ship
const TrailSpec * trail_spec
Pilot slot that can contain outfits.
For threaded loading of outfits.
A ship outfit, depends radically on the type.
union Outfit::@052125200133344144252153256241104103242010347340 u
OutfitModificationData mod
OutfitAfterburnerData afb
glTexture ** gfx_overlays
Represents relative ship statistics as a linked list.
struct ShipStatList_ * next
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
Abstraction for rendering sprite sheets.