naev 0.12.6
economy.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
14#include <stdio.h>
15
16#if HAVE_SUITESPARSE_CS_H
17#include <suitesparse/cs.h>
18#else /* HAVE_SUITESPARSE_CS_H */
19#include <cs.h>
20#endif /* HAVE_SUITESPARSE_CS_H */
21
22#include "naev.h"
24
25#include "economy.h"
26
27#include "array.h"
28#include "log.h"
29#include "ndata.h"
30#include "ntime.h"
31#include "nxml.h"
32#include "space.h"
33
34/*
35 * Economy Nodal Analysis parameters.
36 */
37#define ECON_BASE_RES 30.
38#define ECON_SELF_RES 3.
39#define ECON_FACTION_MOD 0.1
40#define ECON_PROD_MODIFIER \
41 500000.
42#define ECON_PROD_VAR 0.01
43
44/* systems stack. */
45extern StarSystem *systems_stack;
46
47/* @TODO get rid of these externs. */
49
50/*
51 * Nodal analysis simulation for dynamic economies.
52 */
53static int econ_initialized = 0;
54static int econ_queued = 0;
55static cs *econ_G = NULL;
56int *econ_comm = NULL;
57
58/*
59 * Prototypes.
60 */
61/* Economy. */
62// static double econ_calcJumpR( StarSystem *A, StarSystem *B );
63// static double econ_calcSysI( unsigned int dt, StarSystem *sys, int price );
64// static int econ_createGMatrix (void);
65
66/*
67 * Externed prototypes.
68 */
69int economy_sysSave( xmlTextWriterPtr writer );
70int economy_sysLoad( xmlNodePtr parent );
71
80credits_t economy_getPrice( const Commodity *com, const StarSystem *sys,
81 const Spob *p )
82{
83 /* Get current time in periods. */
84 return economy_getPriceAtTime( com, sys, p, ntime_get() );
85}
86
96credits_t economy_getPriceAtTime( const Commodity *com, const StarSystem *sys,
97 const Spob *p, ntime_t tme )
98{
99 (void)sys;
100 int i, k;
101 double price;
102 double t;
103 CommodityPrice *commPrice;
104
105 /* If commodity is using a reference, just return that. */
106 if ( com->price_ref != NULL ) {
107 const Commodity *ref = commodity_get( com->price_ref );
108 if ( ref == NULL )
109 return 1e6; /* Just arbitrary large number so players notice. */
110 price = economy_getPriceAtTime( ref, sys, p, tme ) * com->price_mod;
111 return (credits_t)( price + 0.5 );
112 }
113
114 /* Constant price. */
115 if ( commodity_isFlag( com, COMMODITY_FLAG_PRICE_CONSTANT ) )
116 return com->price;
117
118 /* Get current time in periods.
119 * Note, taking off and landing takes about 1e7 ntime, which is 1 period.
120 * Time does not advance when on a spob.
121 * Journey with a single jump takes approx 3e7, so about 3 periods. */
122 t = ntime_convertSeconds( tme ) / NT_PERIOD_SECONDS;
123
124 /* Get position in stack. */
125 k = com - commodity_stack;
126
127 /* Find what commodity that is. */
128 for ( i = 0; i < array_size( econ_comm ); i++ )
129 if ( econ_comm[i] == k )
130 break;
131
132 /* Check if found. */
133 if ( i >= array_size( econ_comm ) ) {
134 WARN( _( "Price for commodity '%s' not known." ), com->name );
135 return 0;
136 }
137
138 /* and get the index on this spob */
139 for ( i = 0; i < array_size( p->commodities ); i++ ) {
140 if ( ( strcmp( p->commodities[i]->name, com->name ) == 0 ) )
141 break;
142 }
143 if ( i >= array_size( p->commodities ) ) {
144 WARN( _( "Price for commodity '%s' not known on this spob." ),
145 com->name );
146 return 0;
147 }
148 commPrice = &p->commodityPrice[i];
149 /* Calculate price. */
150 /* price = (double) com->price; */
151 /* price *= sys->prices[i]; */
152 price =
153 ( commPrice->price +
154 commPrice->sysVariation * sin( 2. * M_PI * t / commPrice->sysPeriod ) +
155 commPrice->spobVariation *
156 sin( 2. * M_PI * t / commPrice->spobPeriod ) );
157 return (credits_t)( price + 0.5 ); /* +0.5 to round */
158}
159
172 credits_t *mean, double *std )
173{
174 int i, k;
175 CommodityPrice *commPrice;
176
177 if ( com->price_ref != NULL ) {
178 const Commodity *ref = commodity_get( com->price_ref );
179 if ( ref == NULL )
180 return -1;
181 int ret = economy_getAverageSpobPrice( ref, p, mean, std );
182 if ( !ret )
183 return ret;
184 *mean = (credits_t)( (double)*mean * com->price_mod + 0.5 );
185 *std = ( *std * com->price_mod );
186 return 0;
187 }
188
189 /* Constant price. */
190 if ( commodity_isFlag( com, COMMODITY_FLAG_PRICE_CONSTANT ) ) {
191 *mean = com->price;
192 *std = 0.;
193 return 0;
194 }
195
196 /* Get position in stack */
197 k = com - commodity_stack;
198
199 /* Find what commodity this is */
200 for ( i = 0; i < array_size( econ_comm ); i++ )
201 if ( econ_comm[i] == k )
202 break;
203
204 /* Check if found */
205 if ( i >= array_size( econ_comm ) ) {
206 WARN( _( "Average price for commodity '%s' not known." ), com->name );
207 *mean = 0;
208 *std = 0;
209 return -1;
210 }
211
212 /* and get the index on this spob */
213 for ( i = 0; i < array_size( p->commodities ); i++ ) {
214 if ( ( strcmp( p->commodities[i]->name, com->name ) == 0 ) )
215 break;
216 }
217 if ( i >= array_size( p->commodities ) ) {
218 WARN( _( "Price for commodity '%s' not known on this spob." ),
219 com->name );
220 *mean = 0;
221 *std = 0;
222 return -1;
223 }
224 commPrice = &p->commodityPrice[i];
225 if ( commPrice->cnt > 0 ) {
226 *mean = (credits_t)( commPrice->sum / commPrice->cnt +
227 0.5 ); /* +0.5 to round*/
228 *std = ( sqrt( commPrice->sum2 / commPrice->cnt -
229 ( commPrice->sum * commPrice->sum ) /
230 ( commPrice->cnt * commPrice->cnt ) ) );
231 } else {
232 *mean = 0;
233 *std = 0;
234 return -1;
235 }
236 return 0;
237}
238
248int economy_getAveragePrice( const Commodity *com, credits_t *mean,
249 double *std )
250{
251 if ( com->price_ref != NULL ) {
252 const Commodity *ref = commodity_get( com->price_ref );
253 if ( ref == NULL )
254 return -1;
255 int ret = economy_getAveragePrice( ref, mean, std );
256 *mean = (credits_t)( (double)*mean * com->price_mod + 0.5 );
257 *std = ( *std * com->price_mod );
258 return ( (double)ret * com->price_mod + 0.5 );
259 }
260
261 /* Constant price. */
262 if ( commodity_isFlag( com, COMMODITY_FLAG_PRICE_CONSTANT ) ) {
263 *mean = com->price;
264 *std = 0.;
265 return com->price;
266 }
267
268 double av = 0;
269 double av2 = 0;
270 int cnt = 0;
271 for ( int i = 0; i < array_size( systems_stack ); i++ ) {
272 const StarSystem *sys = &systems_stack[i];
273 for ( int j = 0; j < array_size( sys->spobs ); j++ ) {
274 const Spob *p = sys->spobs[j];
275 for ( int k = 0; k < array_size( p->commodities ); k++ ) {
276 if ( ( strcmp( p->commodities[k]->name, com->name ) != 0 ) )
277 continue;
278 const CommodityPrice *commPrice = &p->commodityPrice[k];
279 if ( commPrice->cnt > 0 ) {
280 av += commPrice->sum / commPrice->cnt;
281 av2 += commPrice->sum * commPrice->sum /
282 ( commPrice->cnt * commPrice->cnt );
283 cnt++;
284 }
285 break;
286 }
287 }
288 }
289 if ( cnt > 0 ) {
290 av /= cnt;
291 av2 = sqrt( av2 / cnt - av * av );
292 *mean = (credits_t)( av + 0.5 );
293 *std = av2;
294 } else {
295 WARN( _( "Average price for commodity '%s' not known." ), com->name );
296 *mean = 0;
297 *std = 0;
298 }
299 return 0;
300}
301
302#if 0
310static double econ_calcJumpR( StarSystem *A, StarSystem *B )
311{
312 double R;
313
314 /* Set to base to ensure price change. */
315 R = ECON_BASE_RES;
316
317 /* Modify based on system conditions. */
318 R += (A->nebu_density + B->nebu_density) / 1000.; /* Density shouldn't affect much. */
319 R += (A->nebu_volatility + B->nebu_volatility) / 100.; /* Volatility should. */
320
321 /* Modify based on global faction. */
322 if ((A->faction != -1) && (B->faction != -1)) {
323 if (areEnemies(A->faction, B->faction))
325 else if (areAllies(A->faction, B->faction))
327 }
328
329 /* @todo Modify based on fleets. */
330
331 return R;
332}
333
339static double econ_calcSysI( unsigned int dt, StarSystem *sys, int price )
340{
341 int i;
342 double I;
343 double prodfactor, p;
344 double ddt;
345 Spob *spob;
346
347 ddt = (double)(dt / NTIME_UNIT_LENGTH);
348
349 /* Calculate production level. */
350 p = 0.;
351 for (i=0; i<sys->nspobs; i++) {
352 spob = sys->spobs[i];
353 if (spob_hasService(spob, SPOB_SERVICE_INHABITED)) {
354 /*
355 * Calculate production.
356 */
357 /* We base off the current production. */
358 prodfactor = spob->cur_prodfactor;
359 /* Add a variability factor based on the Gaussian distribution. */
360 prodfactor += ECON_PROD_VAR * RNG_2SIGMA() * ddt;
361 /* Add a tendency to return to the spob's base production. */
362 prodfactor -= ECON_PROD_VAR *
363 (spob->cur_prodfactor - prodfactor)*ddt;
364 /* Save for next iteration. */
365 spob->cur_prodfactor = prodfactor;
366 /* We base off the sqrt of the population otherwise it changes too fast. */
367 p += prodfactor * sqrt(spob->population);
368 }
369 }
370
371 /* The intensity is basically the modified production. */
372 I = p / ECON_PROD_MODIFIER;
373
374 return I;
375}
376
382static int econ_createGMatrix (void)
383{
384 int ret;
385 double R, Rsum;
386 cs *M;
387
388 /* Create the matrix. */
389 M = cs_spalloc( array_size(systems_stack), array_size(systems_stack), 1, 1, 1 );
390 if (M == NULL)
391 WARN(_("Unable to create CSparse Matrix."));
392
393 /* Fill the matrix. */
394 for (int i=0; i < array_size(systems_stack); i++) {
395 StarSystem *sys = &systems_stack[i];
396 Rsum = 0.;
397
398 /* Set some values. */
399 for (int j=0; j < array_size(sys->jumps); j++) {
400
401 /* Get the resistances. */
402 R = econ_calcJumpR( sys, sys->jumps[j].target );
403 R = 1./R; /* Must be inverted. */
404 Rsum += R;
405
406 /* Matrix is symmetrical and non-diagonal is negative. */
407 ret = cs_entry( M, i, sys->jumps[j].target->id, -R );
408 if (ret != 1)
409 WARN(_("Unable to enter CSparse Matrix Cell."));
410 ret = cs_entry( M, sys->jumps[j].target->id, i, -R );
411 if (ret != 1)
412 WARN(_("Unable to enter CSparse Matrix Cell."));
413 }
414
415 /* Set the diagonal. */
416 Rsum += 1./ECON_SELF_RES; /* We add a resistance for dampening. */
417 cs_entry( M, i, i, Rsum );
418 }
419
420 /* Compress M matrix and put into G. */
421 cs_spfree( econ_G );
422 econ_G = cs_compress( M );
423 if (econ_G == NULL)
424 WARN(_("Unable to create economy G Matrix."));
425
426 /* Clean up. */
427 cs_spfree(M);
428
429 return 0;
430}
431#endif
432
438int economy_init( void )
439{
440 /* Must not be initialized. */
441 if ( econ_initialized )
442 return 0;
443
444 /* Allocate price space. */
445 for ( int i = 0; i < array_size( systems_stack ); i++ ) {
446 free( systems_stack[i].prices );
447 systems_stack[i].prices =
448 calloc( array_size( econ_comm ), sizeof( double ) );
449 }
450
451 /* Mark economy as initialized. */
453
454 /* Refresh economy. */
456
457 return 0;
458}
459
466{
467 econ_queued++;
468}
469
474{
475 if ( econ_queued )
476 return economy_refresh();
477
478 return 0;
479}
480
486{
487 /* Economy must be initialized. */
488 if ( econ_initialized == 0 )
489 return 0;
490
491 /* Create the resistance matrix. */
492 // if (econ_createGMatrix())
493 // return -1;
494
495 /* Initialize the prices. */
496 economy_update( 0 );
497
498 return 0;
499}
500
506int economy_update( unsigned int dt )
507{
508 (void)dt;
509#if 0
510 int i, j;
511 double *X;
512 double scale, offset;
513 /*double min, max;*/
514
515 /* Economy must be initialized. */
516 if (econ_initialized == 0)
517 return 0;
518
519 /* Create the vector to solve the system. */
520 X = malloc(sizeof(double)*array_size(systems_stack));
521 if (X == NULL) {
522 WARN(_("Out of Memory"));
523 return -1;
524 }
525
526 /* Calculate the results for each price set. */
527 for (j=0; j<array_size(econ_comm); j++) {
528
529 /* First we must load the vector with intensities. */
530 for (i=0; i<array_size(systems_stack); i++)
531 X[i] = econ_calcSysI( dt, &systems_stack[i], j );
532
533 /* Solve the system. */
541 ret = cs_qrsol( 3, econ_G, X );
542 if (ret != 1)
543 WARN(_("Failed to solve the Economy System."));
544
545 /*
546 * Get the minimum and maximum to scale.
547 */
548 /*
549 min = +HUGE_VALF;
550 max = -HUGE_VALF;
551 for (i=0; i<array_size(systems_stack); i++) {
552 if (X[i] < min)
553 min = X[i];
554 if (X[i] > max)
555 max = X[i];
556 }
557 scale = 1. / (max - min);
558 offset = 0.5 - min * scale;
559 */
560
561 /*
562 * I'm not sure I like the filtering of the results, but it would take
563 * much more work to get a good system working without the need of post
564 * filtering.
565 */
566 scale = 1.;
567 offset = 1.;
568 for (i=0; i<array_size(systems_stack); i++)
569 systems_stack[i].prices[j] = X[i] * scale + offset;
570 }
571
572 /* Clean up. */
573 free(X);
574
575#endif
576 econ_queued = 0;
577 return 0;
578}
579
583void economy_destroy( void )
584{
585 /* Must be initialized. */
586 if ( !econ_initialized )
587 return;
588
589 /* Clean up the prices in the systems stack. */
590 for ( int i = 0; i < array_size( systems_stack ); i++ ) {
591 free( systems_stack[i].prices );
592 systems_stack[i].prices = NULL;
593 }
594
595 /* Destroy the economy matrix. */
596 cs_spfree( econ_G );
597 econ_G = NULL;
598
599 /* Economy is now deinitialized. */
601}
602
612static int economy_calcPrice( Spob *spob, Commodity *commodity,
613 CommodityPrice *commodityPrice )
614{
616 double base, scale, factor;
617 const char *factionname;
618
619 /* Ignore spobs with no commodity stuff. */
620 if ( !spob_hasService( spob, SPOB_SERVICE_COMMODITY ) )
621 return 0;
622
623 /* Check the faction is not NULL.*/
624 if ( spob->presence.faction == -1 ) {
625 WARN( _( "Spob '%s' appears to have commodity '%s' defined, but no "
626 "faction." ),
627 spob->name, commodity->name );
628 return 1;
629 }
630
631 /* Reset price to the base commodity price. */
632 commodityPrice->price = commodity->price;
633
634 /* Get the cost modifier suitable for spob type/class. */
635 cm = commodity->spob_modifier;
636 scale = 1.;
637 while ( cm != NULL ) {
638 if ( ( strcmp( spob->class, cm->name ) == 0 ) ) {
639 scale = cm->value;
640 break;
641 }
642 cm = cm->next;
643 }
644 commodityPrice->price *= scale;
645 commodityPrice->spobVariation = 0.5;
646 commodityPrice->sysVariation = 0.;
647 /*commodityPrice->sum = 0.;
648 commodityPrice->sum2 = 0.;
649 commodityPrice->cnt = 0;
650 commodityPrice->updateTime = 0;*/
651 /* Use filename to specify a variation period. */
652 base = 100;
653 commodity->period =
654 32 * ( spob->gfx_spaceName[strlen( SPOB_GFX_SPACE_PATH )] % 32 ) +
655 spob->gfx_spaceName[strlen( SPOB_GFX_SPACE_PATH ) + 1] % 32;
656 commodityPrice->spobPeriod = commodity->period + base;
657
658 /* Use filename of exterior graphic to modify the variation period.
659 No rhyme or reason, just gives some variability. */
660 scale = 1 + ( strlen( spob->gfx_exterior ) -
661 strlen( SPOB_GFX_EXTERIOR_PATH ) - 19 ) /
662 100.;
663 commodityPrice->spobPeriod *= scale;
664
665 /* Use population to modify price and variability. The tanh function scales
666 from -1 (small population) to +1 (large population), done on a log scale.
667 Price is then modified by this factor, scaled by a value defined in the
668 xml, as is variation. So for some commodities, prices increase with
669 population, while for others, prices decrease. */
670 factor = -1;
671 if ( spob->population > 0 )
672 factor = tanh( ( log( (double)spob->population ) - log( 1e8 ) ) / 2 );
673 base = commodity->population_modifier;
674 commodityPrice->price *= 1 + factor * base;
675 commodityPrice->spobVariation *= 0.5 - factor * 0.25;
676 commodityPrice->spobPeriod *= 1 + factor * 0.5;
677
678 /* Modify price based on faction (as defined in the xml).
679 Some factions place a higher value on certain goods.
680 Some factions are more stable than others.*/
681 scale = 1.;
682 cm = commodity->spob_modifier;
683
684 factionname = faction_name( spob->presence.faction );
685 while ( cm != NULL ) {
686 if ( strcmp( factionname, cm->name ) == 0 ) {
687 scale = cm->value;
688 break;
689 }
690 cm = cm->next;
691 }
692 commodityPrice->price *= scale;
693
694 /* Range seems to go from 0-5, with median being 2. Increased range
695 * will increase safety and so lower prices and improve stability */
696 commodityPrice->price *= ( 1 - spob->presence.range / 30. );
697 commodityPrice->spobPeriod *= 1 / ( 1 - spob->presence.range / 30. );
698
699 /* Make sure the price is always positive and non-zero */
700 commodityPrice->price = MAX( commodityPrice->price, 1 );
701
702 return 0;
703}
704
710static void economy_modifySystemCommodityPrice( StarSystem *sys )
711{
712 int k;
713 CommodityPrice *avprice;
714
715 avprice = array_create( CommodityPrice );
716 for ( int i = 0; i < array_size( sys->spobs ); i++ ) {
717 Spob *spob = sys->spobs[i];
718 for ( int j = 0; j < array_size( spob->commodityPrice ); j++ ) {
719 /* Largest is approx 35000. Increased radius will increase price since
720 further to travel, and also increase stability, since longer for
721 prices to fluctuate, but by a larger amount when they do.*/
722 spob->commodityPrice[j].price *= 1 + sys->radius / 200e3;
723 spob->commodityPrice[j].spobPeriod *= 1 / ( 1 - sys->radius / 200e3 );
724 spob->commodityPrice[j].spobVariation *=
725 1 / ( 1 - sys->radius / 300e3 );
726
727 /* Increase price with volatility, which goes up to about 600.
728 And with interference, since systems are harder to find, which goes
729 up to about 1000.*/
730 spob->commodityPrice[j].price *= 1 + sys->nebu_volatility / 600.;
731 spob->commodityPrice[j].price *= 1 + sys->interference / 10e3;
732
733 /* Use number of jumps to determine sytsem time period. More jumps
734 means more options for trade so shorter period. Between 1 to 6
735 jumps. Make the base time 1000.*/
736 spob->commodityPrice[j].sysPeriod =
737 2000. / ( array_size( sys->jumps ) + 1 );
738
739 for ( k = 0; k < array_size( avprice ); k++ ) {
740 if ( ( strcmp( spob->commodities[j]->name, avprice[k].name ) ==
741 0 ) ) {
742 avprice[k].updateTime++;
743 avprice[k].price += spob->commodityPrice[j].price;
744 avprice[k].spobPeriod += spob->commodityPrice[j].spobPeriod;
745 avprice[k].sysPeriod += spob->commodityPrice[j].sysPeriod;
746 avprice[k].spobVariation +=
748 avprice[k].sysVariation += spob->commodityPrice[j].sysVariation;
749 break;
750 }
751 }
752 if ( k == array_size( avprice ) ) { /* first visit of this commodity
753 for this system */
754 (void)array_grow( &avprice );
755 avprice[k].name = spob->commodities[j]->name;
756 avprice[k].updateTime = 1;
757 avprice[k].price = spob->commodityPrice[j].price;
758 avprice[k].spobPeriod = spob->commodityPrice[j].spobPeriod;
759 avprice[k].sysPeriod = spob->commodityPrice[j].sysPeriod;
760 avprice[k].spobVariation = spob->commodityPrice[j].spobVariation;
761 avprice[k].sysVariation = spob->commodityPrice[j].sysVariation;
762 }
763 }
764 }
765 /* Do some inter-spob averaging */
766 for ( k = 0; k < array_size( avprice ); k++ ) {
767 avprice[k].price /= avprice[k].updateTime;
768 avprice[k].spobPeriod /= avprice[k].updateTime;
769 avprice[k].sysPeriod /= avprice[k].updateTime;
770 avprice[k].spobVariation /= avprice[k].updateTime;
771 avprice[k].sysVariation /= avprice[k].updateTime;
772 }
773 /* And now apply the averaging */
774 for ( int i = 0; i < array_size( sys->spobs ); i++ ) {
775 Spob *spob = sys->spobs[i];
776 for ( int j = 0; j < array_size( spob->commodities ); j++ ) {
777 for ( k = 0; k < array_size( avprice ); k++ ) {
778 if ( ( strcmp( spob->commodities[j]->name, avprice[k].name ) ==
779 0 ) ) {
780 spob->commodityPrice[j].price *= 0.25;
781 spob->commodityPrice[j].price += 0.75 * avprice[k].price;
783 0.2 * avprice[k].spobVariation;
784 }
785 }
786 }
787 }
788 array_shrink( &avprice );
789 array_free( sys->averagePrice );
790 sys->averagePrice = avprice;
791}
792
798static void economy_smoothCommodityPrice( StarSystem *sys )
799{
800 StarSystem *neighbour;
801 CommodityPrice *avprice = sys->averagePrice;
802 /*Now modify based on neighbouring systems */
803 /*First, calculate mean price of neighbouring systems */
804
805 for ( int j = 0; j < array_size( avprice );
806 j++ ) { /* for each commodity in this system */
807 double price = 0.;
808 int n = 0;
809 for ( int i = 0; i < array_size( sys->jumps );
810 i++ ) { /* for each neighbouring system */
811 neighbour = sys->jumps[i].target;
812 for ( int k = 0; k < array_size( neighbour->averagePrice ); k++ ) {
813 if ( ( strcmp( neighbour->averagePrice[k].name, avprice[j].name ) ==
814 0 ) ) {
815 price += neighbour->averagePrice[k].price;
816 n++;
817 break;
818 }
819 }
820 }
821 if ( n != 0 )
822 avprice[j].sum = price / n;
823 else
824 avprice[j].sum = avprice[j].price;
825 }
826}
827
833static void economy_calcUpdatedCommodityPrice( StarSystem *sys )
834{
835 CommodityPrice *avprice = sys->averagePrice;
836 for ( int j = 0; j < array_size( avprice ); j++ ) {
837 /*Use mean price to adjust current price */
838 avprice[j].price = 0.5 * ( avprice[j].price + avprice[j].sum );
839 }
840 /*and finally modify spobs based on the means */
841 for ( int i = 0; i < array_size( sys->spobs ); i++ ) {
842 Spob *spob = sys->spobs[i];
843 for ( int j = 0; j < array_size( spob->commodities ); j++ ) {
844 for ( int k = 0; k < array_size( avprice ); k++ ) {
845 if ( ( strcmp( avprice[k].name, spob->commodities[j]->name ) ==
846 0 ) ) {
847 spob->commodityPrice[j].price =
848 ( 0.25 * spob->commodityPrice[j].price +
849 0.75 * avprice[k].price );
851 ( 0.1 * ( 0.5 * avprice[k].spobVariation +
852 0.5 * spob->commodityPrice[j].spobVariation ) );
853 spob->commodityPrice[j].spobVariation *=
854 spob->commodityPrice[j].price;
855 spob->commodityPrice[j].sysVariation *=
856 spob->commodityPrice[j].price;
857 break;
858 }
859 }
860 }
861 }
862 array_free( sys->averagePrice );
863 sys->averagePrice = NULL;
864}
865
871{
872 /* First use spob attributes to set prices and variability */
873 for ( int k = 0; k < array_size( systems_stack ); k++ ) {
874 StarSystem *sys = &systems_stack[k];
875 for ( int j = 0; j < array_size( sys->spobs ); j++ ) {
876 Spob *spob = sys->spobs[j];
877 /* Set up the commodity prices on the system, based on its attributes.
878 */
879 for ( int i = 0; i < array_size( spob->commodities ); i++ ) {
880 if ( economy_calcPrice( spob, spob->commodities[i],
881 &spob->commodityPrice[i] ) )
882 return;
883 }
884 }
885 }
886
887 /* Modify prices and availability based on system attributes, and do some
888 * inter-spob averaging to smooth prices */
889 for ( int i = 0; i < array_size( systems_stack ); i++ ) {
890 StarSystem *sys = &systems_stack[i];
892 }
893
894 /* Compute average prices for all systems */
895 for ( int i = 0; i < array_size( systems_stack ); i++ ) {
896 StarSystem *sys = &systems_stack[i];
898 }
899
900 /* Smooth prices based on neighbouring systems */
901 for ( int i = 0; i < array_size( systems_stack ); i++ ) {
902 StarSystem *sys = &systems_stack[i];
904 }
905 /* And now free temporary commodity information */
906 for ( int i = 0; i < array_size( commodity_stack ); i++ ) {
907 CommodityModifier *this, *next;
908 Commodity *com = &commodity_stack[i];
909 next = com->spob_modifier;
910 com->spob_modifier = NULL;
911 while ( next != NULL ) {
912 this = next;
913 next = this->next;
914 free( this->name );
915 free( this );
916 }
917 next = com->faction_modifier;
918 com->faction_modifier = NULL;
919 while ( next != NULL ) {
920 this = next;
921 next = this->next;
922 free( this->name );
923 free( this );
924 }
925 }
926}
927
928/*
929 * Calculates commodity prices for a single spob (e.g. as added by the unidiff),
930 * and does some smoothing over the system, but not neighbours.
931 */
932void economy_initialiseSingleSystem( StarSystem *sys, Spob *spob )
933{
934 for ( int i = 0; i < array_size( spob->commodities ); i++ )
935 economy_calcPrice( spob, spob->commodities[i], &spob->commodityPrice[i] );
937}
938
939void economy_averageSeenPrices( const Spob *p )
940{
941 ntime_t t = ntime_get();
942 for ( int i = 0; i < array_size( p->commodities ); i++ ) {
943 Commodity *c = p->commodities[i];
944 CommodityPrice *cp = &p->commodityPrice[i];
945 if ( cp->updateTime <
946 t ) { /* has not yet been updated at present time. */
947 credits_t price;
948 cp->updateTime = t;
949 /* Calculate values for mean and std */
950 cp->cnt++;
951 price = economy_getPrice( c, NULL, p );
952 cp->sum += price;
953 cp->sum2 += price * price;
954 }
955 }
956}
957
958void economy_averageSeenPricesAtTime( const Spob *p, const ntime_t tupdate )
959{
960 ntime_t t = ntime_get();
961 for ( int i = 0; i < array_size( p->commodities ); i++ ) {
962 Commodity *c = p->commodities[i];
963 CommodityPrice *cp = &p->commodityPrice[i];
964 if ( cp->updateTime <
965 t ) { /* has not yet been updated at present time. */
966 credits_t price;
967 cp->updateTime = t;
968 cp->cnt++;
969 price = economy_getPriceAtTime( c, NULL, p, tupdate );
970 cp->sum += price;
971 cp->sum2 += price * price;
972 }
973 }
974}
975
980{
981 for ( int i = 0; i < array_size( systems_stack ); i++ ) {
982 StarSystem *sys = &systems_stack[i];
983 for ( int j = 0; j < array_size( sys->spobs ); j++ ) {
984 Spob *p = sys->spobs[j];
985 for ( int k = 0; k < array_size( p->commodityPrice ); k++ ) {
986 CommodityPrice *cp = &p->commodityPrice[k];
987 cp->cnt = 0;
988 cp->sum = 0;
989 cp->sum2 = 0;
990 cp->updateTime = 0;
991 }
992 }
993 }
994 for ( int i = 0; i < array_size( commodity_stack ); i++ )
995 commodity_stack[i].lastPurchasePrice = 0;
996}
997
1003{
1004 for ( int k = 0; k < array_size( p->commodityPrice ); k++ ) {
1005 CommodityPrice *cp = &p->commodityPrice[k];
1006 cp->cnt = 0;
1007 cp->sum = 0;
1008 cp->sum2 = 0;
1009 cp->updateTime = 0;
1010 }
1011}
1012
1019int economy_sysLoad( xmlNodePtr parent )
1020{
1021 xmlNodePtr node = parent->xmlChildrenNode;
1022
1024
1025 do {
1026 if ( xml_isNode( node, "economy" ) ) {
1027 xmlNodePtr cur = node->xmlChildrenNode;
1028 do {
1029 char *str;
1030 xml_onlyNodes( cur );
1031 if ( xml_isNode( cur, "system" ) ) {
1032 /* Ignore "name" attribute. */
1033 xmlNodePtr nodeSpob = cur->xmlChildrenNode;
1034 do {
1035 xml_onlyNodes( nodeSpob );
1036 if ( xml_isNode( nodeSpob, "spob" ) ) {
1037 xmlr_attr_strd( nodeSpob, "name", str );
1038 Spob *spob = spob_get( str );
1039 if ( spob == NULL )
1040 WARN( _( "Spob '%s' has saved economy data but doesn't "
1041 "exist!" ),
1042 str );
1043 free( str );
1044 if ( spob == NULL )
1045 continue;
1046 xmlNodePtr nodeCommodity = nodeSpob->xmlChildrenNode;
1047 do {
1048 xml_onlyNodes( nodeCommodity );
1049 if ( xml_isNode( nodeCommodity, "commodity" ) ) {
1050 xmlr_attr_strd( nodeCommodity, "name", str );
1051 CommodityPrice *cp = NULL;
1052 for ( int i = 0; i < array_size( spob->commodities );
1053 i++ ) {
1054 if ( ( strcmp( str,
1055 spob->commodities[i]->name ) ==
1056 0 ) ) {
1057 cp = &spob->commodityPrice[i];
1058 break;
1059 }
1060 }
1061 free( str );
1062 if ( cp != NULL ) {
1063 xmlr_attr_float( nodeCommodity, "sum", cp->sum );
1064 xmlr_attr_float( nodeCommodity, "sum2",
1065 cp->sum2 );
1066 xmlr_attr_int( nodeCommodity, "cnt", cp->cnt );
1067 xmlr_attr_long( nodeCommodity, "time",
1068 cp->updateTime );
1069 }
1070 }
1071 } while ( xml_nextNode( nodeCommodity ) );
1072 }
1073 } while ( xml_nextNode( nodeSpob ) );
1074 } else if ( xml_isNode( cur, "lastPurchase" ) ) {
1075 xmlr_attr_strd( cur, "name", str );
1076 if ( str ) {
1077 Commodity *c = commodity_get( str );
1078 c->lastPurchasePrice = xml_getLong( cur );
1079 free( str );
1080 }
1081 }
1082 } while ( xml_nextNode( cur ) );
1083 }
1084 } while ( xml_nextNode( node ) );
1085 return 0;
1086}
1087
1094int economy_sysSave( xmlTextWriterPtr writer )
1095{
1096 /* Save what the player has seen of the economy at each spob */
1097 xmlw_startElem( writer, "economy" );
1098 for ( int i = 0; i < array_size( commodity_stack ); i++ ) {
1099 if ( commodity_stack[i].lastPurchasePrice > 0 ) {
1100 xmlw_startElem( writer, "lastPurchase" );
1101 xmlw_attr( writer, "name", "%s", commodity_stack[i].name );
1102 xmlw_str( writer, "%" PRIu64, commodity_stack[i].lastPurchasePrice );
1103 xmlw_endElem( writer );
1104 }
1105 }
1106 for ( int i = 0; i < array_size( systems_stack ); i++ ) {
1107 int doneSys = 0;
1108 StarSystem *sys = &systems_stack[i];
1109 for ( int j = 0; j < array_size( sys->spobs ); j++ ) {
1110 Spob *p = sys->spobs[j];
1111 int doneSpob = 0;
1112 for ( int k = 0; k < array_size( p->commodities ); k++ ) {
1113 CommodityPrice *cp = &p->commodityPrice[k];
1114 if ( cp->cnt >
1115 0 ) { /* Player has seen this commodity at this location */
1116 if ( doneSys == 0 ) {
1117 doneSys = 1;
1118 xmlw_startElem( writer, "system" );
1119 xmlw_attr( writer, "name", "%s", sys->name );
1120 }
1121 if ( doneSpob == 0 ) {
1122 doneSpob = 1;
1123 xmlw_startElem( writer, "spob" );
1124 xmlw_attr( writer, "name", "%s", p->name );
1125 }
1126 xmlw_startElem( writer, "commodity" );
1127 xmlw_attr( writer, "name", "%s", p->commodities[k]->name );
1128 xmlw_attr( writer, "sum", "%f", cp->sum );
1129 xmlw_attr( writer, "sum2", "%f", cp->sum2 );
1130 xmlw_attr( writer, "cnt", "%d", cp->cnt );
1131 xmlw_attr( writer, "time", "%" PRIu64, cp->updateTime );
1132 xmlw_endElem( writer ); /* commodity */
1133 }
1134 }
1135 if ( doneSpob == 1 )
1136 xmlw_endElem( writer ); /* spob */
1137 }
1138 if ( doneSys == 1 )
1139 xmlw_endElem( writer ); /* system */
1140 }
1141 xmlw_endElem( writer ); /* economy */
1142 return 0;
1143}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:170
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_shrink(ptr_array)
Shrinks memory to fit only ‘size’ elements.
Definition array.h:160
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
Commodity * commodity_get(const char *name)
Gets a commodity by name.
Definition commodity.c:151
int * econ_comm
Definition economy.c:56
Commodity * commodity_stack
Definition commodity.c:35
StarSystem * systems_stack
Definition space.c:94
static void economy_smoothCommodityPrice(StarSystem *sys)
Calculates smoothing of commodity price based on neighbouring systems.
Definition economy.c:798
static int economy_calcPrice(Spob *spob, Commodity *commodity, CommodityPrice *commodityPrice)
Used during startup to set price and variation of the economy, depending on spob information.
Definition economy.c:612
static cs * econ_G
Definition economy.c:55
#define ECON_BASE_RES
Definition economy.c:37
int economy_init(void)
Initializes the economy.
Definition economy.c:438
void economy_clearKnown(void)
Clears all system knowledge.
Definition economy.c:979
static int econ_initialized
Definition economy.c:53
int economy_update(unsigned int dt)
Updates the economy.
Definition economy.c:506
void economy_addQueuedUpdate(void)
Increments the queued update counter.
Definition economy.c:465
int economy_sysLoad(xmlNodePtr parent)
Loads player's economy properties from an XML node.
Definition economy.c:1019
void economy_destroy(void)
Destroys the economy.
Definition economy.c:583
static void economy_calcUpdatedCommodityPrice(StarSystem *sys)
Modifies commodity price based on neighbouring systems.
Definition economy.c:833
credits_t economy_getPriceAtTime(const Commodity *com, const StarSystem *sys, const Spob *p, ntime_t tme)
Gets the price of a good on a spob in a system.
Definition economy.c:96
static void economy_modifySystemCommodityPrice(StarSystem *sys)
Modifies commodity price based on system characteristics.
Definition economy.c:710
int economy_getAveragePrice(const Commodity *com, credits_t *mean, double *std)
Gets the average price of a good as seen by the player (anywhere).
Definition economy.c:248
void economy_initialiseCommodityPrices(void)
Initialises commodity prices for the sinusoidal economy model.
Definition economy.c:870
void economy_clearSingleSpob(Spob *p)
Clears all economy knowledge of a given spob. Used by the unidiff system.
Definition economy.c:1002
#define ECON_PROD_MODIFIER
Definition economy.c:40
int economy_execQueued(void)
Calls economy_refresh if an economy update is queued.
Definition economy.c:473
credits_t economy_getPrice(const Commodity *com, const StarSystem *sys, const Spob *p)
Gets the price of a good on a spob in a system.
Definition economy.c:80
#define ECON_SELF_RES
Definition economy.c:38
#define ECON_PROD_VAR
Definition economy.c:42
#define ECON_FACTION_MOD
Definition economy.c:39
int economy_sysSave(xmlTextWriterPtr writer)
Saves what is needed to be saved for economy.
Definition economy.c:1094
int economy_refresh(void)
Regenerates the economy matrix. Should be used if the universe changes in any permanent way.
Definition economy.c:485
int economy_getAverageSpobPrice(const Commodity *com, const Spob *p, credits_t *mean, double *std)
Gets the average price of a good on a spob in a system, using a rolling average over the times the pl...
Definition economy.c:171
static int econ_queued
Definition economy.c:54
int areEnemies(int a, int b)
Checks whether two factions are enemies.
Definition faction.c:1450
const char * faction_name(int f)
Gets a factions "real" (internal) name.
Definition faction.c:331
int areAllies(int a, int b)
Checks whether two factions are allies or not.
Definition faction.c:1476
Header file with generic functions and naev-specifics.
#define MAX(x, y)
Definition naev.h:37
ntime_t ntime_get(void)
Gets the current time.
Definition ntime.c:113
double ntime_convertSeconds(ntime_t t)
Converts the time to seconds.
Definition ntime.c:158
static const double c[]
Definition rng.c:256
Spob * spob_get(const char *spobname)
Gets a spob based on its name.
Definition space.c:1107
Represents a dictionary of values used to modify a commodity.
Definition commodity.h:46
double sysVariation
Definition commodity.h:93
double spobVariation
Definition commodity.h:92
int64_t updateTime
Definition commodity.h:97
double spobPeriod
Definition commodity.h:89
Represents a commodity.
Definition commodity.h:57
CommodityModifier * spob_modifier
Definition commodity.h:79
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
double period
Definition commodity.h:80
char * price_ref
Definition commodity.h:63
int range
Definition space.h:82
int faction
Definition space.h:79
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
Definition space.h:102
Commodity ** commodities
Definition space.h:134
char * gfx_spaceName
Definition space.h:147
char * class
Definition space.h:116
char * gfx_exterior
Definition space.h:149
char * name
Definition space.h:105
CommodityPrice * commodityPrice
Definition space.h:136
double population
Definition space.h:118
SpobPresence presence
Definition space.h:121