imginterface: add rcb tags for lepton
[rofl0r-pato.git] / pato.c
blobb394301486e946bc50e144894c057338855ce1bd
1 /*
2 *
3 * this is a game prototype modeled after "händler des nordens", aka "patrizier online"
4 * it uses a ncurses based gui capable of 256 colors.
5 *
6 * author (C) 2011 rofl0r
7 *
8 * all source code files are
9 *
10 * LICENSE: GPL v3
12 * creative content such as graphics or world describing texts are CC by SA (Creative Commons License)
16 TODO
18 wenn goodsreq4good > gesamtlager kapa, lager kaufen
19 bug workers hamburgpc total > worker players
20 gui.c menus for players
22 bug repeated resizing of the terminal will crash game
26 #include <stdlib.h>
27 #include <time.h>
28 #include <stdio.h>
29 #include <assert.h>
30 #include <unistd.h>
31 #include <stdint.h>
32 #include <math.h>
33 #include <errno.h>
35 #include "../lib/include/iniparser.h"
37 #include "pato.h"
38 #include "gui.h"
40 Gui *gui;
42 // gcc -Wall -Wextra -g -DNOKDEV pato.c ../lib/stringptr.c ../lib/iniparser.c -lm -o pato
44 size_t GAME_SPEED = 1;
46 const unsigned long long goodcat_minprice[] = {
48 32,
49 60,
50 100,
54 #ifndef IN_KDEVELOP_PARSER
55 const ShipProps shipProps[] = {
56 {NULL,
57 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
58 {SPLITERAL("schnigge"),
59 50 , 20, 15000, 10, 81, 70 , 8 , 4 , 24, 30, 10, 5 , 5 , 80 },
60 {SPLITERAL("kraier"),
61 80 , 30, 40000, 14, 90, 80 , 12, 12, 42, 50, 20, 10, 10, 240 },
62 {SPLITERAL("kogge"),
63 130, 30, 30000, 8 , 60, 100, 16, 6 , 56, 50, 15, 15, 10, 120 },
64 {SPLITERAL("holk"),
65 140, 40, 70000, 12, 75, 120, 20, 16, 76, 80, 30, 15, 20, 320 }
67 #endif
69 #ifndef IN_KDEVELOP_PARSER
70 const stringptr* populationDesc[] = {
71 SPLITERAL("none"),
72 SPLITERAL("beggar"),
73 SPLITERAL("worker"),
74 SPLITERAL("commoner"),
75 SPLITERAL("aristocrat"),
77 #endif
79 const unsigned long long rentTax[PT_MAX] = {
87 FactoryCommon factoryProps;
89 Factory Factories[GT_MAX];
91 char externalGoodRequiredForProduction[GT_MAX]; // if another good is req. for the production of this
92 char goodRequiredForProduction[GT_MAX]; // if this good is required for the production of another good
94 Warehouse Storage;
95 Warehouse Branchoffice;
97 #ifndef IN_KDEVELOP_PARSER
99 const Good Goods[] = {
100 { SPLITERAL("none"), GT_NONE, GC_NONE },
101 { SPLITERAL("wood"), GT_WOOD, GC_A },
102 { SPLITERAL("meat"), GT_MEAT, GC_A },
103 { SPLITERAL("veg"), GT_VEG, GC_A },
104 { SPLITERAL("barleyjuice"), GT_BARLEYJUICE, GC_A },
105 { SPLITERAL("crop"), GT_CROP, GC_A },
106 { SPLITERAL("brick"), GT_BRICK, GC_A },
107 { SPLITERAL("ore"), GT_ORE, GC_B },
108 { SPLITERAL("hemp"), GT_HEMP, GC_B },
109 { SPLITERAL("honey"), GT_HONEY, GC_B },
110 { SPLITERAL("copper"), GT_COPPER, GC_B },
111 { SPLITERAL("salt"), GT_SALT, GC_B },
112 { SPLITERAL("wool"), GT_WOOL, GC_B },
113 { SPLITERAL("tool"), GT_TOOL, GC_C },
114 { SPLITERAL("rope"), GT_ROPE, GC_C },
115 { SPLITERAL("met"), GT_MET, GC_C },
116 { SPLITERAL("ceramics"), GT_CERAMICS, GC_C },
117 { SPLITERAL("ham"), GT_HAM, GC_C },
118 { SPLITERAL("cloth"), GT_CLOTH, GC_C },
119 { SPLITERAL("beer"), GT_BEER, GC_D },
120 { SPLITERAL("fur"), GT_FUR, GC_D },
121 { SPLITERAL("cheese"), GT_CHEESE, GC_D },
122 { SPLITERAL("pitch"), GT_PITCH, GC_D },
123 { SPLITERAL("fish"), GT_FISH, GC_D },
124 { SPLITERAL("wine"), GT_WINE, GC_D },
127 const stringptr* NotificationNames[NT_MAX] = {
128 [NT_NONE] = NULL,
129 [NT_NO_MONEY] = SPL("out of money"),
130 [NT_BAD_MOOD] = SPL("bad mood!"),
131 [NT_OUT_OF_PRODUCTIONGOODS] = SPL("out of prod. goods"),
132 [NT_STOCK_FULL] = SPL("stock full!"),
133 [NT_SHIPS_BOUGHT] = SPL("bought ships"),
136 #endif
139 const Notification emptyNotification = {NT_NONE, 0, 0, 0.f, 0.f};
140 Player Players[MAX_PLAYERS];
141 World world;
142 City Cities[MAX_CITIES];
144 //lookup table for producer cities for certain goods.
145 size_t producers[GT_MAX][MAX_CITIES];
146 size_t producerCount[GT_MAX];
148 size_t numCities;
149 size_t numPlayers;
151 float populationConsumation[PT_MAX][GT_MAX];
154 PlayerType playerTypeFromString(stringptr* pt) {
155 if(stringptr_eq(pt, SPLITERAL("pc")))
156 return PLT_CPU;
157 else return PLT_USER;
161 Notification makeNotification(NotificationType nt, size_t val1, size_t val2, float fval1, float fval2) {
162 Notification result;
163 result.nt = nt;
164 result.val1 = val1;
165 result.val2 = val2;
166 result.fval1 = fval1;
167 result.fval2 = fval2;
168 return result;
171 stringptr* getNotificationName(NotificationType x) {
172 return (stringptr*) NotificationNames[x];
175 void notify(size_t player, Notification n) {
176 Notification* last;
177 last = &Players[player].inbox.notifications[Players[player].inbox.last];
178 if(last->nt == n.nt && last->val1 == n.val1 && last->val2 == n.val2)
179 return;
180 gui_notify(gui, player, n);
181 Players[player].inbox.last++;
182 if(Players[player].inbox.last >= MAX_NOTIFICATIONS)
183 Players[player].inbox.last = 0;
184 Players[player].inbox.notifications[Players[player].inbox.last] = n;
187 stringptr* stringFromPopulationType(populationType p) {
188 if (p < PT_MAX) return (stringptr*) populationDesc[p];
189 return (stringptr*) populationDesc[0];
192 Goodtype goodTypeFromString(stringptr* good) {
193 size_t i;
194 for(i = 0; i < GT_MAX; i++) {
195 if(stringptr_eq(good, Goods[i].name))
196 return Goods[i].type;
198 return GT_NONE;
201 stringptr* stringFromGoodType(Goodtype g) {
202 if(g < GT_MAX) return Goods[g].name;
203 return Goods[0].name;
206 void initWorld(void) {
207 stringptr* cf = stringptr_fromfile("world.txt");
208 stringptrlist* lines = stringptr_linesplit(cf);
209 stringptr inibuf;
210 ini_section sec;
211 sec = iniparser_get_section(lines, SPLITERAL("main"));
212 iniparser_getvalue(lines, &sec, SPLITERAL("date"), &inibuf);
213 world.date = atoi(inibuf.ptr);
214 iniparser_getvalue(lines, &sec, SPLITERAL("monthsperyear"), &inibuf);
215 world.monthsperyear = atoi(inibuf.ptr);
216 iniparser_getvalue(lines, &sec, SPLITERAL("dayspermonth"), &inibuf);
217 world.dayspermonth = atoi(inibuf.ptr);
218 iniparser_getvalue(lines, &sec, SPLITERAL("hoursperday"), &inibuf);
219 world.hoursperday = atoi(inibuf.ptr);
220 iniparser_getvalue(lines, &sec, SPLITERAL("minutesperhour"), &inibuf);
221 world.minutesperhour = atoi(inibuf.ptr);
222 iniparser_getvalue(lines, &sec, SPLITERAL("secondsperminute"), &inibuf);
223 world.secondsperminute = atoi(inibuf.ptr);
225 world._dayseconds = world.secondsperminute * world.minutesperhour * world.hoursperday;
226 world._fdaysperyear = (float) world.dayspermonth * (float) world.monthsperyear;
228 stringptr_free(cf);
229 stringptrlist_free(lines);
232 void initConsumationTable(void) {
233 size_t i, g;
234 char buf[64];
235 stringptr bufptr;
236 stringptr* cf = stringptr_fromfile("consumation.txt");
237 stringptrlist* lines = stringptr_linesplit(cf);
238 stringptr inibuf;
239 ini_section sec;
240 bufptr.ptr = buf;
241 for(g = 1; g < PT_MAX; g++) {
242 sec = iniparser_get_section(lines, stringFromPopulationType(g));
243 for(i = 1; i < GT_MAX; i++) {
244 bufptr.size = snprintf(buf, sizeof(buf), "consumation_%s", stringFromGoodType(i)->ptr);
245 iniparser_getvalue(lines, &sec, &bufptr, &inibuf);
246 populationConsumation[g][i] = atof(inibuf.ptr);
249 stringptr_free(cf);
250 stringptrlist_free(lines);
253 void freeCities(void) {
254 size_t i;
255 for (i = 0; i < numCities; i++) {
256 if(Cities[i].name)
257 stringptr_free(Cities[i].name);
261 void initCities(void) {
262 size_t i, g;
263 char buf[32];
264 stringptr bufptr;
265 stringptr* cf = stringptr_fromfile("cities.txt");
266 stringptrlist* lines = stringptr_linesplit(cf);
267 ini_section sec = iniparser_get_section(lines, SPLITERAL("main"));
268 stringptr inibuf;
269 iniparser_getvalue(lines, &sec, SPLITERAL("citycount"), &inibuf);
270 numCities = atoi(inibuf.ptr);
271 bufptr.ptr = buf;
272 memset(producerCount, 0, sizeof(producerCount));
273 for (i = 0; i < numCities; i++) {
274 bufptr.size = snprintf(buf, sizeof(buf), "city%.3d", (int) i);
275 sec = iniparser_get_section(lines, &bufptr);
276 iniparser_getvalue(lines, &sec, SPLITERAL("name"), &inibuf);
277 Cities[i].name = stringptr_copy(&inibuf);
278 iniparser_getvalue(lines, &sec, SPLITERAL("x"), &inibuf);
279 Cities[i].coords.x = atof(inibuf.ptr);
280 iniparser_getvalue(lines, &sec, SPLITERAL("y"), &inibuf);
281 Cities[i].coords.y = atof(inibuf.ptr);
283 iniparser_getvalue(lines, &sec, SPLITERAL("tax"), &inibuf);
284 Cities[i].tax = atof(inibuf.ptr);
285 iniparser_getvalue(lines, &sec, SPLITERAL("money"), &inibuf);
286 Cities[i].money = atoll(inibuf.ptr);
288 // available industry
289 for(g = 0; g < CITY_MAX_INDUSTRYTYPES; g++) {
290 bufptr.size = snprintf(buf, sizeof(buf), "industry%.3d", (int) g);
291 iniparser_getvalue(lines, &sec, &bufptr, &inibuf);
292 Cities[i].industry[g] = goodTypeFromString(&inibuf);
293 producers[Cities[i].industry[g]][producerCount[Cities[i].industry[g]]++] = i;
295 // market
296 for(g = 1; g < GT_MAX; g++) {
297 bufptr.size = snprintf(buf, sizeof(buf), "stock_%s", stringFromGoodType(g)->ptr);
298 iniparser_getvalue(lines, &sec, &bufptr, &inibuf);
299 Cities[i].market.stock[g] = atoi(inibuf.ptr);
301 bufptr.size = snprintf(buf, sizeof(buf), "avgprice_%s", stringFromGoodType(g)->ptr);
302 iniparser_getvalue(lines, &sec, &bufptr, &inibuf);
303 Cities[i].market.avgPricePayed[g] = atof(inibuf.ptr);
305 // population
306 for(g = 1; g < PT_MAX; g++) {
307 bufptr.size = snprintf(buf, sizeof(buf), "%s_mood", stringFromPopulationType(g)->ptr);
308 iniparser_getvalue(lines, &sec, &bufptr, &inibuf);
309 Cities[i].populationMood[g] = atof(inibuf.ptr);
310 if(Cities[i].populationMood[g] > 100)
311 Cities[i].populationMood[g] = 100.0f;
312 if(g == PT_WORKER) {
313 Cities[i].population[g] = 0;
314 continue;
316 bufptr.size = snprintf(buf, sizeof(buf), "%s_count", stringFromPopulationType(g)->ptr);
317 iniparser_getvalue(lines, &sec, &bufptr, &inibuf);
318 Cities[i].population[g] = atoi(inibuf.ptr);
321 // shipyard
322 for (g = 1; g < ST_MAX; g++) {
323 bufptr.size = snprintf(buf, sizeof(buf), "shiptype%dcount", (int) (g - 1));
324 iniparser_getvalue(lines, &sec, &bufptr, &inibuf);
325 Cities[i].shipyard.availableShips.numShips[g] = atoi(inibuf.ptr);
328 stringptr_free(cf);
329 stringptrlist_free(lines);
332 ptrdiff_t findCityFromString(stringptr* name) {
333 size_t i;
334 for(i = 0; i < numCities; i++) {
335 if(stringptr_eq(Cities[i].name, name))
336 return i;
338 return -1;
341 ShipLocationType shipLocationTypeFromString(stringptr* loc) {
342 if(stringptr_eq(loc, SPLITERAL("sea")))
343 return SLT_SEA;
344 else
345 return SLT_CITY;
348 stringptr* getPlayerName(size_t playerid) {
349 return Players[playerid].name;
352 void freePlayers(void) {
353 size_t p, s;
354 for(p = 0; p < numPlayers; p++) {
355 if(Players[p].name)
356 stringptr_free(Players[p].name);
357 for (s = 0; s < Players[p].numConvoys; s++) {
358 if(Players[p].convoys[s].name)
359 stringptr_free(Players[p].convoys[s].name);
364 void initPlayers(void) {
365 size_t i, p, s, t;
366 char buf[32];
367 stringptr bufptr;
368 stringptr* cf = stringptr_fromfile("players.txt");
369 stringptrlist* lines = stringptr_linesplit(cf);
370 ini_section sec = iniparser_get_section(lines, SPLITERAL("main"));
371 stringptr inibuf;
372 iniparser_getvalue(lines, &sec, SPLITERAL("playercount"), &inibuf);
373 numPlayers = atoi(inibuf.ptr);
374 bufptr.ptr = buf;
375 stringptr_free(cf);
376 stringptrlist_free(lines);
377 for(p = 0; p < numPlayers; p++) {
378 bufptr.size = snprintf(buf, sizeof(buf), "players/player%.6d.txt", (int) p);
379 cf = stringptr_fromfile(buf);
380 lines = stringptr_linesplit(cf);
381 sec = iniparser_get_section(lines, SPLITERAL("main"));
382 iniparser_getvalue(lines, &sec, SPLITERAL("name"), &inibuf);
383 Players[p].name = stringptr_copy(&inibuf);
384 iniparser_getvalue(lines, &sec, SPLITERAL("type"), &inibuf);
385 Players[p].type = playerTypeFromString(&inibuf);
386 iniparser_getvalue(lines, &sec, SPLITERAL("branches"), &inibuf);
387 Players[p].numBranches = atoi(inibuf.ptr);
388 iniparser_getvalue(lines, &sec, SPLITERAL("convoys"), &inibuf);
389 Players[p].numConvoys = atoi(inibuf.ptr);
390 iniparser_getvalue(lines, &sec, SPLITERAL("othershiplocations"), &inibuf);
391 Players[p].numShipLocations = atoi(inibuf.ptr);
392 iniparser_getvalue(lines, &sec, SPLITERAL("money"), &inibuf);
393 Players[p].money = atoll(inibuf.ptr);
394 //branches
395 for(s = 0; s < Players[p].numBranches; s++) {
396 bufptr.size = snprintf(buf, sizeof(buf), "branch%.3d", (int) s);
397 sec = iniparser_get_section(lines, &bufptr);
398 iniparser_getvalue(lines, &sec, SPLITERAL("city"), &inibuf);
399 Players[p].branchCity[s] = findCityFromString(&inibuf);
400 assert((ptrdiff_t) Players[p].branchCity[s] >= 0);
401 iniparser_getvalue(lines, &sec, SPLITERAL("workers"), &inibuf);
402 Players[p].branchWorkers[s] = atoi(inibuf.ptr);
403 Cities[Players[p].branchCity[s]].population[PT_WORKER] += Players[p].branchWorkers[s];
404 for(i = 0; i < CITY_MAX_INDUSTRYTYPES + 1; i++) {
405 bufptr.size = snprintf(buf, sizeof(buf), "factorycount%.3d", (int) i);
406 iniparser_getvalue(lines, &sec, &bufptr, &inibuf);
407 Players[p].branchFactories[s][i] = atoi(inibuf.ptr);
409 // market
410 for(i = 1; i < GT_MAX; i++) {
411 bufptr.size = snprintf(buf, sizeof(buf), "stock_%s", stringFromGoodType(i)->ptr);
412 iniparser_getvalue(lines, &sec, &bufptr, &inibuf);
413 Players[p].branchStock[s].stock[i] = atoi(inibuf.ptr);
415 bufptr.size = snprintf(buf, sizeof(buf), "avgprice_%s", stringFromGoodType(i)->ptr);
416 iniparser_getvalue(lines, &sec, &bufptr, &inibuf);
417 Players[p].branchStock[s].avgPricePayed[i] = atof(inibuf.ptr);
421 //convoys
422 for (s = 0; s < Players[p].numConvoys; s++) {
423 bufptr.size = snprintf(buf, sizeof(buf), "convoy%.3d", (int) s);
424 sec = iniparser_get_section(lines, &bufptr);
426 iniparser_getvalue(lines, &sec, SPLITERAL("name"), &inibuf);
427 Players[p].convoys[s].name = stringptr_copy(&inibuf);
429 iniparser_getvalue(lines, &sec, SPLITERAL("captainsalary"), &inibuf);
430 Players[p].convoys[s].captainSalary = atoi(inibuf.ptr);
432 iniparser_getvalue(lines, &sec, SPLITERAL("sailors"), &inibuf);
433 Players[p].convoys[s].numSailors = atoi(inibuf.ptr);
435 iniparser_getvalue(lines, &sec, SPLITERAL("condition"), &inibuf);
436 Players[p].convoys[s].condition = atof(inibuf.ptr);
438 iniparser_getvalue(lines, &sec, SPLITERAL("location"), &inibuf);
439 Players[p].convoys[s].loc = shipLocationTypeFromString(&inibuf);
441 if(Players[p].convoys[s].loc == SLT_CITY) {
442 Players[p].convoys[s].locCity = findCityFromString(&inibuf);
443 } else {
444 iniparser_getvalue(lines, &sec, SPLITERAL("x"), &inibuf);
445 Players[p].convoys[s].coords.x = atof(inibuf.ptr);
447 iniparser_getvalue(lines, &sec, SPLITERAL("y"), &inibuf);
448 Players[p].convoys[s].coords.y = atof(inibuf.ptr);
450 for(i = 1; i < ST_MAX; i++) {
451 bufptr.size = snprintf(buf, sizeof(buf), "shiptype%dcount", (int) (i - 1));
452 iniparser_getvalue(lines, &sec, &bufptr, &inibuf);
453 Players[p].convoys[s].shipcounter.numShips[i] = atoi(inibuf.ptr);
455 // goods loaded
456 Players[p].convoys[s].totalload = 0;
457 for(i = 1; i < GT_MAX; i++) {
458 bufptr.size = snprintf(buf, sizeof(buf), "stock_%s", stringFromGoodType(i)->ptr);
459 iniparser_getvalue(lines, &sec, &bufptr, &inibuf);
460 Players[p].convoys[s].load.stock[i] = atoi(inibuf.ptr);
461 Players[p].convoys[s].totalload += Players[p].convoys[s].load.stock[i];
463 bufptr.size = snprintf(buf, sizeof(buf), "avgprice_%s", stringFromGoodType(i)->ptr);
464 iniparser_getvalue(lines, &sec, &bufptr, &inibuf);
465 Players[p].convoys[s].load.avgPricePayed[i] = atof(inibuf.ptr);
470 // single ships (well, kinda)
471 // size_t shipLocations[MAX_CITIES]; // stores the city "id" with single ships (those not in a convoy)
472 // ShipCounter singleShips[MAX_CITIES]; // stored in the order of shipLocations
475 for (s = 0; s < Players[p].numShipLocations; s++) {
476 bufptr.size = snprintf(buf, sizeof(buf), "shiplocation%.3d", (int) s);
477 sec = iniparser_get_section(lines, &bufptr);
478 iniparser_getvalue(lines, &sec, SPLITERAL("city"), &inibuf);
479 t = findCityFromString(&inibuf);
481 assert((ptrdiff_t) t >= 0);
483 Players[p].shipLocations[s] = t;
484 Players[p].singleShips[s].total = 0;
486 for(i = 1; i < ST_MAX; i++) {
487 bufptr.size = snprintf(buf, sizeof(buf), "shiptype%dcount", (int) (i - 1));
488 iniparser_getvalue(lines, &sec, &bufptr, &inibuf);
489 Players[p].singleShips[s].numShips[i] = atoi(inibuf.ptr);
490 Players[p].singleShips[s].total += Players[p].singleShips[s].numShips[i];
492 bufptr.size = snprintf(buf, sizeof(buf), "shiptype%dcondition", (int) (i - 1));
493 iniparser_getvalue(lines, &sec, &bufptr, &inibuf);
494 Players[p].singleShipConditions[s].condition[i] = atof(inibuf.ptr);
498 stringptr_free(cf);
499 stringptrlist_free(lines);
503 void initBuildings(void) {
504 size_t i, g;
505 char buf[32];
506 stringptr bufptr;
507 bufptr.ptr = buf;
508 stringptr inibuf;
509 stringptr* cf = stringptr_fromfile("buildings.txt");
510 stringptrlist* lines = stringptr_linesplit(cf);
511 ini_section sec;
512 int temp;
514 sec = iniparser_get_section(lines, SPLITERAL("main"));
515 iniparser_getvalue(lines, &sec, SPLITERAL("maxworkersperfactory"), &inibuf);
516 factoryProps.maxworkersperfactory = atoi(inibuf.ptr);
517 iniparser_getvalue(lines, &sec, SPLITERAL("workersalary"), &inibuf);
518 factoryProps.workersalary = atoi(inibuf.ptr);
520 sec = iniparser_get_section(lines, SPLITERAL("branch"));
521 iniparser_getvalue(lines, &sec, SPLITERAL("buildcost"), &inibuf);
522 Branchoffice.buildcost = atoi(inibuf.ptr);
523 iniparser_getvalue(lines, &sec, SPLITERAL("storage"), &inibuf);
524 Branchoffice.storage = atoi(inibuf.ptr);
526 sec = iniparser_get_section(lines, SPLITERAL("warehouse"));
527 iniparser_getvalue(lines, &sec, SPLITERAL("buildcost"), &inibuf);
528 Storage.buildcost = atoi(inibuf.ptr);
529 iniparser_getvalue(lines, &sec, SPLITERAL("storage"), &inibuf);
530 Storage.storage = atoi(inibuf.ptr);
532 memset(goodRequiredForProduction, 0, sizeof(goodRequiredForProduction));
534 for(i = 1; i < GT_MAX; i++) {
535 bufptr.size = snprintf(buf, sizeof(buf), "factory_%s", stringFromGoodType(i)->ptr);
536 sec = iniparser_get_section(lines, &bufptr);
537 iniparser_getvalue(lines, &sec, SPLITERAL("buildcost"), &inibuf);
538 Factories[i].buildcost = atoi(inibuf.ptr);
539 iniparser_getvalue(lines, &sec, SPLITERAL("yield"), &inibuf);
540 Factories[i].yield = atof(inibuf.ptr);
541 temp = 0;
542 for(g = 1; g < GT_MAX; g++) {
543 bufptr.size = snprintf(buf, sizeof(buf), "consumation_%s", stringFromGoodType(g)->ptr);
544 iniparser_getvalue(lines, &sec, &bufptr, &inibuf);
545 Factories[i].consumation[g] = inibuf.ptr ? atof(inibuf.ptr) : 0.0f;
546 if(Factories[i].consumation[g] > 0.f) {
547 temp = 1;
548 goodRequiredForProduction[g] = 1;
551 if(temp)
552 externalGoodRequiredForProduction[i] = 1;
553 else
554 externalGoodRequiredForProduction[i] = 0;
556 stringptr_free(cf);
557 stringptrlist_free(lines);
560 size_t getMaxWorkerCount(size_t branch, size_t player) {
561 size_t result = 0;
562 size_t i;
563 for(i = 0; i < CITY_MAX_INDUSTRYTYPES; i++) {
564 result += Players[player].branchFactories[branch][i] * 10;
566 return result;
569 size_t getFreeJobs(size_t branch, size_t player) {
570 size_t result = getMaxWorkerCount(branch, player);
571 if(result) result -= Players[player].branchWorkers[branch];
572 return result;
575 void getPlayersWithFreeWorkCapacity(size_t city, size_t* totalcapacity, size_t* num_players, size_t* affected_players, size_t* free_jobs) {
576 size_t p, b, c;
577 *num_players = 0;
578 *totalcapacity = 0;
580 for(p = 0; p < numPlayers; p++) {
581 if(Players[p].money) {
582 for(b = 0; b < Players[p].numBranches; b++) {
583 if(Players[p].branchCity[b] == city) {
584 c = getFreeJobs(b, p);
585 if(c) {
586 *totalcapacity += c;
587 (*num_players)++;
588 affected_players[*num_players - 1] = p;
589 free_jobs[*num_players - 1] = c;
590 break;
598 ptrdiff_t getShipLocationIDFromCity(size_t city, size_t player) {
599 size_t i;
600 for(i = 0; i < Players[player].numShipLocations; i++) {
601 if(Players[player].shipLocations[i] == city)
602 return i;
604 return -1;
607 ptrdiff_t getCityIDFromBranch(size_t branch, size_t player) {
608 if(branch >= Players[player].numBranches) return -1;
609 return Players[player].branchCity[branch];
612 ptrdiff_t getBranchIDFromCity(size_t city, size_t player) {
613 size_t b;
614 for(b = 0; b < Players[player].numBranches; b++) {
615 if(Players[player].branchCity[b] == city) {
616 return b;
619 return -1;
622 size_t getPlayerMaxBranchStorage(size_t branch, size_t player) {
623 size_t result = 0;
624 result += Players[player].branchFactories[branch][CITY_MAX_INDUSTRYTYPES] * Storage.storage;
625 result += Branchoffice.storage;
626 return result;
629 float getPlayerFreeBranchStorage(size_t branch, size_t player) {
630 float result = (float) getPlayerMaxBranchStorage(branch, player);
631 size_t i;
632 for(i = 1; i < GT_MAX; i++) {
633 result -= Players[player].branchStock[branch].stock[i];
635 return result;
638 size_t getPlayerFactoryCount(size_t branch, size_t player) {
639 size_t result = 0;
640 size_t i;
641 for (i = 0; i < CITY_MAX_INDUSTRYTYPES; i++) {
642 result += Players[player].branchFactories[branch][i];
644 return result;
647 size_t getCityPopulation(size_t city) {
648 size_t i, result = 0;
649 for(i = 1; i < PT_MAX; i++) {
650 result += Cities[city].population[i];
652 return result;
655 // return price for 1 ton
656 float calculatePrice(size_t city, Goodtype g, float amount, unsigned sell) {
657 if(amount > Cities[city].market.stock[g])
658 amount = Cities[city].market.stock[g];
660 float minprice = goodcat_minprice[Goods[g].cat];
661 float maxprice = minprice * (sell ? 4 : 3);
663 float consumationperday = getPopulationConsumation(g, city);
664 float remaining_stock_lasts_days = (Cities[city].market.stock[g] - (sell ? amount : -amount) ) / consumationperday;
665 /* if the stock lasts for less than 10 days, the maximum price holds */
666 const float lo_tresh = 10.f;
667 /* if it lasts for longer than this, it's sold for the minimum */
668 const float hi_tresh = 100.f;
669 const float scale = hi_tresh - lo_tresh;
670 float price;
671 if(remaining_stock_lasts_days < lo_tresh)
672 price = maxprice;
673 else if(remaining_stock_lasts_days > hi_tresh)
674 price = minprice;
675 else {
676 float x = remaining_stock_lasts_days - lo_tresh;
677 float y = x / scale; //returns a value between 0 and 1. 1 for the full span (bestand == hitresh)
678 price = minprice + ( 1 - y ) * (maxprice - minprice);
682 //Cities[city].market.avgPricePayed[g];
683 float factor = (float) ((Cities[city].market.stock[g] - amount) * 2) + 0.99f / (float) (getCityPopulation(city) + 1);
684 float value = minprice + ((minprice * (sell ? 3 : 2)) * (1.f - factor));
685 if (value < (float) minprice)
686 value = (float) minprice;
687 if(sell)
688 value = value * (1.f + (Cities[city].tax / 100.f));
689 return (unsigned long long) value; */
691 if(sell) price *= (1.f + (Cities[city].tax / 100.f));
693 return price;
697 void sell(size_t city, size_t player, size_t branch, size_t convoy, Goodtype g, float amount, sellFlags flags) {
698 long long price;
699 Market* buyermarket;
700 Market* sellermarket;
701 long long* buyerportemonnaie;
702 long long* sellerportemonnaie;
703 unsigned citysells = 0;
704 if(flags & SELL_TO_CITY) {
705 buyermarket = &Cities[city].market;
706 buyerportemonnaie = &Cities[city].money;
707 } else if (flags & SELL_TO_CONVOY) {
708 citysells = 1;
709 buyermarket = &Players[player].convoys[convoy].load;
710 Players[player].convoys[convoy].totalload += amount;
711 buyerportemonnaie = &Players[player].money;
712 } else if (flags & SELL_TO_PLAYERSTOCK) {
713 citysells = 1;
714 buyermarket = &Players[player].branchStock[branch];
715 buyerportemonnaie = &Players[player].money;
716 } else if (flags & SELL_TO_POPULATION) {
717 citysells = 1;
718 buyermarket = NULL;
719 buyerportemonnaie = NULL;
720 } else {
721 assert(0);
724 if(flags & SELL_FROM_OVERPRODUCTION) {
725 sellermarket = NULL;
726 sellerportemonnaie = &Players[player].money;
727 } else if (flags & SELL_FROM_CONVOY) {
728 sellermarket = &Players[player].convoys[convoy].load;
729 Players[player].convoys[convoy].totalload -= amount;
730 sellerportemonnaie = &Players[player].money;
731 } else if (flags & SELL_FROM_STOCK) {
732 sellermarket = &Players[player].branchStock[branch];
733 sellerportemonnaie = &Players[player].money;
734 } else if (flags & SELL_FROM_CITY) {
735 #ifdef CONSOLE_DEBUG
736 if(player != (size_t) -1)
737 printf("player %d buys %f %s from %d!\n", (int) player, amount, Goods[g].name->ptr, (int) city);
738 #endif
739 sellermarket = &Cities[city].market;
740 sellerportemonnaie = &Cities[city].money;
741 } else {
742 assert(0);
745 price = calculatePrice(city, g, amount, citysells);
746 assert(price >= 0);
748 if(buyerportemonnaie) {
749 *buyerportemonnaie -= (unsigned long long) ((float) price * amount);
750 assert(*buyerportemonnaie >= 0);
753 if(sellerportemonnaie) {
754 *sellerportemonnaie += (unsigned long long) ((float) price * amount);
755 assert(*sellerportemonnaie >= 0);
758 if(sellermarket) {
759 sellermarket->stock[g] -= amount;
760 assert(sellermarket->stock[g] >= 0.0);
763 if(buyermarket) {
764 buyermarket->stock[g] += amount;
765 assert(buyermarket->stock[g] >= 0.0);
770 #ifdef CONSOLE_DEBUG
771 void showDate(void) {
772 puts("=================================================");
773 printf("day: %lu\n", world.date / world._dayseconds);
776 void showPlayerStatus(size_t player, size_t branch) {
777 puts("=================================================");
778 printf("status for %s\n", Players[player].name->ptr);
779 printf("money: %llu\n", Players[player].money);
780 printf("workers: %d\n", (int) Players[player].branchWorkers[branch]);
781 printf("free stock: %d\n", (int) getPlayerFreeBranchStorage(branch, player));
784 void showCityStatus(size_t city) {
785 size_t i;
786 puts("=================================================");
787 printf("status for %s\n", Cities[city].name->ptr);
788 printf("money: %llu\n", Cities[city].money);
789 printf("population:\n");
790 for(i = 1; i < PT_MAX; i++) {
791 printf("%s: count: %d, mood: %f\n", stringFromPopulationType(i)->ptr, (int) Cities[city].population[i], Cities[city].populationMood[i]);
793 printf("\nmarket:\n");
794 for(i = 1; i < GT_MAX; i++) {
795 printf("%s: stock %f\n", stringFromGoodType(i)->ptr, Cities[city].market.stock[i]);
798 #endif
800 // c == city
801 float getPopulationConsumationPerPopulationType(Goodtype g, size_t c, populationType p) {
802 return (populationConsumation[p][g] / world._fdaysperyear) * Cities[c].population[p];
805 float getPopulationConsumation(Goodtype g, size_t c) {
806 float res = 0.f;
807 size_t p;
808 // consumation of goods.
809 for(p = 1; p < PT_MAX; p++) {
810 res += getPopulationConsumationPerPopulationType(g, c, p);
812 return res;
815 void newDay(void) {
816 size_t i, g, p, pl, plsave, b, f;
817 float stock;
818 float maxconsumption[PT_MAX];
819 float consumed[PT_MAX];
820 float wantsconsume;
821 float percent, leavepercent;
822 size_t leaving, leavingp;
823 size_t affected_players[MAX_PLAYERS];
824 size_t free_jobs[MAX_PLAYERS];
825 size_t totalcapacity;
826 size_t num_players_affected;
827 size_t luckyplayer;
828 ptrdiff_t branchid = -1;
829 float freestorage;
830 float workersPerFactory;
831 float produced, required;
832 long long cost;
834 for (i = 0; i < numCities; i++) {
835 // create ships
836 if(Cities[i].shipyard.availableShips.numShips[ST_A] < 2) {
837 Cities[i].shipyard.availableShips.numShips[ST_A] = 2;
838 // TODO: decrease stock/money for goods necessary to build ships...
841 // cash-in rent-tax...
842 for(p = 1; p < PT_MAX; p++) {
843 Cities[i].money += rentTax[p] * Cities[i].population[p];
846 // consumation of goods.
847 for(p = 1; p < PT_MAX; p++) {
848 maxconsumption[p] = 0.0f;
849 consumed[p] = 0.0f;
850 for(g = 1; g < GT_MAX; g++) {
851 maxconsumption[p] += (populationConsumation[p][g] / world._fdaysperyear) * Cities[i].population[p];
855 for(g = 1; g < GT_MAX; g++) {
856 for(p = 1; p < PT_MAX; p++) {
857 if(Cities[i].population[p] && populationConsumation[p][g] > 0.f) {
858 stock = Cities[i].market.stock[g];
859 wantsconsume = (populationConsumation[p][g] / world._fdaysperyear) * Cities[i].population[p];
860 if(stock > wantsconsume) {
861 sell(i, -1, -1, -1, g, wantsconsume, SELL_TO_POPULATION | SELL_FROM_CITY);
862 //Cities[i].market.stock[g] -= wantsconsume;
863 consumed[p] += wantsconsume;
864 } else {
866 consumed[p] = Cities[i].market.stock[g];
867 sell(i, -1, -1, -1, g, consumed[p], SELL_TO_POPULATION | SELL_FROM_CITY);
868 //Cities[i].market.stock[g] = 0;
870 } else {
871 consumed[p] = 0.f;
875 // decrease mood by max 2%, if no wares consumed, and let a random number between 0 and 10% percent leave the city if mood below 10%
876 for(p = 1; p < PT_MAX; p++) {
878 // only do mood adjustement if there ARE some ppl of that type
879 if(Cities[i].population[p]) {
880 percent = maxconsumption[p] / 100;
881 percent = consumed[p] / percent;
882 if(percent > 66.6f)
883 Cities[i].populationMood[p] += (1.0f / 100.0f) * percent;
884 else {
885 Cities[i].populationMood[p] -= 2.0f - ((2.0f / 100.0f) * percent);
886 // notify players with branches...
889 for(pl = 0; pl < numPlayers; pl++) {
890 b = getBranchIDFromCity(i, pl);
891 if((ptrdiff_t) b >= 0)
892 notify(pl, makeNotification(NT_BAD_MOOD, b, 0, 0.f, 0.f));
895 // correct bounds
896 if(Cities[i].populationMood[p] > 100.0f)
897 Cities[i].populationMood[p] = 100.0f;
898 else if(Cities[i].populationMood[p] < 0.0f)
899 Cities[i].populationMood[p] = 0.0f;
902 } else {
903 // if there are no ppl we take the average of the other folk moods, and increase it a bit.
904 wantsconsume = 0.f;
905 for(pl = 1; pl < PT_MAX; pl++) {
906 if(pl != p)
907 wantsconsume += Cities[i].populationMood[pl];
909 Cities[i].populationMood[p] += (wantsconsume / (float) (PT_MAX -2)) + 3.f; // PT_MAX -2 == number of effective population types.
910 if(Cities[i].populationMood[p] > 100.f)
911 Cities[i].populationMood[p] = 100.f;
914 if(Cities[i].population[p] && Cities[i].populationMood[p] < 10.0f) {
915 leaving = Cities[i].population[p] > 20 ? rand() % (Cities[i].population[p] / 10) : 1;
916 if(leaving > Cities[i].population[p])
917 leaving = Cities[i].population[p];
919 leavepercent = leaving / ((float) Cities[i].population[p] / 100.0f);
920 Cities[i].population[p] -= leaving;
922 if(p == PT_WORKER) {
924 for(pl = 0; pl < numPlayers; pl++) {
925 for(b = 0; b < Players[pl].numBranches; b++) {
926 if(Players[pl].branchCity[b] == i && Players[pl].branchWorkers[b]) {
927 leavingp = (size_t) (((float) Players[pl].branchWorkers[b] / 100.0f) * leavepercent) + 0.99f;
928 if (leavingp > leaving)
929 leavingp = leaving;
930 leaving -= leavingp;
931 Players[pl].branchWorkers[b] -= leavingp;
932 if (!leaving) break;
937 } else if (p != PT_WORKER && Cities[i].populationMood[p] > 85.0f) {
938 // mood is good, so they recommend the city to their friends.
939 percent = (float) Cities[i].population[p] / 100.0f;
940 // lets say 2 percent talk to one outside friend each day, from those a random number will join the city.
941 // read "leaving" in this context as "joining"
942 if(!Cities[i].population[p] && p == PT_BEGGAR)
943 percent = (float) Cities[i].population[PT_WORKER] / 100.0f;
944 if(percent > 0.01f)
945 leaving = rand() % (1 + (int) ((2.f * percent) + 0.999f));
946 else
947 leaving = rand() % 2;
949 Cities[i].population[p] += leaving;
952 if (Cities[i].population[PT_BEGGAR] > 0 && Cities[i].populationMood[PT_WORKER] > 60.f) {
953 // see if we have work for beggars...
954 leaving = Cities[i].population[PT_BEGGAR];
955 // we need a list of players with free resources, and their number n
956 // then we pick a random one for n times, and give them a random number of workers from the "pool".
957 // if there are some left, we'll iterate the list and distribute them evenly.
958 getPlayersWithFreeWorkCapacity(i, &totalcapacity, &num_players_affected, affected_players, free_jobs);
959 if(totalcapacity < leaving)
960 leaving = totalcapacity;
962 totalcapacity = leaving; // we use that variable now for reminding how many ppl we have left to distribute.
963 for(pl = 0; pl < num_players_affected; pl++) {
964 leavingp = rand() % totalcapacity;
965 plsave = rand() % num_players_affected;
966 luckyplayer = affected_players[plsave];
967 if(leavingp > free_jobs[plsave])
968 leavingp = free_jobs[plsave];
969 branchid = getBranchIDFromCity(i, luckyplayer);
970 Players[luckyplayer].branchWorkers[branchid] += leavingp;
971 totalcapacity -= leavingp;
972 Cities[i].population[PT_WORKER] += leavingp;
973 Cities[i].population[PT_BEGGAR] -= leavingp;
974 if(!totalcapacity) break;
976 if(totalcapacity) {
977 getPlayersWithFreeWorkCapacity(i, &leavingp, &num_players_affected, affected_players, free_jobs);
978 leaving = totalcapacity / num_players_affected;
979 for(pl = 0; pl < num_players_affected; pl++) {
980 leavingp = leaving;
981 luckyplayer = affected_players[pl];
982 if(leavingp > free_jobs[pl]) {
983 leavingp = free_jobs[pl];
984 totalcapacity -= free_jobs[pl];
985 leaving = totalcapacity / (num_players_affected - (pl + 1));
986 } else
987 totalcapacity -= leavingp;
988 Players[luckyplayer].branchWorkers[branchid] += leavingp;
989 Cities[i].population[PT_WORKER] += leavingp;
990 Cities[i].population[PT_BEGGAR] -= leavingp;
995 for(pl = 0; pl < numPlayers; pl++) {
997 branchid = getBranchIDFromCity(i, pl);
998 if(branchid >= 0) {
999 // now to the factories.
1000 if(Players[pl].branchWorkers[branchid]) {
1001 workersPerFactory = (float) Players[pl].branchWorkers[branchid] / (float) getPlayerFactoryCount(branchid, pl);
1002 percent = workersPerFactory / ((float) factoryProps.maxworkersperfactory / 100.f);
1003 for(f = 0; f < CITY_MAX_INDUSTRYTYPES; f++) {
1004 if(Players[pl].branchFactories[branchid][f]) {
1005 produced = (Factories[Cities[i].industry[f]].yield / 100.f) * percent * Players[pl].branchFactories[branchid][f];
1007 if(externalGoodRequiredForProduction[Cities[i].industry[f]]) {
1008 // check if all required goods are in stock, and reduce produced amount if not
1009 for(g = 1; g < GT_MAX; g++) {
1010 if(Factories[Cities[i].industry[f]].consumation[g] > 0.f) {
1011 required = Factories[Cities[i].industry[f]].consumation[g] * percent * Players[pl].branchFactories[branchid][f];
1012 if(required > 0.f) {
1013 if(Players[pl].branchStock[branchid].stock[g] < required) {
1014 produced = Players[pl].branchStock[branchid].stock[g] / required * produced;
1015 notify(pl, makeNotification(NT_OUT_OF_PRODUCTIONGOODS, branchid, g, required, 0.f));
1020 if(produced > 0.f) {
1021 for(g = 1; g < GT_MAX; g++) {
1022 if(Factories[Cities[i].industry[f]].consumation[g] > 0.f) {
1023 required = (Factories[Cities[i].industry[f]].yield / Factories[Cities[i].industry[f]].consumation[g]) * produced;
1024 Players[pl].branchStock[branchid].stock[g] -= required;
1029 if(produced > 0.f) {
1030 freestorage = getPlayerFreeBranchStorage(branchid, pl);
1031 if(freestorage >= produced)
1032 Players[pl].branchStock[branchid].stock[Cities[i].industry[f]] += produced;
1033 else {
1034 Players[pl].branchStock[branchid].stock[Cities[i].industry[f]] += freestorage;
1035 sell(i, pl, branchid, (size_t) -1, Cities[i].industry[f], produced - freestorage, SELL_FROM_OVERPRODUCTION | SELL_TO_CITY);
1036 notify(pl, makeNotification(NT_STOCK_FULL, branchid, 0, produced - freestorage, 0.f));
1042 // and to the money.
1043 cost = Players[pl].branchWorkers[branchid] * factoryProps.workersalary;
1044 if(Players[pl].money >= cost)
1045 Players[pl].money -= cost;
1046 else {
1047 leaving = Players[pl].branchWorkers[branchid];
1048 produced = (float) Players[pl].money / (float) factoryProps.workersalary;
1049 if(produced >= 1.f) {
1050 leaving -= (size_t) produced;
1052 Players[pl].money = 0;
1053 Players[pl].branchWorkers[branchid] -= leaving;
1054 Cities[i].population[PT_WORKER] -= leaving;
1055 Cities[i].population[PT_BEGGAR] += leaving;
1056 notify(pl, makeNotification(NT_NO_MONEY, 0, 0, 0.f, 0.f));
1061 #ifdef CONSOLE_DEBUG
1062 showDate();
1063 showPlayerStatus(0, 0);
1064 showCityStatus(0);
1065 #endif
1068 size_t buyShips(size_t city, size_t player, size_t shipcount, ShipTypes t) {
1069 size_t singleShipCost = (size_t) ((float) shipProps[t].buildCost * 1.6f);
1070 size_t i;
1071 ptrdiff_t loc = -1;
1072 size_t price;
1073 if(shipcount * singleShipCost > Players[player].money)
1074 shipcount = Players[player].money / singleShipCost;
1075 if(shipcount) {
1076 price = singleShipCost * shipcount;
1077 Players[player].money -= price;
1078 Cities[city].money += price;
1079 Cities[city].shipyard.availableShips.numShips[t] -= shipcount;
1080 for(i = 0; i < Players[player].numShipLocations; i++) {
1081 if(Players[player].shipLocations[i] == city) {
1082 loc = i;
1083 break;
1086 if(loc < 0) {
1087 loc = Players[player].numShipLocations;
1088 Players[player].numShipLocations++;
1089 Players[player].shipLocations[loc] = city;
1091 Players[player].singleShips[loc].numShips[t] += shipcount;
1092 Players[player].singleShips[loc].total += shipcount;
1093 notify(player, makeNotification(NT_SHIPS_BOUGHT, shipcount, (size_t) t, (float) price, (float) Players[player].money));
1095 return shipcount;
1098 void addToConvoy(Convoy* c, size_t shiploc, size_t player, size_t numships, ShipTypes t) {
1099 size_t i;
1100 if(t != ST_NONE) {
1101 if(numships > Players[player].singleShips[shiploc].numShips[t])
1102 numships = Players[player].singleShips[shiploc].numShips[t];
1103 c->shipcounter.numShips[t] += numships;
1104 c->shipcounter.total += numships;
1105 Players[player].singleShips[shiploc].numShips[t] -= numships;
1106 Players[player].singleShips[shiploc].total -= numships;
1107 } else {
1108 // ST_NONE acts as Wildcard here
1109 for (i = 1; i < ST_MAX; i++) {
1110 c->shipcounter.numShips[i] += Players[player].singleShips[shiploc].numShips[i];
1111 c->shipcounter.total += Players[player].singleShips[shiploc].numShips[i];
1112 Players[player].singleShips[shiploc].numShips[i] = 0;
1113 Players[player].singleShips[shiploc].total = 0;
1116 if(!Players[player].singleShips[shiploc].total && Players[player].numShipLocations == 1)
1117 Players[player].numShipLocations = 0;
1120 ptrdiff_t makeConvoy(size_t city, size_t shiploc, size_t player, size_t numships, ShipTypes t) {
1121 Convoy* c;
1122 char buf[16];
1123 stringptr bufptr;
1124 bufptr.ptr = buf;
1125 ptrdiff_t result = -1;
1126 if(Players[player].numConvoys < MAX_CONVOYS) {
1127 c = &Players[player].convoys[Players[player].numConvoys];
1128 memset(c, 0, sizeof(Convoy));
1129 bufptr.size = snprintf(buf, sizeof(buf), "Convoy%.3d", (int) Players[player].numConvoys);
1130 c->name = stringptr_copy(&bufptr);
1131 c->loc = SLT_CITY;
1132 c->locCity = city;
1133 addToConvoy(c, shiploc, player, numships, t);
1135 result = Players[player].numConvoys;
1136 Players[player].numConvoys++;
1138 return result;
1141 size_t getMinCrew(size_t convoy, size_t player) {
1142 size_t i;
1143 size_t result = 0;
1144 if(convoy >= MAX_CONVOYS) return 0;
1145 for(i = 1; i < ST_MAX; i++) {
1146 result += shipProps[i].minCrew * Players[player].convoys[convoy].shipcounter.numShips[i];;
1148 return result;
1151 void hireCrew(size_t convoy, size_t player, size_t numsailors) {
1152 if(convoy >= MAX_CONVOYS) return;
1153 Players[player].convoys[convoy].numSailors += numsailors;
1156 void hireCaptain(size_t convoy, size_t player) {
1157 if(convoy >= MAX_CONVOYS) return;
1158 Players[player].convoys[convoy].captainSalary = (rand() % 30) + 10;
1161 unsigned requireProductionGood(size_t player, size_t branch, Goodtype t) {
1162 size_t i;
1163 if(!goodRequiredForProduction[t]) return 0;
1164 ptrdiff_t city = getCityIDFromBranch(branch, player);
1165 if(city == -1) return 0;
1166 // look if we have factories needing that good.
1167 for(i = 0; i < CITY_MAX_INDUSTRYTYPES; i++) {
1168 if(Players[player].branchFactories[i] && Factories[Cities[city].industry[i]].consumation[t] > 0.0f) {
1169 return 1;
1172 return 0;
1175 void queueSellOrder(size_t player, size_t from_branch, size_t to_city, Goodtype g, float amount) {
1176 size_t c = getCityIDFromBranch(from_branch, player);
1177 if(c == to_city) {
1178 sell(to_city, player, from_branch, -1, g, amount, SELL_TO_CITY | SELL_FROM_STOCK);
1179 } else {
1180 // FIXME add code
1185 int sellBestSellingProductionGoods(size_t player, size_t branch) {
1186 size_t c = getCityIDFromBranch(branch, player);
1187 size_t ct;
1188 size_t i, g;
1189 float bestprofit = 0.f;
1190 ptrdiff_t bestgood = -1, bestcity = -1;
1191 float bestamount = 0.f;
1192 Player* p = &Players[player];
1193 for (i = 0; i < CITY_MAX_INDUSTRYTYPES; i++) {
1194 g = Cities[c].industry[i];
1195 float stock = p->branchStock[branch].stock[g];
1196 if(stock > 0.f) {
1197 for(ct = 0; ct < numCities; ct++) {
1198 float price = calculatePrice(ct, g, stock, 1);
1199 float res = price * stock;
1200 if (res > bestprofit) {
1201 bestprofit = res;
1202 bestgood = g;
1203 bestcity = ct;
1204 bestamount = stock;
1209 if(bestprofit > 0.f) {
1210 queueSellOrder(player, branch, bestcity, bestgood, bestamount);
1211 return 1;
1213 return 0;
1216 void sellWholeStock(size_t player, size_t branch, unsigned skiprequired) {
1217 size_t g;
1218 for (g = 1; g < GT_MAX; g++) {
1219 if (Players[player].branchStock[branch].stock[g] > 0.f) {
1220 if(!skiprequired || !requireProductionGood(player, branch, g))
1221 sell(Players[player].branchCity[branch], player, branch, -1, g, Players[player].branchStock[branch].stock[g], SELL_TO_CITY | SELL_FROM_STOCK);
1226 unsigned convoysAvailable(size_t player, size_t branch) {
1227 size_t i;
1228 size_t city;
1229 city = getCityIDFromBranch(branch, player);
1230 for(i = 0; i < Players[player].numConvoys; i++) {
1231 if(Players[player].convoys[i].loc == SLT_CITY && Players[player].convoys[i].locCity == city)
1232 return 1;
1234 return 0;
1237 float calculateDistance(Coords* a, Coords* b) {
1238 // TODO do proper pathfinding
1239 float x = a->x - b->x;
1240 float y = a->y - b->y;
1241 return (float) sqrtf((x * x) + (y * y));
1244 ptrdiff_t findNearestCityWithGood(size_t city, Goodtype neededgood) {
1245 float mindistance = 1000000000000.f;
1246 float distance;
1247 size_t g;
1248 for(g = 0; g < producerCount[neededgood]; g++) {
1249 distance = calculateDistance(&Cities[city].coords, &Cities[producers[neededgood][g]].coords);
1250 if(distance < mindistance)
1251 mindistance = distance;
1253 for(g = 0; g < producerCount[neededgood]; g++) {
1254 distance = calculateDistance(&Cities[city].coords, &Cities[producers[neededgood][g]].coords);
1255 if(distance == mindistance) {
1256 return producers[neededgood][g];
1259 return -1;
1262 void embark(Convoy* convoy, size_t to_city) {
1263 convoy->fromCity = convoy->locCity;
1264 assert(convoy->locCity != to_city);
1265 convoy->loc = SLT_SEA;
1266 convoy->locCity = to_city;
1267 convoy->coords = Cities[convoy->fromCity].coords;
1268 convoy->stepsdone = 0;
1271 void land(Convoy* convoy) {
1272 convoy->loc = SLT_CITY;
1273 convoy->coords = Cities[convoy->locCity].coords;
1276 float getConvoyMaxStorage(Convoy* c) {
1277 size_t s;
1278 float result = 0.f;
1279 for(s = 1; s < ST_MAX; s++) {
1280 result += shipProps[s].maxload * c->shipcounter.numShips[s];
1282 return result;
1285 // blindly moves the goods from/to a market to a convoy. no checks of any sort.
1286 void moveGoodsConvoy(Convoy* c, Market* m, Goodtype g, float amount, unsigned fromConvoy) {
1287 if (fromConvoy)
1288 amount = -amount;
1290 c->load.stock[g] += amount;
1291 c->totalload += amount;
1292 m->stock[g] -= amount;
1295 float getSaneAmount(size_t player, size_t city, float amount, Goodtype g) {
1296 float price = calculatePrice(city, g, amount, 1);
1297 if(amount * price > Players[player].money) amount = Players[player].money / price;
1298 while(amount > 2.f && (price = calculatePrice(city, g, amount, 1)) &&
1299 ((long long) (price * amount) > Players[player].money
1300 || price > (goodcat_minprice[Goods[g].cat] * 2)
1302 ) amount /= 2.f;
1303 if(amount * price > Players[player].money) amount = 0.f;
1304 return amount;
1307 void purchaseFactories(size_t player, size_t city, Goodtype g, size_t amount) {
1308 long long price = 0;
1309 size_t industry_id = 0;
1310 ptrdiff_t branch_id = -1;
1311 size_t i;
1313 // check if the city can produce the good...
1314 for(i = 0; i < CITY_MAX_INDUSTRYTYPES; i++) {
1315 if(Cities[city].industry[i] == g) {
1316 price = 1;
1317 break;
1320 if(!industry_id)
1321 return;
1322 // check if the player has a branch there...
1323 for (i=0; i < Players[player].numBranches; i++) {
1324 if(Players[player].branchCity[i] == city) {
1325 branch_id = i;
1326 break;
1329 if(branch_id == -1)
1330 return;
1332 while ((price = (long long) (Factories[i].buildcost * amount)) && price > Players[player].money)
1333 if(amount == 1)
1334 amount = 0;
1335 else amount = amount / 2;
1337 Players[player].money -= price;
1338 Players[player].branchFactories[branch_id][industry_id] += amount;
1341 unsigned canProduce(size_t city, Goodtype g) {
1342 size_t i;
1343 for (i = 0; i < CITY_MAX_INDUSTRYTYPES; i++) {
1344 if(Cities[city].industry[i] == g)
1345 return 1;
1347 return 0;
1350 void aiThink(void) {
1351 size_t p;
1352 size_t b, c;
1353 size_t g, x;
1354 size_t bought;
1355 ptrdiff_t city, shiploc;
1356 ptrdiff_t new_convoy;
1357 Notification* last;
1358 float freestorage = 0.f;
1359 size_t neededgood = GT_NONE;
1360 ptrdiff_t nearestcity;
1361 ptrdiff_t branchOfInterest;
1362 unsigned jumpedhere;
1363 size_t here;
1364 Player* dude;
1365 Convoy* convoy;
1366 unsigned jobdone;
1368 for(p = 0; p < numPlayers; p++) {
1369 dude = &Players[p];
1371 if(dude->type == PLT_CPU) {
1373 // go through notifications and setup a plan accordingly, or act directy.
1374 while((last = &dude->inbox.notifications[dude->inbox.last]) && last->nt != NT_NONE) {
1375 switch(last->nt) {
1376 case NT_NO_MONEY:
1377 for (b = 0; b < dude->numBranches; b++) {
1378 sellWholeStock(p, b, 0);
1380 break;
1381 case NT_BAD_MOOD:
1382 b = last->val1;
1383 if(!(dude->plan & AIP_SATISFY_MOODS)) {
1384 if(dude->numConvoys == 0) {
1385 // cpu needs at least one convoy to organise goods
1386 bought = 0;
1387 for(g = 1; g < ST_MAX; g++) {
1388 if(Cities[dude->branchCity[b]].shipyard.availableShips.numShips[g])
1389 bought += buyShips(dude->branchCity[b], p, Cities[dude->branchCity[b]].shipyard.availableShips.numShips[g], g);
1391 if(bought) {
1392 city = getCityIDFromBranch(b, p);
1393 assert(city != -1);
1394 shiploc = getShipLocationIDFromCity(city, p);
1395 assert(shiploc != -1);
1396 new_convoy = makeConvoy(city, shiploc, p, bought, ST_NONE);
1397 hireCrew(new_convoy, p, getMinCrew(new_convoy, p));
1398 hireCaptain(new_convoy, p);
1401 dude->plan |= AIP_SATISFY_MOODS;
1402 dude->plandata.moodybranch = b; // if theres more than one moody branch, the others have to wait...
1404 break;
1405 case NT_OUT_OF_PRODUCTIONGOODS:
1406 dude->plan |= AIP_PURCHASE_PRODUCTION_GOODS;
1407 dude->plandata.branch_req_goods = last->val1;
1408 dude->plandata.req_production_good = last->val2;
1409 dude->plandata.amount_required = last->fval1;
1410 break;
1411 case NT_STOCK_FULL:
1412 b = last->val1;
1413 if(!convoysAvailable(p, b))
1414 sellWholeStock(p, b, 1);
1415 else
1416 dude->plan |= AIP_SELL_GOODS_FROM_STOCK;
1417 break;
1418 default:
1419 break;
1422 *last = emptyNotification;
1423 if(!dude->inbox.last)
1424 dude->inbox.last = MAX_NOTIFICATIONS;
1425 dude->inbox.last--;
1428 if(dude->plan & AIP_SATISFY_MOODS) {
1429 // easy route: see if we got the stuff in our own stock and sell it to the city.
1430 jobdone = 0;
1431 for(g = 1; g < GT_MAX; g++) {
1432 if(Cities[dude->branchCity[dude->plandata.moodybranch]].market.stock[g] < 1.f) {
1433 if(dude->branchStock[dude->plandata.moodybranch].stock[g] > 0.f) {
1434 sell(dude->branchCity[dude->plandata.moodybranch], p, dude->plandata.moodybranch, -1, g, dude->branchStock[dude->plandata.moodybranch].stock[g], SELL_FROM_STOCK | SELL_TO_CITY);
1435 jobdone = 1;
1436 } else if (canProduce(dude->branchCity[dude->plandata.moodybranch], g)) {
1437 purchaseFactories(p, dude->branchCity[dude->plandata.moodybranch], g, 1);
1441 if(jobdone) {
1442 dude->plan = dude->plan & ~AIP_SATISFY_MOODS;
1446 // iterate through convoys, if they're in a branch city put load into stock, otherwise sell to city and try to purchase required goods.
1447 for(c = 0; c < dude->numConvoys; c++) {
1449 convoy = &dude->convoys[c];
1451 if(convoy->loc == SLT_SEA)
1452 continue;
1454 here = convoy->locCity; // location of the convoi we're dealing with.
1456 if(dude->numShipLocations) {
1457 for(x = 0; x < dude->numShipLocations; x++) {
1458 if(dude->singleShips[x].total && dude->shipLocations[x] == here)
1459 addToConvoy(convoy, x, p, dude->singleShips[x].total, ST_NONE);
1463 jumpedhere = 0;
1464 // check if the convoy is in a branch of ours
1465 for(b = 0; b < dude->numBranches; b++) {
1466 if(here == dude->branchCity[b]) {
1467 if(convoy->totalload > 0.f) {
1468 // try to put load into warehouse, or sell to city if no space.
1469 for(g = 1; g < GT_MAX; g++) {
1470 if(convoy->load.stock[g] > 0.f) {
1471 freestorage = getPlayerFreeBranchStorage(b, p);
1472 if((dude->plan & AIP_SATISFY_MOODS) && b == dude->plandata.moodybranch) {
1473 // sell everything to city.
1474 freestorage = 0.f;
1475 dude->plan = dude->plan & ~ AIP_SATISFY_MOODS;
1476 } else {
1477 if(freestorage < convoy->load.stock[g]) {
1478 sellWholeStock(p, b, 1);
1479 freestorage = getPlayerFreeBranchStorage(b, p);
1481 if(freestorage > convoy->load.stock[g])
1482 freestorage = convoy->load.stock[g];
1484 convoy->load.stock[g] -= freestorage;
1485 convoy->totalload -= freestorage;
1486 dude->branchStock[b].stock[g] += freestorage;
1487 if(convoy->load.stock[g] > 0.f)
1488 sell(dude->branchCity[b], p, b, c, g, convoy->load.stock[g], SELL_FROM_CONVOY | SELL_TO_CITY);
1490 if((dude->plan & AIP_PURCHASE_PRODUCTION_GOODS) && g == dude->plandata.req_production_good && b == dude->plandata.branch_req_goods)
1491 dude->plan = dude->plan & ~ AIP_PURCHASE_PRODUCTION_GOODS;
1496 // now that the load is cleared, use the convoy for our plans...
1497 // in the order of importance.
1498 // #TAG1
1500 jump:
1502 branchOfInterest = -1;
1503 if(dude->plan & AIP_SATISFY_MOODS) {
1504 //check which good we need
1505 neededgood = GT_NONE;
1506 for(g = 1; g < GT_MAX; g++) {
1507 if(Cities[dude->branchCity[dude->plandata.moodybranch]].market.stock[g] < 1.f) {
1508 neededgood = g;
1509 break;
1512 if(neededgood == GT_NONE) {
1513 dude->plan = dude->plan & ~AIP_SATISFY_MOODS;
1514 } else {
1515 branchOfInterest = dude->plandata.moodybranch;
1518 if(dude->plan & AIP_SATISFY_MOODS || dude->plan & AIP_PURCHASE_PRODUCTION_GOODS) {
1519 if(branchOfInterest == -1) {
1520 branchOfInterest = dude->plandata.branch_req_goods;
1521 neededgood = dude->plandata.req_production_good;
1524 //calculate daily required amount for production, and if convoy maxload is below that, buy additional ships
1525 if ((dude->plan & AIP_PURCHASE_PRODUCTION_GOODS) && getConvoyMaxStorage(convoy) < dude->plandata.amount_required)
1526 buyShips(here, p, 1, ST_A);
1528 if (jumpedhere) goto jump2;
1530 // the branch were the convoy is the branch which needs stuff.
1531 if(b == (size_t) branchOfInterest) {
1532 // choose a city which produces the required good, and send convoy there.
1533 if(producerCount[neededgood] == 0 || rand() % 2)
1534 nearestcity = findNearestCityWithGood(here, neededgood);
1535 else
1536 nearestcity = producers[neededgood][rand() % producerCount[neededgood]];
1537 if(nearestcity == -1) {
1538 fprintf(stderr, "warning, there's no supplier for good %d", (int) neededgood);
1539 return;
1540 } else if ((size_t) nearestcity == dude->branchCity[branchOfInterest]) {
1541 // it's not useful to send a ship to where it already is...
1542 for(x = 0; x < producerCount[neededgood]; x++) {
1543 if(producers[neededgood][x] != (size_t) nearestcity) {
1544 nearestcity = producers[neededgood][x];
1545 break;
1548 if ((size_t) nearestcity == dude->branchCity[branchOfInterest]) {
1549 // only city producing the good...so lets increase production
1550 purchaseFactories(p, dude->branchCity[branchOfInterest], neededgood, 1);
1551 dude->plan = dude->plan & ~ AIP_PURCHASE_PRODUCTION_GOODS;
1552 return;
1555 // plan to take regional wares with us - unfortunately that gives us soon moods
1557 freestorage = getConvoyMaxStorage(convoy) - convoy->totalload;
1558 if(freestorage > 0.f) {
1559 for(g = 0; g < CITY_MAX_INDUSTRYTYPES; g++) {
1560 if(dude->branchStock[b].stock[Cities[dude->branchCity[b]].industry[g]] > 0.f)
1561 moveGoodsConvoy(convoy, &dude->branchStock[b], Cities[dude->branchCity[b]].industry[g], freestorage / (float) CITY_MAX_INDUSTRYTYPES, 0);
1563 freestorage = getConvoyMaxStorage(convoy) - convoy->totalload;
1564 if(freestorage > 0.f) {
1565 // see if we can take a regional good with us...
1566 if(Cities[nearestcity].industry[CITY_MAX_INDUSTRYTYPES -1] != Cities[dude->branchCity[b]].industry[CITY_MAX_INDUSTRYTYPES -1]
1567 && Cities[dude->branchCity[b]].market.stock[CITY_MAX_INDUSTRYTYPES -1] > 0.f
1570 freestorage = getSaneAmount(p, dude->branchCity[b], freestorage, Cities[dude->branchCity[b]].industry[CITY_MAX_INDUSTRYTYPES -1]);
1571 sell(dude->branchCity[b], p, b, c, Cities[dude->branchCity[b]].industry[CITY_MAX_INDUSTRYTYPES -1], freestorage, SELL_FROM_CITY | SELL_TO_CONVOY);
1576 embark(convoy, nearestcity);
1577 } else {
1578 // see if our branch here or the city has the good we want,
1580 if(dude->branchStock[b].stock[neededgood] > 0.f) {
1581 if(dude->branchStock[b].stock[neededgood] < freestorage)
1582 freestorage = dude->branchStock[b].stock[neededgood];
1583 else
1584 freestorage = getConvoyMaxStorage(convoy) - convoy->totalload;
1586 moveGoodsConvoy(convoy, &dude->branchStock[b], neededgood, freestorage, 0);
1589 jump2:
1591 if(dude->money) {
1592 freestorage = getConvoyMaxStorage(convoy) - convoy->totalload;
1593 if(freestorage > 0.f && Cities[here].market.stock[neededgood] > 0.f) {
1594 if(Cities[here].market.stock[neededgood] < freestorage)
1595 freestorage = Cities[here].market.stock[neededgood];
1597 freestorage = getSaneAmount(p, here, freestorage, neededgood);
1598 // FIXME this loop might impact performance. also we should check if the price is much higher than the minprice.
1599 //while(freestorage >= 1.f && (price = calculatePrice(here, neededgood, freestorage, 1)) && (price * freestorage > dude->money)) freestorage /= 2.f;
1600 sell(here, p, b, c, neededgood, freestorage, SELL_FROM_CITY | SELL_TO_CONVOY);
1602 embark(convoy, dude->branchCity[branchOfInterest]);
1605 } else {
1606 // no specific plan, buy a random good and gtfo
1607 // TODO
1611 break;
1615 if(jumpedhere) // remove once the TODO 5 lines above is removed
1616 continue;
1618 // if the convoy was in a branch of ours, we certainly sent it back to sea
1619 if(convoy->loc == SLT_SEA)
1620 continue;
1621 // so we'll get here only if it is in a city without a branch.
1622 // means we sell our load, buy something useful and be gone.
1623 if(convoy->totalload > 0.f) {
1624 for(g = 1; g < GT_MAX; g++) {
1625 if(convoy->load.stock[g] > 0.f)
1626 sell(here, p, -1, c, g, convoy->load.stock[g], SELL_FROM_CONVOY | SELL_TO_CITY);
1629 jumpedhere = 1;
1630 goto jump;
1636 ShipTypes getSlowestShip(Convoy* convoy) {
1637 size_t slowest = 10000000;
1638 size_t s;
1639 ShipTypes result = ST_NONE;
1640 for(s = 1; s < ST_MAX; s++) {
1641 if(convoy->shipcounter.numShips[s] && shipProps[s].speed < slowest) {
1642 slowest = shipProps[s].speed;
1643 result = s;
1646 return result;
1648 FILE* dbg;
1649 void newSec(void) {
1651 size_t p, c, s;
1652 Player* player;
1653 Convoy* convoy;
1654 float milesperminute[ST_MAX];
1656 Coords diff, dir;
1657 ShipTypes slowestship;
1658 float dist;
1659 float stepsneeded;
1661 world.date += GAME_SPEED + (world.date % GAME_SPEED);
1663 if(world.date % world.secondsperminute == 0) {
1664 for(s = 1; s < ST_MAX; s++) {
1665 milesperminute[s] = ((float) shipProps[s].speed) / ((float) world.minutesperhour);
1667 // calculate position of ships
1668 for(p = 0; p < numPlayers; p++) {
1669 player = &Players[p];
1670 for(c = 0; c < player->numConvoys; c++) {
1671 convoy = &player->convoys[c];
1672 if(convoy->loc == SLT_SEA) {
1673 slowestship = getSlowestShip(convoy);
1674 if(shipProps[slowestship].speed != 10)
1675 fprintf(dbg, "slowest: %d, speed: %d\n", slowestship, (int) shipProps[slowestship].speed);
1676 dist = calculateDistance(&Cities[convoy->fromCity].coords, &Cities[convoy->locCity].coords);
1677 fprintf(dbg, "dist from %s to %s: %f\n", Cities[convoy->fromCity].name->ptr, Cities[convoy->locCity].name->ptr, dist);
1678 stepsneeded = dist / milesperminute[slowestship];
1679 dir.x = Cities[convoy->fromCity].coords.x - Cities[convoy->locCity].coords.x;
1680 dir.y = Cities[convoy->fromCity].coords.y - Cities[convoy->locCity].coords.y;
1681 diff.x = dir.x / (dist / milesperminute[slowestship]); // movement on x axis per turn
1682 diff.y = dir.y / (dist / milesperminute[slowestship]); // -"- y
1683 convoy->stepsdone++;
1684 convoy->condition -= 0.01;
1685 if (convoy->stepsdone >= stepsneeded) {
1686 land(convoy);
1687 } else {
1688 convoy->coords.x = Cities[convoy->fromCity].coords.x - convoy->stepsdone * diff.x;
1689 convoy->coords.y = Cities[convoy->fromCity].coords.y - convoy->stepsdone * diff.y;
1696 if(world.date % world._dayseconds == 0) {
1697 newDay();
1700 if(world.date % (world.secondsperminute * (world.minutesperhour / 4)) == 0)
1701 aiThink(); // let the ai think only every 15 minutes... so that we can see its ships at the coast...
1705 int microsleep(long microsecs) {
1706 struct timespec req, rem;
1707 req.tv_sec = microsecs / 1000000;
1708 req.tv_nsec = (microsecs % 1000000) * 1000;
1709 int ret;
1710 while((ret = nanosleep(&req, &rem)) == -1 && errno == EINTR) req = rem;
1711 return ret;
1714 int main(int argc, char** argv) {
1715 (void) argc;
1716 (void) argv;
1717 Gui gui_buf;
1718 gui = &gui_buf;
1720 dbg = fopen("debug.pato", "w");
1722 srand(time(NULL));
1723 initWorld();
1724 initConsumationTable();
1725 initCities();
1726 initBuildings();
1727 initPlayers();
1728 gui_init(gui);
1729 while(1) {
1730 // lets cheat... to speed up
1731 //world.date += world.secondsperminute - 1;
1732 newSec();
1733 microsleep(1000);
1734 if(gui_processInput(gui) == -1) break;
1735 if(world.date % (world.secondsperminute * GAME_SPEED) == 0)
1736 gui_repaint(gui);
1738 gui_free(gui);
1739 freeCities();
1740 freePlayers();
1741 return 0;