11#include "physfsrwops.h"
27#include "nlua_camera.h"
33#include "threadpool.h"
36#define XML_SHIP "ship"
38#define SHIP_ENGINE "_engine"
39#define SHIP_TARGET "_target"
40#define SHIP_COMM "_comm"
43#define VIEW_HEIGHT 300
45#define BUTTON_WIDTH 80
46#define BUTTON_HEIGHT 30
48#define STATS_DESC_MAX 512
53typedef struct ShipThreadData_ {
64static int ship_loadGFX(
Ship *temp,
const char *buf,
int sx,
int sy,
int engine );
73static int ship_cmp(
const void *p1,
const void *p2 )
76 s1 = (
const Ship*) p1;
77 s2 = (
const Ship*) p2;
91 WARN(_(
"Ship %s does not exist"), name);
103 const Ship s = {.name = (
char*)name };
134 s1 = * (
const Ship**) arg1;
135 s2 = * (
const Ship**) arg2;
156 return strcmp( s1->
name, s2->
name );
192 case SHIP_CLASS_NULL:
195 case SHIP_CLASS_YACHT:
197 case SHIP_CLASS_COURIER:
198 return N_(
"Courier");
199 case SHIP_CLASS_FREIGHTER:
200 return N_(
"Freighter");
201 case SHIP_CLASS_ARMOURED_TRANSPORT:
202 return N_(
"Armoured Transport");
203 case SHIP_CLASS_BULK_FREIGHTER:
204 return N_(
"Bulk Freighter");
206 case SHIP_CLASS_SCOUT:
208 case SHIP_CLASS_INTERCEPTOR:
209 return N_(
"Interceptor");
210 case SHIP_CLASS_FIGHTER:
211 return N_(
"Fighter");
212 case SHIP_CLASS_BOMBER:
214 case SHIP_CLASS_CORVETTE:
215 return N_(
"Corvette");
216 case SHIP_CLASS_DESTROYER:
217 return N_(
"Destroyer");
218 case SHIP_CLASS_CRUISER:
219 return N_(
"Cruiser");
220 case SHIP_CLASS_BATTLESHIP:
221 return N_(
"Battleship");
222 case SHIP_CLASS_CARRIER:
223 return N_(
"Carrier");
226 return N_(
"Unknown");
230#define STRTOSHIP( x, y ) if (strcmp(str,x)==0) return y
239 return SHIP_CLASS_NULL;
241 STRTOSHIP(
"Yacht", SHIP_CLASS_YACHT );
242 STRTOSHIP(
"Courier", SHIP_CLASS_COURIER );
243 STRTOSHIP(
"Freighter", SHIP_CLASS_FREIGHTER );
244 STRTOSHIP(
"Armoured Transport", SHIP_CLASS_ARMOURED_TRANSPORT );
245 STRTOSHIP(
"Bulk Freighter", SHIP_CLASS_BULK_FREIGHTER );
248 STRTOSHIP(
"Scout", SHIP_CLASS_SCOUT );
249 STRTOSHIP(
"Interceptor", SHIP_CLASS_INTERCEPTOR );
250 STRTOSHIP(
"Fighter", SHIP_CLASS_FIGHTER );
251 STRTOSHIP(
"Bomber", SHIP_CLASS_BOMBER );
252 STRTOSHIP(
"Corvette", SHIP_CLASS_CORVETTE );
253 STRTOSHIP(
"Destroyer", SHIP_CLASS_DESTROYER );
254 STRTOSHIP(
"Cruiser", SHIP_CLASS_CRUISER );
255 STRTOSHIP(
"Battleship", SHIP_CLASS_BATTLESHIP);
256 STRTOSHIP(
"Carrier", SHIP_CLASS_CARRIER );
259 return SHIP_CLASS_NULL;
319 case SHIP_CLASS_YACHT:
320 case SHIP_CLASS_SCOUT:
321 case SHIP_CLASS_INTERCEPTOR:
324 case SHIP_CLASS_COURIER:
325 case SHIP_CLASS_FIGHTER:
326 case SHIP_CLASS_BOMBER:
329 case SHIP_CLASS_FREIGHTER:
330 case SHIP_CLASS_CORVETTE:
333 case SHIP_CLASS_DESTROYER:
334 case SHIP_CLASS_ARMOURED_TRANSPORT:
337 case SHIP_CLASS_BULK_FREIGHTER:
338 case SHIP_CLASS_CRUISER:
341 case SHIP_CLASS_BATTLESHIP:
342 case SHIP_CLASS_CARRIER:
355 SDL_Surface *gfx, *gfx_store;
357 SDL_Rect rtemp, dstrect;
365 SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE);
368 gfx = SDL_CreateRGBSurface( 0, sw, sh,
369 surface->format->BytesPerPixel*8, RGBAMASK );
370 gfx_store = SDL_CreateRGBSurface( 0, SHIP_TARGET_W, SHIP_TARGET_H,
371 surface->format->BytesPerPixel*8, RGBAMASK );
374 WARN( _(
"Unable to create ship '%s' targeting surface."), temp->
name );
388 SDL_BlitSurface( surface, &rtemp, gfx, &dstrect );
391 dstrect.x = (SHIP_TARGET_W - sw) / 2;
392 dstrect.y = (SHIP_TARGET_H - sh) / 2;
395 SDL_BlitSurface( surface, &rtemp, gfx_store, &dstrect );
398 snprintf( buf,
sizeof(buf),
"%s_gfx_store", temp->
name );
402 snprintf( buf,
sizeof(buf),
"%s_gfx_target", temp->
name );
419 SDL_Surface *surface;
423 rw = PHYSFSRWOPS_openRead( str );
425 WARN(_(
"Unable to open '%s' for reading!"), str);
428 surface = IMG_Load_RW( rw, 0 );
433 OPENGL_TEX_MIPMAPS | OPENGL_TEX_VFLIP,
434 surface->w, surface->h, sx, sy, 0 );
437 OPENGL_TEX_MAPTRANS | OPENGL_TEX_MIPMAPS | OPENGL_TEX_VFLIP,
438 surface->w, surface->h, sx, sy, 0 );
447 SDL_FreeSurface( surface );
480 char str[
PATH_MAX], *ext, *base, *delim;
483 delim = strchr( buf,
'_' );
484 base = delim==NULL ? strdup( buf ) :
strndup( buf, delim-buf );
487 snprintf(str,
sizeof(str), SHIP_3DGFX_PATH
"%s/%s/%s.obj", base, buf, buf);
488 if (PHYSFS_exists(str))
493 snprintf( str,
sizeof(str), SHIP_GFX_PATH
"%s/%s%s", base, buf, ext );
494 if (!PHYSFS_exists(str)) {
496 snprintf( str,
sizeof(str), SHIP_GFX_PATH
"%s/%s%s", base, buf, ext );
502 snprintf( str,
sizeof(str), SHIP_GFX_PATH
"%s/%s"SHIP_ENGINE"%s", base, buf, ext );
505 WARN(_(
"Ship '%s' does not have an engine sprite (%s)."), temp->
name, str );
510 WARN(_(
"Ship '%s' has doubly defined 'gfx_comm'!"),temp->
name);
513 SDL_asprintf( &temp->
gfx_comm, SHIP_GFX_PATH
"%s/%s"SHIP_COMM"%s", base, buf, ext );
532 snprintf( file,
sizeof(file),
"%s%s.xml", SHIP_POLYGON_PATH, buf );
535 if (!PHYSFS_exists(file)) {
536 WARN(_(
"%s xml collision polygon does not exist!\n \
537 Please use the script 'polygon_from_sprite.py' if sprites are used,\n \
538 And 'polygonSTL.py' if 3D model is used in game.\n \
539 These files can be found in Naev's artwork repo."), file);
548 node = doc->xmlChildrenNode;
551 WARN(_(
"Malformed %s file: does not contain elements"), file);
556 if (xml_isNode(node,
"polygons")) {
557 xmlNodePtr cur = node->children;
560 if (xml_isNode(cur,
"polygon")) {
564 }
while (xml_nextNode(cur));
566 }
while (xml_nextNode(node));
583 OutfitSlotSize base_size;
589 xmlr_attr_strd( node,
"size", buf );
593 WARN(_(
"Ship '%s' has undefined slot size, setting to '%s'"),temp->
name,
"Small");
594 base_size = OUTFIT_SLOT_SIZE_LIGHT;
599 if (type == OUTFIT_SLOT_WEAPON) {
600 xmlr_attr_float( node,
"x", slot->
mount.
x );
601 xmlr_attr_float( node,
"y", slot->
mount.
y );
605 xmlr_attr_float( node,
"h", slot->
mount.
h );
609 xmlr_attr_strd( node,
"prop", buf );
632 xmlr_attr_int_def( node,
"locked", slot->
locked, slot->
locked );
635 xmlr_attr_strd( node,
"name", slot->
name );
642 WARN( _(
"Ship '%s' has default outfit '%s' which does not exist."), temp->
name, buf );
652 WARN(_(
"Ship '%s' has required slot without a default outfit."), temp->
name);
666 xmlNodePtr parent, node;
679 parent = doc->xmlChildrenNode;
680 if (parent == NULL) {
682 WARN(_(
"Malformed %s file: does not contain elements"), filename);
687 memset( temp, 0,
sizeof(
Ship) );
703 xmlr_attr_strd( parent,
"name", temp->
name );
704 if (temp->
name == NULL)
705 WARN( _(
"Ship in %s has invalid or no name"), SHIP_DATA_PATH );
711 node = parent->xmlChildrenNode;
717 if (xml_isNode(node,
"class")) {
722 if (xml_isNode(node,
"GFX")) {
724 char *buf = xml_get(node);
726 WARN(_(
"Ship '%s': GFX element is NULL"), temp->
name);
731 xmlr_attr_float_def(node,
"size", temp->
gfx_3d_scale, 1);
732 xmlr_attr_int_def( node,
"sx", sx, 8 );
733 xmlr_attr_int_def( node,
"sy", sy, 8 );
735 xmlr_attr_int(node,
"noengine", noengine );
746 if (xml_isNode(node,
"gfx_space")) {
750 char *buf = xml_get(node);
752 WARN(_(
"Ship '%s': gfx_space element is NULL"), temp->
name);
755 snprintf( str,
sizeof(str), GFX_PATH
"%s", buf );
758 xmlr_attr_int_def( node,
"sx", sx, 8 );
759 xmlr_attr_int_def( node,
"sy", sy, 8 );
762 xmlr_attr_strd( node,
"polygon", plg );
773 if (xml_isNode(node,
"gfx_engine")) {
775 char *buf = xml_get(node);
777 WARN(_(
"Ship '%s': gfx_engine element is NULL"), temp->
name);
780 snprintf( str,
sizeof(str), GFX_PATH
"%s", buf );
783 xmlr_attr_int_def( node,
"sx", sx, 8 );
784 xmlr_attr_int_def( node,
"sy", sy, 8 );
792 if (xml_isNode(node,
"gfx_comm")) {
794 char *buf = xml_get(node);
796 WARN(_(
"Ship '%s': gfx_comm element is NULL"), temp->
name);
799 snprintf( str,
sizeof(str), GFX_PATH
"%s", buf );
801 WARN(_(
"Ship '%s' has doubly defined 'gfx_comm'!"),temp->
name);
807 if (xml_isNode(node,
"gfx_overlays")) {
808 xmlNodePtr cur = node->children;
812 if (xml_isNode(cur,
"gfx_overlay"))
815 }
while (xml_nextNode(cur));
819 if (xml_isNode(node,
"sound")) {
820 xmlr_attr_float_def( node,
"pitch", temp->
engine_pitch, 1. );
824 xmlr_strd(node,
"base_type",temp->
base_type);
826 xmlr_long(node,
"price",temp->
price);
827 xmlr_strd(node,
"license",temp->
license);
828 xmlr_strd(node,
"cond",temp->
cond);
829 xmlr_strd(node,
"condstr",temp->
condstr);
830 xmlr_strd(node,
"fabricator",temp->
fabricator);
832 xmlr_strd(node,
"desc_extra",temp->
desc_extra);
833 xmlr_int(node,
"points",temp->
points);
834 xmlr_int(node,
"rarity",temp->
rarity);
835 xmlr_strd(node,
"lua",temp->
lua_file);
837 if (xml_isNode(node,
"flags")) {
838 xmlNodePtr cur = node->children;
841 if (xml_isNode(cur,
"noplayer")) {
842 ship_setFlag( temp, SHIP_NOPLAYER );
845 if (xml_isNode(cur,
"noescort")) {
846 ship_setFlag( temp, SHIP_NOESCORT );
849 if (xml_isNode(cur,
"unique")) {
850 ship_setFlag( temp, SHIP_UNIQUE );
853 WARN(_(
"Ship '%s' has unknown flags node '%s'."), temp->
name, cur->name);
854 }
while (xml_nextNode(cur));
858 if (xml_isNode(node,
"trail_generator")) {
860 xmlr_attr_float( node,
"x", trail.
x_engine );
861 xmlr_attr_float( node,
"y", trail.
y_engine );
862 xmlr_attr_float( node,
"h", trail.
h_engine );
863 xmlr_attr_int_def( node,
"always_under", trail.
always_under, 0 );
876 if (xml_isNode(node,
"movement")) {
877 xmlNodePtr cur = node->children;
880 xmlr_float(cur,
"accel",temp->
accel);
881 xmlr_float(cur,
"turn",temp->
turn);
882 xmlr_float(cur,
"speed",temp->
speed);
884 WARN(_(
"Ship '%s' has unknown movement node '%s'."), temp->
name, cur->name);
885 }
while (xml_nextNode(cur));
888 if (xml_isNode(node,
"health")) {
889 xmlNodePtr cur = node->children;
893 xmlr_float(cur,
"armour",temp->
armour);
895 xmlr_float(cur,
"shield",temp->
shield);
897 xmlr_float(cur,
"energy",temp->
energy);
900 WARN(_(
"Ship '%s' has unknown health node '%s'."), temp->
name, cur->name);
901 }
while (xml_nextNode(cur));
904 if (xml_isNode(node,
"characteristics")) {
905 xmlNodePtr cur = node->children;
908 xmlr_int(cur,
"crew",temp->
crew);
909 xmlr_float(cur,
"mass",temp->
mass);
910 xmlr_float(cur,
"cpu",temp->
cpu);
911 xmlr_int(cur,
"fuel",temp->
fuel);
915 WARN(_(
"Ship '%s' has unknown characteristic node '%s'."), temp->
name, cur->name);
916 }
while (xml_nextNode(cur));
919 if (xml_isNode(node,
"slots")) {
926 xmlNodePtr cur = node->children;
929 if (xml_isNode(cur,
"structure"))
931 else if (xml_isNode(cur,
"utility"))
933 else if (xml_isNode(cur,
"weapon"))
935 else if (xml_isNode(cur,
"intrinsic")) {
938 WARN(_(
"Ship '%s' has unknown intrinsic outfit '%s'"), temp->
name, xml_get(cur));
946 WARN(_(
"Ship '%s' has unknown slot node '%s'."), temp->
name, cur->name);
947 }
while (xml_nextNode(cur));
955 if (xml_isNode(node,
"stats")) {
956 xmlNodePtr cur = node->children;
965 WARN(_(
"Ship '%s' has unknown stat '%s'."), temp->
name, cur->name);
966 }
while (xml_nextNode(cur));
974 if (temp->
stats != NULL) {
988 if (xml_isNode(node,
"tags")) {
989 xmlNodePtr cur = node->children;
993 if (xml_isNode(cur,
"tag")) {
994 char *tmp = xml_get(cur);
999 WARN(_(
"Ship '%s' has unknown node in tags '%s'."), temp->
name, cur->name );
1000 }
while (xml_nextNode(cur));
1005 if (xml_isNode(node,
"mission"))
1008 DEBUG(_(
"Ship '%s' has unknown node '%s'."), temp->
name, node->name);
1009 }
while (xml_nextNode(node));
1013 temp->
turn *= M_PI / 180.;
1017 WARN(_(
"Ship '%s' has inexistent license requirement '%s'!"), temp->
name, temp->
license);
1021 WARN(_(
"Ship '%s' has no collision polygon!"), temp->
name );
1025 WARN(_(
"Ship '%s': the number of collision polygons is wrong.\n \
1026 npolygon = %i and sx*sy = %i"),
1032#define MELEMENT(o,s) if (o) WARN( _("Ship '%s' missing '%s' element"), temp->name, s)
1033 MELEMENT(temp->
name==NULL,
"name");
1034 MELEMENT(temp->
base_type==NULL,
"base_type");
1036 MELEMENT(temp->
class==SHIP_CLASS_NULL,
"class");
1037 MELEMENT(temp->
points==0,
"points");
1038 MELEMENT(temp->
price==0,
"price");
1040 MELEMENT(temp->
fabricator==NULL,
"fabricator");
1042 MELEMENT(temp->
armour==0.,
"armour");
1043 MELEMENT((temp->
cond!=NULL) && (temp->
condstr==NULL),
"condstr");
1044 MELEMENT((temp->
cond==NULL) && (temp->
condstr!=NULL),
"cond");
1053 MELEMENT(temp->
crew==0,
"crew");
1054 MELEMENT(temp->
mass==0.,
"mass");
1091 Uint32 time = SDL_GetTicks();
1092 ThreadQueue *tq = vpool_create();
1106 for (
int i=0; i<nfiles; i++) {
1112 free( ship_files[i] );
1141 WARN(_(
"Duplicated ship name '%s' detected!"),
ship_stack[i].name);
1157 WARN(_(
"Ship '%s' failed to read Lua '%s'!"), s->
name, s->
lua_file );
1161 env = nlua_newEnv();
1169 if (nlua_dobufenv( env, dat, sz, s->
lua_file ) != 0) {
1170 WARN(_(
"Ship '%s' Lua error:\n%s"), s->
name, lua_tostring(naevL,-1));
1180 nlua_getenv( naevL, env,
"update_dt" );
1181 if (!lua_isnoneornil(naevL,-1))
1182 s->
lua_dt = luaL_checknumber(naevL,-1);
1193 time = SDL_GetTicks() - time;
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 LoadPolygon(CollPoly *polygon, xmlNodePtr node)
Loads a polygon from an xml node.
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.
char * strndup(const char *s, size_t n)
Return a pointer to a new string, which is a duplicate of the string s (or, if necessary,...
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.
void object_free(Object *object)
Frees memory reserved for the object.
Object * object_loadFromFile(const char *filename)
Loads object.
glTexture * gl_newSprite(const char *path, const int sx, const int sy, const unsigned int flags)
Loads the texture immediately, but also sets it as a sprite.
glTexture * gl_loadImagePad(const char *name, SDL_Surface *surface, unsigned int flags, int w, int h, int sx, int sy, int freesur)
Loads the already padded SDL_Surface to a glTexture.
glTexture * gl_loadImagePadTrans(const char *name, SDL_Surface *surface, SDL_RWops *rw, unsigned int flags, int w, int h, int sx, int sy, int freesur)
Wrapper for gl_loadImagePad that includes transparency mapping.
glTexture * gl_newImage(const char *path, const unsigned int flags)
Loads an image as a texture.
void gl_getSpriteFromDir(int *x, int *y, const glTexture *t, const double dir)
Sets x and y to be the appropriate sprite for glTexture using dir.
void gl_freeTexture(glTexture *texture)
Frees a texture.
const Outfit * outfit_get(const char *name)
Gets an outfit by name.
OutfitSlotSize outfit_toSlotSize(const char *s)
Gets the outfit slot size from a human readable string.
int outfit_licenseExists(const char *name)
Checks to see if a license exists.
void outfit_freeSlot(OutfitSlot *s)
Frees an outfit slot.
int ship_size(const Ship *s)
Gets the size of the ship.
ShipClass ship_classFromString(const char *str)
Gets the machine ship class identifier from a human readable string.
static int ship_parseSlot(Ship *temp, ShipOutfitSlot *slot, OutfitSlotType type, xmlNodePtr node)
Parses a slot for a ship.
const char * ship_class(const Ship *s)
Gets the ship's class name in human readable form.
const char * ship_classDisplay(const Ship *s)
Gets the ship's display class in human readable form.
credits_t ship_basePrice(const Ship *s)
Gets the ship's base price (no outfits).
static int ship_parseThread(void *ptr)
Wrapper for threaded loading.
credits_t ship_buyPrice(const Ship *s)
The ship buy price, includes default outfits.
const Ship * ship_getW(const char *name)
Gets a ship based on its name without warning.
int ships_load(void)
Loads all the ships in the data files.
static int ship_loadEngineImage(Ship *temp, char *str, int sx, int sy)
Loads the space graphics for a ship from an image.
static int ship_cmp(const void *p1, const void *p2)
Compares two ship pointers for qsort.
const Ship * ship_getAll(void)
Gets the array (array.h) of all ships.
static int ship_loadSpaceImage(Ship *temp, char *str, int sx, int sy)
Loads the space graphics for a ship from an image.
void ships_free(void)
Frees all the ships.
static int ship_loadGFX(Ship *temp, const char *buf, int sx, int sy, int engine)
Loads the graphics for a ship.
int ship_compareTech(const void *arg1, const void *arg2)
Comparison function for qsort().
const char * ship_classToString(ShipClass class)
Gets the ship class name in human readable form.
static int ship_parse(Ship *temp, const char *filename)
Extracts the in-game ship from an XML node.
glTexture * ship_loadCommGFX(const Ship *s)
Loads the ship's comm graphic.
static int ship_loadPLG(Ship *temp, const char *buf, int size_hint)
Loads the collision polygon for a ship.
const Ship * ship_get(const char *name)
Gets a ship based on its name.
static int ship_genTargetGFX(Ship *temp, SDL_Surface *surface, int sx, int sy)
Generates a target graphic for a ship.
const char * ship_existsCase(const char *name)
Checks to see if an ship exists matching name (case insensitive).
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.
int ss_statsMergeFromList(ShipStats *stats, const ShipStatList *list)
Updates a stat structure from a stat list.
int ss_statsInit(ShipStats *stats)
Initializes a stat structure.
int ss_check(void)
Checks for validity.
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.
int sp_locked(unsigned int spid)
Gets whether or not a slot property is locked.
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 sp_exclusive(unsigned int spid)
Gets whether or not a slot property is exclusive.
int sound_get(const char *name)
Gets the buffer to sound of name.
const TrailSpec * trailSpec_get(const char *name)
Gets a trail spec by name.
Represents a polygon used for collision detection.
A ship outfit, depends radically on the type.
Represents relative ship statistics as a linked list.
struct ShipStatList_ * next
Structure for threaded loading.
const TrailSpec * trail_spec
unsigned int always_under
ShipOutfitSlot * outfit_utility
Outfit const ** outfit_intrinsic
ShipOutfitSlot * outfit_weapon
ShipTrailEmitter * trail_emitters
glTexture ** gfx_overlays
ShipOutfitSlot * outfit_structure
Abstraction for rendering sprite sheets.