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