naev 0.10.4
commodity.c
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
14#include <stdio.h>
15#include <stdint.h>
16#include "physfs.h"
17
18#include "naev.h"
21#include "commodity.h"
22
23#include "conf.h"
24#include "array.h"
25#include "economy.h"
26#include "gatherable.h"
27#include "hook.h"
28#include "log.h"
29#include "ndata.h"
30#include "nstring.h"
31#include "ntime.h"
32#include "nxml.h"
33#include "pilot.h"
34#include "player.h"
35#include "rng.h"
36#include "space.h"
37#include "spfx.h"
38
39#define XML_COMMODITY_ID "commodity"
40#define CRED_TEXT_MAX (ECON_CRED_STRLEN-4) /* Maximum length of just credits2str text, no markup */
41
42/* commodity stack */
44static Commodity** commodity_temp = NULL;
46/* @TODO remove externs. */
47extern int *econ_comm;
48
49/*
50 * Prototypes.
51 */
52/* Commodity. */
53static void commodity_freeOne( Commodity* com );
54static int commodity_parse( Commodity *temp, const char *filename );
55
63void credits2str( char *str, credits_t credits, int decimals )
64{
65 if (decimals < 0) {
66 /* TODO support , separator like fmt.credits(). */
67 snprintf( str, CRED_TEXT_MAX, _("%.*f ¤"), 0, (double)credits );
68 }
69 else if (credits >= 1000000000000000000LL)
70 snprintf( str, CRED_TEXT_MAX, _("%.*f E¤"), decimals, (double)credits / 1e18 );
71 else if (credits >= 1000000000000000LL)
72 snprintf( str, CRED_TEXT_MAX, _("%.*f P¤"), decimals, (double)credits / 1e15 );
73 else if (credits >= 1000000000000LL)
74 snprintf( str, CRED_TEXT_MAX, _("%.*f T¤"), decimals, (double)credits / 1e12 );
75 else if (credits >= 1000000000L)
76 snprintf( str, CRED_TEXT_MAX, _("%.*f G¤"), decimals, (double)credits / 1e9 );
77 else if (credits >= 1000000)
78 snprintf( str, CRED_TEXT_MAX, _("%.*f M¤"), decimals, (double)credits / 1e6 );
79 else if (credits >= 1000)
80 snprintf( str, CRED_TEXT_MAX, _("%.*f k¤"), decimals, (double)credits / 1e3 );
81 else
82 snprintf (str, CRED_TEXT_MAX, _("%.*f ¤"), decimals, (double)credits );
83}
84
93void price2str(char *str, credits_t price, credits_t credits, int decimals )
94{
95 char buf[ CRED_TEXT_MAX ];
96
97 if (price <= credits) {
98 credits2str( str, price, decimals );
99 return;
100 }
101
102 credits2str( buf, price, decimals );
103 snprintf( str, ECON_CRED_STRLEN, "#r%s#0", (char*)buf );
104}
105
112void tonnes2str( char *str, int tonnes )
113{
114 snprintf( str, ECON_MASS_STRLEN, n_( "%d tonne", "%d tonnes", tonnes ), tonnes );
115}
116
120Commodity* commodity_getAll (void)
121{
122 return commodity_stack;
123}
124
131Commodity* commodity_get( const char* name )
132{
133 Commodity *c = commodity_getW( name );
134 if (c!=NULL)
135 return c;
136 WARN(_("Commodity '%s' not found in stack"), name);
137 return NULL;
138}
139
146Commodity* commodity_getW( const char* name )
147{
148 for (int i=0; i<array_size(commodity_stack); i++)
149 if (strcmp(commodity_stack[i].name, name) == 0)
150 return &commodity_stack[i];
151 for (int i=0; i<array_size(commodity_temp); i++)
152 if (strcmp(commodity_temp[i]->name, name) == 0)
153 return commodity_temp[i];
154 return NULL;
155}
156
162int commodity_getN (void)
163{
164 return array_size(econ_comm);
165}
166
173Commodity* commodity_getByIndex( const int indx )
174{
175 if (indx < 0 || indx >= array_size(econ_comm)) {
176 WARN(_("Commodity with index %d not found"),indx);
177 return NULL;
178 }
179 return &commodity_stack[econ_comm[indx]];
180}
181
187static void commodity_freeOne( Commodity* com )
188{
189 CommodityModifier *this,*next;
190 free(com->name);
191 free(com->description);
192 free(com->price_ref);
195 next = com->spob_modifier;
196 com->spob_modifier = NULL;
197 while (next != NULL ) {
198 this = next;
199 next = this->next;
200 free(this->name);
201 free(this);
202 }
203 next = com->faction_modifier;
204 com->faction_modifier = NULL;
205 while (next != NULL ) {
206 this=next;
207 next=this->next;
208 free(this->name);
209 free(this);
210 }
211 array_free(com->illegalto);
212 /* Clear the memory. */
213 memset(com, 0, sizeof(Commodity));
214}
215
223int commodity_compareTech( const void *commodity1, const void *commodity2 )
224{
225 const Commodity *c1, *c2;
226
227 /* Get commodities. */
228 c1 = * (const Commodity**) commodity1;
229 c2 = * (const Commodity**) commodity2;
230
231 /* Compare price. */
232 if (c1->price < c2->price)
233 return +1;
234 else if (c1->price > c2->price)
235 return -1;
236
237 /* It turns out they're the same. */
238 return strcmp( c1->name, c2->name );
239}
240
244Commodity ** standard_commodities (void)
245{
248 for (int i=0; i<n; i++) {
250 if (commodity_isFlag(c,COMMODITY_FLAG_STANDARD))
251 array_push_back( &com, c );
252 }
253 return com;
254}
255
263static int commodity_parse( Commodity *temp, const char *filename )
264{
265 xmlNodePtr node, parent;
266 xmlDocPtr doc;
267
268 doc = xml_parsePhysFS( filename );
269 if (doc == NULL)
270 return -1;
271
272 parent = doc->xmlChildrenNode; /* Commodities node */
273 if (strcmp((char*)parent->name,XML_COMMODITY_ID)) {
274 ERR(_("Malformed %s file: missing root element '%s'"), filename, XML_COMMODITY_ID);
275 return -1;
276 }
277
278 /* Clear memory and set defaults. */
279 memset( temp, 0, sizeof(Commodity) );
280 temp->period = 200;
281 temp->price_mod = 1.;
282
283 /* Parse body. */
284 node = parent->xmlChildrenNode;
285 do {
286 xml_onlyNodes(node);
287
288 xmlr_strd(node, "name", temp->name);
289 xmlr_strd(node, "description", temp->description);
290 xmlr_int(node, "price", temp->price);
291 xmlr_float(node, "price_mod", temp->price_mod);
292 xmlr_strd(node, "price_ref", temp->price_ref);
293
294 if (xml_isNode(node,"gfx_space")) {
295 temp->gfx_space = xml_parseTexture( node,
296 COMMODITY_GFX_PATH"space/%s", 1, 1, OPENGL_TEX_MIPMAPS );
297 continue;
298 }
299 if (xml_isNode(node,"gfx_store")) {
300 temp->gfx_store = xml_parseTexture( node,
301 COMMODITY_GFX_PATH"%s", 1, 1, OPENGL_TEX_MIPMAPS );
302 continue;
303 }
304 if (xml_isNode(node, "standard")) {
305 commodity_setFlag( temp, COMMODITY_FLAG_STANDARD );
306 continue;
307 }
308 if (xml_isNode(node, "always_can_sell")) {
309 commodity_setFlag( temp, COMMODITY_FLAG_ALWAYS_CAN_SELL );
310 continue;
311 }
312 if (xml_isNode(node, "price_constant")) {
313 commodity_setFlag( temp, COMMODITY_FLAG_PRICE_CONSTANT );
314 continue;
315 }
316 if (xml_isNode(node, "illegalto")) {
317 xmlNodePtr cur = node->xmlChildrenNode;
318 temp->illegalto = array_create( int );
319 do {
320 xml_onlyNodes(cur);
321 if (xml_isNode(cur, "faction")) {
322 int f = faction_get( xml_get(cur) );
323 array_push_back( &temp->illegalto, f );
324 }
325 } while (xml_nextNode(node));
326 continue;
327 }
328 xmlr_float(node, "population_modifier", temp->population_modifier);
329 xmlr_float(node, "period", temp->period);
330 if (xml_isNode(node, "spob_modifier")) {
331 CommodityModifier *newdict = malloc(sizeof(CommodityModifier));
332 newdict->next = temp->spob_modifier;
333 xmlr_attr_strd(node, "type", newdict->name);
334 newdict->value = xml_getFloat(node);
335 temp->spob_modifier = newdict;
336 continue;
337 }
338 if (xml_isNode(node, "faction_modifier")) {
339 CommodityModifier *newdict = malloc(sizeof(CommodityModifier));
340 newdict->next = temp->faction_modifier;
341 xmlr_attr_strd(node, "type", newdict->name);
342 newdict->value = xml_getFloat(node);
343 temp->faction_modifier = newdict;
344 continue;
345 }
346
347 WARN(_("Commodity '%s' has unknown node '%s'"),temp->name, node->name);
348 } while (xml_nextNode(node));
349
350 if (temp->name == NULL)
351 WARN( _("Commodity from %s has invalid or no name"), COMMODITY_DATA_PATH);
352
353 if ((temp->price > 0) || (temp->price_ref != NULL)) {
354 if (temp->gfx_store == NULL) {
355 WARN(_("No <gfx_store> node found, using default texture for commodity \"%s\""), temp->name);
356 temp->gfx_store = gl_newImage( COMMODITY_GFX_PATH"_default.webp", 0 );
357 }
358 }
359 if (temp->gfx_space == NULL)
360 temp->gfx_space = gl_newImage( COMMODITY_GFX_PATH"space/_default.webp", 0 );
361
362 if (temp->price_ref != NULL) {
363 if (temp->price > 0.)
364 WARN(_("Commodity '%s' is setting both 'price' and 'price_ref'."),temp->name);
365 }
366
367#if 0 /* shouldn't be needed atm */
368#define MELEMENT(o,s) if (o) WARN( _("Commodity '%s' missing '"s"' element"), temp->name)
369 MELEMENT(temp->description==NULL,"description");
370 MELEMENT(temp->high==0,"high");
371 MELEMENT(temp->medium==0,"medium");
372 MELEMENT(temp->low==0,"low");
373#undef MELEMENT
374#endif
375
376 xmlFreeDoc(doc);
377
378 return 0;
379}
380
388int commodity_checkIllegal( const Commodity *com, int faction )
389{
390 for (int i=0; i<array_size(com->illegalto); i++) {
391 if (com->illegalto[i] == faction)
392 return 1;
393 }
394 return 0;
395}
396
403int commodity_isTemp( const char* name )
404{
405 for (int i=0; i<array_size(commodity_temp); i++)
406 if (strcmp(commodity_temp[i]->name, name) == 0)
407 return 1;
408 for (int i=0; i<array_size(commodity_stack); i++)
409 if (strcmp(commodity_stack[i].name,name)==0)
410 return 0;
411
412 WARN(_("Commodity '%s' not found in stack"), name);
413 return 0;
414}
415
423Commodity* commodity_newTemp( const char* name, const char* desc )
424{
425 Commodity **c;
426 if (commodity_temp == NULL)
427 commodity_temp = array_create( Commodity* );
428
429 c = &array_grow(&commodity_temp);
430 *c = calloc( 1, sizeof(Commodity) );
431 (*c)->istemp = 1;
432 (*c)->name = strdup(name);
433 (*c)->description = strdup(desc);
434 return *c;
435}
436
440int commodity_tempIllegalto( Commodity *com, int faction )
441{
442 int *f;
443
444 if (!com->istemp) {
445 WARN(_("Trying to modify temporary commodity '%s'!"), com->name);
446 return -1;
447 }
448
449 if (com->illegalto==NULL)
450 com->illegalto = array_create( int );
451
452 /* Don't add twice. */
453 for (int i=0; i<array_size(com->illegalto); i++) {
454 if (com->illegalto[i] == faction)
455 return 0;
456 }
457
458 f = &array_grow(&com->illegalto);
459 *f = faction;
460
461 return 0;
462}
463
469int commodity_load (void)
470{
471 char **commodities = ndata_listRecursive( COMMODITY_DATA_PATH );
472 Uint32 time = SDL_GetTicks();
473
475 econ_comm = array_create( int );
476
477 gatherable_load();
478
479 for (int i=0; i<array_size(commodities); i++) {
480 Commodity c;
481 int ret = commodity_parse( &c, commodities[i] );
482 if (ret == 0) {
484
485 /* See if should get added to commodity list. */
486 if (c.price > 0.) {
487 int *e = &array_grow( &econ_comm );
489 }
490
491 /* Render if necessary. */
493 }
494 free( commodities[i] );
495 }
496 array_free( commodities );
497
498 if (conf.devmode) {
499 time = SDL_GetTicks() - time;
500 DEBUG( n_( "Loaded %d Commodity in %.3f s", "Loaded %d Commodities in %.3f s", array_size(commodity_stack) ), array_size(commodity_stack), time/1000. );
501 }
502 else
503 DEBUG( n_( "Loaded %d Commodity", "Loaded %d Commodities", array_size(commodity_stack) ), array_size(commodity_stack) );
504
505 return 0;
506}
507
511void commodity_free (void)
512{
513 for (int i=0; i<array_size(commodity_stack); i++)
514 commodity_freeOne( &commodity_stack[i] );
516 commodity_stack = NULL;
517
518 for (int i=0; i<array_size(commodity_temp); i++) {
519 commodity_freeOne( commodity_temp[i] );
520 free( commodity_temp[i] );
521 }
522 array_free( commodity_temp );
523 commodity_temp = NULL;
524
525 /* More clean up. */
527 econ_comm = NULL;
528
529 gatherable_cleanup();
530}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition: array.h:158
#define array_create_size(basic_type, capacity)
Creates a new dynamic array of ‘basic_type’ with an initial capacity.
Definition: array.h:102
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition: array.h:168
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition: array.h:119
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition: array.h:129
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition: array.h:93
int * econ_comm
Definition: economy.c:61
Commodity * commodity_stack
Definition: commodity.c:43
int faction_get(const char *name)
Gets a faction ID by name.
Definition: faction.c:182
void naev_renderLoadscreen(void)
Renders the loadscreen if necessary.
Definition: naev.c:549
Header file with generic functions and naev-specifics.
char ** ndata_listRecursive(const char *path)
Lists all the visible files in a directory, at any depth.
Definition: ndata.c:232
glTexture * xml_parseTexture(xmlNodePtr node, const char *path, int defsx, int defsy, const unsigned int flags)
Parses a texture handling the sx and sy elements.
Definition: nxml.c:29
xmlDocPtr xml_parsePhysFS(const char *filename)
Analogous to xmlParseMemory/xmlParseFile.
Definition: nxml.c:75
glTexture * gl_newImage(const char *path, const unsigned int flags)
Loads an image as a texture.
Definition: opengl_tex.c:570
void gl_freeTexture(glTexture *texture)
Frees a texture.
Definition: opengl_tex.c:755
static const double c[]
Definition: rng.c:264
Represents a dictionary of values used to modify a commodity.
Definition: commodity.h:32
Represents a commodity.
Definition: commodity.h:43
char * description
Definition: commodity.h:45
CommodityModifier * spob_modifier
Definition: commodity.h:62
glTexture * gfx_store
Definition: commodity.h:53
int * illegalto
Definition: commodity.h:59
double population_modifier
Definition: commodity.h:64
char * name
Definition: commodity.h:44
CommodityModifier * faction_modifier
Definition: commodity.h:65
double price_mod
Definition: commodity.h:50
double price
Definition: commodity.h:52
glTexture * gfx_space
Definition: commodity.h:54
double period
Definition: commodity.h:63
int istemp
Definition: commodity.h:58
char * price_ref
Definition: commodity.h:49
int devmode
Definition: conf.h:158