+ int font_nr = font_to_graphic[i].font_nr;
+ int special = font_to_graphic[i].special;
+ int graphic = font_to_graphic[i].graphic;
+
+ if (special != -1)
+ continue;
+
+ font_info[font_nr].graphic = graphic;
+ }
+
+ /* always start with reliable default values (special font graphics) */
+ for (i = 0; i < NUM_FONTS; i++)
+ {
+ for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
+ {
+ font_info[i].special_graphic[j] = font_info[i].graphic;
+ font_info[i].special_bitmap_id[j] = i;
+ }
+ }
+
+ /* initialize special font/graphic mapping from static configuration */
+ for (i = 0; font_to_graphic[i].font_nr > -1; i++)
+ {
+ int font_nr = font_to_graphic[i].font_nr;
+ int special = font_to_graphic[i].special;
+ int graphic = font_to_graphic[i].graphic;
+
+ if (special >= 0 && special < NUM_SPECIAL_GFX_ARGS)
+ {
+ font_info[font_nr].special_graphic[special] = graphic;
+ font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
+ num_font_bitmaps++;
+ }
+ }
+
+ /* initialize special element/graphic mapping from dynamic configuration */
+ for (i = 0; i < num_property_mappings; i++)
+ {
+ int font_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
+ int special = property_mapping[i].ext3_index;
+ int graphic = property_mapping[i].artwork_index;
+
+ if (font_nr < 0)
+ continue;
+
+ if (special >= 0 && special < NUM_SPECIAL_GFX_ARGS)
+ {
+ font_info[font_nr].special_graphic[special] = graphic;
+ font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
+ num_font_bitmaps++;
+ }
+ }
+
+ /* ---------- initialize font bitmap array ---------- */
+
+ if (font_bitmap_info != NULL)
+ FreeFontInfo(font_bitmap_info);
+
+ font_bitmap_info =
+ checked_calloc(num_font_bitmaps * sizeof(struct FontBitmapInfo));
+
+ /* ---------- initialize font bitmap definitions ---------- */
+
+ for (i = 0; i < NUM_FONTS; i++)
+ {
+ if (i < NUM_INITIAL_FONTS)
+ {
+ font_bitmap_info[i] = font_initial[i];
+ continue;
+ }
+
+ for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
+ {
+ int font_bitmap_id = font_info[i].special_bitmap_id[j];
+ int graphic = font_info[i].special_graphic[j];
+
+ /* set 'graphic_info' for font entries, if uninitialized (guessed) */
+ if (graphic_info[graphic].anim_frames < MIN_NUM_CHARS_PER_FONT)
+ {
+ graphic_info[graphic].anim_frames = DEFAULT_NUM_CHARS_PER_FONT;
+ graphic_info[graphic].anim_frames_per_line= DEFAULT_NUM_CHARS_PER_LINE;
+ }
+
+ /* copy font relevant information from graphics information */
+ font_bitmap_info[font_bitmap_id].bitmap = graphic_info[graphic].bitmap;
+ font_bitmap_info[font_bitmap_id].src_x = graphic_info[graphic].src_x;
+ font_bitmap_info[font_bitmap_id].src_y = graphic_info[graphic].src_y;
+ font_bitmap_info[font_bitmap_id].width = graphic_info[graphic].width;
+ font_bitmap_info[font_bitmap_id].height = graphic_info[graphic].height;
+ font_bitmap_info[font_bitmap_id].draw_x = graphic_info[graphic].draw_x;
+ font_bitmap_info[font_bitmap_id].draw_y = graphic_info[graphic].draw_y;
+
+ font_bitmap_info[font_bitmap_id].num_chars =
+ graphic_info[graphic].anim_frames;
+ font_bitmap_info[font_bitmap_id].num_chars_per_line =
+ graphic_info[graphic].anim_frames_per_line;
+ }
+ }
+
+ InitFontInfo(font_bitmap_info, num_font_bitmaps, getFontBitmapID);
+}
+
+void InitElementGraphicInfo()
+{
+ struct PropertyMapping *property_mapping = getImageListPropertyMapping();
+ int num_property_mappings = getImageListPropertyMappingSize();
+ int i, act, dir;
+
+ if (graphic_info == NULL) /* still at startup phase */
+ return;
+
+ /* set values to -1 to identify later as "uninitialized" values */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ for (act = 0; act < NUM_ACTIONS; act++)
+ {
+ element_info[i].graphic[act] = -1;
+ element_info[i].crumbled[act] = -1;
+
+ for (dir = 0; dir < NUM_DIRECTIONS; dir++)
+ {
+ element_info[i].direction_graphic[act][dir] = -1;
+ element_info[i].direction_crumbled[act][dir] = -1;
+ }
+ }
+ }
+
+ /* initialize normal element/graphic mapping from static configuration */
+ for (i = 0; element_to_graphic[i].element > -1; i++)
+ {
+ int element = element_to_graphic[i].element;
+ int action = element_to_graphic[i].action;
+ int direction = element_to_graphic[i].direction;
+ boolean crumbled = element_to_graphic[i].crumbled;
+ int graphic = element_to_graphic[i].graphic;
+ int base_graphic = el2baseimg(element);
+
+ if (graphic_info[graphic].bitmap == NULL)
+ continue;
+
+ if ((action > -1 || direction > -1 || crumbled == TRUE) &&
+ base_graphic != -1)
+ {
+ boolean base_redefined = getImageListEntry(base_graphic)->redefined;
+ boolean act_dir_redefined = getImageListEntry(graphic)->redefined;
+
+ /* if the base graphic ("emerald", for example) has been redefined,
+ but not the action graphic ("emerald.falling", for example), do not
+ use an existing (in this case considered obsolete) action graphic
+ anymore, but use the automatically determined default graphic */
+ if (base_redefined && !act_dir_redefined)
+ continue;
+ }
+
+ if (action < 0)
+ action = ACTION_DEFAULT;
+
+ if (crumbled)
+ {
+ if (direction > -1)
+ element_info[element].direction_crumbled[action][direction] = graphic;
+ else
+ element_info[element].crumbled[action] = graphic;
+ }
+ else
+ {
+ if (direction > -1)
+ element_info[element].direction_graphic[action][direction] = graphic;
+ else
+ element_info[element].graphic[action] = graphic;
+ }
+ }
+
+ /* initialize normal element/graphic mapping from dynamic configuration */
+ for (i = 0; i < num_property_mappings; i++)
+ {
+ int element = property_mapping[i].base_index;
+ int action = property_mapping[i].ext1_index;
+ int direction = property_mapping[i].ext2_index;
+ int special = property_mapping[i].ext3_index;
+ int graphic = property_mapping[i].artwork_index;
+ boolean crumbled = FALSE;
+
+ if (special == GFX_SPECIAL_ARG_CRUMBLED)
+ {
+ special = -1;
+ crumbled = TRUE;
+ }
+
+ if (graphic_info[graphic].bitmap == NULL)
+ continue;
+
+ if (element >= MAX_NUM_ELEMENTS || special != -1)
+ continue;
+
+ if (action < 0)
+ action = ACTION_DEFAULT;
+
+ if (crumbled)
+ {
+ if (direction < 0)
+ for (dir = 0; dir < NUM_DIRECTIONS; dir++)
+ element_info[element].direction_crumbled[action][dir] = -1;
+
+ if (direction > -1)
+ element_info[element].direction_crumbled[action][direction] = graphic;
+ else
+ element_info[element].crumbled[action] = graphic;
+ }
+ else
+ {
+ if (direction < 0)
+ for (dir = 0; dir < NUM_DIRECTIONS; dir++)
+ element_info[element].direction_graphic[action][dir] = -1;
+
+ if (direction > -1)
+ element_info[element].direction_graphic[action][direction] = graphic;
+ else
+ element_info[element].graphic[action] = graphic;
+ }
+ }
+
+ /* now copy all graphics that are defined to be cloned from other graphics */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ int graphic = element_info[i].graphic[ACTION_DEFAULT];
+ int crumbled_like, diggable_like;
+
+ if (graphic == -1)
+ continue;
+
+ crumbled_like = graphic_info[graphic].crumbled_like;
+ diggable_like = graphic_info[graphic].diggable_like;
+
+ if (crumbled_like != -1 && element_info[i].crumbled[ACTION_DEFAULT] == -1)
+ {
+ for (act = 0; act < NUM_ACTIONS; act++)
+ element_info[i].crumbled[act] =
+ element_info[crumbled_like].crumbled[act];
+ for (act = 0; act < NUM_ACTIONS; act++)
+ for (dir = 0; dir < NUM_DIRECTIONS; dir++)
+ element_info[i].direction_crumbled[act][dir] =
+ element_info[crumbled_like].direction_crumbled[act][dir];
+ }
+
+ if (diggable_like != -1 && element_info[i].graphic[ACTION_DIGGING] == -1)
+ {
+ element_info[i].graphic[ACTION_DIGGING] =
+ element_info[diggable_like].graphic[ACTION_DIGGING];
+ for (dir = 0; dir < NUM_DIRECTIONS; dir++)
+ element_info[i].direction_graphic[ACTION_DIGGING][dir] =
+ element_info[diggable_like].direction_graphic[ACTION_DIGGING][dir];
+ }
+ }
+
+#if 1
+ /* set hardcoded definitions for some runtime elements without graphic */
+ element_info[EL_AMOEBA_TO_DIAMOND].graphic[ACTION_DEFAULT] = IMG_AMOEBA_DEAD;
+#endif
+
+#if 1
+ /* now set all undefined/invalid graphics to -1 to set to default after it */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ for (act = 0; act < NUM_ACTIONS; act++)
+ {
+ int graphic;
+
+ graphic = element_info[i].graphic[act];
+ if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
+ element_info[i].graphic[act] = -1;
+
+ graphic = element_info[i].crumbled[act];
+ if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
+ element_info[i].crumbled[act] = -1;
+
+ for (dir = 0; dir < NUM_DIRECTIONS; dir++)
+ {
+ graphic = element_info[i].direction_graphic[act][dir];
+ if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
+ element_info[i].direction_graphic[act][dir] = -1;
+
+ graphic = element_info[i].direction_crumbled[act][dir];
+ if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
+ element_info[i].direction_crumbled[act][dir] = -1;
+ }
+ }
+ }
+#endif
+
+ /* now set all '-1' values to element specific default values */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ int default_graphic = element_info[i].graphic[ACTION_DEFAULT];
+ int default_crumbled = element_info[i].crumbled[ACTION_DEFAULT];
+ int default_direction_graphic[NUM_DIRECTIONS];
+ int default_direction_crumbled[NUM_DIRECTIONS];
+
+ if (default_graphic == -1)
+ default_graphic = IMG_UNKNOWN;
+ if (default_crumbled == -1)
+ default_crumbled = IMG_EMPTY;
+
+ for (dir = 0; dir < NUM_DIRECTIONS; dir++)
+ {
+ default_direction_graphic[dir] =
+ element_info[i].direction_graphic[ACTION_DEFAULT][dir];
+ default_direction_crumbled[dir] =
+ element_info[i].direction_crumbled[ACTION_DEFAULT][dir];
+
+ if (default_direction_graphic[dir] == -1)
+ default_direction_graphic[dir] = default_graphic;
+ if (default_direction_crumbled[dir] == -1)
+ default_direction_crumbled[dir] = default_crumbled;
+ }
+
+ for (act = 0; act < NUM_ACTIONS; act++)
+ {
+ boolean act_remove = ((IS_DIGGABLE(i) && act == ACTION_DIGGING) ||
+ (IS_SNAPPABLE(i) && act == ACTION_SNAPPING) ||
+ (IS_COLLECTIBLE(i) && act == ACTION_COLLECTING));
+ boolean act_turning = (act == ACTION_TURNING_FROM_LEFT ||
+ act == ACTION_TURNING_FROM_RIGHT ||
+ act == ACTION_TURNING_FROM_UP ||
+ act == ACTION_TURNING_FROM_DOWN);
+
+ /* generic default action graphic (defined by "[default]" directive) */
+ int default_action_graphic = element_info[EL_DEFAULT].graphic[act];
+ int default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
+
+ /* look for special default action graphic (classic game specific) */
+ if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].graphic[act] != -1)
+ default_action_graphic = element_info[EL_BD_DEFAULT].graphic[act];
+ if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].graphic[act] != -1)
+ default_action_graphic = element_info[EL_SP_DEFAULT].graphic[act];
+ if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].graphic[act] != -1)
+ default_action_graphic = element_info[EL_SB_DEFAULT].graphic[act];
+
+ if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].crumbled[act] != -1)
+ default_action_crumbled = element_info[EL_BD_DEFAULT].crumbled[act];
+ if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].crumbled[act] != -1)
+ default_action_crumbled = element_info[EL_SP_DEFAULT].crumbled[act];
+ if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].crumbled[act] != -1)
+ default_action_crumbled = element_info[EL_SB_DEFAULT].crumbled[act];
+
+#if 1
+ /* !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!! */
+ /* !!! make this better !!! */
+ if (i == EL_EMPTY_SPACE)
+ {
+ default_action_graphic = element_info[EL_DEFAULT].graphic[act];
+ default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
+ }
+#endif
+
+ if (default_action_graphic == -1)
+ default_action_graphic = default_graphic;
+ if (default_action_crumbled == -1)
+ default_action_crumbled = default_crumbled;
+
+ for (dir = 0; dir < NUM_DIRECTIONS; dir++)
+ {
+ int default_action_direction_graphic = element_info[i].graphic[act];
+ int default_action_direction_crumbled = element_info[i].crumbled[act];
+
+ /* no graphic for current action -- use default direction graphic */
+ if (default_action_direction_graphic == -1)
+ default_action_direction_graphic =
+ (act_remove ? IMG_EMPTY :
+ act_turning ?
+ element_info[i].direction_graphic[ACTION_TURNING][dir] :
+ default_direction_graphic[dir]);
+ if (default_action_direction_crumbled == -1)
+ default_action_direction_crumbled =
+ (act_remove ? IMG_EMPTY :
+ act_turning ?
+ element_info[i].direction_crumbled[ACTION_TURNING][dir] :
+ default_direction_crumbled[dir]);
+
+ if (element_info[i].direction_graphic[act][dir] == -1)
+ element_info[i].direction_graphic[act][dir] =
+ default_action_direction_graphic;
+ if (element_info[i].direction_crumbled[act][dir] == -1)
+ element_info[i].direction_crumbled[act][dir] =
+ default_action_direction_crumbled;
+ }
+
+ /* no graphic for this specific action -- use default action graphic */
+ if (element_info[i].graphic[act] == -1)
+ element_info[i].graphic[act] =
+ (act_remove ? IMG_EMPTY :
+ act_turning ? element_info[i].graphic[ACTION_TURNING] :
+ default_action_graphic);
+ if (element_info[i].crumbled[act] == -1)
+ element_info[i].crumbled[act] =
+ (act_remove ? IMG_EMPTY :
+ act_turning ? element_info[i].crumbled[ACTION_TURNING] :
+ default_action_crumbled);
+ }
+ }
+
+#if 1
+ /* set animation mode to "none" for each graphic with only 1 frame */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ for (act = 0; act < NUM_ACTIONS; act++)
+ {
+ int graphic = element_info[i].graphic[act];
+ int crumbled = element_info[i].crumbled[act];
+
+ if (graphic_info[graphic].anim_frames == 1)
+ graphic_info[graphic].anim_mode = ANIM_NONE;
+ if (graphic_info[crumbled].anim_frames == 1)
+ graphic_info[crumbled].anim_mode = ANIM_NONE;
+
+ for (dir = 0; dir < NUM_DIRECTIONS; dir++)
+ {
+ graphic = element_info[i].direction_graphic[act][dir];
+ crumbled = element_info[i].direction_crumbled[act][dir];
+
+ if (graphic_info[graphic].anim_frames == 1)
+ graphic_info[graphic].anim_mode = ANIM_NONE;
+ if (graphic_info[crumbled].anim_frames == 1)
+ graphic_info[crumbled].anim_mode = ANIM_NONE;
+ }
+ }
+ }
+#endif
+
+#if 0
+#if DEBUG
+ if (options.verbose)
+ {
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ if (element_info[i].graphic[ACTION_DEFAULT] == IMG_UNKNOWN &&
+ i != EL_UNKNOWN)
+ Error(ERR_RETURN, "warning: no graphic for element '%s' (%d)",
+ element_info[i].token_name, i);
+ }
+#endif
+#endif
+}
+
+void InitElementSpecialGraphicInfo()
+{
+ struct PropertyMapping *property_mapping = getImageListPropertyMapping();
+ int num_property_mappings = getImageListPropertyMappingSize();
+ int i, j;
+
+ /* always start with reliable default values */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
+ element_info[i].special_graphic[j] =
+ element_info[i].graphic[ACTION_DEFAULT];
+
+ /* initialize special element/graphic mapping from static configuration */
+ for (i = 0; element_to_special_graphic[i].element > -1; i++)
+ {
+ int element = element_to_special_graphic[i].element;
+ int special = element_to_special_graphic[i].special;
+ int graphic = element_to_special_graphic[i].graphic;
+ int base_graphic = el2baseimg(element);
+ boolean base_redefined = getImageListEntry(base_graphic)->redefined;
+ boolean special_redefined = getImageListEntry(graphic)->redefined;
+
+ /* if the base graphic ("emerald", for example) has been redefined,
+ but not the special graphic ("emerald.EDITOR", for example), do not
+ use an existing (in this case considered obsolete) special graphic
+ anymore, but use the automatically created (down-scaled) graphic */
+ if (base_redefined && !special_redefined)
+ continue;
+
+ element_info[element].special_graphic[special] = graphic;
+ }
+
+ /* initialize special element/graphic mapping from dynamic configuration */
+ for (i = 0; i < num_property_mappings; i++)
+ {
+ int element = property_mapping[i].base_index;
+ int special = property_mapping[i].ext3_index;
+ int graphic = property_mapping[i].artwork_index;
+
+ if (element >= MAX_NUM_ELEMENTS)
+ continue;
+
+ if (special >= 0 && special < NUM_SPECIAL_GFX_ARGS)
+ element_info[element].special_graphic[special] = graphic;
+ }
+
+#if 1
+ /* now set all undefined/invalid graphics to default */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
+ if (graphic_info[element_info[i].special_graphic[j]].bitmap == NULL)
+ element_info[i].special_graphic[j] =
+ element_info[i].graphic[ACTION_DEFAULT];
+#endif
+}
+
+static int get_element_from_token(char *token)
+{
+ int i;
+
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ if (strcmp(element_info[i].token_name, token) == 0)
+ return i;
+
+ return -1;
+}
+
+static void set_graphic_parameters(int graphic, char **parameter_raw)
+{
+ Bitmap *src_bitmap = getBitmapFromImageID(graphic);
+ int parameter[NUM_GFX_ARGS];
+ int anim_frames_per_row = 1, anim_frames_per_col = 1;
+ int anim_frames_per_line = 1;
+ int i;
+
+ /* get integer values from string parameters */
+ for (i = 0; i < NUM_GFX_ARGS; i++)
+ {
+ parameter[i] =
+ get_parameter_value(image_config_suffix[i].token, parameter_raw[i],
+ image_config_suffix[i].type);
+
+ if (image_config_suffix[i].type == TYPE_TOKEN)
+ parameter[i] = get_element_from_token(parameter_raw[i]);
+ }
+
+ graphic_info[graphic].bitmap = src_bitmap;
+
+ /* start with reliable default values */
+ graphic_info[graphic].src_x = 0;
+ graphic_info[graphic].src_y = 0;
+ graphic_info[graphic].width = TILEX;
+ graphic_info[graphic].height = TILEY;
+ graphic_info[graphic].offset_x = 0; /* one or both of these values ... */
+ graphic_info[graphic].offset_y = 0; /* ... will be corrected later */
+ graphic_info[graphic].crumbled_like = -1; /* do not use clone element */
+ graphic_info[graphic].diggable_like = -1; /* do not use clone element */
+ graphic_info[graphic].border_size = TILEX / 8; /* "CRUMBLED" border size */
+ graphic_info[graphic].scale_up_factor = 1; /* default: no scaling up */
+ graphic_info[graphic].anim_delay_fixed = 0;
+ graphic_info[graphic].anim_delay_random = 0;
+ graphic_info[graphic].post_delay_fixed = 0;
+ graphic_info[graphic].post_delay_random = 0;
+
+ /* optional x and y tile position of animation frame sequence */
+ if (parameter[GFX_ARG_XPOS] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].src_x = parameter[GFX_ARG_XPOS] * TILEX;
+ if (parameter[GFX_ARG_YPOS] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].src_y = parameter[GFX_ARG_YPOS] * TILEY;
+
+ /* optional x and y pixel position of animation frame sequence */
+ if (parameter[GFX_ARG_X] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].src_x = parameter[GFX_ARG_X];
+ if (parameter[GFX_ARG_Y] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].src_y = parameter[GFX_ARG_Y];
+
+ /* optional width and height of each animation frame */
+ if (parameter[GFX_ARG_WIDTH] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].width = parameter[GFX_ARG_WIDTH];
+ if (parameter[GFX_ARG_HEIGHT] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].height = parameter[GFX_ARG_HEIGHT];
+
+ /* optional zoom factor for scaling up the image to a larger size */
+ if (parameter[GFX_ARG_SCALE_UP_FACTOR] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].scale_up_factor = parameter[GFX_ARG_SCALE_UP_FACTOR];
+ if (graphic_info[graphic].scale_up_factor < 1)
+ graphic_info[graphic].scale_up_factor = 1; /* no scaling */
+
+ if (src_bitmap)
+ {
+ /* bitmap is not scaled at this stage, so calculate final size */
+ int scale_up_factor = graphic_info[graphic].scale_up_factor;
+ int src_bitmap_width = src_bitmap->width * scale_up_factor;
+ int src_bitmap_height = src_bitmap->height * scale_up_factor;
+
+ anim_frames_per_row = src_bitmap_width / graphic_info[graphic].width;
+ anim_frames_per_col = src_bitmap_height / graphic_info[graphic].height;
+ }
+
+ /* correct x or y offset dependent of vertical or horizontal frame order */
+ if (parameter[GFX_ARG_VERTICAL]) /* frames are ordered vertically */
+ {
+ graphic_info[graphic].offset_y =
+ (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
+ parameter[GFX_ARG_OFFSET] : graphic_info[graphic].height);
+ anim_frames_per_line = anim_frames_per_col;
+ }
+ else /* frames are ordered horizontally */
+ {
+ graphic_info[graphic].offset_x =
+ (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
+ parameter[GFX_ARG_OFFSET] : graphic_info[graphic].width);
+ anim_frames_per_line = anim_frames_per_row;
+ }
+
+ /* optionally, the x and y offset of frames can be specified directly */
+ if (parameter[GFX_ARG_XOFFSET] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].offset_x = parameter[GFX_ARG_XOFFSET];
+ if (parameter[GFX_ARG_YOFFSET] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].offset_y = parameter[GFX_ARG_YOFFSET];
+
+ /* automatically determine correct number of frames, if not defined */
+ if (parameter[GFX_ARG_FRAMES] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].anim_frames = parameter[GFX_ARG_FRAMES];
+ else if (parameter[GFX_ARG_XPOS] == 0 && !parameter[GFX_ARG_VERTICAL])
+ graphic_info[graphic].anim_frames = anim_frames_per_row;
+ else if (parameter[GFX_ARG_YPOS] == 0 && parameter[GFX_ARG_VERTICAL])
+ graphic_info[graphic].anim_frames = anim_frames_per_col;
+ else
+ graphic_info[graphic].anim_frames = 1;
+
+ graphic_info[graphic].anim_frames_per_line =
+ (parameter[GFX_ARG_FRAMES_PER_LINE] != ARG_UNDEFINED_VALUE ?
+ parameter[GFX_ARG_FRAMES_PER_LINE] : anim_frames_per_line);
+
+ graphic_info[graphic].anim_delay = parameter[GFX_ARG_DELAY];
+ if (graphic_info[graphic].anim_delay == 0) /* delay must be at least 1 */
+ graphic_info[graphic].anim_delay = 1;
+
+ graphic_info[graphic].anim_mode = parameter[GFX_ARG_ANIM_MODE];
+#if 0
+ if (graphic_info[graphic].anim_frames == 1)
+ graphic_info[graphic].anim_mode = ANIM_NONE;
+#endif
+
+ /* automatically determine correct start frame, if not defined */
+ if (parameter[GFX_ARG_START_FRAME] == ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].anim_start_frame = 0;
+ else if (graphic_info[graphic].anim_mode & ANIM_REVERSE)
+ graphic_info[graphic].anim_start_frame =
+ graphic_info[graphic].anim_frames - parameter[GFX_ARG_START_FRAME] - 1;
+ else
+ graphic_info[graphic].anim_start_frame = parameter[GFX_ARG_START_FRAME];
+
+ /* animation synchronized with global frame counter, not move position */
+ graphic_info[graphic].anim_global_sync = parameter[GFX_ARG_GLOBAL_SYNC];
+
+ /* optional element for cloning crumble graphics */
+ if (parameter[GFX_ARG_CRUMBLED_LIKE] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].crumbled_like = parameter[GFX_ARG_CRUMBLED_LIKE];
+
+ /* optional element for cloning digging graphics */
+ if (parameter[GFX_ARG_DIGGABLE_LIKE] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].diggable_like = parameter[GFX_ARG_DIGGABLE_LIKE];
+
+ /* optional border size for "crumbling" diggable graphics */
+ if (parameter[GFX_ARG_BORDER_SIZE] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].border_size = parameter[GFX_ARG_BORDER_SIZE];
+
+ /* this is only used for player "boring" and "sleeping" actions */
+ if (parameter[GFX_ARG_ANIM_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].anim_delay_fixed =
+ parameter[GFX_ARG_ANIM_DELAY_FIXED];
+ if (parameter[GFX_ARG_ANIM_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].anim_delay_random =
+ parameter[GFX_ARG_ANIM_DELAY_RANDOM];
+ if (parameter[GFX_ARG_POST_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].post_delay_fixed =
+ parameter[GFX_ARG_POST_DELAY_FIXED];
+ if (parameter[GFX_ARG_POST_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].post_delay_random =
+ parameter[GFX_ARG_POST_DELAY_RANDOM];
+
+ /* this is only used for toon animations */
+ graphic_info[graphic].step_offset = parameter[GFX_ARG_STEP_OFFSET];
+ graphic_info[graphic].step_delay = parameter[GFX_ARG_STEP_DELAY];
+
+ /* this is only used for drawing font characters */
+ graphic_info[graphic].draw_x = parameter[GFX_ARG_DRAW_XOFFSET];
+ graphic_info[graphic].draw_y = parameter[GFX_ARG_DRAW_YOFFSET];
+
+ /* this is only used for drawing envelope graphics */
+ graphic_info[graphic].draw_masked = parameter[GFX_ARG_DRAW_MASKED];
+}
+
+static void InitGraphicInfo()
+{
+ int fallback_graphic = IMG_CHAR_EXCLAM;
+ struct FileInfo *fallback_image = getImageListEntry(fallback_graphic);
+ Bitmap *fallback_bitmap = getBitmapFromImageID(fallback_graphic);
+ int num_images = getImageListSize();
+ int i;
+
+#if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
+ static boolean clipmasks_initialized = FALSE;
+ Pixmap src_pixmap;
+ XGCValues clip_gc_values;
+ unsigned long clip_gc_valuemask;
+ GC copy_clipmask_gc = None;
+#endif
+
+ checked_free(graphic_info);
+
+ graphic_info = checked_calloc(num_images * sizeof(struct GraphicInfo));
+
+#if 0
+ printf("::: graphic_info: %d entries\n", num_images);
+#endif
+
+#if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
+ if (clipmasks_initialized)
+ {
+ for (i = 0; i < num_images; i++)
+ {
+ if (graphic_info[i].clip_mask)
+ XFreePixmap(display, graphic_info[i].clip_mask);
+ if (graphic_info[i].clip_gc)
+ XFreeGC(display, graphic_info[i].clip_gc);
+
+ graphic_info[i].clip_mask = None;
+ graphic_info[i].clip_gc = None;
+ }
+ }
+#endif
+
+ for (i = 0; i < num_images; i++)
+ {
+ struct FileInfo *image = getImageListEntry(i);
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+ int first_frame, last_frame;
+ int scale_up_factor, src_bitmap_width, src_bitmap_height;
+
+#if 0
+ printf("::: image: '%s' [%d]\n", image->token, i);
+#endif
+
+#if 0
+ printf("::: image # %d: '%s' ['%s']\n",
+ i, image->token,
+ getTokenFromImageID(i));
+#endif
+
+ set_graphic_parameters(i, image->parameter);
+
+ /* now check if no animation frames are outside of the loaded image */
+
+ if (graphic_info[i].bitmap == NULL)
+ continue; /* skip check for optional images that are undefined */
+
+ /* bitmap is not scaled at this stage, so calculate final size */
+ scale_up_factor = graphic_info[i].scale_up_factor;
+ src_bitmap_width = graphic_info[i].bitmap->width * scale_up_factor;
+ src_bitmap_height = graphic_info[i].bitmap->height * scale_up_factor;
+
+ first_frame = 0;
+ getGraphicSource(i, first_frame, &src_bitmap, &src_x, &src_y);
+ if (src_x < 0 || src_y < 0 ||
+ src_x + TILEX > src_bitmap_width ||
+ src_y + TILEY > src_bitmap_height)
+ {
+ Error(ERR_RETURN_LINE, "-");
+ Error(ERR_RETURN, "warning: error found in config file:");
+ Error(ERR_RETURN, "- config file: '%s'",
+ getImageConfigFilename());
+ Error(ERR_RETURN, "- config token: '%s'",
+ getTokenFromImageID(i));
+ Error(ERR_RETURN, "- image file: '%s'",
+ src_bitmap->source_filename);
+ Error(ERR_RETURN,
+ "error: first animation frame out of bounds (%d, %d)",
+ src_x, src_y);
+ Error(ERR_RETURN, "custom graphic rejected for this element/action");
+
+#if 1
+ Error(ERR_RETURN, "scale_up_factor == %d", scale_up_factor);
+#endif
+
+ if (i == fallback_graphic)
+ Error(ERR_EXIT, "fatal error: no fallback graphic available");
+
+ Error(ERR_RETURN, "fallback done to 'char_exclam' for this graphic");
+ Error(ERR_RETURN_LINE, "-");
+
+ set_graphic_parameters(i, fallback_image->default_parameter);
+ graphic_info[i].bitmap = fallback_bitmap;
+ }
+
+ last_frame = graphic_info[i].anim_frames - 1;
+ getGraphicSource(i, last_frame, &src_bitmap, &src_x, &src_y);
+ if (src_x < 0 || src_y < 0 ||
+ src_x + TILEX > src_bitmap_width ||
+ src_y + TILEY > src_bitmap_height)
+ {
+ Error(ERR_RETURN_LINE, "-");
+ Error(ERR_RETURN, "warning: error found in config file:");
+ Error(ERR_RETURN, "- config file: '%s'",
+ getImageConfigFilename());
+ Error(ERR_RETURN, "- config token: '%s'",
+ getTokenFromImageID(i));
+ Error(ERR_RETURN, "- image file: '%s'",
+ src_bitmap->source_filename);
+ Error(ERR_RETURN,
+ "error: last animation frame (%d) out of bounds (%d, %d)",
+ last_frame, src_x, src_y);
+ Error(ERR_RETURN, "custom graphic rejected for this element/action");
+
+ if (i == fallback_graphic)
+ Error(ERR_EXIT, "fatal error: no fallback graphic available");
+
+ Error(ERR_RETURN, "fallback done to 'char_exclam' for this graphic");
+ Error(ERR_RETURN_LINE, "-");
+
+ set_graphic_parameters(i, fallback_image->default_parameter);
+ graphic_info[i].bitmap = fallback_bitmap;
+ }
+
+#if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
+ /* currently we need only a tile clip mask from the first frame */
+ getGraphicSource(i, first_frame, &src_bitmap, &src_x, &src_y);
+
+ if (copy_clipmask_gc == None)
+ {
+ clip_gc_values.graphics_exposures = False;
+ clip_gc_valuemask = GCGraphicsExposures;
+ copy_clipmask_gc = XCreateGC(display, src_bitmap->clip_mask,
+ clip_gc_valuemask, &clip_gc_values);
+ }
+
+ graphic_info[i].clip_mask =
+ XCreatePixmap(display, window->drawable, TILEX, TILEY, 1);
+
+ src_pixmap = src_bitmap->clip_mask;
+ XCopyArea(display, src_pixmap, graphic_info[i].clip_mask,
+ copy_clipmask_gc, src_x, src_y, TILEX, TILEY, 0, 0);
+
+ clip_gc_values.graphics_exposures = False;
+ clip_gc_values.clip_mask = graphic_info[i].clip_mask;
+ clip_gc_valuemask = GCGraphicsExposures | GCClipMask;
+
+ graphic_info[i].clip_gc =
+ XCreateGC(display, window->drawable, clip_gc_valuemask, &clip_gc_values);
+#endif
+ }
+
+#if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
+ if (copy_clipmask_gc)
+ XFreeGC(display, copy_clipmask_gc);
+
+ clipmasks_initialized = TRUE;
+#endif
+}
+
+static void InitElementSoundInfo()
+{
+ struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
+ int num_property_mappings = getSoundListPropertyMappingSize();
+ int i, j, act;
+
+ /* set values to -1 to identify later as "uninitialized" values */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ for (act = 0; act < NUM_ACTIONS; act++)
+ element_info[i].sound[act] = -1;
+
+ /* initialize element/sound mapping from static configuration */
+ for (i = 0; element_to_sound[i].element > -1; i++)
+ {
+ int element = element_to_sound[i].element;
+ int action = element_to_sound[i].action;
+ int sound = element_to_sound[i].sound;
+ boolean is_class = element_to_sound[i].is_class;
+
+ if (action < 0)
+ action = ACTION_DEFAULT;
+
+ if (!is_class)
+ element_info[element].sound[action] = sound;
+ else
+ for (j = 0; j < MAX_NUM_ELEMENTS; j++)
+ if (strcmp(element_info[j].class_name,
+ element_info[element].class_name) == 0)
+ element_info[j].sound[action] = sound;
+ }
+
+ /* initialize element class/sound mapping from dynamic configuration */
+ for (i = 0; i < num_property_mappings; i++)
+ {
+ int element_class = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
+ int action = property_mapping[i].ext1_index;
+ int sound = property_mapping[i].artwork_index;
+
+ if (element_class < 0 || element_class >= MAX_NUM_ELEMENTS)
+ continue;
+
+ if (action < 0)
+ action = ACTION_DEFAULT;
+
+ for (j = 0; j < MAX_NUM_ELEMENTS; j++)
+ if (strcmp(element_info[j].class_name,
+ element_info[element_class].class_name) == 0)
+ element_info[j].sound[action] = sound;
+ }
+
+ /* initialize element/sound mapping from dynamic configuration */
+ for (i = 0; i < num_property_mappings; i++)
+ {
+ int element = property_mapping[i].base_index;
+ int action = property_mapping[i].ext1_index;
+ int sound = property_mapping[i].artwork_index;
+
+ if (element >= MAX_NUM_ELEMENTS)
+ continue;
+
+ if (action < 0)
+ action = ACTION_DEFAULT;
+
+ element_info[element].sound[action] = sound;
+ }
+
+ /* now set all '-1' values to element specific default values */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ for (act = 0; act < NUM_ACTIONS; act++)
+ {
+ /* generic default action sound (defined by "[default]" directive) */
+ int default_action_sound = element_info[EL_DEFAULT].sound[act];
+
+ /* look for special default action sound (classic game specific) */
+ if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].sound[act] != -1)
+ default_action_sound = element_info[EL_BD_DEFAULT].sound[act];
+ if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].sound[act] != -1)
+ default_action_sound = element_info[EL_SP_DEFAULT].sound[act];
+ if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].sound[act] != -1)
+ default_action_sound = element_info[EL_SB_DEFAULT].sound[act];
+
+ /* !!! there's no such thing as a "default action sound" !!! */
+#if 0
+ /* look for element specific default sound (independent from action) */
+ if (element_info[i].sound[ACTION_DEFAULT] != -1)
+ default_action_sound = element_info[i].sound[ACTION_DEFAULT];
+#endif
+
+#if 1
+ /* !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!! */
+ /* !!! make this better !!! */
+ if (i == EL_EMPTY_SPACE)
+ default_action_sound = element_info[EL_DEFAULT].sound[act];
+#endif
+
+ /* no sound for this specific action -- use default action sound */
+ if (element_info[i].sound[act] == -1)
+ element_info[i].sound[act] = default_action_sound;
+ }
+ }
+}
+
+static void InitGameModeSoundInfo()
+{
+ int i;
+
+ /* set values to -1 to identify later as "uninitialized" values */
+ for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
+ menu.sound[i] = -1;
+
+ /* initialize gamemode/sound mapping from static configuration */
+ for (i = 0; gamemode_to_sound[i].sound > -1; i++)
+ {
+ int gamemode = gamemode_to_sound[i].gamemode;
+ int sound = gamemode_to_sound[i].sound;
+
+ if (gamemode < 0)
+ gamemode = GAME_MODE_DEFAULT;
+
+ menu.sound[gamemode] = sound;
+ }
+
+ /* now set all '-1' values to levelset specific default values */
+ for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
+ if (menu.sound[i] == -1)
+ menu.sound[i] = menu.sound[GAME_MODE_DEFAULT];
+
+#if 0
+ /* TEST ONLY */
+ for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
+ if (menu.sound[i] != -1)
+ printf("::: menu.sound[%d] == %d\n", i, menu.sound[i]);
+#endif
+}
+
+static void set_sound_parameters(int sound, char **parameter_raw)
+{
+ int parameter[NUM_SND_ARGS];
+ int i;
+
+ /* get integer values from string parameters */
+ for (i = 0; i < NUM_SND_ARGS; i++)
+ parameter[i] =
+ get_parameter_value(sound_config_suffix[i].token, parameter_raw[i],
+ sound_config_suffix[i].type);
+
+ /* explicit loop mode setting in configuration overrides default value */
+ if (parameter[SND_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
+ sound_info[sound].loop = parameter[SND_ARG_MODE_LOOP];
+
+ /* sound volume to change the original volume when loading the sound file */
+ sound_info[sound].volume = parameter[SND_ARG_VOLUME];
+
+ /* sound priority to give certain sounds a higher or lower priority */
+ sound_info[sound].volume = parameter[SND_ARG_VOLUME];
+}
+
+static void InitSoundInfo()
+{
+#if 0
+ struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
+ int num_property_mappings = getSoundListPropertyMappingSize();
+#endif
+ int *sound_effect_properties;
+ int num_sounds = getSoundListSize();
+ int i, j;
+
+ checked_free(sound_info);
+
+ sound_effect_properties = checked_calloc(num_sounds * sizeof(int));
+ sound_info = checked_calloc(num_sounds * sizeof(struct SoundInfo));
+
+ /* initialize sound effect for all elements to "no sound" */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ for (j = 0; j < NUM_ACTIONS; j++)
+ element_info[i].sound[j] = SND_UNDEFINED;
+
+ for (i = 0; i < num_sounds; i++)
+ {
+ struct FileInfo *sound = getSoundListEntry(i);
+ int len_effect_text = strlen(sound->token);
+
+ sound_effect_properties[i] = ACTION_OTHER;
+ sound_info[i].loop = FALSE; /* default: play sound only once */
+
+#if 0
+ printf("::: sound %d: '%s'\n", i, sound->token);
+#endif
+
+ /* determine all loop sounds and identify certain sound classes */
+
+ for (j = 0; element_action_info[j].suffix; j++)
+ {
+ int len_action_text = strlen(element_action_info[j].suffix);
+
+ if (len_action_text < len_effect_text &&
+ strcmp(&sound->token[len_effect_text - len_action_text],
+ element_action_info[j].suffix) == 0)
+ {
+ sound_effect_properties[i] = element_action_info[j].value;
+ sound_info[i].loop = element_action_info[j].is_loop_sound;
+
+ break;
+ }
+ }
+
+#if 0
+ if (strcmp(sound->token, "custom_42") == 0)
+ printf("::: '%s' -> %d\n", sound->token, sound_info[i].loop);
+#endif
+
+ /* associate elements and some selected sound actions */
+
+ for (j = 0; j < MAX_NUM_ELEMENTS; j++)
+ {
+ if (element_info[j].class_name)
+ {
+ int len_class_text = strlen(element_info[j].class_name);
+
+ if (len_class_text + 1 < len_effect_text &&
+ strncmp(sound->token,
+ element_info[j].class_name, len_class_text) == 0 &&
+ sound->token[len_class_text] == '.')
+ {
+ int sound_action_value = sound_effect_properties[i];
+
+ element_info[j].sound[sound_action_value] = i;
+ }
+ }
+ }
+
+ set_sound_parameters(i, sound->parameter);
+ }
+
+ free(sound_effect_properties);
+
+#if 0
+ /* !!! now handled in InitElementSoundInfo() !!! */
+ /* initialize element/sound mapping from dynamic configuration */
+ for (i = 0; i < num_property_mappings; i++)
+ {
+ int element = property_mapping[i].base_index;
+ int action = property_mapping[i].ext1_index;
+ int sound = property_mapping[i].artwork_index;
+
+ if (action < 0)
+ action = ACTION_DEFAULT;
+
+ printf("::: %d: %d, %d, %d ['%s']\n",
+ i, element, action, sound, element_info[element].token_name);
+
+ element_info[element].sound[action] = sound;
+ }
+#endif
+
+#if 0
+ /* TEST ONLY */
+ {
+ int element = EL_CUSTOM_11;
+ int j = 0;
+
+ while (element_action_info[j].suffix)
+ {
+ printf("element %d, sound action '%s' == %d\n",
+ element, element_action_info[j].suffix,
+ element_info[element].sound[j]);
+ j++;
+ }
+ }
+
+ PlaySoundLevelElementAction(0,0, EL_CUSTOM_11, ACTION_PUSHING);
+#endif
+
+#if 0
+ /* TEST ONLY */
+ {
+ int element = EL_SAND;
+ int sound_action = ACTION_DIGGING;
+ int j = 0;
+
+ while (element_action_info[j].suffix)
+ {
+ if (element_action_info[j].value == sound_action)
+ printf("element %d, sound action '%s' == %d\n",
+ element, element_action_info[j].suffix,
+ element_info[element].sound[sound_action]);
+ j++;
+ }
+ }
+#endif
+}
+
+static void InitGameModeMusicInfo()
+{
+ struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
+ int num_property_mappings = getMusicListPropertyMappingSize();
+ int default_levelset_music = -1;
+ int i;
+
+ /* set values to -1 to identify later as "uninitialized" values */
+ for (i = 0; i < MAX_LEVELS; i++)
+ levelset.music[i] = -1;
+ for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
+ menu.music[i] = -1;
+
+ /* initialize gamemode/music mapping from static configuration */
+ for (i = 0; gamemode_to_music[i].music > -1; i++)
+ {
+ int gamemode = gamemode_to_music[i].gamemode;
+ int music = gamemode_to_music[i].music;
+
+#if 0
+ printf("::: gamemode == %d, music == %d\n", gamemode, music);
+#endif
+
+ if (gamemode < 0)
+ gamemode = GAME_MODE_DEFAULT;
+
+ menu.music[gamemode] = music;
+ }
+
+ /* initialize gamemode/music mapping from dynamic configuration */
+ for (i = 0; i < num_property_mappings; i++)
+ {
+ int prefix = property_mapping[i].base_index;
+ int gamemode = property_mapping[i].ext1_index;
+ int level = property_mapping[i].ext2_index;
+ int music = property_mapping[i].artwork_index;
+
+#if 0
+ printf("::: prefix == %d, gamemode == %d, level == %d, music == %d\n",
+ prefix, gamemode, level, music);
+#endif
+
+ if (prefix < 0 || prefix >= NUM_MUSIC_PREFIXES)
+ continue;
+
+ if (gamemode < 0)
+ gamemode = GAME_MODE_DEFAULT;
+
+ /* level specific music only allowed for in-game music */
+ if (level != -1 && gamemode == GAME_MODE_DEFAULT)
+ gamemode = GAME_MODE_PLAYING;
+
+ if (level == -1)
+ {
+ level = 0;
+ default_levelset_music = music;
+ }
+
+ if (gamemode == GAME_MODE_PLAYING || gamemode == GAME_MODE_DEFAULT)
+ levelset.music[level] = music;
+ if (gamemode != GAME_MODE_PLAYING)
+ menu.music[gamemode] = music;
+ }
+
+ /* now set all '-1' values to menu specific default values */
+ /* (undefined values of "levelset.music[]" might stay at "-1" to
+ allow dynamic selection of music files from music directory!) */
+ for (i = 0; i < MAX_LEVELS; i++)
+ if (levelset.music[i] == -1)
+ levelset.music[i] = default_levelset_music;
+ for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
+ if (menu.music[i] == -1)
+ menu.music[i] = menu.music[GAME_MODE_DEFAULT];
+
+#if 0
+ /* TEST ONLY */
+ for (i = 0; i < MAX_LEVELS; i++)
+ if (levelset.music[i] != -1)
+ printf("::: levelset.music[%d] == %d\n", i, levelset.music[i]);
+ for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
+ if (menu.music[i] != -1)
+ printf("::: menu.music[%d] == %d\n", i, menu.music[i]);
+#endif
+}
+
+static void set_music_parameters(int music, char **parameter_raw)
+{
+ int parameter[NUM_MUS_ARGS];
+ int i;
+
+ /* get integer values from string parameters */
+ for (i = 0; i < NUM_MUS_ARGS; i++)
+ parameter[i] =
+ get_parameter_value(music_config_suffix[i].token, parameter_raw[i],
+ music_config_suffix[i].type);
+
+ /* explicit loop mode setting in configuration overrides default value */
+ if (parameter[MUS_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
+ music_info[music].loop = parameter[MUS_ARG_MODE_LOOP];
+}
+
+static void InitMusicInfo()
+{
+ int num_music = getMusicListSize();
+ int i, j;
+
+ checked_free(music_info);
+
+ music_info = checked_calloc(num_music * sizeof(struct MusicInfo));
+
+ for (i = 0; i < num_music; i++)
+ {
+ struct FileInfo *music = getMusicListEntry(i);
+ int len_music_text = strlen(music->token);
+
+ music_info[i].loop = TRUE; /* default: play music in loop mode */
+
+ /* determine all loop music */
+
+ for (j = 0; music_prefix_info[j].prefix; j++)
+ {
+ int len_prefix_text = strlen(music_prefix_info[j].prefix);
+
+ if (len_prefix_text < len_music_text &&
+ strncmp(music->token,
+ music_prefix_info[j].prefix, len_prefix_text) == 0)
+ {
+ music_info[i].loop = music_prefix_info[j].is_loop_music;
+
+ break;
+ }
+ }
+
+ set_music_parameters(i, music->parameter);
+ }
+}
+
+static void ReinitializeGraphics()
+{
+ InitGraphicInfo(); /* graphic properties mapping */
+ InitElementGraphicInfo(); /* element game graphic mapping */
+ InitElementSpecialGraphicInfo(); /* element special graphic mapping */
+
+ InitElementSmallImages(); /* scale images to all needed sizes */
+ InitFontGraphicInfo(); /* initialize text drawing functions */
+
+ SetMainBackgroundImage(IMG_BACKGROUND);
+ SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
+
+ InitGadgets();
+ InitToons();
+}
+
+static void ReinitializeSounds()
+{
+ InitSoundInfo(); /* sound properties mapping */
+ InitElementSoundInfo(); /* element game sound mapping */
+ InitGameModeSoundInfo(); /* game mode sound mapping */
+
+ InitPlayLevelSound(); /* internal game sound settings */
+}
+
+static void ReinitializeMusic()
+{
+ InitMusicInfo(); /* music properties mapping */
+ InitGameModeMusicInfo(); /* game mode music mapping */
+}
+
+static int get_special_property_bit(int element, int property_bit_nr)
+{
+ struct PropertyBitInfo
+ {
+ int element;
+ int bit_nr;
+ };
+
+ static struct PropertyBitInfo pb_can_move_into_acid[] =
+ {
+ /* the player may be able fall into acid when gravity is activated */
+ { EL_PLAYER_1, 0 },
+ { EL_PLAYER_2, 0 },
+ { EL_PLAYER_3, 0 },
+ { EL_PLAYER_4, 0 },
+ { EL_SP_MURPHY, 0 },
+ { EL_SOKOBAN_FIELD_PLAYER, 0 },
+
+ /* all element that can move may be able to also move into acid */
+ { EL_BUG, 1 },
+ { EL_BUG_LEFT, 1 },
+ { EL_BUG_RIGHT, 1 },
+ { EL_BUG_UP, 1 },
+ { EL_BUG_DOWN, 1 },
+ { EL_SPACESHIP, 2 },
+ { EL_SPACESHIP_LEFT, 2 },
+ { EL_SPACESHIP_RIGHT, 2 },
+ { EL_SPACESHIP_UP, 2 },
+ { EL_SPACESHIP_DOWN, 2 },
+ { EL_BD_BUTTERFLY, 3 },
+ { EL_BD_BUTTERFLY_LEFT, 3 },
+ { EL_BD_BUTTERFLY_RIGHT, 3 },
+ { EL_BD_BUTTERFLY_UP, 3 },
+ { EL_BD_BUTTERFLY_DOWN, 3 },
+ { EL_BD_FIREFLY, 4 },
+ { EL_BD_FIREFLY_LEFT, 4 },
+ { EL_BD_FIREFLY_RIGHT, 4 },
+ { EL_BD_FIREFLY_UP, 4 },
+ { EL_BD_FIREFLY_DOWN, 4 },
+ { EL_YAMYAM, 5 },
+ { EL_DARK_YAMYAM, 6 },
+ { EL_ROBOT, 7 },
+ { EL_PACMAN, 8 },
+ { EL_PACMAN_LEFT, 8 },
+ { EL_PACMAN_RIGHT, 8 },
+ { EL_PACMAN_UP, 8 },
+ { EL_PACMAN_DOWN, 8 },
+ { EL_MOLE, 9 },
+ { EL_MOLE_LEFT, 9 },
+ { EL_MOLE_RIGHT, 9 },
+ { EL_MOLE_UP, 9 },
+ { EL_MOLE_DOWN, 9 },
+ { EL_PENGUIN, 10 },
+ { EL_PIG, 11 },
+ { EL_DRAGON, 12 },
+ { EL_SATELLITE, 13 },
+ { EL_SP_SNIKSNAK, 14 },
+ { EL_SP_ELECTRON, 15 },
+ { EL_BALLOON, 16 },
+ { EL_SPRING, 17 },
+
+ { -1, -1 },
+ };
+
+ static struct PropertyBitInfo pb_dont_collide_with[] =
+ {
+ { EL_SP_SNIKSNAK, 0 },
+ { EL_SP_ELECTRON, 1 },
+
+ { -1, -1 },
+ };
+
+ static struct
+ {
+ int bit_nr;
+ struct PropertyBitInfo *pb_info;
+ } pb_definition[] =
+ {
+ { EP_CAN_MOVE_INTO_ACID, pb_can_move_into_acid },
+ { EP_DONT_COLLIDE_WITH, pb_dont_collide_with },
+
+ { -1, NULL },
+ };
+
+ struct PropertyBitInfo *pb_info = NULL;
+ int i;
+
+ for (i = 0; pb_definition[i].bit_nr != -1; i++)
+ if (pb_definition[i].bit_nr == property_bit_nr)
+ pb_info = pb_definition[i].pb_info;
+
+ if (pb_info == NULL)
+ return -1;
+
+ for (i = 0; pb_info[i].element != -1; i++)
+ if (pb_info[i].element == element)
+ return pb_info[i].bit_nr;
+
+ return -1;
+}
+
+#if 1
+void setBitfieldProperty(int *bitfield, int property_bit_nr, int element,
+ boolean property_value)
+{
+ int bit_nr = get_special_property_bit(element, property_bit_nr);
+
+ if (bit_nr > -1)
+ {
+ if (property_value)
+ *bitfield |= (1 << bit_nr);
+ else
+ *bitfield &= ~(1 << bit_nr);
+ }
+}
+
+boolean getBitfieldProperty(int *bitfield, int property_bit_nr, int element)
+{
+ int bit_nr = get_special_property_bit(element, property_bit_nr);
+
+ if (bit_nr > -1)
+ return ((*bitfield & (1 << bit_nr)) != 0);
+
+ return FALSE;
+}
+
+#else
+
+void setMoveIntoAcidProperty(struct LevelInfo *level, int element, boolean set)
+{
+ int bit_nr = get_special_property_bit(element, EP_CAN_MOVE_INTO_ACID);
+
+ if (bit_nr > -1)
+ {
+ level->can_move_into_acid_bits &= ~(1 << bit_nr);
+
+ if (set)
+ level->can_move_into_acid_bits |= (1 << bit_nr);
+ }
+}
+
+boolean getMoveIntoAcidProperty(struct LevelInfo *level, int element)
+{
+ int bit_nr = get_special_property_bit(element, EP_CAN_MOVE_INTO_ACID);
+
+ if (bit_nr > -1)
+ return ((level->can_move_into_acid_bits & (1 << bit_nr)) != 0);
+
+ return FALSE;
+}
+#endif
+
+void InitElementPropertiesStatic()
+{
+ static int ep_diggable[] =
+ {
+ EL_SAND,
+ EL_SP_BASE,
+ EL_SP_BUGGY_BASE,
+ EL_SP_BUGGY_BASE_ACTIVATING,
+ EL_TRAP,
+ EL_INVISIBLE_SAND,
+ EL_INVISIBLE_SAND_ACTIVE,
+
+ /* !!! currently not diggable, but handled by 'ep_dont_run_into' !!! */
+ /* (if amoeba can grow into anything diggable, maybe keep these out) */
+#if 0
+ EL_LANDMINE,
+ EL_TRAP_ACTIVE,
+ EL_SP_BUGGY_BASE_ACTIVE,
+#endif
+ -1
+ };
+
+ static int ep_collectible_only[] =
+ {
+ EL_BD_DIAMOND,
+ EL_EMERALD,
+ EL_DIAMOND,
+ EL_EMERALD_YELLOW,
+ EL_EMERALD_RED,
+ EL_EMERALD_PURPLE,
+ EL_KEY_1,
+ EL_KEY_2,
+ EL_KEY_3,
+ EL_KEY_4,
+ EL_EM_KEY_1,
+ EL_EM_KEY_2,
+ EL_EM_KEY_3,
+ EL_EM_KEY_4,
+ EL_DYNAMITE,
+ EL_DYNABOMB_INCREASE_NUMBER,
+ EL_DYNABOMB_INCREASE_SIZE,
+ EL_DYNABOMB_INCREASE_POWER,
+ EL_SP_INFOTRON,
+ EL_SP_DISK_RED,
+ EL_PEARL,
+ EL_CRYSTAL,
+ EL_KEY_WHITE,
+ EL_SHIELD_NORMAL,
+ EL_SHIELD_DEADLY,
+ EL_EXTRA_TIME,
+ EL_ENVELOPE_1,
+ EL_ENVELOPE_2,
+ EL_ENVELOPE_3,
+ EL_ENVELOPE_4,
+ EL_SPEED_PILL,
+ -1
+ };
+
+ static int ep_dont_run_into[] =
+ {
+ /* same elements as in 'ep_dont_touch' */
+ EL_BUG,
+ EL_SPACESHIP,
+ EL_BD_BUTTERFLY,
+ EL_BD_FIREFLY,
+
+ /* same elements as in 'ep_dont_collide_with' */
+ EL_YAMYAM,
+ EL_DARK_YAMYAM,
+ EL_ROBOT,
+ EL_PACMAN,
+ EL_SP_SNIKSNAK,
+ EL_SP_ELECTRON,
+
+ /* new elements */
+ EL_AMOEBA_DROP,
+ EL_ACID,
+
+ /* !!! maybe this should better be handled by 'ep_diggable' !!! */
+#if 1
+ EL_LANDMINE,
+ EL_TRAP_ACTIVE,
+ EL_SP_BUGGY_BASE_ACTIVE,
+#endif
+ -1
+ };
+
+ static int ep_dont_collide_with[] =
+ {
+ /* same elements as in 'ep_dont_touch' */
+ EL_BUG,
+ EL_SPACESHIP,
+ EL_BD_BUTTERFLY,
+ EL_BD_FIREFLY,
+
+ /* new elements */
+ EL_YAMYAM,
+ EL_DARK_YAMYAM,
+ EL_ROBOT,
+ EL_PACMAN,
+ EL_SP_SNIKSNAK,
+ EL_SP_ELECTRON,
+ -1
+ };
+
+ static int ep_dont_touch[] =
+ {
+ EL_BUG,
+ EL_SPACESHIP,
+ EL_BD_BUTTERFLY,
+ EL_BD_FIREFLY,
+ -1
+ };
+
+ static int ep_indestructible[] =
+ {
+ EL_STEELWALL,
+ EL_ACID,
+ EL_ACID_POOL_TOPLEFT,
+ EL_ACID_POOL_TOPRIGHT,
+ EL_ACID_POOL_BOTTOMLEFT,
+ EL_ACID_POOL_BOTTOM,
+ EL_ACID_POOL_BOTTOMRIGHT,
+ EL_SP_HARDWARE_GRAY,
+ EL_SP_HARDWARE_GREEN,
+ EL_SP_HARDWARE_BLUE,
+ EL_SP_HARDWARE_RED,
+ EL_SP_HARDWARE_YELLOW,
+ EL_SP_HARDWARE_BASE_1,
+ EL_SP_HARDWARE_BASE_2,
+ EL_SP_HARDWARE_BASE_3,
+ EL_SP_HARDWARE_BASE_4,
+ EL_SP_HARDWARE_BASE_5,
+ EL_SP_HARDWARE_BASE_6,
+ EL_INVISIBLE_STEELWALL,
+ EL_INVISIBLE_STEELWALL_ACTIVE,
+ EL_CONVEYOR_BELT_1_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
+ EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
+ EL_CONVEYOR_BELT_2_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
+ EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
+ EL_CONVEYOR_BELT_3_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
+ EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
+ EL_CONVEYOR_BELT_4_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
+ EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
+ EL_LIGHT_SWITCH,
+ EL_LIGHT_SWITCH_ACTIVE,
+ EL_SIGN_EXCLAMATION,
+ EL_SIGN_RADIOACTIVITY,
+ EL_SIGN_STOP,
+ EL_SIGN_WHEELCHAIR,
+ EL_SIGN_PARKING,
+ EL_SIGN_ONEWAY,
+ EL_SIGN_HEART,
+ EL_SIGN_TRIANGLE,
+ EL_SIGN_ROUND,
+ EL_SIGN_EXIT,
+ EL_SIGN_YINYANG,
+ EL_SIGN_OTHER,
+ EL_STEELWALL_SLIPPERY,
+ EL_EMC_STEELWALL_1,
+ EL_EMC_STEELWALL_2,
+ EL_EMC_STEELWALL_3,
+ EL_EMC_STEELWALL_4,
+ EL_CRYSTAL,
+ EL_GATE_1,
+ EL_GATE_2,
+ EL_GATE_3,
+ EL_GATE_4,
+ EL_GATE_1_GRAY,
+ EL_GATE_2_GRAY,
+ EL_GATE_3_GRAY,
+ EL_GATE_4_GRAY,
+ EL_EM_GATE_1,
+ EL_EM_GATE_2,
+ EL_EM_GATE_3,
+ EL_EM_GATE_4,
+ EL_EM_GATE_1_GRAY,
+ EL_EM_GATE_2_GRAY,
+ EL_EM_GATE_3_GRAY,
+ EL_EM_GATE_4_GRAY,
+ EL_SWITCHGATE_OPEN,
+ EL_SWITCHGATE_OPENING,
+ EL_SWITCHGATE_CLOSED,
+ EL_SWITCHGATE_CLOSING,
+#if 0
+ EL_SWITCHGATE_SWITCH_UP,
+ EL_SWITCHGATE_SWITCH_DOWN,
+#endif
+ EL_TIMEGATE_OPEN,
+ EL_TIMEGATE_OPENING,
+ EL_TIMEGATE_CLOSED,
+ EL_TIMEGATE_CLOSING,
+#if 0
+ EL_TIMEGATE_SWITCH,
+ EL_TIMEGATE_SWITCH_ACTIVE,
+#endif
+ EL_TUBE_ANY,
+ EL_TUBE_VERTICAL,
+ EL_TUBE_HORIZONTAL,
+ EL_TUBE_VERTICAL_LEFT,
+ EL_TUBE_VERTICAL_RIGHT,
+ EL_TUBE_HORIZONTAL_UP,
+ EL_TUBE_HORIZONTAL_DOWN,
+ EL_TUBE_LEFT_UP,
+ EL_TUBE_LEFT_DOWN,
+ EL_TUBE_RIGHT_UP,
+ EL_TUBE_RIGHT_DOWN,
+ -1
+ };
+
+ static int ep_slippery[] =
+ {
+ EL_WALL_SLIPPERY,
+ EL_BD_WALL,
+ EL_ROCK,
+ EL_BD_ROCK,
+ EL_EMERALD,
+ EL_BD_DIAMOND,
+ EL_EMERALD_YELLOW,
+ EL_EMERALD_RED,
+ EL_EMERALD_PURPLE,
+ EL_DIAMOND,
+ EL_BOMB,
+ EL_NUT,
+ EL_ROBOT_WHEEL_ACTIVE,
+ EL_ROBOT_WHEEL,
+ EL_TIME_ORB_FULL,
+ EL_TIME_ORB_EMPTY,
+ EL_LAMP_ACTIVE,
+ EL_LAMP,
+ EL_ACID_POOL_TOPLEFT,
+ EL_ACID_POOL_TOPRIGHT,
+ EL_SATELLITE,
+ EL_SP_ZONK,
+ EL_SP_INFOTRON,
+ EL_SP_CHIP_SINGLE,
+ EL_SP_CHIP_LEFT,
+ EL_SP_CHIP_RIGHT,
+ EL_SP_CHIP_TOP,
+ EL_SP_CHIP_BOTTOM,
+ EL_SPEED_PILL,
+ EL_STEELWALL_SLIPPERY,
+ EL_PEARL,
+ EL_CRYSTAL,
+ -1
+ };
+
+ static int ep_can_change[] =
+ {
+ -1
+ };
+
+ static int ep_can_move[] =
+ {
+ /* same elements as in 'pb_can_move_into_acid' */
+ EL_BUG,
+ EL_SPACESHIP,
+ EL_BD_BUTTERFLY,
+ EL_BD_FIREFLY,
+ EL_YAMYAM,
+ EL_DARK_YAMYAM,
+ EL_ROBOT,
+ EL_PACMAN,
+ EL_MOLE,
+ EL_PENGUIN,
+ EL_PIG,
+ EL_DRAGON,
+ EL_SATELLITE,
+ EL_SP_SNIKSNAK,
+ EL_SP_ELECTRON,
+ EL_BALLOON,
+ EL_SPRING,
+ -1
+ };
+
+ static int ep_can_fall[] =
+ {
+ EL_ROCK,
+ EL_BD_ROCK,
+ EL_EMERALD,
+ EL_BD_DIAMOND,
+ EL_EMERALD_YELLOW,
+ EL_EMERALD_RED,
+ EL_EMERALD_PURPLE,
+ EL_DIAMOND,
+ EL_BOMB,
+ EL_NUT,
+ EL_AMOEBA_DROP,
+ EL_QUICKSAND_FULL,
+ EL_MAGIC_WALL_FULL,
+ EL_BD_MAGIC_WALL_FULL,
+ EL_TIME_ORB_FULL,
+ EL_TIME_ORB_EMPTY,
+ EL_SP_ZONK,
+ EL_SP_INFOTRON,
+ EL_SP_DISK_ORANGE,
+ EL_PEARL,
+ EL_CRYSTAL,
+ EL_SPRING,
+ EL_DX_SUPABOMB,
+ -1
+ };
+
+ static int ep_can_smash_player[] =
+ {
+ EL_ROCK,
+ EL_BD_ROCK,
+ EL_EMERALD,
+ EL_BD_DIAMOND,
+ EL_EMERALD_YELLOW,
+ EL_EMERALD_RED,
+ EL_EMERALD_PURPLE,
+ EL_DIAMOND,
+ EL_BOMB,
+ EL_NUT,
+ EL_AMOEBA_DROP,
+ EL_TIME_ORB_FULL,
+ EL_TIME_ORB_EMPTY,
+ EL_SP_ZONK,
+ EL_SP_INFOTRON,
+ EL_SP_DISK_ORANGE,
+ EL_PEARL,
+ EL_CRYSTAL,
+ EL_SPRING,
+ EL_DX_SUPABOMB,
+ -1
+ };
+
+ static int ep_can_smash_enemies[] =
+ {
+ EL_ROCK,
+ EL_BD_ROCK,
+ EL_SP_ZONK,
+ -1
+ };
+
+ static int ep_can_smash_everything[] =
+ {
+ EL_ROCK,
+ EL_BD_ROCK,
+ EL_SP_ZONK,
+ -1
+ };
+
+ static int ep_explodes_by_fire[] =
+ {
+ /* same elements as in 'ep_explodes_impact' */
+ EL_BOMB,
+ EL_SP_DISK_ORANGE,
+ EL_DX_SUPABOMB,
+
+ /* same elements as in 'ep_explodes_smashed' */
+ EL_SATELLITE,
+ EL_PIG,
+ EL_DRAGON,
+ EL_MOLE,
+
+ /* new elements */
+ EL_DYNAMITE_ACTIVE,
+ EL_DYNAMITE,
+ EL_DYNABOMB_PLAYER_1_ACTIVE,
+ EL_DYNABOMB_PLAYER_2_ACTIVE,
+ EL_DYNABOMB_PLAYER_3_ACTIVE,
+ EL_DYNABOMB_PLAYER_4_ACTIVE,
+ EL_DYNABOMB_INCREASE_NUMBER,
+ EL_DYNABOMB_INCREASE_SIZE,
+ EL_DYNABOMB_INCREASE_POWER,
+ EL_SP_DISK_RED_ACTIVE,
+ EL_BUG,
+ EL_PENGUIN,
+ EL_SP_DISK_RED,
+ EL_SP_DISK_YELLOW,
+ EL_SP_SNIKSNAK,
+ EL_SP_ELECTRON,
+#if 0
+ EL_BLACK_ORB,
+#endif
+ -1
+ };
+
+ static int ep_explodes_smashed[] =
+ {
+ /* same elements as in 'ep_explodes_impact' */
+ EL_BOMB,
+ EL_SP_DISK_ORANGE,
+ EL_DX_SUPABOMB,
+
+ /* new elements */
+ EL_SATELLITE,
+ EL_PIG,
+ EL_DRAGON,
+ EL_MOLE,
+ -1
+ };
+
+ static int ep_explodes_impact[] =
+ {
+ EL_BOMB,
+ EL_SP_DISK_ORANGE,
+ EL_DX_SUPABOMB,
+ -1
+ };
+
+ static int ep_walkable_over[] =
+ {
+ EL_EMPTY_SPACE,
+ EL_SP_EMPTY_SPACE,
+ EL_SOKOBAN_FIELD_EMPTY,
+ EL_EXIT_OPEN,
+ EL_SP_EXIT_OPEN,
+ EL_SP_EXIT_OPENING,
+ EL_GATE_1,
+ EL_GATE_2,
+ EL_GATE_3,
+ EL_GATE_4,
+ EL_GATE_1_GRAY,
+ EL_GATE_2_GRAY,
+ EL_GATE_3_GRAY,
+ EL_GATE_4_GRAY,
+ EL_PENGUIN,
+ EL_PIG,
+ EL_DRAGON,
+ -1
+ };
+
+ static int ep_walkable_inside[] =
+ {
+ EL_TUBE_ANY,
+ EL_TUBE_VERTICAL,
+ EL_TUBE_HORIZONTAL,
+ EL_TUBE_VERTICAL_LEFT,
+ EL_TUBE_VERTICAL_RIGHT,
+ EL_TUBE_HORIZONTAL_UP,
+ EL_TUBE_HORIZONTAL_DOWN,
+ EL_TUBE_LEFT_UP,
+ EL_TUBE_LEFT_DOWN,
+ EL_TUBE_RIGHT_UP,
+ EL_TUBE_RIGHT_DOWN,
+ -1
+ };
+
+ static int ep_walkable_under[] =
+ {
+ -1
+ };
+
+ static int ep_passable_over[] =
+ {
+ EL_EM_GATE_1,
+ EL_EM_GATE_2,
+ EL_EM_GATE_3,
+ EL_EM_GATE_4,
+ EL_EM_GATE_1_GRAY,
+ EL_EM_GATE_2_GRAY,
+ EL_EM_GATE_3_GRAY,
+ EL_EM_GATE_4_GRAY,
+ EL_SWITCHGATE_OPEN,
+ EL_TIMEGATE_OPEN,
+ -1
+ };
+
+ static int ep_passable_inside[] =
+ {
+ EL_SP_PORT_LEFT,
+ EL_SP_PORT_RIGHT,
+ EL_SP_PORT_UP,
+ EL_SP_PORT_DOWN,
+ EL_SP_PORT_HORIZONTAL,
+ EL_SP_PORT_VERTICAL,
+ EL_SP_PORT_ANY,
+ EL_SP_GRAVITY_PORT_LEFT,
+ EL_SP_GRAVITY_PORT_RIGHT,
+ EL_SP_GRAVITY_PORT_UP,
+ EL_SP_GRAVITY_PORT_DOWN,
+ EL_SP_GRAVITY_ON_PORT_LEFT,
+ EL_SP_GRAVITY_ON_PORT_RIGHT,
+ EL_SP_GRAVITY_ON_PORT_UP,
+ EL_SP_GRAVITY_ON_PORT_DOWN,
+ EL_SP_GRAVITY_OFF_PORT_LEFT,
+ EL_SP_GRAVITY_OFF_PORT_RIGHT,
+ EL_SP_GRAVITY_OFF_PORT_UP,
+ EL_SP_GRAVITY_OFF_PORT_DOWN,
+ -1
+ };
+
+ static int ep_passable_under[] =
+ {
+ -1
+ };
+
+ static int ep_droppable[] =
+ {
+ -1
+ };
+
+ static int ep_explodes_1x1_old[] =
+ {
+ -1
+ };
+
+ static int ep_pushable[] =
+ {
+ EL_ROCK,
+ EL_BOMB,
+ EL_DX_SUPABOMB,
+ EL_NUT,
+ EL_TIME_ORB_EMPTY,
+ EL_SP_ZONK,
+ EL_SP_DISK_ORANGE,
+ EL_SPRING,
+ EL_BD_ROCK,
+ EL_SOKOBAN_OBJECT,
+ EL_SOKOBAN_FIELD_FULL,
+ EL_SATELLITE,
+ EL_SP_DISK_YELLOW,
+ EL_BALLOON,
+ -1
+ };
+
+ static int ep_explodes_cross_old[] =
+ {
+ -1
+ };
+
+ static int ep_protected[] =
+ {
+ /* same elements as in 'ep_walkable_inside' */
+ EL_TUBE_ANY,
+ EL_TUBE_VERTICAL,
+ EL_TUBE_HORIZONTAL,
+ EL_TUBE_VERTICAL_LEFT,
+ EL_TUBE_VERTICAL_RIGHT,
+ EL_TUBE_HORIZONTAL_UP,
+ EL_TUBE_HORIZONTAL_DOWN,
+ EL_TUBE_LEFT_UP,
+ EL_TUBE_LEFT_DOWN,
+ EL_TUBE_RIGHT_UP,
+ EL_TUBE_RIGHT_DOWN,
+
+ /* same elements as in 'ep_passable_over' */
+ EL_EM_GATE_1,
+ EL_EM_GATE_2,
+ EL_EM_GATE_3,
+ EL_EM_GATE_4,
+ EL_EM_GATE_1_GRAY,
+ EL_EM_GATE_2_GRAY,
+ EL_EM_GATE_3_GRAY,
+ EL_EM_GATE_4_GRAY,
+ EL_SWITCHGATE_OPEN,
+ EL_TIMEGATE_OPEN,
+
+ /* same elements as in 'ep_passable_inside' */
+ EL_SP_PORT_LEFT,
+ EL_SP_PORT_RIGHT,
+ EL_SP_PORT_UP,
+ EL_SP_PORT_DOWN,
+ EL_SP_PORT_HORIZONTAL,
+ EL_SP_PORT_VERTICAL,
+ EL_SP_PORT_ANY,
+ EL_SP_GRAVITY_PORT_LEFT,
+ EL_SP_GRAVITY_PORT_RIGHT,
+ EL_SP_GRAVITY_PORT_UP,
+ EL_SP_GRAVITY_PORT_DOWN,
+ EL_SP_GRAVITY_ON_PORT_LEFT,
+ EL_SP_GRAVITY_ON_PORT_RIGHT,
+ EL_SP_GRAVITY_ON_PORT_UP,
+ EL_SP_GRAVITY_ON_PORT_DOWN,
+ EL_SP_GRAVITY_OFF_PORT_LEFT,
+ EL_SP_GRAVITY_OFF_PORT_RIGHT,
+ EL_SP_GRAVITY_OFF_PORT_UP,
+ EL_SP_GRAVITY_OFF_PORT_DOWN,
+ -1
+ };
+
+ static int ep_throwable[] =
+ {
+ -1
+ };
+
+ static int ep_can_explode[] =
+ {
+ /* same elements as in 'ep_explodes_impact' */
+ EL_BOMB,
+ EL_SP_DISK_ORANGE,
+ EL_DX_SUPABOMB,
+
+ /* same elements as in 'ep_explodes_smashed' */
+ EL_SATELLITE,
+ EL_PIG,
+ EL_DRAGON,
+ EL_MOLE,
+
+ /* elements that can explode by explosion or by dragonfire */
+ EL_DYNAMITE_ACTIVE,
+ EL_DYNAMITE,
+ EL_DYNABOMB_PLAYER_1_ACTIVE,
+ EL_DYNABOMB_PLAYER_2_ACTIVE,
+ EL_DYNABOMB_PLAYER_3_ACTIVE,
+ EL_DYNABOMB_PLAYER_4_ACTIVE,
+ EL_DYNABOMB_INCREASE_NUMBER,
+ EL_DYNABOMB_INCREASE_SIZE,
+ EL_DYNABOMB_INCREASE_POWER,
+ EL_SP_DISK_RED_ACTIVE,
+ EL_BUG,
+ EL_PENGUIN,
+ EL_SP_DISK_RED,
+ EL_SP_DISK_YELLOW,
+ EL_SP_SNIKSNAK,
+ EL_SP_ELECTRON,
+
+ /* elements that can explode only by explosion */
+ EL_BLACK_ORB,
+ -1
+ };
+
+ static int ep_gravity_reachable[] =
+ {
+ EL_SAND,
+ EL_SP_BASE,
+ EL_TRAP,
+ EL_INVISIBLE_SAND,
+ EL_INVISIBLE_SAND_ACTIVE,
+ EL_SP_PORT_LEFT,
+ EL_SP_PORT_RIGHT,
+ EL_SP_PORT_UP,
+ EL_SP_PORT_DOWN,
+ EL_SP_PORT_HORIZONTAL,
+ EL_SP_PORT_VERTICAL,
+ EL_SP_PORT_ANY,
+ EL_SP_GRAVITY_PORT_LEFT,
+ EL_SP_GRAVITY_PORT_RIGHT,
+ EL_SP_GRAVITY_PORT_UP,
+ EL_SP_GRAVITY_PORT_DOWN,
+ EL_SP_GRAVITY_ON_PORT_LEFT,
+ EL_SP_GRAVITY_ON_PORT_RIGHT,
+ EL_SP_GRAVITY_ON_PORT_UP,
+ EL_SP_GRAVITY_ON_PORT_DOWN,
+ EL_SP_GRAVITY_OFF_PORT_LEFT,
+ EL_SP_GRAVITY_OFF_PORT_RIGHT,
+ EL_SP_GRAVITY_OFF_PORT_UP,
+ EL_SP_GRAVITY_OFF_PORT_DOWN,
+ -1
+ };
+
+ static int ep_player[] =
+ {
+ EL_PLAYER_1,
+ EL_PLAYER_2,
+ EL_PLAYER_3,
+ EL_PLAYER_4,
+ EL_SP_MURPHY,
+ EL_SOKOBAN_FIELD_PLAYER,
+ EL_TRIGGER_PLAYER,
+ -1
+ };
+
+ static int ep_can_pass_magic_wall[] =
+ {
+ EL_ROCK,
+ EL_BD_ROCK,
+ EL_EMERALD,
+ EL_BD_DIAMOND,
+ EL_EMERALD_YELLOW,
+ EL_EMERALD_RED,
+ EL_EMERALD_PURPLE,
+ EL_DIAMOND,
+ -1
+ };
+
+ static int ep_switchable[] =
+ {
+ EL_ROBOT_WHEEL,
+ EL_SP_TERMINAL,
+ EL_CONVEYOR_BELT_1_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
+ EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
+ EL_CONVEYOR_BELT_2_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
+ EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
+ EL_CONVEYOR_BELT_3_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
+ EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
+ EL_CONVEYOR_BELT_4_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
+ EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
+ EL_SWITCHGATE_SWITCH_UP,
+ EL_SWITCHGATE_SWITCH_DOWN,
+ EL_LIGHT_SWITCH,
+ EL_LIGHT_SWITCH_ACTIVE,
+ EL_TIMEGATE_SWITCH,
+ EL_BALLOON_SWITCH_LEFT,
+ EL_BALLOON_SWITCH_RIGHT,
+ EL_BALLOON_SWITCH_UP,
+ EL_BALLOON_SWITCH_DOWN,
+ EL_BALLOON_SWITCH_ANY,
+ EL_LAMP,
+ EL_TIME_ORB_FULL,
+ -1
+ };
+
+ static int ep_bd_element[] =
+ {
+ EL_EMPTY,
+ EL_SAND,
+ EL_WALL_SLIPPERY,
+ EL_BD_WALL,
+ EL_ROCK,
+ EL_BD_ROCK,
+ EL_BD_DIAMOND,
+ EL_BD_MAGIC_WALL,
+ EL_EXIT_CLOSED,
+ EL_EXIT_OPEN,
+ EL_STEELWALL,
+ EL_PLAYER_1,
+ EL_PLAYER_2,
+ EL_PLAYER_3,
+ EL_PLAYER_4,
+ EL_BD_FIREFLY,
+ EL_BD_FIREFLY_1,
+ EL_BD_FIREFLY_2,
+ EL_BD_FIREFLY_3,
+ EL_BD_FIREFLY_4,
+ EL_BD_BUTTERFLY,
+ EL_BD_BUTTERFLY_1,
+ EL_BD_BUTTERFLY_2,
+ EL_BD_BUTTERFLY_3,
+ EL_BD_BUTTERFLY_4,
+ EL_BD_AMOEBA,
+ EL_CHAR_QUESTION,
+ EL_UNKNOWN,
+ -1
+ };
+
+ static int ep_sp_element[] =
+ {
+ /* should always be valid */
+ EL_EMPTY,
+
+ /* standard classic Supaplex elements */
+ EL_SP_EMPTY,
+ EL_SP_ZONK,
+ EL_SP_BASE,
+ EL_SP_MURPHY,
+ EL_SP_INFOTRON,
+ EL_SP_CHIP_SINGLE,
+ EL_SP_HARDWARE_GRAY,
+ EL_SP_EXIT_CLOSED,
+ EL_SP_EXIT_OPEN,
+ EL_SP_DISK_ORANGE,
+ EL_SP_PORT_RIGHT,
+ EL_SP_PORT_DOWN,
+ EL_SP_PORT_LEFT,
+ EL_SP_PORT_UP,
+ EL_SP_GRAVITY_PORT_RIGHT,
+ EL_SP_GRAVITY_PORT_DOWN,
+ EL_SP_GRAVITY_PORT_LEFT,
+ EL_SP_GRAVITY_PORT_UP,
+ EL_SP_SNIKSNAK,
+ EL_SP_DISK_YELLOW,
+ EL_SP_TERMINAL,
+ EL_SP_DISK_RED,
+ EL_SP_PORT_VERTICAL,
+ EL_SP_PORT_HORIZONTAL,
+ EL_SP_PORT_ANY,
+ EL_SP_ELECTRON,
+ EL_SP_BUGGY_BASE,
+ EL_SP_CHIP_LEFT,
+ EL_SP_CHIP_RIGHT,
+ EL_SP_HARDWARE_BASE_1,
+ EL_SP_HARDWARE_GREEN,
+ EL_SP_HARDWARE_BLUE,
+ EL_SP_HARDWARE_RED,
+ EL_SP_HARDWARE_YELLOW,
+ EL_SP_HARDWARE_BASE_2,
+ EL_SP_HARDWARE_BASE_3,
+ EL_SP_HARDWARE_BASE_4,
+ EL_SP_HARDWARE_BASE_5,
+ EL_SP_HARDWARE_BASE_6,
+ EL_SP_CHIP_TOP,
+ EL_SP_CHIP_BOTTOM,
+
+ /* additional elements that appeared in newer Supaplex levels */
+ EL_INVISIBLE_WALL,
+
+ /* additional gravity port elements (not switching, but setting gravity) */
+ EL_SP_GRAVITY_ON_PORT_LEFT,
+ EL_SP_GRAVITY_ON_PORT_RIGHT,
+ EL_SP_GRAVITY_ON_PORT_UP,
+ EL_SP_GRAVITY_ON_PORT_DOWN,
+ EL_SP_GRAVITY_OFF_PORT_LEFT,
+ EL_SP_GRAVITY_OFF_PORT_RIGHT,
+ EL_SP_GRAVITY_OFF_PORT_UP,
+ EL_SP_GRAVITY_OFF_PORT_DOWN,
+
+ /* more than one Murphy in a level results in an inactive clone */
+ EL_SP_MURPHY_CLONE,
+
+ /* runtime Supaplex elements */
+ EL_SP_DISK_RED_ACTIVE,
+ EL_SP_TERMINAL_ACTIVE,
+ EL_SP_BUGGY_BASE_ACTIVATING,
+ EL_SP_BUGGY_BASE_ACTIVE,
+ EL_SP_EXIT_OPENING,
+ EL_SP_EXIT_CLOSING,
+ -1
+ };
+
+ static int ep_sb_element[] =
+ {
+ EL_EMPTY,
+ EL_STEELWALL,
+ EL_SOKOBAN_OBJECT,
+ EL_SOKOBAN_FIELD_EMPTY,
+ EL_SOKOBAN_FIELD_FULL,
+ EL_SOKOBAN_FIELD_PLAYER,
+ EL_PLAYER_1,
+ EL_PLAYER_2,
+ EL_PLAYER_3,
+ EL_PLAYER_4,
+ EL_INVISIBLE_STEELWALL,
+ -1
+ };
+
+ static int ep_gem[] =
+ {
+ EL_BD_DIAMOND,
+ EL_EMERALD,
+ EL_EMERALD_YELLOW,
+ EL_EMERALD_RED,
+ EL_EMERALD_PURPLE,
+ EL_DIAMOND,
+ -1
+ };
+
+ static int ep_food_dark_yamyam[] =
+ {
+ EL_SAND,
+ EL_BUG,
+ EL_SPACESHIP,
+ EL_BD_BUTTERFLY,
+ EL_BD_FIREFLY,
+ EL_YAMYAM,
+ EL_ROBOT,
+ EL_PACMAN,
+ EL_AMOEBA_DROP,
+ EL_AMOEBA_DEAD,
+ EL_AMOEBA_WET,
+ EL_AMOEBA_DRY,
+ EL_AMOEBA_FULL,
+ EL_BD_AMOEBA,
+ EL_EMERALD,
+ EL_BD_DIAMOND,
+ EL_EMERALD_YELLOW,
+ EL_EMERALD_RED,
+ EL_EMERALD_PURPLE,
+ EL_DIAMOND,
+ EL_PEARL,
+ EL_CRYSTAL,
+ -1
+ };
+
+ static int ep_food_penguin[] =
+ {
+ EL_EMERALD,
+ EL_BD_DIAMOND,
+ EL_EMERALD_YELLOW,
+ EL_EMERALD_RED,
+ EL_EMERALD_PURPLE,
+ EL_DIAMOND,
+ EL_PEARL,
+ EL_CRYSTAL,
+ -1
+ };
+
+ static int ep_food_pig[] =
+ {
+ EL_EMERALD,
+ EL_BD_DIAMOND,
+ EL_EMERALD_YELLOW,
+ EL_EMERALD_RED,
+ EL_EMERALD_PURPLE,
+ EL_DIAMOND,
+ -1
+ };
+
+ static int ep_historic_wall[] =
+ {
+ EL_STEELWALL,
+ EL_GATE_1,
+ EL_GATE_2,
+ EL_GATE_3,
+ EL_GATE_4,
+ EL_GATE_1_GRAY,
+ EL_GATE_2_GRAY,
+ EL_GATE_3_GRAY,
+ EL_GATE_4_GRAY,
+ EL_EM_GATE_1,
+ EL_EM_GATE_2,
+ EL_EM_GATE_3,
+ EL_EM_GATE_4,
+ EL_EM_GATE_1_GRAY,
+ EL_EM_GATE_2_GRAY,
+ EL_EM_GATE_3_GRAY,
+ EL_EM_GATE_4_GRAY,
+ EL_EXIT_CLOSED,
+ EL_EXIT_OPENING,
+ EL_EXIT_OPEN,
+ EL_WALL,
+ EL_WALL_SLIPPERY,
+ EL_EXPANDABLE_WALL,
+ EL_EXPANDABLE_WALL_HORIZONTAL,
+ EL_EXPANDABLE_WALL_VERTICAL,
+ EL_EXPANDABLE_WALL_ANY,
+ EL_EXPANDABLE_WALL_GROWING,
+ EL_BD_WALL,
+ EL_SP_CHIP_SINGLE,
+ EL_SP_CHIP_LEFT,
+ EL_SP_CHIP_RIGHT,
+ EL_SP_CHIP_TOP,
+ EL_SP_CHIP_BOTTOM,
+ EL_SP_HARDWARE_GRAY,
+ EL_SP_HARDWARE_GREEN,
+ EL_SP_HARDWARE_BLUE,
+ EL_SP_HARDWARE_RED,
+ EL_SP_HARDWARE_YELLOW,
+ EL_SP_HARDWARE_BASE_1,
+ EL_SP_HARDWARE_BASE_2,
+ EL_SP_HARDWARE_BASE_3,
+ EL_SP_HARDWARE_BASE_4,
+ EL_SP_HARDWARE_BASE_5,
+ EL_SP_HARDWARE_BASE_6,
+ EL_SP_TERMINAL,
+ EL_SP_TERMINAL_ACTIVE,
+ EL_SP_EXIT_CLOSED,
+ EL_SP_EXIT_OPEN,
+ EL_INVISIBLE_STEELWALL,
+ EL_INVISIBLE_STEELWALL_ACTIVE,
+ EL_INVISIBLE_WALL,
+ EL_INVISIBLE_WALL_ACTIVE,
+ EL_STEELWALL_SLIPPERY,
+ EL_EMC_STEELWALL_1,
+ EL_EMC_STEELWALL_2,
+ EL_EMC_STEELWALL_3,
+ EL_EMC_STEELWALL_4,
+ EL_EMC_WALL_1,
+ EL_EMC_WALL_2,
+ EL_EMC_WALL_3,
+ EL_EMC_WALL_4,
+ EL_EMC_WALL_5,
+ EL_EMC_WALL_6,
+ EL_EMC_WALL_7,
+ EL_EMC_WALL_8,
+ -1
+ };
+
+ static int ep_historic_solid[] =
+ {
+ EL_WALL,
+ EL_EXPANDABLE_WALL,
+ EL_EXPANDABLE_WALL_HORIZONTAL,
+ EL_EXPANDABLE_WALL_VERTICAL,
+ EL_EXPANDABLE_WALL_ANY,
+ EL_BD_WALL,
+ EL_WALL_SLIPPERY,
+ EL_EXIT_CLOSED,
+ EL_EXIT_OPENING,
+ EL_EXIT_OPEN,
+ EL_AMOEBA_DEAD,
+ EL_AMOEBA_WET,
+ EL_AMOEBA_DRY,
+ EL_AMOEBA_FULL,
+ EL_BD_AMOEBA,
+ EL_QUICKSAND_EMPTY,
+ EL_QUICKSAND_FULL,
+ EL_QUICKSAND_FILLING,
+ EL_QUICKSAND_EMPTYING,
+ EL_MAGIC_WALL,
+ EL_MAGIC_WALL_ACTIVE,
+ EL_MAGIC_WALL_EMPTYING,
+ EL_MAGIC_WALL_FILLING,
+ EL_MAGIC_WALL_FULL,
+ EL_MAGIC_WALL_DEAD,
+ EL_BD_MAGIC_WALL,
+ EL_BD_MAGIC_WALL_ACTIVE,
+ EL_BD_MAGIC_WALL_EMPTYING,
+ EL_BD_MAGIC_WALL_FULL,
+ EL_BD_MAGIC_WALL_FILLING,
+ EL_BD_MAGIC_WALL_DEAD,
+ EL_GAME_OF_LIFE,
+ EL_BIOMAZE,
+ EL_SP_CHIP_SINGLE,
+ EL_SP_CHIP_LEFT,
+ EL_SP_CHIP_RIGHT,
+ EL_SP_CHIP_TOP,
+ EL_SP_CHIP_BOTTOM,
+ EL_SP_TERMINAL,
+ EL_SP_TERMINAL_ACTIVE,
+ EL_SP_EXIT_CLOSED,
+ EL_SP_EXIT_OPEN,
+ EL_INVISIBLE_WALL,
+ EL_INVISIBLE_WALL_ACTIVE,
+ EL_SWITCHGATE_SWITCH_UP,
+ EL_SWITCHGATE_SWITCH_DOWN,
+ EL_TIMEGATE_SWITCH,
+ EL_TIMEGATE_SWITCH_ACTIVE,
+ EL_EMC_WALL_1,
+ EL_EMC_WALL_2,
+ EL_EMC_WALL_3,
+ EL_EMC_WALL_4,
+ EL_EMC_WALL_5,
+ EL_EMC_WALL_6,
+ EL_EMC_WALL_7,
+ EL_EMC_WALL_8,
+ EL_WALL_PEARL,
+ EL_WALL_CRYSTAL,
+
+ /* the following elements are a direct copy of "indestructible" elements,
+ except "EL_ACID", which is "indestructible", but not "solid"! */
+#if 0
+ EL_ACID,
+#endif
+ EL_STEELWALL,
+ EL_ACID_POOL_TOPLEFT,
+ EL_ACID_POOL_TOPRIGHT,
+ EL_ACID_POOL_BOTTOMLEFT,
+ EL_ACID_POOL_BOTTOM,
+ EL_ACID_POOL_BOTTOMRIGHT,
+ EL_SP_HARDWARE_GRAY,
+ EL_SP_HARDWARE_GREEN,
+ EL_SP_HARDWARE_BLUE,
+ EL_SP_HARDWARE_RED,
+ EL_SP_HARDWARE_YELLOW,
+ EL_SP_HARDWARE_BASE_1,
+ EL_SP_HARDWARE_BASE_2,
+ EL_SP_HARDWARE_BASE_3,
+ EL_SP_HARDWARE_BASE_4,
+ EL_SP_HARDWARE_BASE_5,
+ EL_SP_HARDWARE_BASE_6,
+ EL_INVISIBLE_STEELWALL,
+ EL_INVISIBLE_STEELWALL_ACTIVE,
+ EL_CONVEYOR_BELT_1_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
+ EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
+ EL_CONVEYOR_BELT_2_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
+ EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
+ EL_CONVEYOR_BELT_3_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
+ EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
+ EL_CONVEYOR_BELT_4_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
+ EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
+ EL_LIGHT_SWITCH,
+ EL_LIGHT_SWITCH_ACTIVE,
+ EL_SIGN_EXCLAMATION,
+ EL_SIGN_RADIOACTIVITY,
+ EL_SIGN_STOP,
+ EL_SIGN_WHEELCHAIR,
+ EL_SIGN_PARKING,
+ EL_SIGN_ONEWAY,
+ EL_SIGN_HEART,
+ EL_SIGN_TRIANGLE,
+ EL_SIGN_ROUND,
+ EL_SIGN_EXIT,
+ EL_SIGN_YINYANG,
+ EL_SIGN_OTHER,
+ EL_STEELWALL_SLIPPERY,
+ EL_EMC_STEELWALL_1,
+ EL_EMC_STEELWALL_2,
+ EL_EMC_STEELWALL_3,
+ EL_EMC_STEELWALL_4,
+ EL_CRYSTAL,
+ EL_GATE_1,
+ EL_GATE_2,
+ EL_GATE_3,
+ EL_GATE_4,
+ EL_GATE_1_GRAY,
+ EL_GATE_2_GRAY,
+ EL_GATE_3_GRAY,
+ EL_GATE_4_GRAY,