1 /*
2  *
3  *   Copyright (c) 2004-2010 Arthur Huillet
4  *
5  *
6  *  This file is part of Freedroid
7  *
8  *  Freedroid is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  Freedroid is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with Freedroid; see the file COPYING. If not, write to the
20  *  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  *  MA  02111-1307  USA
22  *
23  */
24 
25 /**
26  * This file contains all the actions performed by the level editor, ie. the functions that act on the level.
27  */
28 
29 #define _leveleditor_actions_c
30 
31 #include "system.h"
32 
33 #include "defs.h"
34 #include "struct.h"
35 #include "global.h"
36 #include "proto.h"
37 
38 
39 #include "lvledit/lvledit.h"
40 #include "lvledit/lvledit_widgets.h"
41 #include "lvledit/lvledit_tool_select.h"
42 #include "lvledit/lvledit_tool_place.h"
43 
44 /* Undo/redo action lists */
45 LIST_HEAD(to_undo);
46 LIST_HEAD(to_redo);
47 
48 static int push_mode = NORMAL;
49 
50 /**
51  *  @fn void clear_action(action * pos)
52  *
53  *  @brief clears an action from its list, and pointers held within the action
54  */
clear_action(action * action)55 static void clear_action(action * action)
56 {
57 	if (action->type == ACT_SET_OBSTACLE_LABEL && action->d.change_obstacle_name.new_name != NULL)
58 		free(action->d.change_obstacle_name.new_name);
59 	else if (action->type == ACT_SET_MAP_LABEL && action->d.change_label_name.new_name != NULL)
60 		free(action->d.change_label_name.new_name);
61 	else if (action->type == ACT_CREATE_MAP_LABEL && action->d.create_map_label.label_name != NULL)
62 		free(action->d.create_map_label.label_name);
63 	else if (action->type == ACT_CREATE_ENEMY && action->d.create_enemy != NULL)
64 		enemy_free(action->d.create_enemy);
65 
66 	list_del(&action->node);	//< removes an action from a list
67 	free(action);		//< frees the action
68 }
69 
70 /**
71  *  @fn void clear_action_list(struct list_head *list)
72  *
73  *  @brief clears an action list, and all its data
74  */
clear_action_list(struct list_head * list)75 static void clear_action_list(struct list_head *list)
76 {
77 	// free actions individually
78 	action *pos, *next;
79 	list_for_each_entry_safe(pos, next, list, node)
80 	    clear_action(pos);
81 }
82 
83 /**
84  *  @fn static void action_freestack(void)
85  *
86  *  @brief clears to_undo and to_redo when LevelEditor() exits
87  */
action_freestack(void)88 void action_freestack(void)
89 {
90 	clear_action_list(&to_redo);
91 	clear_action_list(&to_undo);
92 }
93 
action_create(int type,va_list args)94 static action *action_create(int type, va_list args)
95 {
96 	action *act = malloc(sizeof(action));
97 	act->type = type;
98 	switch (type) {
99 		case ACT_CREATE_OBSTACLE:
100 			act->d.create_obstacle.x = va_arg(args, double);
101 			act->d.create_obstacle.y = va_arg(args, double);
102 			act->d.create_obstacle.new_obstacle_type = va_arg(args, int);
103 			break;
104 		case ACT_REMOVE_OBSTACLE:
105 			act->d.delete_obstacle = va_arg(args, obstacle *);
106 			break;
107 		case ACT_MOVE_OBSTACLE:
108 			act->d.move_obstacle.obstacle = va_arg(args, obstacle *);
109 			act->d.move_obstacle.newx = va_arg(args, double);
110 			act->d.move_obstacle.newy = va_arg(args, double);
111 			break;
112 		case ACT_CREATE_ITEM:
113 			act->d.create_item.x = (float)va_arg(args, double);
114 			act->d.create_item.y = (float)va_arg(args, double);
115 			act->d.create_item.type = va_arg(args, int);
116 			break;
117 		case ACT_REMOVE_ITEM:
118 			act->d.delete_item = va_arg(args, item *);
119 			break;
120 		case ACT_MOVE_ITEM:
121 			act->d.move_item.item = va_arg(args, item *);
122 			act->d.move_item.newx = (float)va_arg(args, double);
123 			act->d.move_item.newy = (float)va_arg(args, double);
124 			break;
125 		case ACT_CREATE_WAYPOINT:
126 			act->d.create_waypoint.x = va_arg(args, int);
127 			act->d.create_waypoint.y = va_arg(args, int);
128 			act->d.create_waypoint.suppress_random_spawn = va_arg(args, int);
129 			break;
130 		case ACT_REMOVE_WAYPOINT:
131 			act->d.delete_waypoint.x = va_arg(args, int);
132 			act->d.delete_waypoint.y = va_arg(args, int);
133 			break;
134 		case ACT_MOVE_WAYPOINT:
135 			act->d.move_waypoint.w = va_arg(args, waypoint *);
136 			act->d.move_waypoint.newx = va_arg(args, int);
137 			act->d.move_waypoint.newy = va_arg(args, int);
138 			break;
139 		case ACT_TOGGLE_WAYPOINT_RSPAWN:
140 			act->d.toggle_waypoint_rspawn.x = va_arg(args, int);
141 			act->d.toggle_waypoint_rspawn.y = va_arg(args, int);
142 			break;
143 		case ACT_TOGGLE_WAYPOINT_CONNECTION:
144 			act->d.toggle_waypoint_connection.x = va_arg(args, int);
145 			act->d.toggle_waypoint_connection.y = va_arg(args, int);
146 			break;
147 		case ACT_TILE_FLOOR_SET:
148 			act->d.change_floor.x = va_arg(args, int);
149 			act->d.change_floor.y = va_arg(args, int);
150 			act->d.change_floor.layer = va_arg(args, int);
151 			act->d.change_floor.type = va_arg(args, int);
152 			break;
153 		case ACT_MULTIPLE_ACTIONS:
154 			act->d.number_actions = va_arg(args, int);
155 			break;
156 		case ACT_SET_OBSTACLE_LABEL:
157 			act->d.change_obstacle_name.obstacle = va_arg(args, obstacle *);
158 			act->d.change_obstacle_name.new_name = va_arg(args, char *);
159 			break;
160 		case ACT_SET_MAP_LABEL:
161 			act->d.change_label_name.id = va_arg(args, int);
162 			act->d.change_label_name.new_name = va_arg(args, char *);
163 			act->d.change_label_name.x = va_arg(args, int);
164 			act->d.change_label_name.y = va_arg(args, int);
165 			break;
166 		case ACT_JUMP_TO_LEVEL:
167 			act->d.jump_to_level.target_level = va_arg(args, int);
168 			act->d.jump_to_level.x = (float)va_arg(args, double);
169 			act->d.jump_to_level.y = (float)va_arg(args, double);
170 			break;
171 		case ACT_CHANGE_FLOOR_LAYER:
172 			act->d.target_layer = va_arg(args, int);
173 			break;
174 		case ACT_CREATE_MAP_LABEL:
175 			act->d.create_map_label.x = va_arg(args, int);
176 			act->d.create_map_label.y = va_arg(args, int);
177 			act->d.create_map_label.label_name = va_arg(args, char *);
178 			break;
179 		case ACT_REMOVE_MAP_LABEL:
180 			act->d.delete_map_label.x = va_arg(args, int);
181 			act->d.delete_map_label.y = va_arg(args, int);
182 			break;
183 		case ACT_CREATE_ENEMY:
184 			act->d.create_enemy = va_arg(args, enemy *);
185 			break;
186 		case ACT_REMOVE_ENEMY:
187 			act->d.delete_enemy = va_arg(args, enemy *);
188 			break;
189 		default:
190 			error_message(__FUNCTION__, "Unknown action type %d", PLEASE_INFORM | IS_FATAL, type);
191 	}
192 
193 	return act;
194 }
195 
action_push(int type,...)196 void action_push(int type, ...)
197 {
198 	va_list args;
199 	va_start(args, type);
200 
201 	action *act = action_create(type, args);
202 
203 	switch (push_mode) {
204 		case UNDO:
205 			list_add(&act->node, &to_redo);
206 			break;
207 		case REDO:
208 			list_add(&act->node, &to_undo);
209 			break;
210 		case NORMAL:
211 			list_add(&act->node, &to_undo);
212 			clear_action_list(&to_redo);
213 			break;
214 	}
215 
216 	va_end(args);
217 }
218 
action_move_obstacle(level * EditLevel,obstacle * obs,float newx,float newy)219 void action_move_obstacle(level * EditLevel, obstacle * obs, float newx, float newy)
220 {
221 	action_push(ACT_MOVE_OBSTACLE, obs, obs->pos.x, obs->pos.y);
222 
223 	move_obstacle(obs, newx, newy);
224 }
225 
action_create_obstacle_user(Level EditLevel,double x,double y,int new_obstacle)226 obstacle *action_create_obstacle_user(Level EditLevel, double x, double y, int new_obstacle)
227 {
228 	obstacle *o = add_obstacle(EditLevel, x, y, new_obstacle);
229 	if (o) {
230 		action_push(ACT_REMOVE_OBSTACLE, o);
231 	}
232 	return o;
233 }
234 
235 /**
236  * Change an obstacle label, possibly removing it.
237  * @return the number of actions that were pushed on the stack.
238  */
action_change_obstacle_label(level * EditLevel,obstacle * obstacle,char * name,int undoable)239 static int action_change_obstacle_label(level *EditLevel, obstacle *obstacle, char *name, int undoable)
240 {
241 	// If the obstacle already has a label, remove it
242 	char *old_label = get_obstacle_extension(EditLevel, obstacle, OBSTACLE_EXTENSION_LABEL);
243 	if (old_label) {
244 		old_label = strdup(old_label);
245 		del_obstacle_extension(EditLevel, obstacle, OBSTACLE_EXTENSION_LABEL);
246 	}
247 
248 	// Create the undo action if appropriate
249 	if (undoable) {
250 		action_push(ACT_SET_OBSTACLE_LABEL, obstacle, old_label);
251 	} else {
252 		if (old_label) {
253 			free(old_label);
254 			old_label = NULL;
255 		}
256 	}
257 
258 	// If the new label is empty, we are done
259 	if (!name || !strlen(name))
260 		return 0;
261 
262 	// Assign the new label
263 	add_obstacle_extension(EditLevel, obstacle, OBSTACLE_EXTENSION_LABEL, strdup(name));
264 
265 	return undoable;
266 }
267 
action_change_obstacle_label_user(level * EditLevel,obstacle * our_obstacle)268 void action_change_obstacle_label_user(level *EditLevel, obstacle *our_obstacle)
269 {
270 	char *name = NULL;
271 
272 	if (!our_obstacle)
273 		return;
274 
275 	char *old_label = get_obstacle_extension(EditLevel, our_obstacle, OBSTACLE_EXTENSION_LABEL);
276 	name = GetEditableStringInPopupWindow(1000, _("\nPlease enter obstacle label: \n\n"), old_label ? old_label : "");
277 
278 	if (name) {
279 		action_change_obstacle_label(EditLevel, our_obstacle, name, 1);
280 		free(name);
281 	}
282 }
283 
action_remove_obstacle_user(Level EditLevel,obstacle * our_obstacle)284 void action_remove_obstacle_user(Level EditLevel, obstacle * our_obstacle)
285 {
286 	/* Save obstacle information for the undo action */
287 	double posx, posy;
288 	int type;
289 
290 	type = our_obstacle->type;
291 	posx = our_obstacle->pos.x;
292 	posy = our_obstacle->pos.y;
293 
294 	// The obstacle can be in the selection list
295 	remove_element_from_selection(our_obstacle);
296 
297 	// make an undoable removal
298 	del_obstacle(our_obstacle);
299 	action_push(ACT_CREATE_OBSTACLE, posx, posy, type);
300 }
301 
302 /**
303  * Create an item on the map, add this action in the stack undo/redo
304  * \param EditLevel Pointer towards the editing level where create the item
305  * \param x The x position of the item
306  * \param y The y position of the item
307  * \param type The type of the item
308  * \return The new item created
309  */
action_create_item(level * EditLevel,float x,float y,int type)310 item *action_create_item(level *EditLevel, float x, float y, int type)
311 {
312 	int multiplicity = 1;
313 
314 	if (ItemMap[type].item_group_together_in_inventory) {
315 		// Display a popup window with a number selector in order to choose the
316 		// multiplicity of the item
317 		multiplicity =
318 		    do_graphical_number_selection_in_range(1, (!item_spec_eq_id(type, "Valuable Circuits")) ? 100 : 1000, 1, 0);
319 
320 		if (multiplicity == 0) {
321 			// Do not drop an item on the floor when the user cancelled the action.
322 			return NULL;
323 		}
324 	}
325 
326 	// Create an item on the map
327 	item *it = DropItemAt(type, EditLevel->levelnum, x, y, multiplicity);
328 	if (it) {
329 		action_push(ACT_REMOVE_ITEM, it);
330 	}
331 	return it;
332 }
333 
334 /**
335  * Remove an item on the map and add this action in the stack undo/redo
336  * \param EditLevel Pointer towards the editing level where delete the item
337  * \param it The item which we want deleted
338  */
action_remove_item(level * EditLevel,item * it)339 void action_remove_item(level *EditLevel, item *it)
340 {
341 	float x, y;
342 	int type;
343 
344 	// Save item information for the undo action
345 	type = it->type;
346 	x = it->pos.x;
347 	y = it->pos.y;
348 
349 	// The item can be in the selection list
350 	remove_element_from_selection(it);
351 
352 	// Remove the item on the map
353 	DeleteItem(it);
354 
355 	// Make an undoable removal
356 	action_push(ACT_CREATE_ITEM, x, y, type);
357 }
358 
359 /**
360  * Move an item on the map and add this action in stack undo/redo
361  * \param EditLevel Pointer towards the editing level where move the item
362  * \param it The item which we want moved
363  * \param x The new x position of the item
364  * \param y The new y position of the item
365  */
action_move_item(level * EditLevel,item * it,float x,float y)366 void action_move_item(level *EditLevel, item *it, float x, float y)
367 {
368 	float oldx, oldy;
369 
370 	// Save item position for the undo action
371 	oldx = it->pos.x;
372 	oldy = it->pos.y;
373 
374 	// Define the new position of the item
375 	it->pos.x = x;
376 	it->pos.y = y;
377 
378 	action_push(ACT_MOVE_ITEM, it, oldx, oldy);
379 }
380 
381 /**
382  * Create a waypoint on the map, add this action in the undo/redo stack
383  * \param EditLevel Pointer towards the currently edited level where create the waypoint
384  * \param x The x position of the waypoint
385  * \param y The y position of the waypoint
386  * \param random_spawn TRUE if the waypoint can be used to place a random bot
387  * \return The new waypoint created
388  */
action_create_waypoint(level * EditLevel,int x,int y,int random_spawn)389 waypoint *action_create_waypoint(level *EditLevel, int x, int y, int random_spawn)
390 {
391 	waypoint *wpts = EditLevel->waypoints.arr;
392 	int wpnum;
393 
394 	wpnum = get_waypoint(EditLevel, x, y);
395 	if (wpnum < 0) {
396 		// When the waypoint doesn't exist on the map, create it
397 		wpnum = add_waypoint(EditLevel, x, y, random_spawn);
398 
399 		// The waypoints array may have been moved by the add_waypoint call
400 		wpts = EditLevel->waypoints.arr;
401 
402 		// Make an undoable action
403 		action_push(ACT_REMOVE_WAYPOINT, wpts[wpnum].x, wpts[wpnum].y);
404 	}
405 
406 	return &wpts[wpnum];
407 }
408 
409 /**
410  * Remove a waypoint on the map and add this action in the undo/redo stack
411  * \param EditLevel Pointer towards the currently edited level where delete the waypoint
412  * \param x The x position of the waypoint
413  * \param y The y position of the waypoint
414  */
action_remove_waypoint(level * EditLevel,int x,int y)415 void action_remove_waypoint(level *EditLevel, int x, int y)
416 {
417 	waypoint *wpts = EditLevel->waypoints.arr;
418 
419 	int wpnum = get_waypoint(EditLevel, x, y);
420 	if (wpnum < 0) {
421 		// When the waypoint doesn't exist on the map, we are done
422 		return;
423 	}
424 
425 	// Save waypoint information for the undo action
426 	int old_random_spawn = wpts[wpnum].suppress_random_spawn;
427 
428 	// The waypoint can be in the selection list
429 	remove_element_from_selection(&wpts[wpnum]);
430 
431 	// Remove the waypoint on the map
432 	del_waypoint(EditLevel, x, y);
433 
434 	// If a route was being traced from this waypoint, remove it
435 	leveleditor_place_reset_waypoint_route(wpnum);
436 
437 	// Make an undoable action
438 	action_push(ACT_CREATE_WAYPOINT, x, y, old_random_spawn);
439 }
440 
action_move_waypoint(level * lvl,waypoint * w,int x,int y)441 void action_move_waypoint(level *lvl, waypoint *w, int x, int y)
442 {
443 	int oldx, oldy;
444 
445 	oldx = w->x;
446 	oldy = w->y;
447 
448 	move_waypoint(lvl, w, x, y);
449 
450 	action_push(ACT_MOVE_WAYPOINT, w, oldx, oldy);
451 }
452 
453 /**
454  * Set/unset the flag for random bots and add this action in the undo/redo stack
455  * \param EditLevel Pointer towards the currently edited level where toggle the waypoint
456  * \param x The x position of the waypoint
457  * \param y The y position of the waypoint
458  */
action_toggle_waypoint_randomspawn(level * EditLevel,int x,int y)459 void action_toggle_waypoint_randomspawn(level *EditLevel, int x, int y)
460 {
461 	waypoint *wpts = EditLevel->waypoints.arr;
462 
463 	int wpnum = get_waypoint(EditLevel, x, y);
464 	if (wpnum < 0) {
465 		return;
466 	}
467 
468 	// Toggle the flag for random bots
469 	wpts[wpnum].suppress_random_spawn = !wpts[wpnum].suppress_random_spawn;
470 
471 	// Make an undoable action
472 	action_push(ACT_TOGGLE_WAYPOINT_RSPAWN, x, y);
473 }
474 
action_toggle_waypoint_connection(level * EditLevel,int id_origin,int id_target,int removeifpresent,int undoable)475 int action_toggle_waypoint_connection(level *EditLevel, int id_origin, int id_target, int removeifpresent, int undoable)
476 {
477 	waypoint *wpts = EditLevel->waypoints.arr;
478 	int *connections;
479 	int i;
480 
481 	// Get the connections of the waypoint;
482 	connections = wpts[id_origin].connections.arr;
483 
484 	for (i = 0; i < wpts[id_origin].connections.size; i++) {
485 		if (connections[i] == id_target) {
486 			if (removeifpresent) {
487 				// Delete the connection of the waypoint
488 				dynarray_del(&wpts[id_origin].connections, i, sizeof(int));
489 			}
490 			if (undoable)
491 				action_push(ACT_TOGGLE_WAYPOINT_CONNECTION, id_origin, id_target);
492 			return -1;
493 		}
494 	}
495 
496 	// Add the target connection of the waypoint
497 	dynarray_add(&wpts[id_origin].connections, &id_target, sizeof(int));
498 
499 	if (undoable)
500 		action_push(ACT_TOGGLE_WAYPOINT_CONNECTION, id_origin, id_target);
501 
502 	return 1;
503 }
504 
level_editor_action_toggle_waypoint_connection_user(level * EditLevel,int xpos,int ypos)505 void level_editor_action_toggle_waypoint_connection_user(level * EditLevel, int xpos, int ypos)
506 {
507 	int wpnum;
508 
509 	wpnum = get_waypoint(EditLevel, xpos, ypos);
510 	if (wpnum < 0) {
511 		// No waypoint is currently targeted.
512 		return;
513 	}
514 
515 	if (OriginWaypoint == -1) {
516 		// Set the origin waypoint for the next connection.
517 		OriginWaypoint = wpnum;
518 		return;
519 	}
520 
521 	if (OriginWaypoint != wpnum) {
522 		// Toggle a connection between the origin waypoint and the currently targeted.
523 		action_toggle_waypoint_connection(EditLevel, OriginWaypoint, wpnum, 1, 1);
524 	}
525 
526 	OriginWaypoint = -1;
527 }
528 
lvledit_action_toggle_waypoint(int randomspawn)529 void lvledit_action_toggle_waypoint(int randomspawn)
530 {
531 	int wpnum = get_waypoint(EditLevel(), EditX(), EditY());
532 	if (wpnum < 0) {
533 		// If the waypoint doesn't exist at the map position, create it
534 		action_create_waypoint(EditLevel(), EditX(), EditY(), randomspawn);
535 	} else {
536 		// An existing waypoint will be removed or have its
537 		// randomspawn flag toggled
538 		waypoint *wpts = EditLevel()->waypoints.arr;
539 		if (randomspawn) {
540 			action_toggle_waypoint_randomspawn(EditLevel(), wpts[wpnum].x, wpts[wpnum].y);
541 		} else {
542 			action_remove_waypoint(EditLevel(), wpts[wpnum].x, wpts[wpnum].y);
543 		}
544 	}
545 }
546 
action_set_floor_layer(Level EditLevel,int x,int y,int layer,int type)547 void action_set_floor_layer(Level EditLevel, int x, int y, int layer, int type)
548 {
549 	if (layer >= EditLevel->floor_layers)
550 		EditLevel->floor_layers++;
551 
552 	int old = EditLevel->map[y][x].floor_values[layer];
553 	EditLevel->map[y][x].floor_values[layer] = type;
554 	action_push(ACT_TILE_FLOOR_SET, x, y, layer, old);
555 }
556 
action_set_floor(Level EditLevel,int x,int y,int type)557 void action_set_floor(Level EditLevel, int x, int y, int type)
558 {
559 	// Underlay floor tiles are placed in layer #0.
560 	// Overlay floor tiles are placed in layer #1.
561 	int layer = type < MAX_UNDERLAY_FLOOR_TILES ? 0 : 1;
562 	action_set_floor_layer(EditLevel, x, y, layer, type);
563 }
564 
action_change_floor_layer(level * lvl,int layer)565 void action_change_floor_layer(level *lvl, int layer)
566 {
567 	int old_floor_layer = current_floor_layer;
568 	current_floor_layer = layer;
569 	action_push(ACT_CHANGE_FLOOR_LAYER, old_floor_layer);
570 }
571 
action_change_map_label(level * EditLevel,int i,char * name,int x,int y)572 static void action_change_map_label(level *EditLevel, int i, char *name, int x, int y)
573 {
574 	struct map_label *map_label;
575 	char *old_label = NULL;
576 
577 	// If the map label exist, remove it
578 	if (i < EditLevel->map_labels.size) {
579 		// Get the map label
580 		map_label = &ACCESS_MAP_LABEL(EditLevel->map_labels, i);
581 
582 		// Get the old label for undoable actions
583 		old_label = strdup(map_label->label_name);
584 
585 		// Delete the map label
586 		del_map_label(EditLevel, map_label->label_name);
587 	}
588 
589 	action_push(ACT_SET_MAP_LABEL, i, old_label, x, y);
590 
591 	// If the new label is empty, we are done
592 	if (!name || !strlen(name))
593 		return;
594 
595 	// Create a new map label at the position of cursor
596 	add_map_label(EditLevel, x, y, strdup(name));
597 }
598 
level_editor_action_change_map_label_user(level * EditLevel,float x,float y)599 void level_editor_action_change_map_label_user(level *EditLevel, float x, float y)
600 {
601 	struct map_label *map_label = NULL;
602 	char *name;
603 	char *old_name = NULL;
604 	char suggested_label[200];
605 	int i;
606 
607 	suggested_label[0] = '\0';
608 
609 	// We check if a map label already exists for this spot
610 	for (i = 0; i < EditLevel->map_labels.size; i++) {
611 		map_label = &ACCESS_MAP_LABEL(EditLevel->map_labels, i);
612 
613 		if ((fabs(map_label->pos.x + 0.5 - x) < 0.5) &&
614 			 (fabs(map_label->pos.y + 0.5 - y) < 0.5)) {
615 
616 			// Use the old label as a suggestion
617 			old_name = map_label->label_name;
618 			strncpy(suggested_label, old_name, sizeof(suggested_label) - 1);
619 			suggested_label[sizeof(suggested_label) - 1] = '\0';
620 			break;
621 		}
622 	}
623 
624 	// Check if the name entered already exists
625 
626 	while (1) {
627 		// Show popup window to enter a new map label
628 		name = GetEditableStringInPopupWindow(sizeof(suggested_label) - 1, _("\nPlease enter map label: \n\n"), suggested_label);
629 		if (!name || (old_name && !strcmp(name, old_name))) {
630 			// Do not change label
631 			free(name);
632 			return;
633 		}
634 
635 		map_label = map_label_exists(name);
636 		if (!map_label) {
637 			// When the new name of the map label does not exist, we are done
638 			break;
639 		}
640 
641 		// When the new name already exists, we must not create an other map
642 		// label with the same name, but we want to display an alert window,
643 		// and then go back to input box with the name still present
644 		alert_window("%s", _("The new name of the map label already exists, please choose an other name."));
645 
646 		// Copy the name in order to have it in the input box
647 		strncpy(suggested_label, name, sizeof(suggested_label) - 1);
648 		suggested_label[sizeof(suggested_label) - 1] = '\0';
649 		free(name);
650 
651 		// Restore the menu background in order to correctly draw the next popup window
652 		RestoreMenuBackground(1);
653 	}
654 
655 	// Change a map label when the name enter by the user is valid
656 	action_change_map_label(EditLevel, i, name, rintf(x - 0.5), rintf(y - 0.5));
657 	free(name);
658 }
659 
660 /**
661  * @brief Create a map label on the map and push this action on the undo/redo stack.
662  *
663  * @param lvl Pointer towards the level where the map label is created.
664  * @param x The x position of the map label.
665  * @param y The y position of the map label.
666  * @param label_name The name of the map label.
667  */
action_create_map_label(level * lvl,int x,int y,char * label_name)668 void action_create_map_label(level *lvl, int x, int y, char *label_name)
669 {
670 	add_map_label(lvl, x, y, strdup(label_name));
671 
672 	action_push(ACT_REMOVE_MAP_LABEL, x, y);
673 }
674 
675 /**
676  * @brief Remove a map label on the map and push this action on the undo/redo stack.
677  *
678  * @param lvl Pointer towards the level where the map label is removed.
679  * @param x The x position of the map label.
680  * @param y The y position of the map label.
681  */
action_remove_map_label(level * lvl,int x,int y)682 void action_remove_map_label(level *lvl, int x, int y)
683 {
684 	char *old_label_name;
685 	map_label *m;
686 
687 	m = get_map_label_from_coords(lvl, x, y);
688 	if (m) {
689 		old_label_name = strdup(m->label_name);
690 
691 		remove_element_from_selection(m);
692 
693 		del_map_label(lvl, m->label_name);
694 
695 		action_push(ACT_CREATE_MAP_LABEL, x, y, old_label_name);
696 	}
697 }
698 
action_create_enemy(level * lvl,enemy * en)699 enemy *action_create_enemy(level *lvl, enemy *en)
700 {
701 	enemy_insert_into_lists(en, TRUE);
702 	action_push(ACT_REMOVE_ENEMY, en);
703 	return en;
704 }
705 
action_remove_enemy(level * lvl,enemy * en)706 void action_remove_enemy(level *lvl, enemy *en)
707 {
708 	enemy *current_enemy, *nerot;
709 
710 	remove_element_from_selection(en);
711 
712 	BROWSE_ALIVE_BOTS_SAFE(current_enemy, nerot) {
713 		if (current_enemy == en)
714 			list_del(&current_enemy->global_list);
715 	}
716 
717 	BROWSE_DEAD_BOTS_SAFE(current_enemy, nerot) {
718 		if (current_enemy == en)
719 			list_del(&current_enemy->global_list);
720 	}
721 
722 	BROWSE_LEVEL_BOTS_SAFE(current_enemy, nerot, lvl->levelnum) {
723 		if (current_enemy == en)
724 			list_del(&current_enemy->level_list);
725 	}
726 
727 	action_push(ACT_CREATE_ENEMY, en);
728 }
729 
730 /**
731  *  @fn void jump_to_level( int target_map, float x, float y)
732  *
733  *  @brief jumps to a target level, saving this level on the undo/redo stack
734  */
action_jump_to_level(int target_level,double x,double y)735 void action_jump_to_level(int target_level, double x, double y)
736 {
737 	// When the user wants to change the current edited level, reset tools
738 	lvledit_reset_tools();
739 
740 	action_push(ACT_JUMP_TO_LEVEL, EditLevel()->levelnum, Me.pos.x, Me.pos.y);	//< sets undo or redo stack, depending on push_mode state
741 	reset_visible_levels();
742 	Teleport(target_level, (float)x, (float)y, FALSE, TRUE);
743 }
744 
745 /**
746  * Jump to the center of a level.
747  *
748  * @param level_num The level id.
749  */
action_jump_to_level_center(int level_num)750 void action_jump_to_level_center(int level_num)
751 {
752 	// Calculate the center of the level.
753 	float x = curShip.AllLevels[level_num]->xlen / 2;
754 	float y = curShip.AllLevels[level_num]->ylen / 2;
755 
756 	action_jump_to_level(level_num, x, y);
757 }
758 
action_do(level * level,action * a)759 static void action_do(level * level, action * a)
760 {
761 	switch (a->type) {
762 	case ACT_CREATE_OBSTACLE:
763 		action_create_obstacle_user(level, a->d.create_obstacle.x, a->d.create_obstacle.y, a->d.create_obstacle.new_obstacle_type);
764 		break;
765 	case ACT_REMOVE_OBSTACLE:
766 		action_remove_obstacle_user(level, a->d.delete_obstacle);
767 		break;
768 	case ACT_MOVE_OBSTACLE:
769 		action_move_obstacle(level, a->d.move_obstacle.obstacle, a->d.move_obstacle.newx, a->d.move_obstacle.newy);
770 		break;
771 	case ACT_CREATE_ITEM:
772 		action_create_item(level, a->d.create_item.x, a->d.create_item.y, a->d.create_item.type);
773 		break;
774 	case ACT_REMOVE_ITEM:
775 		action_remove_item(level, a->d.delete_item);
776 		break;
777 	case ACT_MOVE_ITEM:
778 		action_move_item(level, a->d.move_item.item, a->d.move_item.newx, a->d.move_item.newy);
779 		break;
780 	case ACT_CREATE_WAYPOINT:
781 		action_create_waypoint(level, a->d.create_waypoint.x, a->d.create_waypoint.y, a->d.create_waypoint.suppress_random_spawn);
782 		break;
783 	case ACT_REMOVE_WAYPOINT:
784 		action_remove_waypoint(level, a->d.delete_waypoint.x, a->d.delete_waypoint.y);
785 		break;
786 	case ACT_MOVE_WAYPOINT:
787 		action_move_waypoint(level, a->d.move_waypoint.w, a->d.move_waypoint.newx, a->d.move_waypoint.newy);
788 		break;
789 	case ACT_TOGGLE_WAYPOINT_RSPAWN:
790 		action_toggle_waypoint_randomspawn(level, a->d.toggle_waypoint_rspawn.x, a->d.toggle_waypoint_rspawn.y);
791 		break;
792 	case ACT_TOGGLE_WAYPOINT_CONNECTION:
793 		action_toggle_waypoint_connection(level, a->d.toggle_waypoint_connection.x, a->d.toggle_waypoint_connection.y, 1, 1);
794 		break;
795 	case ACT_TILE_FLOOR_SET:
796 		action_set_floor_layer(level, a->d.change_floor.x, a->d.change_floor.y, a->d.change_floor.layer, a->d.change_floor.type);
797 		break;
798 	case ACT_MULTIPLE_ACTIONS:
799 		error_message(__FUNCTION__, "Passed a multiple actions meta-action as parameter. A real action is needed.", PLEASE_INFORM);
800 		break;
801 	case ACT_SET_OBSTACLE_LABEL:
802 		action_change_obstacle_label(level, a->d.change_obstacle_name.obstacle, a->d.change_obstacle_name.new_name, 1);
803 		break;
804 	case ACT_SET_MAP_LABEL:
805 		action_change_map_label(level, a->d.change_label_name.id, a->d.change_label_name.new_name, a->d.change_label_name.x, a->d.change_label_name.y);
806 		break;
807 	case ACT_JUMP_TO_LEVEL:
808 		action_jump_to_level(a->d.jump_to_level.target_level, a->d.jump_to_level.x, a->d.jump_to_level.y);
809 		break;
810 	case ACT_CHANGE_FLOOR_LAYER:
811 		action_change_floor_layer(level, a->d.target_layer);
812 		break;
813 	case ACT_CREATE_MAP_LABEL:
814 		action_create_map_label(level, a->d.create_map_label.x, a->d.create_map_label.y, a->d.create_map_label.label_name);
815 		break;
816 	case ACT_REMOVE_MAP_LABEL:
817 		action_remove_map_label(level, a->d.delete_map_label.x, a->d.delete_map_label.y);
818 		break;
819 	case ACT_CREATE_ENEMY:
820 		action_create_enemy(level, a->d.create_enemy);
821 		a->d.create_enemy = NULL;
822 		break;
823 	case ACT_REMOVE_ENEMY:
824 		action_remove_enemy(level, a->d.delete_enemy);
825 		break;
826 	}
827 }
828 
__level_editor_do_action_from_stack(struct list_head * stack)829 static void __level_editor_do_action_from_stack(struct list_head *stack)
830 {
831 	action *a;
832 
833 	if (list_empty(stack))
834 		return;
835 
836 	// Get the top action from the undo/redo stack
837 	a = list_entry(stack->next, action, node);
838 
839 	if (a->type == ACT_MULTIPLE_ACTIONS) {
840 		// When the action is multiple, we must execute each action,
841 		// and push all actions in the stack in order to undo
842 		int i, max;
843 
844 		max = a->d.number_actions;
845 		clear_action(a);
846 
847 		// Execute all actions
848 		for (i = 0; i < max; i++) {
849 			__level_editor_do_action_from_stack(stack);
850 		}
851 
852 		// Push all actions in the stack in order to undo
853 		action_push(ACT_MULTIPLE_ACTIONS, max);
854 	} else {
855 		action_do(EditLevel(), a);
856 		clear_action(a);
857 	}
858 }
859 
level_editor_action_undo()860 void level_editor_action_undo()
861 {
862 	push_mode = UNDO;
863 	__level_editor_do_action_from_stack(&to_undo);
864 	push_mode = NORMAL;
865 }
866 
level_editor_action_redo()867 void level_editor_action_redo()
868 {
869 	push_mode = REDO;
870 	__level_editor_do_action_from_stack(&to_redo);
871 	push_mode = NORMAL;
872 }
873 
874 /**
875  * Place an aligned object on the map with the keypad
876  * \param positionid The position of the object on the purple grid
877  */
level_editor_place_aligned_object(int positionid)878 void level_editor_place_aligned_object(int positionid)
879 {
880 	float position_offset_x[9] = { 0, 0.5, 1.0, 0, 0.5, 1.0, 0, 0.5, 1.0 };
881 	float position_offset_y[9] = { 1.0, 1.0, 1.0, 0.5, 0.5, 0.5, 0, 0, 0 };
882 	struct widget_lvledit_categoryselect *cs = get_current_object_type();
883 	int type = cs->indices[cs->selected_tile_nb];
884 	moderately_finepoint pos;
885 
886 	positionid--;
887 
888 	// Calculate the position of the object
889 	pos.x = (int)Me.pos.x + position_offset_x[positionid];
890 	pos.y = (int)Me.pos.y + position_offset_y[positionid];
891 
892 	if (!pos_inside_level(pos.x, pos.y, EditLevel()))
893 	{
894 		// Do not place an aligned object outside the current level.
895 		return;
896 	}
897 
898 	switch (cs->type) {
899 	case OBJECT_OBSTACLE:
900 			action_create_obstacle_user(EditLevel(), pos.x, pos.y, type);
901 			break;
902 	case OBJECT_FLOOR:
903 			action_set_floor(EditLevel(), (int)Me.pos.x, (int)Me.pos.y, type);
904 			break;
905 	case OBJECT_ITEM:
906 			action_create_item(EditLevel(), pos.x, pos.y, type);
907 			break;
908 	default:
909 			break;
910 	}
911 }
912 
913 /**
914  * This function should create a completely new level into the existing
915  * ship structure that we already have.  The new level will be rather
916  * small and simple.
917  */
CreateNewMapLevel(int level_num)918 void CreateNewMapLevel(int level_num)
919 {
920 	level *NewLevel;
921 	int i, k;
922 
923 	// Get the memory for one level
924 	//
925 	NewLevel = (Level) MyMalloc(sizeof(level));
926 
927 	DebugPrintf(0, "\n-----------------------------------------------------------------");
928 	DebugPrintf(0, "\nStarting to create and add a completely new level to the ship.");
929 
930 	// Now we proceed in the order of the struct 'level' in the
931 	// struct.h file so that we can easily verify if we've handled
932 	// all the data structure or left something out which could
933 	// be terrible!
934 	//
935 	NewLevel->levelnum = level_num;
936 	NewLevel->xlen = 90;
937 	NewLevel->ylen = 90;
938 	NewLevel->floor_layers = 1;
939 	NewLevel->light_bonus = 19;
940 	NewLevel->minimum_light_value = 19;
941 	NewLevel->infinite_running_on_this_level = FALSE;
942 	NewLevel->random_dungeon = 0;
943 	NewLevel->dungeon_generated = FALSE;
944 	NewLevel->Levelname = strdup("New level just created");
945 	NewLevel->Background_Song_Name = strdup("TheBeginning.ogg");
946 
947 	// First we initialize the floor with 'empty' values
948 	//
949 	for (i = 0; i < NewLevel->ylen; i++) {
950 		NewLevel->map[i] = MyMalloc(NewLevel->xlen * sizeof(map_tile));
951 		for (k = 0; k < NewLevel->xlen; k++) {
952 			init_map_tile(&NewLevel->map[i][k]);
953 		}
954 	}
955 	// Now we initialize the level jump interface variables with 'empty' values
956 	//
957 	NewLevel->jump_target_north = (-1);
958 	NewLevel->jump_target_south = (-1);
959 	NewLevel->jump_target_east = (-1);
960 	NewLevel->jump_target_west = (-1);
961 
962 	// Now we initialize the map obstacles with 'empty' information
963 	//
964 	for (i = 0; i < MAX_OBSTACLES_ON_MAP; i++) {
965 		NewLevel->obstacle_list[i].type = (-1);
966 		NewLevel->obstacle_list[i].pos.x = (-1);
967 		NewLevel->obstacle_list[i].pos.y = (-1);
968 		NewLevel->obstacle_list[i].pos.z = level_num;
969 	}
970 
971 	// First we initialize the items arrays with 'empty' information
972 	//
973 	for (i = 0; i < MAX_ITEMS_PER_LEVEL; i++) {
974 		NewLevel->ItemList[i].pos.x = (-1);
975 		NewLevel->ItemList[i].pos.y = (-1);
976 		NewLevel->ItemList[i].pos.z = (-1);
977 		NewLevel->ItemList[i].type = (-1);
978 
979 	}
980 
981 	// Initialize obstacle extensions
982 	dynarray_init(&NewLevel->obstacle_extensions, 10, sizeof(struct obstacle_extension));
983 
984 	// Initialize map labels
985 	dynarray_init(&NewLevel->map_labels, 10, sizeof(struct map_label));
986 
987 	// Initialize waypoints
988 	dynarray_init(&NewLevel->waypoints, 10, sizeof(struct waypoint));
989 
990 	curShip.AllLevels[level_num] = NewLevel;
991 }
992 
delete_map_level(int lnum)993 void delete_map_level(int lnum)
994 {
995 	// Remove references to this level from others
996 	level *l = NULL;
997 
998 	BROWSE_LEVELS(l) {
999 		if (l->jump_target_north == lnum)
1000 			l->jump_target_north = -1;
1001 		if (l->jump_target_south == lnum)
1002 			l->jump_target_south = -1;
1003 		if (l->jump_target_west == lnum)
1004 			l->jump_target_west = -1;
1005 		if (l->jump_target_east == lnum)
1006 			l->jump_target_east = -1;
1007 	}
1008 
1009 	// Free memory
1010 	free_ship_level(curShip.AllLevels[lnum]);
1011 	curShip.AllLevels[lnum] = NULL;
1012 
1013 	if (lnum == curShip.num_levels - 1)
1014 		curShip.num_levels--;
1015 
1016 	// Removing a level cannot be undo, so we need to clear everything that could have
1017 	// a reference to an object of the deleted level.
1018 	action_freestack();
1019 	clear_selection(-1);
1020 	clear_clipboard(-1);
1021 }
1022 
get_chest_contents(level * l,obstacle * o,item * items[MAX_ITEMS_IN_INVENTORY])1023 static int get_chest_contents(level *l, obstacle *o, item *items[MAX_ITEMS_IN_INVENTORY])
1024 {
1025 	struct dynarray *itemlist = get_obstacle_extension(l, o, OBSTACLE_EXTENSION_CHEST_ITEMS);
1026 
1027 	memset(items, 0, MAX_ITEMS_IN_INVENTORY*sizeof(item *));
1028 
1029 	if (!itemlist) {
1030 		return 0;
1031 	}
1032 
1033 	int i;
1034 	int curitem = 0;
1035 	for (i = 0; i < itemlist->size && i < MAX_ITEMS_IN_INVENTORY; i++) {
1036 		items[curitem++] = &((item *)itemlist->arr)[i];
1037 	}
1038 
1039 	return curitem;
1040 }
1041 
level_editor_edit_chest(obstacle * o)1042 void level_editor_edit_chest(obstacle *o)
1043 {
1044 	item *chest_items[MAX_ITEMS_IN_INVENTORY];
1045 	item *user_items[2];
1046 	int chest_nb_items;
1047 	int done = 0;
1048 	shop_decision shop_order;
1049 	item *tmp;
1050 
1051 	item dummy_addtochest;
1052 	init_item(&dummy_addtochest);
1053 	dummy_addtochest.type = 1;
1054 	FillInItemProperties(&dummy_addtochest, 2, 1);
1055 
1056 	user_items[0] = &dummy_addtochest;
1057 	user_items[1] = NULL;
1058 
1059 	struct dynarray *itemlist = get_obstacle_extension(CURLEVEL(), o, OBSTACLE_EXTENSION_CHEST_ITEMS);
1060 
1061 	// Safety check
1062 	struct obstacle_spec *obs_spec = get_obstacle_spec(o->type);
1063 	if (!obs_spec->action || strncmp(obs_spec->action, "chest", 5)) {
1064 		error_message(__FUNCTION__, "Tried to edit the contents of a chest, but the obstacle is not a chest.",
1065 				     PLEASE_INFORM | IS_FATAL);
1066 	}
1067 
1068 	while (!done) {
1069 
1070 		// Build the list of items in the chest
1071 		chest_nb_items = get_chest_contents(CURLEVEL(), o, chest_items);
1072 
1073 		// Display the shop interface
1074 		done = GreatShopInterface(chest_nb_items, chest_items, 1, user_items, &shop_order);
1075 
1076 		// BUY removes an item from the chest
1077 		// SELL spawns the drop item interface
1078 		switch (shop_order.shop_command) {
1079 		case BUY_1_ITEM:
1080 			DeleteItem(chest_items[shop_order.item_selected]);
1081 			dynarray_del(itemlist, shop_order.item_selected, sizeof(item));
1082 			break;
1083 		case SELL_1_ITEM:
1084 			tmp = ItemDropFromLevelEditor();
1085 			if (tmp) {
1086 
1087 				if (!itemlist) {
1088 					itemlist = dynarray_alloc(10, sizeof(item));
1089 					add_obstacle_extension(CURLEVEL(), o, OBSTACLE_EXTENSION_CHEST_ITEMS, itemlist);
1090 				}
1091 
1092 				dynarray_add(itemlist, tmp, sizeof(item));
1093 
1094 				// delete the ground copy
1095 				DeleteItem(tmp);
1096 			}
1097 			break;
1098 		default:
1099 			;
1100 		}
1101 	}
1102 }
1103 
1104 #undef _leveleditor_actions_c
1105