Bug Summary

File:src/lvledit/lvledit_actions.c
Location:line 212, column 2
Description:Potential leak of memory pointed to by 'act'

Annotated Source Code

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 */
43LIST_HEAD(to_undo)struct list_head to_undo = { &(to_undo), &(to_undo) };
44LIST_HEAD(to_redo)struct list_head to_redo = { &(to_redo), &(to_redo) };
45
46static 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 */
53static 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 */
71static 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 */
84void action_freestack(void)
85{
86 clear_action_list(&to_redo);
87 clear_action_list(&to_undo);
88}
89
90static action *action_create(int type, va_list args)
91{
92 action *act = malloc(sizeof(action));
2
Memory is allocated
93 act->type = type;
94 switch (type) {
3
Control jumps to the 'default' case at line 185
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
192void 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);
1
Calling 'action_create'
4
Returned allocated memory
198
199 switch (push_mode) {
5
'Default' branch taken. Execution continues on line 212
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);
6
Within the expansion of the macro 'va_end':
a
Potential leak of memory pointed to by 'act'
213}
214
215void 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
222obstacle *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 */
235static 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
262void 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
278void 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 */
301item *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 */
330void 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 */
354void 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 */
377waypoint *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 */
403void 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
423void 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 */
441void 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
457int 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
487void 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
511void 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
529void 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
539void 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
547void 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
554static 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
583void 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 */
648void 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 */
662void 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
677enemy *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
684static 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) );}); &current_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(&current_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) );}); &current_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(&current_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) );}); &current_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(&current_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 */
711void 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 */
726void 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
735static 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
805static 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
836void level_editor_action_undo()
837{
838 push_mode = UNDO;
839 __level_editor_do_action_from_stack(&to_undo);
840 push_mode = NORMAL;
841}
842
843void 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 */
854void 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 */
894void CreateNewMapLevel(int level_num)
895{
896 level *NewLevel;
897 int i, k, l;
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 NewLevel->map[i][k].floor_values[0] = ISO_FLOOR_SAND;
929 for (l = 1; l < MAX_FLOOR_LAYERS2; l++)
930 NewLevel->map[i][k].floor_values[l] = ISO_FLOOR_EMPTY;
931 dynarray_init(&NewLevel->map[i][k].glued_obstacles, 0, sizeof(int));
932 }
933 }
934 // Now we initialize the level jump interface variables with 'empty' values
935 //
936 NewLevel->jump_target_north = (-1);
937 NewLevel->jump_target_south = (-1);
938 NewLevel->jump_target_east = (-1);
939 NewLevel->jump_target_west = (-1);
940
941 // Now we initialize the map obstacles with 'empty' information
942 //
943 for (i = 0; i < MAX_OBSTACLES_ON_MAP4000; i++) {
944 NewLevel->obstacle_list[i].type = (-1);
945 NewLevel->obstacle_list[i].pos.x = (-1);
946 NewLevel->obstacle_list[i].pos.y = (-1);
947 NewLevel->obstacle_list[i].pos.z = level_num;
948 }
949
950 // First we initialize the items arrays with 'empty' information
951 //
952 for (i = 0; i < MAX_ITEMS_PER_LEVEL300; i++) {
953 NewLevel->ItemList[i].pos.x = (-1);
954 NewLevel->ItemList[i].pos.y = (-1);
955 NewLevel->ItemList[i].pos.z = (-1);
956 NewLevel->ItemList[i].type = (-1);
957
958 }
959
960 // Initialize obstacle extensions
961 dynarray_init(&NewLevel->obstacle_extensions, 10, sizeof(struct obstacle_extension));
962
963 // Initialize map labels
964 dynarray_init(&NewLevel->map_labels, 10, sizeof(struct map_label));
965
966 // Initialize waypoints
967 dynarray_init(&NewLevel->waypoints, 10, sizeof(struct waypoint));
968
969 curShip.AllLevels[level_num] = NewLevel;
970}
971
972void delete_map_level(int lnum)
973{
974 int i;
975 level *l = curShip.AllLevels[lnum];
976
977 free(l->Levelname);
978 free(l->Background_Song_Name);
979
980 // Clear out all the existing glue information.
981 free_glued_obstacles(l);
982
983 // Delete floor tiles
984 for (i = 0; i < l->ylen; i++)
985 free(l->map[i]);
986
987 // Remove references to this level from others
988 for (i = 0; i < MAX_LEVELS100; i++) {
989 if (!level_exists(i))
990 continue;
991
992 if (curShip.AllLevels[i]->jump_target_north == lnum)
993 curShip.AllLevels[i]->jump_target_north = -1;
994 if (curShip.AllLevels[i]->jump_target_south == lnum)
995 curShip.AllLevels[i]->jump_target_south = -1;
996 if (curShip.AllLevels[i]->jump_target_west == lnum)
997 curShip.AllLevels[i]->jump_target_west = -1;
998 if (curShip.AllLevels[i]->jump_target_east == lnum)
999 curShip.AllLevels[i]->jump_target_east = -1;
1000 }
1001
1002 // Free obstacle extensions.
1003 dynarray_free(&l->obstacle_extensions);
1004
1005 // Free map labels.
1006 dynarray_free(&l->map_labels);
1007
1008 // Free waypoints.
1009 dynarray_free(&l->waypoints);
1010
1011 // Free memory
1012 free(curShip.AllLevels[lnum]);
1013
1014 curShip.AllLevels[lnum] = NULL((void*)0);
1015
1016 if (lnum == curShip.num_levels - 1)
1017 curShip.num_levels--;
1018
1019}
1020
1021static int get_chest_contents(level *l, obstacle *o, item *items[MAX_ITEMS_IN_INVENTORY100])
1022{
1023 struct dynarray *itemlist = get_obstacle_extension(l, o, OBSTACLE_EXTENSION_CHEST_ITEMS);
1024
1025 memset(items, 0, MAX_ITEMS_IN_INVENTORY100);
1026
1027 if (!itemlist) {
1028 return 0;
1029 }
1030
1031 int i;
1032 int curitem = 0;
1033 for (i = 0; i < itemlist->size; i++) {
1034 items[curitem++] = &((item *)itemlist->arr)[i];
1035 if (curitem == MAX_ITEMS_IN_INVENTORY100 - 1)
1036 break;
1037 }
1038
1039 return curitem;
1040}
1041
1042void level_editor_edit_chest(obstacle *o)
1043{
1044 item *chest_items[MAX_ITEMS_IN_INVENTORY100];
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((void*)0);
1058
1059 struct dynarray *itemlist = get_obstacle_extension(CURLEVEL()(curShip.AllLevels[Me.pos.z]), 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)(__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)
))
) {
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()(curShip.AllLevels[Me.pos.z]), 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()(curShip.AllLevels[Me.pos.z]), 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