210 lines
6.1 KiB
C
210 lines
6.1 KiB
C
#include <math.h>
|
|
#include <string.h>
|
|
|
|
#include "main.h"
|
|
#include "log.h"
|
|
#include "pack.h"
|
|
#include "xml.h"
|
|
#include "outfit.h"
|
|
|
|
#define XML_OUTFIT_ID "Outfits"
|
|
#define XML_OUTFIT_TAG "outfit"
|
|
|
|
#define OUTFIT_DATA "../dat/outfit.xml"
|
|
#define OUTFIT_GFX "../gfx/outfit/"
|
|
|
|
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);
|
|
|
|
// 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 1 if outfit is a weapon.
|
|
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));
|
|
|
|
}
|
|
|
|
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"
|
|
};
|
|
|
|
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->speed = xml_getFloat(node);
|
|
else if(xml_isNode(node, "delay")) tmp->delay = xml_getInt(node);
|
|
else if(xml_isNode(node, "range")) tmp->range = xml_getFloat(node);
|
|
else if(xml_isNode(node, "accuracy")) tmp->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->gfx_space = gl_newSprite(str, 6, 6);
|
|
}
|
|
else if(xml_isNode(node, "damage")) {
|
|
cur = node->children;
|
|
do {
|
|
if(xml_isNode(cur, "armor")) tmp->damage_armor = xml_getFloat(cur);
|
|
else if(xml_isNode(cur, "shield")) tmp->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)
|
|
MELEMENT(tmp->accuracy, "tech");
|
|
MELEMENT(tmp->delay, "delay");
|
|
MELEMENT(tmp->speed, "speed");
|
|
MELEMENT(tmp->range, "range");
|
|
MELEMENT(tmp->accuracy, "accuracy");
|
|
MELEMENT(tmp->damage_armor, "armor' from element 'damage");
|
|
MELEMENT(tmp->damage_shield, "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);
|
|
} while((cur = cur->next));
|
|
}
|
|
else if(xml_isNode(node, "specific")) {
|
|
// Has to be processed seperately.
|
|
prop = xml_nodeProp(node, "type");
|
|
if(prop == NULL)
|
|
ERR("Outfit '%s' element 'specific' missing property 'type'", tmp->name);
|
|
tmp->type = atoi(prop);
|
|
free(prop);
|
|
switch(tmp->type) {
|
|
case OUTFIT_TYPE_NULL:
|
|
WARN("Outfit '%s' is of type NONE", tmp->name);
|
|
break;
|
|
case OUTFIT_TYPE_BOLT:
|
|
outfit_parseSWeapon(tmp, node);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} while((node = node->next));
|
|
#define MELEMENT(o,s) if((o) == 0) WARN("Outfit '%s' missing '"s"' element", tmp->name)
|
|
MELEMENT(tmp->max, "max");
|
|
MELEMENT(tmp->tech, "tech");
|
|
MELEMENT(tmp->mass, "mass");
|
|
MELEMENT(tmp->type, "type");
|
|
#undef MELEMENT
|
|
|
|
DEBUG("Loaded outfit '%s' of type '%s'", tmp->name, outfit_getType(tmp));
|
|
|
|
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();
|
|
return 0;
|
|
}
|
|
|
|
// Frees the outfit stack.
|
|
void outfit_free(void) {
|
|
int i;
|
|
for(i = 0; i < outfits; i++) {
|
|
if(outfit_isWeapon(&outfit_stack[i]) && outfit_stack[i].gfx_space)
|
|
gl_freeTexture(outfit_stack[i].gfx_space);
|
|
free(outfit_stack[i].name);
|
|
}
|
|
free(outfit_stack);
|
|
}
|
|
|