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