389 lines
12 KiB
C
389 lines
12 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/"
|
|
|
|
extern SDL_mutex* sound_lock; // Sound.c
|
|
|
|
// The Stack.
|
|
static Outfit* outfit_stack = NULL;
|
|
static int outfits = 0;
|
|
|
|
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);
|
|
|
|
// Return an outfit.
|
|
Outfit* outfit_get(const char* name) {
|
|
int i;
|
|
for(i = 0; i < outfits; i++)
|
|
if(strcmp(name, outfit_stack[i].name)==0)
|
|
return &outfit_stack[i];
|
|
return NULL;
|
|
}
|
|
|
|
// Return all the outfits.
|
|
char** outfit_getAll(int* n) {
|
|
char** outfitnames = malloc(sizeof(Outfit*) * outfits);
|
|
|
|
for((*n) = 0; (*n) < outfits; (*n)++)
|
|
outfitnames[*n] = strdup(outfit_stack[*n].name);
|
|
|
|
return outfitnames;
|
|
}
|
|
|
|
// 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));
|
|
}
|
|
|
|
// 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_dmgShield(const Outfit* o) {
|
|
if(outfit_isWeapon(o)) return o->u.blt.damage_armour;
|
|
else if(outfit_isAmmo(o)) return o->u.amm.damage_armour;
|
|
else if(outfit_isTurret(o)) return o->u.blt.damage_armour;
|
|
return -1;
|
|
}
|
|
|
|
double outfit_dmgArmour(const Outfit* o) {
|
|
if(outfit_isWeapon(o)) return o->u.blt.damage_shield;
|
|
else if(outfit_isAmmo(o)) return o->u.amm.damage_shield;
|
|
else if(outfit_isTurret(o)) return o->u.blt.damage_shield;
|
|
return -1;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
const char* outfit_typename[] = {
|
|
"NULL",
|
|
"Bolt Cannon",
|
|
"Beam Cannon",
|
|
"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",
|
|
"Bolt Turret",
|
|
"Beam Turret"
|
|
};
|
|
|
|
// Return the broad outfit type.
|
|
const char* outfit_typenamebroad[] = {
|
|
"NULL",
|
|
"Weapon",
|
|
"Launcher",
|
|
"Ammo",
|
|
"Turret"
|
|
};
|
|
|
|
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;
|
|
|
|
return outfit_typenamebroad[i];
|
|
}
|
|
|
|
const char* outfit_getType(const Outfit* o) {
|
|
return outfit_typename[o->type];
|
|
}
|
|
|
|
// Parses the specific area for a weapon and loads it into outfit.
|
|
static void outfit_parseSWeapon(Outfit* tmp, const xmlNodePtr parent) {
|
|
xmlNodePtr cur, node;
|
|
node = parent->xmlChildrenNode;
|
|
|
|
char str[PATH_MAX] = "\0";
|
|
|
|
do {
|
|
// Load all the things.
|
|
if(xml_isNode(node, "speed")) tmp->u.blt.speed = xml_getFloat(node);
|
|
else if(xml_isNode(node, "delay")) tmp->u.blt.delay = xml_getInt(node);
|
|
else if(xml_isNode(node, "range")) tmp->u.blt.range = xml_getFloat(node);
|
|
else if(xml_isNode(node, "accuracy")) tmp->u.blt.accuracy = xml_getFloat(node);
|
|
else if(xml_isNode(node, "gfx")) {
|
|
snprintf(str, strlen(xml_get(node))+sizeof(OUTFIT_GFX)+4,
|
|
OUTFIT_GFX"%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")) {
|
|
cur = node->children;
|
|
do {
|
|
if(xml_isNode(cur, "armour"))
|
|
tmp->u.blt.damage_armour = xml_getFloat(cur);
|
|
else if(xml_isNode(cur, "shield"))
|
|
tmp->u.blt.damage_shield = xml_getFloat(cur);
|
|
} while((cur = cur->next));
|
|
}
|
|
} while((node = node->next));
|
|
#define MELEMENT(o,s) if((o) == 0) \
|
|
WARN("Outfit '%s' missing '"s"' element", tmp->name)
|
|
if(tmp->u.blt.gfx_space == NULL)
|
|
WARN("Outfit '%s' missing 'gfx' element", tmp->name);
|
|
MELEMENT(tmp->u.blt.sound, "sound");
|
|
MELEMENT(tmp->u.blt.delay, "delay");
|
|
MELEMENT(tmp->u.blt.speed, "speed");
|
|
MELEMENT(tmp->u.blt.range, "range");
|
|
MELEMENT(tmp->u.blt.accuracy, "accuracy");
|
|
MELEMENT(tmp->u.blt.damage_armour, "armour' from element 'damage");
|
|
MELEMENT(tmp->u.blt.damage_shield, "shield' from element '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 cur, node;
|
|
node = parent->xmlChildrenNode;
|
|
|
|
char str[PATH_MAX] = "\0";
|
|
|
|
do {
|
|
if(xml_isNode(node, "thrust")) tmp->u.amm.thrust = xml_getFloat(node);
|
|
else if(xml_isNode(node, "turn")) tmp->u.amm.turn = xml_getFloat(node);
|
|
else if(xml_isNode(node, "speed")) tmp->u.amm.speed = xml_getFloat(node);
|
|
else if(xml_isNode(node, "duration"))
|
|
tmp->u.amm.duration = (unsigned int)1000.*xml_getFloat(node);
|
|
else if(xml_isNode(node, "lockon"))
|
|
tmp->u.amm.lockon = (unsigned int)1000.*xml_getFloat(node);
|
|
else if(xml_isNode(node, "gfx")) {
|
|
snprintf(str, strlen(xml_get(node))+sizeof(OUTFIT_GFX)+4,
|
|
OUTFIT_GFX"%s.png", xml_get(node));
|
|
tmp->u.amm.gfx_space = gl_newSprite(str, 6, 6);
|
|
}
|
|
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")) {
|
|
cur = node->children;
|
|
do {
|
|
if(xml_isNode(cur, "armour")) tmp->u.amm.damage_armour = xml_getFloat(cur);
|
|
else if(xml_isNode(cur, "shield")) tmp->u.amm.damage_shield = xml_getFloat(cur);
|
|
} while((cur = cur->next));
|
|
}
|
|
} while((node = node->next));
|
|
#define MELEMENT(o,s) if(o) WARN("Outfit '%s' missing '"s"' element", tmp->name)
|
|
MELEMENT(tmp->u.amm.gfx_space == NULL, "gfx");
|
|
MELEMENT((sound_lock != NULL) && (tmp->u.amm.sound == 0), "sound");
|
|
MELEMENT(tmp->u.amm.thrust==0, "thrust");
|
|
MELEMENT(tmp->u.amm.turn==0, "turn");
|
|
MELEMENT(tmp->u.amm.speed==0, "speed");
|
|
MELEMENT(tmp->u.amm.duration==0, "duration");
|
|
MELEMENT(tmp->u.amm.lockon==0, "lockon");
|
|
MELEMENT(tmp->u.amm.damage_armour==0, "armour' from element 'damage");
|
|
MELEMENT(tmp->u.amm.damage_shield==0, "shield' from element 'damage");
|
|
#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;
|
|
|
|
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 {
|
|
if(xml_isNode(cur, "max")) tmp->max = xml_getInt(cur);
|
|
else if(xml_isNode(cur, "tech")) tmp->tech = xml_getInt(cur);
|
|
else if(xml_isNode(cur, "mass")) tmp->mass = xml_getInt(cur);
|
|
else if(xml_isNode(cur, "price")) tmp->price = xml_getInt(cur);
|
|
else if(xml_isNode(cur, "description")) tmp->description = strdup(xml_get(cur));
|
|
} 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 = atoi(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);
|
|
}
|
|
} 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->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)*(++outfits));
|
|
memcpy(outfit_stack+outfits-1, tmp, sizeof(Outfit));
|
|
free(tmp);
|
|
}
|
|
} while((node = node->next));
|
|
|
|
xmlFreeDoc(doc);
|
|
free(buf);
|
|
xmlCleanupParser();
|
|
|
|
DEBUG("Loaded %d outfit%s", outfits, (outfits==1) ? "" : "s");
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Frees the outfit stack.
|
|
void outfit_free(void) {
|
|
int i;
|
|
for(i = 0; i < outfits; 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);
|
|
|
|
free(outfit_stack[i].name);
|
|
}
|
|
free(outfit_stack);
|
|
}
|
|
|