1 #include "warehouse.h"
2 
3 #include "building/count.h"
4 #include "building/model.h"
5 #include "building/storage.h"
6 #include "city/buildings.h"
7 #include "city/finance.h"
8 #include "city/military.h"
9 #include "city/resource.h"
10 #include "core/calc.h"
11 #include "core/image.h"
12 #include "empire/trade_prices.h"
13 #include "game/tutorial.h"
14 #include "map/image.h"
15 #include "map/road_access.h"
16 #include "scenario/property.h"
17 
building_warehouse_get_space_info(building * warehouse)18 int building_warehouse_get_space_info(building *warehouse)
19 {
20     int total_loads = 0;
21     int empty_spaces = 0;
22     building *space = warehouse;
23     for (int i = 0; i < 8; i++) {
24         space = building_next(space);
25         if (space->id <= 0) {
26             return 0;
27         }
28         if (space->subtype.warehouse_resource_id) {
29             total_loads += space->loads_stored;
30         } else {
31             empty_spaces++;
32         }
33     }
34     if (empty_spaces > 0) {
35         return WAREHOUSE_ROOM;
36     } else if (total_loads < 32) {
37         return WAREHOUSE_SOME_ROOM;
38     } else {
39         return WAREHOUSE_FULL;
40     }
41 }
42 
building_warehouse_get_amount(building * warehouse,int resource)43 int building_warehouse_get_amount(building *warehouse, int resource)
44 {
45     int loads = 0;
46     building *space = warehouse;
47     for (int i = 0; i < 8; i++) {
48         space = building_next(space);
49         if (space->id <= 0) {
50             return 0;
51         }
52         if (space->subtype.warehouse_resource_id && space->subtype.warehouse_resource_id == resource) {
53             loads += space->loads_stored;
54         }
55     }
56     return loads;
57 }
58 
building_warehouse_add_resource(building * b,int resource)59 int building_warehouse_add_resource(building *b, int resource)
60 {
61     if (b->id <= 0) {
62         return 0;
63     }
64     // check building itself
65     int find_space = 0;
66     if (b->subtype.warehouse_resource_id && b->subtype.warehouse_resource_id != resource) {
67         find_space = 1;
68     } else if (b->loads_stored >= 4) {
69         find_space = 1;
70     } else if (b->type == BUILDING_WAREHOUSE) {
71         find_space = 1;
72     }
73     if (find_space) {
74         int space_found = 0;
75         building *space = building_main(b);
76         for (int i = 0; i < 8; i++) {
77             space = building_next(space);
78             if (!space->id) {
79                 return 0;
80             }
81             if (!space->subtype.warehouse_resource_id || space->subtype.warehouse_resource_id == resource) {
82                 if (space->loads_stored < 4) {
83                     space_found = 1;
84                     b = space;
85                     break;
86                 }
87             }
88         }
89         if (!space_found) {
90             return 0;
91         }
92     }
93     city_resource_add_to_warehouse(resource, 1);
94     b->subtype.warehouse_resource_id = resource;
95     b->loads_stored++;
96     tutorial_on_add_to_warehouse();
97     building_warehouse_space_set_image(b, resource);
98     return 1;
99 }
100 
building_warehouse_remove_resource(building * warehouse,int resource,int amount)101 int building_warehouse_remove_resource(building *warehouse, int resource, int amount)
102 {
103     // returns amount still needing removal
104     if (warehouse->type != BUILDING_WAREHOUSE) {
105         return amount;
106     }
107     building *space = warehouse;
108     for (int i = 0; i < 8; i++) {
109         if (amount <= 0) {
110             return 0;
111         }
112         space = building_next(space);
113         if (space->id <= 0) {
114             continue;
115         }
116         if (space->subtype.warehouse_resource_id != resource || space->loads_stored <= 0) {
117             continue;
118         }
119         if (space->loads_stored > amount) {
120             city_resource_remove_from_warehouse(resource, amount);
121             space->loads_stored -= amount;
122             amount = 0;
123         } else {
124             city_resource_remove_from_warehouse(resource, space->loads_stored);
125             amount -= space->loads_stored;
126             space->loads_stored = 0;
127             space->subtype.warehouse_resource_id = RESOURCE_NONE;
128         }
129         building_warehouse_space_set_image(space, resource);
130     }
131     return amount;
132 }
133 
building_warehouse_remove_resource_curse(building * warehouse,int amount)134 void building_warehouse_remove_resource_curse(building *warehouse, int amount)
135 {
136     if (warehouse->type != BUILDING_WAREHOUSE) {
137         return;
138     }
139     building *space = warehouse;
140     for (int i = 0; i < 8 && amount > 0; i++) {
141         space = building_next(space);
142         if (space->id <= 0 || space->loads_stored <= 0) {
143             continue;
144         }
145         int resource = space->subtype.warehouse_resource_id;
146         if (space->loads_stored > amount) {
147             city_resource_remove_from_warehouse(resource, amount);
148             space->loads_stored -= amount;
149             amount = 0;
150         } else {
151             city_resource_remove_from_warehouse(resource, space->loads_stored);
152             amount -= space->loads_stored;
153             space->loads_stored = 0;
154             space->subtype.warehouse_resource_id = RESOURCE_NONE;
155         }
156         building_warehouse_space_set_image(space, resource);
157     }
158 }
159 
building_warehouse_space_set_image(building * space,int resource)160 void building_warehouse_space_set_image(building *space, int resource)
161 {
162     int image_id;
163     if (space->loads_stored <= 0) {
164         image_id = image_group(GROUP_BUILDING_WAREHOUSE_STORAGE_EMPTY);
165     } else {
166         image_id = image_group(GROUP_BUILDING_WAREHOUSE_STORAGE_FILLED) +
167             4 * (resource - 1) + resource_image_offset(resource, RESOURCE_IMAGE_STORAGE) +
168                    space->loads_stored - 1;
169     }
170     map_image_set(space->grid_offset, image_id);
171 }
172 
building_warehouse_space_add_import(building * space,int resource)173 void building_warehouse_space_add_import(building *space, int resource)
174 {
175     city_resource_add_to_warehouse(resource, 1);
176     space->loads_stored++;
177     space->subtype.warehouse_resource_id = resource;
178 
179     int price = trade_price_buy(resource);
180     city_finance_process_import(price);
181 
182     building_warehouse_space_set_image(space, resource);
183 }
184 
building_warehouse_space_remove_export(building * space,int resource)185 void building_warehouse_space_remove_export(building *space, int resource)
186 {
187     city_resource_remove_from_warehouse(resource, 1);
188     space->loads_stored--;
189     if (space->loads_stored <= 0) {
190         space->subtype.warehouse_resource_id = RESOURCE_NONE;
191     }
192 
193     int price = trade_price_sell(resource);
194     city_finance_process_export(price);
195 
196     building_warehouse_space_set_image(space, resource);
197 }
198 
building_warehouses_add_resource(int resource,int amount)199 void building_warehouses_add_resource(int resource, int amount)
200 {
201     int building_id = city_resource_last_used_warehouse();
202     for (int i = 1; i < MAX_BUILDINGS && amount > 0; i++) {
203         building_id++;
204         if (building_id >= MAX_BUILDINGS) {
205             building_id = 1;
206         }
207         building *b = building_get(building_id);
208         if (b->state == BUILDING_STATE_IN_USE && b->type == BUILDING_WAREHOUSE) {
209             city_resource_set_last_used_warehouse(building_id);
210             while (amount && building_warehouse_add_resource(b, resource)) {
211                 amount--;
212             }
213         }
214     }
215 }
216 
building_warehouses_remove_resource(int resource,int amount)217 int building_warehouses_remove_resource(int resource, int amount)
218 {
219     int amount_left = amount;
220     int building_id = city_resource_last_used_warehouse();
221     // first go for non-getting warehouses
222     for (int i = 1; i < MAX_BUILDINGS && amount_left > 0; i++) {
223         building_id++;
224         if (building_id >= MAX_BUILDINGS) {
225             building_id = 1;
226         }
227         building *b = building_get(building_id);
228         if (b->state == BUILDING_STATE_IN_USE && b->type == BUILDING_WAREHOUSE) {
229             if (building_storage_get(b->storage_id)->resource_state[resource] != BUILDING_STORAGE_STATE_GETTING) {
230                 city_resource_set_last_used_warehouse(building_id);
231                 amount_left = building_warehouse_remove_resource(b, resource, amount_left);
232             }
233         }
234     }
235     // if that doesn't work, take it anyway
236     for (int i = 1; i < MAX_BUILDINGS && amount_left > 0; i++) {
237         building_id++;
238         if (building_id >= MAX_BUILDINGS) {
239             building_id = 1;
240         }
241         building *b = building_get(building_id);
242         if (b->state == BUILDING_STATE_IN_USE && b->type == BUILDING_WAREHOUSE) {
243             city_resource_set_last_used_warehouse(building_id);
244             amount_left = building_warehouse_remove_resource(b, resource, amount_left);
245         }
246     }
247     return amount - amount_left;
248 }
249 
building_warehouse_for_storing(int src_building_id,int x,int y,int resource,int distance_from_entry,int road_network_id,int * understaffed,map_point * dst)250 int building_warehouse_for_storing(int src_building_id, int x, int y, int resource,
251                                    int distance_from_entry, int road_network_id, int *understaffed,
252                                    map_point *dst)
253 {
254     int min_dist = 10000;
255     int min_building_id = 0;
256     for (int i = 1; i < MAX_BUILDINGS; i++) {
257         building *b = building_get(i);
258         if (b->state != BUILDING_STATE_IN_USE || b->type != BUILDING_WAREHOUSE_SPACE) {
259             continue;
260         }
261         if (!b->has_road_access || b->distance_from_entry <= 0 || b->road_network_id != road_network_id) {
262             continue;
263         }
264         building *building_dst = building_main(b);
265         if (src_building_id == building_dst->id) {
266             continue;
267         }
268         const building_storage *s = building_storage_get(building_dst->storage_id);
269         if (s->resource_state[resource] == BUILDING_STORAGE_STATE_NOT_ACCEPTING || s->empty_all) {
270             continue;
271         }
272         int pct_workers = calc_percentage(building_dst->num_workers, model_get_building(building_dst->type)->laborers);
273         if (pct_workers < 100) {
274             if (understaffed) {
275                 *understaffed += 1;
276             }
277             continue;
278         }
279         int dist;
280         if (b->subtype.warehouse_resource_id == RESOURCE_NONE) { // empty warehouse space
281             dist = calc_distance_with_penalty(b->x, b->y, x, y, distance_from_entry, b->distance_from_entry);
282         } else if (b->subtype.warehouse_resource_id == resource && b->loads_stored < 4) {
283             dist = calc_distance_with_penalty(b->x, b->y, x, y, distance_from_entry, b->distance_from_entry);
284         } else {
285             dist = 0;
286         }
287         if (dist > 0 && dist < min_dist) {
288             min_dist = dist;
289             min_building_id = i;
290         }
291     }
292     building *b = building_main(building_get(min_building_id));
293     if (b->has_road_access == 1) {
294         map_point_store_result(b->x, b->y, dst);
295     } else if (!map_has_road_access(b->x, b->y, 3, dst)) {
296         return 0;
297     }
298     return min_building_id;
299 }
300 
building_warehouse_for_getting(building * src,int resource,map_point * dst)301 int building_warehouse_for_getting(building *src, int resource, map_point *dst)
302 {
303     int min_dist = 10000;
304     building *min_building = 0;
305     for (int i = 1; i < MAX_BUILDINGS; i++) {
306         building *b = building_get(i);
307         if (b->state != BUILDING_STATE_IN_USE || b->type != BUILDING_WAREHOUSE) {
308             continue;
309         }
310         if (i == src->id) {
311             continue;
312         }
313         int loads_stored = 0;
314         building *space = b;
315         const building_storage *s = building_storage_get(b->storage_id);
316         for (int t = 0; t < 8; t++) {
317             space = building_next(space);
318             if (space->id > 0 && space->loads_stored > 0) {
319                 if (space->subtype.warehouse_resource_id == resource) {
320                     loads_stored += space->loads_stored;
321                 }
322             }
323         }
324         if (loads_stored > 0 && s->resource_state[resource] != BUILDING_STORAGE_STATE_GETTING) {
325             int dist = calc_distance_with_penalty(b->x, b->y, src->x, src->y,
326                                                   src->distance_from_entry, b->distance_from_entry);
327             dist -= 4 * loads_stored;
328             if (dist < min_dist) {
329                 min_dist = dist;
330                 min_building = b;
331             }
332         }
333     }
334     if (min_building) {
335         map_point_store_result(min_building->road_access_x, min_building->road_access_y, dst);
336         return min_building->id;
337     } else {
338         return 0;
339     }
340 }
341 
determine_granary_accept_foods(int resources[RESOURCE_MAX_FOOD])342 static int determine_granary_accept_foods(int resources[RESOURCE_MAX_FOOD])
343 {
344     if (scenario_property_rome_supplies_wheat()) {
345         return 0;
346     }
347     for (int i = 0; i < RESOURCE_MAX_FOOD; i++) {
348         resources[i] = 0;
349     }
350     int can_accept = 0;
351     for (int i = 1; i < MAX_BUILDINGS; i++) {
352         building *b = building_get(i);
353         if (b->state != BUILDING_STATE_IN_USE || b->type != BUILDING_GRANARY || !b->has_road_access) {
354             continue;
355         }
356         int pct_workers = calc_percentage(b->num_workers, model_get_building(b->type)->laborers);
357         if (pct_workers >= 100 && b->data.granary.resource_stored[RESOURCE_NONE] >= 1200) {
358             const building_storage *s = building_storage_get(b->storage_id);
359             if (!s->empty_all) {
360                 for (int r = 0; r < RESOURCE_MAX_FOOD; r++) {
361                     if (s->resource_state[r] != BUILDING_STORAGE_STATE_NOT_ACCEPTING) {
362                         resources[r]++;
363                         can_accept = 1;
364                     }
365                 }
366             }
367         }
368     }
369     return can_accept;
370 }
371 
determine_granary_get_foods(int resources[RESOURCE_MAX_FOOD])372 static int determine_granary_get_foods(int resources[RESOURCE_MAX_FOOD])
373 {
374     if (scenario_property_rome_supplies_wheat()) {
375         return 0;
376     }
377     for (int i = 0; i < RESOURCE_MAX_FOOD; i++) {
378         resources[i] = 0;
379     }
380     int can_get = 0;
381     for (int i = 1; i < MAX_BUILDINGS; i++) {
382         building *b = building_get(i);
383         if (b->state != BUILDING_STATE_IN_USE || b->type != BUILDING_GRANARY || !b->has_road_access) {
384             continue;
385         }
386         int pct_workers = calc_percentage(b->num_workers, model_get_building(b->type)->laborers);
387         if (pct_workers >= 100 && b->data.granary.resource_stored[RESOURCE_NONE] > 100) {
388             const building_storage *s = building_storage_get(b->storage_id);
389             if (!s->empty_all) {
390                 for (int r = 0; r < RESOURCE_MAX_FOOD; r++) {
391                     if (s->resource_state[r] == BUILDING_STORAGE_STATE_GETTING) {
392                         resources[r]++;
393                         can_get = 1;
394                     }
395                 }
396             }
397         }
398     }
399     return can_get;
400 }
401 
contains_non_stockpiled_food(building * space,const int * resources)402 static int contains_non_stockpiled_food(building *space, const int *resources)
403 {
404     if (space->id <= 0) {
405         return 0;
406     }
407     if (space->loads_stored <= 0) {
408         return 0;
409     }
410     int resource = space->subtype.warehouse_resource_id;
411     if (city_resource_is_stockpiled(resource)) {
412         return 0;
413     }
414     if (resource == RESOURCE_WHEAT || resource == RESOURCE_VEGETABLES ||
415         resource == RESOURCE_FRUIT || resource == RESOURCE_MEAT) {
416         if (resources[resource] > 0) {
417             return 1;
418         }
419     }
420     return 0;
421 }
422 
building_warehouse_determine_worker_task(building * warehouse,int * resource)423 int building_warehouse_determine_worker_task(building *warehouse, int *resource)
424 {
425     int pct_workers = calc_percentage(warehouse->num_workers, model_get_building(warehouse->type)->laborers);
426     if (pct_workers < 50) {
427         return WAREHOUSE_TASK_NONE;
428     }
429     const building_storage *s = building_storage_get(warehouse->storage_id);
430     building *space;
431     // get resources
432     for (int r = RESOURCE_MIN; r < RESOURCE_MAX; r++) {
433         if (s->resource_state[r] != BUILDING_STORAGE_STATE_GETTING || city_resource_is_stockpiled(r)) {
434             continue;
435         }
436         int loads_stored = 0;
437         space = warehouse;
438         for (int i = 0; i < 8; i++) {
439             space = building_next(space);
440             if (space->id > 0 && space->loads_stored > 0) {
441                 if (space->subtype.warehouse_resource_id == r) {
442                     loads_stored += space->loads_stored;
443                 }
444             }
445         }
446         int room = 0;
447         space = warehouse;
448         for (int i = 0; i < 8; i++) {
449             space = building_next(space);
450             if (space->id > 0) {
451                 if (space->loads_stored <= 0) {
452                     room += 4;
453                 }
454                 if (space->subtype.warehouse_resource_id == r) {
455                     room += 4 - space->loads_stored;
456                 }
457             }
458         }
459         if (room >= 8 && loads_stored <= 4 && city_resource_count(r) - loads_stored > 4) {
460             *resource = r;
461             return WAREHOUSE_TASK_GETTING;
462         }
463     }
464     // deliver weapons to barracks
465     if (building_count_active(BUILDING_BARRACKS) > 0 && city_military_has_legionary_legions() &&
466         !city_resource_is_stockpiled(RESOURCE_WEAPONS)) {
467         building *barracks = building_get(city_buildings_get_barracks());
468         if (barracks->loads_stored < 4 &&
469                 warehouse->road_network_id == barracks->road_network_id) {
470             space = warehouse;
471             for (int i = 0; i < 8; i++) {
472                 space = building_next(space);
473                 if (space->id > 0 && space->loads_stored > 0 &&
474                     space->subtype.warehouse_resource_id == RESOURCE_WEAPONS) {
475                     *resource = RESOURCE_WEAPONS;
476                     return WAREHOUSE_TASK_DELIVERING;
477                 }
478             }
479         }
480     }
481     // deliver raw materials to workshops
482     space = warehouse;
483     for (int i = 0; i < 8; i++) {
484         space = building_next(space);
485         if (space->id > 0 && space->loads_stored > 0) {
486             if (!city_resource_is_stockpiled(space->subtype.warehouse_resource_id)) {
487                 int workshop_type = resource_to_workshop_type(space->subtype.warehouse_resource_id);
488                 if (workshop_type != WORKSHOP_NONE && city_resource_has_workshop_with_room(workshop_type)) {
489                     *resource = space->subtype.warehouse_resource_id;
490                     return WAREHOUSE_TASK_DELIVERING;
491                 }
492             }
493         }
494     }
495     // deliver food to getting granary
496     int granary_resources[RESOURCE_MAX_FOOD];
497     if (determine_granary_get_foods(granary_resources)) {
498         space = warehouse;
499         for (int i = 0; i < 8; i++) {
500             space = building_next(space);
501             if (contains_non_stockpiled_food(space, granary_resources)) {
502                 *resource = space->subtype.warehouse_resource_id;
503                 return WAREHOUSE_TASK_DELIVERING;
504             }
505         }
506     }
507     // deliver food to accepting granary
508     if (determine_granary_accept_foods(granary_resources) && !scenario_property_rome_supplies_wheat()) {
509         space = warehouse;
510         for (int i = 0; i < 8; i++) {
511             space = building_next(space);
512             if (contains_non_stockpiled_food(space, granary_resources)) {
513                 *resource = space->subtype.warehouse_resource_id;
514                 return WAREHOUSE_TASK_DELIVERING;
515             }
516         }
517     }
518     // move goods to other warehouses
519     if (s->empty_all) {
520         space = warehouse;
521         for (int i = 0; i < 8; i++) {
522             space = building_next(space);
523             if (space->id > 0 && space->loads_stored > 0) {
524                 *resource = space->subtype.warehouse_resource_id;
525                 return WAREHOUSE_TASK_DELIVERING;
526             }
527         }
528     }
529     return WAREHOUSE_TASK_NONE;
530 }
531