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