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