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