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