1 #include "maintenance.h"
2 
3 #include "building/building.h"
4 #include "building/destruction.h"
5 #include "building/list.h"
6 #include "building/monument.h"
7 #include "city/buildings.h"
8 #include "city/map.h"
9 #include "city/message.h"
10 #include "city/population.h"
11 #include "city/sentiment.h"
12 #include "city/view.h"
13 #include "city/warning.h"
14 #include "core/calc.h"
15 #include "core/random.h"
16 #include "figuretype/migrant.h"
17 #include "game/tutorial.h"
18 #include "game/undo.h"
19 #include "map/building.h"
20 #include "map/building_tiles.h"
21 #include "map/grid.h"
22 #include "map/random.h"
23 #include "map/road_access.h"
24 #include "map/road_network.h"
25 #include "map/routing.h"
26 #include "map/routing_terrain.h"
27 #include "map/tiles.h"
28 #include "scenario/property.h"
29 #include "sound/effect.h"
30 
31 static int fire_spread_direction = 0;
32 
building_maintenance_update_fire_direction(void)33 void building_maintenance_update_fire_direction(void)
34 {
35     fire_spread_direction = random_byte() & 7;
36 }
37 
building_maintenance_update_burning_ruins(void)38 void building_maintenance_update_burning_ruins(void)
39 {
40     scenario_climate climate = scenario_property_climate();
41     int recalculate_terrain = 0;
42     building_list_burning_clear();
43     for (int i = 1; i < building_count(); i++) {
44         building *b = building_get(i);
45         if ((b->state != BUILDING_STATE_IN_USE && b->state != BUILDING_STATE_MOTHBALLED) || b->type != BUILDING_BURNING_RUIN) {
46             continue;
47         }
48         if (b->fire_duration < 0) {
49             b->fire_duration = 0;
50         }
51         b->fire_duration++;
52         if (b->fire_duration > 32) {
53             game_undo_disable();
54             b->state = BUILDING_STATE_RUBBLE;
55             map_building_tiles_set_rubble(i, b->x, b->y, b->size);
56             recalculate_terrain = 1;
57             continue;
58         }
59         if (b->ruin_has_plague) {
60             continue;
61         }
62         building_list_burning_add(i);
63         if (climate == CLIMATE_DESERT) {
64             if (b->fire_duration & 3) { // check spread every 4 ticks
65                 continue;
66             }
67         } else {
68             if (b->fire_duration & 7) { // check spread every 8 ticks
69                 continue;
70             }
71         }
72         if ((b->house_figure_generation_delay & 3) != (random_byte() & 3)) {
73             continue;
74         }
75         int dir1 = fire_spread_direction - 1;
76         if (dir1 < 0) {
77             dir1 = 7;
78         }
79         int dir2 = fire_spread_direction + 1;
80         if (dir2 > 7) {
81             dir2 = 0;
82         }
83 
84         int grid_offset = b->grid_offset;
85         int next_building_id = map_building_at(grid_offset + map_grid_direction_delta(fire_spread_direction));
86         if (next_building_id && !building_get(next_building_id)->fire_proof) {
87             building_destroy_by_fire(building_get(next_building_id));
88             sound_effect_play(SOUND_EFFECT_EXPLOSION);
89             recalculate_terrain = 1;
90         } else {
91             next_building_id = map_building_at(grid_offset + map_grid_direction_delta(dir1));
92             if (next_building_id && !building_get(next_building_id)->fire_proof) {
93                 building_destroy_by_fire(building_get(next_building_id));
94                 sound_effect_play(SOUND_EFFECT_EXPLOSION);
95                 recalculate_terrain = 1;
96             } else {
97                 next_building_id = map_building_at(grid_offset + map_grid_direction_delta(dir2));
98                 if (next_building_id && !building_get(next_building_id)->fire_proof) {
99                     building_destroy_by_fire(building_get(next_building_id));
100                     sound_effect_play(SOUND_EFFECT_EXPLOSION);
101                     recalculate_terrain = 1;
102                 }
103             }
104         }
105     }
106     if (recalculate_terrain) {
107         map_routing_update_land();
108     }
109 }
110 
building_maintenance_get_closest_burning_ruin(int x,int y,int * distance)111 int building_maintenance_get_closest_burning_ruin(int x, int y, int *distance)
112 {
113     int min_free_building_id = 0;
114     int min_occupied_building_id = 0;
115     int min_occupied_dist = *distance = 10000;
116 
117     int burning_size = building_list_burning_size();
118     for (int i = 0; i < burning_size; i++) {
119         int building_id = building_list_burning_item(i);
120         building *b = building_get(building_id);
121         if ((b->state == BUILDING_STATE_IN_USE || b->state == BUILDING_STATE_MOTHBALLED) && b->type == BUILDING_BURNING_RUIN && !b->ruin_has_plague && b->distance_from_entry) {
122             int dist = calc_maximum_distance(x, y, b->x, b->y);
123             if (b->figure_id4) {
124                 if (dist < min_occupied_dist) {
125                     min_occupied_dist = dist;
126                     min_occupied_building_id = building_id;
127                 }
128             } else if (dist < *distance) {
129                 *distance = dist;
130                 min_free_building_id = building_id;
131             }
132         }
133     }
134     if (!min_free_building_id && min_occupied_dist <= 2) {
135         min_free_building_id = min_occupied_building_id;
136         *distance = 2;
137     }
138     return min_free_building_id;
139 }
140 
collapse_building(building * b)141 static void collapse_building(building *b)
142 {
143     city_message_apply_sound_interval(MESSAGE_CAT_COLLAPSE);
144     if (!tutorial_handle_collapse()) {
145         city_message_post_with_popup_delay(MESSAGE_CAT_COLLAPSE, MESSAGE_COLLAPSED_BUILDING, b->type, b->grid_offset);
146     }
147 
148     game_undo_disable();
149     building_destroy_by_collapse(b);
150 }
151 
fire_building(building * b)152 static void fire_building(building *b)
153 {
154     city_message_apply_sound_interval(MESSAGE_CAT_FIRE);
155     if (!tutorial_handle_fire()) {
156         city_message_post_with_popup_delay(MESSAGE_CAT_FIRE, MESSAGE_FIRE, b->type, b->grid_offset);
157     }
158 
159     building_destroy_by_fire(b);
160     sound_effect_play(SOUND_EFFECT_EXPLOSION);
161 }
162 
building_maintenance_check_fire_collapse(void)163 void building_maintenance_check_fire_collapse(void)
164 {
165     city_sentiment_reset_protesters_criminals();
166 
167     scenario_climate climate = scenario_property_climate();
168     int recalculate_terrain = 0;
169     int random_global = random_byte() & 7;
170 
171     for (int i = 1; i < building_count(); i++) {
172         building *b = building_get(i);
173         if (b->state != BUILDING_STATE_IN_USE || b->fire_proof) {
174             continue;
175         }
176         if (b->type == BUILDING_HIPPODROME && b->prev_part_building_id) {
177             continue;
178         }
179         int random_building = (i + map_random_get(b->grid_offset)) & 7;
180         // damage
181         b->damage_risk += random_building == random_global ? 3 : 1;
182         if (tutorial_extra_damage_risk()) {
183             b->damage_risk += 5;
184         }
185         if (b->house_size && b->subtype.house_level <= HOUSE_LARGE_TENT) {
186             b->damage_risk = 0;
187         }
188         if (b->damage_risk > 200) {
189             collapse_building(b);
190             recalculate_terrain = 1;
191             continue;
192         }
193         // fire
194         if (random_building == random_global) {
195             int fire_increase = 0;
196             if (!b->house_size) {
197                 fire_increase += 5;
198             } else if (b->house_population <= 0) {
199                 fire_increase = 0;
200             } else if (b->subtype.house_level <= HOUSE_LARGE_SHACK) {
201                 fire_increase += 10;
202             } else if (b->subtype.house_level <= HOUSE_GRAND_INSULA) {
203                 fire_increase += 5;
204             } else {
205                 fire_increase += 2;
206             }
207             if (tutorial_extra_fire_risk()) {
208                 fire_increase += 5;
209             }
210             if (climate == CLIMATE_NORTHERN) {
211                 fire_increase = 0;
212             } else if (climate == CLIMATE_DESERT) {
213                 fire_increase += 3;
214             }
215 
216             b->fire_risk += fire_increase;
217         }
218         if (b->fire_risk > 100) {
219             fire_building(b);
220             recalculate_terrain = 1;
221         }
222     }
223 
224     if (recalculate_terrain) {
225         map_routing_update_land();
226     }
227 }
228 
building_maintenance_check_rome_access(void)229 void building_maintenance_check_rome_access(void)
230 {
231     const map_tile *entry_point = city_map_entry_point();
232     map_routing_calculate_distances(entry_point->x, entry_point->y);
233     int problem_grid_offset = 0;
234     for (int i = 1; i < building_count(); i++) {
235         building *b = building_get(i);
236         if (b->state != BUILDING_STATE_IN_USE) {
237             continue;
238         }
239         if (b->house_size) {
240             int x_road, y_road;
241             if (!map_closest_road_within_radius(b->x, b->y, b->size, 2, &x_road, &y_road)) {
242                 // no road: eject people
243                 b->distance_from_entry = 0;
244                 b->house_unreachable_ticks++;
245                 if (b->house_unreachable_ticks > 4) {
246                     if (b->house_population) {
247                         figure_create_homeless(b, b->house_population);
248                         b->house_population = 0;
249                         b->house_unreachable_ticks = 0;
250                     }
251                     b->state = BUILDING_STATE_UNDO;
252                 }
253             } else if (map_routing_distance(map_grid_offset(x_road, y_road))) {
254                 // reachable from rome
255                 b->distance_from_entry = map_routing_distance(map_grid_offset(x_road, y_road));
256                 b->house_unreachable_ticks = 0;
257             } else if (map_closest_reachable_road_within_radius(b->x, b->y, b->size, 2, &x_road, &y_road)) {
258                 b->distance_from_entry = map_routing_distance(map_grid_offset(x_road, y_road));
259                 b->house_unreachable_ticks = 0;
260             } else {
261                 // no reachable road in radius
262                 if (!b->house_unreachable_ticks) {
263                     problem_grid_offset = b->grid_offset;
264                 }
265                 b->house_unreachable_ticks++;
266                 if (b->house_unreachable_ticks > 8) {
267                     b->distance_from_entry = 0;
268                     b->house_unreachable_ticks = 0;
269                     b->state = BUILDING_STATE_UNDO;
270                 }
271             }
272         } else if (b->type == BUILDING_WAREHOUSE) {
273             b->distance_from_entry = 0;
274             int x_road, y_road;
275             int road_grid_offset = map_road_to_largest_network_rotation(b->subtype.orientation, b->x, b->y, 3, &x_road, &y_road);
276             if (road_grid_offset >= 0) {
277                 b->road_network_id = map_road_network_get(road_grid_offset);
278                 b->distance_from_entry = map_routing_distance(road_grid_offset);
279                 b->road_access_x = x_road;
280                 b->road_access_y = y_road;
281             }
282         } else if (b->type == BUILDING_WAREHOUSE_SPACE) {
283             b->distance_from_entry = 0;
284             building *main_building = building_main(b);
285             b->road_network_id = main_building->road_network_id;
286             b->distance_from_entry = main_building->distance_from_entry;
287             b->road_access_x = main_building->road_access_x;
288             b->road_access_y = main_building->road_access_y;
289         } else if (b->type == BUILDING_HIPPODROME) {
290             b->distance_from_entry = 0;
291             int x_road, y_road;
292             int rotated = b->subtype.orientation;
293             int road_grid_offset = map_road_to_largest_network_hippodrome(b->x, b->y, &x_road, &y_road, rotated);
294             if (road_grid_offset >= 0) {
295                 b->road_network_id = map_road_network_get(road_grid_offset);
296                 b->distance_from_entry = map_routing_distance(road_grid_offset);
297                 b->road_access_x = x_road;
298                 b->road_access_y = y_road;
299             }
300         } else if ((b->type >= BUILDING_GRAND_TEMPLE_CERES && b->type <= BUILDING_GRAND_TEMPLE_VENUS) ||
301             (b->type == BUILDING_PANTHEON)) {
302             b->distance_from_entry = 0;
303             int x_road, y_road;
304             int road_grid_offset = map_road_to_largest_network_grand_temple(b->x, b->y, &x_road, &y_road);
305             if (road_grid_offset >= 0) {
306                 b->road_network_id = map_road_network_get(road_grid_offset);
307                 b->distance_from_entry = map_routing_distance(road_grid_offset);
308                 b->road_access_x = x_road;
309                 b->road_access_y = y_road;
310             }
311         } else if (b->type == BUILDING_COLOSSEUM) {
312             b->distance_from_entry = 0;
313             int x_road, y_road;
314             int road_grid_offset = map_road_to_largest_network_colosseum(b->x, b->y, &x_road, &y_road);
315             if (road_grid_offset >= 0) {
316                 b->road_network_id = map_road_network_get(road_grid_offset);
317                 b->distance_from_entry = map_routing_distance(road_grid_offset);
318                 b->road_access_x = x_road;
319                 b->road_access_y = y_road;
320             }
321         } else if (b->type == BUILDING_LIGHTHOUSE || b->type == BUILDING_LARGE_MAUSOLEUM ||
322             b->type == BUILDING_NYMPHAEUM ||
323             (b->type >= BUILDING_LARGE_TEMPLE_CERES && b->type <= BUILDING_LARGE_TEMPLE_VENUS)) {
324             b->distance_from_entry = 0;
325             int x_road, y_road;
326             int road_grid_offset = map_road_to_largest_network_lighthouse(b->x, b->y, &x_road, &y_road);
327             if (road_grid_offset >= 0) {
328                 b->road_network_id = map_road_network_get(road_grid_offset);
329                 b->distance_from_entry = map_routing_distance(road_grid_offset);
330                 b->road_access_x = x_road;
331                 b->road_access_y = y_road;
332             }
333         } else if (b->type == BUILDING_CARAVANSERAI) {
334             b->distance_from_entry = 0;
335             int x_road, y_road;
336             int road_grid_offset = map_road_to_largest_network_caravanserai(b->x, b->y, &x_road, &y_road);
337             if (road_grid_offset >= 0) {
338                 b->road_network_id = map_road_network_get(road_grid_offset);
339                 b->distance_from_entry = map_routing_distance(road_grid_offset);
340                 b->road_access_x = x_road;
341                 b->road_access_y = y_road;
342             }
343         } else { // other building
344             b->distance_from_entry = 0;
345             int x_road, y_road;
346             int road_grid_offset = map_road_to_largest_network(b->x, b->y, b->size, &x_road, &y_road);
347             if (road_grid_offset >= 0) {
348                 b->road_network_id = map_road_network_get(road_grid_offset);
349                 b->distance_from_entry = map_routing_distance(road_grid_offset);
350                 b->road_access_x = x_road;
351                 b->road_access_y = y_road;
352             }
353         }
354     }
355     const map_tile *exit_point = city_map_exit_point();
356     if (!map_routing_distance(exit_point->grid_offset)) {
357         // no route through city
358         if (city_population() <= 0) {
359             return;
360         }
361         for (int i = 0; i < 15; i++) {
362             map_routing_delete_first_wall_or_aqueduct(entry_point->x, entry_point->y);
363             map_routing_delete_first_wall_or_aqueduct(exit_point->x, exit_point->y);
364             map_routing_calculate_distances(entry_point->x, entry_point->y);
365 
366             map_tiles_update_all_walls();
367             map_tiles_update_all_aqueducts(0);
368             map_tiles_update_all_empty_land();
369             map_tiles_update_all_meadow();
370 
371             map_routing_update_land();
372             map_routing_update_walls();
373 
374             if (map_routing_distance(exit_point->grid_offset)) {
375                 city_message_post(1, MESSAGE_ROAD_TO_ROME_OBSTRUCTED, 0, 0);
376                 game_undo_disable();
377                 return;
378             }
379         }
380         building_destroy_last_placed();
381     } else if (problem_grid_offset) {
382         // parts of city disconnected
383         city_warning_show(WARNING_CITY_BOXED_IN);
384         city_warning_show(WARNING_CITY_BOXED_IN_PEOPLE_WILL_PERISH);
385         city_view_go_to_grid_offset(problem_grid_offset);
386     }
387 }
388