1 #include "docker.h"
2
3 #include "building/building.h"
4 #include "building/storage.h"
5 #include "building/warehouse.h"
6 #include "city/buildings.h"
7 #include "city/trade.h"
8 #include "core/calc.h"
9 #include "core/image.h"
10 #include "empire/city.h"
11 #include "empire/empire.h"
12 #include "empire/trade_route.h"
13 #include "figure/combat.h"
14 #include "figure/image.h"
15 #include "figure/movement.h"
16 #include "figure/route.h"
17 #include "figure/trader.h"
18 #include "map/road_access.h"
19
try_import_resource(int building_id,int resource,int city_id)20 static int try_import_resource(int building_id, int resource, int city_id)
21 {
22 building *warehouse = building_get(building_id);
23 if (warehouse->type != BUILDING_WAREHOUSE) {
24 return 0;
25 }
26
27 int route_id = empire_city_get_route_id(city_id);
28 // try existing storage bay with the same resource
29 building *space = warehouse;
30 for (int i = 0; i < 8; i++) {
31 space = building_next(space);
32 if (space->id > 0) {
33 if (space->loads_stored && space->loads_stored < 4 && space->subtype.warehouse_resource_id == resource) {
34 trade_route_increase_traded(route_id, resource);
35 building_warehouse_space_add_import(space, resource);
36 return 1;
37 }
38 }
39 }
40 // try unused storage bay
41 space = warehouse;
42 for (int i = 0; i < 8; i++) {
43 space = building_next(space);
44 if (space->id > 0) {
45 if (space->subtype.warehouse_resource_id == RESOURCE_NONE) {
46 trade_route_increase_traded(route_id, resource);
47 building_warehouse_space_add_import(space, resource);
48 return 1;
49 }
50 }
51 }
52 return 0;
53 }
54
try_export_resource(int building_id,int resource,int city_id)55 static int try_export_resource(int building_id, int resource, int city_id)
56 {
57 building *warehouse = building_get(building_id);
58 if (warehouse->type != BUILDING_WAREHOUSE) {
59 return 0;
60 }
61
62 building *space = warehouse;
63 for (int i = 0; i < 8; i++) {
64 space = building_next(space);
65 if (space->id > 0) {
66 if (space->loads_stored && space->subtype.warehouse_resource_id == resource) {
67 trade_route_increase_traded(empire_city_get_route_id(city_id), resource);
68 building_warehouse_space_remove_export(space, resource);
69 return 1;
70 }
71 }
72 }
73 return 0;
74 }
75
get_closest_warehouse_for_import(int x,int y,int city_id,int distance_from_entry,int road_network_id,map_point * warehouse,int * import_resource)76 static int get_closest_warehouse_for_import(int x, int y, int city_id, int distance_from_entry, int road_network_id,
77 map_point *warehouse, int *import_resource)
78 {
79 int importable[16];
80 importable[RESOURCE_NONE] = 0;
81 for (int r = RESOURCE_MIN; r < RESOURCE_MAX; r++) {
82 importable[r] = empire_can_import_resource_from_city(city_id, r);
83 }
84 int resource = city_trade_next_docker_import_resource();
85 for (int i = RESOURCE_MIN; i < RESOURCE_MAX && !importable[resource]; i++) {
86 resource = city_trade_next_docker_import_resource();
87 }
88 if (!importable[resource]) {
89 return 0;
90 }
91 int min_distance = 10000;
92 int min_building_id = 0;
93 for (int i = 1; i < MAX_BUILDINGS; i++) {
94 building *b = building_get(i);
95 if (b->state != BUILDING_STATE_IN_USE || b->type != BUILDING_WAREHOUSE) {
96 continue;
97 }
98 if (!b->has_road_access || b->distance_from_entry <= 0) {
99 continue;
100 }
101 if (b->road_network_id != road_network_id) {
102 continue;
103 }
104 const building_storage *storage = building_storage_get(b->storage_id);
105 if (storage->resource_state[resource] != BUILDING_STORAGE_STATE_NOT_ACCEPTING && !storage->empty_all) {
106 int distance_penalty = 32;
107 building *space = b;
108 for (int s = 0; s < 8; s++) {
109 space = building_next(space);
110 if (space->id && space->subtype.warehouse_resource_id == RESOURCE_NONE) {
111 distance_penalty -= 8;
112 }
113 if (space->id && space->subtype.warehouse_resource_id == resource && space->loads_stored < 4) {
114 distance_penalty -= 4;
115 }
116 }
117 if (distance_penalty < 32) {
118 int distance = calc_distance_with_penalty(
119 b->x, b->y, x, y, distance_from_entry, b->distance_from_entry);
120 // prefer emptier warehouse
121 distance += distance_penalty;
122 if (distance < min_distance) {
123 min_distance = distance;
124 min_building_id = i;
125 }
126 }
127 }
128 }
129 if (!min_building_id) {
130 return 0;
131 }
132 building *min = building_get(min_building_id);
133 if (min->has_road_access == 1) {
134 map_point_store_result(min->x, min->y, warehouse);
135 } else if (!map_has_road_access(min->x, min->y, 3, warehouse)) {
136 return 0;
137 }
138 *import_resource = resource;
139 return min_building_id;
140 }
141
get_closest_warehouse_for_export(int x,int y,int city_id,int distance_from_entry,int road_network_id,map_point * warehouse,int * export_resource)142 static int get_closest_warehouse_for_export(int x, int y, int city_id, int distance_from_entry, int road_network_id,
143 map_point *warehouse, int *export_resource)
144 {
145 int exportable[16];
146 exportable[RESOURCE_NONE] = 0;
147 for (int r = RESOURCE_MIN; r < RESOURCE_MAX; r++) {
148 exportable[r] = empire_can_export_resource_to_city(city_id, r);
149 }
150 int resource = city_trade_next_docker_export_resource();
151 for (int i = RESOURCE_MIN; i < RESOURCE_MAX && !exportable[resource]; i++) {
152 resource = city_trade_next_docker_export_resource();
153 }
154 if (!exportable[resource]) {
155 return 0;
156 }
157 int min_distance = 10000;
158 int min_building_id = 0;
159 for (int i = 1; i < MAX_BUILDINGS; i++) {
160 building *b = building_get(i);
161 if (b->state != BUILDING_STATE_IN_USE || b->type != BUILDING_WAREHOUSE) {
162 continue;
163 }
164 if (!b->has_road_access || b->distance_from_entry <= 0) {
165 continue;
166 }
167 if (b->road_network_id != road_network_id) {
168 continue;
169 }
170 int distance_penalty = 32;
171 building *space = b;
172 for (int s = 0; s < 8; s++) {
173 space = building_next(space);
174 if (space->id && space->subtype.warehouse_resource_id == resource && space->loads_stored > 0) {
175 distance_penalty--;
176 }
177 }
178 if (distance_penalty < 32) {
179 int distance = calc_distance_with_penalty(b->x, b->y, x, y, distance_from_entry, b->distance_from_entry);
180 // prefer fuller warehouse
181 distance += distance_penalty;
182 if (distance < min_distance) {
183 min_distance = distance;
184 min_building_id = i;
185 }
186 }
187 }
188 if (!min_building_id) {
189 return 0;
190 }
191 building *min = building_get(min_building_id);
192 if (min->has_road_access == 1) {
193 map_point_store_result(min->x, min->y, warehouse);
194 } else if (!map_has_road_access(min->x, min->y, 3, warehouse)) {
195 return 0;
196 }
197 *export_resource = resource;
198 return min_building_id;
199 }
200
get_trade_center_location(const figure * f,int * x,int * y)201 static void get_trade_center_location(const figure *f, int *x, int *y)
202 {
203 int trade_center_id = city_buildings_get_trade_center();
204 if (trade_center_id) {
205 building *trade_center = building_get(trade_center_id);
206 *x = trade_center->x;
207 *y = trade_center->y;
208 } else {
209 *x = f->x;
210 *y = f->y;
211 }
212 }
213
deliver_import_resource(figure * f,building * dock)214 static int deliver_import_resource(figure *f, building *dock)
215 {
216 int ship_id = dock->data.dock.trade_ship_id;
217 if (!ship_id) {
218 return 0;
219 }
220 figure *ship = figure_get(ship_id);
221 if (ship->action_state != FIGURE_ACTION_112_TRADE_SHIP_MOORED || ship->loads_sold_or_carrying <= 0) {
222 return 0;
223 }
224 int x, y;
225 get_trade_center_location(f, &x, &y);
226 map_point tile;
227 int resource;
228 int warehouse_id = get_closest_warehouse_for_import(x, y, ship->empire_city_id,
229 dock->distance_from_entry, dock->road_network_id, &tile, &resource);
230 if (!warehouse_id) {
231 return 0;
232 }
233 ship->loads_sold_or_carrying--;
234 f->destination_building_id = warehouse_id;
235 f->wait_ticks = 0;
236 f->action_state = FIGURE_ACTION_133_DOCKER_IMPORT_QUEUE;
237 f->destination_x = tile.x;
238 f->destination_y = tile.y;
239 f->resource_id = resource;
240 return 1;
241 }
242
fetch_export_resource(figure * f,building * dock)243 static int fetch_export_resource(figure *f, building *dock)
244 {
245 int ship_id = dock->data.dock.trade_ship_id;
246 if (!ship_id) {
247 return 0;
248 }
249 figure *ship = figure_get(ship_id);
250 if (ship->action_state != FIGURE_ACTION_112_TRADE_SHIP_MOORED || ship->trader_amount_bought >= 12) {
251 return 0;
252 }
253 int x, y;
254 get_trade_center_location(f, &x, &y);
255 map_point tile;
256 int resource;
257 int warehouse_id = get_closest_warehouse_for_export(x, y, ship->empire_city_id,
258 dock->distance_from_entry, dock->road_network_id, &tile, &resource);
259 if (!warehouse_id) {
260 return 0;
261 }
262 ship->trader_amount_bought++;
263 f->destination_building_id = warehouse_id;
264 f->action_state = FIGURE_ACTION_136_DOCKER_EXPORT_GOING_TO_WAREHOUSE;
265 f->wait_ticks = 0;
266 f->destination_x = tile.x;
267 f->destination_y = tile.y;
268 f->resource_id = resource;
269 return 1;
270 }
271
set_cart_graphic(figure * f)272 static void set_cart_graphic(figure *f)
273 {
274 f->cart_image_id = image_group(GROUP_FIGURE_CARTPUSHER_CART) + 8 * f->resource_id;
275 f->cart_image_id += resource_image_offset(f->resource_id, RESOURCE_IMAGE_CART);
276 }
277
figure_docker_action(figure * f)278 void figure_docker_action(figure *f)
279 {
280 building *b = building_get(f->building_id);
281 figure_image_increase_offset(f, 12);
282 f->cart_image_id = 0;
283 if (b->state != BUILDING_STATE_IN_USE) {
284 f->state = FIGURE_STATE_DEAD;
285 }
286 if (b->type != BUILDING_DOCK && b->type != BUILDING_WHARF) {
287 f->state = FIGURE_STATE_DEAD;
288 }
289 if (b->data.dock.num_ships) {
290 b->data.dock.num_ships--;
291 }
292 if (b->data.dock.trade_ship_id) {
293 figure *ship = figure_get(b->data.dock.trade_ship_id);
294 if (ship->state != FIGURE_STATE_ALIVE || ship->type != FIGURE_TRADE_SHIP) {
295 b->data.dock.trade_ship_id = 0;
296 } else if (trader_has_traded_max(ship->trader_id)) {
297 b->data.dock.trade_ship_id = 0;
298 } else if (ship->action_state == FIGURE_ACTION_115_TRADE_SHIP_LEAVING) {
299 b->data.dock.trade_ship_id = 0;
300 }
301 }
302 f->terrain_usage = TERRAIN_USAGE_ROADS;
303 switch (f->action_state) {
304 case FIGURE_ACTION_150_ATTACK:
305 figure_combat_handle_attack(f);
306 break;
307 case FIGURE_ACTION_149_CORPSE:
308 figure_combat_handle_corpse(f);
309 break;
310 case FIGURE_ACTION_132_DOCKER_IDLING:
311 f->resource_id = 0;
312 f->cart_image_id = 0;
313 if (!deliver_import_resource(f, b)) {
314 fetch_export_resource(f, b);
315 }
316 f->image_offset = 0;
317 break;
318 case FIGURE_ACTION_133_DOCKER_IMPORT_QUEUE:
319 f->cart_image_id = 0;
320 f->image_offset = 0;
321 if (b->data.dock.queued_docker_id <= 0) {
322 b->data.dock.queued_docker_id = f->id;
323 f->wait_ticks = 0;
324 }
325 if (b->data.dock.queued_docker_id == f->id) {
326 b->data.dock.num_ships = 120;
327 f->wait_ticks++;
328 if (f->wait_ticks >= 80) {
329 f->action_state = FIGURE_ACTION_135_DOCKER_IMPORT_GOING_TO_WAREHOUSE;
330 f->wait_ticks = 0;
331 set_cart_graphic(f);
332 b->data.dock.queued_docker_id = 0;
333 }
334 } else {
335 int has_queued_docker = 0;
336 for (int i = 0; i < 3; i++) {
337 if (b->data.dock.docker_ids[i]) {
338 figure *docker = figure_get(b->data.dock.docker_ids[i]);
339 if (docker->id == b->data.dock.queued_docker_id && docker->state == FIGURE_STATE_ALIVE) {
340 if (docker->action_state == FIGURE_ACTION_133_DOCKER_IMPORT_QUEUE ||
341 docker->action_state == FIGURE_ACTION_134_DOCKER_EXPORT_QUEUE) {
342 has_queued_docker = 1;
343 }
344 }
345 }
346 }
347 if (!has_queued_docker) {
348 b->data.dock.queued_docker_id = 0;
349 }
350 }
351 break;
352 case FIGURE_ACTION_134_DOCKER_EXPORT_QUEUE:
353 set_cart_graphic(f);
354 if (b->data.dock.queued_docker_id <= 0) {
355 b->data.dock.queued_docker_id = f->id;
356 f->wait_ticks = 0;
357 }
358 if (b->data.dock.queued_docker_id == f->id) {
359 b->data.dock.num_ships = 120;
360 f->wait_ticks++;
361 if (f->wait_ticks >= 80) {
362 f->action_state = FIGURE_ACTION_132_DOCKER_IDLING;
363 f->wait_ticks = 0;
364 f->image_id = 0;
365 f->cart_image_id = 0;
366 b->data.dock.queued_docker_id = 0;
367 }
368 }
369 f->wait_ticks++;
370 if (f->wait_ticks >= 20) {
371 f->action_state = FIGURE_ACTION_132_DOCKER_IDLING;
372 f->wait_ticks = 0;
373 }
374 f->image_offset = 0;
375 break;
376 case FIGURE_ACTION_135_DOCKER_IMPORT_GOING_TO_WAREHOUSE:
377 set_cart_graphic(f);
378 figure_movement_move_ticks(f, 1);
379 if (f->direction == DIR_FIGURE_AT_DESTINATION) {
380 f->action_state = FIGURE_ACTION_139_DOCKER_IMPORT_AT_WAREHOUSE;
381 } else if (f->direction == DIR_FIGURE_REROUTE) {
382 figure_route_remove(f);
383 } else if (f->direction == DIR_FIGURE_LOST) {
384 f->state = FIGURE_STATE_DEAD;
385 }
386 if (building_get(f->destination_building_id)->state != BUILDING_STATE_IN_USE) {
387 f->state = FIGURE_STATE_DEAD;
388 }
389 break;
390 case FIGURE_ACTION_136_DOCKER_EXPORT_GOING_TO_WAREHOUSE:
391 f->cart_image_id = image_group(GROUP_FIGURE_CARTPUSHER_CART); // empty
392 figure_movement_move_ticks(f, 1);
393 if (f->direction == DIR_FIGURE_AT_DESTINATION) {
394 f->action_state = FIGURE_ACTION_140_DOCKER_EXPORT_AT_WAREHOUSE;
395 } else if (f->direction == DIR_FIGURE_REROUTE) {
396 figure_route_remove(f);
397 } else if (f->direction == DIR_FIGURE_LOST) {
398 f->state = FIGURE_STATE_DEAD;
399 }
400 if (building_get(f->destination_building_id)->state != BUILDING_STATE_IN_USE) {
401 f->state = FIGURE_STATE_DEAD;
402 }
403 break;
404 case FIGURE_ACTION_137_DOCKER_EXPORT_RETURNING:
405 set_cart_graphic(f);
406 figure_movement_move_ticks(f, 1);
407 if (f->direction == DIR_FIGURE_AT_DESTINATION) {
408 f->action_state = FIGURE_ACTION_134_DOCKER_EXPORT_QUEUE;
409 f->wait_ticks = 0;
410 } else if (f->direction == DIR_FIGURE_REROUTE) {
411 figure_route_remove(f);
412 } else if (f->direction == DIR_FIGURE_LOST) {
413 f->state = FIGURE_STATE_DEAD;
414 }
415 if (building_get(f->destination_building_id)->state != BUILDING_STATE_IN_USE) {
416 f->state = FIGURE_STATE_DEAD;
417 }
418 break;
419 case FIGURE_ACTION_138_DOCKER_IMPORT_RETURNING:
420 set_cart_graphic(f);
421 figure_movement_move_ticks(f, 1);
422 if (f->direction == DIR_FIGURE_AT_DESTINATION) {
423 f->action_state = FIGURE_ACTION_132_DOCKER_IDLING;
424 } else if (f->direction == DIR_FIGURE_REROUTE) {
425 figure_route_remove(f);
426 } else if (f->direction == DIR_FIGURE_LOST) {
427 f->state = FIGURE_STATE_DEAD;
428 }
429 break;
430 case FIGURE_ACTION_139_DOCKER_IMPORT_AT_WAREHOUSE:
431 set_cart_graphic(f);
432 f->wait_ticks++;
433 if (f->wait_ticks > 10) {
434 int trade_city_id;
435 if (b->data.dock.trade_ship_id) {
436 trade_city_id = figure_get(b->data.dock.trade_ship_id)->empire_city_id;
437 } else {
438 trade_city_id = 0;
439 }
440 if (try_import_resource(f->destination_building_id, f->resource_id, trade_city_id)) {
441 int trader_id = figure_get(b->data.dock.trade_ship_id)->trader_id;
442 trader_record_sold_resource(trader_id, f->resource_id);
443 f->action_state = FIGURE_ACTION_138_DOCKER_IMPORT_RETURNING;
444 f->wait_ticks = 0;
445 f->destination_x = f->source_x;
446 f->destination_y = f->source_y;
447 f->resource_id = 0;
448 fetch_export_resource(f, b);
449 } else {
450 f->action_state = FIGURE_ACTION_138_DOCKER_IMPORT_RETURNING;
451 f->destination_x = f->source_x;
452 f->destination_y = f->source_y;
453 }
454 f->wait_ticks = 0;
455 }
456 f->image_offset = 0;
457 break;
458 case FIGURE_ACTION_140_DOCKER_EXPORT_AT_WAREHOUSE:
459 f->cart_image_id = image_group(GROUP_FIGURE_CARTPUSHER_CART); // empty
460 f->wait_ticks++;
461 if (f->wait_ticks > 10) {
462 int trade_city_id;
463 if (b->data.dock.trade_ship_id) {
464 trade_city_id = figure_get(b->data.dock.trade_ship_id)->empire_city_id;
465 } else {
466 trade_city_id = 0;
467 }
468 f->action_state = FIGURE_ACTION_138_DOCKER_IMPORT_RETURNING;
469 f->destination_x = f->source_x;
470 f->destination_y = f->source_y;
471 f->wait_ticks = 0;
472 if (try_export_resource(f->destination_building_id, f->resource_id, trade_city_id)) {
473 int trader_id = figure_get(b->data.dock.trade_ship_id)->trader_id;
474 trader_record_bought_resource(trader_id, f->resource_id);
475 f->action_state = FIGURE_ACTION_137_DOCKER_EXPORT_RETURNING;
476 } else {
477 fetch_export_resource(f, b);
478 }
479 }
480 f->image_offset = 0;
481 break;
482 }
483
484 int dir = figure_image_normalize_direction(f->direction < 8 ? f->direction : f->previous_tile_direction);
485
486 if (f->action_state == FIGURE_ACTION_149_CORPSE) {
487 f->image_id = image_group(GROUP_FIGURE_CARTPUSHER) + figure_image_corpse_offset(f) + 96;
488 f->cart_image_id = 0;
489 } else {
490 f->image_id = image_group(GROUP_FIGURE_CARTPUSHER) + dir + 8 * f->image_offset;
491 }
492 if (f->cart_image_id) {
493 f->cart_image_id += dir;
494 figure_image_set_cart_offset(f, dir);
495 } else {
496 f->image_id = 0;
497 }
498 }
499