Skip to content
Fetching contributors…
Cannot retrieve contributors at this time
652 lines (538 sloc) 17.7 KB
/*
* See Licensing and Copyright notice in naev.h
*/
/**
* @file shipstats.c
*
* @brief Handles the ship statistics.
*/
#include "shipstats.h"
#include "naev.h"
#include "log.h"
#include "nstring.h"
/**
* @brief The data type.
*/
typedef enum StatDataType_ {
SS_DATA_TYPE_DOUBLE, /**< Relative [0:inf] value. */
SS_DATA_TYPE_DOUBLE_ABSOLUTE, /**< Absolute double value. */
SS_DATA_TYPE_INTEGER, /**< Absolute integer value. */
SS_DATA_TYPE_BOOLEAN /**< Boolean value, defaults 0. */
} StatDataType;
/**
* @brief Internal look up table for ship stats.
*
* Makes it much easier to work with stats at the cost of some minor performance.
*/
typedef struct ShipStatsLookup_ {
/* Explicitly set. */
ShipStatsType type; /**< Type of the stat. */
const char *name; /**< Name to look into XML for, must match name in the structure. */
const char *display; /**< Display name for visibility by player. */
StatDataType data; /**< Type of data for the stat. */
int inverted; /**< Indicates whether the good value is inverted, by
default positive is good, with this set negative
is good. */
/* Self calculated. */
size_t offset; /**< Stores the byte offset in the structure. */
} ShipStatsLookup;
/* Flexible do everything macro. */
#define ELEM( t, n, dsp, d , i) \
{ .type=t, .name=#n, .display=dsp, .data=d, .inverted=i, .offset=offsetof( ShipStats, n ) }
/* Standard types. */
#define D__ELEM( t, n, dsp ) \
ELEM( t, n, dsp, SS_DATA_TYPE_DOUBLE, 0 )
#define A__ELEM( t, n, dsp ) \
ELEM( t, n, dsp, SS_DATA_TYPE_DOUBLE_ABSOLUTE, 0 )
#define I__ELEM( t, n, dsp ) \
ELEM( t, n, dsp, SS_DATA_TYPE_INTEGER, 0 )
#define B__ELEM( t, n, dsp ) \
ELEM( t, n, dsp, SS_DATA_TYPE_BOOLEAN, 0 )
/* Inverted types. */
#define DI_ELEM( t, n, dsp ) \
ELEM( t, n, dsp, SS_DATA_TYPE_DOUBLE, 1 )
#define AI_ELEM( t, n, dsp ) \
ELEM( t, n, dsp, SS_DATA_TYPE_DOUBLE_ABSOLUTE, 1 )
#define II_ELEM( t, n, dsp ) \
ELEM( t, n, dsp, SS_DATA_TYPE_INTEGER, 1 )
#define BI_ELEM( t, n, dsp ) \
ELEM( t, n, dsp, SS_DATA_TYPE_BOOLEAN, 1 )
/** Nil element. */
#define N__ELEM( t ) \
{ .type=t, .name=NULL, .display=NULL, .inverted=0, .offset=0 }
/**
* The ultimate look up table for ship stats, everything goes through this.
*/
static const ShipStatsLookup ss_lookup[] = {
/* Null element. */
N__ELEM( SS_TYPE_NIL ),
D__ELEM( SS_TYPE_D_SPEED_MOD, speed_mod, "Speed" ),
D__ELEM( SS_TYPE_D_TURN_MOD, turn_mod, "Turn" ),
D__ELEM( SS_TYPE_D_THRUST_MOD, thrust_mod, "Thrust" ),
D__ELEM( SS_TYPE_D_CARGO_MOD, cargo_mod, "Cargo space" ),
D__ELEM( SS_TYPE_D_ARMOUR_MOD, armour_mod, "Armour strength" ),
D__ELEM( SS_TYPE_D_ARMOUR_REGEN_MOD, armour_regen_mod, "Armour regeneration" ),
D__ELEM( SS_TYPE_D_SHIELD_MOD, shield_mod, "Shield strength" ),
D__ELEM( SS_TYPE_D_SHIELD_REGEN_MOD, shield_regen_mod, "Shield regeneration" ),
D__ELEM( SS_TYPE_D_ENERGY_MOD, energy_mod, "Energy capacity" ),
D__ELEM( SS_TYPE_D_ENERGY_REGEN_MOD, energy_regen_mod, "Energy regeneration" ),
D__ELEM( SS_TYPE_D_CPU_MOD, cpu_mod, "CPU capacity" ),
DI_ELEM( SS_TYPE_D_JUMP_DELAY, jump_delay, "Jump Time" ),
DI_ELEM( SS_TYPE_D_CARGO_INERTIA, cargo_inertia, "Cargo Inertia" ),
D__ELEM( SS_TYPE_D_EW_HIDE, ew_hide, "Cloaking" ),
D__ELEM( SS_TYPE_D_EW_DETECT, ew_detect, "Detection" ),
D__ELEM( SS_TYPE_D_EW_JUMPDETECT, ew_jump_detect, "Jump Detection" ),
D__ELEM( SS_TYPE_D_LAUNCH_RATE, launch_rate, "Launch Rate" ),
D__ELEM( SS_TYPE_D_LAUNCH_RANGE, launch_range, "Launch Range" ),
D__ELEM( SS_TYPE_D_AMMO_CAPACITY, ammo_capacity, "Ammo Capacity" ),
D__ELEM( SS_TYPE_D_LAUNCH_LOCKON, launch_lockon, "Launch Lockon" ),
DI_ELEM( SS_TYPE_D_FORWARD_HEAT, fwd_heat, "Heat (Cannon)" ),
D__ELEM( SS_TYPE_D_FORWARD_DAMAGE, fwd_damage, "Damage (Cannon)" ),
D__ELEM( SS_TYPE_D_FORWARD_FIRERATE, fwd_firerate, "Fire Rate (Cannon)" ),
DI_ELEM( SS_TYPE_D_FORWARD_ENERGY, fwd_energy, "Energy Usage (Cannon)" ),
DI_ELEM( SS_TYPE_D_TURRET_HEAT, tur_heat, "Heat (Turret)" ),
D__ELEM( SS_TYPE_D_TURRET_DAMAGE, tur_damage, "Damage (Turret)" ),
D__ELEM( SS_TYPE_D_TURRET_TRACKING, tur_tracking, "Tracking (Turret)" ),
D__ELEM( SS_TYPE_D_TURRET_FIRERATE, tur_firerate, "Fire Rate (Turret)" ),
DI_ELEM( SS_TYPE_D_TURRET_ENERGY, tur_energy, "Energy Usage (Turret)" ),
D__ELEM( SS_TYPE_D_NEBULA_ABSORB_SHIELD, nebu_absorb_shield, "Nebula Resistance (Shield)" ),
D__ELEM( SS_TYPE_D_NEBULA_ABSORB_ARMOUR, nebu_absorb_armour, "Nebula Resistance (Armour)" ),
D__ELEM( SS_TYPE_D_HEAT_DISSIPATION, heat_dissipation, "Heat Dissipation" ),
D__ELEM( SS_TYPE_D_STRESS_DISSIPATION, stress_dissipation, "Stress Dissipation" ),
D__ELEM( SS_TYPE_D_CREW, crew_mod, "Crew" ),
D__ELEM( SS_TYPE_D_MASS, mass_mod, "Mass" ),
D__ELEM( SS_TYPE_D_ENGINE_LIMIT_REL, engine_limit_rel, "Engine Mass Limit" ),
A__ELEM( SS_TYPE_A_ENERGY_FLAT, energy_flat, "Energy Capacity" ),
AI_ELEM( SS_TYPE_A_ENERGY_REGEN_FLAT, energy_usage, "Energy Usage" ),
A__ELEM( SS_TYPE_A_CPU_MAX, cpu_max, "CPU Capacity" ),
A__ELEM( SS_TYPE_A_ENGINE_LIMIT, engine_limit, "Engine Mass Limit" ),
I__ELEM( SS_TYPE_I_HIDDEN_JUMP_DETECT, misc_hidden_jump_detect, "Hidden Jump Detection" ),
B__ELEM( SS_TYPE_B_INSTANT_JUMP, misc_instant_jump, "Instant Jump" ),
B__ELEM( SS_TYPE_B_REVERSE_THRUST, misc_reverse_thrust, "Reverse Thrusters" ),
/* Sentinel. */
N__ELEM( SS_TYPE_SENTINEL )
};
/*
* Prototypes.
*/
static const char* ss_printD_colour( double d, const ShipStatsLookup *sl );
static const char* ss_printI_colour( int i, const ShipStatsLookup *sl );
static int ss_printD( char *buf, int len, int newline, double d, const ShipStatsLookup *sl );
static int ss_printA( char *buf, int len, int newline, double d, const ShipStatsLookup *sl );
static int ss_printI( char *buf, int len, int newline, int i, const ShipStatsLookup *sl );
static int ss_printB( char *buf, int len, int newline, int b, const ShipStatsLookup *sl );
/**
* @brief Creatse a shipstat list element from an xml node.
*
* @param node Node to create element from.
* @return Liste lement created from node.
*/
ShipStatList* ss_listFromXML( xmlNodePtr node )
{
const ShipStatsLookup *sl;
ShipStatList *ll;
ShipStatsType type;
/* Try to get type. */
type = ss_typeFromName( (char*) node->name );
if (type == SS_TYPE_NIL)
return NULL;
/* Allocate. */
ll = malloc( sizeof(ShipStatList) );
ll->next = NULL;
ll->target = 0;
ll->type = type;
/* Set the data. */
sl = &ss_lookup[ type ];
switch (sl->data) {
case SS_DATA_TYPE_DOUBLE:
ll->d.d = xml_getFloat(node) / 100.;
break;
case SS_DATA_TYPE_DOUBLE_ABSOLUTE:
ll->d.d = xml_getFloat(node);
break;
case SS_DATA_TYPE_BOOLEAN:
ll->d.i = !!xml_getInt(node);
break;
case SS_DATA_TYPE_INTEGER:
ll->d.i = xml_getInt(node);
break;
}
return ll;
}
/**
* @brief Checks for sanity.
*/
int ss_check (void)
{
ShipStatsType i;
for (i=0; i<=SS_TYPE_SENTINEL; i++) {
if (ss_lookup[i].type != i) {
WARN("ss_lookup: %s should have id %d but has %d",
ss_lookup[i].name, i, ss_lookup[i].type );
return -1;
}
}
return 0;
}
/**
* @brief Initializes a stat structure.
*/
int ss_statsInit( ShipStats *stats )
{
int i;
char *ptr;
double *dbl;
const ShipStatsLookup *sl;
/* Clear the memory. */
memset( stats, 0, sizeof(ShipStats) );
ptr = (char*) stats;
for (i=0; i<SS_TYPE_SENTINEL; i++) {
sl = &ss_lookup[ i ];
/* Only want valid names. */
if (sl->name == NULL)
continue;
/* Handle doubles. */
switch (sl->data) {
case SS_DATA_TYPE_DOUBLE:
dbl = (double*) &ptr[ sl->offset ];
*dbl = 1.0;
break;
/* No need to set, memset does the work. */
case SS_DATA_TYPE_DOUBLE_ABSOLUTE:
case SS_DATA_TYPE_INTEGER:
case SS_DATA_TYPE_BOOLEAN:
break;
}
}
return 0;
}
/**
* @brief Modifies a stat structure using a single element.
*
* @param stats Stat structure to modify.
* @param list Single element to apply.
* @param amount If non nil stores the number found in amount.
* @return 0 on success.
*/
int ss_statsModSingle( ShipStats *stats, const ShipStatList* list, const ShipStats *amount )
{
char *ptr;
double *dbl;
int *i;
const ShipStatsLookup *sl = &ss_lookup[ list->type ];
ptr = (char*) stats;
switch (sl->data) {
case SS_DATA_TYPE_DOUBLE:
case SS_DATA_TYPE_DOUBLE_ABSOLUTE:
dbl = (double*) &ptr[ sl->offset ];
*dbl += list->d.d;
if ((sl->data==SS_DATA_TYPE_DOUBLE) && (*dbl < 0.)) /* Don't let the values go negative. */
*dbl = 0.;
/* We'll increment amount. */
if (amount != NULL) {
if ((sl->inverted && (list->d.d < 0.)) ||
(!sl->inverted && (list->d.d > 0.))) {
ptr = (char*) amount;
dbl = (double*) &ptr[ sl->offset ];
(*dbl) += 1.0;
}
}
break;
case SS_DATA_TYPE_INTEGER:
i = (int*) &ptr[ sl->offset ];
*i += list->d.i;
if (amount != NULL) {
if ((sl->inverted && (list->d.i < 0)) ||
(!sl->inverted && (list->d.i > 0))) {
ptr = (char*) amount;
i = (int*) &ptr[ sl->offset ];
(*i) += 1;
}
}
break;
case SS_DATA_TYPE_BOOLEAN:
i = (int*) &ptr[ sl->offset ];
*i = 1; /* Can only set to true. */
break;
}
return 0;
}
/**
* @brief Updates a stat structure from a stat list.
*
* @param stats Stats to update.
* @param list List to update from.
* @param amount If non nil stores the number found in amount.
*/
int ss_statsModFromList( ShipStats *stats, const ShipStatList* list, const ShipStats *amount )
{
int ret;
const ShipStatList *ll;
ret = 0;
for (ll = list; ll != NULL; ll = ll->next)
ret |= ss_statsModSingle( stats, ll, amount );
return ret;
}
/**
* @brief Gets the name from type.
*
* O(1) look up.
*
* @param type Type to get name of.
* @return Name of the type.
*/
const char* ss_nameFromType( ShipStatsType type )
{
return ss_lookup[ type ].name;
}
/**
* @brief Gets the offset from type.
*
* @param type Type to get offset of.
* @return Offset of the type.
*/
size_t ss_offsetFromType( ShipStatsType type )
{
return ss_lookup[ type ].offset;
}
/**
* @brief Gets the type from the name.
*
* @param name Name to get type of.
* @return Type matching the name.
*/
ShipStatsType ss_typeFromName( const char *name )
{
int i;
for (i=0; i<SS_TYPE_SENTINEL; i++)
if ((ss_lookup[i].name != NULL) && (strcmp(name,ss_lookup[i].name)==0))
return ss_lookup[i].type;
WARN("ss_typeFromName: No ship stat matching '%s'", name);
return SS_TYPE_NIL;
}
/**
* @brief Some colour coding for ship stats doubles.
*/
static const char* ss_printD_colour( double d, const ShipStatsLookup *sl )
{
if (sl->inverted) {
if (d < 0.)
return "D";
return "r";
}
if (d > 0.)
return "D";
return "r";
}
/**
* @brief Some colour coding for ship stats integers.
*/
static const char* ss_printI_colour( int i, const ShipStatsLookup *sl )
{
if (sl->inverted) {
if (i < 0)
return "D";
return "r";
}
if (i > 0)
return "D";
return "r";
}
/**
* @brief Helper to print doubles.
*/
static int ss_printD( char *buf, int len, int newline, double d, const ShipStatsLookup *sl )
{
if (fabs(d) < 1e-10)
return 0;
return nsnprintf( buf, len, "%s\e%s%+.0f%% %s\e0",
(newline) ? "\n" : "",
ss_printD_colour( d, sl ),
d*100., sl->display );
}
/**
* @brief Helper to print absolute doubles.
*/
static int ss_printA( char *buf, int len, int newline, double d, const ShipStatsLookup *sl )
{
if (fabs(d) < 1e-10)
return 0;
return nsnprintf( buf, len, "%s\e%s%+.0f %s\e0",
(newline) ? "\n" : "",
ss_printD_colour( d, sl ),
d, sl->display );
}
/**
* @brief Helper to print integers.
*/
static int ss_printI( char *buf, int len, int newline, int i, const ShipStatsLookup *sl )
{
if (i == 0)
return 0;
return nsnprintf( buf, len, "%s\e%s%+d %s\e0",
(newline) ? "\n" : "",
ss_printI_colour( i, sl ),
i, sl->display );
}
/**
* @brief Helper to print booleans.
*/
static int ss_printB( char *buf, int len, int newline, int b, const ShipStatsLookup *sl )
{
if (!b)
return 0;
return nsnprintf( buf, len, "%s\e%s%s\e0",
(newline) ? "\n" : "",
ss_printI_colour( b, sl ),
sl->display );
}
/**
* @brief Writes the ship statistics description.
*
* @param s Ship stats to use.
* @param buf Buffer to write to.
* @param len Space left in the buffer.
* @param newline Add a newline at start.
* @return Number of characters written.
*/
int ss_statsListDesc( const ShipStatList *ll, char *buf, int len, int newline )
{
int i, left, newl;
const ShipStatsLookup *sl;
i = 0;
newl = newline;
for ( ; ll != NULL; ll=ll->next) {
left = len-i;
if (left < 0)
break;
sl = &ss_lookup[ ll->type ];
switch (sl->data) {
case SS_DATA_TYPE_DOUBLE:
i += ss_printD( &buf[i], left, newl, ll->d.d, sl );
break;
case SS_DATA_TYPE_DOUBLE_ABSOLUTE:
i += ss_printA( &buf[i], left, newl, ll->d.d, sl );
break;
case SS_DATA_TYPE_INTEGER:
i += ss_printI( &buf[i], left, newl, ll->d.i, sl );
break;
case SS_DATA_TYPE_BOOLEAN:
i += ss_printB( &buf[i], left, newl, ll->d.i, sl );
break;
}
newl = 1;
}
return i;
}
/**
* @brief Writes the ship statistics description.
*
* @param s Ship stats to use.
* @param buf Buffer to write to.
* @param len Space left in the buffer.
* @param newline Add a newline at start.
* @param pilot Stats come from a pilot.
* @return Number of characters written.
*/
int ss_statsDesc( const ShipStats *s, char *buf, int len, int newline )
{
int i, l, left;
char *ptr;
double *dbl;
int *num;
const ShipStatsLookup *sl;
l = 0;
ptr = (char*) s;
for (i=0; i<SS_TYPE_SENTINEL; i++) {
sl = &ss_lookup[ i ];
/* Only want valid names. */
if (sl->name == NULL)
continue;
/* Calculate offset left. */
left = len-l;
if (left < 0)
break;
switch (sl->data) {
case SS_DATA_TYPE_DOUBLE:
dbl = (double*) &ptr[ sl->offset ];
l += ss_printD( &buf[l], left, (newline||(l!=0)), ((*dbl)-1.), sl );
break;
case SS_DATA_TYPE_DOUBLE_ABSOLUTE:
dbl = (double*) &ptr[ sl->offset ];
l += ss_printA( &buf[l], left, (newline||(l!=0)), (*dbl), sl );
break;
case SS_DATA_TYPE_INTEGER:
num = (int*) &ptr[ sl->offset ];
l += ss_printI( &buf[l], left, (newline||(l!=0)), (*num), sl );
break;
case SS_DATA_TYPE_BOOLEAN:
num = (int*) &ptr[ sl->offset ];
l += ss_printB( &buf[l], left, (newline||(l!=0)), (*num), sl );
break;
}
}
return l;
}
/**
* @brief Generates CSV output for the given ship stats.
*
* @param s Ship stats to use.
* @param[out] buf Buffer to write to.
* @param len Space left in the buffer.
* @return Number of characters written.
*/
int ss_csv( const ShipStats *s, char *buf, int len )
{
int i, l, left, num;
double dbl;
char *ptr;
const ShipStatsLookup *sl;
l = 0;
ptr = (char*) s;
for (i=0; i<SS_TYPE_SENTINEL; i++) {
sl = &ss_lookup[ i ];
/* Only want valid names. */
if (sl->name == NULL)
continue;
/* Calculate offset left. */
left = len - l;
if (left < 0) {
WARN("Buffer out of space, CSV output truncated");
break;
}
if (s == NULL) {
l += nsnprintf( &buf[l], left, "%s,", sl->name );
continue;
}
switch (sl->data) {
case SS_DATA_TYPE_DOUBLE:
dbl = *(double*) &ptr[ sl->offset ];
l += nsnprintf( &buf[l], left, "%f,", (dbl - 1.) * 100. );
break;
case SS_DATA_TYPE_DOUBLE_ABSOLUTE:
dbl = *(double*) &ptr[ sl->offset ];
l += nsnprintf( &buf[l], left, "%f,", dbl );
break;
case SS_DATA_TYPE_INTEGER:
case SS_DATA_TYPE_BOOLEAN:
num = *(int*) &ptr[ sl->offset ];
l += nsnprintf( &buf[l], left, "%d,", num );
break;
}
}
buf[l-1] = '\n'; /* Terminate with a newline. */
return l;
}
/**
* @brief Frees a list of ship stats.
*
* @param ll List to free.
*/
void ss_free( ShipStatList *ll )
{
while (ll != NULL) {
ShipStatList *tmp = ll;
ll = ll->next;
free(tmp);
}
}
Jump to Line
Something went wrong with that request. Please try again.