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