1 /***********************************************************************
2 Freeciv - Copyright (C) 2005 - The Freeciv Project
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
13
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17
18 #include <stdarg.h>
19 #include <string.h>
20
21 /* utility */
22 #include "bitvector.h"
23 #include "fcintl.h"
24 #include "log.h"
25 #include "support.h"
26
27 /* common */
28 #include "game.h"
29 #include "map.h"
30 #include "movement.h"
31 #include "packets.h"
32
33 /* client */
34 #include "client_main.h"
35 #include "climap.h"
36 #include "control.h"
37 #include "editor.h"
38 #include "mapctrl_common.h"
39 #include "tilespec.h"
40 #include "zoom.h"
41
42 /* client/include */
43 #include "editgui_g.h"
44 #include "mapview_g.h"
45
46
47 enum selection_modes {
48 SELECTION_MODE_NEW = 0,
49 SELECTION_MODE_ADD,
50 SELECTION_MODE_REMOVE
51 };
52
53 enum editor_tool_flags {
54 ETF_NO_FLAGS = 0,
55 ETF_HAS_VALUE = 1<<0,
56 ETF_HAS_SIZE = 1<<1,
57 ETF_HAS_COUNT = 1<<2,
58 ETF_HAS_APPLIED_PLAYER = 1<<3,
59 ETF_HAS_VALUE_ERASE = 1<<4
60 };
61
62 struct edit_buffer {
63 int type_flags;
64 struct tile_list *vtiles;
65 const struct tile *origin;
66 };
67
68 struct editor_tool {
69 int flags;
70 enum editor_tool_mode mode;
71 int size;
72 int count;
73 int applied_player_no;
74 const char *name;
75 int value;
76 const char *tooltip;
77 };
78
79 struct editor_state {
80 enum editor_tool_type tool;
81 struct editor_tool tools[NUM_EDITOR_TOOL_TYPES];
82
83 const struct tile *current_tile;
84 bool tool_active;
85
86 bool selrect_active;
87 int selrect_start_x;
88 int selrect_start_y;
89 int selrect_x;
90 int selrect_y;
91 int selrect_width;
92 int selrect_height;
93
94 enum selection_modes selection_mode;
95
96 struct tile_hash *selected_tile_table;
97 struct edit_buffer *copybuf;
98 };
99
100 static struct editor_state *editor = NULL;
101
102 /****************************************************************************
103 Set tool to some value legal under current ruleset.
104 ****************************************************************************/
tool_set_init_value(enum editor_tool_type ett)105 static void tool_set_init_value(enum editor_tool_type ett)
106 {
107 struct editor_tool *tool = editor->tools + ett;
108
109 if (ett == ETT_TERRAIN_SPECIAL
110 || ett == ETT_ROAD
111 || ett == ETT_MILITARY_BASE) {
112 struct extra_type *first = NULL;
113
114 extra_type_iterate(pextra) {
115 if (ett == ETT_ROAD) {
116 if (is_extra_caused_by(pextra, EC_ROAD)) {
117 first = pextra;
118 break;
119 }
120 } else if (ett == ETT_MILITARY_BASE) {
121 if (is_extra_caused_by(pextra, EC_BASE)) {
122 first = pextra;
123 break;
124 }
125 } else {
126 /* Considers extras that are neither bases or roads, specials */
127 first = pextra;
128 break;
129 }
130 } extra_type_iterate_end;
131
132 if (first != NULL) {
133 tool->value = extra_index(first);
134 } else {
135 tool->value = 0;
136 }
137 } else {
138 tool->value = 0;
139 }
140 }
141
142 /****************************************************************************
143 Initialize editor tool data.
144 ****************************************************************************/
tool_init(enum editor_tool_type ett,const char * name,int flags,const char * tooltip)145 static void tool_init(enum editor_tool_type ett, const char *name,
146 int flags, const char *tooltip)
147 {
148 struct editor_tool *tool;
149
150 if (!editor || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)) {
151 return;
152 }
153
154 tool = editor->tools + ett;
155
156 if (ett == ETT_COPYPASTE) {
157 tool->mode = ETM_COPY;
158 } else {
159 tool->mode = ETM_PAINT;
160 }
161 tool->name = name;
162 tool->flags = flags;
163 tool->tooltip = tooltip;
164 tool->size = 1;
165 tool->count = 1;
166 tool->applied_player_no = 0;
167
168 tool_set_init_value(ett);
169 }
170
171 /****************************************************************************
172 Adjust editor for changed ruleset.
173 ****************************************************************************/
editor_ruleset_changed(void)174 void editor_ruleset_changed(void)
175 {
176 int t;
177
178 for (t = 0; t < NUM_EDITOR_TOOL_TYPES; t++) {
179 tool_set_init_value(t);
180 }
181 }
182
183 /****************************************************************************
184 Initialize the client's editor state information to some suitable default
185 values. This only needs to be done once at client start.
186 ****************************************************************************/
editor_init(void)187 void editor_init(void)
188 {
189 fc_assert(editor == NULL);
190
191 editor = fc_calloc(1, sizeof(struct editor_state));
192
193 tool_init(ETT_TERRAIN, _("Terrain"),
194 ETF_HAS_VALUE | ETF_HAS_SIZE,
195 _("Change tile terrain.\nShortcut: t\n"
196 "Select terrain type: shift+t or right-click here."));
197 tool_init(ETT_TERRAIN_RESOURCE, _("Terrain Resource"),
198 ETF_HAS_VALUE | ETF_HAS_SIZE,
199 _("Change tile terrain resources.\nShortcut: r\n"
200 "Select resource type: shift+r or right-click here."));
201 tool_init(ETT_TERRAIN_SPECIAL, _("Terrain Special"), ETF_HAS_VALUE
202 | ETF_HAS_SIZE | ETF_HAS_VALUE_ERASE,
203 _("Modify tile specials.\nShortcut: s\n"
204 "Select special type: shift+s or right-click here."));
205 tool_init(ETT_ROAD, _("Road"), ETF_HAS_VALUE
206 | ETF_HAS_SIZE | ETF_HAS_VALUE_ERASE,
207 _("Modify roads on tile.\nShortcut: p\n"
208 "Select road type: shift+p or right-click here."));
209 tool_init(ETT_MILITARY_BASE, _("Military Base"), ETF_HAS_VALUE
210 | ETF_HAS_SIZE | ETF_HAS_VALUE_ERASE,
211 _("Create a military base.\nShortcut: m\n"
212 "Select base type: shift+m or right-click here."));
213 tool_init(ETT_UNIT, _("Unit"), ETF_HAS_VALUE | ETF_HAS_COUNT
214 | ETF_HAS_APPLIED_PLAYER | ETF_HAS_VALUE_ERASE,
215 _("Create unit.\nShortcut: u\nSelect unit "
216 "type: shift+u or right-click here."));
217 tool_init(ETT_CITY, _("City"), ETF_HAS_SIZE | ETF_HAS_APPLIED_PLAYER,
218 _("Create city.\nShortcut: c"));
219 tool_init(ETT_VISION, _("Vision"), ETF_HAS_SIZE,
220 _("Modify player's tile knowledge.\nShortcut: v"));
221 tool_init(ETT_STARTPOS, _("Start Position"), ETF_NO_FLAGS,
222 _("Place a start position which allows any nation to "
223 "start at the tile. To allow only certain nations to "
224 "start there, middle click on the start position on "
225 "the map and use the property editor.\nShortcut: b"));
226
227 tool_init(ETT_COPYPASTE, _("Copy/Paste"), ETF_HAS_SIZE,
228 _("Copy and paste tiles.\n"
229 "Shortcut for copy mode: shift-c\n"
230 "Shoftcut for paste mode: shift-v"));
231 editor->copybuf = edit_buffer_new(EBT_ALL);
232
233 editor->selected_tile_table = tile_hash_new();
234 tile_hash_set_no_shrink(editor->selected_tile_table, TRUE);
235 }
236
237 /****************************************************************************
238 Clear the editor data which is game dependent.
239 ****************************************************************************/
editor_clear(void)240 void editor_clear(void)
241 {
242 fc_assert_ret(editor != NULL);
243
244 edit_buffer_clear(editor->copybuf);
245 tile_hash_clear(editor->selected_tile_table);
246 }
247
248 /****************************************************************************
249 Free the client's editor.
250 ****************************************************************************/
editor_free(void)251 void editor_free(void)
252 {
253 if (editor != NULL) {
254 edit_buffer_free(editor->copybuf);
255 tile_hash_destroy(editor->selected_tile_table);
256 free(editor);
257 editor = NULL;
258 }
259 }
260
261 /****************************************************************************
262 Set the current tool to be used by the editor.
263 ****************************************************************************/
editor_set_tool(enum editor_tool_type ett)264 void editor_set_tool(enum editor_tool_type ett)
265 {
266 if (editor == NULL) {
267 return;
268 }
269
270 if (!(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)) {
271 return;
272 }
273
274 editor->tool = ett;
275 }
276
277 /****************************************************************************
278 Get the current tool used by the editor.
279 ****************************************************************************/
editor_get_tool(void)280 enum editor_tool_type editor_get_tool(void)
281 {
282 if (editor == NULL) {
283 return NUM_EDITOR_TOOL_TYPES;
284 }
285
286 return editor->tool;
287 }
288
289 /****************************************************************************
290 Set the mode for the editor tool.
291 ****************************************************************************/
editor_tool_set_mode(enum editor_tool_type ett,enum editor_tool_mode etm)292 void editor_tool_set_mode(enum editor_tool_type ett,
293 enum editor_tool_mode etm)
294 {
295 if (editor == NULL || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)
296 || !(0 <= etm && etm < NUM_EDITOR_TOOL_MODES)
297 || !editor_tool_has_mode(ett, etm)) {
298 return;
299 }
300
301 editor->tools[ett].mode = etm;
302 }
303
304 /****************************************************************************
305 Return TRUE if the given tool supports the given mode.
306 ****************************************************************************/
editor_tool_has_mode(enum editor_tool_type ett,enum editor_tool_mode etm)307 bool editor_tool_has_mode(enum editor_tool_type ett,
308 enum editor_tool_mode etm)
309 {
310 if (editor == NULL || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)
311 || !(0 <= etm && etm < NUM_EDITOR_TOOL_MODES)) {
312 return FALSE;
313 }
314
315 if (etm == ETM_COPY || etm == ETM_PASTE) {
316 return ett == ETT_COPYPASTE;
317 }
318
319 if (ett == ETT_COPYPASTE) {
320 return etm == ETM_COPY || etm == ETM_PASTE;
321 }
322
323 return TRUE;
324 }
325
326 /****************************************************************************
327 Get the mode for the tool.
328 ****************************************************************************/
editor_tool_get_mode(enum editor_tool_type ett)329 enum editor_tool_mode editor_tool_get_mode(enum editor_tool_type ett)
330 {
331 if (editor == NULL || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)) {
332 return NUM_EDITOR_TOOL_MODES;
333 }
334 return editor->tools[ett].mode;
335 }
336
337 /****************************************************************************
338 Returns TRUE if the *client* is in edit mode.
339 ****************************************************************************/
editor_is_active(void)340 bool editor_is_active(void)
341 {
342 return can_conn_edit(&client.conn);
343 }
344
345 /****************************************************************************
346 Returns TRUE if the given tool should be made available to the user via
347 the editor GUI. For example, this will return FALSE for ETT_MILITARY_BASE
348 if there are no bases defined in the ruleset.
349
350 NB: This depends on the ruleset information received from the server, so
351 it will return FALSE if the client does not have it yet.
352 ****************************************************************************/
editor_tool_is_usable(enum editor_tool_type ett)353 bool editor_tool_is_usable(enum editor_tool_type ett)
354 {
355 if (!editor || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)) {
356 return FALSE;
357 }
358
359 switch (ett) {
360 case ETT_MILITARY_BASE:
361 return base_count() > 0;
362 case ETT_ROAD:
363 return road_count() > 0;
364 case ETT_TERRAIN_RESOURCE:
365 return resource_count() > 0;
366 case ETT_UNIT:
367 return utype_count() > 0;
368 default:
369 break;
370 }
371 return TRUE;
372 }
373
374 /****************************************************************************
375 Returns TRUE if the given tool type has sub-values (e.g. the terrain
376 tool has values corresponding to the terrain types).
377 ****************************************************************************/
editor_tool_has_value(enum editor_tool_type ett)378 bool editor_tool_has_value(enum editor_tool_type ett)
379 {
380 if (!editor || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)) {
381 return FALSE;
382 }
383 return editor->tools[ett].flags & ETF_HAS_VALUE;
384 }
385
386 /****************************************************************************
387 Set the value ID for the given tool. How the value is interpreted depends
388 on the tool type.
389 ****************************************************************************/
editor_tool_set_value(enum editor_tool_type ett,int value)390 void editor_tool_set_value(enum editor_tool_type ett, int value)
391 {
392 if (!editor || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)
393 || !editor_tool_has_value(ett)) {
394 return;
395 }
396 editor->tools[ett].value = value;
397 }
398
399 /****************************************************************************
400 Get the current tool sub-value.
401 ****************************************************************************/
editor_tool_get_value(enum editor_tool_type ett)402 int editor_tool_get_value(enum editor_tool_type ett)
403 {
404 if (!editor || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)
405 || !editor_tool_has_value(ett)) {
406 return 0;
407 }
408
409 return editor->tools[ett].value;
410 }
411
412 /****************************************************************************
413 Record the start of the selection rectangle.
414 ****************************************************************************/
editor_start_selection_rectangle(int canvas_x,int canvas_y)415 static void editor_start_selection_rectangle(int canvas_x, int canvas_y)
416 {
417 if (!editor) {
418 return;
419 }
420
421 if (editor->selection_mode == SELECTION_MODE_NEW
422 && editor_selection_count() > 0) {
423 editor_selection_clear();
424 update_map_canvas_visible();
425 }
426
427 editor->selrect_start_x = canvas_x;
428 editor->selrect_start_y = canvas_y;
429 editor->selrect_width = 0;
430 editor->selrect_height = 0;
431 editor->selrect_active = TRUE;
432 }
433
434 /****************************************************************************
435 Temporary convenience function to work-around the fact that certain
436 special values (S_RESOURCE_VALID) do not in fact
437 correspond to drawable special types.
438 ****************************************************************************/
tile_really_has_any_specials(const struct tile * ptile)439 static inline bool tile_really_has_any_specials(const struct tile *ptile)
440 {
441 if (!ptile) {
442 return FALSE;
443 }
444
445 extra_type_by_cause_iterate(EC_SPECIAL, pextra) {
446 if (tile_has_extra(ptile, pextra)) {
447 return TRUE;
448 }
449 } extra_type_by_cause_iterate_end;
450
451 return FALSE;
452 }
453
454 /****************************************************************************
455 Set the editor's current applied player number according to what exists
456 on the given tile.
457 ****************************************************************************/
editor_grab_applied_player(const struct tile * ptile)458 static void editor_grab_applied_player(const struct tile *ptile)
459 {
460 int apno = -1;
461
462 if (!editor || !ptile) {
463 return;
464 }
465
466 if (client_has_player()
467 && tile_get_known(ptile, client_player()) == TILE_UNKNOWN) {
468 return;
469 }
470
471 if (tile_city(ptile) != NULL) {
472 apno = player_number(city_owner(tile_city(ptile)));
473 } else if (unit_list_size(ptile->units) > 0) {
474 struct unit *punit = unit_list_get(ptile->units, 0);
475 apno = player_number(unit_owner(punit));
476 } else if (tile_owner(ptile) != NULL) {
477 apno = player_number(tile_owner(ptile));
478 }
479
480 if (player_by_number(apno) != NULL) {
481 editor_tool_set_applied_player(editor_get_tool(), apno);
482 editgui_refresh();
483 }
484 }
485
486 /****************************************************************************
487 Set the editor's current tool according to what exists at the given tile.
488 ****************************************************************************/
editor_grab_tool(const struct tile * ptile)489 static void editor_grab_tool(const struct tile *ptile)
490 {
491 int ett = -1, value = 0;
492 struct extra_type *first_base = NULL;
493 struct extra_type *first_road = NULL;
494
495 if (!editor) {
496 return;
497 }
498
499 if (!ptile) {
500 return;
501 }
502
503 extra_type_by_cause_iterate(EC_BASE, pextra) {
504 if (tile_has_extra(ptile, pextra)) {
505 first_base = pextra;
506 break;
507 }
508 } extra_type_by_cause_iterate_end;
509
510 extra_type_by_cause_iterate(EC_ROAD, pextra) {
511 if (tile_has_extra(ptile, pextra)) {
512 first_road = pextra;
513 break;
514 }
515 } extra_type_by_cause_iterate_end;
516
517 if (client_has_player()
518 && tile_get_known(ptile, client_player()) == TILE_UNKNOWN) {
519 ett = ETT_VISION;
520
521 } else if (tile_city(ptile)) {
522 ett = ETT_CITY;
523
524 } else if (unit_list_size(ptile->units) > 0) {
525 int max_score = 0, score;
526 struct unit *grabbed_punit = NULL;
527
528 unit_list_iterate(ptile->units, punit) {
529 if (uclass_has_flag(unit_class_get(punit), UCF_UNREACHABLE)) {
530 score = 5;
531 } else if (utype_move_type(unit_type_get(punit)) == UMT_LAND) {
532 score = 4;
533 } else if (utype_move_type(unit_type_get(punit)) == UMT_SEA) {
534 score = 3;
535 } else {
536 score = 2;
537 }
538 if (unit_transported(punit)) {
539 score = 1;
540 }
541
542 if (score > max_score) {
543 max_score = score;
544 grabbed_punit = punit;
545 }
546 } unit_list_iterate_end;
547
548 if (grabbed_punit) {
549 ett = ETT_UNIT;
550 value = utype_number(unit_type_get(grabbed_punit));
551 }
552 } else if (first_base != NULL) {
553 ett = ETT_MILITARY_BASE;
554 value = extra_index(first_base);
555
556 } else if (first_road != NULL) {
557 ett = ETT_ROAD;
558 value = extra_index(first_road);
559
560 } else if (tile_really_has_any_specials(ptile)) {
561 struct extra_type *specials_array[MAX_EXTRA_TYPES];
562 int count = 0, i;
563 struct extra_type *special = NULL;
564
565 extra_type_by_cause_iterate(EC_SPECIAL, s) {
566 specials_array[count++] = s;
567 } extra_type_by_cause_iterate_end;
568
569 /* Grab specials in reverse order of enum tile_special_type. */
570
571 for (i = count - 1; i >= 0; i--) {
572 if (tile_has_extra(ptile, specials_array[i])) {
573 special = specials_array[i];
574 break;
575 }
576 }
577
578 if (special != NULL) {
579 ett = ETT_TERRAIN_SPECIAL;
580 value = extra_index(special);
581 }
582 } else if (tile_resource(ptile) != NULL) {
583 ett = ETT_TERRAIN_RESOURCE;
584 value = resource_number(tile_resource(ptile));
585
586 } else if (tile_terrain(ptile) != NULL) {
587 ett = ETT_TERRAIN;
588 value = terrain_number(tile_terrain(ptile));
589 }
590
591 if (ett < 0) {
592 return;
593 }
594
595 editor_set_tool(ett);
596 if (editor_tool_has_value(ett)) {
597 editor_tool_set_value(ett, value);
598 }
599 editgui_refresh();
600 }
601
602 /****************************************************************************
603 Returns TRUE if the given tile has some objects with editable properties.
604 ****************************************************************************/
can_edit_tile_properties(struct tile * ptile)605 static inline bool can_edit_tile_properties(struct tile *ptile)
606 {
607 return ptile != NULL;
608 }
609
610 /****************************************************************************
611 Handle a request to edit the properties for the given tile. If the tile
612 is part of a selection, then all selected tiles are passed to the
613 property editor.
614 ****************************************************************************/
popup_properties(struct tile * ptile)615 static void popup_properties(struct tile *ptile)
616 {
617 struct tile_list *tiles;
618
619 if (!ptile) {
620 return;
621 }
622
623 tiles = tile_list_new();
624
625 if (editor_tile_is_selected(ptile)) {
626 tile_hash_iterate(editor->selected_tile_table, sel_tile) {
627 if (can_edit_tile_properties(sel_tile)) {
628 tile_list_append(tiles, sel_tile);
629 }
630 } tile_hash_iterate_end;
631 } else {
632 if (can_edit_tile_properties(ptile)) {
633 tile_list_append(tiles, ptile);
634 }
635 }
636
637 editgui_popup_properties(tiles, NUM_OBJTYPES);
638
639 tile_list_destroy(tiles);
640 }
641
642 /****************************************************************************
643 Handle a user's mouse button press at the given point on the map canvas.
644 ****************************************************************************/
editor_mouse_button_press(int canvas_x,int canvas_y,int button,int modifiers)645 void editor_mouse_button_press(int canvas_x, int canvas_y,
646 int button, int modifiers)
647 {
648 struct tile *ptile;
649
650 if (editor == NULL) {
651 return;
652 }
653
654 ptile = canvas_pos_to_tile(canvas_x, canvas_y);
655 if (ptile == NULL) {
656 return;
657 }
658
659 switch (button) {
660
661 case MOUSE_BUTTON_LEFT:
662 if (modifiers == EKM_SHIFT) {
663 editor_grab_tool(ptile);
664 } else if (modifiers == EKM_CTRL) {
665 editor_grab_applied_player(ptile);
666 } else if (modifiers == EKM_NONE) {
667 editor->tool_active = TRUE;
668 editor_apply_tool(ptile, FALSE);
669 editor_notify_edit_finished();
670 editor_set_current_tile(ptile);
671 }
672 break;
673
674 case MOUSE_BUTTON_RIGHT:
675 if (modifiers == (EKM_ALT | EKM_CTRL)) {
676 popup_properties(ptile);
677 break;
678 }
679
680 if (modifiers == EKM_SHIFT) {
681 editor->selection_mode = SELECTION_MODE_ADD;
682 } else if (modifiers == EKM_ALT) {
683 editor->selection_mode = SELECTION_MODE_REMOVE;
684 } else if (modifiers == EKM_NONE) {
685 editor->selection_mode = SELECTION_MODE_NEW;
686 } else {
687 break;
688 }
689 editor_start_selection_rectangle(canvas_x, canvas_y);
690 break;
691
692 case MOUSE_BUTTON_MIDDLE:
693 if (modifiers == EKM_NONE) {
694 popup_properties(ptile);
695 }
696 break;
697
698 default:
699 break;
700 }
701 }
702
703 /****************************************************************************
704 Record and handle the end of the selection rectangle.
705 ****************************************************************************/
editor_end_selection_rectangle(int canvas_x,int canvas_y)706 static void editor_end_selection_rectangle(int canvas_x, int canvas_y)
707 {
708 int w, h;
709
710 if (!editor) {
711 return;
712 }
713
714 editor->selrect_active = FALSE;
715
716 if (editor->selrect_width <= 0 || editor->selrect_height <= 0) {
717 struct tile *ptile;
718
719 ptile = canvas_pos_to_tile(canvas_x, canvas_y);
720 if (ptile && editor->selection_mode == SELECTION_MODE_ADD) {
721 editor_selection_add(ptile);
722 } else if (ptile && editor->selection_mode == SELECTION_MODE_REMOVE) {
723 editor_selection_remove(ptile);
724 } else {
725 recenter_button_pressed(canvas_x, canvas_y);
726 return;
727 }
728
729 if (ptile) {
730 refresh_tile_mapcanvas(ptile, TRUE, TRUE);
731 }
732
733 return;
734 }
735
736 gui_rect_iterate(mapview.gui_x0 + editor->selrect_x,
737 mapview.gui_y0 + editor->selrect_y,
738 editor->selrect_width, editor->selrect_height,
739 ptile, pedge, pcorner, map_zoom) {
740 if (ptile == NULL) {
741 continue;
742 }
743 if (editor->selection_mode == SELECTION_MODE_NEW
744 || editor->selection_mode == SELECTION_MODE_ADD) {
745 editor_selection_add(ptile);
746 } else if (editor->selection_mode == SELECTION_MODE_REMOVE) {
747 editor_selection_remove(ptile);
748 }
749 } gui_rect_iterate_end;
750
751 w = tileset_tile_width(tileset);
752 h = tileset_tile_height(tileset);
753
754 update_map_canvas(editor->selrect_x - w,
755 editor->selrect_y - h,
756 editor->selrect_width + 2 * w,
757 editor->selrect_height + 2 * h);
758 flush_dirty();
759 }
760
761 /****************************************************************************
762 Draws the editor selection rectangle using draw_selection_rectangle().
763 ****************************************************************************/
editor_draw_selrect(void)764 static void editor_draw_selrect(void)
765 {
766 if (!editor) {
767 return;
768 }
769
770 if (editor->selrect_active && editor->selrect_width > 0
771 && editor->selrect_height > 0) {
772 draw_selection_rectangle(editor->selrect_x,
773 editor->selrect_y,
774 editor->selrect_width,
775 editor->selrect_height);
776 }
777 }
778
779 /****************************************************************************
780 Handle the release of a mouse button click.
781 ****************************************************************************/
editor_mouse_button_release(int canvas_x,int canvas_y,int button,int modifiers)782 void editor_mouse_button_release(int canvas_x, int canvas_y,
783 int button, int modifiers)
784 {
785 switch (button) {
786
787 case MOUSE_BUTTON_LEFT:
788 editor_set_current_tile(NULL);
789 editor->tool_active = FALSE;
790 break;
791
792 case MOUSE_BUTTON_RIGHT:
793 if (editor->selrect_active) {
794 editor_end_selection_rectangle(canvas_x, canvas_y);
795 }
796 break;
797
798 case MOUSE_BUTTON_MIDDLE:
799 break;
800
801 default:
802 break;
803 }
804 }
805
806 /****************************************************************************
807 Handle a change in the size of the selection rectangle. The given point
808 is the new extremity of the rectangle.
809 ****************************************************************************/
editor_resize_selection_rectangle(int canvas_x,int canvas_y)810 static void editor_resize_selection_rectangle(int canvas_x, int canvas_y)
811 {
812 int xl, yt, xr, yb;
813
814 if (editor->selrect_start_x <= canvas_x) {
815 xl = editor->selrect_start_x;
816 xr = canvas_x;
817 } else {
818 xl = canvas_x;
819 xr = editor->selrect_start_x;
820 }
821
822 if (editor->selrect_start_y <= canvas_y) {
823 yt = editor->selrect_start_y;
824 yb = canvas_y;
825 } else {
826 yt = canvas_y;
827 yb = editor->selrect_start_y;
828 }
829
830 /* Erase the previously drawn rectangle. */
831 editor_draw_selrect();
832
833 if (xl == xr || yt == yb) {
834 editor->selrect_width = 0;
835 editor->selrect_height = 0;
836 return;
837 }
838
839 editor->selrect_x = xl;
840 editor->selrect_y = yt;
841 editor->selrect_width = xr - xl;
842 editor->selrect_height = yb - yt;
843
844 editor_draw_selrect();
845 }
846
847 /****************************************************************************
848 Handle the mouse moving over the map canvas.
849 ****************************************************************************/
editor_mouse_move(int canvas_x,int canvas_y,int modifiers)850 void editor_mouse_move(int canvas_x, int canvas_y, int modifiers)
851 {
852 const struct tile *ptile, *old;
853
854 if (!editor) {
855 return;
856 }
857
858 old = editor_get_current_tile();
859 ptile = canvas_pos_to_tile(canvas_x, canvas_y);
860
861 if (!ptile) {
862 return;
863 }
864
865 if (editor->tool_active && old != NULL && old != ptile) {
866 editor_apply_tool(ptile, FALSE);
867 editor_notify_edit_finished();
868 editor_set_current_tile(ptile);
869 }
870
871 if (editor->selrect_active) {
872 editor_resize_selection_rectangle(canvas_x, canvas_y);
873 }
874 }
875
876 /****************************************************************************
877 Notify the server that a batch of edits has completed. This is used as
878 a hint for the server to now do any checks it has saved while the batch
879 was being processed.
880 ****************************************************************************/
editor_notify_edit_finished(void)881 void editor_notify_edit_finished(void)
882 {
883 send_packet_edit_check_tiles(&client.conn);
884 }
885
886 /****************************************************************************
887 Apply the current editor tool to the given tile. This function is
888 suitable to called over multiple tiles at once. Once the batch of
889 operations is finished you should call editor_notify_edit_finished.
890 The 'part_of_selection' parameter should be TRUE if the tool is
891 being applied to a tile from a selection.
892 ****************************************************************************/
editor_apply_tool(const struct tile * ptile,bool part_of_selection)893 void editor_apply_tool(const struct tile *ptile,
894 bool part_of_selection)
895 {
896 enum editor_tool_type ett;
897 enum editor_tool_mode etm;
898 int value, size, count, apno, tile, id;
899 bool erase;
900 struct connection *my_conn = &client.conn;
901
902 if (editor == NULL || ptile == NULL) {
903 return;
904 }
905
906 ett = editor_get_tool();
907 etm = editor_tool_get_mode(ett);
908 size = editor_tool_get_size(ett);
909 count = editor_tool_get_count(ett);
910 value = editor_tool_get_value(ett);
911 apno = editor_tool_get_applied_player(ett);
912
913 if (ett != ETT_VISION && !client_is_global_observer()
914 && client_has_player()
915 && tile_get_known(ptile, client_player()) == TILE_UNKNOWN) {
916 return;
917 }
918
919 if (editor_tool_has_applied_player(ett)
920 && player_by_number(apno) == NULL) {
921 return;
922 }
923
924 if (ett == ETT_COPYPASTE) {
925 struct edit_buffer *ebuf;
926 ebuf = editor_get_copy_buffer();
927 if (etm == ETM_COPY) {
928 if (part_of_selection) {
929 edit_buffer_copy(ebuf, ptile);
930 } else {
931 edit_buffer_clear(ebuf);
932 edit_buffer_copy_square(ebuf, ptile, size);
933 editgui_refresh();
934 }
935 } else if (etm == ETM_PAINT || etm == ETM_PASTE) {
936 edit_buffer_paste(ebuf, ptile);
937 }
938 return;
939 }
940
941 if (part_of_selection && ett != ETT_CITY) {
942 size = 1;
943 }
944
945 erase = (etm == ETM_ERASE);
946 tile = tile_index(ptile);
947
948 switch (ett) {
949
950 case ETT_TERRAIN:
951 dsend_packet_edit_tile_terrain(my_conn, tile, erase ? 0 : value, size);
952 break;
953
954 case ETT_TERRAIN_RESOURCE:
955 dsend_packet_edit_tile_resource(my_conn, tile,
956 erase ? resource_count() : value,
957 size);
958 break;
959
960 case ETT_TERRAIN_SPECIAL:
961 case ETT_ROAD:
962 case ETT_MILITARY_BASE:
963 dsend_packet_edit_tile_extra(my_conn, tile, value, erase, size);
964 break;
965
966 case ETT_UNIT:
967 if (erase) {
968 dsend_packet_edit_unit_remove(my_conn, apno, tile, value, count);
969 } else {
970 dsend_packet_edit_unit_create(my_conn, apno, tile, value, count, 0);
971 }
972 break;
973
974 case ETT_CITY:
975 if (erase) {
976 struct city *pcity = tile_city(ptile);
977 if (pcity != NULL) {
978 id = pcity->id;
979 dsend_packet_edit_city_remove(my_conn, id);
980 }
981 } else {
982 dsend_packet_edit_city_create(my_conn, apno, tile, size, 0);
983 }
984 break;
985
986 case ETT_VISION:
987 if (client_has_player()) {
988 id = client_player_number();
989 dsend_packet_edit_player_vision(my_conn, id, tile, !erase, size);
990 }
991 break;
992
993 case ETT_STARTPOS:
994 dsend_packet_edit_startpos(my_conn, tile, erase, 0);
995 break;
996
997 default:
998 break;
999 }
1000 }
1001
1002 /****************************************************************************
1003 Sets the tile currently assumed to be under the user's mouse pointer.
1004 ****************************************************************************/
editor_set_current_tile(const struct tile * ptile)1005 void editor_set_current_tile(const struct tile *ptile)
1006 {
1007 if (editor == NULL) {
1008 return;
1009 }
1010
1011 editor->current_tile = ptile;
1012 }
1013
1014 /****************************************************************************
1015 Get the tile that the user's mouse pointer is currently over.
1016 ****************************************************************************/
editor_get_current_tile(void)1017 const struct tile *editor_get_current_tile(void)
1018 {
1019 if (editor == NULL) {
1020 return NULL;
1021 }
1022
1023 return editor->current_tile;
1024 }
1025
1026 /****************************************************************************
1027 Toggle the current tool mode between the given mode and ETM_PAINT (or
1028 ETM_COPY for the copy & paste tool).
1029 ****************************************************************************/
editor_tool_toggle_mode(enum editor_tool_type ett,enum editor_tool_mode etm)1030 void editor_tool_toggle_mode(enum editor_tool_type ett,
1031 enum editor_tool_mode etm)
1032 {
1033 if (!editor_tool_has_mode(ett, etm)) {
1034 return;
1035 }
1036 if (editor_tool_get_mode(ett) == etm) {
1037 editor_tool_set_mode(ett, ett == ETT_COPYPASTE
1038 ? ETM_COPY : ETM_PAINT);
1039 } else {
1040 editor_tool_set_mode(ett, etm);
1041 }
1042 }
1043
1044 /****************************************************************************
1045 Set the editor tool mode to the next available mode.
1046 ****************************************************************************/
editor_tool_cycle_mode(enum editor_tool_type ett)1047 void editor_tool_cycle_mode(enum editor_tool_type ett)
1048 {
1049 int mode, count;
1050 bool found = FALSE;
1051
1052 mode = editor_tool_get_mode(ett);
1053 if (!(0 <= mode && mode < NUM_EDITOR_TOOL_MODES)) {
1054 return;
1055 }
1056
1057 for (count = 0; count < NUM_EDITOR_TOOL_MODES; count++) {
1058 mode = (mode + 1) % NUM_EDITOR_TOOL_MODES;
1059 if (editor_tool_has_mode(ett, mode)) {
1060 found = TRUE;
1061 break;
1062 }
1063 }
1064
1065 if (found) {
1066 editor_tool_set_mode(ett, mode);
1067 }
1068 }
1069
1070 /****************************************************************************
1071 Unselect all selected tiles.
1072 ****************************************************************************/
editor_selection_clear(void)1073 void editor_selection_clear(void)
1074 {
1075 if (!editor) {
1076 return;
1077 }
1078 tile_hash_clear(editor->selected_tile_table);
1079 }
1080
1081 /****************************************************************************
1082 Add the given tile to the current selection.
1083 ****************************************************************************/
editor_selection_add(const struct tile * ptile)1084 void editor_selection_add(const struct tile *ptile)
1085 {
1086 if (!editor || !ptile) {
1087 return;
1088 }
1089 tile_hash_insert(editor->selected_tile_table, ptile, NULL);
1090 }
1091
1092 /****************************************************************************
1093 Remove the given tile from the current selection.
1094 ****************************************************************************/
editor_selection_remove(const struct tile * ptile)1095 void editor_selection_remove(const struct tile *ptile)
1096 {
1097 if (!editor || !ptile) {
1098 return;
1099 }
1100 tile_hash_remove(editor->selected_tile_table, ptile);
1101 }
1102
1103 /****************************************************************************
1104 Returns TRUE if the given tile is selected.
1105 ****************************************************************************/
editor_tile_is_selected(const struct tile * ptile)1106 bool editor_tile_is_selected(const struct tile *ptile)
1107 {
1108 if (!editor || !ptile) {
1109 return FALSE;
1110 }
1111 return tile_hash_lookup(editor->selected_tile_table, ptile, NULL);
1112 }
1113
1114 /****************************************************************************
1115 Apply the current editor tool to all tiles in the current selection.
1116 ****************************************************************************/
editor_apply_tool_to_selection(void)1117 void editor_apply_tool_to_selection(void)
1118 {
1119 enum editor_tool_type ett;
1120
1121 if (!editor || editor_selection_count() <= 0) {
1122 return;
1123 }
1124
1125 ett = editor_get_tool();
1126 if (editor_tool_get_mode(ett) == ETM_COPY) {
1127 struct edit_buffer *ebuf;
1128 ebuf = editor_get_copy_buffer();
1129 edit_buffer_clear(ebuf);
1130 edit_buffer_set_origin(ebuf, editor_get_selection_center());
1131 }
1132
1133 connection_do_buffer(&client.conn);
1134 tile_hash_iterate(editor->selected_tile_table, ptile) {
1135 editor_apply_tool(ptile, TRUE);
1136 } tile_hash_iterate_end;
1137 editor_notify_edit_finished();
1138 connection_do_unbuffer(&client.conn);
1139
1140 if (editor_tool_get_mode(ett) == ETM_COPY) {
1141 editgui_refresh();
1142 }
1143 }
1144
1145 /****************************************************************************
1146 Get the translated name of the given tool type.
1147 ****************************************************************************/
editor_tool_get_name(enum editor_tool_type ett)1148 const char *editor_tool_get_name(enum editor_tool_type ett)
1149 {
1150 if (!editor || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)) {
1151 return "";
1152 }
1153
1154 return editor->tools[ett].name;
1155 }
1156
1157 /****************************************************************************
1158 Get the translated name of the given tool value. If no such name exists,
1159 returns an empty string.
1160 ****************************************************************************/
editor_tool_get_value_name(enum editor_tool_type emt,int value)1161 const char *editor_tool_get_value_name(enum editor_tool_type emt, int value)
1162 {
1163 struct terrain *pterrain;
1164 struct resource *presource;
1165 struct unit_type *putype;
1166 struct extra_type *pextra;
1167
1168 if (!editor) {
1169 return "";
1170 }
1171
1172 switch (emt) {
1173 case ETT_TERRAIN:
1174 pterrain = terrain_by_number(value);
1175 return pterrain ? terrain_name_translation(pterrain) : "";
1176 break;
1177 case ETT_TERRAIN_RESOURCE:
1178 presource = resource_by_number(value);
1179 return presource ? resource_name_translation(presource) : "";
1180 break;
1181 case ETT_TERRAIN_SPECIAL:
1182 case ETT_ROAD:
1183 case ETT_MILITARY_BASE:
1184 pextra = extra_by_number(value);
1185 return pextra != NULL ? extra_name_translation(pextra) : "";
1186 break;
1187 case ETT_UNIT:
1188 putype = utype_by_number(value);
1189 return putype ? utype_name_translation(putype) : "";
1190 break;
1191 default:
1192 break;
1193 }
1194 return "";
1195 }
1196
1197 /****************************************************************************
1198 Return TRUE if the given editor tool uses the 'size' parameter.
1199 ****************************************************************************/
editor_tool_has_size(enum editor_tool_type ett)1200 bool editor_tool_has_size(enum editor_tool_type ett)
1201 {
1202 if (!editor || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)) {
1203 return FALSE;
1204 }
1205 return editor->tools[ett].flags & ETF_HAS_SIZE;
1206 }
1207
1208 /****************************************************************************
1209 Returns the current size parameter for the given editor tools.
1210 ****************************************************************************/
editor_tool_get_size(enum editor_tool_type ett)1211 int editor_tool_get_size(enum editor_tool_type ett)
1212 {
1213 if (!editor || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)) {
1214 return 1;
1215 }
1216 return editor->tools[ett].size;
1217 }
1218
1219 /****************************************************************************
1220 Sets the size parameter for the given tool.
1221 ****************************************************************************/
editor_tool_set_size(enum editor_tool_type ett,int size)1222 void editor_tool_set_size(enum editor_tool_type ett, int size)
1223 {
1224 if (!editor || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)) {
1225 return;
1226 }
1227 editor->tools[ett].size = MAX(1, size);
1228 }
1229
1230 /****************************************************************************
1231 Return TRUE if it is meaningful for the given tool to use the 'count'
1232 parameter.
1233 ****************************************************************************/
editor_tool_has_count(enum editor_tool_type ett)1234 bool editor_tool_has_count(enum editor_tool_type ett)
1235 {
1236 if (!editor || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)) {
1237 return FALSE;
1238 }
1239 return editor->tools[ett].flags & ETF_HAS_COUNT;
1240 }
1241
1242 /****************************************************************************
1243 Returns the 'count' parameter for the editor tool.
1244 ****************************************************************************/
editor_tool_get_count(enum editor_tool_type ett)1245 int editor_tool_get_count(enum editor_tool_type ett)
1246 {
1247 if (!editor || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)) {
1248 return 1;
1249 }
1250 return editor->tools[ett].count;
1251 }
1252
1253 /****************************************************************************
1254 Sets the 'count' parameter of the tool to the given value.
1255 ****************************************************************************/
editor_tool_set_count(enum editor_tool_type ett,int count)1256 void editor_tool_set_count(enum editor_tool_type ett, int count)
1257 {
1258 if (!editor || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)) {
1259 return;
1260 }
1261 editor->tools[ett].count = MAX(1, count);
1262 }
1263
1264 /****************************************************************************
1265 Returns a sprite containing an icon for the given tool type. Returns
1266 NULL if no such sprite exists.
1267 ****************************************************************************/
editor_tool_get_sprite(enum editor_tool_type ett)1268 struct sprite *editor_tool_get_sprite(enum editor_tool_type ett)
1269 {
1270 const struct editor_sprites *sprites;
1271
1272 if (!tileset || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)) {
1273 return NULL;
1274 }
1275
1276 sprites = get_editor_sprites(tileset);
1277 if (!sprites) {
1278 return NULL;
1279 }
1280
1281 switch (ett) {
1282 case ETT_COPYPASTE:
1283 return sprites->copypaste;
1284 break;
1285 case ETT_TERRAIN:
1286 return sprites->terrain;
1287 break;
1288 case ETT_TERRAIN_RESOURCE:
1289 return sprites->terrain_resource;
1290 break;
1291 case ETT_TERRAIN_SPECIAL:
1292 return sprites->terrain_special;
1293 break;
1294 case ETT_ROAD:
1295 return sprites->road;
1296 case ETT_MILITARY_BASE:
1297 return sprites->military_base;
1298 case ETT_UNIT:
1299 return sprites->unit;
1300 break;
1301 case ETT_CITY:
1302 return sprites->city;
1303 break;
1304 case ETT_VISION:
1305 return sprites->vision;
1306 break;
1307 case ETT_STARTPOS:
1308 return sprites->startpos;
1309 break;
1310 default:
1311 break;
1312 }
1313
1314 return NULL;
1315 }
1316
1317 /****************************************************************************
1318 Returns a translated "tooltip" description for the given tool type.
1319 ****************************************************************************/
editor_tool_get_tooltip(enum editor_tool_type ett)1320 const char *editor_tool_get_tooltip(enum editor_tool_type ett)
1321 {
1322 if (!editor || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)
1323 || !editor->tools[ett].tooltip) {
1324 return "";
1325 }
1326 return editor->tools[ett].tooltip;
1327 }
1328
1329 /****************************************************************************
1330 Returns the current applied player number for the editor tool.
1331
1332 May return a player number for which player_by_number returns NULL.
1333 ****************************************************************************/
editor_tool_get_applied_player(enum editor_tool_type ett)1334 int editor_tool_get_applied_player(enum editor_tool_type ett)
1335 {
1336 if (!editor || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)) {
1337 return -1;
1338 }
1339 return editor->tools[ett].applied_player_no;
1340 }
1341
1342 /****************************************************************************
1343 Sets the editor tool's applied player number to the given value.
1344 ****************************************************************************/
editor_tool_set_applied_player(enum editor_tool_type ett,int player_no)1345 void editor_tool_set_applied_player(enum editor_tool_type ett,
1346 int player_no)
1347 {
1348 if (!editor || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)) {
1349 return;
1350 }
1351 editor->tools[ett].applied_player_no = player_no;
1352 }
1353
1354 /****************************************************************************
1355 Returns TRUE if the given tool makes use of the editor's applied player
1356 number.
1357 ****************************************************************************/
editor_tool_has_applied_player(enum editor_tool_type ett)1358 bool editor_tool_has_applied_player(enum editor_tool_type ett)
1359 {
1360 if (!editor || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)) {
1361 return FALSE;
1362 }
1363 return editor->tools[ett].flags & ETF_HAS_APPLIED_PLAYER;
1364 }
1365
1366 /****************************************************************************
1367 Returns TRUE if erase mode for the given tool erases by sub-value instead
1368 of any object corresponding to the tool type.
1369 ****************************************************************************/
editor_tool_has_value_erase(enum editor_tool_type ett)1370 bool editor_tool_has_value_erase(enum editor_tool_type ett)
1371 {
1372 if (!editor || !(0 <= ett && ett < NUM_EDITOR_TOOL_TYPES)) {
1373 return FALSE;
1374 }
1375 return editor->tools[ett].flags & ETF_HAS_VALUE_ERASE;
1376 }
1377
1378 /****************************************************************************
1379 Returns the number of currently selected tiles.
1380 ****************************************************************************/
editor_selection_count(void)1381 int editor_selection_count(void)
1382 {
1383 if (!editor) {
1384 return 0;
1385 }
1386 return tile_hash_size(editor->selected_tile_table);
1387 }
1388
1389 /****************************************************************************
1390 Creates a virtual unit (like unit_virtual_create) based on the current
1391 editor state. You should free() the unit when it is no longer needed.
1392 If creation is not possible, then NULL is returned.
1393
1394 The virtual unit has no homecity or tile. It is owned by the player
1395 corresponding to the current 'applied player' parameter and has unit type
1396 given by the sub-value of the unit tool (ETT_UNIT).
1397 ****************************************************************************/
editor_unit_virtual_create(void)1398 struct unit *editor_unit_virtual_create(void)
1399 {
1400 struct unit *vunit;
1401 struct player *pplayer;
1402 struct unit_type *putype;
1403 int apno, value;
1404
1405 value = editor_tool_get_value(ETT_UNIT);
1406 putype = utype_by_number(value);
1407
1408 if (!putype) {
1409 return NULL;
1410 }
1411
1412 apno = editor_tool_get_applied_player(ETT_UNIT);
1413 pplayer = player_by_number(apno);
1414 if (!pplayer) {
1415 return NULL;
1416 }
1417
1418 vunit = unit_virtual_create(pplayer, NULL, putype, 0);
1419
1420 return vunit;
1421 }
1422
1423 /****************************************************************************
1424 Create a new edit buffer corresponding to all types set in 'type_flags'.
1425 ****************************************************************************/
edit_buffer_new(int type_flags)1426 struct edit_buffer *edit_buffer_new(int type_flags)
1427 {
1428 struct edit_buffer *ebuf;
1429
1430 if (!(0 <= type_flags && type_flags <= EBT_ALL)) {
1431 return NULL;
1432 }
1433
1434 ebuf = fc_calloc(1, sizeof(*ebuf));
1435 ebuf->type_flags = type_flags;
1436 ebuf->vtiles = tile_list_new();
1437
1438 return ebuf;
1439 }
1440
1441 /****************************************************************************
1442 Free all memory allocated for the edit buffer.
1443 ****************************************************************************/
edit_buffer_free(struct edit_buffer * ebuf)1444 void edit_buffer_free(struct edit_buffer *ebuf)
1445 {
1446 if (!ebuf) {
1447 return;
1448 }
1449
1450 if (ebuf->vtiles) {
1451 tile_list_iterate(ebuf->vtiles, vtile) {
1452 tile_virtual_destroy(vtile);
1453 } tile_list_iterate_end;
1454 tile_list_destroy(ebuf->vtiles);
1455 ebuf->vtiles = NULL;
1456 }
1457 free(ebuf);
1458 }
1459
1460 /****************************************************************************
1461 Remove all copy data stored in the edit buffer.
1462 ****************************************************************************/
edit_buffer_clear(struct edit_buffer * ebuf)1463 void edit_buffer_clear(struct edit_buffer *ebuf)
1464 {
1465 if (!ebuf || !ebuf->vtiles) {
1466 return;
1467 }
1468
1469 tile_list_iterate(ebuf->vtiles, vtile) {
1470 tile_virtual_destroy(vtile);
1471 } tile_list_iterate_end;
1472 tile_list_clear(ebuf->vtiles);
1473
1474 edit_buffer_set_origin(ebuf, NULL);
1475 }
1476
1477 /****************************************************************************
1478 Copy from a square region of half-width 'radius' centered around 'center'
1479 into the buffer.
1480 ****************************************************************************/
edit_buffer_copy_square(struct edit_buffer * ebuf,const struct tile * center,int radius)1481 void edit_buffer_copy_square(struct edit_buffer *ebuf,
1482 const struct tile *center,
1483 int radius)
1484 {
1485 if (!ebuf || !center || radius < 1) {
1486 return;
1487 }
1488
1489 edit_buffer_set_origin(ebuf, center);
1490 square_iterate(center, radius - 1, ptile) {
1491 edit_buffer_copy(ebuf, ptile);
1492 } square_iterate_end;
1493 }
1494
1495 /****************************************************************************
1496 Append a single tile to the copy buffer.
1497 ****************************************************************************/
edit_buffer_copy(struct edit_buffer * ebuf,const struct tile * ptile)1498 void edit_buffer_copy(struct edit_buffer *ebuf, const struct tile *ptile)
1499 {
1500 struct tile *vtile;
1501 struct unit *vunit;
1502 bool copied = FALSE;
1503
1504 if (!ebuf || !ptile) {
1505 return;
1506 }
1507
1508 vtile = tile_virtual_new(NULL);
1509 vtile->index = tile_index(ptile);
1510
1511 edit_buffer_type_iterate(ebuf, type) {
1512 switch (type) {
1513 case EBT_TERRAIN:
1514 if (tile_terrain(ptile)) {
1515 tile_set_terrain(vtile, tile_terrain(ptile));
1516 copied = TRUE;
1517 }
1518 break;
1519 case EBT_RESOURCE:
1520 if (tile_resource(ptile)) {
1521 tile_set_resource(vtile, tile_resource(ptile));
1522 copied = TRUE;
1523 }
1524 break;
1525 case EBT_SPECIAL:
1526 extra_type_by_cause_iterate(EC_SPECIAL, pextra) {
1527 if (tile_has_extra(ptile, pextra)) {
1528 tile_add_extra(vtile, pextra);
1529 copied = TRUE;
1530 }
1531 } extra_type_by_cause_iterate_end;
1532 break;
1533 case EBT_BASE:
1534 extra_type_iterate(pextra) {
1535 if (tile_has_extra(ptile, pextra)
1536 && is_extra_caused_by(pextra, EC_BASE)) {
1537 tile_add_extra(vtile, pextra);
1538 copied = TRUE;
1539 }
1540 } extra_type_iterate_end;
1541 break;
1542 case EBT_ROAD:
1543 extra_type_iterate(pextra) {
1544 if (tile_has_extra(ptile, pextra)
1545 && is_extra_caused_by(pextra, EC_ROAD)) {
1546 tile_add_extra(vtile, pextra);
1547 copied = TRUE;
1548 }
1549 } extra_type_iterate_end;
1550 break;
1551 case EBT_UNIT:
1552 unit_list_iterate(ptile->units, punit) {
1553 if (!punit) {
1554 continue;
1555 }
1556 vunit = unit_virtual_create(unit_owner(punit), NULL,
1557 unit_type_get(punit), punit->veteran);
1558 vunit->homecity = punit->homecity;
1559 vunit->hp = punit->hp;
1560 unit_list_append(vtile->units, vunit);
1561 copied = TRUE;
1562 } unit_list_iterate_end;
1563 break;
1564 case EBT_CITY:
1565 if (tile_city(ptile)) {
1566 struct city *pcity, *vcity;
1567 char name[MAX_LEN_NAME];
1568
1569 pcity = tile_city(ptile);
1570 fc_snprintf(name, sizeof(name), "Copy of %s",
1571 city_name_get(pcity));
1572 vcity = create_city_virtual(city_owner(pcity), NULL, name);
1573 city_size_set(vcity, city_size_get(pcity));
1574 improvement_iterate(pimprove) {
1575 if (!is_improvement(pimprove)
1576 || !city_has_building(pcity, pimprove)) {
1577 continue;
1578 }
1579 city_add_improvement(vcity, pimprove);
1580 } improvement_iterate_end;
1581 tile_set_worked(vtile, vcity);
1582 copied = TRUE;
1583 }
1584 break;
1585 default:
1586 break;
1587 }
1588 } edit_buffer_type_iterate_end;
1589
1590 if (copied) {
1591 tile_list_append(ebuf->vtiles, vtile);
1592 } else {
1593 tile_virtual_destroy(vtile);
1594 }
1595 }
1596
1597 /****************************************************************************
1598 Helper function to fill in an edit packet with the tile's current values.
1599 ****************************************************************************/
fill_tile_edit_packet(struct packet_edit_tile * packet,const struct tile * ptile)1600 static void fill_tile_edit_packet(struct packet_edit_tile *packet,
1601 const struct tile *ptile)
1602 {
1603 const struct resource *presource;
1604 const struct terrain *pterrain;
1605
1606 if (!packet || !ptile) {
1607 return;
1608 }
1609 packet->tile = tile_index(ptile);
1610 packet->extras = *tile_extras(ptile);
1611
1612 presource = tile_resource(ptile);
1613 packet->resource = presource
1614 ? resource_number(presource)
1615 : resource_count();
1616
1617 pterrain = tile_terrain(ptile);
1618 packet->terrain = pterrain
1619 ? terrain_number(pterrain)
1620 : terrain_count();
1621
1622 if (ptile->label == NULL) {
1623 packet->label[0] = '\0';
1624 } else {
1625 sz_strlcpy(packet->label, ptile->label);
1626 }
1627 }
1628
1629 /****************************************************************************
1630 Helper function for edit_buffer_paste(). Do a single paste of the stuff set
1631 in the buffer on the virtual tile to the destination tile 'ptile_dest'.
1632 ****************************************************************************/
paste_tile(struct edit_buffer * ebuf,const struct tile * vtile,const struct tile * ptile_dest)1633 static void paste_tile(struct edit_buffer *ebuf,
1634 const struct tile *vtile,
1635 const struct tile *ptile_dest)
1636 {
1637 struct connection *my_conn = &client.conn;
1638 struct packet_edit_tile tile_packet;
1639 struct city *vcity;
1640 int value, owner, tile;
1641 bool send_edit_tile = FALSE;
1642
1643 if (!ebuf || !vtile || !ptile_dest) {
1644 return;
1645 }
1646
1647 tile = tile_index(ptile_dest);
1648
1649 fill_tile_edit_packet(&tile_packet, ptile_dest);
1650
1651 edit_buffer_type_iterate(ebuf, type) {
1652 switch (type) {
1653 case EBT_TERRAIN:
1654 if (!tile_terrain(vtile)) {
1655 continue;
1656 }
1657 value = terrain_number(tile_terrain(vtile));
1658 dsend_packet_edit_tile_terrain(my_conn, tile, value, 1);
1659 break;
1660 case EBT_RESOURCE:
1661 if (!tile_resource(vtile)) {
1662 continue;
1663 }
1664 value = resource_number(tile_resource(vtile));
1665 dsend_packet_edit_tile_resource(my_conn, tile, value, 1);
1666 break;
1667 case EBT_SPECIAL:
1668 extra_type_by_cause_iterate(EC_SPECIAL, pextra) {
1669 if (tile_has_extra(vtile, pextra)) {
1670 BV_SET(tile_packet.extras, extra_index(pextra));
1671 send_edit_tile = TRUE;
1672 }
1673 } extra_type_by_cause_iterate_end;
1674 break;
1675 case EBT_BASE:
1676 extra_type_iterate(pextra) {
1677 if (tile_has_extra(vtile, pextra)
1678 && is_extra_caused_by(pextra, EC_BASE)) {
1679 BV_SET(tile_packet.extras, extra_index(pextra));
1680 send_edit_tile = TRUE;
1681 }
1682 } extra_type_iterate_end;
1683 break;
1684 case EBT_ROAD:
1685 extra_type_iterate(pextra) {
1686 if (tile_has_extra(vtile, pextra)
1687 && is_extra_caused_by(pextra, EC_ROAD)) {
1688 BV_SET(tile_packet.extras, extra_index(pextra));
1689 send_edit_tile = TRUE;
1690 }
1691 } extra_type_iterate_end;
1692 break;
1693 case EBT_UNIT:
1694 unit_list_iterate(vtile->units, vunit) {
1695 value = utype_number(unit_type_get(vunit));
1696 owner = player_number(unit_owner(vunit));
1697 dsend_packet_edit_unit_create(my_conn, owner, tile, value, 1, 0);
1698 } unit_list_iterate_end;
1699 break;
1700 case EBT_CITY:
1701 vcity = tile_city(vtile);
1702 if (!vcity) {
1703 continue;
1704 }
1705 owner = player_number(city_owner(vcity));
1706 value = city_size_get(vcity);
1707 dsend_packet_edit_city_create(my_conn, owner, tile, value, 0);
1708 break;
1709 default:
1710 break;
1711 }
1712 } edit_buffer_type_iterate_end;
1713
1714 if (send_edit_tile) {
1715 send_packet_edit_tile(my_conn, &tile_packet);
1716 }
1717 }
1718
1719 /****************************************************************************
1720 Paste the entire contents of the edit buffer using 'dest' as the origin.
1721 ****************************************************************************/
edit_buffer_paste(struct edit_buffer * ebuf,const struct tile * dest)1722 void edit_buffer_paste(struct edit_buffer *ebuf, const struct tile *dest)
1723 {
1724 struct connection *my_conn = &client.conn;
1725 const struct tile *origin, *ptile;
1726 int dx, dy;
1727
1728 if (!ebuf || !dest) {
1729 return;
1730 }
1731
1732 /* Calculate vector. */
1733 origin = edit_buffer_get_origin(ebuf);
1734 fc_assert_ret(origin != NULL);
1735 map_distance_vector(&dx, &dy, origin, dest);
1736
1737 connection_do_buffer(my_conn);
1738 tile_list_iterate(ebuf->vtiles, vtile) {
1739 int virt_x, virt_y;
1740
1741 index_to_map_pos(&virt_x, &virt_y, tile_index(vtile));
1742 ptile = map_pos_to_tile(virt_x + dx, virt_y + dy);
1743 if (!ptile) {
1744 continue;
1745 }
1746 paste_tile(ebuf, vtile, ptile);
1747 } tile_list_iterate_end;
1748 connection_do_unbuffer(my_conn);
1749 }
1750
1751 /****************************************************************************
1752 Returns the copy buffer for the given tool.
1753 ****************************************************************************/
editor_get_copy_buffer(void)1754 struct edit_buffer *editor_get_copy_buffer(void)
1755 {
1756 if (!editor) {
1757 return NULL;
1758 }
1759 return editor->copybuf;
1760 }
1761
1762 /****************************************************************************
1763 Returns the translated string name for the given mode.
1764 ****************************************************************************/
editor_tool_get_mode_name(enum editor_tool_type ett,enum editor_tool_mode etm)1765 const char *editor_tool_get_mode_name(enum editor_tool_type ett,
1766 enum editor_tool_mode etm)
1767 {
1768 bool value_erase;
1769
1770 value_erase = editor_tool_has_value_erase(ett);
1771
1772 switch (etm) {
1773 case ETM_PAINT:
1774 return _("Paint");
1775 break;
1776 case ETM_ERASE:
1777 if (value_erase) {
1778 return _("Erase Value");
1779 } else {
1780 return _("Erase");
1781 }
1782 break;
1783 case ETM_COPY:
1784 return _("Copy");
1785 break;
1786 case ETM_PASTE:
1787 return _("Paste");
1788 break;
1789 default:
1790 log_error("Unrecognized editor tool mode %d "
1791 "in editor_tool_get_mode_name().", etm);
1792 break;
1793 }
1794
1795 return "";
1796 }
1797
1798 /****************************************************************************
1799 Returns a translated tooltip string assumed to be used for the toggle
1800 button for this tool mode in the editor gui.
1801 ****************************************************************************/
editor_get_mode_tooltip(enum editor_tool_mode etm)1802 const char *editor_get_mode_tooltip(enum editor_tool_mode etm)
1803 {
1804 switch (etm) {
1805 case ETM_ERASE:
1806 return _("Toggle erase mode.\nShortcut: shift-d");
1807 break;
1808 case ETM_COPY:
1809 return _("Toggle copy mode.\nShortcut: shift-c");
1810 break;
1811 case ETM_PASTE:
1812 return _("Toggle paste mode.\nShortcut: shift-v");
1813 break;
1814 default:
1815 break;
1816 }
1817
1818 return NULL;
1819 }
1820
1821 /****************************************************************************
1822 Returns the editor sprite corresponding to the tool mode.
1823 ****************************************************************************/
editor_get_mode_sprite(enum editor_tool_mode etm)1824 struct sprite *editor_get_mode_sprite(enum editor_tool_mode etm)
1825 {
1826 const struct editor_sprites *sprites;
1827
1828 sprites = get_editor_sprites(tileset);
1829 if (!sprites) {
1830 return NULL;
1831 }
1832
1833 switch (etm) {
1834 case ETM_PAINT:
1835 return sprites->brush;
1836 break;
1837 case ETM_ERASE:
1838 return sprites->erase;
1839 break;
1840 case ETM_COPY:
1841 return sprites->copy;
1842 break;
1843 case ETM_PASTE:
1844 return sprites->paste;
1845 break;
1846 default:
1847 break;
1848 }
1849
1850 return NULL;
1851 }
1852
1853 /****************************************************************************
1854 Fill the supplied buffer with a translated string describing the edit
1855 buffer's current state. Returns the number of bytes used.
1856 ****************************************************************************/
edit_buffer_get_status_string(const struct edit_buffer * ebuf,char * buf,int buflen)1857 int edit_buffer_get_status_string(const struct edit_buffer *ebuf,
1858 char *buf, int buflen)
1859 {
1860 int ret, total;
1861 const char *fmt;
1862
1863 if (!buf || buflen < 1) {
1864 return 0;
1865 }
1866
1867 ret = fc_strlcpy(buf, _("Buffer empty."), buflen);
1868 if (!ebuf || !ebuf->vtiles) {
1869 return ret;
1870 }
1871
1872 total = tile_list_size(ebuf->vtiles);
1873 if (total > 0) {
1874 fmt = PL_("%d tile copied.", "%d tiles copied.", total);
1875 ret = fc_snprintf(buf, buflen, fmt, total);
1876 }
1877
1878 return ret;
1879 }
1880
1881 /****************************************************************************
1882 Set the "origin" for subsequent copy operations. This controls the x and
1883 y offset of newly created virtual tiles in the buffer.
1884 ****************************************************************************/
edit_buffer_set_origin(struct edit_buffer * ebuf,const struct tile * ptile)1885 void edit_buffer_set_origin(struct edit_buffer *ebuf,
1886 const struct tile *ptile)
1887 {
1888 if (!ebuf) {
1889 return;
1890 }
1891 ebuf->origin = ptile;
1892 }
1893
1894 /****************************************************************************
1895 Return the previously set origin, or NULL if none.
1896 ****************************************************************************/
edit_buffer_get_origin(const struct edit_buffer * ebuf)1897 const struct tile *edit_buffer_get_origin(const struct edit_buffer *ebuf)
1898 {
1899 if (!ebuf) {
1900 return NULL;
1901 }
1902 return ebuf->origin;
1903 }
1904
1905 /****************************************************************************
1906 Returns TRUE if the edit buffer was created with the given type flag.
1907 ****************************************************************************/
edit_buffer_has_type(const struct edit_buffer * ebuf,int type)1908 bool edit_buffer_has_type(const struct edit_buffer *ebuf, int type)
1909 {
1910 if (!ebuf) {
1911 return FALSE;
1912 }
1913 return ebuf->type_flags & type;
1914 }
1915
1916 /****************************************************************************
1917 Returns the "center" tile of a group of selected tiles, or NULL.
1918 The center is calculated as the vector sum divided by the number of tiles,
1919 i.e. the average of the map distance vectors of the selected tiles.
1920 ****************************************************************************/
editor_get_selection_center(void)1921 const struct tile *editor_get_selection_center(void)
1922 {
1923 int count;
1924 const struct tile *origin, *center;
1925 int dx, dy, cx, cy;
1926 int xsum = 0, ysum = 0;
1927
1928 if (!editor || !editor->selected_tile_table) {
1929 return NULL;
1930 }
1931
1932 count = tile_hash_size(editor->selected_tile_table);
1933 if (count < 1) {
1934 return NULL;
1935 }
1936
1937 origin = map_pos_to_tile(0, 0);
1938 tile_hash_iterate(editor->selected_tile_table, ptile) {
1939 map_distance_vector(&dx, &dy, origin, ptile);
1940 xsum += dx;
1941 ysum += dy;
1942 } tile_hash_iterate_end;
1943
1944 cx = xsum / count;
1945 cy = ysum / count;
1946 center = map_pos_to_tile(cx, cy);
1947
1948 return center;
1949 }
1950