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