1 /*
2  * See Licensing and Copyright notice in naev.h
3  */
4 
5 /**
6  * @file damagetype.c
7  *
8  * @brief Handles damage types.
9  */
10 
11 
12 #include "damagetype.h"
13 #include "naev.h"
14 
15 #include <inttypes.h>
16 
17 #include "SDL.h"
18 
19 #include "log.h"
20 #include "pilot.h"
21 #include "pause.h"
22 #include "rng.h"
23 #include "ndata.h"
24 #include "nxml.h"
25 #include "shipstats.h"
26 
27 
28 #define DTYPE_XML_ID     "dtypes"   /**< XML Document tag. */
29 #define DTYPE_XML_TAG    "dtype"    /**< DTYPE XML node tag. */
30 
31 #define DTYPE_CHUNK_MIN  256     /**< Minimum chunk to alloc when needed */
32 
33 /**
34  * @struct DTYPE
35  *
36  * @brief A damage type.
37  */
38 typedef struct DTYPE_ {
39    char* name;    /**< Name of the damage type */
40    double sdam;   /**< Shield damage multiplier */
41    double adam;   /**< Armour damage multiplier */
42    double knock;  /**< Knockback */
43    size_t soffset; /**< Offset for shield modifier ship statistic. */
44    size_t aoffset; /**< Offset for armour modifier ship statistic. */
45 } DTYPE;
46 
47 static DTYPE* dtype_types  = NULL;  /**< Total damage types. */
48 static int dtype_ntypes    = 0;     /**< Total number of damage types. */
49 
50 
51 /*
52  * prototypes
53  */
54 static int DTYPE_parse( DTYPE *temp, const xmlNodePtr parent );
55 static void DTYPE_free( DTYPE *damtype );
56 static DTYPE* dtype_validType( int type );
57 
58 
59 /**
60  * @brief Parses an xml node containing a DTYPE.
61  *
62  *    @param temp Address to load DTYPE into.
63  *    @param parent XML Node containing the DTYPE data.
64  *    @return 0 on success.
65  */
DTYPE_parse(DTYPE * temp,const xmlNodePtr parent)66 static int DTYPE_parse( DTYPE *temp, const xmlNodePtr parent )
67 {
68    xmlNodePtr node;
69    char *stat;
70 
71    /* Clear data. */
72    memset( temp, 0, sizeof(DTYPE) );
73 
74    /* Get the name (mallocs). */
75    temp->name = xml_nodeProp(parent,"name");
76 
77    /* Extract the data. */
78    node = parent->xmlChildrenNode;
79    do {
80       xml_onlyNodes(node);
81 
82       if (xml_isNode(node, "shield")) {
83          temp->sdam = xml_getFloat(node);
84 
85          xmlr_attr(node, "stat", stat);
86          if (stat != NULL) {
87             temp->soffset = ss_offsetFromType( ss_typeFromName(stat) );
88             free(stat);
89          }
90 
91          continue;
92       }
93       else if (xml_isNode(node, "armour")) {
94          temp->adam = xml_getFloat(node);
95 
96          xmlr_attr(node, "stat", stat);
97          if (stat != NULL) {
98             temp->aoffset = ss_offsetFromType( ss_typeFromName(stat) );
99             free(stat);
100          }
101 
102          continue;
103       }
104       xmlr_float(node, "armour", temp->adam);
105       xmlr_float(node, "knockback", temp->knock);
106 
107       WARN("Unknown node of type '%s' in damage node '%s'.", node->name, temp->name);
108    } while (xml_nextNode(node));
109 
110 #define MELEMENT(o,s) \
111    if (o) WARN("DTYPE '%s' invalid '"s"' element", temp->name) /**< Define to help check for data errors. */
112    MELEMENT(temp->sdam<0.,"shield");
113    MELEMENT(temp->adam<0.,"armour");
114    MELEMENT(temp->knock<0.,"knockback");
115 #undef MELEMENT
116 
117    return 0;
118 }
119 
120 
121 /**
122  * @brief Frees a DTYPE.
123  *
124  *    @param damtype DTYPE to free.
125  */
DTYPE_free(DTYPE * damtype)126 static void DTYPE_free( DTYPE *damtype )
127 {
128    if (damtype->name != NULL) {
129       free(damtype->name);
130       damtype->name = NULL;
131    }
132 }
133 
134 
135 /**
136  * @brief Gets the id of a dtype based on name.
137  *
138  *    @param name Name to match.
139  *    @return ID of the damage type or -1 on error.
140  */
dtype_get(char * name)141 int dtype_get( char* name )
142 {
143    int i;
144    for (i=0; i<dtype_ntypes; i++)
145       if (strcmp(dtype_types[i].name, name)==0)
146          return i;
147    WARN("Damage type '%s' not found in stack.", name);
148    return -1;
149 }
150 
151 
152 /**
153  * @brief Gets the damage type.
154  */
dtype_validType(int type)155 static DTYPE* dtype_validType( int type )
156 {
157    if ((type < 0) || (type >= dtype_ntypes)) {
158       WARN("Damage type '%d' is invalid.", type);
159       return NULL;
160    }
161    return &dtype_types[ type ];
162 }
163 
164 
165 /**
166  * @brief Gets the human readable string from damage type.
167  */
dtype_damageTypeToStr(int type)168 char* dtype_damageTypeToStr( int type )
169 {
170    DTYPE *dmg = dtype_validType( type );
171    if (dmg == NULL)
172       return NULL;
173    return dmg->name;
174 }
175 
176 
177 /**
178  * @brief Loads the dtype stack.
179  *
180  *    @return 0 on success.
181  */
dtype_load(void)182 int dtype_load (void)
183 {
184    int mem;
185    uint32_t bufsize;
186    char *buf;
187    xmlNodePtr node;
188    xmlDocPtr doc;
189 
190    /* Load and read the data. */
191    buf = ndata_read( DTYPE_DATA_PATH, &bufsize );
192    doc = xmlParseMemory( buf, bufsize );
193 
194    /* Check to see if document exists. */
195    node = doc->xmlChildrenNode;
196    if (!xml_isNode(node,DTYPE_XML_ID)) {
197       ERR("Malformed '"DTYPE_DATA_PATH"' file: missing root element '"DTYPE_XML_ID"'");
198       return -1;
199    }
200 
201    /* Check to see if is populated. */
202    node = node->xmlChildrenNode; /* first system node */
203    if (node == NULL) {
204       ERR("Malformed '"DTYPE_DATA_PATH"' file: does not contain elements");
205       return -1;
206    }
207 
208    /* Load up the individual damage types. */
209    mem = 0;
210    do {
211       xml_onlyNodes(node);
212 
213       if (!xml_isNode(node,DTYPE_XML_TAG)) {
214          WARN("'"DTYPE_DATA_PATH"' has unknown node '%s'.", node->name);
215          continue;
216       }
217 
218       dtype_ntypes++;
219       if (dtype_ntypes > mem) {
220          if (mem == 0)
221             mem = DTYPE_CHUNK_MIN;
222          else
223             mem *= 2;
224          dtype_types = realloc(dtype_types, sizeof(DTYPE)*mem);
225       }
226       DTYPE_parse( &dtype_types[dtype_ntypes-1], node );
227 
228    } while (xml_nextNode(node));
229    /* Shrink back to minimum - shouldn't change ever. */
230    dtype_types = realloc(dtype_types, sizeof(DTYPE) * dtype_ntypes);
231 
232    /* Clean up. */
233    xmlFreeDoc(doc);
234    free(buf);
235 
236    return 0;
237 }
238 
239 
240 /**
241  * @brief Frees the dtype stack.
242  */
dtype_free(void)243 void dtype_free (void)
244 {
245    int i;
246 
247    /* clear the damtypes */
248    for (i=0; i<dtype_ntypes; i++)
249       DTYPE_free( &dtype_types[i] );
250    free( dtype_types );
251    dtype_types    = NULL;
252    dtype_ntypes   = 0;
253 }
254 
255 
256 /**
257  * @brief Gives the real shield damage, armour damage and knockback modifier.
258  *
259  *    @param[out] dshield Real shield damage.
260  *    @param[out] darmour Real armour damage.
261  *    @param[out] knockback Knockback modifier.
262  *    @param[in] absorb Absorption value.
263  *    @param[in] dmg Damage information.
264  */
dtype_calcDamage(double * dshield,double * darmour,double absorb,double * knockback,const Damage * dmg,ShipStats * s)265 void dtype_calcDamage( double *dshield, double *darmour, double absorb, double *knockback, const Damage *dmg, ShipStats *s )
266 {
267    DTYPE *dtype;
268    char *ptr;
269 
270    /* Must be valid. */
271    dtype = dtype_validType( dmg->type );
272    if (dtype == NULL)
273       return;
274 
275    /* Set if non-nil. */
276    if (dshield != NULL) {
277       if ((dtype->soffset <= 0) || (s == NULL))
278          *dshield    = dtype->sdam * dmg->damage * absorb;
279       else {
280          /*
281           * If an offset has been specified, look for a double at that offset
282           * in the ShipStats struct, and used it as a multiplier.
283           *
284           * The 2. - n logic serves to undo the initialization done by
285           * ss_statsInit and turn the value into a multiplier.
286           */
287          ptr = (char*) s;
288          *dshield = dtype->sdam * dmg->damage * absorb *
289                (2. - *(double*) &ptr[ dtype->soffset ]);
290       }
291    }
292    if (darmour != NULL) {
293       if ((dtype->aoffset) <= 0 || (s == NULL))
294          *darmour    = dtype->adam * dmg->damage * absorb;
295       else {
296          ptr = (char*) s;
297          *darmour = dtype->adam * dmg->damage * absorb *
298                (2. - *(double*) &ptr[ dtype->aoffset ]);
299       }
300    }
301 
302    if (knockback != NULL)
303       *knockback  = dtype->knock;
304 }
305 
306 
307