naev 0.12.6
commodity.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
10#include <stdio.h>
11
12#include "SDL_timer.h"
13
14#include "naev.h"
16
17#include "commodity.h"
18
19#include "array.h"
20#include "conf.h"
21#include "faction.h"
22#include "gatherable.h"
23#include "log.h"
24#include "ndata.h"
25#include "nxml.h"
26#include "opengl.h"
27#include "threadpool.h"
28
29#define XML_COMMODITY_ID "commodity"
30#define CRED_TEXT_MAX \
31 ( ECON_CRED_STRLEN - \
32 4 ) /* Maximum length of just credits2str text, no markup */
33
34/* commodity stack */
37 NULL;
38
39/* @TODO remove externs. */
40extern int *econ_comm;
41
45typedef struct CommodityThreadData_ {
46 char *filename;
47 Commodity com;
48 int ret;
50
51/*
52 * Prototypes.
53 */
54/* Commodity. */
55static int commodity_parseThread( void *ptr );
56static void commodity_freeOne( Commodity *com );
57static int commodity_parse( Commodity *temp, const char *filename );
58static int commodity_cmp( const void *p1, const void *p2 );
59
60static int commodity_cmp( const void *p1, const void *p2 )
61{
62 const Commodity *c1 = p1;
63 const Commodity *c2 = p2;
64 return strcmp( c1->name, c2->name );
65}
66
75void credits2str( char *str, credits_t credits, int decimals )
76{
77 if ( decimals < 0 ) {
78 /* TODO support , separator like fmt.credits(). */
79 snprintf( str, CRED_TEXT_MAX, _( "%.*f ¤" ), 0, (double)credits );
80 } else if ( credits >= 1000000000000000000LL )
81 snprintf( str, CRED_TEXT_MAX, _( "%.*f E¤" ), decimals,
82 (double)credits / 1e18 );
83 else if ( credits >= 1000000000000000LL )
84 snprintf( str, CRED_TEXT_MAX, _( "%.*f P¤" ), decimals,
85 (double)credits / 1e15 );
86 else if ( credits >= 1000000000000LL )
87 snprintf( str, CRED_TEXT_MAX, _( "%.*f T¤" ), decimals,
88 (double)credits / 1e12 );
89 else if ( credits >= 1000000000L )
90 snprintf( str, CRED_TEXT_MAX, _( "%.*f G¤" ), decimals,
91 (double)credits / 1e9 );
92 else if ( credits >= 1000000 )
93 snprintf( str, CRED_TEXT_MAX, _( "%.*f M¤" ), decimals,
94 (double)credits / 1e6 );
95 else if ( credits >= 1000 )
96 snprintf( str, CRED_TEXT_MAX, _( "%.*f k¤" ), decimals,
97 (double)credits / 1e3 );
98 else
99 snprintf( str, CRED_TEXT_MAX, _( "%.*f ¤" ), decimals, (double)credits );
100}
101
111void price2str( char *str, credits_t price, credits_t credits, int decimals )
112{
113 char buf[CRED_TEXT_MAX];
114
115 if ( price <= credits ) {
116 credits2str( str, price, decimals );
117 return;
118 }
119
120 credits2str( buf, price, decimals );
121 snprintf( str, ECON_CRED_STRLEN, "#r%s#0", (char *)buf );
122}
123
131void tonnes2str( char *str, int tonnes )
132{
133 snprintf( str, ECON_MASS_STRLEN, n_( "%d tonne", "%d tonnes", tonnes ),
134 tonnes );
135}
136
141{
142 return commodity_stack;
143}
144
151Commodity *commodity_get( const char *name )
152{
153 Commodity *c = commodity_getW( name );
154 if ( c != NULL )
155 return c;
156 WARN( _( "Commodity '%s' not found in stack" ), name );
157 return NULL;
158}
159
166Commodity *commodity_getW( const char *name )
167{
168 const Commodity q = { .name = (char *)name };
170 sizeof( Commodity ), commodity_cmp );
171 if ( r != NULL )
172 return r;
173 for ( int i = 0; i < array_size( commodity_temp ); i++ )
174 if ( strcmp( commodity_temp[i]->name, name ) == 0 )
175 return commodity_temp[i];
176 return NULL;
177}
178
184int commodity_getN( void )
185{
186 return array_size( econ_comm );
187}
188
196{
197 if ( indx < 0 || indx >= array_size( econ_comm ) ) {
198 WARN( _( "Commodity with index %d not found" ), indx );
199 return NULL;
200 }
201 return &commodity_stack[econ_comm[indx]];
202}
203
209static void commodity_freeOne( Commodity *com )
210{
211 CommodityModifier *this, *next;
212 free( com->name );
213 free( com->description );
214 free( com->price_ref );
217 next = com->spob_modifier;
218 com->spob_modifier = NULL;
219 while ( next != NULL ) {
220 this = next;
221 next = this->next;
222 free( this->name );
223 free( this );
224 }
225 next = com->faction_modifier;
226 com->faction_modifier = NULL;
227 while ( next != NULL ) {
228 this = next;
229 next = this->next;
230 free( this->name );
231 free( this );
232 }
233 array_free( com->illegalto );
234 /* Clear the memory. */
235 memset( com, 0, sizeof( Commodity ) );
236}
237
245int commodity_compareTech( const void *commodity1, const void *commodity2 )
246{
247 const Commodity *c1, *c2;
248
249 /* Get commodities. */
250 c1 = *(const Commodity **)commodity1;
251 c2 = *(const Commodity **)commodity2;
252
253 /* Compare price. */
254 if ( c1->price < c2->price )
255 return +1;
256 else if ( c1->price > c2->price )
257 return -1;
258
259 /* It turns out they're the same. */
260 return strcmp( c1->name, c2->name );
261}
262
268{
269 int n = array_size( commodity_stack );
270 Commodity **com = array_create_size( Commodity *, n );
271 for ( int i = 0; i < n; i++ ) {
273 if ( commodity_isFlag( c, COMMODITY_FLAG_STANDARD ) )
274 array_push_back( &com, c );
275 }
276 return com;
277}
278
286static int commodity_parse( Commodity *temp, const char *filename )
287{
288 xmlNodePtr node, parent;
289 xmlDocPtr doc;
290
291 doc = xml_parsePhysFS( filename );
292 if ( doc == NULL )
293 return -1;
294
295 parent = doc->xmlChildrenNode; /* Commodities node */
296 if ( strcmp( (char *)parent->name, XML_COMMODITY_ID ) ) {
297 WARN( _( "Malformed %s file: missing root element '%s'" ), filename,
299 return -1;
300 }
301
302 /* Clear memory and set defaults. */
303 memset( temp, 0, sizeof( Commodity ) );
304 temp->period = 200;
305 temp->price_mod = 1.;
306
307 /* Parse body. */
308 node = parent->xmlChildrenNode;
309 do {
310 xml_onlyNodes( node );
311
312 xmlr_strd( node, "name", temp->name );
313 xmlr_strd( node, "description", temp->description );
314 xmlr_int( node, "price", temp->price );
315 xmlr_float( node, "price_mod", temp->price_mod );
316 xmlr_strd( node, "price_ref", temp->price_ref );
317
318 if ( xml_isNode( node, "gfx_space" ) ) {
320 node, COMMODITY_GFX_PATH "space/%s", 1, 1, OPENGL_TEX_MIPMAPS );
321 continue;
322 }
323 if ( xml_isNode( node, "gfx_store" ) ) {
324 temp->gfx_store = xml_parseTexture( node, COMMODITY_GFX_PATH "%s", 1,
325 1, OPENGL_TEX_MIPMAPS );
326 continue;
327 }
328 if ( xml_isNode( node, "standard" ) ) {
329 commodity_setFlag( temp, COMMODITY_FLAG_STANDARD );
330 continue;
331 }
332 if ( xml_isNode( node, "always_can_sell" ) ) {
333 commodity_setFlag( temp, COMMODITY_FLAG_ALWAYS_CAN_SELL );
334 continue;
335 }
336 if ( xml_isNode( node, "price_constant" ) ) {
337 commodity_setFlag( temp, COMMODITY_FLAG_PRICE_CONSTANT );
338 continue;
339 }
340 if ( xml_isNode( node, "illegalto" ) ) {
341 xmlNodePtr cur = node->xmlChildrenNode;
342 temp->illegalto = array_create( int );
343 do {
344 xml_onlyNodes( cur );
345 if ( xml_isNode( cur, "faction" ) ) {
346 int f = faction_get( xml_get( cur ) );
347 array_push_back( &temp->illegalto, f );
348 }
349 } while ( xml_nextNode( node ) );
350 continue;
351 }
352 xmlr_float( node, "population_modifier", temp->population_modifier );
353 xmlr_float( node, "period", temp->period );
354 if ( xml_isNode( node, "spob_modifier" ) ) {
355 CommodityModifier *newdict = malloc( sizeof( CommodityModifier ) );
356 newdict->next = temp->spob_modifier;
357 xmlr_attr_strd( node, "type", newdict->name );
358 newdict->value = xml_getFloat( node );
359 temp->spob_modifier = newdict;
360 continue;
361 }
362 if ( xml_isNode( node, "faction_modifier" ) ) {
363 CommodityModifier *newdict = malloc( sizeof( CommodityModifier ) );
364 newdict->next = temp->faction_modifier;
365 xmlr_attr_strd( node, "type", newdict->name );
366 newdict->value = xml_getFloat( node );
367 temp->faction_modifier = newdict;
368 continue;
369 }
370
371 WARN( _( "Commodity '%s' has unknown node '%s'" ), temp->name,
372 node->name );
373 } while ( xml_nextNode( node ) );
374
375 if ( temp->name == NULL )
376 WARN( _( "Commodity from %s has invalid or no name" ),
377 COMMODITY_DATA_PATH );
378
379 if ( ( temp->price > 0 ) || ( temp->price_ref != NULL ) ) {
380 if ( temp->gfx_store == NULL ) {
381 WARN( _( "No <gfx_store> node found, using default texture for "
382 "commodity \"%s\"" ),
383 temp->name );
384 temp->gfx_store = gl_newImage( COMMODITY_GFX_PATH "_default.webp", 0 );
385 }
386 }
387 if ( temp->gfx_space == NULL )
388 temp->gfx_space =
389 gl_newImage( COMMODITY_GFX_PATH "space/_default.webp", 0 );
390
391 if ( temp->price_ref != NULL ) {
392 if ( temp->price > 0. )
393 WARN( _( "Commodity '%s' is setting both 'price' and 'price_ref'." ),
394 temp->name );
395 }
396
397#if 0 /* shouldn't be needed atm */
398#define MELEMENT( o, s ) \
399 if ( o ) \
400 WARN( _( "Commodity '%s' missing '"s \
401 "' element" ), \
402 temp->name )
403 MELEMENT(temp->description==NULL,"description");
404 MELEMENT(temp->high==0,"high");
405 MELEMENT(temp->medium==0,"medium");
406 MELEMENT(temp->low==0,"low");
407#undef MELEMENT
408#endif
409
410 xmlFreeDoc( doc );
411
412 return 0;
413}
414
422int commodity_checkIllegal( const Commodity *com, int faction )
423{
424 for ( int i = 0; i < array_size( com->illegalto ); i++ ) {
425 if ( com->illegalto[i] == faction )
426 return 1;
427 }
428 return 0;
429}
430
437int commodity_isTemp( const char *name )
438{
439 for ( int i = 0; i < array_size( commodity_temp ); i++ )
440 if ( strcmp( commodity_temp[i]->name, name ) == 0 )
441 return 1;
442 for ( int i = 0; i < array_size( commodity_stack ); i++ )
443 if ( strcmp( commodity_stack[i].name, name ) == 0 )
444 return 0;
445
446 WARN( _( "Commodity '%s' not found in stack" ), name );
447 return 0;
448}
449
457Commodity *commodity_newTemp( const char *name, const char *desc )
458{
459 Commodity **c;
460 if ( commodity_temp == NULL )
462
464 *c = calloc( 1, sizeof( Commodity ) );
465 ( *c )->istemp = 1;
466 ( *c )->name = strdup( name );
467 ( *c )->description = strdup( desc );
468 return *c;
469}
470
474int commodity_tempIllegalto( Commodity *com, int faction )
475{
476 if ( !com->istemp ) {
477 WARN( _( "Trying to modify temporary commodity '%s'!" ), com->name );
478 return -1;
479 }
480
481 if ( com->illegalto == NULL )
482 com->illegalto = array_create( int );
483
484 /* Don't add twice. */
485 for ( int i = 0; i < array_size( com->illegalto ); i++ ) {
486 if ( com->illegalto[i] == faction )
487 return 0;
488 }
489
490 array_push_back( &com->illegalto, faction );
491
492 return 0;
493}
494
495static int commodity_parseThread( void *ptr )
496{
497 CommodityThreadData *data = ptr;
498 data->ret = commodity_parse( &data->com, data->filename );
499 /* Render if necessary. */
501 gl_contextSet();
503 gl_contextUnset();
504 }
505 return data->ret;
506}
507
513int commodity_load( void )
514{
515#if DEBUGGING
516 Uint32 time = SDL_GetTicks();
517#endif /* DEBUGGING */
518 ThreadQueue *tq = vpool_create();
520 char **commodities = ndata_listRecursive( COMMODITY_DATA_PATH );
521
523 econ_comm = array_create( int );
524
526
527 /* Prepare files to run. */
528 for ( int i = 0; i < array_size( commodities ); i++ ) {
529 if ( ndata_matchExt( commodities[i], "xml" ) ) {
530 CommodityThreadData *cd = &array_grow( &cdata );
531 cd->filename = commodities[i];
532 } else
533 free( commodities[i] );
534 }
535 array_free( commodities );
536
537 /* Enqueue the jobs after the data array is done. */
538 SDL_GL_MakeCurrent( gl_screen.window, NULL );
539 for ( int i = 0; i < array_size( cdata ); i++ )
540 vpool_enqueue( tq, commodity_parseThread, &cdata[i] );
541 /* Wait until done processing. */
542 vpool_wait( tq );
543 vpool_cleanup( tq );
544 SDL_GL_MakeCurrent( gl_screen.window, gl_screen.context );
545
546 /* Finally load. */
547 for ( int i = 0; i < array_size( cdata ); i++ ) {
548 CommodityThreadData *cd = &cdata[i];
549 if ( !cd->ret )
550 array_push_back( &commodity_stack, cd->com );
551 free( cd->filename );
552 }
553 array_free( cdata );
554
555 /* Sort. */
557 commodity_cmp );
558
559 /* Load into commodity stack. */
560 for ( int i = 0; i < array_size( commodity_stack ); i++ ) {
561 const Commodity *com = &commodity_stack[i];
562 /* See if should get added to commodity list. */
563 if ( com->price > 0. )
565 }
566
567#if DEBUGGING
568 if ( conf.devmode ) {
569 time = SDL_GetTicks() - time;
570 DEBUG( n_( "Loaded %d Commodity in %.3f s",
571 "Loaded %d Commodities in %.3f s",
573 array_size( commodity_stack ), time / 1000. );
574 } else
575 DEBUG( n_( "Loaded %d Commodity", "Loaded %d Commodities",
578#endif /* DEBUGGING */
579
580 return 0;
581}
582
586void commodity_free( void )
587{
588 for ( int i = 0; i < array_size( commodity_stack ); i++ )
591 commodity_stack = NULL;
592
593 for ( int i = 0; i < array_size( commodity_temp ); i++ ) {
595 free( commodity_temp[i] );
596 }
598 commodity_temp = NULL;
599
600 /* More clean up. */
602 econ_comm = NULL;
603
605}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:170
#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:179
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition array.h:122
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition array.h:134
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
Commodity * commodity_getAll(void)
Gets all the commodities.
Definition commodity.c:140
Commodity * commodity_get(const char *name)
Gets a commodity by name.
Definition commodity.c:151
#define XML_COMMODITY_ID
Definition commodity.c:29
Commodity * commodity_newTemp(const char *name, const char *desc)
Creates a new temporary commodity.
Definition commodity.c:457
static void commodity_freeOne(Commodity *com)
Frees a commodity.
Definition commodity.c:209
static Commodity ** commodity_temp
Definition commodity.c:36
void commodity_free(void)
Frees all the loaded commodities.
Definition commodity.c:586
Commodity * commodity_getByIndex(const int indx)
Gets a commodity by index.
Definition commodity.c:195
static int commodity_parse(Commodity *temp, const char *filename)
Loads a commodity.
Definition commodity.c:286
void credits2str(char *str, credits_t credits, int decimals)
Converts credits to a usable string for displaying.
Definition commodity.c:75
void tonnes2str(char *str, int tonnes)
Converts tonnes to a usable string for displaying.
Definition commodity.c:131
int commodity_getN(void)
Return the number of commodities globally.
Definition commodity.c:184
int commodity_compareTech(const void *commodity1, const void *commodity2)
Function meant for use with C89, C99 algorithm qsort().
Definition commodity.c:245
void price2str(char *str, credits_t price, credits_t credits, int decimals)
Given a price and on-hand credits, outputs a colourized string.
Definition commodity.c:111
int commodity_load(void)
Loads all the commodity data.
Definition commodity.c:513
int * econ_comm
Definition economy.c:56
Commodity ** standard_commodities(void)
Return an array (array.h) of standard commodities. Free with array_free. (Don't free contents....
Definition commodity.c:267
int commodity_checkIllegal(const Commodity *com, int faction)
Checks to see if a commodity is illegal to a faction.
Definition commodity.c:422
Commodity * commodity_getW(const char *name)
Gets a commodity by name without warning.
Definition commodity.c:166
Commodity * commodity_stack
Definition commodity.c:35
int commodity_tempIllegalto(Commodity *com, int faction)
Makes a temporary commodity illegal to something.
Definition commodity.c:474
int commodity_isTemp(const char *name)
Checks to see if a commodity is temporary.
Definition commodity.c:437
int faction_get(const char *name)
Gets a faction ID by name.
Definition faction.c:209
int gatherable_load(void)
Loads the gatherable system.
Definition gatherable.c:40
void gatherable_cleanup(void)
Cleans up after the gatherable system.
Definition gatherable.c:50
void naev_renderLoadscreen(void)
Renders the loadscreen if necessary.
Definition naev.c:640
int naev_shouldRenderLoadscreen(void)
Whether or not we want to render the loadscreen.
Definition naev.c:596
Header file with generic functions and naev-specifics.
int ndata_matchExt(const char *path, const char *ext)
Sees if a file matches an extension.
Definition ndata.c:420
char ** ndata_listRecursive(const char *path)
Lists all the visible files in a directory, at any depth.
Definition ndata.c:286
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:25
xmlDocPtr xml_parsePhysFS(const char *filename)
Analogous to xmlParseMemory/xmlParseFile.
Definition nxml.c:70
glInfo gl_screen
Definition opengl.c:47
glTexture * gl_newImage(const char *path, const unsigned int flags)
Loads an image as a texture.
Definition opengl_tex.c:587
void gl_freeTexture(glTexture *texture)
Frees a texture.
Definition opengl_tex.c:835
static const double c[]
Definition rng.c:256
Represents a dictionary of values used to modify a commodity.
Definition commodity.h:46
For threaded loading of commodities.
Definition commodity.c:45
Represents a commodity.
Definition commodity.h:57
char * description
Definition commodity.h:59
CommodityModifier * spob_modifier
Definition commodity.h:79
glTexture * gfx_store
Definition commodity.h:68
int * illegalto
Definition commodity.h:75
double population_modifier
Definition commodity.h:81
char * name
Definition commodity.h:58
CommodityModifier * faction_modifier
Definition commodity.h:84
double price_mod
Definition commodity.h:65
double price
Definition commodity.h:67
glTexture * gfx_space
Definition commodity.h:69
double period
Definition commodity.h:80
int istemp
Definition commodity.h:74
char * price_ref
Definition commodity.h:63