1 #include "tool.h"
2 
3 #include "building/construction_routed.h"
4 #include "core/image.h"
5 #include "core/image_group_editor.h"
6 #include "core/random.h"
7 #include "editor/tool_restriction.h"
8 #include "game/undo.h"
9 #include "map/building_tiles.h"
10 #include "map/elevation.h"
11 #include "map/grid.h"
12 #include "map/image_context.h"
13 #include "map/property.h"
14 #include "map/routing.h"
15 #include "map/routing_terrain.h"
16 #include "map/tiles.h"
17 #include "map/terrain.h"
18 #include "scenario/editor_events.h"
19 #include "scenario/editor_map.h"
20 #include "city/warning.h"
21 #include "widget/minimap.h"
22 
23 #define TERRAIN_PAINT_MASK ~(TERRAIN_TREE | TERRAIN_ROCK | TERRAIN_WATER | TERRAIN_BUILDING |\
24                             TERRAIN_SHRUB | TERRAIN_GARDEN | TERRAIN_ROAD | TERRAIN_MEADOW)
25 
26 static struct {
27     int active;
28     tool_type type;
29     int id;
30     int brush_size;
31     int build_in_progress;
32     int start_elevation;
33     map_tile start_tile;
34 } data = {0, TOOL_GRASS, 0, 3, 0};
35 
editor_tool_type(void)36 tool_type editor_tool_type(void)
37 {
38     return data.type;
39 }
40 
editor_tool_is_active(void)41 int editor_tool_is_active(void)
42 {
43     return data.active;
44 }
45 
editor_tool_deactivate(void)46 void editor_tool_deactivate(void)
47 {
48     if (editor_tool_is_updatable() && data.build_in_progress) {
49         game_undo_restore_map(1);
50         data.build_in_progress = 0;
51     } else {
52         data.active = 0;
53     }
54 }
55 
editor_tool_set_type(tool_type type)56 void editor_tool_set_type(tool_type type)
57 {
58     editor_tool_set_with_id(type, 0);
59 }
60 
editor_tool_set_with_id(tool_type type,int id)61 void editor_tool_set_with_id(tool_type type, int id)
62 {
63     data.active = 1;
64     data.type = type;
65     data.id = id;
66 }
67 
editor_tool_brush_size(void)68 int editor_tool_brush_size(void)
69 {
70     return data.brush_size;
71 }
72 
editor_tool_set_brush_size(int size)73 void editor_tool_set_brush_size(int size)
74 {
75     data.brush_size = size;
76 }
77 
editor_tool_foreach_brush_tile(void (* callback)(const void * user_data,int dx,int dy),const void * user_data)78 void editor_tool_foreach_brush_tile(void (*callback)(const void *user_data, int dx, int dy), const void *user_data)
79 {
80     if (data.type == TOOL_RAISE_LAND || data.type == TOOL_LOWER_LAND) {
81         for (int dy = -1; dy <= 1; dy++) {
82             for (int dx = -1; dx <= 1; dx++) {
83                 callback(user_data, dx, dy);
84             }
85         }
86     } else {
87         for (int dy = -data.brush_size + 1; dy < data.brush_size; dy++) {
88             for (int dx = -data.brush_size + 1; dx < data.brush_size; dx++) {
89                 int steps = (dx < 0 ? -dx : dx) + (dy < 0 ? -dy : dy);
90                 if (steps < data.brush_size) {
91                     callback(user_data, dx, dy);
92                 }
93             }
94         }
95     }
96 }
97 
editor_tool_is_updatable(void)98 int editor_tool_is_updatable(void)
99 {
100     return data.type == TOOL_ROAD;
101 }
102 
editor_tool_is_in_use(void)103 int editor_tool_is_in_use(void)
104 {
105     return data.build_in_progress;
106 }
107 
editor_tool_start_use(const map_tile * tile)108 void editor_tool_start_use(const map_tile *tile)
109 {
110     if (!data.active) {
111         return;
112     }
113     data.build_in_progress = 1;
114     data.start_elevation = map_elevation_at(tile->grid_offset);
115     data.start_tile = *tile;
116     if (data.type == TOOL_ROAD) {
117         game_undo_start_build(BUILDING_ROAD);
118         map_routing_update_land();
119     }
120 }
121 
editor_tool_is_brush(void)122 int editor_tool_is_brush(void)
123 {
124     switch (data.type) {
125         case TOOL_GRASS:
126         case TOOL_TREES:
127         case TOOL_WATER:
128         case TOOL_SHRUB:
129         case TOOL_ROCKS:
130         case TOOL_MEADOW:
131         case TOOL_RAISE_LAND:
132         case TOOL_LOWER_LAND:
133             return 1;
134         default:
135             return 0;
136     }
137 }
138 
raise_land_tile(int x,int y,int grid_offset,int terrain)139 static int raise_land_tile(int x, int y, int grid_offset, int terrain)
140 {
141     int elevation = map_elevation_at(grid_offset);
142     if (elevation < 5 && elevation == data.start_elevation) {
143         if (!(terrain & (TERRAIN_ACCESS_RAMP | TERRAIN_ELEVATION))) {
144             map_property_set_multi_tile_size(grid_offset, 1);
145             map_elevation_set(grid_offset, elevation + 1);
146             terrain &= ~(TERRAIN_WATER | TERRAIN_BUILDING | TERRAIN_GARDEN | TERRAIN_ROAD);
147         }
148     }
149     return terrain;
150 }
151 
lower_land_tile(int x,int y,int grid_offset,int terrain)152 static int lower_land_tile(int x, int y, int grid_offset, int terrain)
153 {
154     if (terrain & TERRAIN_ACCESS_RAMP) {
155         terrain |= TERRAIN_ELEVATION;
156         terrain &= ~(TERRAIN_ACCESS_RAMP);
157         map_property_set_multi_tile_size(grid_offset, 1);
158         map_property_set_multi_tile_xy(grid_offset, 0, 0, 1);
159     }
160     int elevation = map_elevation_at(grid_offset);
161     if (elevation <= 0) {
162         terrain &= ~(TERRAIN_ELEVATION);
163     } else if (elevation == data.start_elevation) {
164         terrain &= ~(TERRAIN_ELEVATION | TERRAIN_ACCESS_RAMP);
165         map_elevation_set(grid_offset, elevation - 1);
166     }
167     return terrain;
168 }
169 
add_terrain(const void * tile_data,int dx,int dy)170 static void add_terrain(const void *tile_data, int dx, int dy)
171 {
172     const map_tile *tile = (const map_tile *) tile_data;
173     int x = tile->x + dx;
174     int y = tile->y + dy;
175     if (!map_grid_is_inside(x, y, 1)) {
176         return;
177     }
178     int grid_offset = tile->grid_offset + map_grid_delta(dx, dy);
179     int terrain = map_terrain_get(grid_offset);
180     if (terrain & TERRAIN_BUILDING) {
181         map_building_tiles_remove(0, x, y);
182         terrain = map_terrain_get(grid_offset);
183     }
184     switch (data.type) {
185         case TOOL_GRASS:
186             terrain &= TERRAIN_PAINT_MASK;
187             break;
188         case TOOL_TREES:
189             if (!(terrain & TERRAIN_TREE)) {
190                 terrain &= TERRAIN_PAINT_MASK;
191                 terrain |= TERRAIN_TREE;
192             }
193             break;
194         case TOOL_ROCKS:
195             if (!(terrain & TERRAIN_ROCK)) {
196                 terrain &= TERRAIN_PAINT_MASK;
197                 terrain |= TERRAIN_ROCK;
198             }
199             break;
200         case TOOL_WATER:
201             if (!map_elevation_at(grid_offset) && !(terrain & TERRAIN_WATER)) {
202                 terrain &= TERRAIN_PAINT_MASK;
203                 terrain |= TERRAIN_WATER;
204             }
205             break;
206         case TOOL_SHRUB:
207             if (!(terrain & TERRAIN_SHRUB)) {
208                 terrain &= TERRAIN_PAINT_MASK;
209                 terrain |= TERRAIN_SHRUB;
210             }
211             break;
212         case TOOL_MEADOW:
213             if (!(terrain & TERRAIN_MEADOW)) {
214                 terrain &= TERRAIN_PAINT_MASK;
215                 terrain |= TERRAIN_MEADOW;
216             }
217             break;
218         case TOOL_RAISE_LAND:
219             terrain = raise_land_tile(x, y, grid_offset, terrain);
220             break;
221         case TOOL_LOWER_LAND:
222             terrain = lower_land_tile(x, y, grid_offset, terrain);
223             break;
224         default:
225             break;
226     }
227     map_terrain_set(grid_offset, terrain);
228 }
229 
editor_tool_update_use(const map_tile * tile)230 void editor_tool_update_use(const map_tile *tile)
231 {
232     if (!data.build_in_progress) {
233         return;
234     }
235     if (data.type == TOOL_ROAD) {
236         building_construction_place_road(1, data.start_tile.x, data.start_tile.y, tile->x, tile->y);
237         return;
238     }
239     if (!editor_tool_is_brush()) {
240         return;
241     }
242 
243     editor_tool_foreach_brush_tile(add_terrain, tile);
244 
245     int x_min = tile->x - data.brush_size;
246     int x_max = tile->x + data.brush_size;
247     int y_min = tile->y - data.brush_size;
248     int y_max = tile->y + data.brush_size;
249     switch (data.type) {
250         case TOOL_GRASS:
251             map_image_context_reset_water();
252             map_tiles_update_region_water(x_min, y_min, x_max, y_max);
253             map_tiles_update_all_rocks();
254             map_tiles_update_region_empty_land(x_min, y_min, x_max, y_max);
255             map_tiles_update_region_meadow(x_min, y_min, x_max, y_max);
256             break;
257         case TOOL_TREES:
258             map_image_context_reset_water();
259             map_tiles_update_region_water(x_min, y_min, x_max, y_max);
260             map_tiles_update_all_rocks();
261             map_tiles_update_region_trees(x_min, y_min, x_max, y_max);
262             break;
263         case TOOL_WATER:
264         case TOOL_ROCKS:
265             map_image_context_reset_water();
266             map_tiles_update_all_rocks();
267             map_tiles_update_region_water(x_min, y_min, x_max, y_max);
268             break;
269         case TOOL_SHRUB:
270             map_image_context_reset_water();
271             map_tiles_update_region_water(x_min, y_min, x_max, y_max);
272             map_tiles_update_all_rocks();
273             map_tiles_update_region_shrub(x_min, y_min, x_max, y_max);
274             break;
275         case TOOL_MEADOW:
276             map_image_context_reset_water();
277             map_tiles_update_region_water(x_min, y_min, x_max, y_max);
278             map_tiles_update_all_rocks();
279             map_tiles_update_region_meadow(x_min, y_min, x_max, y_max);
280             break;
281         case TOOL_RAISE_LAND:
282         case TOOL_LOWER_LAND:
283             map_image_context_reset_water();
284             map_image_context_reset_elevation();
285             map_tiles_update_all_elevation();
286             map_tiles_update_region_water(x_min, y_min, x_max, y_max);
287             map_tiles_update_region_trees(x_min, y_min, x_max, y_max);
288             map_tiles_update_region_shrub(x_min, y_min, x_max, y_max);
289             map_tiles_update_all_rocks();
290             map_tiles_update_region_empty_land(x_min, y_min, x_max, y_max);
291             map_tiles_update_region_meadow(x_min, y_min, x_max, y_max);
292             break;
293         default:
294             break;
295     }
296 
297     scenario_editor_updated_terrain();
298     widget_minimap_invalidate();
299 }
300 
place_earthquake_flag(const map_tile * tile)301 static void place_earthquake_flag(const map_tile *tile)
302 {
303     int warning = 0;
304     if (editor_tool_can_place_flag(data.type, tile, &warning)) {
305         if (scenario_editor_earthquake_severity()) {
306             scenario_editor_set_earthquake_point(tile->x, tile->y);
307         } else {
308             city_warning_show(WARNING_EDITOR_NO_EARTHQUAKE_SCHEDULED);
309         }
310     } else {
311         city_warning_show(warning);
312     }
313 }
314 
place_flag(const map_tile * tile,void (* update)(int x,int y))315 static void place_flag(const map_tile *tile, void (*update)(int x, int y))
316 {
317     int warning = 0;
318     if (editor_tool_can_place_flag(data.type, tile, &warning)) {
319         update(tile->x, tile->y);
320     } else {
321         city_warning_show(warning);
322     }
323 }
324 
place_flag_with_id(const map_tile * tile,void (* update)(int id,int x,int y))325 static void place_flag_with_id(const map_tile *tile, void (*update)(int id, int x, int y))
326 {
327     int warning = 0;
328     if (editor_tool_can_place_flag(data.type, tile, &warning)) {
329         update(data.id, tile->x, tile->y);
330     } else {
331         city_warning_show(warning);
332     }
333 }
334 
place_building(const map_tile * tile)335 static void place_building(const map_tile *tile)
336 {
337     int image_id;
338     int size;
339     building_type type;
340     switch (data.type) {
341         case TOOL_NATIVE_HUT:
342             type = BUILDING_NATIVE_HUT;
343             image_id = image_group(GROUP_EDITOR_BUILDING_NATIVE) + (random_byte() & 1);
344             size = 1;
345             break;
346         case TOOL_NATIVE_CENTER:
347             type = BUILDING_NATIVE_MEETING;
348             image_id = image_group(GROUP_EDITOR_BUILDING_NATIVE) + 2;
349             size = 2;
350             break;
351         case TOOL_NATIVE_FIELD:
352             type = BUILDING_NATIVE_CROPS;
353             image_id = image_group(GROUP_EDITOR_BUILDING_CROPS);
354             size = 1;
355             break;
356         default:
357             return;
358     }
359 
360     if (editor_tool_can_place_building(tile, size * size, 0)) {
361         building *b = building_create(type, tile->x, tile->y);
362         map_building_tiles_add(b->id, tile->x, tile->y, size, image_id, TERRAIN_BUILDING);
363         scenario_editor_updated_terrain();
364     } else {
365         city_warning_show(WARNING_EDITOR_CANNOT_PLACE);
366     }
367 }
368 
update_terrain_after_elevation_changes(void)369 static void update_terrain_after_elevation_changes(void)
370 {
371     map_elevation_remove_cliffs();
372 
373     map_image_context_reset_water();
374     map_image_context_reset_elevation();
375     map_tiles_update_all_elevation();
376     map_tiles_update_all_rocks();
377     map_tiles_update_all_empty_land();
378     map_tiles_update_all_meadow();
379 
380     scenario_editor_updated_terrain();
381 }
382 
place_access_ramp(const map_tile * tile)383 static void place_access_ramp(const map_tile *tile)
384 {
385     int orientation = 0;
386     if (editor_tool_can_place_access_ramp(tile, &orientation)) {
387         int terrain_mask = ~(TERRAIN_ROCK | TERRAIN_WATER | TERRAIN_BUILDING | TERRAIN_GARDEN | TERRAIN_AQUEDUCT);
388         for (int dy = 0; dy < 2; dy++) {
389             for (int dx = 0; dx < 2; dx++) {
390                 int grid_offset = tile->grid_offset + map_grid_delta(dx, dy);
391                 map_terrain_set(grid_offset, map_terrain_get(grid_offset) & terrain_mask);
392             }
393         }
394         map_building_tiles_add(0, tile->x, tile->y, 2,
395             image_group(GROUP_TERRAIN_ACCESS_RAMP) + orientation, TERRAIN_ACCESS_RAMP);
396 
397         update_terrain_after_elevation_changes();
398         scenario_editor_updated_terrain();
399     } else {
400         city_warning_show(WARNING_EDITOR_CANNOT_PLACE);
401     }
402 }
403 
place_road(const map_tile * start_tile,const map_tile * end_tile)404 static void place_road(const map_tile *start_tile, const map_tile *end_tile)
405 {
406     if (building_construction_place_road(0, start_tile->x, start_tile->y, end_tile->x, end_tile->y)) {
407         scenario_editor_updated_terrain();
408     }
409 }
410 
editor_tool_end_use(const map_tile * tile)411 void editor_tool_end_use(const map_tile *tile)
412 {
413     if (!data.build_in_progress) {
414         return;
415     }
416     data.build_in_progress = 0;
417     if (!tile->grid_offset) {
418         return;
419     }
420     switch (data.type) {
421         case TOOL_EARTHQUAKE_POINT:
422             place_earthquake_flag(tile);
423             break;
424         case TOOL_ENTRY_POINT:
425             place_flag(tile, scenario_editor_set_entry_point);
426             break;
427         case TOOL_EXIT_POINT:
428             place_flag(tile, scenario_editor_set_exit_point);
429             break;
430         case TOOL_RIVER_ENTRY_POINT:
431             place_flag(tile, scenario_editor_set_river_entry_point);
432             break;
433         case TOOL_RIVER_EXIT_POINT:
434             place_flag(tile, scenario_editor_set_river_exit_point);
435             break;
436         case TOOL_INVASION_POINT:
437             place_flag_with_id(tile, scenario_editor_set_invasion_point);
438             break;
439         case TOOL_FISHING_POINT:
440             place_flag_with_id(tile, scenario_editor_set_fishing_point);
441             break;
442         case TOOL_HERD_POINT:
443             place_flag_with_id(tile, scenario_editor_set_herd_point);
444             break;
445         case TOOL_NATIVE_CENTER:
446         case TOOL_NATIVE_FIELD:
447         case TOOL_NATIVE_HUT:
448             place_building(tile);
449             break;
450         case TOOL_RAISE_LAND:
451         case TOOL_LOWER_LAND:
452             update_terrain_after_elevation_changes();
453             break;
454         case TOOL_ACCESS_RAMP:
455             place_access_ramp(tile);
456             break;
457         case TOOL_ROAD:
458             place_road(&data.start_tile, tile);
459             break;
460         default:
461             break;
462     }
463 }
464