308 lines
9.7 KiB
C
308 lines
9.7 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 1 if outfit is a weapon.
|
|
int outfit_isWeapon(const Outfit* o) {
|
|
return ((o->type == OUTFIT_TYPE_BOLT) || (o->type == OUTFIT_TYPE_BEAM));
|
|
}
|
|
|
|
// Return the broad outfit type.
|
|
const char* outfit_typenamebroad[] = { "NULL", "Weapon", "Launcher", "Ammo" };
|
|
|
|
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(0)) i = 3;
|
|
|
|
return outfit_typenamebroad[i];
|
|
}
|
|
|
|
// 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, "spfx"))
|
|
tmp->spfx = spfx_get(xml_get(node));
|
|
else if(xml_isNode(node, "sound"))
|
|
tmp->sound = sound_get(xml_get(node));
|
|
else if(xml_isNode(node, "damage")) {
|
|
cur = node->children;
|
|
do {
|
|
if(xml_isNode(cur, "armour")) tmp->damage_armour = 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)
|
|
if(tmp->gfx_space == NULL)
|
|
WARN("Outfit '%s' missing 'gfx' element", tmp->name);
|
|
MELEMENT(tmp->sound, "sound");
|
|
MELEMENT(tmp->delay, "delay");
|
|
MELEMENT(tmp->speed, "speed");
|
|
MELEMENT(tmp->range, "range");
|
|
MELEMENT(tmp->accuracy, "accuracy");
|
|
MELEMENT(tmp->damage_armour, "armour' from element 'damage");
|
|
MELEMENT(tmp->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->delay = xml_getInt(node);
|
|
else if(xml_isNode(node, "ammo")) tmp->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->ammo == NULL, "ammo");
|
|
MELEMENT(tmp->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->thrust = xml_getFloat(node);
|
|
else if(xml_isNode(node, "turn")) tmp->turn = xml_getFloat(node);
|
|
else if(xml_isNode(node, "speed")) tmp->speed = xml_getFloat(node);
|
|
else if(xml_isNode(node, "duration")) tmp->duration = 1000*(unsigned int)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, "spfx"))
|
|
tmp->spfx = spfx_get(xml_get(node));
|
|
else if(xml_isNode(node, "sound"))
|
|
tmp->sound = sound_get(xml_get(node));
|
|
else if(xml_isNode(node, "damage")) {
|
|
cur = node->children;
|
|
do {
|
|
if(xml_isNode(cur, "armour")) tmp->damage_armour = 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) WARN("Outfit '%s' missing '"s"' element", tmp->name)
|
|
MELEMENT(tmp->gfx_space == NULL, "gfx");
|
|
MELEMENT((sound_lock != NULL) && (tmp->sound == 0), "sound");
|
|
MELEMENT(tmp->thrust==0, "thrust");
|
|
MELEMENT(tmp->turn==0, "turn");
|
|
MELEMENT(tmp->speed==0, "speed");
|
|
MELEMENT(tmp->range==0, "duration");
|
|
MELEMENT(tmp->damage_armour==0, "armour' from element 'damage");
|
|
MELEMENT(tmp->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);
|
|
} 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);
|
|
}
|
|
} 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");
|
|
#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++) {
|
|
// Free graphics.
|
|
if(outfit_stack[i].gfx_space) gl_freeTexture(outfit_stack[i].gfx_space);
|
|
|
|
if(outfit_isLauncher(&outfit_stack[i]) && outfit_stack[i].ammo)
|
|
free(outfit_stack[i].ammo);
|
|
|
|
free(outfit_stack[i].name);
|
|
}
|
|
free(outfit_stack);
|
|
}
|
|
|