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