706 lines
22 KiB
C
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);
|
|
}
|
|
|