1 #include "city.h"
2 
3 #include "core/calc.h"
4 #include "city/buildings.h"
5 #include "city/finance.h"
6 #include "city/map.h"
7 #include "city/message.h"
8 #include "city/trade.h"
9 #include "city/resource.h"
10 #include "empire/object.h"
11 #include "empire/trade_route.h"
12 #include "empire/type.h"
13 #include "figuretype/trader.h"
14 #include "scenario/map.h"
15 
16 #include <string.h>
17 
18 #define MAX_CITIES 41
19 #define RESOURCES_TO_TRADER_RATIO 60
20 
21 static empire_city cities[MAX_CITIES];
22 
empire_city_clear_all(void)23 void empire_city_clear_all(void)
24 {
25     memset(cities, 0, sizeof(cities));
26 }
27 
empire_city_get(int city_id)28 empire_city *empire_city_get(int city_id)
29 {
30     if (city_id >= 0 && city_id < MAX_CITIES) {
31         return &cities[city_id];
32     } else {
33         return 0;
34     }
35 }
36 
empire_city_get_route_id(int city_id)37 int empire_city_get_route_id(int city_id)
38 {
39     return cities[city_id].route_id;
40 }
41 
empire_can_import_resource(int resource)42 int empire_can_import_resource(int resource)
43 {
44     for (int i = 0; i < MAX_CITIES; i++) {
45         if (cities[i].in_use &&
46             cities[i].type == EMPIRE_CITY_TRADE &&
47             cities[i].is_open &&
48             cities[i].sells_resource[resource] == 1) {
49             return 1;
50         }
51     }
52     return 0;
53 }
54 
empire_can_import_resource_potentially(int resource)55 int empire_can_import_resource_potentially(int resource)
56 {
57     for (int i = 0; i < MAX_CITIES; i++) {
58         if (cities[i].in_use &&
59             cities[i].type == EMPIRE_CITY_TRADE &&
60             cities[i].sells_resource[resource] == 1) {
61             return 1;
62         }
63     }
64     return 0;
65 }
66 
empire_has_access_to_resource(int resource)67 int empire_has_access_to_resource(int resource)
68 {
69     for (int i = 0; i < MAX_CITIES; i++) {
70         if (cities[i].in_use &&
71             (cities[i].type == EMPIRE_CITY_OURS || cities[i].type == EMPIRE_CITY_TRADE) &&
72             cities[i].sells_resource[resource] == 1) {
73             return 1;
74         }
75     }
76     return 0;
77 }
78 
empire_can_export_resource_potentially(int resource)79 int empire_can_export_resource_potentially(int resource)
80 {
81     for (int i = 0; i < MAX_CITIES; i++) {
82         if (cities[i].in_use &&
83             cities[i].type == EMPIRE_CITY_TRADE &&
84             cities[i].buys_resource[resource] == 1) {
85             return 1;
86         }
87     }
88     return 0;
89 }
90 
empire_can_export_resource(int resource)91 int empire_can_export_resource(int resource)
92 {
93     for (int i = 0; i < MAX_CITIES; i++) {
94         if (cities[i].in_use &&
95             cities[i].type == EMPIRE_CITY_TRADE &&
96             cities[i].is_open &&
97             cities[i].buys_resource[resource] == 1) {
98             return 1;
99         }
100     }
101     return 0;
102 }
103 
can_produce_resource(int resource)104 int can_produce_resource(int resource)
105 {
106     for (int i = 0; i < MAX_CITIES; i++) {
107         if (cities[i].in_use &&
108             cities[i].type == EMPIRE_CITY_OURS &&
109             cities[i].sells_resource[resource] == 1) {
110             return 1;
111         }
112     }
113     return 0;
114 }
115 
get_raw_resource(int resource)116 static int get_raw_resource(int resource)
117 {
118     switch (resource) {
119         case RESOURCE_POTTERY:
120             return RESOURCE_CLAY;
121         case RESOURCE_FURNITURE:
122             return RESOURCE_TIMBER;
123         case RESOURCE_OIL:
124             return RESOURCE_OLIVES;
125         case RESOURCE_WINE:
126             return RESOURCE_VINES;
127         case RESOURCE_WEAPONS:
128             return RESOURCE_IRON;
129         default:
130             return resource;
131     }
132 }
133 
empire_can_produce_resource(int resource)134 int empire_can_produce_resource(int resource)
135 {
136     int raw_resource = get_raw_resource(resource);
137     // finished goods: check imports of raw materials
138     if (raw_resource != resource && empire_can_import_resource(raw_resource)) {
139         return 1;
140     }
141     // check if we can produce the raw materials
142     return can_produce_resource(raw_resource);
143 }
144 
empire_can_produce_resource_potentially(int resource)145 int empire_can_produce_resource_potentially(int resource)
146 {
147     int raw_resource = get_raw_resource(resource);
148     // finished goods: check imports of raw materials
149     if (raw_resource != resource && empire_can_import_resource_potentially(raw_resource)) {
150         return 1;
151     }
152     // check if we can produce the raw materials
153     return can_produce_resource(raw_resource);
154 }
155 
empire_city_get_for_object(int empire_object_id)156 int empire_city_get_for_object(int empire_object_id)
157 {
158     for (int i = 0; i < MAX_CITIES; i++) {
159         if (cities[i].in_use && cities[i].empire_object_id == empire_object_id) {
160             return i;
161         }
162     }
163     return 0;
164 }
165 
empire_city_get_for_trade_route(int route_id)166 int empire_city_get_for_trade_route(int route_id)
167 {
168     for (int i = 0; i < MAX_CITIES; i++) {
169         if (cities[i].in_use && cities[i].route_id == route_id) {
170             return i;
171         }
172     }
173     return -1;
174 }
175 
empire_city_is_trade_route_open(int route_id)176 int empire_city_is_trade_route_open(int route_id)
177 {
178     for (int i = 0; i < MAX_CITIES; i++) {
179         if (cities[i].in_use && cities[i].route_id == route_id) {
180             return cities[i].is_open ? 1 : 0;
181         }
182     }
183     return 0;
184 }
185 
empire_city_reset_yearly_trade_amounts(void)186 void empire_city_reset_yearly_trade_amounts(void)
187 {
188     for (int i = 0; i < MAX_CITIES; i++) {
189         if (cities[i].in_use && cities[i].is_open) {
190             trade_route_reset_traded(cities[i].route_id);
191         }
192     }
193 }
194 
empire_city_count_wine_sources(void)195 int empire_city_count_wine_sources(void)
196 {
197     int sources = 0;
198     for (int i = 1; i < MAX_CITIES; i++) {
199         if (cities[i].in_use &&
200             cities[i].is_open &&
201             cities[i].sells_resource[RESOURCE_WINE]) {
202             sources++;
203         }
204     }
205     return sources;
206 }
207 
empire_city_get_vulnerable_roman(void)208 int empire_city_get_vulnerable_roman(void)
209 {
210     int city = 0;
211     for (int i = 0; i < MAX_CITIES; i++) {
212         if (cities[i].in_use) {
213             if (cities[i].type == EMPIRE_CITY_VULNERABLE_ROMAN) {
214                 city = i;
215             }
216         }
217     }
218     return city;
219 }
220 
empire_city_expand_empire(void)221 void empire_city_expand_empire(void)
222 {
223     for (int i = 0; i < MAX_CITIES; i++) {
224         if (!cities[i].in_use) {
225             continue;
226         }
227         if (cities[i].type == EMPIRE_CITY_FUTURE_TRADE) {
228             cities[i].type = EMPIRE_CITY_TRADE;
229         } else if (cities[i].type == EMPIRE_CITY_FUTURE_ROMAN) {
230             cities[i].type = EMPIRE_CITY_DISTANT_ROMAN;
231         } else {
232             continue;
233         }
234         empire_object_set_expanded(cities[i].empire_object_id, cities[i].type);
235     }
236 }
237 
238 // Override hardcoded empire data to allow new trade
empire_city_force_sell(int route,int resource)239 void empire_city_force_sell(int route, int resource)
240 {
241     for (int i = 0; i < MAX_CITIES; i++) {
242         if (cities[i].route_id == route) {
243             cities[i].sells_resource[resource] = 1;
244             empire_object_city_force_sell_resource(cities[i].empire_object_id, resource);
245             break;
246         }
247     }
248 }
249 
generate_trader(int city_id,empire_city * city)250 static int generate_trader(int city_id, empire_city *city)
251 {
252     int trade_potential = 0;
253     for (int r = RESOURCE_MIN; r < RESOURCE_MAX; r++) {
254         if (city->buys_resource[r] || city->sells_resource[r]) {
255             trade_potential += trade_route_limit(city->route_id, r);
256         }
257     }
258     if (trade_potential <= 0) {
259         return 0;
260     }
261     int max_traders = calc_bound(trade_potential / RESOURCES_TO_TRADER_RATIO + 1, 1, 3);
262     int index;
263     if (max_traders == 1) {
264         if (!city->trader_figure_ids[0]) {
265             index = 0;
266         } else {
267             return 0;
268         }
269     } else if (max_traders == 2) {
270         if (!city->trader_figure_ids[0]) {
271             index = 0;
272         } else if (!city->trader_figure_ids[1]) {
273             index = 1;
274         } else {
275             return 0;
276         }
277     } else { // 3
278         if (!city->trader_figure_ids[0]) {
279             index = 0;
280         } else if (!city->trader_figure_ids[1]) {
281             index = 1;
282         } else if (!city->trader_figure_ids[2]) {
283             index = 2;
284         } else {
285             return 0;
286         }
287     }
288 
289     if (city->trader_entry_delay > 0) {
290         city->trader_entry_delay--;
291         return 0;
292     }
293     city->trader_entry_delay = city->is_sea_trade ? 30 : 4;
294 
295     if (city->is_sea_trade) {
296         // generate ship
297         if (city_buildings_has_working_dock() && scenario_map_has_river_entry()
298             && !city_trade_has_sea_trade_problems()) {
299             map_point river_entry = scenario_map_river_entry();
300             city->trader_figure_ids[index] = figure_create_trade_ship(river_entry.x, river_entry.y, city_id);
301             return 1;
302         }
303     } else {
304         // generate caravan and donkeys
305         if (!city_trade_has_land_trade_problems()) {
306             // caravan head
307             const map_tile *entry = city_map_entry_point();
308             city->trader_figure_ids[index] = figure_create_trade_caravan(entry->x, entry->y, city_id);
309             return 1;
310         }
311     }
312     return 0;
313 }
314 
empire_city_open_trade(int city_id)315 void empire_city_open_trade(int city_id)
316 {
317     empire_city *city = &cities[city_id];
318     city_finance_process_construction(city->cost_to_open);
319     city->is_open = 1;
320 }
321 
empire_city_generate_trader(void)322 void empire_city_generate_trader(void)
323 {
324     for (int i = 1; i < MAX_CITIES; i++) {
325         if (!cities[i].in_use || !cities[i].is_open) {
326             continue;
327         }
328         if (cities[i].is_sea_trade) {
329             if (!city_buildings_has_working_dock()) {
330                 // delay of 384 = 1 year
331                 city_message_post_with_message_delay(MESSAGE_CAT_NO_WORKING_DOCK, 1, MESSAGE_NO_WORKING_DOCK, 384);
332                 continue;
333             }
334             if (!scenario_map_has_river_entry()) {
335                 continue;
336             }
337             city_trade_add_sea_trade_route();
338         } else {
339             city_trade_add_land_trade_route();
340         }
341         if (generate_trader(i, &cities[i])) {
342             break;
343         }
344     }
345 }
346 
empire_city_remove_trader(int city_id,int figure_id)347 void empire_city_remove_trader(int city_id, int figure_id)
348 {
349     for (int i = 0; i < 3; i++) {
350         if (cities[city_id].trader_figure_ids[i] == figure_id) {
351             cities[city_id].trader_figure_ids[i] = 0;
352         }
353     }
354 }
355 
empire_city_set_vulnerable(int city_id)356 void empire_city_set_vulnerable(int city_id)
357 {
358     cities[city_id].type = EMPIRE_CITY_VULNERABLE_ROMAN;
359 }
360 
empire_city_set_foreign(int city_id)361 void empire_city_set_foreign(int city_id)
362 {
363     cities[city_id].type = EMPIRE_CITY_DISTANT_FOREIGN;
364 }
365 
empire_unlock_all_resources(void)366 int empire_unlock_all_resources(void)
367 {
368 	for (int i = 0; i < MAX_CITIES; i++) {
369         if (cities[i].in_use && (cities[i].type == EMPIRE_CITY_OURS)) {
370             for (int resource = RESOURCE_MIN; resource < RESOURCE_MAX; resource++) {
371                 cities[i].sells_resource[resource] = 1;
372             }
373             return 1;
374         }
375 	}
376 	return 0;
377 }
378 
empire_city_save_state(buffer * buf)379 void empire_city_save_state(buffer *buf)
380 {
381     for (int i = 0; i < MAX_CITIES; i++) {
382         empire_city *city = &cities[i];
383         buffer_write_u8(buf, city->in_use);
384         buffer_write_u8(buf, 0);
385         buffer_write_u8(buf, city->type);
386         buffer_write_u8(buf, city->name_id);
387         buffer_write_u8(buf, city->route_id);
388         buffer_write_u8(buf, city->is_open);
389         for (int r = 0; r < RESOURCE_MAX; r++) {
390             buffer_write_u8(buf, city->buys_resource[r]);
391         }
392         for (int r = 0; r < RESOURCE_MAX; r++) {
393             buffer_write_u8(buf, city->sells_resource[r]);
394         }
395         buffer_write_i16(buf, city->cost_to_open);
396         buffer_skip(buf, 2);
397         buffer_write_i16(buf, city->trader_entry_delay);
398         buffer_write_i16(buf, 0);
399         buffer_write_i16(buf, city->empire_object_id);
400         buffer_write_u8(buf, city->is_sea_trade);
401         buffer_write_u8(buf, 0);
402         for (int f = 0; f < 3; f++) {
403             buffer_write_i16(buf, city->trader_figure_ids[f]);
404         }
405         for (int p = 0; p < 10; p++) {
406             buffer_write_u8(buf, 0);
407         }
408     }
409 }
410 
empire_city_load_state(buffer * buf)411 void empire_city_load_state(buffer *buf)
412 {
413     for (int i = 0; i < MAX_CITIES; i++) {
414         empire_city *city = &cities[i];
415         city->in_use = buffer_read_u8(buf);
416         buffer_skip(buf, 1);
417         city->type = buffer_read_u8(buf);
418         city->name_id = buffer_read_u8(buf);
419         city->route_id = buffer_read_u8(buf);
420         city->is_open = buffer_read_u8(buf);
421         for (int r = 0; r < RESOURCE_MAX; r++) {
422             city->buys_resource[r] = buffer_read_u8(buf);
423         }
424         for (int r = 0; r < RESOURCE_MAX; r++) {
425             city->sells_resource[r] = buffer_read_u8(buf);
426         }
427         city->cost_to_open = buffer_read_i16(buf);
428         buffer_skip(buf, 2);
429         city->trader_entry_delay = buffer_read_i16(buf);
430         buffer_skip(buf, 2);
431         city->empire_object_id = buffer_read_i16(buf);
432         city->is_sea_trade = buffer_read_u8(buf);
433         buffer_skip(buf, 1);
434         for (int f = 0; f < 3; f++) {
435             city->trader_figure_ids[f] = buffer_read_i16(buf);
436         }
437         buffer_skip(buf, 10);
438     }
439 }
440