Lephisto/src/outfit.c
2013-08-28 15:08:22 +01:00

706 lines
22 KiB
C

#include <math.h>
#include <string.h>
#include "SDL_thread.h"
#include "lephisto.h"
#include "log.h"
#include "pack.h"
#include "xml.h"
#include "spfx.h"
#include "outfit.h"
#define outfit_setProp(o,p) ((o)->properties |= p)
#define XML_OUTFIT_ID "Outfits"
#define XML_OUTFIT_TAG "outfit"
#define OUTFIT_DATA "../dat/outfit.xml"
#define OUTFIT_GFX "../gfx/outfit/"
/* The Stack. */
static Outfit* outfit_stack = NULL;
static int outfit_nstack = 0;
/* Misc. */
static DamageType outfit_strToDamageType(char* buf);
static OutfitType outfit_strToOutfitType(char* buf);
/* Parsing. */
static int outfit_parseDamage(DamageType* dtype, double* dmg, xmlNodePtr node);
static Outfit* outfit_parse(const xmlNodePtr parent);
static void outfit_parseSWeapon(Outfit* tmp, const xmlNodePtr parent);
static void outfit_parseSLauncher(Outfit* tmp, const xmlNodePtr parent);
static void outfit_parseSAmmo(Outfit* tmp, const xmlNodePtr parent);
static void outfit_parseSMod(Outfit* tmp, const xmlNodePtr parent);
static void outfit_parseSAfterburner(Outfit* tmp, const xmlNodePtr parent);
static void outfit_parseSMap(Outfit* tmp, const xmlNodePtr parent);
static void outfit_parseSJammer(Outfit* tmp, const xmlNodePtr parent);
/* Return an outfit. */
Outfit* outfit_get(const char* name) {
int i;
for(i = 0; i < outfit_nstack; i++)
if(strcmp(name, outfit_stack[i].name)==0)
return &outfit_stack[i];
WARN("Outfit '%s' not found in stack.", name);
return NULL;
}
/* Return all the outfits. */
char** outfit_getTech(int* n, const int* tech, const int techmax) {
int i, j, k, num, price;
Outfit** outfits;
char** outfitnames;
OutfitType type;
outfits = malloc(sizeof(Outfit*)*outfit_nstack);
/* Get the available techs. */
num = 0;
for(i = 0; i < outfit_nstack; i++)
if(outfit_stack[i].tech <= tech[0]) { /* Check vs base tech. */
outfits[num] = &outfit_stack[i];
num++;
} else {
for(j = 0; j < techmax; j++) /* Check vs special techs. */
if(tech[j] == outfit_stack[i].tech) {
outfits[num] = &outfit_stack[i];
num++;
}
}
/* Now sort by type and price. */
*n = 0;
price = -1;
type = OUTFIT_TYPE_NULL+1; /* First type. */
outfitnames = malloc(sizeof(char*)*num);
/* Sort by type */
while(type < OUTFIT_TYPE_SENTINEL) {
/* Check for cheapest. */
for(j = 0; j < num; j++) {
/* Must be of the current type. */
if(outfits[j]->type != type) continue;
/* Is this the cheapest? */
if((price == -1) || (outfits[price]->price > outfits[j]->price)) {
/* Check if already in stack. */
for(k = 0; k < (*n); k++)
if(strcmp(outfitnames[k], outfits[j]->name)==0)
break;
/* Not in stack and therefore is cheapest. */
if(k == (*n))
price = j;
}
}
if(price == -1)
type++;
else {
/* Add current cheapest to stack. */
outfitnames[*n] = strdup(outfits[price]->name);
(*n)++;
price = -1;
}
}
/* Cleanup. */
free(outfits);
return outfitnames;
}
/* Give the real shield damage, armour damage and knockback modifier. */
void outfit_calcDamage(double* dshield, double* darmour, double* knockback,
DamageType dtype, double dmg) {
switch(dtype) {
case DAMAGE_TYPE_ENERGY:
(*dshield) = dmg*1.1;
(*darmour) = dmg*0.7;
(*knockback) = 0.1;
break;
case DAMAGE_TYPE_KINETIC:
(*dshield) = dmg*0.8;
(*darmour) = dmg*1.2;
(*knockback) = 1.;
break;
case DAMAGE_TYPE_ION:
(*dshield) = 0.;
(*darmour) = dmg;
(*knockback) = 0.4;
break;
case DAMAGE_TYPE_RADIATION:
(*dshield) = 0.15; /* Still take damage, just very little. */
(*darmour) = dmg;
(*knockback) = 0.8;
default:
WARN("Unknown damage type: %d!", dtype);
(*dshield) = (*darmour) = (*knockback) = 0.;
break;
}
}
/* Return 1 if outfit is a weapon (beam/bolt). */
int outfit_isWeapon(const Outfit* o) {
return ((o->type == OUTFIT_TYPE_BOLT) || (o->type == OUTFIT_TYPE_BEAM));
}
/* Return 1 if outfit is a launcher. */
int outfit_isLauncher(const Outfit* o) {
return((o->type == OUTFIT_TYPE_MISSILE_DUMB) ||
(o->type == OUTFIT_TYPE_MISSILE_SEEK) ||
(o->type == OUTFIT_TYPE_MISSILE_SEEK_SMART) ||
(o->type == OUTFIT_TYPE_MISSILE_SWARM) ||
(o->type == OUTFIT_TYPE_MISSILE_SWARM_SMART));
}
/* Return 1 if outfit is weapon ammunition. */
int outfit_isAmmo(const Outfit* o) {
return((o->type == OUTFIT_TYPE_MISSILE_DUMB_AMMO) ||
(o->type == OUTFIT_TYPE_MISSILE_SEEK_AMMO) ||
(o->type == OUTFIT_TYPE_MISSILE_SEEK_SMART_AMMO) ||
(o->type == OUTFIT_TYPE_MISSILE_SWARM_AMMO) ||
(o->type == OUTFIT_TYPE_MISSILE_SWARM_SMART_AMMO));
}
int outfit_isTurret(const Outfit* o) {
return ((o->type == OUTFIT_TYPE_TURRET_BOLT) ||
(o->type == OUTFIT_TYPE_TURRET_BEAM));
}
/* Return 1 if o is a modification. */
int outfit_isMod(const Outfit* o) {
return (o->type == OUTFIT_TYPE_MODIFICATION);
}
/* Return 1 if o is an afterburner. */
int outfit_isAfterburner(const Outfit* o) {
return (o->type == OUTFIT_TYPE_AFTERBURNER);
}
int outfit_isMap(const Outfit* o) {
return(o->type == OUTFIT_TYPE_MAP);
}
/* Return 1 if o is a jammer. */
int outfit_isJammer(const Outfit* o) {
return(o->type == OUTFIT_TYPE_JAMMER);
}
/* Get the outfit graphics. */
glTexture* outfit_gfx(const Outfit* o) {
if(outfit_isWeapon(o)) return o->u.blt.gfx_space;
else if(outfit_isAmmo(o)) return o->u.amm.gfx_space;
else if(outfit_isTurret(o)) return o->u.blt.gfx_space;
return NULL;
}
/* Get the outfit spfx if applicable. */
int outfit_spfx(const Outfit* o) {
if(outfit_isWeapon(o)) return o->u.blt.spfx;
else if(outfit_isAmmo(o)) return o->u.amm.spfx;
else if(outfit_isTurret(o)) return o->u.blt.spfx;
return -1;
}
double outfit_damage(const Outfit* o) {
if(outfit_isWeapon(o)) return o->u.blt.damage;
else if(outfit_isAmmo(o)) return o->u.amm.damage;
else if(outfit_isTurret(o)) return o->u.blt.damage;
return -1;
}
DamageType outfit_damageType(const Outfit* o) {
if(outfit_isWeapon(o)) return o->u.blt.dtype;
else if(outfit_isAmmo(o)) return o->u.amm.dtype;
else if(outfit_isTurret(o)) return o->u.blt.dtype;
return DAMAGE_TYPE_NULL;
}
int outfit_delay(const Outfit* o) {
if(outfit_isWeapon(o)) return o->u.blt.delay;
else if(outfit_isLauncher(o)) return o->u.lau.delay;
else if(outfit_isTurret(o)) return o->u.blt.delay;
return -1;
}
double outfit_energy(const Outfit* o) {
if(outfit_isWeapon(o)) return o->u.blt.energy;
else if(outfit_isAmmo(o)) return o->u.amm.energy;
else if(outfit_isTurret(o)) return o->u.blt.energy;
return -1.;
}
double outfit_range(const Outfit* o) {
if(outfit_isWeapon(o)) return o->u.blt.range;
else if(outfit_isAmmo(o)) return 0.8*o->u.amm.speed*o->u.amm.duration;
else if(outfit_isTurret(o)) return o->u.blt.range;
return -1.;
}
double outfit_speed(const Outfit* o) {
if(outfit_isWeapon(o)) return o->u.blt.speed;
else if(outfit_isAmmo(o)) return o->u.amm.speed;
else if(outfit_isTurret(o)) return o->u.blt.speed;
return -1.;
}
int outfit_isSeeker(const Outfit* o) {
if((o->type == OUTFIT_TYPE_MISSILE_SEEK_AMMO) ||
(o->type == OUTFIT_TYPE_MISSILE_SEEK_SMART_AMMO) ||
(o->type == OUTFIT_TYPE_MISSILE_SWARM_AMMO) ||
(o->type == OUTFIT_TYPE_MISSILE_SWARM_SMART_AMMO))
return 1;
return 0;
}
const char* outfit_typename[] = {
"NULL",
"Bolt Cannon",
"Beam Cannon",
"Bolt Turret",
"Beam Turret",
"Dumb Missile",
"Dumb Missile Ammunition",
"Seeker Missile",
"Seeker Missile Ammunition",
"Smart Seeker Missile",
"Smart Seeker Missile Ammunition",
"Swam Missile",
"Swarm Missile Ammunition Pack",
"Smart Swarm Missile",
"Smart Swarm Missile Ammunition Pack",
"Ship Modification",
"Afterburner",
"Map",
"Jammer"
};
const char* outfit_getType(const Outfit* o) {
return outfit_typename[o->type];
}
/* Return the broad outfit type. */
const char* outfit_typenamebroad[] = {
"NULL",
"Weapon",
"Launcher",
"Ammo",
"Turret",
"Modification",
"Afterburner",
"Map",
"Jammer"
};
const char* outfit_getTypeBroad(const Outfit* o) {
int i = 0;
if(outfit_isWeapon(o)) i = 1;
else if(outfit_isLauncher(o)) i = 2;
else if(outfit_isAmmo(o)) i = 3;
else if(outfit_isTurret(o)) i = 4;
else if(outfit_isMod(o)) i = 5;
else if(outfit_isAfterburner(o)) i = 6;
else if(outfit_isMap(o)) i = 7;
else if(outfit_isJammer(o)) i = 8;
return outfit_typenamebroad[i];
}
/* Return the damage type from a str. */
static DamageType outfit_strToDamageType(char* buf) {
if(strcmp(buf, "energy")==0) return DAMAGE_TYPE_ENERGY;
else if(strcmp(buf, "kinetic")==0) return DAMAGE_TYPE_KINETIC;
else if(strcmp(buf, "ion")==0) return DAMAGE_TYPE_ION;
else if(strcmp(buf, "radiation")==0) return DAMAGE_TYPE_RADIATION;
WARN("Invalid damage type: '%s'", buf);
return DAMAGE_TYPE_NULL;
}
/* Return the outfit type from string. */
#define O_CMP(s, t) \
if(strcmp(buf, (s))==0) return t;
static OutfitType outfit_strToOutfitType(char* buf) {
O_CMP("bolt", OUTFIT_TYPE_BOLT);
O_CMP("beam", OUTFIT_TYPE_BEAM);
O_CMP("turret bolt", OUTFIT_TYPE_TURRET_BOLT);
O_CMP("turret beam", OUTFIT_TYPE_TURRET_BEAM);
O_CMP("missile dumb", OUTFIT_TYPE_MISSILE_DUMB);
O_CMP("missile dumb ammo", OUTFIT_TYPE_MISSILE_DUMB_AMMO);
O_CMP("missile seek", OUTFIT_TYPE_MISSILE_SEEK);
O_CMP("missile seek ammo", OUTFIT_TYPE_MISSILE_SEEK_AMMO);
O_CMP("missile smart", OUTFIT_TYPE_MISSILE_SEEK_SMART);
O_CMP("missile smart ammo", OUTFIT_TYPE_MISSILE_SEEK_SMART_AMMO);
O_CMP("missile swarm", OUTFIT_TYPE_MISSILE_SWARM);
O_CMP("missile swarm ammo", OUTFIT_TYPE_MISSILE_SWARM_AMMO);
O_CMP("missile swarm smart", OUTFIT_TYPE_MISSILE_SWARM_SMART);
O_CMP("missile swarm smart ammo", OUTFIT_TYPE_MISSILE_SWARM_SMART_AMMO);
O_CMP("modification", OUTFIT_TYPE_MODIFICATION);
O_CMP("afterburner", OUTFIT_TYPE_AFTERBURNER);
O_CMP("map", OUTFIT_TYPE_MAP);
O_CMP("jammer", OUTFIT_TYPE_JAMMER);
WARN("Invalid outfit type '%s'", buf);
return OUTFIT_TYPE_NULL;
}
#undef O_CMP
static int outfit_parseDamage(DamageType* dtype, double* dmg, xmlNodePtr node) {
char* buf;
if(xml_isNode(node, "damage")) {
/* Get type. */
xmlr_attr(node, "type", buf);
(*dtype) = outfit_strToDamageType(buf);
if(buf) free(buf);
/* Get damage. */
(*dmg) = xml_getFloat(node);
return 0;
}
/* Unknown type. */
(*dtype) = DAMAGE_TYPE_NULL;
(*dmg) = 0;
WARN("Trying to parse non-damage node as damage node!");
return 1;
}
/* Parses the specific area for a weapon and loads it into outfit. */
static void outfit_parseSWeapon(Outfit* tmp, const xmlNodePtr parent) {
xmlNodePtr node;
char str[PATH_MAX] = "\0";
/* Defaults. */
tmp->u.blt.sound = -1;
node = parent->xmlChildrenNode;
do {
/* Load all the things. */
xmlr_float(node, "speed", tmp->u.blt.speed);
xmlr_float(node, "delay", tmp->u.blt.delay);
xmlr_float(node, "range", tmp->u.blt.range);
xmlr_float(node, "accuracy", tmp->u.blt.accuracy);
xmlr_float(node, "energy", tmp->u.blt.energy);
if(xml_isNode(node, "gfx")) {
snprintf(str, strlen(xml_get(node))+sizeof(OUTFIT_GFX)+10,
OUTFIT_GFX"space/%s.png", xml_get(node));
tmp->u.blt.gfx_space = gl_newSprite(str, 6, 6);
}
else if(xml_isNode(node, "spfx"))
tmp->u.blt.spfx = spfx_get(xml_get(node));
else if(xml_isNode(node, "sound"))
tmp->u.blt.sound = sound_get(xml_get(node));
else if(xml_isNode(node, "damage"))
outfit_parseDamage(&tmp->u.blt.dtype, &tmp->u.blt.damage, node);
} while((node = node->next));
#define MELEMENT(o,s) \
if (o) WARN("Outfit '%s' missing/invalid '"s"' element", tmp->name)
MELEMENT(tmp->u.blt.gfx_space==NULL, "gfx");
MELEMENT((sound_disabled!=NULL) && (tmp->u.blt.sound==0), "sound");
MELEMENT(tmp->u.blt.delay==0, "delay");
MELEMENT(tmp->u.blt.speed==0, "speed");
MELEMENT(tmp->u.blt.range==0, "range");
MELEMENT(tmp->u.blt.accuracy==0, "accuracy");
MELEMENT(tmp->u.blt.damage==0, "damage");
#undef MELEMENT
}
/* Parse the specific area for a launcher and loads it into Outfit. */
static void outfit_parseSLauncher(Outfit* tmp, const xmlNodePtr parent) {
xmlNodePtr node;
node = parent->xmlChildrenNode;
do {
/* Load the dataz. */
if(xml_isNode(node, "delay")) tmp->u.lau.delay = xml_getInt(node);
else if(xml_isNode(node, "ammo")) tmp->u.lau.ammo = strdup(xml_get(node));
} while((node = node->next));
#define MELEMENT(o,s) if(o) WARN("Outfit '%s' missing '"s"' element", tmp->name)
MELEMENT(tmp->u.lau.ammo == NULL, "ammo");
MELEMENT(tmp->u.lau.delay==0, "delay");
#undef MELEMENT
}
/* Parse the specific area for a weapon and load it into Outfit. */
static void outfit_parseSAmmo(Outfit* tmp, const xmlNodePtr parent) {
xmlNodePtr node;
node = parent->xmlChildrenNode;
char str[PATH_MAX] = "\0";
do {
/* Basic. */
xmlr_float(node, "duration", tmp->u.amm.duration);
xmlr_float(node, "lockon", tmp->u.amm.lockon);
xmlr_float(node, "resist", tmp->u.amm.resist);
/* Movement. */
xmlr_float(node, "thrust", tmp->u.amm.thrust);
xmlr_float(node, "turn", tmp->u.amm.turn);
xmlr_float(node, "speed", tmp->u.amm.speed);
xmlr_float(node, "energy", tmp->u.amm.energy);
if(xml_isNode(node, "gfx")) {
snprintf(str, strlen(xml_get(node))+sizeof(OUTFIT_GFX)+10,
OUTFIT_GFX"space/%s.png", xml_get(node));
tmp->u.amm.gfx_space = gl_newSprite(str, 6, 6);
continue;
}
else if(xml_isNode(node, "spfx"))
tmp->u.amm.spfx = spfx_get(xml_get(node));
else if(xml_isNode(node, "sound"))
tmp->u.amm.sound = sound_get(xml_get(node));
else if(xml_isNode(node, "damage"))
outfit_parseDamage(&tmp->u.amm.dtype, &tmp->u.amm.damage, node);
} while((node = node->next));
/* Post-processing. */
tmp->u.amm.resist /= 100.; /* Set it in per one. */
#define MELEMENT(o,s) if(o) WARN("Outfit '%s' missing '"s"' element", tmp->name)
MELEMENT(tmp->u.amm.gfx_space == NULL, "gfx");
MELEMENT((sound_disabled != NULL) && (tmp->u.amm.sound == 0), "sound");
MELEMENT(tmp->u.amm.thrust==0, "thrust");
/* Dumb missiles don't need everything. */
if(tmp->type != OUTFIT_TYPE_MISSILE_DUMB_AMMO) {
MELEMENT(tmp->u.amm.turn==0, "turn");
MELEMENT(tmp->u.amm.lockon==0, "lockon");
}
MELEMENT(tmp->u.amm.speed==0, "speed");
MELEMENT(tmp->u.amm.duration==0, "duration");
MELEMENT(tmp->u.amm.damage==0, "damage");
#undef MELEMENT
}
static void outfit_parseSMod(Outfit* tmp, const xmlNodePtr parent) {
xmlNodePtr node;
node = parent->children;
/* Load all the data. */
do {
/* Movement. */
xmlr_float(node, "thrust", tmp->u.mod.thrust);
xmlr_float(node, "turn", tmp->u.mod.turn);
xmlr_float(node, "speed", tmp->u.mod.speed);
/* Health. */
xmlr_float(node, "armour", tmp->u.mod.armour);
xmlr_float(node, "shield", tmp->u.mod.shield);
xmlr_float(node, "energy", tmp->u.mod.energy);
xmlr_float(node, "fuel", tmp->u.mod.fuel);
if(xml_isNode(node, "armour_regen"))
tmp->u.mod.armour_regen = xml_getFloat(node)/60.0;
else if(xml_isNode(node, "shield_regen"))
tmp->u.mod.shield_regen = xml_getFloat(node)/60.0;
else if(xml_isNode(node, "energy_regen"))
tmp->u.mod.energy_regen = xml_getFloat(node)/60.0;
/* Misc. */
xmlr_int(node, "cargo", tmp->u.mod.cargo);
} while((node = node->next));
}
/* Parses the afterburner tidbits of the outfit. */
static void outfit_parseSAfterburner(Outfit* tmp, const xmlNodePtr parent) {
xmlNodePtr node;
node = parent->children;
/* Must be >= 1. */
tmp->u.afb.thrust_perc = 1.;
tmp->u.afb.speed_perc = 1.;
do {
xmlr_float(node, "rumble", tmp->u.afb.rumble);
if(xml_isNode(node, "sound"))
tmp->u.afb.sound = sound_get(xml_get(node));
if(xml_isNode(node, "thrust_perc"))
tmp->u.afb.thrust_perc = 1. + xml_getFloat(node)/100.;
xmlr_float(node, "thrust_abs", tmp->u.afb.thrust_abs);
if(xml_isNode(node, "speed_perc"))
tmp->u.afb.speed_perc = 1. + xml_getFloat(node)/100.;
xmlr_float(node, "speed_abs", tmp->u.afb.speed_abs);
xmlr_float(node, "energy", tmp->u.afb.energy);
} while((node = node->next));
}
static void outfit_parseSMap(Outfit* tmp, const xmlNodePtr parent) {
xmlNodePtr node;
node = parent->children;
do {
xmlr_int(node, "radius", tmp->u.map.radius);
} while(xml_nextNode(node));
if(tmp->u.map.radius == 0)
WARN("Outfit '%s' missing/invalid 'radius' element", tmp->name);
}
/* Parses the jammer tidbits of the outfit. */
static void outfit_parseSJammer(Outfit* tmp, const xmlNodePtr parent) {
xmlNodePtr node;
node = parent->children;
do {
xmlr_float(node, "range", tmp->u.jam.range);
xmlr_float(node, "chance", tmp->u.jam.chance);
xmlr_float(node, "energy", tmp->u.jam.energy);
} while(xml_nextNode(node));
tmp->u.jam.chance /= 100.; /* Put in per one, instead of percent. */
tmp->u.jam.energy /= 60.; /* It's per minute. */
#define MELEMENT(o, s) \
if(o) WARN("Outfit '%s' missing/invalid '"s"' element", tmp->name)
MELEMENT(tmp->u.jam.range==0., "range");
MELEMENT(tmp->u.jam.chance==0., "chance");
#undef MELEMENT
}
/* Parse and return Outfits from parent node. */
static Outfit* outfit_parse(const xmlNodePtr parent) {
Outfit* tmp = CALLOC_L(Outfit);
xmlNodePtr cur, node;
char* prop;
char str[PATH_MAX] = "\0";
tmp->name = xml_nodeProp(parent, "name"); /* Already malloced. */
if(tmp->name == NULL) WARN("Outfit in "OUTFIT_DATA" has invalid or no name");
node = parent->xmlChildrenNode;
do {
/* Load all the things. */
if(xml_isNode(node, "general")) {
cur = node->children;
do {
xmlr_int(cur, "max", tmp->max);
xmlr_int(cur, "tech", tmp->tech);
xmlr_int(cur, "mass", tmp->mass);
xmlr_int(cur, "price", tmp->price);
xmlr_strd(cur, "description", tmp->description);
if(xml_isNode(cur, "gfx_store")) {
snprintf(str, strlen(xml_get(cur))+sizeof(OUTFIT_GFX)+10,
OUTFIT_GFX"store/%s.png", xml_get(cur));
tmp->gfx_store = gl_newImage(str);
}
} while((cur = cur->next));
}
else if(xml_isNode(node, "specific")) {
/* Has to be processed seperately. */
/* Get the type. */
prop = xml_nodeProp(node, "type");
if(prop == NULL)
ERR("Outfit '%s' element 'specific' missing property 'type'", tmp->name);
tmp->type = outfit_strToOutfitType(prop);
free(prop);
/* Is this the secondary weapon? */
prop = xml_nodeProp(node, "secondary");
if(prop != NULL) {
if((int)atoi(prop)) outfit_setProp(tmp, OUTFIT_PROP_WEAP_SECONDARY);
free(prop);
}
if(tmp->type == OUTFIT_TYPE_NULL)
WARN("Outfit '%s' is of type NONE", tmp->name);
else if(outfit_isWeapon(tmp))
outfit_parseSWeapon(tmp, node);
else if(outfit_isLauncher(tmp))
outfit_parseSLauncher(tmp, node);
else if(outfit_isAmmo(tmp))
outfit_parseSAmmo(tmp, node);
else if(outfit_isTurret(tmp))
outfit_parseSWeapon(tmp, node);
else if(outfit_isMod(tmp))
outfit_parseSMod(tmp, node);
else if(outfit_isAfterburner(tmp))
outfit_parseSAfterburner(tmp, node);
else if(outfit_isMap(tmp))
outfit_parseSMap(tmp, node);
else if(outfit_isJammer(tmp))
outfit_parseSJammer(tmp, node);
}
} while((node = node->next));
#define MELEMENT(o,s) if(o) WARN("Outfit '%s' missing '"s"' element", tmp->name)
MELEMENT(tmp->name==NULL, "name");
MELEMENT(tmp->max==0, "max");
MELEMENT(tmp->tech==0, "tech");
MELEMENT(tmp->gfx_store==NULL, "gfx_store");
/*MELEMENT(tmp->mass==0, "mass"); */
MELEMENT(tmp->type==0, "type");
MELEMENT(tmp->price==0, "price");
MELEMENT(tmp->description==NULL, "description");
#undef MELEMENT
return tmp;
}
/* Load all the outfits into the outfit stack. */
int outfit_load(void) {
uint32_t bufsize;
char* buf = pack_readfile(DATA, OUTFIT_DATA, &bufsize);
Outfit* tmp;
xmlNodePtr node;
xmlDocPtr doc = xmlParseMemory(buf, bufsize);
node = doc->xmlChildrenNode;
if(!xml_isNode(node, XML_OUTFIT_ID)) {
ERR("Malformed "OUTFIT_DATA" file: missing root element '"XML_OUTFIT_ID"'");
return -1;
}
node = node->xmlChildrenNode; /* First system node. */
if(node == NULL) {
ERR("Malformed "OUTFIT_DATA" file: does not contain elements");
return -1;
}
do {
if(xml_isNode(node, XML_OUTFIT_TAG)) {
tmp = outfit_parse(node);
outfit_stack = realloc(outfit_stack, sizeof(Outfit)*(++outfit_nstack));
memcpy(outfit_stack+outfit_nstack-1, tmp, sizeof(Outfit));
free(tmp);
}
} while((node = node->next));
xmlFreeDoc(doc);
free(buf);
xmlCleanupParser();
DEBUG("Loaded %d outfit%s", outfit_nstack, (outfit_nstack==1) ? "" : "s");
return 0;
}
/* Frees the outfit stack. */
void outfit_free(void) {
int i;
for(i = 0; i < outfit_nstack; i++) {
/* Free graphics. */
if(outfit_gfx(&outfit_stack[i]))
gl_freeTexture(outfit_gfx(&outfit_stack[i]));
/* Strings. */
if(outfit_isLauncher(&outfit_stack[i]) && outfit_stack[i].u.lau.ammo)
free(outfit_stack[i].u.lau.ammo);
if(outfit_stack[i].description)
free(outfit_stack[i].description);
if(outfit_stack[i].gfx_store)
gl_freeTexture(outfit_stack[i].gfx_store);
free(outfit_stack[i].name);
}
free(outfit_stack);
}