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